fotoxx-18.01.1/0000775000175000017500000000000013222767271011664 5ustar micomicofotoxx-18.01.1/doc/0000755000175000017500000000000013222767271012427 5ustar micomicofotoxx-18.01.1/doc/fotoxx.man0000744000175000017500000000766413222767271014471 0ustar micomico.TH FOTOXX 1 2017-08-01 "Linux" "Fotoxx man page" .SH NAME Fotoxx - photo/image editor and collection manager .SH SYNOPSIS \fBFotoxx\fR [ \fBoptions\fR ] [ \fIfile\fR | \fIdirectory\fR ] .SH DESCRIPTION Organize and manage a large image collection. Edit and optimize photos and other images, search images, and perform batch operations. Fotoxx is a GTK application which operates in its own window. The included user manual explains its operation in great detail. The following is a summary of capabilities. .SH OVERVIEW Image edit functions include: - View and edit most image and RAW file types - Works internally with 24-bits/color, outputs 8 or 16 bits/color - Trim (crop), Resize, Flip, Level, Rotate, Add Text - Adjust brightness/color/contrast using movable curves - Edit the brightness distribution directly for best balance - Eliminate color caste and reduce fog/haze - Retinex method - Enhance faint details - magnify gradients, flatten distribution - Sharpen, Blur, Reduce noise, Remove red-eyes, fix perspective - Edit selected objects or areas separately within an image - Special function to select complex edges such as hair or trees - Copy and paste selected objects or areas across images - Remove a color caste that may vary across the image - Fix vignetting and other brightness uniformity problems - Warp (stretch/distort) an image by dragging the mouse - Record edits for one image, apply to any number of images - Special effects (drawing, painting, cartoon, 3D relief ...) - Edit and apply canned and custom convolution kernels - Pixel edit with variable brush transparency and blending - Paint retouch functions locally and gradually with the mouse - Smart erase: remove ground litter, power lines, etc. - Erase dust: remove dust spots on images from scanned slides - Flatten photo of a curved book page, stretch compressed text - Write text on an image (font/color/transparency/shadow/angle) - Mashup: Arrange images and formatted text in a layout - Panorama, HDR and Stack composites (hand-held camera OK) - Combine multiple photos with varying focus depths (HDF) - Show videos as images and thumbnails, edit metadata, play - Use Gimp, ImageMagick, etc. as plug-in functions Utility functions include: - Context aware help available via the F1 key - Thumbnail browser and navigator, click image to view/edit - Hierarchical directory view - expand or collapse at each level - Custom "favorites" menu with user content and layout - Assign custom keyboard shortcuts to menu functions - Bookmark image locations, jump to bookmarked location - Edit any image metadata (EXIF/IPTC tags, dates, comments...) - Convert image color profile (e.g. sRGB <-> Adobe RGB) - Search and report any metadata - reports thumbnails with text - Use geotags from camera GPS, add/edit geotags manually - Search images by time, location, tags ... any metadata - World map at any scale, click on a marker for image gallery - Save and recall named map locations (map position and scale) - Batch convert format, resize, move, export, burn DVD/BlueRay - Batch rename images, insert dates and sequence numbers - Batch convert RAW files to tiff-8/16, png-8/16, jpeg - Print image at any size for any paper format - Calibrate printer for more accurate printed colors - Create albums (views), arrange images, drag & drop - Slide-show with animated transitions and image pan/zoom - Monitor color and gamma test patterns .SH OPTIONS Command line options \fB-v\fR print version and exit \fB-lang\fR \fIcode\fR specify language (de, fr ...) \fB-prev\fR open last file from the previous session \fB-recent\fR show a gallery of most recent files \fIfile\fR initial image file to view or edit \fIdirectory\fR initial directory for image gallery .SH SEE ALSO The home page for Fotoxx is at https://kornelix.net .SH AUTHORS Written by Mike Cornelison https://kornelix.net fotoxx-18.01.1/doc/changelog0000744000175000017500000003622713222767271014314 0ustar micomicoFotoxx Change Log https://kornelix.net ================= 2018 Jan 02 v.18.01.1 Bug Fix: Crash in metadata related functions if the default tag category 'nocatg' has been deleted. This is now replaced automatically. 2018 Jan 01 v.18.01 - changes from prior release 17.08 (August 2017) ------------------- Technical Changes: • The image index format was revised, requiring a new index initialization. This runs fast (up to 7000/min.) since new thumbnails are not needed. • Image Index initialization: work is now divided among 4 parallel threads. On a computer with 4 processor cores and an SSD disk, indexing is about 3x faster (ca. 6000/min). There is no speedup for a rotating disk (1000/min). • The maximum thread count for some functions was increased from 4 to 8, to better utilize the newest processors from Intel and AMD. • Use of the shell 'find' command was mostly replaced with calls to glob(). This is fail-safe against special characters in file names. • There is now an option to place the Fotoxx home directory wherever wanted. The file "/home//.fotoxx-home" may be used to set a custom location. • Font for gallery windows is 1 point less than the user setting for dialogs. • Retained geocoordinate precision was improved (now within 1 meter). • Many GTK widgets were downsized by reducing unnecessary margin padding. This can be revised or undone by editing the file "widgets.css". • The custom [small] spin button widget now spins increasingly faster when the +/- key or up/down arrow key is held down more than 1 second. Functional Changes: • The image index may now include up to 20 user-defined metadata items (search speed for indexed metadata is >1000x faster than non-indexed). • The restriction on sorting a metadata report was removed. • Effects > Pattern: GUI improvements and better responsiveness. • Updating Albums: GUI improvements to make it easier to update affected albums after an image file is updated or a new version is created. • Play video: Keyboard commands revised: 'P' to play/stop/resume, 'Q' to quit. • New slide show transition: the next image swells up from the middle and compresses the previous image out to the sides. • Galleries retain both sort and scroll position within and across sessions. All galleries can be reset to 'file name ascending' order with one button. • Retouch-Combo: GUI improved for setting white balance and black point. • Retinex: new edit function to improve color and contrast for images with extreme fog/haze or color cast (e.g. a scan of a 100 year-old photo). Retinex can also be used to add flair to most other images. • Batch Photo Date/Time: new function to fix missing or wrong photo date/time, or shift the date/time a given amount (e.g. camera in wrong time zone). • Batch Geotags: Location and Country can be updated without changing existing geocoordinates (e.g. rationalize chaotic names from the camera GPS). • Keyboard navigation: some search and select functions that output a list now allow navigation of the list using the arrow, page and home/end keys. You can also enter text to make the list scroll to the next entry with matching text. • KB shortcuts: most can be customized, most are now available in gallery view. The menu functions that can have shortcuts were expanded from 27 to 94. • Menu font and background colors can be set to match a 'theme' or personal taste (these are generated graphics and do not follow themes automatically). • Two batch RAW conversion functions were consolidated into one, with options. • Search Images: search by file mod date added (in addition to photo date). • View360 function: View a 360° panorama image (e.g. Google photosphere). Viewpoint can turn through 360° and wrap-around by dragging the mouse. • Manage Tags: stop auto-delete of unused tags. User must delete if unwanted. • Mosaic: made much faster by using parallel threads. Blend is now instant. • KB shortcuts: make '+' work as 'zoom-in' for any keyboard layout. • Zonal Flatten: allow zone count = 1 (i.e. use the entire image distribtion). • Adjust HSL: use a sample spot of 3x3 pixels instead of one pixel. • Gallery view: use arrow keys to index current file and jump across galleries. • Revised menu names: Zonal Flatten > Flatten, Tone Mapping > Gradients, Flip Image > Mirror Image, Write Text > Add Text, Write Line > Add Line, Brightness Gradient > Brightness Ramp, Local Color > Zonal Colors Bug Fixes: • Panorama: PNG images with alpha channels were sometimes made invisible. • Possible lockup after a long image indexing process. Exiftool memory leak. • Brightness Ramp: direction line sometimes persisted after function exit. • Albums: stop removal of videos from album if 'ffmpeg' or 'totem' is missing. • All directories tree: possible crash if navigating via keyboard arrow keys. • Search Images metadata report: missing data for the first image in the list. • Search Images with metadata selection criteria: minor memory leak plugged. • Vertical Panorama: fine alignment via mouse caused image to partly disappear. • Favorites menu: stop crash if assigned menu name is a 'top' menu. • Favorites menu: stop movement of menu position if 'user settings' selected. • The interface to Raw Thereapee was updated for changes in Raw Therapee. • Some keyboard shortcut keys were not working for non-English locales. • Spurious "kill active dialog" message if image-index [x] button is used. • Crash "max. dialogs exceeded" after 15 "Edit Brightness" done in a script. • Stop spurious log file diagnostic from thumbnail clicks: "f_open re-entry". • Batch Utilities: stop flashing of title bar with each file opened. • Crash "no thumbnail directory" needed a better diagnostic. • Image Index: RAW files were being omitted (auto updated when referenced). • Make all date/time input formats the same. User Guide: The user guide was audited to fix many minor errors and omissions. The images were all updated and some were enlarged for better visibility. A clickable table of contents and a clickable topic index were added. Wayland Note: Fotoxx works OK with Wayland/Xwayland with one exception: when an image is zoomed-in by clicking on a spot, the pointer no longer follows the spot but stays fixed. This is a known bug or omission (only user can move pointer). Bug Fix releases for Fotoxx 17.08 --------------------------------- 17.08.3 • User settings for video file types were not being saved. • Add missing popup diagnostic for libraw (RAW file) errors. • Check for 'hugin' instead of 'hugin-executor'. 17.08.2 • Crash if invalid thumbnail parameter is inherited from a prior release. 17.08.1 • Crash in gallery list view for directory outside the indexed directories. 2017 Aug 01 v.17.08 -------------------- Technical Changes: • Thumbnails were enlarged to 512 pixels for faster galleries when using large thumbnails. To implement, delete your thumbnails directory and re-index. • Use of shell "cp filename" was replaced with a copyfile() function, which makes batch functions fail-safe against special characters in file names. • New widget types were implemented to make some dialogs smaller, covering less of the image being viewed. Number values can be indexed up or down using the mouse wheel, keyboard up/down arrow keys, or the [+] [-] keys. The shift key can be held down to increase the step size and speed by 10x. Functional Changes: • Video files display as both images (initial frame) and gallery thumbnails. Click to play. Also editable metadata, albums, search, and slide show play. • Maps: save and recall user-named map locations (map position and scale). • Panorama: the flatten function was made variable and reversible. • Retouch Combo: the curve edit user interface was improved. • Galleries remember and restore their last scroll position with each view. • Select Hairy: improved algorithm, faster and better user interface. • Select Area > Find Gap: fatter lines are drawn for better visibility. • Custom Kernel: added functionality. Ten more pre-installed filters. • Paint Image: added an HSL color chooser as an additional color method. • Slide Show: 2 new transition types. 5 others made faster on slow computers. • Color Profile: EXIF "ICCProfileName" is updated and embedded profile deleted. • Export Image Files: added metadata relevant for photo web services. • Search Images: match 'all' or 'any' logic was implemented for location. • Gallery List View expanded: show small thumbnails + most relevant metadata. Bug Fixes: • Slide Show: workaround for GDK pixbuf scaling bug. • Flickr Upload stopped working and was removed. Flickr and other photo web services can do this directly. The User Guide describes a simple procedure. • A startup album name (if any) was getting replaced with blanks. • Retouch Combo: failed if directory for saved settings is missing (create it). • Index Image Files: failed if recent files file is missing (create it). • Possible crash if Trim/Rotate [level] button is used (EXIF tilt angle). 2017 Jul 01 v.17.04.3 ---------------------- Bug Fixes: • Manage Albums, Choose Album: [cancel] left all album menus disabled. • Slide Show, zoom image function: fix memory leak. • Select Area, Find Gap: crash if gap search killed before complete. • EXIF data update: possible crash if input keyword data is null. • Color Profile: liblcms2 can produce RGB values < 0. Replace these with 0. • File index process sometimes omitted a few files. This was self-repairing. • Metadata Report: leafpad fails to display report file - replaced it. 2017 May 02 v.17.04.2 ---------------------- Bug Fixes: • Workaround for GTK draw event loop in Paint Image palette selection. • Retouch Combo: failed to update distribution graph with dialog edits. 2017 Apr 11 v.17.04.1 ---------------------- Bug Fixes: • Crash in thumbnail gallery of RAW files if thumbnail size is large. • Sharpen Image: add error message if [apply] and no method was chosen. • Retouch Combo: [reset] omitted reset of "amplify" slider. • Popup menus: offset from mouse position to make menu cancel easier. 2017 Apr 01 v.17.04 -------------------- Technical: • Source code revisions for GTK API changes (Ubuntu 17.04, Fedora 25). • Implementation of AppImage, a distro-agnostic packaging format. • Smoother gallery scrolling via thread to pre-fetch thumbnail images. Functionality: • Image Montage: join selected images together into a compact table format. Images included in the table can be clicked to show a full-size image. • Paint Image: modified to make it easier to use with a Wacom tablet. • Paint Image: use any image as a custom color palette - click to select color. • Edited images get an automatic tag, allowing "edited" as a search criteria. • Alien Colors effect: repaint an image or area with random strange colors. • Stack/Paint: Transient forground objects can be removed automatically, and a moving subject can be shown in multiple positions against the background. • Twist effect: twist or swirl an image around a mouse-selected central point. • Script Files: most of the special effects functions are now scriptable. • Denoise - measure noise: the noise is more completely characterized. • The desktop wallpaper functions were removed because they stopped working. User Interface: • Paint Image and Clone Image were split into separate functions. • Trim/Rotate: margin sides can be dragged in addition to the corners. • Rename, Copy/Move, Delete/Trash: options were added to keep these dialogs open or not (keep open is easier/faster if processing multiple files). • Albums can now be sorted like any other gallery, but cannot be edited when in an out-of-sequence condition. To edit, re-open the album. • User Settings: an album name can be specified as the startup gallery. Bug Fixes: • Startup with file parameter: now works for absolute or relative file name. • Correctly handle file and directory names containing '$' characters. • Voodoo functions can now be assigned to shortcut keys like any other. • Disallow re-sorting of a metadata report, which loses the link between images and their metadata. Sort the gallery before generating the report. • Copy to Desktop or Clipboard: an edited but unsaved file copies correctly. • Trim/Rotate: stop sporadic motion of zoomed image when margins are dragged. Next release: planned for Oct. 2017 if there is significant new stuff. 2017 Feb 28 v.17.01.2 ---------------------- Bug Fixes: • Read TIFF file with alpha channel: alpha data was being ignored. • Panorama: crash if images have too little overlap. 2017 Jan 21 v.17.01.1 ---------------------- The Spanish and Catalan translations were updated. No version change. 2017 Jan 12 v.17.01.1 ---------------------- Bug Fixes: • Rare crash at startup after new image files are found and indexed. (This was self-repairing at the next startup). • Crash if the de-noise dialog was quit with the measure-noise dialog open. 2017 Jan 01 v.17.01 -------------------- New Functionality: • Show the entire image directory hierarchy with [+] and [-] buttons to expand or contract subdirectories at each level. Click any entry for a gallery view. • Blend Image: Paint with the mouse to blend pixels within the mouse circle. • Replace Album File: Make it easier to update albums whenever member images are added or updated. Replace a given old image with a given new image, in selected (or all) albums, or add the new image after the old. • Maps: An option was added to show markers only for images in the current gallery (e.g. search results or album). The default is to show all images. • Color Mode: A new option was added to "rotate" the colors (RGB -> BRG), and each function operates on prior results instead of the original image. • Three new slide show transitions: turn-cube, windmill, pixelize. • Adjust HSL: both source and target colors can be selected with mouse clicks. • Slide Show - button to use current gallery (no album selection). Efficiency Improvements: • Image indexing is 1.9x faster for 5400 rpm disk (slight speedup for SSD). • Thumbnail caching is more efficient, gallery scrolling a little smoother. User Interface Improvements: • Edit Metadata: added [prev] button to recall previous geotag data. • Batch Rename Tags: A GUI replaced the text file for old/new tag names. • Brightness/Color Gradient: Set direction of gradient with a mouse drag. • Favorites menu: Alignment of icons or text to unseen grid was made easier. • Bookmarks Edit and GoTo dialogs now scroll to allow larger entry counts. • Slide Show: transitions dialog is scrollable for use with small monitors. Bug Fixes: • Report by location and date-groups: If only a country is selected, the output gallery did not include all locations within that country. • Metadata report: Failed if no prior reported items list was found. • Sort gallery by file or photo date: Avoid sorting subdirectories (leave them at the top of the list and in alphabetic order) fotoxx-18.01.1/doc/README0000744000175000017500000000407513222767271013316 0ustar micomicoInstallation of fotoxx from source tarball Building fotoxx requires the following packages: g++ Gnu C++ compiler and linker libgtk-3-dev GTK3 graphics library (GUI base) libtiff-*-dev tiff-8/16 image files libpng-*-dev png-8/16 image files liblcms-*-dev color spaces (sRGB/Adobe-RGB) libchamplain-gtk-*-dev geomapping library libclutter-gtk-*-dev graphics library libraw RAW file conversion library At run time the following packages are also needed: exiftool read/write metadata (EXIF etc.) xdg-utils LSB standard Linux utilities binutils GNU binary utilities firefox or chromium web browser The above are recent Debian names. Package naming is chaotic, so you may have to to hunt the names for other Linux flavors. Build and install fotoxx as follows: 1. Download the tar file (fotoxx-N.N.tar.gz) 2. Open a terminal window 3. $ cd Downloads # go to download file 4. $ tar -xzf fotoxx-N.N.tar.gz # unpack to ./fotoxx 5. $ cd fotoxx # go there 6. $ make # build program 7. $ sudo make install # install program Missing dependencies will cause error messages in step 6. Install these from your repository and repeat step 6. Step 7 moves all files to the following locations: /usr/bin/fotoxx binary executable /usr/share/fotoxx/ icons, translations ... /usr/share/doc/fotoxx/ README, man page ... Fotoxx is easy to use but unconventional, so please review the user guide (menu Help > User Guide) before trying fotoxx. The optional fotoxx-maps package may be installed afterwards. This is only useful if you have a poor internet connection. FOR PACKAGE BUILDERS: If $PREFIX is defined, files go there instead of /usr. If $DESTDIR is also defined, files go to $DESTDIR$PREFIX. Please, DO NOT make a separate package for the all-arch parts. fotoxx-18.01.1/doc/copyright0000744000175000017500000000213513222767271014364 0ustar micomicoFormat: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: fotoxx Upstream-Contact: Michael Cornelison Source: https://kornelix.net Files: * Copyright: Copyright 2007-2018 Michael Cornelison License: GPL-3+ License: GPL-3+ 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 3 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the complete text of the GNU General Public License can be found in "/usr/share/common-licenses/GPL-3". fotoxx-18.01.1/locales/0000755000175000017500000000000013222767271013304 5ustar micomicofotoxx-18.01.1/locales/translate-en.po0000644000175000017500000035277013222767271016257 0ustar micomico# English translations for home package. # Copyright (C) 2018 THE home's COPYRIGHT HOLDER # This file is distributed under the same license as the home package. # mico , 2011. # # THIS IS A TEMPLATE PROVIDED FOR TRANSLATIONS # msgid "" msgstr "" "Project-Id-Version: home 2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-12-25 21:38+0100\n" "PO-Revision-Date: 2011-01-01 11:29+0100\n" "Last-Translator: mico \n" "Language-Team: English\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: f.albums.cc:89 msgid "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." msgstr "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." #: f.albums.cc:91 msgid "" "Right-click left/right side of album \n" "thumbnail to insert cached images \n" "before/after the thumbnail." msgstr "" "Right-click left/right side of album \n" "thumbnail to insert cached images \n" "before/after the thumbnail." #: f.albums.cc:94 msgid "Drag album thumbnail to new position." msgstr "Drag album thumbnail to new position." #: f.albums.cc:128 f.widgets.cc:613 msgid "Manage Albums" msgstr "Manage Albums" #: f.albums.cc:140 f.albums.cc:275 msgid "Create or replace an album" msgstr "Create or replace an album" #: f.albums.cc:142 msgid "Album to view or edit" msgstr "Album to view or edit" #: f.albums.cc:144 msgid "Select images, add to cache" msgstr "Select images, add to cache" #: f.albums.cc:146 msgid "Clear image cache" msgstr "Clear image cache" #: f.albums.cc:148 f.albums.cc:170 msgid "Delete an album" msgstr "Delete an album" #: f.albums.cc:169 msgid "Choose Album" msgstr "Choose Album" #: f.albums.cc:171 #, c-format msgid "Image cache has %d images" msgstr "Image cache has %d images" #: f.albums.cc:172 msgid "cache added to empty album" msgstr "cache added to empty album" #: f.albums.cc:228 #, c-format msgid "delete %s ?" msgstr "delete %s ?" #: f.albums.cc:259 #, c-format msgid "fill from image cache (%d images)" msgstr "fill from image cache (%d images)" #: f.albums.cc:277 f.albums.cc:310 msgid "Album Name" msgstr "Album Name" #: f.albums.cc:283 msgid "make an initially empty album" msgstr "make an initially empty album" #: f.albums.cc:285 msgid "fill from current gallery" msgstr "fill from current gallery" #: f.albums.cc:324 msgid "enter an album name" msgstr "enter an album name" #: f.albums.cc:352 msgid "new album created" msgstr "new album created" #: f.albums.cc:368 msgid "gallery is empty" msgstr "gallery is empty" #: f.albums.cc:876 f.widgets.cc:614 msgid "Update Album Files" msgstr "Update Album Files" #: f.albums.cc:878 f.albums.cc:938 f.albums.cc:1007 f.albums.cc:1147 msgid "Choose Albums" msgstr "Choose Album" #: f.albums.cc:1005 f.widgets.cc:615 f.widgets.cc:762 msgid "Replace Album File" msgstr "Replace Album File" #: f.albums.cc:1009 msgid "All Albums" msgstr "All Albums" #: f.albums.cc:1012 msgid "old file" msgstr "old file" #: f.albums.cc:1016 msgid "new file" msgstr "new file" #: f.albums.cc:1020 msgid "replace old" msgstr "replace old" #: f.albums.cc:1021 msgid "add after old" msgstr "add after old" #: f.albums.cc:1081 msgid "no albums chosen" msgstr "no albums chosen" #: f.albums.cc:1162 msgid "ALL albums chosen" msgstr "ALL albums chosen" #: f.albums.cc:1346 msgid "instant" msgstr "instant" #: f.albums.cc:1347 msgid "fade-in" msgstr "fade-in" #: f.albums.cc:1348 msgid "roll-right" msgstr "roll-right" #: f.albums.cc:1349 msgid "roll-down" msgstr "roll-down" #: f.albums.cc:1350 msgid "venetian" msgstr "venetian" #: f.albums.cc:1351 msgid "grate" msgstr "grate" #: f.albums.cc:1352 msgid "rectangle" msgstr "rectangle" #: f.albums.cc:1353 msgid "implode" msgstr "implode" #: f.albums.cc:1354 msgid "explode" msgstr "explode" #: f.albums.cc:1355 msgid "radar" msgstr "radar" #: f.albums.cc:1356 msgid "spiral" msgstr "spiral" #: f.albums.cc:1357 msgid "Japan-fan" msgstr "Japan-fan" #: f.albums.cc:1358 msgid "jaws" msgstr "jaws" #: f.albums.cc:1359 msgid "ellipse" msgstr "ellipse" #: f.albums.cc:1360 msgid "raindrops" msgstr "raindrops" #: f.albums.cc:1361 msgid "doubledoor" msgstr "doubledoor" #: f.albums.cc:1362 msgid "rotate" msgstr "rotate" #: f.albums.cc:1363 msgid "fallover" msgstr "fallover" #: f.albums.cc:1364 msgid "spheroid" msgstr "sphereoid" #: f.albums.cc:1365 msgid "turn-page" msgstr "turn-page" #: f.albums.cc:1366 msgid "french-door" msgstr "french-door" #: f.albums.cc:1367 msgid "turn-cube" msgstr "turn-cube" #: f.albums.cc:1368 msgid "windmill" msgstr "windmill" #: f.albums.cc:1369 msgid "pixelize" msgstr "pixelize" #: f.albums.cc:1370 msgid "twist" msgstr "twist" #: f.albums.cc:1372 msgid "squish" msgstr "squish" #: f.albums.cc:1399 f.widgets.cc:616 msgid "Slide Show" msgstr "Slide Show" #: f.albums.cc:1405 msgid "use gallery" msgstr "use gallery" #: f.albums.cc:1411 msgid "Clip Limit" msgstr "Clip Limit" #: f.albums.cc:1415 msgid "Music File" msgstr "Music File" #: f.albums.cc:1420 msgid "Full Screen" msgstr "Full Screen" #: f.albums.cc:1421 msgid "Auto-replay" msgstr "Auto-replay" #: f.albums.cc:1424 msgid "Customize:" msgstr "Customize:" #: f.albums.cc:1425 msgid "transitions" msgstr "transitions" #: f.albums.cc:1426 msgid "image files" msgstr "image files" #: f.albums.cc:1427 msgid "KB controls" msgstr "KB controls" #: f.albums.cc:1441 f.albums.cc:1509 f.albums.cc:1732 f.albums.cc:2007 #: f.albums.cc:2292 f.albums.cc:2309 f.albums.cc:2526 msgid "invalid album" msgstr "invalid album" #: f.albums.cc:1540 msgid "open album" msgstr "open album" #: f.albums.cc:1556 msgid "Select music file" msgstr "Select music file" #: f.albums.cc:1587 #, c-format msgid "%d images" msgstr "%d images" #: f.albums.cc:1614 msgid "arrow keys show previous or next image instantly" msgstr "arrow keys show previous or next image instantly" #: f.albums.cc:1632 msgid "Keyboard Preferences" msgstr "Keyboard Preferences" #: f.albums.cc:1635 msgid "blank or unblank window" msgstr "blank or unblank window" #: f.albums.cc:1638 msgid "show next image, with transition" msgstr "show next image, with transition" #: f.albums.cc:1641 msgid "pause or resume slide show" msgstr "pause or resume slide show" #: f.albums.cc:1644 msgid "magnify image (loupe tool)" msgstr "magnify image (loupe tool)" #: f.albums.cc:1711 msgid "random sequence" msgstr "random sequence" #: f.albums.cc:1736 msgid "Transition Preferences" msgstr "Transition Preferences" #: f.albums.cc:1738 msgid "Transitions File" msgstr "Transitions File" #: f.albums.cc:1756 f.albums.cc:1760 msgid "transition" msgstr "transition" #: f.albums.cc:1757 f.albums.cc:1761 msgid "use" msgstr "use" #: f.albums.cc:1758 f.albums.cc:1762 msgid "slow" msgstr "slow" #: f.albums.cc:1759 f.albums.cc:1763 msgid "pref" msgstr "pref" #: f.albums.cc:1862 f.albums.cc:1901 msgid "invalid file" msgstr "invalid file" #: f.albums.cc:1954 f.albums.cc:2511 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "file format error: \n" " %s" #: f.albums.cc:2013 msgid "Image Preferences" msgstr "Image Preferences" #: f.albums.cc:2017 f.edit.cc:1405 f.file.cc:1557 f.file.cc:1863 f.meta.cc:820 #: f.meta.cc:1588 f.meta.cc:1780 msgid "Image File:" msgstr "Image File:" #: f.albums.cc:2021 msgid "Play tone when image shows" msgstr "Play tone when image shows" #: f.albums.cc:2024 msgid "Show image caption" msgstr "Show image caption" #: f.albums.cc:2029 msgid "Show image comments" msgstr "Show image comments" #: f.albums.cc:2034 msgid "Wait before zoom" msgstr "Wait before zoom" #: f.albums.cc:2039 msgid "Zoom type:" msgstr "Zoom type:" #: f.albums.cc:2041 msgid "zoom-in" msgstr "zoom-in" #: f.albums.cc:2042 msgid "zoom-out" msgstr "zoom-out" #: f.albums.cc:2045 msgid "Zoom size (x)" msgstr "Zoom size (x)" #: f.albums.cc:2048 msgid "Steps" msgstr "Steps" #: f.albums.cc:2052 msgid "Zoom Center" msgstr "Zoom Center" #: f.albums.cc:2056 msgid "Wait after zoom" msgstr "Wait after zoom" #: f.albums.cc:2061 msgid "Transition to next image" msgstr "Transition to next image" #: f.albums.cc:2064 f.albums.cc:2167 msgid "next" msgstr "next" #: f.albums.cc:2157 msgid "click on thumbnail to set zoom center" msgstr "click on thumbnail to set zoom center" #: f.area.cc:81 msgid "Select Area for Edits" msgstr "Select Area for Edits" #: f.area.cc:82 f.area.cc:1855 f.edit.cc:9094 msgid "Press F1 for help" msgstr "Press F1 for help" #: f.area.cc:91 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Select Area not supported \n" "by this edit function" #: f.area.cc:120 msgid "select rectangle" msgstr "select rectangle" #: f.area.cc:121 msgid "select ellipse" msgstr "select ellipse" #: f.area.cc:124 msgid "freehand draw" msgstr "freehand draw" #: f.area.cc:125 msgid "follow edge" msgstr "follow edge" #: f.area.cc:126 msgid "draw/replace" msgstr "draw/replace" #: f.area.cc:129 msgid "select area within mouse circle" msgstr "select area within mouse circle" #: f.area.cc:132 msgid "select one matching color within mouse circle:" msgstr "select one matching color within mouse circle:" #: f.area.cc:134 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "first select the checkbox, then \n" "shift+click on image to set the color" #: f.area.cc:138 msgid "select all matching colors within mouse circle" msgstr "select all matching colors within mouse circle" #: f.area.cc:144 msgid "match level %" msgstr "match level %" #: f.area.cc:147 msgid "search range" msgstr "search range" #: f.area.cc:151 msgid "Area Edge Blend Width" msgstr "Area Edge Blend Width" #: f.area.cc:154 msgid "Edge Creep" msgstr "Edge Creep" #: f.area.cc:159 msgid "Line Color:" msgstr "Line Color:" #: f.area.cc:179 f.area.cc:180 msgid "area edits fade away within edge distance" msgstr "area edits fade away within edge distance" #: f.area.cc:351 f.area.cc:517 #, c-format msgid "exceed %d edits" msgstr "exceed %d edits" #: f.area.cc:1333 msgid "" "Click one time inside each enclosed area. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Click one time inside each enclosed area. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." #: f.area.cc:1360 msgid "finish area" msgstr "finish area" #: f.area.cc:1389 f.area.cc:1534 #, c-format msgid "found %d pixels" msgstr "found %d pixels" #: f.area.cc:1639 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." #: f.area.cc:1658 msgid "find outline gap" msgstr "find outline gap" #: f.area.cc:1748 msgid "cannot find area outline" msgstr "cannot find area outline" #: f.area.cc:1854 f.widgets.cc:458 msgid "Select Hairy" msgstr "Select Hairy" #: f.area.cc:1862 msgid "select the area first" msgstr "select the area first" #: f.area.cc:1867 f.area.cc:2489 f.area.cc:2507 f.area.cc:2527 f.area.cc:2851 #: f.area.cc:2971 msgid "the area is not finished" msgstr "the area is not finished" #: f.area.cc:1940 msgid "select" msgstr "select" #: f.area.cc:1946 msgid "deselect" msgstr "deselect" #: f.area.cc:2616 msgid "Edge calculation in progress" msgstr "Edge calculation in progress" #: f.area.cc:2625 msgid "Area Edge Calc" msgstr "Area Edge Calc" #: f.area.cc:2957 f.area.cc:3029 f.widgets.cc:465 msgid "Copy Area" msgstr "Copy Area" #: f.area.cc:2960 f.widgets.cc:468 msgid "Save Area File" msgstr "Save Area File" #: f.area.cc:3035 msgid "save area as a PNG file" msgstr "save area as a PNG file" #: f.area.cc:3159 msgid "position with mouse click/drag" msgstr "position with mouse click/drag" #: f.area.cc:3219 msgid "Paste Image" msgstr "Paste Image" #: f.area.cc:3224 f.process.cc:1311 msgid "resize" msgstr "resize" #: f.combine.cc:169 f.combine.cc:815 f.combine.cc:1416 f.combine.cc:2039 msgid "Select 2 to 9 files" msgstr "Select 2 to 9 files" #: f.combine.cc:191 f.combine.cc:837 f.combine.cc:1438 f.combine.cc:2061 msgid "Images are not all the same size" msgstr "Images are not all the same size" #: f.combine.cc:550 msgid "Adjust Image Contributions" msgstr "Adjust Image Contributions" #: f.combine.cc:554 msgid "dark pixels" msgstr "dark pixels" #: f.combine.cc:556 msgid "light pixels" msgstr "light pixels" #: f.combine.cc:558 msgid "file:" msgstr "file:" #: f.combine.cc:1018 msgid "Paint and Warp Image" msgstr "Paint and Warp Image" #: f.combine.cc:1026 f.edit.cc:5686 msgid "paint" msgstr "paint" #: f.combine.cc:1027 msgid "warp" msgstr "warp" #: f.combine.cc:1623 msgid "Select and Paint Image" msgstr "Select and Paint Image" #: f.combine.cc:1631 msgid "Transient Objects" msgstr "Transient Objects" #: f.combine.cc:2232 msgid "Adjust Pixel Composition" msgstr "Adjust Pixel Composition" #: f.combine.cc:2234 msgid "use average" msgstr "use average" #: f.combine.cc:2235 msgid "use median" msgstr "use median" #: f.combine.cc:2237 msgid "omit low pixel" msgstr "omit low pixel" #: f.combine.cc:2238 msgid "omit high pixel" msgstr "omit high pixel" #: f.combine.cc:2502 f.combine.cc:3653 msgid "Select 2 to 4 files" msgstr "Select 2 to 4 files" #: f.combine.cc:2573 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." #: f.combine.cc:2575 f.combine.cc:3726 msgid "no curve (scanned image)" msgstr "no curve (scanned image)" #: f.combine.cc:2576 f.combine.cc:3727 msgid "Search for lens mm" msgstr "Search for lens mm" #: f.combine.cc:2577 f.combine.cc:3728 msgid "Save lens mm → image EXIF" msgstr "Save lens mm → image EXIF" #: f.combine.cc:2639 f.combine.cc:3790 msgid "Pre-align Images" msgstr "Pre-align Images" #: f.combine.cc:2643 f.combine.cc:3794 msgid "lens mm" msgstr "lens mm" #: f.combine.cc:2648 f.combine.cc:3799 msgid "no auto warp" msgstr "no auto warp" #: f.combine.cc:2650 f.combine.cc:3801 msgid "manual align" msgstr "manual align" #: f.combine.cc:2652 f.combine.cc:3803 f.widgets.cc:473 f.widgets.cc:768 msgid "Resize" msgstr "Resize" #: f.combine.cc:2653 f.combine.cc:3804 msgid "resize window" msgstr "resize window" #: f.combine.cc:2661 f.combine.cc:3812 msgid "do not warp images during auto-alignment" msgstr "do not warp images during auto-alignment" #: f.combine.cc:2707 f.combine.cc:3858 msgid "use two images only" msgstr "use two images only" #: f.combine.cc:2735 f.combine.cc:2939 f.combine.cc:3127 f.combine.cc:3884 #: f.combine.cc:4088 f.combine.cc:4276 msgid "Too little overlap, cannot align" msgstr "Too little overlap, cannot align" #: f.combine.cc:3205 f.combine.cc:4354 msgid "Match Brightness and Color" msgstr "Match Brightness and Color" #: f.combine.cc:3251 msgid "Select image" msgstr "Select image" #: f.combine.cc:3266 f.combine.cc:4415 msgid "auto color" msgstr "auto color" #: f.combine.cc:3267 f.combine.cc:4416 msgid "file color" msgstr "file color" #: f.combine.cc:3273 f.combine.cc:4422 msgid "mouse warp" msgstr "mouse warp" #: f.combine.cc:3275 f.combine.cc:4424 msgid "flatten image" msgstr "flatten image" #: f.combine.cc:3724 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." #: f.combine.cc:4684 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) not installed" #: f.combine.cc:4693 msgid "Select at least 2 files" msgstr "Select at least 2 files" #: f.edit.cc:98 msgid "Trim: drag middle to move, drag corners to resize" msgstr "Trim: drag middle to move, drag corners to resize" #: f.edit.cc:99 msgid "Minor rotate: drag right edge with mouse" msgstr "Minor rotate: drag right edge with mouse" #: f.edit.cc:180 f.widgets.cc:471 f.widgets.cc:767 msgid "Trim/Rotate" msgstr "Trim/Rotate" #: f.edit.cc:192 f.edit.cc:1306 msgid "ratio" msgstr "ratio" #: f.edit.cc:196 msgid "trim size:" msgstr "trim size:" #: f.edit.cc:201 msgid "Lock Ratio" msgstr "Lock Ratio" #: f.edit.cc:203 msgid "maximize trim box" msgstr "maximize trim box" #: f.edit.cc:204 msgid "use previous size" msgstr "use previous size" #: f.edit.cc:205 msgid "invert width/height" msgstr "invert width/height" #: f.edit.cc:206 msgid "trim transparent edges" msgstr "trim transparent edges" #: f.edit.cc:207 msgid "lock width/height ratio" msgstr "lock width/height ratio" #: f.edit.cc:212 msgid "Customize" msgstr "Customize" #: f.edit.cc:218 msgid "Level" msgstr "Level" #: f.edit.cc:219 msgid "use EXIF data if available" msgstr "use EXIF data if available" #: f.edit.cc:222 msgid "Rotate: degrees" msgstr "Rotate: degrees" #: f.edit.cc:1302 msgid "Trim Buttons" msgstr "Trim Buttons" #: f.edit.cc:1305 msgid "label" msgstr "label" #: f.edit.cc:1402 msgid "Upright Image" msgstr "Upright Image" #: f.edit.cc:1408 f.process.cc:168 f.process.cc:755 f.widgets.cc:472 #: f.widgets.cc:769 msgid "Upright" msgstr "Upright" #: f.edit.cc:1461 msgid "rotation unknown" msgstr "rotation unknown" #: f.edit.cc:1548 msgid "Resize Image" msgstr "Resize Image" #: f.edit.cc:1573 msgid "W/H Ratio:" msgstr "W/H Ratio:" #: f.edit.cc:1575 msgid "Lock" msgstr "Lock" #: f.edit.cc:1577 msgid "use previous settings" msgstr "use previous settings" #: f.edit.cc:2298 f.widgets.cc:476 f.widgets.cc:772 msgid "Retouch Combo" msgstr "Retouch Combo" #: f.edit.cc:2319 msgid "Amplifier" msgstr "Amplifier" #: f.edit.cc:2320 fotoxx.h:1188 msgid "Brightness" msgstr "Brightness" #: f.edit.cc:2321 f.effects.cc:3681 fotoxx.h:1198 msgid "Contrast" msgstr "Contrast" #: f.edit.cc:2322 msgid "Low Color" msgstr "Low Color" #: f.edit.cc:2323 msgid "Warmer" msgstr "Warmer" #: f.edit.cc:2324 f.effects.cc:1332 msgid "Dark Areas" msgstr "Dark Areas" #: f.edit.cc:2333 msgid "Max." msgstr "Max." #: f.edit.cc:2334 f.edit.cc:2335 f.edit.cc:2336 msgid "High" msgstr "High" #: f.edit.cc:2337 msgid "Cooler" msgstr "Cooler" #: f.edit.cc:2338 msgid "Bright" msgstr "Bright" #: f.edit.cc:2341 f.tools.cc:2134 msgid "Brightness Distribution" msgstr "Brightness Distribution" #: f.edit.cc:2344 msgid "Click for white balance" msgstr "Click for white balance" #: f.edit.cc:2345 msgid "Click for black level" msgstr "Click for black level" #: f.edit.cc:2348 msgid "Settings File" msgstr "Settings File" #: f.edit.cc:2352 msgid "recall previous settings used" msgstr "recall previous settings used" #: f.edit.cc:3004 msgid "Adjust Brightness Distribution" msgstr "Adjust Brightness Distribution" #: f.edit.cc:3043 msgid "Low Cutoff" msgstr "Low Cutoff" #: f.edit.cc:3044 msgid "High Cutoff" msgstr "High Cutoff" #: f.edit.cc:3045 msgid "Low Flatten" msgstr "Low Flatten" #: f.edit.cc:3046 msgid "Mid Flatten" msgstr "Mid Flatten" #: f.edit.cc:3047 msgid "High Flatten" msgstr "High Flatten" #: f.edit.cc:3048 msgid "Low Stretch" msgstr "Low Stretch" #: f.edit.cc:3049 msgid "Mid Stretch" msgstr "Mid Stretch" #: f.edit.cc:3050 msgid "High Stretch" msgstr "High Stretch" #: f.edit.cc:3383 msgid "Magnify Gradients" msgstr "Magnify Gradients" #: f.edit.cc:3415 msgid "low" msgstr "low" #: f.edit.cc:3417 msgid "high" msgstr "high" #: f.edit.cc:3420 msgid "Amplify" msgstr "Amplify" #: f.edit.cc:3813 msgid "Flatten Brightness" msgstr "Flatten Brightness" #: f.edit.cc:3864 msgid "Zones" msgstr "Zones" #: f.edit.cc:3871 msgid "Deband Dark" msgstr "Deband Dark" #: f.edit.cc:3874 msgid "Deband Bright" msgstr "Deband Bright" #: f.edit.cc:4386 msgid "Dark Point" msgstr "Dark Point" #: f.edit.cc:4387 msgid "Bright Point" msgstr "Bright Point" #: f.edit.cc:4388 msgid "Multiplyer" msgstr "Multiplyer" #: f.edit.cc:4412 msgid "brightness rescale" msgstr "brightness rescale" #: f.edit.cc:4415 msgid "click bright point" msgstr "click bright point" #: f.edit.cc:4416 msgid "click dark point" msgstr "click dark point" #: f.edit.cc:4427 msgid "blend" msgstr "blend" #: f.edit.cc:4430 msgid "reduce bright" msgstr "reduce bright" #: f.edit.cc:5468 f.widgets.cc:481 msgid "Mirror Image" msgstr "Mirror Image" #: f.edit.cc:5472 f.warp.cc:3300 msgid "horizontal" msgstr "horizontal" #: f.edit.cc:5473 f.warp.cc:3304 msgid "vertical" msgstr "vertical" #: f.edit.cc:5620 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" #: f.edit.cc:5656 msgid "Paint on Image" msgstr "Paint on Image" #: f.edit.cc:5662 msgid "paint color" msgstr "paint color" #: f.edit.cc:5673 f.edit.cc:6640 msgid "brush size" msgstr "brush size" #: f.edit.cc:5674 f.edit.cc:6641 msgid "opacity center" msgstr "opacity center" #: f.edit.cc:5675 f.edit.cc:6642 msgid "opacity edge" msgstr "opacity edge" #: f.edit.cc:5687 msgid "erase" msgstr "erase" #: f.edit.cc:5688 msgid "include transparent areas" msgstr "include transparent areas" #: f.edit.cc:5695 msgid "drag image" msgstr "drag image" #: f.edit.cc:5697 msgid "zoom image" msgstr "zoom image" #: f.edit.cc:6221 msgid "Color Palette" msgstr "Color Palette" #: f.edit.cc:6225 msgid "click on image to choose color" msgstr "click on image to choose color" #: f.edit.cc:6415 f.repair.cc:3799 msgid "Color Hue" msgstr "Color Hue" #: f.edit.cc:6416 f.repair.cc:3768 f.repair.cc:3800 msgid "Saturation" msgstr "Saturation" #: f.edit.cc:6417 f.repair.cc:3769 f.repair.cc:3801 msgid "Lightness" msgstr "Lightness" #: f.edit.cc:6599 msgid "" "shift + left click: pick image position to copy \n" "left drag: copy image to mouse position \n" "right drag: restore image" msgstr "" "shift + left click: pick image position to copy \n" "left drag: copy image to mouse position \n" "right drag: restore image" #: f.edit.cc:6630 f.widgets.cc:483 msgid "Clone Image" msgstr "Clone Image" #: f.edit.cc:6649 msgid "paint over transparent areas" msgstr "paint over transparent areas" #: f.edit.cc:7098 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "left drag: blend image \n" "right drag: restore image" #: f.edit.cc:7126 f.widgets.cc:484 msgid "Blend Image" msgstr "Blend Image" #: f.edit.cc:7134 f.repair.cc:7663 msgid "strength center" msgstr "strength center" #: f.edit.cc:7135 f.repair.cc:7664 msgid "strength edge" msgstr "strength edge" #: f.edit.cc:7359 msgid "+Version" msgstr "+Version" #: f.edit.cc:7382 f.widgets.cc:275 msgid "Add Text to Image" msgstr "Add Text to Image" #: f.edit.cc:7383 msgid "Enter text, click/drag on image, right click to remove" msgstr "Enter text, click/drag on image, right click to remove" #: f.edit.cc:7432 f.edit.cc:8310 msgid "Use settings file" msgstr "Use settings file" #: f.edit.cc:7437 f.mashup.cc:2455 msgid "Text" msgstr "Text" #: f.edit.cc:7442 msgid "Use metadata key" msgstr "Use metadata key" #: f.edit.cc:7461 f.mashup.cc:2473 msgid "text" msgstr "text" #: f.edit.cc:7462 f.edit.cc:8336 f.mashup.cc:2474 f.mashup.cc:2812 msgid "backing" msgstr "backing" #: f.edit.cc:7463 f.edit.cc:8337 f.mashup.cc:2475 f.mashup.cc:2813 msgid "outline" msgstr "outline" #: f.edit.cc:7464 f.edit.cc:8338 f.mashup.cc:2476 f.mashup.cc:2814 msgid "shadow" msgstr "shadow" #: f.edit.cc:7490 msgid "save to current file" msgstr "save to current file" #: f.edit.cc:7491 f.file.cc:2227 msgid "save as new file version" msgstr "save as new file version" #: f.edit.cc:7492 msgid "" "save to current file \n" "open next file with same text" msgstr "" "save to current file \n" "open next file with same text" #: f.edit.cc:7652 f.mashup.cc:2580 f.tools.cc:1464 msgid "select font" msgstr "select font" #: f.edit.cc:7928 f.edit.cc:8766 msgid "text file is defective" msgstr "text file is defective" #: f.edit.cc:8268 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" #: f.edit.cc:8302 f.widgets.cc:276 msgid "Add lines or arrows to an image" msgstr "Add lines or arrows to an image" #: f.edit.cc:8315 f.mashup.cc:2791 msgid "Line length" msgstr "Line length" #: f.edit.cc:8322 f.mashup.cc:2798 msgid "Arrow head" msgstr "Arrow head" #: f.edit.cc:8335 f.mashup.cc:2811 msgid "line" msgstr "line" #: f.edit.cc:8364 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "fix line/arrow in layout \n" " start new line/arrow" #: f.edit.cc:9093 f.widgets.cc:487 msgid "Paint Edits" msgstr "Paint Edits" #: f.edit.cc:9100 f.edit.cc:9323 fotoxx-18.01.cc:3426 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Select area cannot be kept.\n" "Continue?" #: f.edit.cc:9108 f.edit.cc:9331 f.tools.cc:2666 msgid "Edit function must be active" msgstr "Edit function must be active" #: f.edit.cc:9113 msgid "Cannot use Paint Edits" msgstr "Cannot use Paint Edits" #: f.edit.cc:9140 msgid "power: center" msgstr "power: center" #: f.edit.cc:9145 msgid "reset area" msgstr "reset area" #: f.edit.cc:9316 f.widgets.cc:488 msgid "Leverage Edits" msgstr "Leverage Edits" #: f.edit.cc:9317 msgid "Edit Function Amplifier" msgstr "Edit Function Amplifier" #: f.edit.cc:9336 msgid "Cannot use Leverage Edits" msgstr "Cannot use Leverage Edits" #: f.edit.cc:9365 msgid "minimum" msgstr "minimum" #: f.edit.cc:9367 msgid "maximum" msgstr "maximum" #: f.edit.cc:9642 f.edit.cc:9686 msgid "Edit Plugins" msgstr "Edit Plugins" #: f.edit.cc:9643 msgid "Edit plugins menu" msgstr "Edit plugins menu" #: f.edit.cc:9651 msgid "Run as Fotoxx edit function" msgstr "Run as Fotoxx edit function" #: f.edit.cc:9688 msgid "menu name" msgstr "menu name" #: f.edit.cc:9691 msgid "command" msgstr "command" #: f.edit.cc:9888 msgid "Plugin working ..." msgstr "Plugin working ..." #: f.edit.cc:9897 msgid "plugin failed" msgstr "plugin failed" #: f.effects.cc:79 msgid "Convert to Sketch" msgstr "Convert to Sketch" #: f.effects.cc:157 msgid "Clip Level" msgstr "Clip Level" #: f.effects.cc:161 msgid "Algorithm" msgstr "Algorithm" #: f.effects.cc:166 msgid "Foreground" msgstr "Foreground" #: f.effects.cc:169 f.tools.cc:1275 msgid "Background" msgstr "Background" #: f.effects.cc:1054 f.widgets.cc:531 msgid "Line Drawing" msgstr "Line Drawing" #: f.effects.cc:1067 msgid "black/white" msgstr "black/white" #: f.effects.cc:1326 f.widgets.cc:532 msgid "Color Drawing" msgstr "Color Drawing" #: f.effects.cc:1333 msgid "Bright Areas" msgstr "Bright Areas" #: f.effects.cc:1575 f.widgets.cc:533 msgid "Graduated Blur" msgstr "Graduated Blur" #: f.effects.cc:1579 msgid "Contrast Limit" msgstr "Contrast Limit" #: f.effects.cc:1582 f.repair.cc:929 msgid "Blur Radius" msgstr "Blur Radius" #: f.effects.cc:1799 msgid "Simulate Embossing" msgstr "Simulate Embossing" #: f.effects.cc:1820 msgid "depth" msgstr "depth" #: f.effects.cc:2029 msgid "Simulate Tiles" msgstr "Simulate Tiles" #: f.effects.cc:2033 msgid "tile size" msgstr "tile size" #: f.effects.cc:2036 msgid "tile gap" msgstr "tile gap" #: f.effects.cc:2039 msgid "3D depth" msgstr "3D depth" #: f.effects.cc:2258 msgid "Convert Image to Dots" msgstr "Convert Image to Dots" #: f.effects.cc:2262 msgid "dot size" msgstr "dot size" #: f.effects.cc:2484 msgid "Simulate Painting" msgstr "Simulate Painting" #: f.effects.cc:2502 msgid "color depth" msgstr "color depth" #: f.effects.cc:2506 msgid "patch area goal" msgstr "patch area goal" #: f.effects.cc:2510 msgid "req. color match" msgstr "req. color match" #: f.effects.cc:2514 msgid "borders" msgstr "borders" #: f.effects.cc:3081 f.widgets.cc:538 msgid "Vignette" msgstr "Vignette" #: f.effects.cc:3430 msgid "Add Texture" msgstr "Add Texture" #: f.effects.cc:3646 msgid "Background Pattern" msgstr "Background Pattern" #: f.effects.cc:3650 msgid "Pattern File:" msgstr "Pattern File:" #: f.effects.cc:3655 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3658 msgid "Geometry" msgstr "Geometry" #: f.effects.cc:3659 msgid "Calculate" msgstr "Calculate" #: f.effects.cc:3662 f.widgets.cc:540 msgid "Pattern" msgstr "Pattern" #: f.effects.cc:3670 msgid "Overlap" msgstr "Overlap" #: f.effects.cc:3678 msgid "Opacity" msgstr "Opacity" #: f.effects.cc:4124 msgid "Create Mosaic" msgstr "Create Mosaic" #: f.effects.cc:4170 msgid "Tile" msgstr "Tile" #: f.effects.cc:4178 f.widgets.cc:535 msgid "Tiles" msgstr "Tiles" #: f.effects.cc:4184 msgid "Tile blending" msgstr "Tile blending" #: f.effects.cc:4265 #, c-format msgid "exceeded max. tiles: %d" msgstr "exceeded max. tiles: %d" #: f.effects.cc:4272 #, c-format msgid "only %d tile images found" msgstr "only %d tile images found" #: f.effects.cc:4756 f.widgets.cc:542 msgid "Custom Kernel" msgstr "Custom Kernel" #: f.effects.cc:4760 msgid "Kernel size" msgstr "Kernel size" #: f.effects.cc:4777 msgid "multiply" msgstr "multiply" #: f.effects.cc:4780 msgid "add" msgstr "add" #: f.effects.cc:4784 msgid "Data file" msgstr "Data file" #: f.effects.cc:4804 fotoxx-18.01.cc:3690 msgid "Load settings from file" msgstr "Load settings from file" #: f.effects.cc:5046 msgid "Pull image using the mouse." msgstr "Pull image using the mouse." #: f.effects.cc:5067 f.widgets.cc:543 msgid "Directed Blur" msgstr "Directed Blur" #: f.effects.cc:5071 msgid "blur span" msgstr "blur span" #: f.effects.cc:5074 msgid "intensity" msgstr "intensity" #: f.effects.cc:5276 f.widgets.cc:544 msgid "Blur Background" msgstr "Blur Background" #: f.effects.cc:5280 msgid "constant blur" msgstr "constant blur" #: f.effects.cc:5283 msgid "increase blur with distance" msgstr "increase blur with distance" #: f.effects.cc:5285 msgid "min. blur radius" msgstr "min. blur radius" #: f.effects.cc:5288 msgid "max. blur radius" msgstr "max. blur radius" #: f.effects.cc:5322 f.warp.cc:819 f.warp.cc:1252 msgid "no active Select Area" msgstr "no active Select Area" #: f.effects.cc:5515 f.widgets.cc:545 msgid "Alien Colors" msgstr "Alien Colors" #: f.effects.cc:5518 msgid "blocksize" msgstr "blocksize" #: f.effects.cc:5521 f.warp.cc:3298 msgid "amplitude" msgstr "amplitude" #: f.file.cc:190 msgid "" "Use EXIF photo date or \n" " file modification date?" msgstr "" "Use EXIF photo date or \n" " file modification date?" #: f.file.cc:204 f.widgets.cc:626 msgid "File" msgstr "File" #: f.file.cc:378 f.process.cc:1345 msgid "Raw Therapee not installed" msgstr "Raw Therapee not installed" #: f.file.cc:396 f.widgets.cc:432 f.widgets.cc:777 msgid "Open RAW (Raw Therapee)" msgstr "Open RAW (Raw Therapee" #: f.file.cc:401 msgid "RAW type not registered in User Settings" msgstr "RAW type not registered in User Settings" #: f.file.cc:416 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee produced no tif file" #: f.file.cc:802 f.widgets.cc:429 msgid "Open Image File" msgstr "Open Image File" #: f.file.cc:821 f.process.cc:597 msgid "unknown file type" msgstr "unknown file type" #: f.file.cc:999 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Start of gallery, preceding gallery: %s" #: f.file.cc:1000 #, c-format msgid "End of gallery, following gallery: %s" msgstr "End of gallery, following gallery: %s" #: f.file.cc:1108 msgid "Create Blank Image" msgstr "Create Blank Image" #: f.file.cc:1110 msgid "file name" msgstr "file name" #: f.file.cc:1144 msgid "supply a file name" msgstr "supply a file name" #: f.file.cc:1314 f.widgets.cc:436 msgid "Rename Image File" msgstr "Rename Image File" #: f.file.cc:1321 msgid "Old Name" msgstr "Old Name" #: f.file.cc:1322 f.process.cc:130 msgid "New Name" msgstr "New Name" #: f.file.cc:1331 msgid "previous name" msgstr "previous name" #: f.file.cc:1332 msgid "Add 1" msgstr "Add 1" #: f.file.cc:1335 f.file.cc:1571 f.file.cc:1866 msgid "keep this dialog open" msgstr "keep this dialog open" #: f.file.cc:1520 msgid "Copy or Move Image File" msgstr "Copy or Move Image File" #: f.file.cc:1562 msgid "New Location:" msgstr "New Location:" #: f.file.cc:1567 msgid "copy (duplicate file)" msgstr "copy (duplicate file)" #: f.file.cc:1568 msgid "move (remove original)" msgstr "move (remove original)" #: f.file.cc:1609 f.process.cc:573 f.process.cc:1503 f.process.cc:2625 msgid "Select directory" msgstr "Select directory" #: f.file.cc:1647 msgid "new location is not a directory" msgstr "new location is not a directory" #: f.file.cc:1688 f.file.cc:1942 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "delete failed: \n" " %s" #: f.file.cc:1827 f.widgets.cc:442 msgid "Delete/Trash Image File" msgstr "Delete/Trash Image File" #: f.file.cc:1896 msgid "GTK g_file_trash() function failed" msgstr "GTK g_file_trash() function failed" #: f.file.cc:1915 msgid "not a known image file" msgstr "not a known image file" #: f.file.cc:1921 msgid "Delete read-only file?" msgstr "Delete read-only file?" #: f.file.cc:1923 msgid "Trash read-only file?" msgstr "Trash read-only file?" #: f.file.cc:1971 msgid "no more images" msgstr "no more images" #: f.file.cc:2209 msgid "Save Image File" msgstr "Save Image File" #: f.file.cc:2219 msgid "new version" msgstr "new version" #: f.file.cc:2220 msgid "new file ..." msgstr "new file ..." #: f.file.cc:2221 msgid "replace file" msgstr "replace file" #: f.file.cc:2228 f.file.cc:2800 msgid "save as new file name or type" msgstr "save as new file name or type" #: f.file.cc:2230 msgid "replace old file (OVERWRITE)" msgstr "replace old file (OVERWRITE)" #: f.file.cc:2267 f.file.cc:2558 msgid "cannot save as RAW type" msgstr "cannot save as RAW type" #: f.file.cc:2375 msgid "too many file versions: 99" msgstr "too many file versions: 99" #: f.file.cc:2552 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "Transparency map will be lost.\n" "save to PNG file to retain." #: f.file.cc:2637 msgid "save anyway" msgstr "save anyway" #: f.file.cc:2706 msgid "Unable to copy EXIF/IPTC data" msgstr "Unable to copy EXIF/IPTC data" #: f.file.cc:2818 f.file.cc:2821 msgid "make current" msgstr "make current" #: f.file.cc:2819 msgid "(new file becomes current file)" msgstr "(new file becomes current file)" #: f.file.cc:2932 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Overwrite file? \n" " %s" #: f.file.cc:3127 f.widgets.cc:599 msgid "Quick Start" msgstr "Quick Start" #: f.file.cc:3130 f.widgets.cc:600 msgid "User Guide" msgstr "User Guide" #: f.file.cc:3133 f.widgets.cc:601 msgid "User Guide Changes" msgstr "User Guide Changes" #: f.file.cc:3136 f.widgets.cc:602 msgid "README" msgstr "README" #: f.file.cc:3139 f.widgets.cc:603 msgid "Change Log" msgstr "Change Log" #: f.file.cc:3142 f.widgets.cc:604 msgid "Log File" msgstr "Log File" #: f.file.cc:3145 f.widgets.cc:605 msgid "Translations" msgstr "Translations" #: f.file.cc:3148 f.widgets.cc:606 msgid "Home Page" msgstr "Home Page" #: f.file.cc:3151 f.widgets.cc:607 msgid "About" msgstr "About" #: f.file.cc:3157 f.widgets.cc:639 f.widgets.cc:663 f.widgets.cc:676 #: f.widgets.cc:690 fotoxx.h:1223 msgid "Help" msgstr "Help" #: f.file.cc:3362 f.file.cc:3367 f.file.cc:3437 #, c-format msgid "file type not supported: %s" msgstr "file type not supported: %s" #: f.gallery.cc:1221 msgid "GoTo" msgstr "GoTo" #: f.gallery.cc:1227 f.widgets.cc:655 msgid "Sort" msgstr "Sort" #: f.gallery.cc:1233 f.widgets.cc:653 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:1243 f.widgets.cc:654 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:1255 msgid "Row Up" msgstr "Row Up" #: f.gallery.cc:1263 msgid "Row Down" msgstr "Row Down" #: f.gallery.cc:1271 f.widgets.cc:658 msgid "Page Up" msgstr "Page Up" #: f.gallery.cc:1279 f.widgets.cc:659 msgid "Page Down" msgstr "Page Down" #: f.gallery.cc:1287 f.widgets.cc:656 msgid "First" msgstr "First" #: f.gallery.cc:1294 f.widgets.cc:657 msgid "Last" msgstr "Last" #: f.gallery.cc:1425 f.gallery.cc:1445 msgid "recent images" msgstr "recent images" #: f.gallery.cc:1426 f.gallery.cc:1450 msgid "newest images" msgstr "newest images" #: f.gallery.cc:1504 msgid "no albums found" msgstr "no albums found" #: f.gallery.cc:1541 msgid "Albums cannot be sorted" msgstr "Albums cannot be sorted" #: f.gallery.cc:1545 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" " Reset all galleries\n" " to file name ascending" #: f.gallery.cc:1564 msgid "Gallery Sort" msgstr "Gallery Sort" #: f.gallery.cc:1568 msgid "File Name" msgstr "File Name" #: f.gallery.cc:1569 msgid "File Mod Date/Time" msgstr "File Mod Date/Time" #: f.gallery.cc:1570 msgid "Photo Date/Time (EXIF)" msgstr "Photo Date/Time (EXIF" #: f.gallery.cc:1572 msgid "ascending" msgstr "ascending" #: f.gallery.cc:1573 msgid "descending" msgstr "descending" #: f.gallery.cc:2925 f.gallery.cc:2929 msgid "Image File" msgstr "Image File" #: f.gallery.cc:2927 msgid "select thumbnail" msgstr "select thumbnail" #: f.gallery.cc:3037 fotoxx.h:1276 msgid "Select Files" msgstr "Select Files" #: f.gallery.cc:3097 #, c-format msgid "exceed %d selected files" msgstr "exceed %d selected files" #: f.gallery.cc:3457 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." #: f.gallery.cc:3484 f.gallery.cc:3734 msgid "Edit Bookmarks" msgstr "Edit Bookmarks" #: f.gallery.cc:3662 msgid "unable to save bookmarks file" msgstr "unable to save bookmarks file" #: f.gallery.cc:3734 msgid "Go To Bookmark" msgstr "Go To Bookmark" #: f.mashup.cc:170 msgid "Mashup layout and background image" msgstr "Mashup layout and background image" #: f.mashup.cc:213 msgid "choose an image file" msgstr "choose an image file" #: f.mashup.cc:214 msgid "use current image file" msgstr "use current image file" #: f.mashup.cc:215 msgid "specify layout size and color" msgstr "specify layout size and color" #: f.mashup.cc:216 msgid "open a Mashup project file" msgstr "open a Mashup project file" #: f.mashup.cc:259 msgid "no current file" msgstr "no current file" #: f.mashup.cc:291 msgid "Make Layout Image" msgstr "Make Layout Image" #: f.mashup.cc:293 msgid "project name" msgstr "project name" #: f.mashup.cc:319 msgid "supply a project name" msgstr "supply a project name" #: f.mashup.cc:411 msgid "Edit Images" msgstr "Edit Images" #: f.mashup.cc:412 f.mashup.cc:2450 msgid "Edit Text" msgstr "Edit Text" #: f.mashup.cc:413 msgid "Edit Line" msgstr "Edit Line" #: f.mashup.cc:414 msgid "Rescale" msgstr "Rescale" #: f.mashup.cc:419 msgid "add or edit images" msgstr "add or edit images" #: f.mashup.cc:421 msgid "add or edit text" msgstr "add or edit text" #: f.mashup.cc:423 msgid "add or edit lines/arrows" msgstr "add or edit lines/arrows" #: f.mashup.cc:425 msgid "change project scale" msgstr "change project scale" #: f.mashup.cc:427 msgid "project complete" msgstr "project complete" #: f.mashup.cc:429 msgid "cancel project" msgstr "cancel project" #: f.mashup.cc:447 msgid "rescale project" msgstr "rescale project" #: f.mashup.cc:506 msgid "save Mashup output file" msgstr "save Mashup output file" #: f.mashup.cc:526 msgid "save Mashup project file?" msgstr "save Mashup project file?" #: f.mashup.cc:538 msgid "delete Mashup project file?" msgstr "delete Mashup project file?" #: f.mashup.cc:597 msgid "Open Project" msgstr "Open Project" #: f.mashup.cc:626 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "layout image file missing: \n" " %s" #: f.mashup.cc:683 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "overlay image file missing: \n" " %s" #: f.mashup.cc:989 msgid "project file is defective" msgstr "project file is defective" #: f.mashup.cc:1019 msgid "Save Project" msgstr "Save Project" #: f.mashup.cc:1160 msgid "layout exceeds 2 gigabytes" msgstr "layout exceeds 2 gigabytes" #: f.mashup.cc:1234 msgid "Click image to select, drag image to move." msgstr "Click image to select, drag image to move." #: f.mashup.cc:1235 msgid "Make black margins transparent" msgstr "Make black margins transparent" #: f.mashup.cc:1274 msgid "Current image:" msgstr "Current image:" #: f.mashup.cc:1278 msgid "Cycle through images:" msgstr "Cycle through images:" #: f.mashup.cc:1285 msgid "Scale" msgstr "Scale" #: f.mashup.cc:1294 msgid "Stacking Order" msgstr "Stacking Order" #: f.mashup.cc:1295 msgid "Raise" msgstr "Raise" #: f.mashup.cc:1296 msgid "Lower" msgstr "Lower" #: f.mashup.cc:1299 msgid "Base Transparency" msgstr "Base Transparency" #: f.mashup.cc:1303 msgid "Var. Transparency" msgstr "Var. Transparency" #: f.mashup.cc:1304 msgid "Paint" msgstr "Paint" #: f.mashup.cc:1307 msgid "Bend and fine-align" msgstr "Bend and fine-align" #: f.mashup.cc:1308 f.widgets.cc:634 msgid "Warp" msgstr "Warp" #: f.mashup.cc:1317 zfuncs.cc:11351 zfuncs.cc:11360 msgid "Margins" msgstr "Margins" #: f.mashup.cc:1318 msgid "Hard" msgstr "Hard" #: f.mashup.cc:1319 f.repair.cc:4410 msgid "Blend" msgstr "Blend" #: f.mashup.cc:1333 msgid "add images to layout" msgstr "add images to layout" #: f.mashup.cc:1570 msgid "Paint Image Transparencies" msgstr "Paint Image Transparencies" #: f.mashup.cc:1588 msgid "Gradual" msgstr "Gradual" #: f.mashup.cc:1590 fotoxx.h:1255 msgid "Power" msgstr "Power" #: f.mashup.cc:1823 msgid "Pull on the image with the mouse." msgstr "Pull on the image with the mouse." #: f.mashup.cc:1839 msgid "Warp Image" msgstr "Warp Image" #: f.mashup.cc:1845 f.warp.cc:1383 msgid "warp span" msgstr "warp span" #: f.mashup.cc:2281 #, c-format msgid "exceeded %d images" msgstr "exceeded %d images" #: f.mashup.cc:2423 msgid "Enter text, [Add] to layout, edit properties." msgstr "Enter text, [Add] to layout, edit properties." #: f.mashup.cc:2503 msgid "Text File:" msgstr "Text File:" #: f.mashup.cc:2507 msgid "add entered text to layout" msgstr "add entered text to layout" #: f.mashup.cc:2641 msgid "click position to add text" msgstr "click position to add text" #: f.mashup.cc:2647 #, c-format msgid "exceeded %d text entries" msgstr "exceeded %d text entries" #: f.mashup.cc:2652 f.widgets.cc:485 msgid "Add Text" msgstr "Add Text" #: f.mashup.cc:2761 msgid "Set line properties, [Add] to layout, edit." msgstr "Set line properties, [Add] to layout, edit." #: f.mashup.cc:2785 msgid "Edit Line/Arrow" msgstr "Edit Line/Arrow" #: f.mashup.cc:2840 msgid "add line/arrow to layout" msgstr "add line/arrow to layout" #: f.mashup.cc:2931 msgid "click position to add line" msgstr "click position to add line" #: f.mashup.cc:2937 #, c-format msgid "exceeded %d line entries" msgstr "exceeded %d line entries" #: f.mashup.cc:2942 f.widgets.cc:486 msgid "Add Line" msgstr "Add Line" #: f.mashup.cc:4189 msgid "Image Montage" msgstr "Image Montage" #: f.mashup.cc:4198 msgid "Frame Width" msgstr "Frame Width" #: f.mashup.cc:4201 f.mashup.cc:4209 msgid "Margin" msgstr "Margin" #: f.mashup.cc:4206 msgid "Image Columns" msgstr "Image Columns" #: f.mashup.cc:4223 #, c-format msgid "exceed %d rows" msgstr "exceed %d row" #: f.mashup.cc:4319 msgid "Optimize" msgstr "Optimize" #: f.mashup.cc:4327 #, c-format msgid "column difference: %d pixels" msgstr "column difference: %d pixel" #: f.mashup.cc:4385 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "column difference: %d pixels \n" "Make columns even?" #: f.mashup.cc:4407 msgid "Save with unique montage name" msgstr "Save with unique montage name" #: f.mashup.cc:4409 msgid "unique name:" msgstr "unique name:" #: f.mashup.cc:4411 msgid "create image map" msgstr "create image map" #: f.mashup.cc:4421 f.meta.cc:8637 msgid "supply a reasonable name" msgstr "supply a reasonable name" #: f.mashup.cc:4432 msgid "save montage" msgstr "save montage" #: f.mashup.cc:4452 #, c-format msgid "map file saved: %s" msgstr "map file saved: %s" #: f.mashup.cc:4492 #, c-format msgid "%d max images exceeded" msgstr "%d max images exceeded" #: f.mashup.cc:4566 f.mashup.cc:4572 #, c-format msgid "image frame is too large: %d x %d" msgstr "image frame is too large: %d x %d" #: f.mashup.cc:4873 #, c-format msgid "montage map file not found: %s" msgstr "montage map file not found: %s" #: f.mashup.cc:4877 #, c-format msgid "montage map file invalid: %s" msgstr "montage map file invalid: %s" #: f.meta.cc:242 msgid "Add Metadata Items" msgstr "Add Metadata Items" #: f.meta.cc:246 f.meta.cc:1583 f.meta.cc:3104 msgid "click to select" msgstr "click to select" #: f.meta.cc:251 msgid "click to unselect" msgstr "click to unselect" #: f.meta.cc:475 msgid "Extras" msgstr "Extras" #: f.meta.cc:475 f.meta.cc:655 f.widgets.cc:754 msgid "View Metadata" msgstr "View Metadata" #: f.meta.cc:813 f.widgets.cc:450 f.widgets.cc:755 msgid "Edit Metadata" msgstr "Edit Metadata" #: f.meta.cc:816 msgid "save metadata to file" msgstr "save metadata to file" #: f.meta.cc:825 msgid "Image Date" msgstr "Image Date" #: f.meta.cc:828 msgid "Time" msgstr "Time" #: f.meta.cc:836 msgid "Rating (stars):" msgstr "Rating (stars):" #: f.meta.cc:848 msgid "Caption" msgstr "Caption" #: f.meta.cc:853 msgid "Comments" msgstr "Comments" #: f.meta.cc:860 msgid "Location" msgstr "Location" #: f.meta.cc:863 msgid "Country" msgstr "Country" #: f.meta.cc:886 msgid "Image Tags" msgstr "Image Tags" #: f.meta.cc:891 f.meta.cc:1992 msgid "Recent Tags" msgstr "Recent Tags" #: f.meta.cc:896 f.meta.cc:1998 msgid "Enter New Tag" msgstr "Enter New Tag" #: f.meta.cc:902 f.meta.cc:2004 f.meta.cc:4565 msgid "Matching Tags" msgstr "Matching Tags" #: f.meta.cc:909 f.meta.cc:2013 f.meta.cc:2511 f.meta.cc:4571 msgid "Defined Tags Category" msgstr "Defined Tags Category" #: f.meta.cc:916 msgid "search known locations" msgstr "search known locations" #: f.meta.cc:917 msgid "search using web service" msgstr "search using web service" #: f.meta.cc:918 f.meta.cc:2020 msgid "create tag categories and tags" msgstr "create tag categories and tags" #: f.meta.cc:1368 f.meta.cc:3673 f.meta.cc:7231 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "bad latitude/longitude: %s %s" #: f.meta.cc:1423 fotoxx.h:1234 msgid "Manage Tags" msgstr "Manage Tags" #: f.meta.cc:1423 msgid "orphan tags" msgstr "orphan tags" #: f.meta.cc:1427 msgid "category" msgstr "category" #: f.meta.cc:1430 msgid "tag" msgstr "tag" #: f.meta.cc:1437 msgid "Defined Tags:" msgstr "Defined Tags:" #: f.meta.cc:1579 f.widgets.cc:451 f.widgets.cc:756 msgid "Edit Any Metadata" msgstr "Edit Any Metadata" #: f.meta.cc:1579 f.meta.cc:3099 msgid "Full List" msgstr "Full List" #: f.meta.cc:1592 f.meta.cc:3115 msgid "key name" msgstr "key name" #: f.meta.cc:1595 f.meta.cc:3116 msgid "key value" msgstr "key value" #: f.meta.cc:1677 f.meta.cc:3259 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" #: f.meta.cc:1777 f.widgets.cc:452 msgid "Delete Metadata" msgstr "Delete Metadata" #: f.meta.cc:1783 fotoxx.h:1179 msgid "All" msgstr "All" #: f.meta.cc:1784 msgid "One Key:" msgstr "One Key:" #: f.meta.cc:1968 f.widgets.cc:568 msgid "Batch Add/Remove Tags" msgstr "Batch Add/Remove Tags" #: f.meta.cc:1981 msgid "tags to add" msgstr "tags to add" #: f.meta.cc:1982 msgid "tags to remove" msgstr "tags to remove" #: f.meta.cc:2095 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " too many tags" #: f.meta.cc:2108 msgid "repeat with same files?" msgstr "repeat with same files?" #: f.meta.cc:2290 msgid "specify files and tags" msgstr "specify files and tags" #: f.meta.cc:2490 f.widgets.cc:569 msgid "Batch Rename Tags" msgstr "Batch Rename Tags" #: f.meta.cc:2500 msgid "(click defined tag)" msgstr "(click defined tag)" #: f.meta.cc:2502 f.process.cc:749 msgid "Rename to" msgstr "Rename to" #: f.meta.cc:2519 msgid "old tag name >> new tag name" msgstr "old tag name >> new tag name" #: f.meta.cc:2576 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" #: f.meta.cc:2720 msgid "max tags exceeded" msgstr "max tags exceeded" #: f.meta.cc:2782 f.widgets.cc:570 msgid "Batch Photo Date/Time" msgstr "Batch Photo Date/Time" #: f.meta.cc:2790 msgid "set a new date/time:" msgstr "set a new date/time:" #: f.meta.cc:2798 msgid "shift existing date/time:" msgstr "shift existing date/time:" #: f.meta.cc:2824 msgid "test: show changes, do not update files" msgstr "test: show changes, do not update files" #: f.meta.cc:2850 f.meta.cc:3190 f.meta.cc:3368 msgid "no files selected" msgstr "no files selected" #: f.meta.cc:2880 f.meta.cc:2888 f.meta.cc:2894 msgid "invalid date/time format" msgstr "invalid date/time format" #: f.meta.cc:3099 f.widgets.cc:571 msgid "Batch Add/Change Metadata" msgstr "Batch Add/Change Metadata" #: f.meta.cc:3184 msgid "enter key names" msgstr "enter key names" #: f.meta.cc:3354 f.widgets.cc:572 msgid "Batch Report Metadata" msgstr "Batch Report Metadata" #: f.meta.cc:3498 f.widgets.cc:573 msgid "Batch Geotags" msgstr "Batch Geotags" #: f.meta.cc:3528 msgid "location" msgstr "location" #: f.meta.cc:3531 msgid "country" msgstr "country" #: f.meta.cc:3659 msgid "" "data is incomplete \n" " proceed?" msgstr "" "data is incomplete \n" " proceed?" #: f.meta.cc:3745 msgid "Report Image Locations" msgstr "Report Image Locations" #: f.meta.cc:3746 msgid "Group by country" msgstr "Group by country" #: f.meta.cc:3747 msgid "Group by country/location" msgstr "Group by country/location" #: f.meta.cc:3748 msgid "Group by country/location/date" msgstr "Group by country/location/date" #: f.meta.cc:3749 msgid "Group by date/country/location" msgstr "Group by date/country/location" #: f.meta.cc:3752 msgid "Combine within" msgstr "Combine within" #: f.meta.cc:3754 msgid "days" msgstr "days" #: f.meta.cc:3860 f.widgets.cc:1026 f.widgets.cc:1048 msgid "Image Locations" msgstr "Image Locations" #: f.meta.cc:4137 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" #: f.meta.cc:4489 msgid "Search Image Metadata" msgstr "Search Image Metadata" #: f.meta.cc:4493 msgid "images to search:" msgstr "images to search:" #: f.meta.cc:4494 msgid "all" msgstr "all" #: f.meta.cc:4495 msgid "current set only" msgstr "current set only" #: f.meta.cc:4498 msgid "matching images:" msgstr "matching images:" #: f.meta.cc:4499 msgid "new set" msgstr "new set" #: f.meta.cc:4500 msgid "add to set" msgstr "add to set" #: f.meta.cc:4501 msgid "remove" msgstr "remove" #: f.meta.cc:4504 msgid "report type:" msgstr "report type:" #: f.meta.cc:4505 msgid "gallery" msgstr "gallery" #: f.meta.cc:4509 msgid "date range" msgstr "date range" #: f.meta.cc:4514 msgid "photo date" msgstr "photo date" #: f.meta.cc:4515 msgid "file date" msgstr "file date" #: f.meta.cc:4516 msgid "(yyyy-mm-dd)" msgstr "(yyyy-mm-dd)" #: f.meta.cc:4519 msgid "stars range" msgstr "stars range" #: f.meta.cc:4522 msgid "last version only" msgstr "last version only" #: f.meta.cc:4524 msgid "all/any" msgstr "all/any" #: f.meta.cc:4527 msgid "search tags" msgstr "search tags" #: f.meta.cc:4533 msgid "search text" msgstr "search text" #: f.meta.cc:4539 msgid "search files" msgstr "search files" #: f.meta.cc:4545 msgid "search locations" msgstr "search locations" #: f.meta.cc:4549 msgid "enter cities, countries" msgstr "enter cities, countries" #: f.meta.cc:4554 msgid "search other metadata" msgstr "search other metadata" #: f.meta.cc:4561 msgid "Enter Search Tag" msgstr "Enter Search Tag" #: f.meta.cc:4837 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "to remove images from current set, \n" "search current set" #: f.meta.cc:4844 msgid "" "to add images to current set, \n" "search all images" msgstr "" "to add images to current set, \n" "search all images" #: f.meta.cc:4890 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "search dates not reasonable \n" " %s %s" #: f.meta.cc:4915 msgid "stars range not reasonable" msgstr "stars range not reasonable" #: f.meta.cc:5129 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "images added: %d removed: %d new count: %d" #: f.meta.cc:5132 msgid "no changes made" msgstr "no changes made" #: f.meta.cc:5303 msgid "" "These items are always reported: \n" "date, stars, tags, caption, comment" msgstr "" "These items are always reported: \n" "date, stars, tags, caption, comment" #: f.meta.cc:5328 msgid "Additional Items for Report" msgstr "Additional Items for Report" #: f.meta.cc:5335 msgid "Keyword" msgstr "Keyword" #: f.meta.cc:5349 msgid "Match Criteria" msgstr "Match Criteria" #: f.meta.cc:5455 #, c-format msgid "bad number: %s" msgstr "bad number: %s" #: f.meta.cc:5712 msgid "date format is YYYY-MM-DD" msgstr "date format is YYYY-MM-DD" #: f.meta.cc:5716 msgid "date is invalid" msgstr "date is invalid" #: f.meta.cc:5750 msgid "time format is HH:MM [:SS]" msgstr "time format is HH:MM [:SS]" #: f.meta.cc:5754 msgid "time is invalid" msgstr "time is invalid" #: f.meta.cc:6851 msgid "not found" msgstr "not found" #: f.meta.cc:6852 msgid "location and country required" msgstr "location and country required" #: f.meta.cc:7111 msgid "choose location" msgstr "choose location" #: f.meta.cc:7408 msgid "Set Map Markers" msgstr "Set Map Markers" #: f.meta.cc:7409 msgid "mark all image files" msgstr "mark all image files" #: f.meta.cc:7410 msgid "mark current gallery" msgstr "mark current gallery" #: f.meta.cc:7510 msgid "choose map file" msgstr "choose map file" #: f.meta.cc:7652 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "fotoxx-maps package not installed \n" "(see https://kornelix.net" #: f.meta.cc:7657 #, c-format msgid "map file %s is missing" msgstr "map file %s is missing" #: f.meta.cc:7661 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" #: f.meta.cc:7981 f.meta.cc:8508 msgid "No matching images found" msgstr "No matching images found" #: f.meta.cc:8073 msgid "Set Map Source" msgstr "Set Map Source" #: f.meta.cc:8584 msgid "Net Map Locations" msgstr "Net Map Locations" #: f.meta.cc:8589 msgid "map location:" msgstr "map location:" #: f.process.cc:123 f.widgets.cc:559 msgid "Batch Convert" msgstr "Batch Convert" #: f.process.cc:135 msgid "Sequence Numbers" msgstr "Sequence Numbers" #: f.process.cc:137 msgid "base" msgstr "base" #: f.process.cc:140 msgid "adder" msgstr "adder" #: f.process.cc:145 msgid "New Location" msgstr "New Location" #: f.process.cc:150 msgid "New File Type" msgstr "New File Type" #: f.process.cc:154 f.process.cc:162 f.process.cc:2538 msgid "no change" msgstr "no change" #: f.process.cc:157 f.process.cc:2533 msgid "max. Width" msgstr "max. Width" #: f.process.cc:166 msgid "Delete Originals" msgstr "Delete Originals" #: f.process.cc:167 f.process.cc:754 msgid "Copy Metadata" msgstr "Copy Metadata" #: f.process.cc:171 f.process.cc:756 f.process.cc:1320 f.repair.cc:121 #: f.widgets.cc:492 msgid "Sharpen" msgstr "Sharpen" #: f.process.cc:181 f.process.cc:757 msgid "Overlay Image" msgstr "Overlay Image" #: f.process.cc:184 msgid "Width %" msgstr "Width %" #: f.process.cc:189 msgid "Position" msgstr "Position" #: f.process.cc:201 msgid "Make constant size for screen:" msgstr "Make constant size for screen:" #: f.process.cc:209 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" #: f.process.cc:331 #, c-format msgid "file type not supported: %s \n" msgstr "file type not supported: %s \n" #: f.process.cc:471 f.process.cc:2588 msgid "cannot create new file" msgstr "cannot create new file" #: f.process.cc:517 msgid "updating albums ..." msgstr "updating albums ..." #: f.process.cc:658 #, c-format msgid "invalid plugin: %s" msgstr "invalid plugin: %s" #: f.process.cc:665 msgid "you must use either $s or $oldname" msgstr "you must use either $s or $oldname" #: f.process.cc:670 msgid "$s plugin needs base and adder" msgstr "$s plugin needs base and adder" #: f.process.cc:675 msgid "base and adder need $s plugin" msgstr "base and adder need $s plugin" #: f.process.cc:697 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "max. size %d x %d is not reasonable" #: f.process.cc:704 msgid "specify overlay image file" msgstr "specify overlay image file" #: f.process.cc:720 msgid "specify overlay position" msgstr "specify overlay position" #: f.process.cc:748 #, c-format msgid "Convert %d image files" msgstr "Convert %d image files" #: f.process.cc:750 msgid "Convert to" msgstr "Convert to" #: f.process.cc:751 msgid "Resize within" msgstr "Resize within" #: f.process.cc:752 msgid "Output to" msgstr "Output to" #: f.process.cc:758 msgid "*** Delete Originals ***" msgstr "*** Delete Originals ***" #: f.process.cc:759 msgid "*** Replace Originals ***" msgstr "*** Replace Originals ***" #: f.process.cc:760 msgid "PROCEED?" msgstr "PROCEED?" #: f.process.cc:913 f.widgets.cc:560 msgid "Batch Upright" msgstr "Batch Upright" #: f.process.cc:919 msgid "Survey all files" msgstr "Survey all files" #: f.process.cc:956 msgid "file cannot be read" msgstr "file cannot be read" #: f.process.cc:1072 msgid "cannot select both options" msgstr "cannot select both options" #: f.process.cc:1114 f.widgets.cc:561 msgid "Batch Delete/Trash" msgstr "Batch Delete/Trash" #: f.process.cc:1119 msgid "delete" msgstr "delete" #: f.process.cc:1122 msgid "trash" msgstr "trash" #: f.process.cc:1260 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Batch Convert RAW Files (Raw Therapee)" #: f.process.cc:1290 msgid "use libraw" msgstr "use libraw" #: f.process.cc:1291 msgid "use Raw Therapee" msgstr "use Raw Therapee" #: f.process.cc:1298 msgid "output location" msgstr "output location" #: f.process.cc:1303 msgid "output file type" msgstr "output file type" #: f.process.cc:1322 msgid "amount" msgstr "amount" #: f.process.cc:1325 msgid "threshold" msgstr "threshold" #: f.process.cc:1614 msgid "script file" msgstr "script file" #: f.process.cc:1627 msgid "begin making a script file" msgstr "begin making a script file" #: f.process.cc:1630 msgid "finish making a script file" msgstr "finish making a script file" #: f.process.cc:1636 msgid "execute a script file" msgstr "execute a script file" #: f.process.cc:1696 msgid "script already started" msgstr "script already started" #: f.process.cc:1700 msgid "start a new script file" msgstr "start a new script file" #: f.process.cc:1723 msgid "perform edits to be included in the script file" msgstr "perform edits to be included in the script file" #: f.process.cc:1741 msgid "script file error" msgstr "script file error" #: f.process.cc:1746 #, c-format msgid "%s added to script" msgstr "%s added to script" #: f.process.cc:1759 msgid "no script file was started" msgstr "no script file was started" #: f.process.cc:1767 msgid "script file closed" msgstr "script file closed" #: f.process.cc:1825 msgid "open script file" msgstr "open script file" #: f.process.cc:1833 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "script file: %s \n" " %s" #: f.process.cc:1855 f.process.cc:1904 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "open failure: %s \n" " %s" #: f.process.cc:1876 #, c-format msgid "unknown edit function: %s" msgstr "unknown edit function: %s" #: f.process.cc:1885 #, c-format msgid "load widgets failed: %s" msgstr "load widgets failed: %s" #: f.process.cc:1913 #, c-format msgid "script file format error: %s" msgstr "script file format error: %s" #: f.process.cc:1949 msgid "growisofs not installed" msgstr "growisofs not installed" #: f.process.cc:2001 msgid "no DVD/BlueRay device found" msgstr "no DVD/BlueRay device found" #: f.process.cc:2024 f.widgets.cc:564 msgid "Burn Images to DVD/BlueRay" msgstr "Burn Images to DVD/BlueRay" #: f.process.cc:2029 msgid "Select device" msgstr "Select device" #: f.process.cc:2136 f.widgets.cc:565 msgid "Find Duplicate Images" msgstr "Find Duplicate Images" #: f.process.cc:2138 msgid "Thumbnail size" msgstr "Thumbnail size" #: f.process.cc:2142 msgid "pixel difference" msgstr "pixel difference" #: f.process.cc:2145 msgid "pixel count" msgstr "pixel count" #: f.process.cc:2152 msgid "Duplicates:" msgstr "Duplicates:" #: f.process.cc:2170 #, c-format msgid "only %d image thumbnails found" msgstr "only %d image thumbnails found" #: f.process.cc:2374 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Create a file of selected image files" #: f.process.cc:2398 f.process.cc:2467 msgid "Output File" msgstr "Output File" #: f.process.cc:2418 msgid "no input files selected" msgstr "no input files selected" #: f.process.cc:2424 msgid "no output file selected" msgstr "no output file selected" #: f.process.cc:2523 f.widgets.cc:567 msgid "Export Image Files" msgstr "Export Image Files" #: f.process.cc:2528 msgid "To Location" msgstr "To Location" #: f.process.cc:2563 msgid "file type not supported" msgstr "file type not supported" #: f.process.cc:2643 msgid "location is not a directory" msgstr "location is not a directory" #: f.repair.cc:164 msgid "dark" msgstr "dark" #: f.repair.cc:165 msgid "light" msgstr "light" #: f.repair.cc:1193 msgid "Apply repeatedly while watching the image." msgstr "Apply repeatedly while watching the image." #: f.repair.cc:1194 msgid "Measure" msgstr "Measure" #: f.repair.cc:1228 msgid "Noise Reduction" msgstr "Noise Reduction" #: f.repair.cc:1247 f.widgets.cc:479 f.widgets.cc:774 fotoxx.h:1218 msgid "Flatten" msgstr "Flatten" #: f.repair.cc:1248 msgid "Median" msgstr "Median" #: f.repair.cc:1263 msgid "dark areas" msgstr "dark areas" #: f.repair.cc:1265 msgid "all areas" msgstr "all areas" #: f.repair.cc:1856 msgid "Measure Noise" msgstr "Measure Noise" #: f.repair.cc:1857 msgid "Move mouse in a monotone image area." msgstr "Move mouse in a monotone image area." #: f.repair.cc:2165 msgid "" "Method 1:\n" " Left-click on red-eye to darken.\n" "Method 2:\n" " Drag down and right to enclose red-eye.\n" " Left-click on red-eye to darken.\n" "Undo red-eye:\n" " Right-click on red-eye." msgstr "" "Method 1:\n" " Left-click on red-eye to darken.\n" "Method 2:\n" " Drag down and right to enclose red-eye.\n" " Left-click on red-eye to darken.\n" "Undo red-eye:\n" " Right-click on red-eye." #: f.repair.cc:2181 msgid "Red Eye Reduction" msgstr "Red Eye Reduction" #: f.repair.cc:2642 f.widgets.cc:496 msgid "Color Mode" msgstr "Color Mode" #: f.repair.cc:2645 msgid "reset" msgstr "reset" #: f.repair.cc:2646 msgid "black/white positive" msgstr "black/white positive" #: f.repair.cc:2647 msgid "black/white negative" msgstr "black/white negative" #: f.repair.cc:2648 msgid "color negative" msgstr "color negative" #: f.repair.cc:2649 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.repair.cc:2650 msgid "sepia" msgstr "sepia" #: f.repair.cc:2861 msgid "Set color depth to 1-16 bits" msgstr "Set color depth to 1-16 bits" #: f.repair.cc:2875 msgid "Set Color Depth" msgstr "Set Color Depth" #: f.repair.cc:3025 f.widgets.cc:498 msgid "Shift Colors" msgstr "Shift Colors" #: f.repair.cc:3259 f.widgets.cc:499 msgid "Color Saturation" msgstr "Color Saturation" #: f.repair.cc:3448 msgid "+Brightness" msgstr "+Brightness" #: f.repair.cc:3449 msgid "+Red -Cyan" msgstr "+Red -Cyan" #: f.repair.cc:3450 msgid "+Green -Magenta" msgstr "+Green -Magenta" #: f.repair.cc:3451 msgid "+Blue -Yellow" msgstr "+Blue -Yellow" #: f.repair.cc:3457 fotoxx.h:1261 msgid "Red" msgstr "Red" #: f.repair.cc:3458 fotoxx.h:1220 msgid "Green" msgstr "Green" #: f.repair.cc:3459 fotoxx.h:1186 msgid "Blue" msgstr "Blue" #: f.repair.cc:3761 msgid "Input color to match and adjust:" msgstr "Input color to match and adjust:" #: f.repair.cc:3763 msgid "shift+click on image to select color" msgstr "shift+click on image to select color" #: f.repair.cc:3766 f.repair.cc:7937 msgid "Match using:" msgstr "Match using:" #: f.repair.cc:3767 msgid "Hue" msgstr "Hue" #: f.repair.cc:3779 msgid "Output Color" msgstr "Output Color" #: f.repair.cc:3802 msgid "Adjustment" msgstr "Adjustment" #: f.repair.cc:4320 msgid "Click image to select areas." msgstr "Click image to select areas." #: f.repair.cc:4352 msgid "Local Color" msgstr "Local Color" #: f.repair.cc:4363 msgid "Metric:" msgstr "Metric:" #: f.repair.cc:4786 msgid "Color Match Images" msgstr "Color Match Images" #: f.repair.cc:4812 msgid "mouse radius for color sample" msgstr "mouse radius for color sample" #: f.repair.cc:4814 f.repair.cc:4819 fotoxx.h:1250 msgid "Open" msgstr "Open" #: f.repair.cc:4815 msgid "image for source color" msgstr "image for source color" #: f.repair.cc:4817 msgid "click on image to get source color" msgstr "click on image to get source color" #: f.repair.cc:4820 msgid "image to set matching color" msgstr "image to set matching color" #: f.repair.cc:4822 msgid "click on image to set matching color" msgstr "click on image to set matching color" #: f.repair.cc:4881 msgid "select source image color first" msgstr "select source image color first" #: f.repair.cc:5059 msgid "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " msgstr "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " #: f.repair.cc:5082 f.widgets.cc:504 msgid "Color Fringes" msgstr "Color Fringes" #: f.repair.cc:5240 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " #: f.repair.cc:5262 f.widgets.cc:505 msgid "Smart Erase" msgstr "Smart Erase" #: f.repair.cc:5267 fotoxx.h:1259 msgid "Radius" msgstr "Radius" #: f.repair.cc:5269 f.widgets.cc:493 msgid "Blur" msgstr "Blur" #: f.repair.cc:5272 msgid "New Area" msgstr "New Area" #: f.repair.cc:5617 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Draw a line across the image in \n" "direction of brightness change." #: f.repair.cc:5656 f.widgets.cc:506 msgid "Brightness Ramp" msgstr "Brightness Ramp" #: f.repair.cc:6182 f.widgets.cc:507 msgid "Remove Dust" msgstr "Remove Dust" #: f.repair.cc:6186 msgid "spot size limit" msgstr "spot size limit" #: f.repair.cc:6189 msgid "max. brightness" msgstr "max. brightness" #: f.repair.cc:6192 msgid "min. contrast" msgstr "min. contrast" #: f.repair.cc:6999 f.widgets.cc:510 msgid "Stuck Pixels" msgstr "Stuck Pixels" #: f.repair.cc:7005 msgid "pixel group" msgstr "pixel group" #: f.repair.cc:7013 f.repair.cc:7085 msgid "stuck pixels:" msgstr "stuck pixels:" #: f.repair.cc:7113 msgid "Load Stuck Pixels" msgstr "Load Stuck Pixels" #: f.repair.cc:7115 msgid "File:" msgstr "File:" #: f.repair.cc:7139 f.repair.cc:7196 msgid "Stuck Pixels file" msgstr "Stuck Pixels file" #: f.repair.cc:7176 f.tools.cc:4220 msgid "file format error" msgstr "file format error" #: f.repair.cc:7622 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "left drag: add transparency \n" "right drag: add opacity" #: f.repair.cc:7654 f.widgets.cc:511 msgid "Paint Transparency" msgstr "Paint Transparency" #: f.repair.cc:7662 msgid "paintbrush radius" msgstr "paintbrush radius" #: f.repair.cc:7669 msgid "gradual paint" msgstr "gradual paint" #: f.repair.cc:7923 f.widgets.cc:512 msgid "Add Transparency" msgstr "Add Transparency" #: f.repair.cc:7927 msgid "Match Brightness" msgstr "Match Brightness" #: f.repair.cc:7932 msgid "Match Color" msgstr "Match Color" #: f.repair.cc:7934 msgid "click on image to select color" msgstr "click on image to select color" #: f.repair.cc:7950 msgid "Invert Match" msgstr "Invert Match" #: f.repair.cc:7953 fotoxx.h:1285 msgid "Transparency" msgstr "Transparency" #: f.tools.cc:86 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" "Select directories containing image files \n" "(subdirectories are included automatically)." #: f.tools.cc:88 msgid "Select to add, click on X to delete." msgstr "Select to add, click on X to delete." #: f.tools.cc:89 msgid "directory for thumbnails" msgstr "directory for thumbnails" #: f.tools.cc:90 msgid "extra metadata items to include in index" msgstr "extra metadata items to include in index" #: f.tools.cc:91 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." #: f.tools.cc:126 f.widgets.cc:579 msgid "Index Image Files" msgstr "Index Image Files" #: f.tools.cc:298 msgid "Choose top image directories" msgstr "Choose top image directories" #: f.tools.cc:299 msgid "Choose thumbnail directory" msgstr "Choose thumbnail directory" #: f.tools.cc:300 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "All image files will be re-indexed. \n" " Continue?" #: f.tools.cc:372 msgid "" "No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files." msgstr "" "No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files." #: f.tools.cc:378 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Invalid directory: \n" " %s \n" "Please remove." #: f.tools.cc:381 #, c-format msgid "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" msgstr "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" #: f.tools.cc:384 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Duplicate directory: \n" " %s \n" "Please remove." #: f.tools.cc:443 msgid "specify 1 thumbnail directory" msgstr "specify 1 thumbnail directory" #: f.tools.cc:628 msgid "Top directories have no images" msgstr "Top directories have no images" #: f.tools.cc:1075 msgid "Cancel image index function?" msgstr "Cancel image index function?" #: f.tools.cc:1195 msgid "Recent Files Gallery" msgstr "Recent Files Gallery" #: f.tools.cc:1196 msgid "Newest Files Gallery" msgstr "Newest Files Gallery" #: f.tools.cc:1197 msgid "Specific Gallery" msgstr "Specific Gallery" #: f.tools.cc:1198 msgid "Album Gallery" msgstr "Album Gallery" #: f.tools.cc:1199 msgid "Previous Gallery" msgstr "Previous Gallery" #: f.tools.cc:1200 msgid "Previous Image File" msgstr "Previous Image File" #: f.tools.cc:1201 msgid "Specific Image File" msgstr "Specific Image File" #: f.tools.cc:1202 f.widgets.cc:435 msgid "Blank Window" msgstr "Blank Window" #: f.tools.cc:1250 f.widgets.cc:580 msgid "User Settings" msgstr "User Settings" #: f.tools.cc:1253 msgid "Startup Display" msgstr "Startup Display" #: f.tools.cc:1264 msgid "Background color:" msgstr "Background color:" #: f.tools.cc:1265 msgid "F-View" msgstr "F-View" #: f.tools.cc:1268 msgid "G-View" msgstr "G-View" #: f.tools.cc:1272 msgid "Menu Text" msgstr "Menu Text" #: f.tools.cc:1279 msgid "Menu Style" msgstr "Menu Style" #: f.tools.cc:1280 msgid "Icons" msgstr "Icons" #: f.tools.cc:1281 msgid "Icons + Text" msgstr "Icons + Text" #: f.tools.cc:1283 msgid "Icon size" msgstr "Icon size" #: f.tools.cc:1287 msgid "Dialog font" msgstr "Dialog font" #: f.tools.cc:1292 msgid "Zoomed Image:" msgstr "Zoomed Image:" #: f.tools.cc:1301 msgid "JPEG save quality" msgstr "JPEG save quality" #: f.tools.cc:1305 msgid "Curve node capture distance" msgstr "Curve node capture distance" #: f.tools.cc:1309 msgid "Map marker size" msgstr "Map marker size" #: f.tools.cc:1313 msgid "show last file version only" msgstr "show last file version only" #: f.tools.cc:1316 msgid "shift image to right margin" msgstr "shift image to right margin" #: f.tools.cc:1319 f.tools.cc:1324 msgid "image index level" msgstr "image index level" #: f.tools.cc:1321 msgid "Fotoxx started directly" msgstr "Fotoxx started directly" #: f.tools.cc:1326 msgid "Fotoxx started by file manager" msgstr "Fotoxx started by file manager" #: f.tools.cc:1329 msgid "RAW file types" msgstr "RAW file types" #: f.tools.cc:1333 msgid "video file types" msgstr "video file types" #: f.tools.cc:1440 msgid "Select startup directory" msgstr "Select startup directory" #: f.tools.cc:1447 msgid "Select startup image file" msgstr "Select startup image file" #: f.tools.cc:1454 msgid "Select startup album" msgstr "Select startup album" #: f.tools.cc:1497 msgid "startup directory is invalid" msgstr "startup directory is invalid" #: f.tools.cc:1508 msgid "startup file is invalid" msgstr "startup file is invalid" #: f.tools.cc:1683 f.widgets.cc:581 msgid "Keyboard Shortcuts" msgstr "Keyboard Shortcuts" #: f.tools.cc:1690 msgid "Reserved Shortcuts \n" msgstr "Reserved Shortcuts \n" #: f.tools.cc:1691 msgid " F1 User Guide for current function \n" msgstr " F1 User Guide for current function \n" #: f.tools.cc:1692 msgid " F10 Full Screen with menus \n" msgstr " F10 Full Screen with menus \n" #: f.tools.cc:1693 msgid " F11 Full Screen without menus \n" msgstr " F11 Full Screen without menus \n" #: f.tools.cc:1694 msgid " Escape Quit dialog; Quit Fotoxx \n" msgstr " Escape Quit dialog; Quit Fotoxx \n" #: f.tools.cc:1695 msgid " Ctrl+H Show hidden files in Gallery mode \n" msgstr " Ctrl+H Show hidden files in Gallery mode \n" #: f.tools.cc:1696 msgid " Delete Delete/Trash dialog \n" msgstr " Delete Delete/Trash dialog \n" #: f.tools.cc:1697 msgid " Arrows Previous/Next Image or Gallery page \n" msgstr " Arrows Previous/Next Image or Gallery page \n" #: f.tools.cc:1757 msgid "Edit KB Shortcuts" msgstr "Edit KB Shortcuts" #: f.tools.cc:1766 msgid "shortcut key:" msgstr "shortcut key:" #: f.tools.cc:1767 msgid "(enter key)" msgstr "(enter key)" #: f.tools.cc:1768 f.tools.cc:1911 f.tools.cc:2015 msgid "(no selection)" msgstr "(no selection)" #: f.tools.cc:1905 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reserved, cannot be used" #: f.tools.cc:2036 msgid "unable to save KB-shortcuts file" msgstr "unable to save KB-shortcuts file" #: f.tools.cc:2356 f.widgets.cc:583 msgid "Grid Lines" msgstr "Grid Lines" #: f.tools.cc:2365 msgid "x-spacing" msgstr "x-spacing" #: f.tools.cc:2366 msgid "x-count" msgstr "x-count" #: f.tools.cc:2367 msgid "x-enable" msgstr "x-enable" #: f.tools.cc:2373 msgid "y-spacing" msgstr "y-spacing" #: f.tools.cc:2374 msgid "y-count" msgstr "y-count" #: f.tools.cc:2375 msgid "y-enable" msgstr "y-enable" #: f.tools.cc:2495 f.widgets.cc:584 msgid "Line Color" msgstr "Line Color" #: f.tools.cc:2560 msgid "Click image to select pixels." msgstr "Click image to select pixels." #: f.tools.cc:2595 f.widgets.cc:585 msgid "Show RGB" msgstr "Show RGB" #: f.tools.cc:2890 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key X to toggle dialog." msgstr "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key X to toggle dialog." #: f.tools.cc:2918 f.widgets.cc:586 msgid "Magnify Image" msgstr "Magnify Image" #: f.tools.cc:2927 msgid "X-size" msgstr "X-size" #: f.tools.cc:3172 msgid "Darkest and Brightest Pixels" msgstr "Darkest and Brightest Pixels" #: f.tools.cc:3195 msgid "Dark Limit" msgstr "Dark Limit" #: f.tools.cc:3196 msgid "Bright Limit" msgstr "Bright Limit" #: f.tools.cc:3285 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." #: f.tools.cc:3376 f.widgets.cc:589 msgid "Monitor Gamma" msgstr "Monitor Gamma" #: f.tools.cc:3443 msgid "Available Translations" msgstr "Available Translations" #: f.tools.cc:3447 msgid "Set Language" msgstr "Set Language" #: f.tools.cc:3573 msgid "Change Color Profile" msgstr "Change Color Profile" #: f.tools.cc:3577 msgid "input profile" msgstr "input profile" #: f.tools.cc:3581 msgid "output profile" msgstr "output profile" #: f.tools.cc:3602 msgid "Unable to change EXIF color profile" msgstr "Unable to change EXIF color profile" #: f.tools.cc:3604 msgid "automatic new version created" msgstr "automatic new version created" #: f.tools.cc:3613 msgid "color profile" msgstr "color profile" #: f.tools.cc:3661 f.tools.cc:3667 #, c-format msgid "unknown cms profile %s" msgstr "unknown cms profile %s" #: f.tools.cc:3770 f.widgets.cc:593 msgid "Calibrate Printer" msgstr "Calibrate Printer" #: f.tools.cc:3795 msgid "print color chart" msgstr "print color chart" #: f.tools.cc:3796 msgid "scan and save color chart" msgstr "scan and save color chart" #: f.tools.cc:3797 msgid "align and trim color chart" msgstr "align and trim color chart" #: f.tools.cc:3798 msgid "open and process color chart" msgstr "open and process color chart" #: f.tools.cc:3799 msgid "print image with revised colors" msgstr "print image with revised color" #: f.tools.cc:3940 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" #: f.tools.cc:3955 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." #: f.tools.cc:3987 msgid "Open the trimmed color chart file" msgstr "Open the trimmed color chart file" #: f.tools.cc:4120 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" "Set the name for the output calibration file \n" "[your calibration name].dat" #: f.tools.cc:4160 msgid "Color map file to use" msgstr "Color map file to use" #: f.tools.cc:4179 msgid "Select the image file to print." msgstr "Select the image file to print." #: f.tools.cc:4244 msgid "converting colors..." msgstr "converting colors..." #: f.tools.cc:4344 msgid "Image colors are converted for printing." msgstr "Image colors are converted for printing." #: f.tools.cc:4467 msgid "This function is for AppImage package only" msgstr "This function is for AppImage package only" #: f.tools.cc:4471 msgid "" "Completely remove the AppImage package \n" "Press F1 for more information. \n" "Continue?" msgstr "" "Completely remove the AppImage package \n" "Press F1 for more information. \n" "Continue?" #: f.warp.cc:94 f.widgets.cc:515 msgid "Unbend" msgstr "Unbend" #: f.warp.cc:371 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." #: f.warp.cc:388 msgid "Perspective Correction" msgstr "Perspective Correction" #: f.warp.cc:621 msgid "must have 4 corners" msgstr "must have 4 corners" #: f.warp.cc:745 msgid "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." msgstr "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." #: f.warp.cc:758 f.widgets.cc:517 msgid "Warp area" msgstr "Warp area" #: f.warp.cc:763 msgid "start warp" msgstr "start warp" #: f.warp.cc:1149 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" #: f.warp.cc:1176 f.widgets.cc:518 msgid "Unwarp Closeup" msgstr "Unwarp Closeup" #: f.warp.cc:1356 f.warp.cc:1671 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." #: f.warp.cc:1374 f.widgets.cc:519 msgid "Warp curved" msgstr "Warp curved" #: f.warp.cc:1689 f.widgets.cc:520 msgid "Warp linear" msgstr "Warp linear" #: f.warp.cc:2005 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." #: f.warp.cc:2021 f.widgets.cc:521 msgid "Warp affine" msgstr "Warp affine" #: f.warp.cc:2368 f.widgets.cc:522 msgid "Flatten Book Page" msgstr "Flatten Book Page" #: f.warp.cc:2369 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " #: f.warp.cc:2372 msgid "Stretch curved-down surfaces:" msgstr "Stretch curved-down surfaces:" #: f.warp.cc:2427 msgid "Top:" msgstr "Top:" #: f.warp.cc:2430 msgid "Bottom:" msgstr "Bottom:" #: f.warp.cc:2811 f.widgets.cc:523 msgid "Spherical Projection" msgstr "Spherical Projection" #: f.warp.cc:2855 msgid "Magnify" msgstr "Magnify" #: f.warp.cc:3029 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." #: f.warp.cc:3045 f.widgets.cc:524 msgid "Selective Rescale" msgstr "Selective Rescale" #: f.warp.cc:3068 msgid "select areas first" msgstr "select areas first" #: f.warp.cc:3290 f.widgets.cc:525 msgid "Make Waves" msgstr "Make Waves" #: f.warp.cc:3297 msgid "wavelength" msgstr "wavelength" #: f.warp.cc:3299 msgid "variance" msgstr "variance" #: f.warp.cc:3310 msgid "perspective" msgstr "perspective" #: f.warp.cc:3484 f.warp.cc:3515 msgid "Twist" msgstr "Twist" #: f.warp.cc:3512 msgid "drag mouse to set center" msgstr "drag mouse to set center" #: f.widgets.cc:103 msgid "Album" msgstr "Album" #: f.widgets.cc:105 msgid "TOP" msgstr "TOP" #: f.widgets.cc:171 msgid "Current Image File (key F)" msgstr "Current Image File (key F)" #: f.widgets.cc:172 msgid "Thumbnail Gallery (key G)" msgstr "Thumbnail Gallery (key G)" #: f.widgets.cc:173 msgid "World Maps (key W)" msgstr "World Maps (key W)" #: f.widgets.cc:174 msgid "Net Maps (key M)" msgstr "Net Maps (key M)" #: f.widgets.cc:177 msgid "Favorite Functions" msgstr "Favorite Functions" #: f.widgets.cc:178 msgid "File: Open, RAW, Rename, Delete, Print" msgstr "File: Open, RAW, Rename, Delete, Print" #: f.widgets.cc:179 msgid "Save modified image file to disk" msgstr "Save modified image file to disk" #: f.widgets.cc:180 msgid "Open previous or next file (left/right mouse click)" msgstr "Open previous or next file (left/right mouse click)" #: f.widgets.cc:181 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadata: Captions, Tags, Ratings, Geotags, Search ... " #: f.widgets.cc:182 msgid "Areas: Select areas to edit, copy and paste" msgstr "Areas: Select areas to edit, copy and paste" #: f.widgets.cc:183 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." #: f.widgets.cc:184 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." #: f.widgets.cc:185 msgid "Warp: Fix Perspective, Warp or unwarp image ..." msgstr "Warp: Fix Perspective, Warp or unwarp image ..." #: f.widgets.cc:186 msgid "Effects: Special Effects, Arty Transforms" msgstr "Effects: Special Effects, Arty Transforms" #: f.widgets.cc:187 msgid "" "left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" #: f.widgets.cc:190 msgid "Tools: Index, Settings, Shortcuts, Magnify ..." msgstr "Tools: Index, Settings, Shortcuts, Magnify ..." #: f.widgets.cc:191 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Help: Quick Start, User Guide, Recent Changes ..." #: f.widgets.cc:194 msgid "Sync Gallery, Albums, Slide Show" msgstr "Sync Gallery, Albums, Slide Show" #: f.widgets.cc:195 msgid "set and recall bookmarked image locations" msgstr "set and recall bookmarked image locations" #: f.widgets.cc:196 msgid "increase thumbnail size" msgstr "increase thumbnail size" #: f.widgets.cc:197 msgid "reduce thumbnail size" msgstr "reduce thumbnail size" #: f.widgets.cc:198 msgid "change sort order" msgstr "change sort order" #: f.widgets.cc:199 msgid "jump to beginning (top)" msgstr "jump to beginning (top)" #: f.widgets.cc:200 msgid "jump to end (bottom)" msgstr "jump to end (bottom)" #: f.widgets.cc:201 msgid "previous page" msgstr "previous page" #: f.widgets.cc:202 msgid "next page" msgstr "next page" #: f.widgets.cc:203 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combine: HDR, HDF, Panorama, Stack, Mashup" #: f.widgets.cc:204 msgid "Process: convert, export, metadata, search ..." msgstr "Process: convert, export, metadata, search ..." #: f.widgets.cc:207 msgid "Choose a map file" msgstr "Choose a map file" #: f.widgets.cc:208 f.widgets.cc:212 msgid "Mark all images or current gallery" msgstr "Mark all images or current gallery" #: f.widgets.cc:211 msgid "Choose Internet map source" msgstr "Choose Internet map source" #: f.widgets.cc:213 msgid "Save and recall named map locations" msgstr "Save and recall named map locations" #: f.widgets.cc:216 msgid "Open another window" msgstr "Open another window" #: f.widgets.cc:217 msgid "Open a new image file" msgstr "Open a new image file" #: f.widgets.cc:218 f.widgets.cc:430 msgid "Cycle 2 Previous Files" msgstr "Cycle 2 Previous Files" #: f.widgets.cc:219 f.widgets.cc:431 msgid "Cycle 3 Previous Files" msgstr "Cycle 3 Previous Files" #: f.widgets.cc:220 msgid "Open a recently seen file" msgstr "Open a recently seen file" #: f.widgets.cc:221 msgid "Open a newly added file" msgstr "Open a newly added file" #: f.widgets.cc:222 msgid "Open and edit a camera RAW file" msgstr "Open and edit a camera RAW file" #: f.widgets.cc:223 msgid "View a 360 degree panorama image file" msgstr "View a 360 degree panorama image file" #: f.widgets.cc:224 msgid "Change the image file name" msgstr "Change the image file name" #: f.widgets.cc:225 msgid "Copy or Move an image file to a new location" msgstr "Copy or Move an image file to a new location" #: f.widgets.cc:226 msgid "Copy an image file to the desktop" msgstr "Copy an image file to the desktop" #: f.widgets.cc:227 msgid "Copy image file to the clipboard" msgstr "Copy image file to the clipboard" #: f.widgets.cc:228 msgid "Copy image file to the image cache" msgstr "Copy image file to the image cache" #: f.widgets.cc:229 msgid "Show location on Interet map" msgstr "Show location on Interet map" #: f.widgets.cc:230 msgid "Create a blank image" msgstr "Create a blank image" #: f.widgets.cc:231 msgid "toggle - blank or restore window" msgstr "toggle - blank or restore window" #: f.widgets.cc:232 msgid "Delete or trash image file" msgstr "Delete or trash image file" #: f.widgets.cc:233 msgid "Print the current image" msgstr "Print the current image" #: f.widgets.cc:234 msgid "Print current image with adjusted colors" msgstr "Print current image with adjusted colors" #: f.widgets.cc:235 f.widgets.cc:445 msgid "Quit Fotoxx" msgstr "Quit Fotoxx" #: f.widgets.cc:238 msgid "List a few key metadata items" msgstr "List a few key metadata items" #: f.widgets.cc:239 msgid "List all metadata items" msgstr "List all metadata items" #: f.widgets.cc:240 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Edit image tags/geotags/caption/rating ..." #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Edit any image metadata" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Remove selected image metadata" #: f.widgets.cc:243 msgid "(Toggle) show captions and comments" msgstr "(Toggle) show captions and comments" #: f.widgets.cc:246 msgid "Select object or area for editing" msgstr "Select object or area for editing" #: f.widgets.cc:247 msgid "Find a gap in an area outline" msgstr "Find a gap in an area outline" #: f.widgets.cc:248 msgid "Select hairy or irregular edge" msgstr "Select hairy or irregular edge" #: f.widgets.cc:249 msgid "Show (outline) existing area" msgstr "Show (outline) existing area" #: f.widgets.cc:250 msgid "Hide existing area" msgstr "Hide existing area" #: f.widgets.cc:251 msgid "Enable area for editing" msgstr "Enable area for editing" #: f.widgets.cc:252 msgid "Disable area for editing" msgstr "Disable area for editing" #: f.widgets.cc:253 msgid "Reverse existing area" msgstr "Reverse existing area" #: f.widgets.cc:254 msgid "Erase existing area" msgstr "Erase existing area" #: f.widgets.cc:255 msgid "Copy area for later pasting into image" msgstr "Copy area for later pasting into image" #: f.widgets.cc:256 msgid "Save area to a file with transparency" msgstr "Save area to a file with transparency" #: f.widgets.cc:257 msgid "Open a file and paste as area into image" msgstr "Open a file and paste as area into image" #: f.widgets.cc:258 msgid "Paste previously copied area into image" msgstr "Paste previously copied area into image" #: f.widgets.cc:261 msgid "Trim/Crop margins and/or Rotate" msgstr "Trim/Crop margins and/or Rotate" #: f.widgets.cc:262 msgid "Upright a rotated image" msgstr "Upright a rotated image" #: f.widgets.cc:263 msgid "Change pixel dimensions" msgstr "Change pixel dimensions" #: f.widgets.cc:264 f.widgets.cc:265 msgid "Fast auto enhance that may work OK" msgstr "Fast auto enhance that may work OK" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Adjust brightness, contrast, color" #: f.widgets.cc:267 msgid "Edit brightness distribution" msgstr "Edit brightness distribution" #: f.widgets.cc:268 msgid "Magnify brightness gradients to enhance details" msgstr "Magnify brightness gradients to enhance details" #: f.widgets.cc:269 msgid "Flatten brightness distribution" msgstr "Flatten brightness distribution" #: f.widgets.cc:270 msgid "Rescale brightness - reduce color caste and fog/haze" msgstr "Rescale brightness - reduce color caste and fog/haze" #: f.widgets.cc:271 msgid "Mirror image horizontally or vertically" msgstr "Mirror image horizontally or vertically" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Paint image pixels using the mouse" #: f.widgets.cc:273 msgid "Clone image pixels using the mouse" msgstr "Clone image pixels using the mouse" #: f.widgets.cc:274 msgid "Blend image pixels using the mouse" msgstr "Blend image pixels using the mouse" #: f.widgets.cc:277 msgid "Paint edit function gradually with mouse" msgstr "Paint edit function gradually with mouse" #: f.widgets.cc:278 msgid "Leverage edits by brightness or color" msgstr "Leverage edits by brightness or color" #: f.widgets.cc:279 msgid "Edit plugins menu or run a plugin function" msgstr "Edit plugins menu or run a plugin function" #: f.widgets.cc:282 msgid "Make the image look sharper" msgstr "Make the image look sharper" #: f.widgets.cc:283 msgid "Make the image look fuzzy" msgstr "Make the image look fuzzy" #: f.widgets.cc:284 msgid "Filter noise from low-light photos" msgstr "Filter noise from low-light photos" #: f.widgets.cc:285 msgid "Fix red-eyes from electronic flash" msgstr "Fix red-eyes from electronic flash" #: f.widgets.cc:286 msgid "Make BW/color, negative/positive, sepia" msgstr "Make BW/color, negative/positive, sepia" #: f.widgets.cc:287 msgid "Reduce color depth (posterize)" msgstr "Reduce color depth (posterize)" #: f.widgets.cc:288 msgid "Shift/convert colors into other colors" msgstr "Shift/convert colors into other colors" #: f.widgets.cc:289 msgid "Adjust color intensity (saturation)" msgstr "Adjust color intensity (saturation)" #: f.widgets.cc:290 msgid "Adjust color using RGB or CMY colors" msgstr "Adjust color using RGB or CMY colors" #: f.widgets.cc:291 msgid "Adjust color using HSL colors" msgstr "Adjust color using HSL colors" #: f.widgets.cc:292 msgid "Adjust color in selected image areas" msgstr "Adjust color in selected image areas" #: f.widgets.cc:293 msgid "Match colors on one image with another" msgstr "Match colors on one image with another" #: f.widgets.cc:294 msgid "Reduce Chromatic Abberation" msgstr "Reduce Chromatic Abberation" #: f.widgets.cc:295 msgid "Remove unwanted objects" msgstr "Remove unwanted objects" #: f.widgets.cc:296 msgid "Add a brightness/color ramp across the image" msgstr "Add a brightness/color ramp across the image" #: f.widgets.cc:297 msgid "Remove dust spots from scanned slides" msgstr "Remove dust spots from scanned slides" #: f.widgets.cc:298 msgid "Smoothen edges with jaggies" msgstr "Smoothen edges with jaggies" #: f.widgets.cc:299 msgid "Erase known hot and dark pixels" msgstr "Erase known hot and dark pixels" #: f.widgets.cc:300 msgid "Paint image transparency using the mouse" msgstr "Paint image transparency using the mouse" #: f.widgets.cc:301 msgid "Add image transparency based on image attributes" msgstr "Add image transparency based on image attributes" #: f.widgets.cc:304 msgid "Remove curvature, esp. panoramas" msgstr "Remove curvature, esp. panoramas" #: f.widgets.cc:305 msgid "Straighten objects seen from an angle" msgstr "Straighten objects seen from an angle" #: f.widgets.cc:306 msgid "Distort image areas using the mouse" msgstr "Distort image areas using the mouse" #: f.widgets.cc:307 msgid "Unwarp closeup face photo to remove distortion" msgstr "Unwarp closeup face photo to remove distortion" #: f.widgets.cc:308 f.widgets.cc:309 f.widgets.cc:310 msgid "Distort the whole image using the mouse" msgstr "Distort the whole image using the mouse" #: f.widgets.cc:311 msgid "Flatten a photographed book page" msgstr "Flatten a photographed book pag" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "Make a spherical projection of an image" #: f.widgets.cc:313 msgid "Rescale image outside selected areas" msgstr "Rescale image outside selected areas" #: f.widgets.cc:314 msgid "Warp an image with a wave pattern" msgstr "Warp an image with a wave pattern" #: f.widgets.cc:315 msgid "Twist image centered at mouse position" msgstr "Twist image centered at mouse position" #: f.widgets.cc:318 msgid "Convert to simulated sketch" msgstr "Convert to simulated sketch" #: f.widgets.cc:319 msgid "Convert image into a cartoon drawing" msgstr "Convert image into a cartoon drawing" #: f.widgets.cc:320 msgid "Convert to line drawing (edge detection)" msgstr "Convert to line drawing (edge detection)" #: f.widgets.cc:321 msgid "Convert to solid color drawing" msgstr "Convert to solid color drawing" #: f.widgets.cc:322 msgid "Graduated Blur depending on contrast" msgstr "Graduated Blur depending on contrast" #: f.widgets.cc:323 msgid "Create an embossed or 3D appearance" msgstr "Create an embossed or 3D appearance" #: f.widgets.cc:324 msgid "Convert to square tiles" msgstr "Convert to square tiles" #: f.widgets.cc:325 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Convert to dots (Roy Lichtenstein effect)" #: f.widgets.cc:326 msgid "Convert into a simulated painting" msgstr "Convert into a simulated painting" #: f.widgets.cc:327 msgid "Change brightness or color radially" msgstr "Change brightness or color radially" #: f.widgets.cc:328 msgid "Add texture to an image" msgstr "Add texture to an image" #: f.widgets.cc:329 msgid "Tile image with a repeating pattern" msgstr "Tile image with a repeating pattern" #: f.widgets.cc:330 msgid "Create a mosaic with tiles made from all images" msgstr "Create a mosaic with tiles made from all images" #: f.widgets.cc:331 msgid "Process an image using a custom kernel" msgstr "Process an image using a custom kernel" #: f.widgets.cc:332 msgid "Blur an image in the direction of mouse drags" msgstr "Blur an image in the direction of mouse drags" #: f.widgets.cc:333 msgid "Increasing blur with distance from selected areas" msgstr "Increasing blur with distance from selected areas" #: f.widgets.cc:334 msgid "Change color hue using an algorithm" msgstr "Change color hue using an algorithm" #: f.widgets.cc:337 msgid "Combine bright/dark images for better detail" msgstr "Combine bright/dark images for better detail" #: f.widgets.cc:338 msgid "Combine near/far focus images for deeper focus" msgstr "Combine near/far focus images for deeper focus" #: f.widgets.cc:339 msgid "Combine images to erase passing people, etc." msgstr "Combine images to erase passing people, etc." #: f.widgets.cc:340 msgid "Combine noisy images into a low-noise image" msgstr "Combine noisy images into a low-noise image" #: f.widgets.cc:341 msgid "Combine images into a panorama" msgstr "Combine images into a panorama" #: f.widgets.cc:342 msgid "Combine images into a vertical panorama" msgstr "Combine images into a vertical panorama" #: f.widgets.cc:343 msgid "Combine images into a panorama (panorama tools)" msgstr "Combine images into a panorama (panorama tools)" #: f.widgets.cc:344 msgid "Arrange images and text in a layout (montage)" msgstr "Arrange images and text in a layout (montage)" #: f.widgets.cc:345 msgid "Combine images into a montage of images" msgstr "Combine images into a montage of images" #: f.widgets.cc:348 msgid "Rename/convert/resize/move multiple files" msgstr "Rename/convert/resize/move multiple files" #: f.widgets.cc:349 msgid "Upright multiple rotated image files" msgstr "Upright multiple rotated image files" #: f.widgets.cc:350 msgid "Delete or Trash multiple files" msgstr "Delete or Trash multiple files" #: f.widgets.cc:351 msgid "Convert camera RAW files using libraw or Raw Therapee" msgstr "Convert camera RAW files using libraw or Raw Therapee" #: f.widgets.cc:352 msgid "Build and run edit script files" msgstr "Build and run edit script files" #: f.widgets.cc:353 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Burn selected image files to DVD/BlueRay disc" #: f.widgets.cc:354 msgid "Search all image files and report duplicates" msgstr "Search all image files and report duplicates" #: f.widgets.cc:356 msgid "Export selected image files to a directory" msgstr "Export selected image files to a directory" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Add/remove tags for multiple images" #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Convert tag names for all images" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "change or shift photo dates/times" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Add/change/delete metadata for multiple images" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Report metadata for multiple images" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Add/revise geotags for multiple images" #: f.widgets.cc:363 msgid "Find all images for a location [date]" msgstr "Find all images for a location [date]" #: f.widgets.cc:364 msgid "Show image counts by month, select and report" msgstr "Show image counts by month, select and report" #: f.widgets.cc:365 msgid "Find images meeting select criteria" msgstr "Find images meeting select criteria" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Index new files and make thumbnails" #: f.widgets.cc:369 msgid "Change user preferences" msgstr "Change user preferences" #: f.widgets.cc:370 msgid "Change Keyboard Shortcut Keys" msgstr "Change Keyboard Shortcut Keys" #: f.widgets.cc:371 msgid "Show a brightness distribution graph" msgstr "Show a brightness distribution graph" #: f.widgets.cc:372 msgid "Show or revise grid lines" msgstr "Show or revise grid lines" #: f.widgets.cc:373 msgid "Change color of foreground lines" msgstr "Change color of foreground lines" #: f.widgets.cc:374 msgid "Show RGB colors at mouse click" msgstr "Show RGB colors at mouse click" #: f.widgets.cc:375 msgid "Magnify image around the mouse position" msgstr "Magnify image around the mouse position" #: f.widgets.cc:376 msgid "Highlight darkest and brightest pixels" msgstr "Highlight darkest and brightest pixels" #: f.widgets.cc:377 msgid "Chart to adjust monitor color" msgstr "Chart to adjust monitor color" #: f.widgets.cc:378 msgid "Chart to adjust monitor gamma" msgstr "Chart to adjust monitor gamma" #: f.widgets.cc:379 msgid "Change the GUI language" msgstr "Change the GUI language" #: f.widgets.cc:380 msgid "Report missing translations" msgstr "Report missing translations" #: f.widgets.cc:381 msgid "Convert to another color profile" msgstr "Convert to another color profile" #: f.widgets.cc:382 msgid "Calibrate printer colors" msgstr "Calibrate printer colors" #: f.widgets.cc:383 msgid "Completely uninstall AppImage package" msgstr "Completely uninstall AppImage package" #: f.widgets.cc:384 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memory and CPU (to terminal/logfile" #: f.widgets.cc:387 msgid "Quick Start mini-guide" msgstr "Quick Start mini-guide" #: f.widgets.cc:388 msgid "Read the user guide" msgstr "Read the user guide" #: f.widgets.cc:389 msgid "Recent user guide changes" msgstr "Recent user guide changes" #: f.widgets.cc:390 msgid "Technical installation notes" msgstr "Technical installation notes" #: f.widgets.cc:391 msgid "List updates by Fotoxx version" msgstr "List updates by Fotoxx version" #: f.widgets.cc:392 msgid "View the log file and error messages" msgstr "View the log file and error messages" #: f.widgets.cc:393 msgid "How to do Fotoxx translations" msgstr "How to do Fotoxx translations" #: f.widgets.cc:394 msgid "Show the Fotoxx web page" msgstr "Show the Fotoxx web page" #: f.widgets.cc:395 msgid "Version, license, contact, credits" msgstr "Version, license, contact, credits" #: f.widgets.cc:398 msgid "list all directories, click any for gallery view" msgstr "list all directories, click any for gallery view" #: f.widgets.cc:399 msgid "Set gallery from current image file" msgstr "Set gallery from current image file" #: f.widgets.cc:400 msgid "Organize images into albums" msgstr "Organize images into albums" #: f.widgets.cc:401 msgid "Update album files to latest version" msgstr "Update album files to latest version" #: f.widgets.cc:402 msgid "Replace album file with another file" msgstr "Replace album file with another file" #: f.widgets.cc:403 msgid "Start a slide show" msgstr "Start a slide show" #: f.widgets.cc:425 msgid "New Window" msgstr "New Window" #: f.widgets.cc:426 f.widgets.cc:610 fotoxx-18.01.cc:1821 msgid "Sync Gallery" msgstr "Sync Gallery" #: f.widgets.cc:427 msgid "Recently Seen Images" msgstr "Recently Seen Images" #: f.widgets.cc:428 msgid "Newest Images" msgstr "Newest Images" #: f.widgets.cc:433 msgid "View 360° Panorama" msgstr "View 360° Panorama" #: f.widgets.cc:434 msgid "New Blank Image" msgstr "New Blank Image" #: f.widgets.cc:437 f.widgets.cc:758 msgid "Copy/Move to Location" msgstr "Copy/Move to Location" #: f.widgets.cc:438 f.widgets.cc:759 msgid "Copy to Desktop" msgstr "Copy to Desktop" #: f.widgets.cc:439 f.widgets.cc:760 msgid "Copy to Clipboard" msgstr "Copy to Clipboard" #: f.widgets.cc:440 f.widgets.cc:764 msgid "Copy to Image Cache" msgstr "Copy to Image Cache" #: f.widgets.cc:441 f.widgets.cc:778 msgid "Show on Net Map" msgstr "Show on Net Map" #: f.widgets.cc:443 msgid "Print Image" msgstr "Print Image" #: f.widgets.cc:444 msgid "Print Calibrated Image" msgstr "Print Calibrated Image" #: f.widgets.cc:448 msgid "View Metadata (short)" msgstr "View Metadata (short)" #: f.widgets.cc:449 msgid "View Metadata (long)" msgstr "View Metadata (long)" #: f.widgets.cc:453 msgid "Show Captions on Image" msgstr "Show Captions on Image" #: f.widgets.cc:456 f.widgets.cc:776 msgid "Select Area" msgstr "Select Area" #: f.widgets.cc:457 msgid "Find Area Gap" msgstr "Find Area Gap" #: f.widgets.cc:459 msgid "Show Area" msgstr "Show Area" #: f.widgets.cc:460 msgid "Hide Area" msgstr "Hide Area" #: f.widgets.cc:461 msgid "Enable Area" msgstr "Enable Area" #: f.widgets.cc:462 msgid "Disable Area" msgstr "Disable Area" #: f.widgets.cc:463 msgid "Invert Area" msgstr "Invert Area" #: f.widgets.cc:464 msgid "Unselect Area" msgstr "Unselect Area" #: f.widgets.cc:466 msgid "Paste Area" msgstr "Paste Area" #: f.widgets.cc:467 msgid "Open Area File" msgstr "Open Area File" #: f.widgets.cc:474 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:475 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:477 f.widgets.cc:773 msgid "Edit Brightness" msgstr "Edit Brightness" #: f.widgets.cc:478 f.widgets.cc:775 msgid "Gradients" msgstr "Gradients" #: f.widgets.cc:482 msgid "Paint Image" msgstr "Paint Image" #: f.widgets.cc:489 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:494 msgid "Denoise" msgstr "Denoise" #: f.widgets.cc:495 msgid "Red Eyes" msgstr "Red Eyes" #: f.widgets.cc:497 msgid "Color Depth" msgstr "Color Depth" #: f.widgets.cc:500 msgid "Adjust RGB/CMY" msgstr "Adjust RGB/CMY" #: f.widgets.cc:501 msgid "Adjust HSL" msgstr "Adjust HSL" #: f.widgets.cc:502 msgid "Zonal Colors" msgstr "Zonal Colors" #: f.widgets.cc:503 msgid "Match Colors" msgstr "Match Colors" #: f.widgets.cc:509 msgid "Anti-Alias" msgstr "Anti-Alias" #: f.widgets.cc:516 msgid "Fix Perspective" msgstr "Fix Perspective" #: f.widgets.cc:526 msgid "Twist Image" msgstr "Twist Image" #: f.widgets.cc:529 msgid "Sketch" msgstr "Sketch" #: f.widgets.cc:530 msgid "Cartoon" msgstr "Cartoon" #: f.widgets.cc:534 msgid "Embossing" msgstr "Embossing" #: f.widgets.cc:536 msgid "Dots" msgstr "Dots" #: f.widgets.cc:537 msgid "Painting" msgstr "Painting" #: f.widgets.cc:539 msgid "Texture" msgstr "Texture" #: f.widgets.cc:541 msgid "Mosaic" msgstr "Mosaic" #: f.widgets.cc:548 msgid "High Dynamic Range" msgstr "High Dynamic Range" #: f.widgets.cc:549 msgid "High Depth of Field" msgstr "High Depth of Field" #: f.widgets.cc:550 msgid "Stack/Paint" msgstr "Stack/Paint" #: f.widgets.cc:551 msgid "Stack/Noise" msgstr "Stack/Noise" #: f.widgets.cc:552 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:553 msgid "Vertical Panorama" msgstr "Vertical Panorama" #: f.widgets.cc:554 msgid "PT Panorama" msgstr "PT Panorama" #: f.widgets.cc:555 msgid "Montage" msgstr "Montage" #: f.widgets.cc:556 msgid "Mashup" msgstr "Mashup" #: f.widgets.cc:562 msgid "Batch RAW" msgstr "Batch RAW" #: f.widgets.cc:563 msgid "Script Files" msgstr "Script Files" #: f.widgets.cc:566 msgid "Export File List" msgstr "Export File List" #: f.widgets.cc:574 msgid "Image Locations/Dates" msgstr "Image Locations/Dates" #: f.widgets.cc:575 msgid "Image Timeline" msgstr "Image Timeline" #: f.widgets.cc:576 msgid "Search Images" msgstr "Search Images" #: f.widgets.cc:582 msgid "Brightness Graph" msgstr "Brightness Graph" #: f.widgets.cc:587 msgid "Dark/Bright Pixels" msgstr "Dark/Bright Pixels" #: f.widgets.cc:588 msgid "Monitor Color" msgstr "Monitor Color" #: f.widgets.cc:590 msgid "Change Language" msgstr "Change Language" #: f.widgets.cc:591 msgid "Missing Translations" msgstr "Missing Translations" #: f.widgets.cc:592 msgid "Color Profile" msgstr "Color Profile" #: f.widgets.cc:594 msgid "Uninstall AppImage" msgstr "Uninstall AppImage" #: f.widgets.cc:611 msgid "All Directories" msgstr "All Directories" #: f.widgets.cc:612 msgid "Bookmarks" msgstr "Bookmarks" #: f.widgets.cc:621 f.widgets.cc:646 f.widgets.cc:670 f.widgets.cc:683 msgid "Gallery" msgstr "Gallery" #: f.widgets.cc:622 f.widgets.cc:647 f.widgets.cc:671 f.widgets.cc:684 msgid "World Maps" msgstr "World Maps" #: f.widgets.cc:623 f.widgets.cc:648 f.widgets.cc:672 f.widgets.cc:685 msgid "Net Maps" msgstr "Net Maps" #: f.widgets.cc:625 f.widgets.cc:650 f.widgets.cc:1066 msgid "Favorites" msgstr "Favorites" #: f.widgets.cc:627 msgid "Save File" msgstr "Save File" #: f.widgets.cc:628 msgid "Prev/Next" msgstr "Prev/Next" #: f.widgets.cc:630 msgid "Areas" msgstr "Areas" #: f.widgets.cc:631 msgid "Undo/Redo" msgstr "Undo/Redo" #: f.widgets.cc:632 fotoxx.h:1208 msgid "Edit" msgstr "Edit" #: f.widgets.cc:633 msgid "Repair" msgstr "Repair" #: f.widgets.cc:635 msgid "Effects" msgstr "Effects" #: f.widgets.cc:636 f.widgets.cc:660 msgid "Combine" msgstr "Combine" #: f.widgets.cc:637 f.widgets.cc:661 msgid "Process" msgstr "Process" #: f.widgets.cc:638 f.widgets.cc:662 msgid "Tools" msgstr "Tools" #: f.widgets.cc:674 msgid "Choose Map" msgstr "Choose Map" #: f.widgets.cc:675 f.widgets.cc:688 msgid "Map Markers" msgstr "Map Markers" #: f.widgets.cc:687 msgid "Map Source" msgstr "Map Source" #: f.widgets.cc:689 msgid "Map Locations" msgstr "Map Locations" #: f.widgets.cc:753 msgid "Popup Image" msgstr "Popup Image" #: f.widgets.cc:757 fotoxx.h:1265 msgid "Rename" msgstr "Rename" #: f.widgets.cc:761 msgid "Remove from Album" msgstr "Remove from Album" #: f.widgets.cc:763 msgid "Cut to Image Cache" msgstr "Cut to Image Cache" #: f.widgets.cc:765 msgid "Paste Image Cache Here (clear)" msgstr "Paste Image Cache Here (clear)" #: f.widgets.cc:766 msgid "Paste Image Cache Here (keep)" msgstr "Paste Image Cache Here (keep)" #: f.widgets.cc:779 msgid "Delete/Trash ..." msgstr "Delete/Trash ..." #: f.widgets.cc:1092 msgid "menu name is invalid" msgstr "menu name is invalid" #: fotoxx-18.01.cc:434 msgid "Please install missing programs:" msgstr "Please install missing programs:" #: fotoxx-18.01.cc:706 #, c-format msgid "Kill active dialog? %s" msgstr "Kill active dialog? %s" #: fotoxx-18.01.cc:801 msgid "(reduced)" msgstr "(reduced" #: fotoxx-18.01.cc:802 msgid "area active" msgstr "area active" #: fotoxx-18.01.cc:803 msgid "dialog open" msgstr "dialog open" #: fotoxx-18.01.cc:804 msgid "blocked" msgstr "blocked" #: fotoxx-18.01.cc:866 msgid "edits" msgstr "edits" #: fotoxx-18.01.cc:1801 msgid "File View" msgstr "File View" #: fotoxx-18.01.cc:1806 msgid "Gallery View" msgstr "Gallery View" #: fotoxx-18.01.cc:1811 msgid "World Map View" msgstr "World Map View" #: fotoxx-18.01.cc:1816 msgid "Net Map View" msgstr "Net Map View" #: fotoxx-18.01.cc:1831 msgid "Show Hidden" msgstr "Show Hidden" #: fotoxx-18.01.cc:3023 msgid "Exceed 50 anchor points" msgstr "Exceed 50 anchor points" #: fotoxx-18.01.cc:3250 msgid "load curve from a file" msgstr "load curve from a file" #: fotoxx-18.01.cc:3316 msgid "curve file is invalid" msgstr "curve file is invalid" #: fotoxx-18.01.cc:3330 msgid "save curve to a file" msgstr "save curve to a file" #: fotoxx-18.01.cc:3399 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "File cannot be edited \n" " %s" #: fotoxx-18.01.cc:3412 msgid "Too many edits, please save image" msgstr "Too many edits, please save image" #: fotoxx-18.01.cc:3417 msgid "this function cannot be scripted" msgstr "this function cannot be scripted" #: fotoxx-18.01.cc:3434 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Select area not active.\n" "Continue?" #: fotoxx-18.01.cc:3749 msgid "file data does not fit dialog" msgstr "file data does not fit dialog" #: fotoxx-18.01.cc:3759 msgid "Save settings to a file" msgstr "Save settings to a file" #: fotoxx-18.01.cc:4140 msgid "This action will discard changes to current image" msgstr "This action will discard changes to current image" #: fotoxx-18.01.cc:4141 msgid "prior function still active" msgstr "prior function still active" #: fotoxx-18.01.cc:4142 fotoxx.h:1229 msgid "Keep" msgstr "Keep" #: fotoxx-18.01.cc:4143 msgid "Discard" msgstr "Discard" #: fotoxx.h:1177 msgid "Add" msgstr "Add" #: fotoxx.h:1178 msgid "Add All" msgstr "Add All" #: fotoxx.h:1180 msgid "Amount" msgstr "Amount" #: fotoxx.h:1181 msgid "Angle" msgstr "Angle" #: fotoxx.h:1182 zfuncs.cc:6782 msgid "Apply" msgstr "Apply" #: fotoxx.h:1183 msgid "Auto" msgstr "Auto" #: fotoxx.h:1184 msgid "Black" msgstr "Black" #: fotoxx.h:1185 msgid "Blend Width" msgstr "Blend Width" #: fotoxx.h:1187 zfuncs.cc:11368 msgid "Bottom" msgstr "Bottom" #: fotoxx.h:1189 zfuncs.cc:6796 msgid "Browse" msgstr "Browse" #: fotoxx.h:1190 zfuncs.cc:6782 msgid "Cancel" msgstr "Cancel" #: fotoxx.h:1191 msgid "Center" msgstr "Center" #: fotoxx.h:1192 msgid "Choose" msgstr "Choose" #: fotoxx.h:1193 msgid "Clear" msgstr "Clear" #: fotoxx.h:1194 msgid "Close" msgstr "Close" #: fotoxx.h:1195 msgid "Color" msgstr "Color" #: fotoxx.h:1196 msgid "COMPLETED" msgstr "COMPLETED" #: fotoxx.h:1197 msgid "continue" msgstr "continue" #: fotoxx.h:1199 msgid "Copy" msgstr "Copy" #: fotoxx.h:1200 msgid "Create" msgstr "Create" #: fotoxx.h:1201 msgid "Curve File:" msgstr "Curve File:" #: fotoxx.h:1202 msgid "Cut" msgstr "Cut" #: fotoxx.h:1203 msgid "Deband" msgstr "Deband" #: fotoxx.h:1204 zfuncs.cc:6782 msgid "Delete" msgstr "Delete" #: fotoxx.h:1205 msgid "Disable" msgstr "Disable" #: fotoxx.h:1206 msgid "Done" msgstr "Done" #: fotoxx.h:1207 msgid "edge" msgstr "edge" #: fotoxx.h:1209 msgid "Enable" msgstr "Enable" #: fotoxx.h:1210 msgid "Erase" msgstr "Erase" #: fotoxx.h:1211 msgid "Fetch" msgstr "Fetch" #: fotoxx.h:1212 msgid "output file already exists" msgstr "output file already exists" #: fotoxx.h:1213 msgid "file not found" msgstr "file not found" #: fotoxx.h:1214 #, c-format msgid "file not found: %s" msgstr "file not found: %s" #: fotoxx.h:1215 #, c-format msgid "%d image files selected" msgstr "%d image files selected" #: fotoxx.h:1216 msgid "Find" msgstr "Find" #: fotoxx.h:1217 msgid "Finish" msgstr "Finish" #: fotoxx.h:1219 msgid "Font" msgstr "Font" #: fotoxx.h:1221 msgid "Grid" msgstr "Grid" #: fotoxx.h:1222 zfuncs.cc:11398 msgid "Height" msgstr "Height" #: fotoxx.h:1224 msgid "Hide" msgstr "Hide" #: fotoxx.h:1225 zfuncs.cc:11390 msgid "Image" msgstr "Image" #: fotoxx.h:1226 msgid "Images" msgstr "Images" #: fotoxx.h:1227 msgid "Insert" msgstr "Insert" #: fotoxx.h:1228 msgid "Invert" msgstr "Invert" #: fotoxx.h:1230 zfuncs.cc:11372 msgid "Left" msgstr "Left" #: fotoxx.h:1231 msgid "limit" msgstr "limit" #: fotoxx.h:1232 msgid "Load" msgstr "Load" #: fotoxx.h:1233 msgid "Make" msgstr "Make" #: fotoxx.h:1235 msgid "Map" msgstr "Map" #: fotoxx.h:1236 msgid "Match Level:" msgstr "Match Level:" #: fotoxx.h:1237 msgid "Max" msgstr "Max" #: fotoxx.h:1238 msgid "mouse radius" msgstr "mouse radius" #: fotoxx.h:1239 msgid "Negative" msgstr "Negative" #: fotoxx.h:1240 msgid "New" msgstr "New" #: fotoxx.h:1241 msgid "Next" msgstr "Next" #: fotoxx.h:1242 zfuncs.cc:10365 msgid "No" msgstr "No" #: fotoxx.h:1243 msgid "no images" msgstr "no images" #: fotoxx.h:1244 msgid "" "image index disabled \n" " Enable?" msgstr "" "image index disabled \n" " Enable?" #: fotoxx.h:1245 msgid "no image files selected" msgstr "no image files selected" #: fotoxx.h:1246 msgid "None" msgstr "None" #: fotoxx.h:1247 msgid "no selection" msgstr "no selection" #: fotoxx.h:1248 msgid "OK" msgstr "OK" #: fotoxx.h:1249 msgid "image index not updated" msgstr "image index not updated" #: fotoxx.h:1251 msgid "Paint Radius" msgstr "Paint Radius" #: fotoxx.h:1252 msgid "Paste" msgstr "Paste" #: fotoxx.h:1253 msgid "Pause" msgstr "Pause" #: fotoxx.h:1254 msgid "Percent" msgstr "Percent" #: fotoxx.h:1256 msgid "Presets" msgstr "Presets" #: fotoxx.h:1257 msgid "Prev" msgstr "Prev" #: fotoxx.h:1258 msgid "Proceed" msgstr "Proceed" #: fotoxx.h:1260 msgid "range" msgstr "range" #: fotoxx.h:1262 msgid "Redo" msgstr "Redo" #: fotoxx.h:1263 msgid "Reduce" msgstr "Reduce" #: fotoxx.h:1264 msgid "Remove" msgstr "Remove" #: fotoxx.h:1266 msgid "Replace" msgstr "Replace" #: fotoxx.h:1267 msgid "Reserved" msgstr "Reserved" #: fotoxx.h:1268 msgid "Reset" msgstr "Reset" #: fotoxx.h:1269 zfuncs.cc:11376 msgid "Right" msgstr "Right" #: fotoxx.h:1270 msgid "Rotate" msgstr "Rotate" #: fotoxx.h:1271 msgid "Run" msgstr "Run" #: fotoxx.h:1272 msgid "Save" msgstr "Save" #: fotoxx.h:1273 msgid "Search" msgstr "Search" #: fotoxx.h:1274 msgid "Seconds" msgstr "Seconds" #: fotoxx.h:1275 msgid "Select" msgstr "Select" #: fotoxx.h:1277 msgid "Show" msgstr "Show" #: fotoxx.h:1278 msgid "Size" msgstr "Size" #: fotoxx.h:1279 msgid "Start" msgstr "Start" #: fotoxx.h:1280 msgid "Stop" msgstr "Stop" #: fotoxx.h:1281 msgid "Strength" msgstr "Strength" #: fotoxx.h:1282 msgid "Threshold" msgstr "Threshold" #: fotoxx.h:1283 #, c-format msgid "exceed %d files" msgstr "exceed %d files" #: fotoxx.h:1284 zfuncs.cc:11364 msgid "Top" msgstr "Top" #: fotoxx.h:1286 msgid "Trash" msgstr "Trash" #: fotoxx.h:1287 msgid "Trim" msgstr "Trim" #: fotoxx.h:1288 msgid "Undo All" msgstr "Undo All" #: fotoxx.h:1289 msgid "Undo Last" msgstr "Undo Last" #: fotoxx.h:1290 msgid "Undo" msgstr "Undo" #: fotoxx.h:1291 msgid "Unfinish" msgstr "Unfinish" #: fotoxx.h:1292 msgid "Unselect" msgstr "Unselect" #: fotoxx.h:1293 msgid "View" msgstr "View" #: fotoxx.h:1294 msgid "Web" msgstr "Web" #: fotoxx.h:1295 msgid "White" msgstr "White" #: fotoxx.h:1296 zfuncs.cc:11394 msgid "Width" msgstr "Width" #: fotoxx.h:1297 msgid "x-offset" msgstr "x-offset" #: fotoxx.h:1298 msgid "y-offset" msgstr "y-offset" #: fotoxx.h:1299 zfuncs.cc:10365 msgid "Yes" msgstr "Yes" #: zfuncs.cc:305 msgid "LOW MEMORY - performance may be poor" msgstr "LOW MEMORY - performance may be poor" #: zfuncs.cc:1722 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "create directory? \n" " %s" #: zfuncs.cc:4878 msgid "user guide not found" msgstr "user guide not found" #: zfuncs.cc:5742 #, c-format msgid "cannot open file %s" msgstr "cannot open file %s" #: zfuncs.cc:5765 msgid "save text to file" msgstr "save text to file" #: zfuncs.cc:6786 msgid "menu text" msgstr "menu text" #: zfuncs.cc:6787 msgid "menu func" msgstr "menu func" #: zfuncs.cc:6788 msgid "menu icon" msgstr "menu icon" #: zfuncs.cc:6789 msgid "icon size" msgstr "icon size" #: zfuncs.cc:6792 msgid "Bold" msgstr "Bold" #: zfuncs.cc:6799 msgid "close window" msgstr "close window" #: zfuncs.cc:10488 zfuncs.cc:11021 zfuncs.cc:11351 msgid "cancel" msgstr "cancel" #: zfuncs.cc:10982 msgid "choose file" msgstr "choose file" #: zfuncs.cc:10987 msgid "choose files" msgstr "choose files" #: zfuncs.cc:10992 msgid "save" msgstr "save" #: zfuncs.cc:10998 msgid "choose folder" msgstr "choose folder" #: zfuncs.cc:11003 msgid "choose folders" msgstr "choose folders" #: zfuncs.cc:11008 msgid "create folder" msgstr "create folder" #: zfuncs.cc:11015 msgid "hidden" msgstr "hidden" #: zfuncs.cc:11351 msgid "done" msgstr "done" #: zfuncs.cc:11381 msgid "image scale" msgstr "image scale" #: zfuncs.cc:11383 msgid "percent" msgstr "percent" #~ msgid "Retinex" #~ msgstr "Retinex" fotoxx-18.01.1/locales/translate-de.po0000644000175000017500000036622513222767271016245 0ustar micomico# German translations for fotoxx package. # Copyright (C) 2018 THE fotoxx COPYRIGHT HOLDER # This file is distributed under the same license as the fotoxx package. # # msgid "" msgstr "" "Project-Id-Version: fotoxx 6.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-12-25 13:41+0100\n" "PO-Revision-Date: 2009-03-06 09:57+0100\n" "Language-Team: German\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: f.albums.cc:89 msgid "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." msgstr "" "Album-Thumbnail rechts klicken, um zu Cache \n" "ausschneiden/kopieren, von Cache einfügen, entfernen." #: f.albums.cc:91 msgid "" "Right-click left/right side of album \n" "thumbnail to insert cached images \n" "before/after the thumbnail." msgstr "" "Linke oder rechte Seite eines Album-Thumbnails \n" "anklicken, um Bildercache vor oder nach der \n" "Thumbnail einzufügen." #: f.albums.cc:94 msgid "Drag album thumbnail to new position." msgstr "Album-Thumbnail zu neuer Stelle ziehen." #: f.albums.cc:128 f.widgets.cc:613 msgid "Manage Albums" msgstr "Alben verwalten" #: f.albums.cc:140 f.albums.cc:275 msgid "Create or replace an album" msgstr "Album erstellen oder ersetzen" #: f.albums.cc:142 msgid "Album to view or edit" msgstr "Album zum Ansehen oder Bearbeiten" #: f.albums.cc:144 msgid "Select images, add to cache" msgstr "Bilder auswählen, ins Bildercache zufügen" #: f.albums.cc:146 msgid "Clear image cache" msgstr "Bildercache löschen" #: f.albums.cc:148 f.albums.cc:170 msgid "Delete an album" msgstr "Album löschen" #: f.albums.cc:169 msgid "Choose Album" msgstr "Album wählen" #: f.albums.cc:171 #, c-format msgid "Image cache has %d images" msgstr "Bildercache hat %d Bilder" #: f.albums.cc:172 msgid "cache added to empty album" msgstr "Cache wurde zu leerem Album zugefügt" #: f.albums.cc:228 #, c-format msgid "delete %s ?" msgstr "%s löschen?" #: f.albums.cc:259 #, c-format msgid "fill from image cache (%d images)" msgstr "Vom Bild-Cache auffüllen (%d Bilder)" #: f.albums.cc:277 f.albums.cc:310 msgid "Album Name" msgstr "Album-Name" #: f.albums.cc:283 msgid "make an initially empty album" msgstr "Ein anfänglich leeres Album erstellen" #: f.albums.cc:285 msgid "fill from current gallery" msgstr "Von der aktuellen Galerie auffüllen" #: f.albums.cc:324 msgid "enter an album name" msgstr "Album-Name eingeben" #: f.albums.cc:352 msgid "new album created" msgstr "Neues Album wurde erstellt" #: f.albums.cc:368 msgid "gallery is empty" msgstr "Galerie ist leer" #: f.albums.cc:876 f.widgets.cc:614 msgid "Update Album Files" msgstr "Album Dateien aktualisieren" #: f.albums.cc:878 f.albums.cc:938 f.albums.cc:1007 f.albums.cc:1147 msgid "Choose Albums" msgstr "Alben auswählen" #: f.albums.cc:1005 f.widgets.cc:615 f.widgets.cc:762 msgid "Replace Album File" msgstr "Album Datei ersetzen" #: f.albums.cc:1009 msgid "All Albums" msgstr "Alle Alben" #: f.albums.cc:1012 msgid "old file" msgstr "alte Datei" #: f.albums.cc:1016 msgid "new file" msgstr "neue Datei" #: f.albums.cc:1020 msgid "replace old" msgstr "alte ersetzen" #: f.albums.cc:1021 msgid "add after old" msgstr "nach alter zufügen" #: f.albums.cc:1081 msgid "no albums chosen" msgstr "keine Alben gewählt" #: f.albums.cc:1162 msgid "ALL albums chosen" msgstr "Alle Alben gewählt" #: f.albums.cc:1346 msgid "instant" msgstr "Sofortig" #: f.albums.cc:1347 msgid "fade-in" msgstr "Einblenden" #: f.albums.cc:1348 msgid "roll-right" msgstr "Nach rechts rollend" #: f.albums.cc:1349 msgid "roll-down" msgstr "Nach unten rollend" #: f.albums.cc:1350 msgid "venetian" msgstr "Jalousie" #: f.albums.cc:1351 msgid "grate" msgstr "Gitter" #: f.albums.cc:1352 msgid "rectangle" msgstr "Rechteck" #: f.albums.cc:1353 msgid "implode" msgstr "Implodieren" #: f.albums.cc:1354 msgid "explode" msgstr "Explodieren" #: f.albums.cc:1355 msgid "radar" msgstr "Radar" #: f.albums.cc:1356 msgid "spiral" msgstr "Spirale" #: f.albums.cc:1357 msgid "Japan-fan" msgstr "Japan-Fächer" #: f.albums.cc:1358 msgid "jaws" msgstr "Haifisch" #: f.albums.cc:1359 msgid "ellipse" msgstr "Ellipse" #: f.albums.cc:1360 msgid "raindrops" msgstr "Regentropfen" #: f.albums.cc:1361 msgid "doubledoor" msgstr "Doppeltür" #: f.albums.cc:1362 msgid "rotate" msgstr "drehen" #: f.albums.cc:1363 msgid "fallover" msgstr "Umkippen" #: f.albums.cc:1364 msgid "spheroid" msgstr "Sphäroid" #: f.albums.cc:1365 msgid "turn-page" msgstr "umblättern" #: f.albums.cc:1366 msgid "french-door" msgstr "Französische Tur" #: f.albums.cc:1367 msgid "turn-cube" msgstr "Dreh-Würfel" #: f.albums.cc:1368 msgid "windmill" msgstr "Windmühle" #: f.albums.cc:1369 msgid "pixelize" msgstr "verpixeln" #: f.albums.cc:1370 msgid "twist" msgstr "verdrehen" #: f.albums.cc:1372 msgid "squish" msgstr "quetschen" #: f.albums.cc:1399 f.widgets.cc:616 msgid "Slide Show" msgstr "Dia-Show" #: f.albums.cc:1405 msgid "use gallery" msgstr "Galerie verwenden" #: f.albums.cc:1411 msgid "Clip Limit" msgstr "Kappenlimit" #: f.albums.cc:1415 msgid "Music File" msgstr "Musikdatei" #: f.albums.cc:1420 msgid "Full Screen" msgstr "Fenster maximieren" #: f.albums.cc:1421 msgid "Auto-replay" msgstr "Auto-Wiederholung" #: f.albums.cc:1424 msgid "Customize:" msgstr "Anpassen:" #: f.albums.cc:1425 msgid "transitions" msgstr "Übergänge" #: f.albums.cc:1426 msgid "image files" msgstr "Bilddateien" #: f.albums.cc:1427 msgid "KB controls" msgstr "KB Regler" #: f.albums.cc:1441 f.albums.cc:1509 f.albums.cc:1732 f.albums.cc:2007 #: f.albums.cc:2292 f.albums.cc:2309 f.albums.cc:2526 msgid "invalid album" msgstr "Ungültiges Album" #: f.albums.cc:1540 msgid "open album" msgstr "Album öffnen" #: f.albums.cc:1556 msgid "Select music file" msgstr "Musikdatei auswählen" #: f.albums.cc:1587 #, c-format msgid "%d images" msgstr "%d Bilder" #: f.albums.cc:1614 msgid "arrow keys show previous or next image instantly" msgstr "Pfeiltasten zeigen vorheriges/nächstes Bild sofort" #: f.albums.cc:1632 msgid "Keyboard Preferences" msgstr "Tastatur Präferenzen" #: f.albums.cc:1635 msgid "blank or unblank window" msgstr "Fenster ausblenden oder zurückbringen" #: f.albums.cc:1638 msgid "show next image, with transition" msgstr "nächstes Bild zeigen, mit Übergang" #: f.albums.cc:1641 msgid "pause or resume slide show" msgstr "Slideshow pausieren oder fortsetzen" #: f.albums.cc:1644 msgid "magnify image (loupe tool)" msgstr "Bild vergrößern (Lupe-Werkzeug)" #: f.albums.cc:1711 msgid "random sequence" msgstr "zufällige Folge" #: f.albums.cc:1736 msgid "Transition Preferences" msgstr "Übergangs Präferenzen" #: f.albums.cc:1738 msgid "Transitions File" msgstr "Übergänge-Datei" #: f.albums.cc:1756 f.albums.cc:1760 msgid "transition" msgstr "Übergang" #: f.albums.cc:1757 f.albums.cc:1761 msgid "use" msgstr "benutzen" #: f.albums.cc:1758 f.albums.cc:1762 msgid "slow" msgstr "bremsen" #: f.albums.cc:1759 f.albums.cc:1763 msgid "pref" msgstr "Vorzug" #: f.albums.cc:1862 f.albums.cc:1901 msgid "invalid file" msgstr "ungültige Datei" #: f.albums.cc:1954 f.albums.cc:2511 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "Dateiformat Fehler: \n" " %s" #: f.albums.cc:2013 msgid "Image Preferences" msgstr "Bild Präferenzen" #: f.albums.cc:2017 f.edit.cc:1405 f.file.cc:1557 f.file.cc:1863 f.meta.cc:820 #: f.meta.cc:1588 f.meta.cc:1780 msgid "Image File:" msgstr "Bilddatei:" #: f.albums.cc:2021 msgid "Play tone when image shows" msgstr "Klang spielen wenn Bild erscheint" #: f.albums.cc:2024 msgid "Show image caption" msgstr "Bild-Titel zeigen" #: f.albums.cc:2029 msgid "Show image comments" msgstr "Bild-Kommentare zeigen" #: f.albums.cc:2034 msgid "Wait before zoom" msgstr "Warten vor Zoom" #: f.albums.cc:2039 msgid "Zoom type:" msgstr "Zoom Art" #: f.albums.cc:2041 msgid "zoom-in" msgstr "zoom-hinein" #: f.albums.cc:2042 msgid "zoom-out" msgstr "zoom-heraus" #: f.albums.cc:2045 msgid "Zoom size (x)" msgstr "Zoomgröße" #: f.albums.cc:2048 msgid "Steps" msgstr "Schritte" #: f.albums.cc:2052 msgid "Zoom Center" msgstr "Zoom-Mitte" #: f.albums.cc:2056 msgid "Wait after zoom" msgstr "Warten nach Zoom" #: f.albums.cc:2061 msgid "Transition to next image" msgstr "Übergang zum nächsten Bild" #: f.albums.cc:2064 f.albums.cc:2167 msgid "next" msgstr "nächst" #: f.albums.cc:2157 msgid "click on thumbnail to set zoom center" msgstr "Thumbnail anklicken um Zoom-Mitte zu setzen" #: f.area.cc:81 msgid "Select Area for Edits" msgstr "Ausschnitt für Bearbeitung auswählen" #: f.area.cc:82 f.area.cc:1855 f.edit.cc:9094 msgid "Press F1 for help" msgstr "F1 für Hilfe drucken" #: f.area.cc:91 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Ausschnitt nicht unterstützt \n" "von dieser Bearbeitungsfunktion" #: f.area.cc:120 msgid "select rectangle" msgstr "Rechteck wählen" #: f.area.cc:121 msgid "select ellipse" msgstr "Ellipse wählen" #: f.area.cc:124 msgid "freehand draw" msgstr "Freihändig zeichnen" #: f.area.cc:125 msgid "follow edge" msgstr "Rand folgen" #: f.area.cc:126 msgid "draw/replace" msgstr "Zeichnen/ersetzen" #: f.area.cc:129 msgid "select area within mouse circle" msgstr "Ausschnitt innerhalb Mauskreis selektieren" #: f.area.cc:132 msgid "select one matching color within mouse circle:" msgstr "Eine übereinstimmende Farbe im Mauskreis selektieren" #: f.area.cc:134 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "Erst Ankreuzfeld selektieren, dann \n" "Bild shift+anklicken zum Farbe feststellen" #: f.area.cc:138 msgid "select all matching colors within mouse circle" msgstr "Alle übereinstimmenden Farben im Mauskreis selektieren" #: f.area.cc:144 msgid "match level %" msgstr "Farbübereinstimmung %" #: f.area.cc:147 msgid "search range" msgstr "Suchentfernung" #: f.area.cc:151 msgid "Area Edge Blend Width" msgstr "Ausschnitt Rand Mischbreite" #: f.area.cc:154 msgid "Edge Creep" msgstr "Rand-Kriech" #: f.area.cc:159 msgid "Line Color:" msgstr "Linienfarbe:" #: f.area.cc:179 f.area.cc:180 msgid "area edits fade away within edge distance" msgstr "Ausschnitt-Änderungen klingen innerhalb Randweite ab" #: f.area.cc:351 f.area.cc:517 #, c-format msgid "exceed %d edits" msgstr "%d Edits überschritten" #: f.area.cc:1333 msgid "" "Click one time inside each enclosed area. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Jeden umschlossenen Ausschnitt einmal innen anklicken. \n" "Mögliche Lücken im Rand werden gefunden. \n" "Für Hilfe F1 drücken." #: f.area.cc:1360 msgid "finish area" msgstr "Ausschnitt fertigstellen" #: f.area.cc:1389 f.area.cc:1534 #, c-format msgid "found %d pixels" msgstr "%d Pixels gefunden" #: f.area.cc:1639 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Irgendeine Stelle am Ausschnitt-Umrand anklicken \n" "Mögliche Lücken im Umrand werden gefunden. \n" "Für Hilfe, F1 drücken." #: f.area.cc:1658 msgid "find outline gap" msgstr "Lücke in Umrandung finden" #: f.area.cc:1748 msgid "cannot find area outline" msgstr "kann Ausschnitt-Rand nicht finden" #: f.area.cc:1854 f.widgets.cc:458 msgid "Select Hairy" msgstr "Flockig selektieren" #: f.area.cc:1862 msgid "select the area first" msgstr "erst den Ausschnitt selektieren" #: f.area.cc:1867 f.area.cc:2489 f.area.cc:2507 f.area.cc:2527 f.area.cc:2851 #: f.area.cc:2971 msgid "the area is not finished" msgstr "Der Ausschnitt ist nicht fertig" #: f.area.cc:1940 msgid "select" msgstr "selektieren" #: f.area.cc:1946 msgid "deselect" msgstr "deselektieren" #: f.area.cc:2616 msgid "Edge calculation in progress" msgstr "Rand Berechnung in Arbeit" #: f.area.cc:2625 msgid "Area Edge Calc" msgstr "Ausschnitt Randberechnung" #: f.area.cc:2957 f.area.cc:3029 f.widgets.cc:465 msgid "Copy Area" msgstr "Ausschnitt kopieren" #: f.area.cc:2960 f.widgets.cc:468 msgid "Save Area File" msgstr "Ausschnitt in Datei speichern" #: f.area.cc:3035 msgid "save area as a PNG file" msgstr "Ausschnitt als PNG Datei speichern" #: f.area.cc:3159 msgid "position with mouse click/drag" msgstr "Mit Maus klicken/ziehen positionieren" #: f.area.cc:3219 msgid "Paste Image" msgstr "Bild einfügen" #: f.area.cc:3224 f.process.cc:1311 msgid "resize" msgstr "Skalieren" #: f.combine.cc:169 f.combine.cc:815 f.combine.cc:1416 f.combine.cc:2039 msgid "Select 2 to 9 files" msgstr "2-9 Dateien auswählen" #: f.combine.cc:191 f.combine.cc:837 f.combine.cc:1438 f.combine.cc:2061 msgid "Images are not all the same size" msgstr "Bilder sind nicht gleich gross" #: f.combine.cc:550 msgid "Adjust Image Contributions" msgstr "Bild Beiträge einstellen" #: f.combine.cc:554 msgid "dark pixels" msgstr "dunkele Pixel" #: f.combine.cc:556 msgid "light pixels" msgstr "helle Pixel" #: f.combine.cc:558 msgid "file:" msgstr "Datei:" #: f.combine.cc:1018 msgid "Paint and Warp Image" msgstr "Bild malen und krümmen" #: f.combine.cc:1026 f.edit.cc:5686 msgid "paint" msgstr "malen" #: f.combine.cc:1027 msgid "warp" msgstr "Krümmen" #: f.combine.cc:1623 msgid "Select and Paint Image" msgstr "Bild auswählen und malen" #: f.combine.cc:1631 msgid "Transient Objects" msgstr "Vorbeigehende Objekte" #: f.combine.cc:2232 msgid "Adjust Pixel Composition" msgstr "Pixel Zusammensetzung einstellen" #: f.combine.cc:2234 msgid "use average" msgstr "Durchschnitt benutzen" #: f.combine.cc:2235 msgid "use median" msgstr "Mittlewert benutzen" #: f.combine.cc:2237 msgid "omit low pixel" msgstr "Niedrigste Pixel weglassen" #: f.combine.cc:2238 msgid "omit high pixel" msgstr "Höchste Pixel weglassen" #: f.combine.cc:2502 f.combine.cc:3653 msgid "Select 2 to 4 files" msgstr "2-4 Dateien auswählen" #: f.combine.cc:2573 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Bilder zur Grobabstimmung mit der Maus ziehen.\n" "Zum Drehen unteren Rand links/rechts ziehen" #: f.combine.cc:2575 f.combine.cc:3726 msgid "no curve (scanned image)" msgstr "Keine Kurve (gescanntes Bild)" #: f.combine.cc:2576 f.combine.cc:3727 msgid "Search for lens mm" msgstr "Suche für Lense Brennweite" #: f.combine.cc:2577 f.combine.cc:3728 msgid "Save lens mm → image EXIF" msgstr "Lens mm in Bild EXIF speichern" #: f.combine.cc:2639 f.combine.cc:3790 msgid "Pre-align Images" msgstr "Bilder grob anpassen" #: f.combine.cc:2643 f.combine.cc:3794 msgid "lens mm" msgstr "Objektiv-Brennweite mm" #: f.combine.cc:2648 f.combine.cc:3799 msgid "no auto warp" msgstr "Auto-Warp ausschalten" #: f.combine.cc:2650 f.combine.cc:3801 msgid "manual align" msgstr "manuell einstellen" #: f.combine.cc:2652 f.combine.cc:3803 f.widgets.cc:473 f.widgets.cc:768 msgid "Resize" msgstr "Größe verändern" #: f.combine.cc:2653 f.combine.cc:3804 msgid "resize window" msgstr "Fenstergröße ändern" #: f.combine.cc:2661 f.combine.cc:3812 msgid "do not warp images during auto-alignment" msgstr "Bilder nicht während Rand-Anpassung krümmen" #: f.combine.cc:2707 f.combine.cc:3858 msgid "use two images only" msgstr "Zwei Bilder benutzen" #: f.combine.cc:2735 f.combine.cc:2939 f.combine.cc:3127 f.combine.cc:3884 #: f.combine.cc:4088 f.combine.cc:4276 msgid "Too little overlap, cannot align" msgstr "Unzureichende Überlappung, Ausrichtung nicht möglich" #: f.combine.cc:3205 f.combine.cc:4354 msgid "Match Brightness and Color" msgstr "Helligkeit und Farbe anpassen" #: f.combine.cc:3251 msgid "Select image" msgstr "Bild wählen" #: f.combine.cc:3266 f.combine.cc:4415 msgid "auto color" msgstr "Auto-Farbe" #: f.combine.cc:3267 f.combine.cc:4416 msgid "file color" msgstr "Datei-Farbe" #: f.combine.cc:3273 f.combine.cc:4422 msgid "mouse warp" msgstr "Maus verformen" #: f.combine.cc:3275 f.combine.cc:4424 msgid "flatten image" msgstr "Bild abflachen" #: f.combine.cc:3724 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Bilder zur Grobabstimmung mit der Maus ziehen.\n" "Zum Drehen, vom rechten Rand ziehen" #: f.combine.cc:4684 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) nicht installiert" #: f.combine.cc:4693 msgid "Select at least 2 files" msgstr "Wenigstens 2 Dateien auswählen" #: f.edit.cc:98 msgid "Trim: drag middle to move, drag corners to resize" msgstr "Schnitt: Zum Bewegen mittig ziehen, Ecken um Größe zu verändern." #: f.edit.cc:99 msgid "Minor rotate: drag right edge with mouse" msgstr "Geringe Drehung: rechten Rand mit der Maus ziehen" #: f.edit.cc:180 f.widgets.cc:471 f.widgets.cc:767 msgid "Trim/Rotate" msgstr "Schneiden/Drehen" #: f.edit.cc:192 f.edit.cc:1306 msgid "ratio" msgstr "Verhältnis" #: f.edit.cc:196 msgid "trim size:" msgstr "Schnittgröße" #: f.edit.cc:201 msgid "Lock Ratio" msgstr "Seitenverhältniss fixieren" #: f.edit.cc:203 msgid "maximize trim box" msgstr "Schnittrand maximieren" #: f.edit.cc:204 msgid "use previous size" msgstr "vorherige Große verwenden" #: f.edit.cc:205 msgid "invert width/height" msgstr "Breite/Höhe austauschen" #: f.edit.cc:206 msgid "trim transparent edges" msgstr "transparente Ränder abschneiden" #: f.edit.cc:207 msgid "lock width/height ratio" msgstr "Breite/Höhe Verhältnis festsetzen" #: f.edit.cc:212 msgid "Customize" msgstr "Anpassen" #: f.edit.cc:218 msgid "Level" msgstr "Einebnen" #: f.edit.cc:219 msgid "use EXIF data if available" msgstr "EXIF-Daten verwenden wenn vorhanden" #: f.edit.cc:222 msgid "Rotate: degrees" msgstr "Drehen: Grad" #: f.edit.cc:1302 msgid "Trim Buttons" msgstr "Schnitt-Knöpfe" #: f.edit.cc:1305 msgid "label" msgstr "Kennsatz" #: f.edit.cc:1402 msgid "Upright Image" msgstr "Bild aufrichten" #: f.edit.cc:1408 f.process.cc:168 f.process.cc:755 f.widgets.cc:472 #: f.widgets.cc:769 msgid "Upright" msgstr "Aufrichten" #: f.edit.cc:1461 msgid "rotation unknown" msgstr "Drehrichtung unbekannt" #: f.edit.cc:1548 msgid "Resize Image" msgstr "Bildgröße verändern" #: f.edit.cc:1573 msgid "W/H Ratio:" msgstr "B/H Verhältnis:" #: f.edit.cc:1575 msgid "Lock" msgstr "Fixieren" #: f.edit.cc:1577 msgid "use previous settings" msgstr "vorherige Einstellungen benutzen" #: f.edit.cc:2298 f.widgets.cc:476 f.widgets.cc:772 msgid "Retouch Combo" msgstr "Retusche Combo" #: f.edit.cc:2319 msgid "Amplifier" msgstr "Verstärker" #: f.edit.cc:2320 fotoxx.h:1188 msgid "Brightness" msgstr "Helligkeit" #: f.edit.cc:2321 f.effects.cc:3681 fotoxx.h:1198 msgid "Contrast" msgstr "Kontrast" #: f.edit.cc:2322 msgid "Low Color" msgstr "Niedrige Farbe" #: f.edit.cc:2323 msgid "Warmer" msgstr "Wärmer" #: f.edit.cc:2324 f.effects.cc:1332 msgid "Dark Areas" msgstr "Dunkle Bereiche" #: f.edit.cc:2333 msgid "Max." msgstr "Max." #: f.edit.cc:2334 f.edit.cc:2335 f.edit.cc:2336 msgid "High" msgstr "Hoch" #: f.edit.cc:2337 msgid "Cooler" msgstr "Kühler" #: f.edit.cc:2338 msgid "Bright" msgstr "Hell" #: f.edit.cc:2341 f.tools.cc:2134 msgid "Brightness Distribution" msgstr "Helligkeitsverteilung" #: f.edit.cc:2344 msgid "Click for white balance" msgstr "für Weißabgleich anklicken" #: f.edit.cc:2345 msgid "Click for black level" msgstr "für Schwarzwert anklicken" #: f.edit.cc:2348 msgid "Settings File" msgstr "Einstellungsdatei" #: f.edit.cc:2352 msgid "recall previous settings used" msgstr "Vorherige Einstellungen wiederverwenden" #: f.edit.cc:3004 msgid "Adjust Brightness Distribution" msgstr "Helligkeit-Verteilung Justieren" #: f.edit.cc:3043 msgid "Low Cutoff" msgstr "Niedrig Verschnitt" #: f.edit.cc:3044 msgid "High Cutoff" msgstr "Hoch Verschnitt" #: f.edit.cc:3045 msgid "Low Flatten" msgstr "Niedrig abflachen" #: f.edit.cc:3046 msgid "Mid Flatten" msgstr "Mittig abflachen" #: f.edit.cc:3047 msgid "High Flatten" msgstr "Hoch abflachen" #: f.edit.cc:3048 msgid "Low Stretch" msgstr "Niedrig strecken" #: f.edit.cc:3049 msgid "Mid Stretch" msgstr "Mittig strecken" #: f.edit.cc:3050 msgid "High Stretch" msgstr "Hoch strecken" #: f.edit.cc:3383 msgid "Magnify Gradients" msgstr "Graduierungen vergrößern" #: f.edit.cc:3415 msgid "low" msgstr "niedrig" #: f.edit.cc:3417 msgid "high" msgstr "hoch" #: f.edit.cc:3420 msgid "Amplify" msgstr "Verstärken" #: f.edit.cc:3813 msgid "Flatten Brightness" msgstr "Helligkeit abflachen" #: f.edit.cc:3864 msgid "Zones" msgstr "Zonen" #: f.edit.cc:3871 msgid "Deband Dark" msgstr "Entstreifen Dunkel" #: f.edit.cc:3874 msgid "Deband Bright" msgstr "Entstreifen Hell" #: f.edit.cc:4386 msgid "Dark Point" msgstr "Schwarzwert" #: f.edit.cc:4387 msgid "Bright Point" msgstr "Weißwert" #: f.edit.cc:4388 msgid "Multiplyer" msgstr "Multiplizierer" #: f.edit.cc:4412 msgid "brightness rescale" msgstr "Helligkeit skalieren" #: f.edit.cc:4415 msgid "click bright point" msgstr "Weißpunkt anklicken" #: f.edit.cc:4416 msgid "click dark point" msgstr "Schwarzpunkt anklicken" #: f.edit.cc:4427 msgid "blend" msgstr "einblenden" #: f.edit.cc:4430 msgid "reduce bright" msgstr "Hell reduzieren" #: f.edit.cc:5468 f.widgets.cc:481 msgid "Mirror Image" msgstr "Bild spiegeln" #: f.edit.cc:5472 f.warp.cc:3300 msgid "horizontal" msgstr "Horizontal" #: f.edit.cc:5473 f.warp.cc:3304 msgid "vertical" msgstr "Vertikal" #: f.edit.cc:5620 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "Shift + links-Klick: Farbe vom Bild wählen \n" "links-ziehen: mit Farbe Bild malen \n" "rechts-ziehen: original Bild zurückbringen" #: f.edit.cc:5656 msgid "Paint on Image" msgstr "Bild bemalen" #: f.edit.cc:5662 msgid "paint color" msgstr "Malfarbe" #: f.edit.cc:5673 f.edit.cc:6640 msgid "brush size" msgstr "Pinselgröße" #: f.edit.cc:5674 f.edit.cc:6641 msgid "opacity center" msgstr "Opazität Mitte" #: f.edit.cc:5675 f.edit.cc:6642 msgid "opacity edge" msgstr "Opazität Rand" #: f.edit.cc:5687 msgid "erase" msgstr "löschen" #: f.edit.cc:5688 msgid "include transparent areas" msgstr "Transperante Bereiche übermalen" #: f.edit.cc:5695 msgid "drag image" msgstr "Bild ziehen" #: f.edit.cc:5697 msgid "zoom image" msgstr "Bild zoomen" #: f.edit.cc:6221 msgid "Color Palette" msgstr "Farbpalette" #: f.edit.cc:6225 msgid "click on image to choose color" msgstr "Bild anklicken um Farbe zu wählen" #: f.edit.cc:6415 f.repair.cc:3799 msgid "Color Hue" msgstr "Farbton" #: f.edit.cc:6416 f.repair.cc:3768 f.repair.cc:3800 msgid "Saturation" msgstr "Farbsättigung" #: f.edit.cc:6417 f.repair.cc:3769 f.repair.cc:3801 msgid "Lightness" msgstr "Helligkeit" #: f.edit.cc:6599 msgid "" "shift + left click: pick image position to copy \n" "left drag: copy image to mouse position \n" "right drag: restore image" msgstr "" "Shift + Links-Klick: Bild-Position zum Kopieren wählen \n" "links-ziehen: vom Bild zu Maus kopieren \n" "rechts-ziehen: original Bild zurückbringen" #: f.edit.cc:6630 f.widgets.cc:483 msgid "Clone Image" msgstr "Bild klonen" #: f.edit.cc:6649 msgid "paint over transparent areas" msgstr "Transperante Bereiche übermalen" #: f.edit.cc:7098 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "Links schleppen: Pixel mischen \n" "Rechts schleppen: zurücksetzen" #: f.edit.cc:7126 f.widgets.cc:484 msgid "Blend Image" msgstr "Pixel mischen" #: f.edit.cc:7134 f.repair.cc:7663 msgid "strength center" msgstr "Stärke Mitte" #: f.edit.cc:7135 f.repair.cc:7664 msgid "strength edge" msgstr "Stärke Rand" #: f.edit.cc:7359 msgid "+Version" msgstr "+Version" #: f.edit.cc:7382 f.widgets.cc:275 msgid "Add Text to Image" msgstr "Texte ins Bild einfügen" #: f.edit.cc:7383 msgid "Enter text, click/drag on image, right click to remove" msgstr "" "Text eingeben, Bild mit der Maus klicken/ziehen, zum Entfernen rechts klicken" #: f.edit.cc:7432 f.edit.cc:8310 msgid "Use settings file" msgstr "Einstellungsdatei benutzen" #: f.edit.cc:7437 f.mashup.cc:2455 msgid "Text" msgstr "Text" #: f.edit.cc:7442 msgid "Use metadata key" msgstr "Metadata Key benutzen" #: f.edit.cc:7461 f.mashup.cc:2473 msgid "text" msgstr "Text" #: f.edit.cc:7462 f.edit.cc:8336 f.mashup.cc:2474 f.mashup.cc:2812 msgid "backing" msgstr "Hinterfarbe" #: f.edit.cc:7463 f.edit.cc:8337 f.mashup.cc:2475 f.mashup.cc:2813 msgid "outline" msgstr "Umriss" #: f.edit.cc:7464 f.edit.cc:8338 f.mashup.cc:2476 f.mashup.cc:2814 msgid "shadow" msgstr "Shatten" #: f.edit.cc:7490 msgid "save to current file" msgstr "in aktueller Datei speichern" #: f.edit.cc:7491 f.file.cc:2227 msgid "save as new file version" msgstr "Als neuen Dateiversion speichern" #: f.edit.cc:7492 msgid "" "save to current file \n" "open next file with same text" msgstr "" "In aktueller Datei speichern \n" "Nächste Datei mit gleichem Text öffnen" #: f.edit.cc:7652 f.mashup.cc:2580 f.tools.cc:1464 msgid "select font" msgstr "Font wählen" #: f.edit.cc:7928 f.edit.cc:8766 msgid "text file is defective" msgstr "Textdatei ist defekt" #: f.edit.cc:8268 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Linie/Pfeil Attribute eingeben, am Bild klicken/ziehen, \n" "zum Entfernen rechts klicken" #: f.edit.cc:8302 f.widgets.cc:276 msgid "Add lines or arrows to an image" msgstr "Linien oder Pfeile einem Bild zufügen" #: f.edit.cc:8315 f.mashup.cc:2791 msgid "Line length" msgstr "Linien-Länge" #: f.edit.cc:8322 f.mashup.cc:2798 msgid "Arrow head" msgstr "Pfeil-Kopf" #: f.edit.cc:8335 f.mashup.cc:2811 msgid "line" msgstr "Linie" #: f.edit.cc:8364 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "Linie/Pfeil im Lageplan festsetzen \n" " Neuen Linie/Pfeil starten" #: f.edit.cc:9093 f.widgets.cc:487 msgid "Paint Edits" msgstr "Änderungen anmalen" #: f.edit.cc:9100 f.edit.cc:9323 fotoxx-18.01.cc:3426 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Ausschnitt kann nicht behalten werden.\n" "Fortfahren?" #: f.edit.cc:9108 f.edit.cc:9331 f.tools.cc:2666 msgid "Edit function must be active" msgstr "Editfunktion muss aktiv sein" #: f.edit.cc:9113 msgid "Cannot use Paint Edits" msgstr "Änderungen Anmalen nicht erlaubt" #: f.edit.cc:9140 msgid "power: center" msgstr "Stärke: Mitte" #: f.edit.cc:9145 msgid "reset area" msgstr "zurücksetzen" #: f.edit.cc:9316 f.widgets.cc:488 msgid "Leverage Edits" msgstr "Änderungs-Hebelkraft" #: f.edit.cc:9317 msgid "Edit Function Amplifier" msgstr "Bearbeitungs-Verstärker" #: f.edit.cc:9336 msgid "Cannot use Leverage Edits" msgstr "Änderungs-Hebelkraft nicht erlaubt" #: f.edit.cc:9365 msgid "minimum" msgstr "Minimum" #: f.edit.cc:9367 msgid "maximum" msgstr "Maximum" #: f.edit.cc:9642 f.edit.cc:9686 msgid "Edit Plugins" msgstr "Plugins bearbeiten" #: f.edit.cc:9643 msgid "Edit plugins menu" msgstr "Plugins-Menü editieren" #: f.edit.cc:9651 msgid "Run as Fotoxx edit function" msgstr "Als Fotoxx-Bearbeitungsfunktion benutzen" #: f.edit.cc:9688 msgid "menu name" msgstr "Menü-Name" #: f.edit.cc:9691 msgid "command" msgstr "Kommando" #: f.edit.cc:9888 msgid "Plugin working ..." msgstr "Plugin arbeitet ..." #: f.edit.cc:9897 msgid "plugin failed" msgstr "Plug-in fehlgeschlagen" #: f.effects.cc:79 msgid "Convert to Sketch" msgstr "In eine Zeichnung verwandeln" #: f.effects.cc:157 msgid "Clip Level" msgstr "Kappenwert" #: f.effects.cc:161 msgid "Algorithm" msgstr "Algorithmus" #: f.effects.cc:166 msgid "Foreground" msgstr "Vordergrund" #: f.effects.cc:169 f.tools.cc:1275 msgid "Background" msgstr "Hintergrund" #: f.effects.cc:1054 f.widgets.cc:531 msgid "Line Drawing" msgstr "Stift-Zeichnung" #: f.effects.cc:1067 msgid "black/white" msgstr "schwarzweiß" #: f.effects.cc:1326 f.widgets.cc:532 msgid "Color Drawing" msgstr "Farb-Zeichnung" #: f.effects.cc:1333 msgid "Bright Areas" msgstr "Hellere Bereiche" #: f.effects.cc:1575 f.widgets.cc:533 msgid "Graduated Blur" msgstr "Gestaffelte Verwischen" #: f.effects.cc:1579 msgid "Contrast Limit" msgstr "Kontrast-Grenzwert" #: f.effects.cc:1582 f.repair.cc:929 msgid "Blur Radius" msgstr "Verwisch-Radius" #: f.effects.cc:1799 msgid "Simulate Embossing" msgstr "Prägen simulieren" #: f.effects.cc:1820 msgid "depth" msgstr "Tiefe" #: f.effects.cc:2029 msgid "Simulate Tiles" msgstr "Kacheln simulieren" #: f.effects.cc:2033 msgid "tile size" msgstr "Kachelgröße" #: f.effects.cc:2036 msgid "tile gap" msgstr "Spaltbreite" #: f.effects.cc:2039 msgid "3D depth" msgstr "3D Tiefe" #: f.effects.cc:2258 msgid "Convert Image to Dots" msgstr "Bild in Rasterpunkte umwandeln" #: f.effects.cc:2262 msgid "dot size" msgstr "Punktgröße" #: f.effects.cc:2484 msgid "Simulate Painting" msgstr "Malen Simulieren" #: f.effects.cc:2502 msgid "color depth" msgstr "Farbtiefe" #: f.effects.cc:2506 msgid "patch area goal" msgstr "Fleckgröße Ziel" #: f.effects.cc:2510 msgid "req. color match" msgstr "Farbanpassung" #: f.effects.cc:2514 msgid "borders" msgstr "Ränder" #: f.effects.cc:3081 f.widgets.cc:538 msgid "Vignette" msgstr "Vignette" #: f.effects.cc:3430 msgid "Add Texture" msgstr "Textur addieren" #: f.effects.cc:3646 msgid "Background Pattern" msgstr "Hintergrund Muster" #: f.effects.cc:3650 msgid "Pattern File:" msgstr "Musterdatei" #: f.effects.cc:3655 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3658 msgid "Geometry" msgstr "Abmaße" #: f.effects.cc:3659 msgid "Calculate" msgstr "Rechnen" #: f.effects.cc:3662 f.widgets.cc:540 msgid "Pattern" msgstr "Muster" #: f.effects.cc:3670 msgid "Overlap" msgstr "Überschneidung" #: f.effects.cc:3678 msgid "Opacity" msgstr "Opazität" #: f.effects.cc:4124 msgid "Create Mosaic" msgstr "Mosaik erstellen" #: f.effects.cc:4170 msgid "Tile" msgstr "Kachel" #: f.effects.cc:4178 f.widgets.cc:535 msgid "Tiles" msgstr "Kacheln" #: f.effects.cc:4184 msgid "Tile blending" msgstr "Kacheln einblenden" #: f.effects.cc:4265 #, c-format msgid "exceeded max. tiles: %d" msgstr "Max. Kacheln überschritten: %d" #: f.effects.cc:4272 #, c-format msgid "only %d tile images found" msgstr "Nur %d Kachelbilder gefunden" #: f.effects.cc:4756 f.widgets.cc:542 msgid "Custom Kernel" msgstr "Benutzerspezifischer Kernel" #: f.effects.cc:4760 msgid "Kernel size" msgstr "Kernelgröße" #: f.effects.cc:4777 msgid "multiply" msgstr "multiplizieren" #: f.effects.cc:4780 msgid "add" msgstr "addieren" #: f.effects.cc:4784 msgid "Data file" msgstr "Kerneldatei" #: f.effects.cc:4804 fotoxx-18.01.cc:3690 msgid "Load settings from file" msgstr "Einstellungen von Datei laden" #: f.effects.cc:5046 msgid "Pull image using the mouse." msgstr "Bild mit der Maus schleppen." #: f.effects.cc:5067 f.widgets.cc:543 msgid "Directed Blur" msgstr "Gerichtete Verwischung" #: f.effects.cc:5071 msgid "blur span" msgstr "Verwischbreite" #: f.effects.cc:5074 msgid "intensity" msgstr "Stärke" #: f.effects.cc:5276 f.widgets.cc:544 msgid "Blur Background" msgstr "Hintergrund unscharf machen" #: f.effects.cc:5280 msgid "constant blur" msgstr "gleichmässige Unschärfe" #: f.effects.cc:5283 msgid "increase blur with distance" msgstr "wachsende Unschärfe mit Abstand" #: f.effects.cc:5285 msgid "min. blur radius" msgstr "min. Unscharf-Radius" #: f.effects.cc:5288 msgid "max. blur radius" msgstr "max. Unscharf-Radius" #: f.effects.cc:5322 f.warp.cc:819 f.warp.cc:1252 msgid "no active Select Area" msgstr "Ausschnitt nicht aktiviert" #: f.effects.cc:5515 f.widgets.cc:545 msgid "Alien Colors" msgstr "Alien-Farben" #: f.effects.cc:5518 msgid "blocksize" msgstr "Blockgröße" #: f.effects.cc:5521 f.warp.cc:3298 msgid "amplitude" msgstr "Amplitude" #: f.file.cc:190 msgid "" "Use EXIF photo date or \n" " file modification date?" msgstr "" "EXIF Fotodatum oder Datei \n" " Änderungsdatum benutzen?" #: f.file.cc:204 f.widgets.cc:626 msgid "File" msgstr "Datei" #: f.file.cc:378 f.process.cc:1345 msgid "Raw Therapee not installed" msgstr "Raw Therapee nicht installiert" #: f.file.cc:396 f.widgets.cc:432 f.widgets.cc:777 msgid "Open RAW (Raw Therapee)" msgstr "RAW öffnen (Raw Therapee)" #: f.file.cc:401 msgid "RAW type not registered in User Settings" msgstr "RAW-Typ nicht in Benutzereinstellungen registriert" #: f.file.cc:416 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee hat keine tif Datei produziert" #: f.file.cc:802 f.widgets.cc:429 msgid "Open Image File" msgstr "Bilddatei öffnen" #: f.file.cc:821 f.process.cc:597 msgid "unknown file type" msgstr "Dateityp unbekannt" #: f.file.cc:999 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Galerie-Anfang, vorherige: %s" #: f.file.cc:1000 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Galerie-Ende, nächste: %s" #: f.file.cc:1108 msgid "Create Blank Image" msgstr "Leerbild erstellen" #: f.file.cc:1110 msgid "file name" msgstr "Dateiname" #: f.file.cc:1144 msgid "supply a file name" msgstr "Dateinamen eingeben" #: f.file.cc:1314 f.widgets.cc:436 msgid "Rename Image File" msgstr "Bilddatei umbenennen" #: f.file.cc:1321 msgid "Old Name" msgstr "Vorheriger Name" #: f.file.cc:1322 f.process.cc:130 msgid "New Name" msgstr "Neuer Name" #: f.file.cc:1331 msgid "previous name" msgstr "Vorheriger Name" #: f.file.cc:1332 msgid "Add 1" msgstr "1 addieren" #: f.file.cc:1335 f.file.cc:1571 f.file.cc:1866 msgid "keep this dialog open" msgstr "Dialog offen halten" #: f.file.cc:1520 msgid "Copy or Move Image File" msgstr "Bilddatei kopieren oder versetzen" #: f.file.cc:1562 msgid "New Location:" msgstr "Neue Speicherstelle" #: f.file.cc:1567 msgid "copy (duplicate file)" msgstr "kopieren (Datei duplizieren)" #: f.file.cc:1568 msgid "move (remove original)" msgstr "versetzen (Original entfernen)" #: f.file.cc:1609 f.process.cc:573 f.process.cc:1503 f.process.cc:2625 msgid "Select directory" msgstr "Bildverzeichnis wählen" #: f.file.cc:1647 msgid "new location is not a directory" msgstr "Neue Speicherstelle muß ein Verzeichnis sein" #: f.file.cc:1688 f.file.cc:1942 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "Löschen fehlgeschlagen: \n" " %s" #: f.file.cc:1827 f.widgets.cc:442 msgid "Delete/Trash Image File" msgstr "Bilddatei löschen / in Abfall schieben" #: f.file.cc:1896 msgid "GTK g_file_trash() function failed" msgstr "GTK g_file_trash() funktioniert nicht" #: f.file.cc:1915 msgid "not a known image file" msgstr "keine bekannte Bilddatei" #: f.file.cc:1921 msgid "Delete read-only file?" msgstr "Schreibgeschützte Datei löschen?" #: f.file.cc:1923 msgid "Trash read-only file?" msgstr "Nicht schreibbare Datei in den Abfall?" #: f.file.cc:1971 msgid "no more images" msgstr "keine Bilder mehr" #: f.file.cc:2209 msgid "Save Image File" msgstr "Bilddatei speichern" #: f.file.cc:2219 msgid "new version" msgstr "Neue Version" #: f.file.cc:2220 msgid "new file ..." msgstr "Neue Datei ..." #: f.file.cc:2221 msgid "replace file" msgstr "Datei ersetzen" #: f.file.cc:2228 f.file.cc:2800 msgid "save as new file name or type" msgstr "Als neuen Dateinamen oder Typ speichern" #: f.file.cc:2230 msgid "replace old file (OVERWRITE)" msgstr "Vorherige Datei ersetzen (ÜBERSCHREIBEN" #: f.file.cc:2267 f.file.cc:2558 msgid "cannot save as RAW type" msgstr "Kann nicht als RAW-Datei speichern" #: f.file.cc:2375 msgid "too many file versions: 99" msgstr "zu viele Datei-Versionen: 99" #: f.file.cc:2552 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "Transparenzdaten gehen verloren.\n" "Als PNG Datei speichern zum behalten." #: f.file.cc:2637 msgid "save anyway" msgstr "trotzdem speichern" #: f.file.cc:2706 msgid "Unable to copy EXIF/IPTC data" msgstr "Kann nicht EXIF/IPTC Daten kopieren" #: f.file.cc:2818 f.file.cc:2821 msgid "make current" msgstr "aktuell machen" #: f.file.cc:2819 msgid "(new file becomes current file)" msgstr "(neue Datei wird aktuelle Datei)" #: f.file.cc:2932 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Datei überschreiben? \n" " %s" #: f.file.cc:3127 f.widgets.cc:599 msgid "Quick Start" msgstr "Schnell-Start" #: f.file.cc:3130 f.widgets.cc:600 msgid "User Guide" msgstr "Benutzeranleitung" #: f.file.cc:3133 f.widgets.cc:601 msgid "User Guide Changes" msgstr "Benutzeranleitung Änderungen" #: f.file.cc:3136 f.widgets.cc:602 msgid "README" msgstr "LIESMICH" #: f.file.cc:3139 f.widgets.cc:603 msgid "Change Log" msgstr "Änderungslog" #: f.file.cc:3142 f.widgets.cc:604 msgid "Log File" msgstr "Fehlerlogdatei" #: f.file.cc:3145 f.widgets.cc:605 msgid "Translations" msgstr "Übersetzungen" #: f.file.cc:3148 f.widgets.cc:606 msgid "Home Page" msgstr "Homepage" #: f.file.cc:3151 f.widgets.cc:607 msgid "About" msgstr "Über Fotoxx" #: f.file.cc:3157 f.widgets.cc:639 f.widgets.cc:663 f.widgets.cc:676 #: f.widgets.cc:690 fotoxx.h:1223 msgid "Help" msgstr "Hilfe" #: f.file.cc:3362 f.file.cc:3367 f.file.cc:3437 #, c-format msgid "file type not supported: %s" msgstr "Dateityp nicht unterstützt: %s" #: f.gallery.cc:1221 msgid "GoTo" msgstr "GehZu" #: f.gallery.cc:1227 f.widgets.cc:655 msgid "Sort" msgstr "Sortieren" #: f.gallery.cc:1233 f.widgets.cc:653 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:1243 f.widgets.cc:654 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:1255 msgid "Row Up" msgstr "Reihe nach oben" #: f.gallery.cc:1263 msgid "Row Down" msgstr "Reihe nach unten" #: f.gallery.cc:1271 f.widgets.cc:658 msgid "Page Up" msgstr "Seite nach oben" #: f.gallery.cc:1279 f.widgets.cc:659 msgid "Page Down" msgstr "Seite nach unten" #: f.gallery.cc:1287 f.widgets.cc:656 msgid "First" msgstr "Erste" #: f.gallery.cc:1294 f.widgets.cc:657 msgid "Last" msgstr "Letzte" #: f.gallery.cc:1425 f.gallery.cc:1445 msgid "recent images" msgstr "kürzlich angesehene Bilder" #: f.gallery.cc:1426 f.gallery.cc:1450 msgid "newest images" msgstr "neueste Bilder" #: f.gallery.cc:1504 msgid "no albums found" msgstr "Keine Alben gefunden" #: f.gallery.cc:1541 msgid "Albums cannot be sorted" msgstr "Alben können nicht sortiert werden" #: f.gallery.cc:1545 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" " Alle Galerien zurücksetzen\n" " auf Dateiname aufsteigend" #: f.gallery.cc:1564 msgid "Gallery Sort" msgstr "Galerie-Sortierung" #: f.gallery.cc:1568 msgid "File Name" msgstr "Dateinamen" #: f.gallery.cc:1569 msgid "File Mod Date/Time" msgstr "Dateiänderungs-Datum/Zeit" #: f.gallery.cc:1570 msgid "Photo Date/Time (EXIF)" msgstr "Foto Datum/Zeit (EXIF)" #: f.gallery.cc:1572 msgid "ascending" msgstr "aufsteigend" #: f.gallery.cc:1573 msgid "descending" msgstr "absteigend" #: f.gallery.cc:2925 f.gallery.cc:2929 msgid "Image File" msgstr "Bilddatei" #: f.gallery.cc:2927 msgid "select thumbnail" msgstr "Thumbnail wählen" #: f.gallery.cc:3037 fotoxx.h:1276 msgid "Select Files" msgstr "Dateien auswählen" #: f.gallery.cc:3097 #, c-format msgid "exceed %d selected files" msgstr "%d gewählte Dateien überschritten" #: f.gallery.cc:3457 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Listenposition anklicken. Galerie Thumbnail für das neue Lesezeichen " "anklicken.\n" "Lesezeichen für Thumbnail wird zugefügt. Name ändern und [umbennen] drücken." #: f.gallery.cc:3484 f.gallery.cc:3734 msgid "Edit Bookmarks" msgstr "Lesezeichen bearbeiten" #: f.gallery.cc:3662 msgid "unable to save bookmarks file" msgstr "Kann Lesezeichen-Datei nicht speichern" #: f.gallery.cc:3734 msgid "Go To Bookmark" msgstr "Zum Lesezeichen springen" #: f.mashup.cc:170 msgid "Mashup layout and background image" msgstr "Mashup Lageplan- und Hintergrund-Bild" #: f.mashup.cc:213 msgid "choose an image file" msgstr "Bilddatei wählen" #: f.mashup.cc:214 msgid "use current image file" msgstr "Aktuelle Bilddatei verwenden" #: f.mashup.cc:215 msgid "specify layout size and color" msgstr "Lageplan Größe und Farbe angeben" #: f.mashup.cc:216 msgid "open a Mashup project file" msgstr "Mashup Projektdatei öffnen" #: f.mashup.cc:259 msgid "no current file" msgstr "Keine aktuelle Bilddatei" #: f.mashup.cc:291 msgid "Make Layout Image" msgstr "Lageplan-Bild erstellen" #: f.mashup.cc:293 msgid "project name" msgstr "Projektname" #: f.mashup.cc:319 msgid "supply a project name" msgstr "Projektname eingeben" #: f.mashup.cc:411 msgid "Edit Images" msgstr "Bilder bearbeiten" #: f.mashup.cc:412 f.mashup.cc:2450 msgid "Edit Text" msgstr "Texte bearbeiten" #: f.mashup.cc:413 msgid "Edit Line" msgstr "Linie bearbeiten" #: f.mashup.cc:414 msgid "Rescale" msgstr "Neu skalieren" #: f.mashup.cc:419 msgid "add or edit images" msgstr "Bilder zufügen oder bearbeiten" #: f.mashup.cc:421 msgid "add or edit text" msgstr "Texte zufügen oder bearbeiten" #: f.mashup.cc:423 msgid "add or edit lines/arrows" msgstr "Linien/Pfeile zufügen oder bearbeiten" #: f.mashup.cc:425 msgid "change project scale" msgstr "Projekt-Maßstab ändern" #: f.mashup.cc:427 msgid "project complete" msgstr "Projekt fertig" #: f.mashup.cc:429 msgid "cancel project" msgstr "Projekt verwerfen" #: f.mashup.cc:447 msgid "rescale project" msgstr "Projekt neu skalieren" #: f.mashup.cc:506 msgid "save Mashup output file" msgstr "Ausgabedatei speichern" #: f.mashup.cc:526 msgid "save Mashup project file?" msgstr "Projektdatei speichern?" #: f.mashup.cc:538 msgid "delete Mashup project file?" msgstr "Projektdatei löschen?" #: f.mashup.cc:597 msgid "Open Project" msgstr "Projekt öffnen" #: f.mashup.cc:626 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "Lageplan-Bilddatei fehlt: \n" " %s" #: f.mashup.cc:683 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "Overlay-Bilddatei fehlt: \n" " %s" #: f.mashup.cc:989 msgid "project file is defective" msgstr "Projektdatei ist defekt" #: f.mashup.cc:1019 msgid "Save Project" msgstr "Projekt speichern" #: f.mashup.cc:1160 msgid "layout exceeds 2 gigabytes" msgstr "Lageplan überschreitet 2 Gigabytes" #: f.mashup.cc:1234 msgid "Click image to select, drag image to move." msgstr "Bild anklicken zum Wählen, schleppen zum Versetzen." #: f.mashup.cc:1235 msgid "Make black margins transparent" msgstr "Schwarze Ränder transparent machen" #: f.mashup.cc:1274 msgid "Current image:" msgstr "Aktuelles Bild" #: f.mashup.cc:1278 msgid "Cycle through images:" msgstr "Aktuelle Bilder durchsuchen" #: f.mashup.cc:1285 msgid "Scale" msgstr "Maßstab" #: f.mashup.cc:1294 msgid "Stacking Order" msgstr "Stapelfolge" #: f.mashup.cc:1295 msgid "Raise" msgstr "Heben" #: f.mashup.cc:1296 msgid "Lower" msgstr "Senken" #: f.mashup.cc:1299 msgid "Base Transparency" msgstr "Grund-Transparenz" #: f.mashup.cc:1303 msgid "Var. Transparency" msgstr "Var. Transparenz" #: f.mashup.cc:1304 msgid "Paint" msgstr "Malen" #: f.mashup.cc:1307 msgid "Bend and fine-align" msgstr "Biegen und fein einstellen" #: f.mashup.cc:1308 f.widgets.cc:634 msgid "Warp" msgstr "Verformen" #: f.mashup.cc:1317 zfuncs.cc:11351 zfuncs.cc:11360 msgid "Margins" msgstr "Ränder" #: f.mashup.cc:1318 msgid "Hard" msgstr "Fest" #: f.mashup.cc:1319 f.repair.cc:4410 msgid "Blend" msgstr "Beimischen" #: f.mashup.cc:1333 msgid "add images to layout" msgstr "Bilder im Lageplan einfügen" #: f.mashup.cc:1570 msgid "Paint Image Transparencies" msgstr "Bild-Transparenze malen" #: f.mashup.cc:1588 msgid "Gradual" msgstr "Stufenweise" #: f.mashup.cc:1590 fotoxx.h:1255 msgid "Power" msgstr "Stärke" #: f.mashup.cc:1823 msgid "Pull on the image with the mouse." msgstr "Bildstelle mit der Maus ziehen" #: f.mashup.cc:1839 msgid "Warp Image" msgstr "Bild verformen" #: f.mashup.cc:1845 f.warp.cc:1383 msgid "warp span" msgstr "Krümmen-Spannweite" #: f.mashup.cc:2281 #, c-format msgid "exceeded %d images" msgstr "%d Bilder überschritten" #: f.mashup.cc:2423 msgid "Enter text, [Add] to layout, edit properties." msgstr "Text eingeben, Layout [Hinzufügen], Attribute justieren." #: f.mashup.cc:2503 msgid "Text File:" msgstr "Textdatei:" #: f.mashup.cc:2507 msgid "add entered text to layout" msgstr "Eingegebenen Text im Lageplan einfügen" #: f.mashup.cc:2641 msgid "click position to add text" msgstr "Bildstelle für Text anklicken" #: f.mashup.cc:2647 #, c-format msgid "exceeded %d text entries" msgstr "%d Texte überschritten" #: f.mashup.cc:2652 f.widgets.cc:485 msgid "Add Text" msgstr "Texte zufügen" #: f.mashup.cc:2761 msgid "Set line properties, [Add] to layout, edit." msgstr "Linie Attribute justieren, Layout [Hinzufügen], einstellen." #: f.mashup.cc:2785 msgid "Edit Line/Arrow" msgstr "Linie/Pfeil bearbeiten" #: f.mashup.cc:2840 msgid "add line/arrow to layout" msgstr "Linie/Pfeil im Lageplan einfügen" #: f.mashup.cc:2931 msgid "click position to add line" msgstr "Bildstelle für Linie anklicken" #: f.mashup.cc:2937 #, c-format msgid "exceeded %d line entries" msgstr "%d Linie überschritten" #: f.mashup.cc:2942 f.widgets.cc:486 msgid "Add Line" msgstr "Linie zufügen" #: f.mashup.cc:4189 msgid "Image Montage" msgstr "Bildmontage" #: f.mashup.cc:4198 msgid "Frame Width" msgstr "Rahmenbreite" #: f.mashup.cc:4201 f.mashup.cc:4209 msgid "Margin" msgstr "Rand" #: f.mashup.cc:4206 msgid "Image Columns" msgstr "Bild-Kolumne" #: f.mashup.cc:4223 #, c-format msgid "exceed %d rows" msgstr "%d Reihen überschritten" #: f.mashup.cc:4319 msgid "Optimize" msgstr "Optimieren" #: f.mashup.cc:4327 #, c-format msgid "column difference: %d pixels" msgstr "Kolumn-Unterschied: %d Pixel" #: f.mashup.cc:4385 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "column difference: %d pixels \n" "Kolumne ebnen?" #: f.mashup.cc:4407 msgid "Save with unique montage name" msgstr "mit einmalige Montage-Name speichern" #: f.mashup.cc:4409 msgid "unique name:" msgstr "einmaliger Name:" #: f.mashup.cc:4411 msgid "create image map" msgstr "Bilder-Tafel erstellen" #: f.mashup.cc:4421 f.meta.cc:8637 msgid "supply a reasonable name" msgstr "vernunftige Name eingeben" #: f.mashup.cc:4432 msgid "save montage" msgstr "Bildmontage speichern" #: f.mashup.cc:4452 #, c-format msgid "map file saved: %s" msgstr "Bilder-Tafel gespeichert: %s" #: f.mashup.cc:4492 #, c-format msgid "%d max images exceeded" msgstr "%d max Bilder überschritten" #: f.mashup.cc:4566 f.mashup.cc:4572 #, c-format msgid "image frame is too large: %d x %d" msgstr "Bild-Rahmen ist zu groß: %d x %d" #: f.mashup.cc:4873 #, c-format msgid "montage map file not found: %s" msgstr "Bilder-Tafel nicht gefunden: %s" #: f.mashup.cc:4877 #, c-format msgid "montage map file invalid: %s" msgstr "Bilder-Tafel Datei ungültig: %s" #: f.meta.cc:242 msgid "Add Metadata Items" msgstr "Metadata Element zufügen" #: f.meta.cc:246 f.meta.cc:1583 f.meta.cc:3104 msgid "click to select" msgstr "Zum Wählen, anklicken" #: f.meta.cc:251 msgid "click to unselect" msgstr "zum Deselektieren anklicken" #: f.meta.cc:475 msgid "Extras" msgstr "Extras" #: f.meta.cc:475 f.meta.cc:655 f.widgets.cc:754 msgid "View Metadata" msgstr "Metadata ansehen" #: f.meta.cc:813 f.widgets.cc:450 f.widgets.cc:755 msgid "Edit Metadata" msgstr "Metadata bearbeiten" #: f.meta.cc:816 msgid "save metadata to file" msgstr "Metadaten in Datei speichern" #: f.meta.cc:825 msgid "Image Date" msgstr "Bild Datum" #: f.meta.cc:828 msgid "Time" msgstr "Zeit" #: f.meta.cc:836 msgid "Rating (stars):" msgstr "Bewertung (Sterne):" #: f.meta.cc:848 msgid "Caption" msgstr "Titel" #: f.meta.cc:853 msgid "Comments" msgstr "Kommentare" #: f.meta.cc:860 msgid "Location" msgstr "Ort" #: f.meta.cc:863 msgid "Country" msgstr "Land" #: f.meta.cc:886 msgid "Image Tags" msgstr "Bild Tags" #: f.meta.cc:891 f.meta.cc:1992 msgid "Recent Tags" msgstr "Kürzliche Tags" #: f.meta.cc:896 f.meta.cc:1998 msgid "Enter New Tag" msgstr "Neues Tag eingeben" #: f.meta.cc:902 f.meta.cc:2004 f.meta.cc:4565 msgid "Matching Tags" msgstr "Passende Tags" #: f.meta.cc:909 f.meta.cc:2013 f.meta.cc:2511 f.meta.cc:4571 msgid "Defined Tags Category" msgstr "Definierte Tags Gruppe" #: f.meta.cc:916 msgid "search known locations" msgstr "bekannte Orte durchsuchen" #: f.meta.cc:917 msgid "search using web service" msgstr "suchen mit Webdienst" #: f.meta.cc:918 f.meta.cc:2020 msgid "create tag categories and tags" msgstr "Tag Kategorien und Tags erstellen" #: f.meta.cc:1368 f.meta.cc:3673 f.meta.cc:7231 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "Latitude/Longitude falsch: %s %s" #: f.meta.cc:1423 fotoxx.h:1234 msgid "Manage Tags" msgstr "Tags verwalten" #: f.meta.cc:1423 msgid "orphan tags" msgstr "verwaiste Tags" #: f.meta.cc:1427 msgid "category" msgstr "Gruppe" #: f.meta.cc:1430 msgid "tag" msgstr "Tag" #: f.meta.cc:1437 msgid "Defined Tags:" msgstr "Definierte Tags:" #: f.meta.cc:1579 f.widgets.cc:451 f.widgets.cc:756 msgid "Edit Any Metadata" msgstr "Beliebige Metadaten bearbeiten" #: f.meta.cc:1579 f.meta.cc:3099 msgid "Full List" msgstr "Vollliste" #: f.meta.cc:1592 f.meta.cc:3115 msgid "key name" msgstr "Schlüsselwort" #: f.meta.cc:1595 f.meta.cc:3116 msgid "key value" msgstr "Inhalt" #: f.meta.cc:1677 f.meta.cc:3259 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" "Das Kommando: $ man Image::ExifTool::TagNames \n" "wird über 15000 \"standard\" Tagnamen zeigen" #: f.meta.cc:1777 f.widgets.cc:452 msgid "Delete Metadata" msgstr "Metadata löschen" #: f.meta.cc:1783 fotoxx.h:1179 msgid "All" msgstr "Alles" #: f.meta.cc:1784 msgid "One Key:" msgstr "Ein Key:" #: f.meta.cc:1968 f.widgets.cc:568 msgid "Batch Add/Remove Tags" msgstr "Tags zuweisen/entfernen Stapelbetrieb" #: f.meta.cc:1981 msgid "tags to add" msgstr "Tags zum Zuweisen" #: f.meta.cc:1982 msgid "tags to remove" msgstr "Tags zum Entfernen" #: f.meta.cc:2095 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " Zuviele Tags" #: f.meta.cc:2108 msgid "repeat with same files?" msgstr "mit den selben Dateien wiederholen?" #: f.meta.cc:2290 msgid "specify files and tags" msgstr "Tags und Dateien angeben" #: f.meta.cc:2490 f.widgets.cc:569 msgid "Batch Rename Tags" msgstr "Stapel Tags umbenennen" #: f.meta.cc:2500 msgid "(click defined tag)" msgstr "(definiertes Tag anklicken)" #: f.meta.cc:2502 f.process.cc:749 msgid "Rename to" msgstr "Umbenennen zu" #: f.meta.cc:2519 msgid "old tag name >> new tag name" msgstr "alte Tag-Name >> neue Tag-Name" #: f.meta.cc:2576 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" "%d Tags zum Umbenennen \n" "in %d Bilddateien. \n" "Ausführen?" #: f.meta.cc:2720 msgid "max tags exceeded" msgstr "max Tags überschritten" #: f.meta.cc:2782 f.widgets.cc:570 msgid "Batch Photo Date/Time" msgstr "Stapel Foto Datum/Zeit" #: f.meta.cc:2790 msgid "set a new date/time:" msgstr "neues Datum/Zeit setzen" #: f.meta.cc:2798 msgid "shift existing date/time:" msgstr "bestehendes Datum/Zeit verschieben" #: f.meta.cc:2824 msgid "test: show changes, do not update files" msgstr "Versuch: neue Werte zeigen, Dateien nicht ändern" #: f.meta.cc:2850 f.meta.cc:3190 f.meta.cc:3368 msgid "no files selected" msgstr "Keine Dateien ausgewählt" #: f.meta.cc:2880 f.meta.cc:2888 f.meta.cc:2894 msgid "invalid date/time format" msgstr "Datum/Zeit Formatfehler" #: f.meta.cc:3099 f.widgets.cc:571 msgid "Batch Add/Change Metadata" msgstr "Metadata addieren/ändern in Stapelfunktion" #: f.meta.cc:3184 msgid "enter key names" msgstr "Key-Namen eingeben" #: f.meta.cc:3354 f.widgets.cc:572 msgid "Batch Report Metadata" msgstr "Stapel Metadaten auflisten" #: f.meta.cc:3498 f.widgets.cc:573 msgid "Batch Geotags" msgstr "Geotags Stapelbetrieb" #: f.meta.cc:3528 msgid "location" msgstr "Ort" #: f.meta.cc:3531 msgid "country" msgstr "Land" #: f.meta.cc:3659 msgid "" "data is incomplete \n" " proceed?" msgstr "" "Daten nicht vollständig \n" " Fortsetzen?" #: f.meta.cc:3745 msgid "Report Image Locations" msgstr "Bilder-Orte berichten" #: f.meta.cc:3746 msgid "Group by country" msgstr "Nach Land gruppieren" #: f.meta.cc:3747 msgid "Group by country/location" msgstr "Nach Land/Ort gruppieren" #: f.meta.cc:3748 msgid "Group by country/location/date" msgstr "Nach Land/Ort/Datum gruppieren" #: f.meta.cc:3749 msgid "Group by date/country/location" msgstr "Nach Datum/Land/Ort gruppieren" #: f.meta.cc:3752 msgid "Combine within" msgstr "Kombinieren innerhalb" #: f.meta.cc:3754 msgid "days" msgstr "Tage" #: f.meta.cc:3860 f.widgets.cc:1026 f.widgets.cc:1048 msgid "Image Locations" msgstr "Bild Orte" #: f.meta.cc:4137 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Jan Feb Mär Apr Mai Jun Jul Aug Sep Okt Nov Dez" #: f.meta.cc:4489 msgid "Search Image Metadata" msgstr "Bilder-Metadaten suchen" #: f.meta.cc:4493 msgid "images to search:" msgstr "Bilder durchsuchen:" #: f.meta.cc:4494 msgid "all" msgstr "Alle" #: f.meta.cc:4495 msgid "current set only" msgstr "nur Aktueller Satz" #: f.meta.cc:4498 msgid "matching images:" msgstr "passende Bilder:" #: f.meta.cc:4499 msgid "new set" msgstr "neuer Satz" #: f.meta.cc:4500 msgid "add to set" msgstr "dem Satz zufügen" #: f.meta.cc:4501 msgid "remove" msgstr "entfernen" #: f.meta.cc:4504 msgid "report type:" msgstr "Bericht-Typ:" #: f.meta.cc:4505 msgid "gallery" msgstr "Galerie" #: f.meta.cc:4509 msgid "date range" msgstr "Datumsbereich" #: f.meta.cc:4514 msgid "photo date" msgstr "Foto-Datum" #: f.meta.cc:4515 msgid "file date" msgstr "Datei-Datum" #: f.meta.cc:4516 msgid "(yyyy-mm-dd)" msgstr "(jjjj-mm-tt)" #: f.meta.cc:4519 msgid "stars range" msgstr "Sterne-Wertebereich" #: f.meta.cc:4522 msgid "last version only" msgstr "Nur Neueste Version" #: f.meta.cc:4524 msgid "all/any" msgstr "alle/irgendeines" #: f.meta.cc:4527 msgid "search tags" msgstr "Such-Tags" #: f.meta.cc:4533 msgid "search text" msgstr "Text durchsuchen" #: f.meta.cc:4539 msgid "search files" msgstr "Dateien suchen" #: f.meta.cc:4545 msgid "search locations" msgstr "Ort suchen" #: f.meta.cc:4549 msgid "enter cities, countries" msgstr "Städte, Länder eingeben" #: f.meta.cc:4554 msgid "search other metadata" msgstr "nach andere Metadaten suchen" #: f.meta.cc:4561 msgid "Enter Search Tag" msgstr "Such-Tag eingeben" #: f.meta.cc:4837 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "Um Bilder vom aktuellen Satz zu entfernen, \n" "aktuellen Satz durchsuchen" #: f.meta.cc:4844 msgid "" "to add images to current set, \n" "search all images" msgstr "" "Um Bilder dem aktuellen Satz zuzufügen, \n" "alle Bilder durchsuchen" #: f.meta.cc:4890 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "Suchdaten sind sinnlos \n" " %s %s" #: f.meta.cc:4915 msgid "stars range not reasonable" msgstr "Stern-Spanne unvernünftig" #: f.meta.cc:5129 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "Bilder zugefügt: %d entfernt: %d neue Anzahl: %d" #: f.meta.cc:5132 msgid "no changes made" msgstr "keine Änderungen gemach" #: f.meta.cc:5303 msgid "" "These items are always reported: \n" "date, stars, tags, caption, comment" msgstr "" "Folgende wird immer berichtet: \n" "Datum, Sterne, Tags, Titel, Kommentare" #: f.meta.cc:5328 msgid "Additional Items for Report" msgstr "Zusätzliche Metadaten im Bericht" #: f.meta.cc:5335 msgid "Keyword" msgstr "Schlüsselwort" #: f.meta.cc:5349 msgid "Match Criteria" msgstr "Trefferkriterien" #: f.meta.cc:5455 #, c-format msgid "bad number: %s" msgstr "Irrwert: %s" #: f.meta.cc:5712 msgid "date format is YYYY-MM-DD" msgstr "Datumformat ist JJJJ-MM-TT" #: f.meta.cc:5716 msgid "date is invalid" msgstr "Datum ist ungültig" #: f.meta.cc:5750 msgid "time format is HH:MM [:SS]" msgstr "Zeitformat ist HH:MM [:SS]" #: f.meta.cc:5754 msgid "time is invalid" msgstr "Zeit ist ungültig" #: f.meta.cc:6851 msgid "not found" msgstr "Nicht gefunden" #: f.meta.cc:6852 msgid "location and country required" msgstr "Ort und Land erforderlich" #: f.meta.cc:7111 msgid "choose location" msgstr "Ort wählen" #: f.meta.cc:7408 msgid "Set Map Markers" msgstr "Karten-Markierungen wählen" #: f.meta.cc:7409 msgid "mark all image files" msgstr "alle Bilddateien markieren" #: f.meta.cc:7410 msgid "mark current gallery" msgstr "aktuelle Galerie-Bilder markieren" #: f.meta.cc:7510 msgid "choose map file" msgstr "Kartendatei wählen" #: f.meta.cc:7652 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "fotoxx-maps Packet nicht installiert \n" "(sehe https://kornelix.net" #: f.meta.cc:7657 #, c-format msgid "map file %s is missing" msgstr "Kartendatei %s fehlt" #: f.meta.cc:7661 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "Karte Breitengrad/Längengrad unlogisch %.3f %.3f %.3f %.3f" #: f.meta.cc:7981 f.meta.cc:8508 msgid "No matching images found" msgstr "Keine übereinstimmenden Bilder gefunden" #: f.meta.cc:8073 msgid "Set Map Source" msgstr "Karten Quelle eingeben" #: f.meta.cc:8584 msgid "Net Map Locations" msgstr "Net Karte Ort" #: f.meta.cc:8589 msgid "map location:" msgstr "Karte Ort:" #: f.process.cc:123 f.widgets.cc:559 msgid "Batch Convert" msgstr "Bilder Stapel konvertieren" #: f.process.cc:135 msgid "Sequence Numbers" msgstr "Sequenznummern" #: f.process.cc:137 msgid "base" msgstr "Basis" #: f.process.cc:140 msgid "adder" msgstr "Zuwachs" #: f.process.cc:145 msgid "New Location" msgstr "Neue Speicherstelle" #: f.process.cc:150 msgid "New File Type" msgstr "Neue Dateityp" #: f.process.cc:154 f.process.cc:162 f.process.cc:2538 msgid "no change" msgstr "Keine Änderung" #: f.process.cc:157 f.process.cc:2533 msgid "max. Width" msgstr "Max. Breite" #: f.process.cc:166 msgid "Delete Originals" msgstr "Originale löschen" #: f.process.cc:167 f.process.cc:754 msgid "Copy Metadata" msgstr "Metadaten kopieren" #: f.process.cc:171 f.process.cc:756 f.process.cc:1320 f.repair.cc:121 #: f.widgets.cc:492 msgid "Sharpen" msgstr "Schärfen" #: f.process.cc:181 f.process.cc:757 msgid "Overlay Image" msgstr "Overlay Bild" #: f.process.cc:184 msgid "Width %" msgstr "Breite %" #: f.process.cc:189 msgid "Position" msgstr "Stelle" #: f.process.cc:201 msgid "Make constant size for screen:" msgstr "Größe konstant für Schirm machen:" #: f.process.cc:209 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "plugins: (Jahr Monat Tag Alt-Name Sequenz) $yyyy $mm $dd $oldname $s" #: f.process.cc:331 #, c-format msgid "file type not supported: %s \n" msgstr "Dateityp nicht unterstützt: %s \n" #: f.process.cc:471 f.process.cc:2588 msgid "cannot create new file" msgstr "Kann neue Datei nicht erstellen" #: f.process.cc:517 msgid "updating albums ..." msgstr "Alben werden aktualisiert ..." #: f.process.cc:658 #, c-format msgid "invalid plugin: %s" msgstr "ungültiges Plugin: %s" #: f.process.cc:665 msgid "you must use either $s or $oldname" msgstr "Sie müssen $s oder $oldname benutzen" #: f.process.cc:670 msgid "$s plugin needs base and adder" msgstr "$s Plugin benötigt Basis und Zuwachs" #: f.process.cc:675 msgid "base and adder need $s plugin" msgstr "Basis und Zuwachs benötigen $s Plugin" #: f.process.cc:697 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "Max. größe %d x %d nicht sinnvoll" #: f.process.cc:704 msgid "specify overlay image file" msgstr "Overlay Bilddatei angeben" #: f.process.cc:720 msgid "specify overlay position" msgstr "Overlay Stelle angeben" #: f.process.cc:748 #, c-format msgid "Convert %d image files" msgstr "%d Bilddatei konvertieren" #: f.process.cc:750 msgid "Convert to" msgstr "Konvertieren zu" #: f.process.cc:751 msgid "Resize within" msgstr "Skalieren innerhalb" #: f.process.cc:752 msgid "Output to" msgstr "Schreiben zu" #: f.process.cc:758 msgid "*** Delete Originals ***" msgstr "*** Originale löschen ***" #: f.process.cc:759 msgid "*** Replace Originals ***" msgstr "*** Originale ersetzen ***" #: f.process.cc:760 msgid "PROCEED?" msgstr "FORTSETZEN?" #: f.process.cc:913 f.widgets.cc:560 msgid "Batch Upright" msgstr "Stapel aufrichten" #: f.process.cc:919 msgid "Survey all files" msgstr "Alle Dateien durchsuchen" #: f.process.cc:956 msgid "file cannot be read" msgstr "Datei ist unlesbar" #: f.process.cc:1072 msgid "cannot select both options" msgstr "kann nicht beide Optionen auswählen" #: f.process.cc:1114 f.widgets.cc:561 msgid "Batch Delete/Trash" msgstr "Stapel Löschen/Papierkorb" #: f.process.cc:1119 msgid "delete" msgstr "löschen" #: f.process.cc:1122 msgid "trash" msgstr "Papierkorb" #: f.process.cc:1260 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Stapel RAW konvertieren (Raw Therapee)" #: f.process.cc:1290 msgid "use libraw" msgstr "libraw benutzen" #: f.process.cc:1291 msgid "use Raw Therapee" msgstr "Raw Therapee benutzen" #: f.process.cc:1298 msgid "output location" msgstr "Ausgabe Speicherstelle" #: f.process.cc:1303 msgid "output file type" msgstr "Ausgabedateityp" #: f.process.cc:1322 msgid "amount" msgstr "Wert" #: f.process.cc:1325 msgid "threshold" msgstr "Schwelle" #: f.process.cc:1614 msgid "script file" msgstr "Skriptdatei" #: f.process.cc:1627 msgid "begin making a script file" msgstr "Skriptdatei neu beginnen" #: f.process.cc:1630 msgid "finish making a script file" msgstr "Skriptdatei fertigstellen" #: f.process.cc:1636 msgid "execute a script file" msgstr "Skriptdatei laufen lassen" #: f.process.cc:1696 msgid "script already started" msgstr "Skriptdatei schon angefangen" #: f.process.cc:1700 msgid "start a new script file" msgstr "neue Skriptdatei starten" #: f.process.cc:1723 msgid "perform edits to be included in the script file" msgstr "Bild-Bearbeitungen durchführen, die in der Skriptdatei zu erfasse sind" #: f.process.cc:1741 msgid "script file error" msgstr "Skriptdatei Fehler" #: f.process.cc:1746 #, c-format msgid "%s added to script" msgstr "%s zu Skript addiert" #: f.process.cc:1759 msgid "no script file was started" msgstr "keine Skriptdatei ist im Aufbau" #: f.process.cc:1767 msgid "script file closed" msgstr "Skriptdatei fertiggestellt" #: f.process.cc:1825 msgid "open script file" msgstr "Skriptdatei öffnen" #: f.process.cc:1833 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "Skriptdatei: %s \n" " %s" #: f.process.cc:1855 f.process.cc:1904 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "Öffnen Fehler: %s \n" " %s" #: f.process.cc:1876 #, c-format msgid "unknown edit function: %s" msgstr "unbekannte Bearbeitungsfunktion: %s" #: f.process.cc:1885 #, c-format msgid "load widgets failed: %s" msgstr "Widgets laden fehlgeschlagen: %s" #: f.process.cc:1913 #, c-format msgid "script file format error: %s" msgstr "Skriptdatei Formatfehler: %s" #: f.process.cc:1949 msgid "growisofs not installed" msgstr "growisofs nicht installiert" #: f.process.cc:2001 msgid "no DVD/BlueRay device found" msgstr "kein DVD/BlueRay Gerät gefunden" #: f.process.cc:2024 f.widgets.cc:564 msgid "Burn Images to DVD/BlueRay" msgstr "Bilder auf DVD/BlueRay brennen" #: f.process.cc:2029 msgid "Select device" msgstr "Gerät wählen" #: f.process.cc:2136 f.widgets.cc:565 msgid "Find Duplicate Images" msgstr "Duplikat-Bilder suchen" #: f.process.cc:2138 msgid "Thumbnail size" msgstr "Thumbnailgröße" #: f.process.cc:2142 msgid "pixel difference" msgstr "Pixel-Differenz" #: f.process.cc:2145 msgid "pixel count" msgstr "Pixel-Anzahl" #: f.process.cc:2152 msgid "Duplicates:" msgstr "Duplikate:" #: f.process.cc:2170 #, c-format msgid "only %d image thumbnails found" msgstr "Nur %d Bilder-Thumbnails gefunden" #: f.process.cc:2374 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Datei von gewählten Bilddateien erstellen" #: f.process.cc:2398 f.process.cc:2467 msgid "Output File" msgstr "Ausgabe-Datei" #: f.process.cc:2418 msgid "no input files selected" msgstr "keine Eingabe-Dateien gewählt" #: f.process.cc:2424 msgid "no output file selected" msgstr "keine Ausgabe-Datei gewählt" #: f.process.cc:2523 f.widgets.cc:567 msgid "Export Image Files" msgstr "Bilddatei exportieren" #: f.process.cc:2528 msgid "To Location" msgstr "Zu Speicherstelle" #: f.process.cc:2563 msgid "file type not supported" msgstr "Dateityp nicht unterstützt" #: f.process.cc:2643 msgid "location is not a directory" msgstr "Speicherstelle ist kein Verzeichnis" #: f.repair.cc:164 msgid "dark" msgstr "dunkel" #: f.repair.cc:165 msgid "light" msgstr "hell" #: f.repair.cc:1193 msgid "Apply repeatedly while watching the image." msgstr "Mehrmals anwenden während der Bildbeobachtung." #: f.repair.cc:1194 msgid "Measure" msgstr "Messen" #: f.repair.cc:1228 msgid "Noise Reduction" msgstr "Rauschverminderung" #: f.repair.cc:1247 f.widgets.cc:479 f.widgets.cc:774 fotoxx.h:1218 msgid "Flatten" msgstr "Ausgleichen" #: f.repair.cc:1248 msgid "Median" msgstr "Mittelwert" #: f.repair.cc:1263 msgid "dark areas" msgstr "Dunkle Bereiche" #: f.repair.cc:1265 msgid "all areas" msgstr "Alle Bereiche" #: f.repair.cc:1856 msgid "Measure Noise" msgstr "Rauschen messen" #: f.repair.cc:1857 msgid "Move mouse in a monotone image area." msgstr "Maus in ein monoton Bereich ziehen" #: f.repair.cc:2165 msgid "" "Method 1:\n" " Left-click on red-eye to darken.\n" "Method 2:\n" " Drag down and right to enclose red-eye.\n" " Left-click on red-eye to darken.\n" "Undo red-eye:\n" " Right-click on red-eye." msgstr "" "Methode 1:\n" " Linksklick in das rote Auge zum Abdunkeln.\n" "Methode 2:\n" " Linke Maustaste gedrückt nach rechts unten ziehen zur Markierung des roten " "Auges.\n" " Linksklick in das rote Auge zum Abdunkeln.\n" "Zurücksetzen:\n" " Rechtsklick in das rote Auge." #: f.repair.cc:2181 msgid "Red Eye Reduction" msgstr "Rote-Augen beseitigen" #: f.repair.cc:2642 f.widgets.cc:496 msgid "Color Mode" msgstr "Farb-Modus" #: f.repair.cc:2645 msgid "reset" msgstr "zurücksetzen" #: f.repair.cc:2646 msgid "black/white positive" msgstr "schwarzweiß Positiv" #: f.repair.cc:2647 msgid "black/white negative" msgstr "schwarzweiß Negativ" #: f.repair.cc:2648 msgid "color negative" msgstr "farbiges Negativ" #: f.repair.cc:2649 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.repair.cc:2650 msgid "sepia" msgstr "Sepia" #: f.repair.cc:2861 msgid "Set color depth to 1-16 bits" msgstr "Farbtiefe auf 1-16 Bits festlegen" #: f.repair.cc:2875 msgid "Set Color Depth" msgstr "Farbtiefe festlegen" #: f.repair.cc:3025 f.widgets.cc:498 msgid "Shift Colors" msgstr "Farben verschieben" #: f.repair.cc:3259 f.widgets.cc:499 msgid "Color Saturation" msgstr "Farb-Sättigung" #: f.repair.cc:3448 msgid "+Brightness" msgstr "+Helligkeit" #: f.repair.cc:3449 msgid "+Red -Cyan" msgstr "+Rot -Zyan" #: f.repair.cc:3450 msgid "+Green -Magenta" msgstr "+Grün -Magenta" #: f.repair.cc:3451 msgid "+Blue -Yellow" msgstr "+Blau -Gelb" #: f.repair.cc:3457 fotoxx.h:1261 msgid "Red" msgstr "Rot" #: f.repair.cc:3458 fotoxx.h:1220 msgid "Green" msgstr "Grün" #: f.repair.cc:3459 fotoxx.h:1186 msgid "Blue" msgstr "Blau" #: f.repair.cc:3761 msgid "Input color to match and adjust:" msgstr "Bildfarbe zum Angleichen und Einstellen" #: f.repair.cc:3763 msgid "shift+click on image to select color" msgstr "Shift+Klick am Bild zum Farbe wählen" #: f.repair.cc:3766 f.repair.cc:7937 msgid "Match using:" msgstr "Angleichen mit:" #: f.repair.cc:3767 msgid "Hue" msgstr "Farbton" #: f.repair.cc:3779 msgid "Output Color" msgstr "Ausgabefarbe" #: f.repair.cc:3802 msgid "Adjustment" msgstr "Einstellung" #: f.repair.cc:4320 msgid "Click image to select areas." msgstr "Bild anklicken, um Bereiche zu selektieren." #: f.repair.cc:4352 msgid "Local Color" msgstr "Lokale Farbe" #: f.repair.cc:4363 msgid "Metric:" msgstr "Einheit" #: f.repair.cc:4786 msgid "Color Match Images" msgstr "Bilderfarben anpassen" #: f.repair.cc:4812 msgid "mouse radius for color sample" msgstr "Mausradius für Farbauswahl" #: f.repair.cc:4814 f.repair.cc:4819 fotoxx.h:1250 msgid "Open" msgstr "Öffnen" #: f.repair.cc:4815 msgid "image for source color" msgstr "Bild für Quellfarbe" #: f.repair.cc:4817 msgid "click on image to get source color" msgstr "Bild anklicken zur Quellfarbe bestimmen" #: f.repair.cc:4820 msgid "image to set matching color" msgstr "Bild zur Abgleichfarbe festlegen" #: f.repair.cc:4822 msgid "click on image to set matching color" msgstr "Bild anklicken zur Abgleichfarbe festlegen" #: f.repair.cc:4881 msgid "select source image color first" msgstr "Farbe vom Quellbild zuerst wählen" #: f.repair.cc:5059 msgid "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " msgstr "" " Die RGB-Farben einstellen um die Farbränder \n" " im äußeren Bildbereiche zu minimieren. " #: f.repair.cc:5082 f.widgets.cc:504 msgid "Color Fringes" msgstr "Farbsaume" #: f.repair.cc:5240 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Zum Selektieren, Maus ziehen. \n" "2. Löschen. 3. Wiederholen. " #: f.repair.cc:5262 f.widgets.cc:505 msgid "Smart Erase" msgstr "Smart löschen" #: f.repair.cc:5267 fotoxx.h:1259 msgid "Radius" msgstr "Radius" #: f.repair.cc:5269 f.widgets.cc:493 msgid "Blur" msgstr "Verwischen" #: f.repair.cc:5272 msgid "New Area" msgstr "Neuer Ausschnitt" #: f.repair.cc:5617 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Linie über das Bild zeichnen \n" "in Richtung des Helligkeits Verlaufes" #: f.repair.cc:5656 f.widgets.cc:506 msgid "Brightness Ramp" msgstr "Helligkeit-Verlauf" #: f.repair.cc:6182 f.widgets.cc:507 msgid "Remove Dust" msgstr "Staub entfernen" #: f.repair.cc:6186 msgid "spot size limit" msgstr "Fleckgrößen Limit" #: f.repair.cc:6189 msgid "max. brightness" msgstr "max. Helligkeit" #: f.repair.cc:6192 msgid "min. contrast" msgstr "Min. Kontrast" #: f.repair.cc:6999 f.widgets.cc:510 msgid "Stuck Pixels" msgstr "Feste Pixels" #: f.repair.cc:7005 msgid "pixel group" msgstr "Pixelgruppe" #: f.repair.cc:7013 f.repair.cc:7085 msgid "stuck pixels:" msgstr "Feste Pixels:" #: f.repair.cc:7113 msgid "Load Stuck Pixels" msgstr "Festpixel laden" #: f.repair.cc:7115 msgid "File:" msgstr "Datei:" #: f.repair.cc:7139 f.repair.cc:7196 msgid "Stuck Pixels file" msgstr "Festpixel Datei" #: f.repair.cc:7176 f.tools.cc:4220 msgid "file format error" msgstr "Dateiformat Fehler" #: f.repair.cc:7622 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "links ziehen: Transparenz addieren \n" "rechts ziehen: Opazität addieren" #: f.repair.cc:7654 f.widgets.cc:511 msgid "Paint Transparency" msgstr "Transparenz malen" #: f.repair.cc:7662 msgid "paintbrush radius" msgstr "Pinsel-Radius" #: f.repair.cc:7669 msgid "gradual paint" msgstr "Stufenweise malen" #: f.repair.cc:7923 f.widgets.cc:512 msgid "Add Transparency" msgstr "Transparenz addieren" #: f.repair.cc:7927 msgid "Match Brightness" msgstr "Helligkeit angleichen" #: f.repair.cc:7932 msgid "Match Color" msgstr "Farbe angleichen" #: f.repair.cc:7934 msgid "click on image to select color" msgstr "Bild anklicken um Farbe zu wählen" #: f.repair.cc:7950 msgid "Invert Match" msgstr "Angleichung invertieren" #: f.repair.cc:7953 fotoxx.h:1285 msgid "Transparency" msgstr "Transparenz" #: f.tools.cc:86 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" "Verzeichnisse mit Bilddateien auswählen \n" "(Unterverzeichnisse automatisch einbezogen)." #: f.tools.cc:88 msgid "Select to add, click on X to delete." msgstr "Auswählen zum Zufügen, X anklicken zum Löschen." #: f.tools.cc:89 msgid "directory for thumbnails" msgstr "Verzeichnis für Thumbnails" #: f.tools.cc:90 msgid "extra metadata items to include in index" msgstr "Extra Metadaten Elemente in Index einzubeziehen" #: f.tools.cc:91 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Indizieren abgebrochen. \n" "Indizieren ist für Such- und Karten-Functionen erforderlich \n" "und damit die Gallerieseiten annehmbar schnell sind." #: f.tools.cc:126 f.widgets.cc:579 msgid "Index Image Files" msgstr "Bilddateien Indizieren" #: f.tools.cc:298 msgid "Choose top image directories" msgstr "Oberstes Bild-Verzeichnis wählen" #: f.tools.cc:299 msgid "Choose thumbnail directory" msgstr "Thumbnails-Verzeichnis wählen" #: f.tools.cc:300 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Alle Dateien werden neu indiziert. \n" " Fortfahren?" #: f.tools.cc:372 msgid "" "No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files." msgstr "" "Kein Bildverzeichnis wurde gefunden.\n" "Ein Bildverzeichnis wird erstellt.\n" "Ihre Bilder werden nicht geändert.\n" "Dieser Vorgang kann etwas dauern wenn \n" "Sie mehrere tausend Bilddatein haben." #: f.tools.cc:378 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Ungültiges Verzeichnis: \n" " %s \n" "Bitte entfernen." #: f.tools.cc:381 #, c-format msgid "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" msgstr "" "Thumbnails Verzeichnis: \n" " %s \n" "muß .../thumbnails gennant sein" #: f.tools.cc:384 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Duplikat Verzeichnis: \n" " %s \n" "Bitte entfernen." #: f.tools.cc:443 msgid "specify 1 thumbnail directory" msgstr "Ein Thumbnail Verzeichnis angeben" #: f.tools.cc:628 msgid "Top directories have no images" msgstr "Oberste Verzeichnisse haben keine Bilder" #: f.tools.cc:1075 msgid "Cancel image index function?" msgstr "Bild-Indizieren abbrechen?" #: f.tools.cc:1195 msgid "Recent Files Gallery" msgstr "Zuletzt gesehene Bilder" #: f.tools.cc:1196 msgid "Newest Files Gallery" msgstr "Neueste Bilddateien" #: f.tools.cc:1197 msgid "Specific Gallery" msgstr "Spezifische Galerie" #: f.tools.cc:1198 msgid "Album Gallery" msgstr "Album Galerie" #: f.tools.cc:1199 msgid "Previous Gallery" msgstr "Vorherige Galerie" #: f.tools.cc:1200 msgid "Previous Image File" msgstr "Vorherige Bilddatei" #: f.tools.cc:1201 msgid "Specific Image File" msgstr "Spezifische Bilddatei" #: f.tools.cc:1202 f.widgets.cc:435 msgid "Blank Window" msgstr "Leeres Fenster" #: f.tools.cc:1250 f.widgets.cc:580 msgid "User Settings" msgstr "Benutzer Einstellungen" #: f.tools.cc:1253 msgid "Startup Display" msgstr "Anfangsanzeige" #: f.tools.cc:1264 msgid "Background color:" msgstr "Hintergrundfarbe" #: f.tools.cc:1265 msgid "F-View" msgstr "F-Ansicht" #: f.tools.cc:1268 msgid "G-View" msgstr "G-Ansicht" #: f.tools.cc:1272 msgid "Menu Text" msgstr "Menütext" #: f.tools.cc:1279 msgid "Menu Style" msgstr "Menü-Stil" #: f.tools.cc:1280 msgid "Icons" msgstr "Icons" #: f.tools.cc:1281 msgid "Icons + Text" msgstr "Icons + Texte" #: f.tools.cc:1283 msgid "Icon size" msgstr "Icon-Größe" #: f.tools.cc:1287 msgid "Dialog font" msgstr "Dialog Font" #: f.tools.cc:1292 msgid "Zoomed Image:" msgstr "Zoomed Bild:" #: f.tools.cc:1301 msgid "JPEG save quality" msgstr "JPEG Qualitätsgrad" #: f.tools.cc:1305 msgid "Curve node capture distance" msgstr "Erfassungsabstand zum Kurvenknoten" #: f.tools.cc:1309 msgid "Map marker size" msgstr "Karten-Punkt grosse" #: f.tools.cc:1313 msgid "show last file version only" msgstr "nur neueste Datei-Version zeigen" #: f.tools.cc:1316 msgid "shift image to right margin" msgstr "Bild zum rechten Rand versetzen" #: f.tools.cc:1319 f.tools.cc:1324 msgid "image index level" msgstr "Bild-Index Grad" #: f.tools.cc:1321 msgid "Fotoxx started directly" msgstr "Fotoxx direkt gestartet" #: f.tools.cc:1326 msgid "Fotoxx started by file manager" msgstr "Fotoxx durch Datei-Manager gestartet" #: f.tools.cc:1329 msgid "RAW file types" msgstr "RAW Dateitypen" #: f.tools.cc:1333 msgid "video file types" msgstr "video Datei-Typen" #: f.tools.cc:1440 msgid "Select startup directory" msgstr "Anfangsverzeichnis wählen" #: f.tools.cc:1447 msgid "Select startup image file" msgstr "Anfangsbilddatei wählen" #: f.tools.cc:1454 msgid "Select startup album" msgstr "Start-Album wählen" #: f.tools.cc:1497 msgid "startup directory is invalid" msgstr "Anfangsverzeichnes ungültig" #: f.tools.cc:1508 msgid "startup file is invalid" msgstr "Anfangsdatei ungültig" #: f.tools.cc:1683 f.widgets.cc:581 msgid "Keyboard Shortcuts" msgstr "Schnelltasten" #: f.tools.cc:1690 msgid "Reserved Shortcuts \n" msgstr "Reservierte Schnelltasten \n" #: f.tools.cc:1691 msgid " F1 User Guide for current function \n" msgstr " F1 Benutzeranweisung für aktuelle Funktion \n" #: f.tools.cc:1692 msgid " F10 Full Screen with menus \n" msgstr " F10 Maximalfenster mit Menüs \n" #: f.tools.cc:1693 msgid " F11 Full Screen without menus \n" msgstr " F11 Maximalfenster ohne Menüs \n" #: f.tools.cc:1694 msgid " Escape Quit dialog; Quit Fotoxx \n" msgstr " Escape Dialog beenden; Fotoxx beenden \n" #: f.tools.cc:1695 msgid " Ctrl+H Show hidden files in Gallery mode \n" msgstr " Strg+H Versteckte Dateien in Galerie Ansicht zeigen \n" #: f.tools.cc:1696 msgid " Delete Delete/Trash dialog \n" msgstr " Delete Dialog zum löschen/Abfall \n" #: f.tools.cc:1697 msgid " Arrows Previous/Next Image or Gallery page \n" msgstr " Pfeile Vorheriges/Nächstes Bild oder Galerieseite \n" #: f.tools.cc:1757 msgid "Edit KB Shortcuts" msgstr "Schnelltasten bearbeiten" #: f.tools.cc:1766 msgid "shortcut key:" msgstr "Schnelltaste:" #: f.tools.cc:1767 msgid "(enter key)" msgstr "(Enter-Taste)" #: f.tools.cc:1768 f.tools.cc:1911 f.tools.cc:2015 msgid "(no selection)" msgstr "(kein Auswahl)" #: f.tools.cc:1905 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reserviert, kann nicht verwendet werden" #: f.tools.cc:2036 msgid "unable to save KB-shortcuts file" msgstr "Kann Schnelltasten-Datei nicht speichern" #: f.tools.cc:2356 f.widgets.cc:583 msgid "Grid Lines" msgstr "Gitterlinien" #: f.tools.cc:2365 msgid "x-spacing" msgstr "x-Abstand" #: f.tools.cc:2366 msgid "x-count" msgstr "x-Anzahl" #: f.tools.cc:2367 msgid "x-enable" msgstr "x-aktivieren" #: f.tools.cc:2373 msgid "y-spacing" msgstr "y-Abstand" #: f.tools.cc:2374 msgid "y-count" msgstr "y-Anzahl" #: f.tools.cc:2375 msgid "y-enable" msgstr "y-aktivieren" #: f.tools.cc:2495 f.widgets.cc:584 msgid "Line Color" msgstr "Linienfarbe" #: f.tools.cc:2560 msgid "Click image to select pixels." msgstr "Bild anklicken zum Pixeln auswählen" #: f.tools.cc:2595 f.widgets.cc:585 msgid "Show RGB" msgstr "RGB-Werte zeigen" #: f.tools.cc:2890 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key X to toggle dialog." msgstr "" "Maus über Bild ziehen. \n" "Links klicken zum Abbrechen. \n" "Taste X zum Dialog starten/enden." #: f.tools.cc:2918 f.widgets.cc:586 msgid "Magnify Image" msgstr "Bild Vergrößern" #: f.tools.cc:2927 msgid "X-size" msgstr "X-Größe" #: f.tools.cc:3172 msgid "Darkest and Brightest Pixels" msgstr "Dunkelste und hellste Pixeln" #: f.tools.cc:3195 msgid "Dark Limit" msgstr "Dunkel Grenzwert" #: f.tools.cc:3196 msgid "Bright Limit" msgstr "Hell Grenzwert" #: f.tools.cc:3285 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "Helligkeit soll einen allmählichen Ansteig \n" "zeigen, auch ganz bis zu den Rändern." #: f.tools.cc:3376 f.widgets.cc:589 msgid "Monitor Gamma" msgstr "Bildschirm-Gamma" #: f.tools.cc:3443 msgid "Available Translations" msgstr "Vorhandene Übersetzungen" #: f.tools.cc:3447 msgid "Set Language" msgstr "Sprache wechseln" #: f.tools.cc:3573 msgid "Change Color Profile" msgstr "Farbraum umwandeln" #: f.tools.cc:3577 msgid "input profile" msgstr "Input-Farbraum" #: f.tools.cc:3581 msgid "output profile" msgstr "Ausgabe-Farbraum" #: f.tools.cc:3602 msgid "Unable to change EXIF color profile" msgstr "EXIF Farbprofil ändern fehlgeschlagen" #: f.tools.cc:3604 msgid "automatic new version created" msgstr "neue Version automatisch kreiert" #: f.tools.cc:3613 msgid "color profile" msgstr "Farbraum" #: f.tools.cc:3661 f.tools.cc:3667 #, c-format msgid "unknown cms profile %s" msgstr "CMS Farbraum unbekannt %s" #: f.tools.cc:3770 f.widgets.cc:593 msgid "Calibrate Printer" msgstr "Drucker calibrieren" #: f.tools.cc:3795 msgid "print color chart" msgstr "Farbkarte drucken" #: f.tools.cc:3796 msgid "scan and save color chart" msgstr "Farbkarte einscannen und speichern" #: f.tools.cc:3797 msgid "align and trim color chart" msgstr "Farbkarte richten und schneiden" #: f.tools.cc:3798 msgid "open and process color chart" msgstr "Farbkarte öffnen und verarbeiten" #: f.tools.cc:3799 msgid "print image with revised colors" msgstr "Bilddatei mit geänderten Farben drucken" #: f.tools.cc:3940 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Gedruckte Farbkarte einscannen. \n" "Dunkelste Reihe muss oben sein. \n" "Speichern in %s/" #: f.tools.cc:3955 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Eingescannte Farbkartedatei öffnen und bearbeiten. \n" "Schräglauf oder Verdrehung (vom Scanner) korrigieren. \n" "(Die Perspektive Verbessern Funktion benutzen.) \n" "Die dünnen grünen Ränder GENAU abschneiden." #: f.tools.cc:3987 msgid "Open the trimmed color chart file" msgstr "Geschnittene Farbkartendatei öffnen" #: f.tools.cc:4120 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" "Ausgabe-Kalibrierungsdateinamen festsetzen \n" "[deinen Kalibrierungsnamen].dat" #: f.tools.cc:4160 msgid "Color map file to use" msgstr "Farbdatendatei zur Verwendung" #: f.tools.cc:4179 msgid "Select the image file to print." msgstr "Bilddatei zum drucken öffnen." #: f.tools.cc:4244 msgid "converting colors..." msgstr "Farben werden konvertiert ..." #: f.tools.cc:4344 msgid "Image colors are converted for printing." msgstr "Bildfarben für das Drucken sind geändert." #: f.tools.cc:4467 msgid "This function is for AppImage package only" msgstr "Diese Funktion ist nur für AppImage Paket" #: f.tools.cc:4471 msgid "" "Completely remove the AppImage package \n" "Press F1 for more information. \n" "Continue?" msgstr "" "AppImage Paket wird vollständig entfernt \n" "Für mehr Information, F1 drücken. \n" "Fortfahren?" #: f.warp.cc:94 f.widgets.cc:515 msgid "Unbend" msgstr "Entkrümmen" #: f.warp.cc:371 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Die vier Ecken eines Tetragonalbereich anklicken. [Anwenden] drücken. \n" " Das Tetragon wird zu einem geraden Rechteck verzogen." #: f.warp.cc:388 msgid "Perspective Correction" msgstr "Perspective Verbessern" #: f.warp.cc:621 msgid "must have 4 corners" msgstr "Vier Ecken sind nötig" #: f.warp.cc:745 msgid "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." msgstr "" " Ausschnitt zum Krümmen wählen mittels Menü, Ausschnitt wählen. \n" " [Krümmen starten] drücken, Ausschnitt mit der Maus ziehen. \n" " Mehrmals ziehen/strecken bis zum erwünschten Ergebniss. \n" " Wenn fertig, anderen Ausschnitt wählen oder [Fertig] drücken." #: f.warp.cc:758 f.widgets.cc:517 msgid "Warp area" msgstr "Ausschnitt Krümmen" #: f.warp.cc:763 msgid "start warp" msgstr "Krümmen starten" #: f.warp.cc:1149 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" " Gesichts Ausschnitt selektieren. \n" " Zentrum der Verzerrung anklichen. \n" " Schieber bewegen. \n" #: f.warp.cc:1176 f.widgets.cc:518 msgid "Unwarp Closeup" msgstr "Großaufnahme entkrümmen" #: f.warp.cc:1356 f.warp.cc:1671 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Bildstelle mit der Maus ziehen. \n" " Mehrmals ziehen bis zufriedenstellend. \n" " Wenn fertig, [Fertig] drücken." #: f.warp.cc:1374 f.widgets.cc:519 msgid "Warp curved" msgstr "Gebogen krümmen" #: f.warp.cc:1689 f.widgets.cc:520 msgid "Warp linear" msgstr "Linear krümmen" #: f.warp.cc:2005 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Bildecke mit der Maus ziehen. \n" " Mehrmals ziehen bis zufriedengestellt. \n" " Wenn fertig, [Fertig] drücken." #: f.warp.cc:2021 f.widgets.cc:521 msgid "Warp affine" msgstr "Affine krümmen" #: f.warp.cc:2368 f.widgets.cc:522 msgid "Flatten Book Page" msgstr "Buchseite flachmachen" #: f.warp.cc:2369 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Bild einer Seite knapp zuschneiden. \n" "Obere und untere Ränder mit 4+ \n" "Mausklicks markieren, dann flachmachen: " #: f.warp.cc:2372 msgid "Stretch curved-down surfaces:" msgstr "Nach unten gebogenen Flächen ausdehnen:" #: f.warp.cc:2427 msgid "Top:" msgstr "Oben:" #: f.warp.cc:2430 msgid "Bottom:" msgstr "Unten:" #: f.warp.cc:2811 f.widgets.cc:523 msgid "Spherical Projection" msgstr "Sphärische Projektion" #: f.warp.cc:2855 msgid "Magnify" msgstr "Vergrößern" #: f.warp.cc:3029 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" " Ausschnitte die unverändert bleiben ausswählen. \n" " Bild von der Ecke oben links ziehen. \n" " Wenn fertig, [fertig] drucken." #: f.warp.cc:3045 f.widgets.cc:524 msgid "Selective Rescale" msgstr "Selektiv Skalieren" #: f.warp.cc:3068 msgid "select areas first" msgstr "Ausschnitte erst auswählen" #: f.warp.cc:3290 f.widgets.cc:525 msgid "Make Waves" msgstr "Wellen machen" #: f.warp.cc:3297 msgid "wavelength" msgstr "Wellenlänge" #: f.warp.cc:3299 msgid "variance" msgstr "Varianz" #: f.warp.cc:3310 msgid "perspective" msgstr "Perspektive" #: f.warp.cc:3484 f.warp.cc:3515 msgid "Twist" msgstr "Verdrehen" #: f.warp.cc:3512 msgid "drag mouse to set center" msgstr "Maus ziehen um die Achse zu stellen" #: f.widgets.cc:103 msgid "Album" msgstr "Album" #: f.widgets.cc:105 msgid "TOP" msgstr "TOP" #: f.widgets.cc:171 msgid "Current Image File (key F)" msgstr "Aktuele Bilddatei (Taste F)" #: f.widgets.cc:172 msgid "Thumbnail Gallery (key G)" msgstr "Thumbnail-Galerie (Taste G)" #: f.widgets.cc:173 msgid "World Maps (key W)" msgstr "Weltkarten (Taste W)" #: f.widgets.cc:174 msgid "Net Maps (key M)" msgstr "Net-Karten (Taste M)" #: f.widgets.cc:177 msgid "Favorite Functions" msgstr "Favoriten Funktionen" #: f.widgets.cc:178 msgid "File: Open, RAW, Rename, Delete, Print" msgstr "Datei: Öffnen, RAW, Umbenennen, Löschen, Drucken" #: f.widgets.cc:179 msgid "Save modified image file to disk" msgstr "Geänderte Bilddatei auf Festplatte speichern" #: f.widgets.cc:180 msgid "Open previous or next file (left/right mouse click)" msgstr "Vorherige oder Nächste Bilddatei öffnen (links/rechts Maus-Klick)" #: f.widgets.cc:181 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadaten: Titeln, Tags, Bewertungen, Geotags, Suchen ..." #: f.widgets.cc:182 msgid "Areas: Select areas to edit, copy and paste" msgstr "Ausschnitt: Bildausschnitte wählen zum Bearbeiten, Kopieren, Einfügen" #: f.widgets.cc:183 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "" "Bearbeiten: Schneiden, Drehen, Skalieren, Helligkeit, Kontrast, Texte ..." #: f.widgets.cc:184 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Ausbessern: Schärfen, Säubern, Rote-Augen, Farbe, Malen, Klonen ..." #: f.widgets.cc:185 msgid "Warp: Fix Perspective, Warp or unwarp image ..." msgstr "Krümmen: Perspektive verbessern, Bild krümmen oder entkrümmen ..." #: f.widgets.cc:186 msgid "Effects: Special Effects, Arty Transforms" msgstr "Effekte: Trickeffekte, kunstlerisch Verwandlungen" #: f.widgets.cc:187 msgid "" "left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "links/rechts Klick: Eine Änderung widerrufen oder zurückholen \n" " mit Taste A: um alle Änderungen einzuschließen mittig Klick: zurück zu " "beliebiger Änderung" #: f.widgets.cc:190 msgid "Tools: Index, Settings, Shortcuts, Magnify ..." msgstr "Werkzeuge: Indizieren, Optionen, Abkürzungen, Vergrößern ..." #: f.widgets.cc:191 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Hilfe: Schnellstart, Benutzeranweisung, neueste Änderungen ..." #: f.widgets.cc:194 msgid "Sync Gallery, Albums, Slide Show" msgstr "Synch. Galerie, Alben, Diaschau" #: f.widgets.cc:195 msgid "set and recall bookmarked image locations" msgstr "Lesezeichen-markierte Stellen setzen und zurückrufen" #: f.widgets.cc:196 msgid "increase thumbnail size" msgstr "Thumbnails vergrößern" #: f.widgets.cc:197 msgid "reduce thumbnail size" msgstr "Thumbnails verkleinern" #: f.widgets.cc:198 msgid "change sort order" msgstr "Reihenfolge ändern" #: f.widgets.cc:199 msgid "jump to beginning (top)" msgstr "Zum Anfang springen (oben)" #: f.widgets.cc:200 msgid "jump to end (bottom)" msgstr "Zu Ende springen (unten)" #: f.widgets.cc:201 msgid "previous page" msgstr "Vorherige Seite" #: f.widgets.cc:202 msgid "next page" msgstr "Nächste Seite" #: f.widgets.cc:203 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Verbund: HDR, HDF, Panorama, Stack, Mashup" #: f.widgets.cc:204 msgid "Process: convert, export, metadata, search ..." msgstr "Prozess: konvertieren, exportieren, Metadaten, Suchfunktionen ..." #: f.widgets.cc:207 msgid "Choose a map file" msgstr "Kartendatei auswählen" #: f.widgets.cc:208 f.widgets.cc:212 msgid "Mark all images or current gallery" msgstr "Alle Bilder oder aktuelle Galerie markieren" #: f.widgets.cc:211 msgid "Choose Internet map source" msgstr "Internet Kartenquelle auswählen" #: f.widgets.cc:213 msgid "Save and recall named map locations" msgstr "Benannte Karten-Orte speichern und zurückrufen" #: f.widgets.cc:216 msgid "Open another window" msgstr "Neues Bild-Fenster aufmachen" #: f.widgets.cc:217 msgid "Open a new image file" msgstr "Neue Bilddatei öffnen" #: f.widgets.cc:218 f.widgets.cc:430 msgid "Cycle 2 Previous Files" msgstr "2 vorherige Bilder abwechseln" #: f.widgets.cc:219 f.widgets.cc:431 msgid "Cycle 3 Previous Files" msgstr "3 vorherige Bilder abwechseln" #: f.widgets.cc:220 msgid "Open a recently seen file" msgstr "Eine kürzliche gesehene Bilddatei öffnen" #: f.widgets.cc:221 msgid "Open a newly added file" msgstr "neu eingefügte Datei öffnen" #: f.widgets.cc:222 msgid "Open and edit a camera RAW file" msgstr "RAW-Datei öffnen und bearbeiten" #: f.widgets.cc:223 msgid "View a 360 degree panorama image file" msgstr "Bilddatei in 360 Grad Panorama-Ansicht betrachten" #: f.widgets.cc:224 msgid "Change the image file name" msgstr "Bilddateinamen ändern" #: f.widgets.cc:225 msgid "Copy or Move an image file to a new location" msgstr "Bilddatei zum neuen Verzeichnis kopieren oder versetzen" #: f.widgets.cc:226 msgid "Copy an image file to the desktop" msgstr "Bild-Datei zu Arbeitsoberfläche kopieren" #: f.widgets.cc:227 msgid "Copy image file to the clipboard" msgstr "Bilddatei zur Zwischenablage kopieren" #: f.widgets.cc:228 msgid "Copy image file to the image cache" msgstr "Bilddatei zur Bildercache kopieren" #: f.widgets.cc:229 msgid "Show location on Interet map" msgstr "Ort in der Net-Karte zeigen" #: f.widgets.cc:230 msgid "Create a blank image" msgstr "Leerbild erstellen" #: f.widgets.cc:231 msgid "toggle - blank or restore window" msgstr "umschalten - Leerschirm oder aktuelles Bild" #: f.widgets.cc:232 msgid "Delete or trash image file" msgstr "Bilddatei löschen oder in Abfall schieben" #: f.widgets.cc:233 msgid "Print the current image" msgstr "Aktuelles Bild drucken" #: f.widgets.cc:234 msgid "Print current image with adjusted colors" msgstr "Aktuelle Bilddatei mit angepassten Farben drucken" #: f.widgets.cc:235 f.widgets.cc:445 msgid "Quit Fotoxx" msgstr "Fotoxx beenden" #: f.widgets.cc:238 msgid "List a few key metadata items" msgstr "Wichtigste Metadaten auflisten" #: f.widgets.cc:239 msgid "List all metadata items" msgstr "Alle Metadaten auflisten" #: f.widgets.cc:240 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Bild Tags/Geotags/Titel/Bewertung ... verarbeiten" #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Beliebige Bild-Metadaten bearbeiten" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Gewählte Bild-Metadaten entfernen" #: f.widgets.cc:243 msgid "(Toggle) show captions and comments" msgstr "(Knebel) Titel und Kommentare zeigen" #: f.widgets.cc:246 msgid "Select object or area for editing" msgstr "Ausschnitt zum Bearbeiten auswählen" #: f.widgets.cc:247 msgid "Find a gap in an area outline" msgstr "Lücke in Ausschnitt-Umrandung finden" #: f.widgets.cc:248 msgid "Select hairy or irregular edge" msgstr "Flockigen oder unregelmässigen Rand selektieren" #: f.widgets.cc:249 msgid "Show (outline) existing area" msgstr "Aktuellen Ausschnitt zeigen (umranden)" #: f.widgets.cc:250 msgid "Hide existing area" msgstr "Aktuellen Ausschnitt ausblenden" #: f.widgets.cc:251 msgid "Enable area for editing" msgstr "Ausschnitt zum Bearbeiten aktivieren" #: f.widgets.cc:252 msgid "Disable area for editing" msgstr "Ausschnitt ausschalten" #: f.widgets.cc:253 msgid "Reverse existing area" msgstr "Aktuellen Ausschnitt ins Gegenteil verkehren" #: f.widgets.cc:254 msgid "Erase existing area" msgstr "Aktuellen Ausschnitt löschen" #: f.widgets.cc:255 msgid "Copy area for later pasting into image" msgstr "Ausschnitt für späteres Einfügen kopieren" #: f.widgets.cc:256 msgid "Save area to a file with transparency" msgstr "Auschnitt mit Transparenz in Datei speichern" #: f.widgets.cc:257 msgid "Open a file and paste as area into image" msgstr "Datei öffnen und als Auschnitt im Bild zufügen" #: f.widgets.cc:258 msgid "Paste previously copied area into image" msgstr "Vorher kopierten Ausschnitt ins Bild einfügen" #: f.widgets.cc:261 msgid "Trim/Crop margins and/or Rotate" msgstr "Ränder schneiden und/oder Drehen" #: f.widgets.cc:262 msgid "Upright a rotated image" msgstr "Gedrehtes Bild aufrichten" #: f.widgets.cc:263 msgid "Change pixel dimensions" msgstr "Pixelgröße ändern" #: f.widgets.cc:264 f.widgets.cc:265 msgid "Fast auto enhance that may work OK" msgstr "Schnelle Auto-Verbesserung die klappen könnte" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Helligkeit, Kontrast, Farbe einstellen" #: f.widgets.cc:267 msgid "Edit brightness distribution" msgstr "Helligkeits-Verteilung ändern" #: f.widgets.cc:268 msgid "Magnify brightness gradients to enhance details" msgstr "Helligkeitsgraduierungen vergrößen um Details hervorzuheben" #: f.widgets.cc:269 msgid "Flatten brightness distribution" msgstr "Helligkeitsverteilung abflachen" #: f.widgets.cc:270 msgid "Rescale brightness - reduce color caste and fog/haze" msgstr "Helligkeit neu skalieren - Farbstich und Trübung reduzieren" #: f.widgets.cc:271 msgid "Mirror image horizontally or vertically" msgstr "Bild horizontal oder vertikal spiegeln" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Bild-Pixel anmalen mit der Maus" #: f.widgets.cc:273 msgid "Clone image pixels using the mouse" msgstr "Bild-Pixel klonen mit der Maus" #: f.widgets.cc:274 msgid "Blend image pixels using the mouse" msgstr "Bild-Pixel mit der Maus mischen" #: f.widgets.cc:277 msgid "Paint edit function gradually with mouse" msgstr "Änderungs-Funktion allmählich mit der Maus anmalen" #: f.widgets.cc:278 msgid "Leverage edits by brightness or color" msgstr "Änderungen verstärken nach Helligkeit oder Farbwert" #: f.widgets.cc:279 msgid "Edit plugins menu or run a plugin function" msgstr "Plugins-Menü editieren oder Plugin-Funktion starten" #: f.widgets.cc:282 msgid "Make the image look sharper" msgstr "Bild schärfer machen" #: f.widgets.cc:283 msgid "Make the image look fuzzy" msgstr "Bild verschwommen machen" #: f.widgets.cc:284 msgid "Filter noise from low-light photos" msgstr "Rausch in unterbelichteten Fotos ausfiltern" #: f.widgets.cc:285 msgid "Fix red-eyes from electronic flash" msgstr "Rote Augen von Blitzfotos entfernen" #: f.widgets.cc:286 msgid "Make BW/color, negative/positive, sepia" msgstr "SW/farbiges Negativ/Positiv, Sepia machen" #: f.widgets.cc:287 msgid "Reduce color depth (posterize)" msgstr "Farbtiefe reduzieren (Posterisation)" #: f.widgets.cc:288 msgid "Shift/convert colors into other colors" msgstr "Farben in andere Farben verwandeln" #: f.widgets.cc:289 msgid "Adjust color intensity (saturation)" msgstr "Farbintensität (Sättigung) justieren" #: f.widgets.cc:290 msgid "Adjust color using RGB or CMY colors" msgstr "Farben justieren mit RGB oder CMY Farben" #: f.widgets.cc:291 msgid "Adjust color using HSL colors" msgstr "Farben justieren mit HSL Farben" #: f.widgets.cc:292 msgid "Adjust color in selected image areas" msgstr "Farben in ausgewählten Bereichen einstellen" #: f.widgets.cc:293 msgid "Match colors on one image with another" msgstr "Farben in einem Bild an ein anderes anpassen" #: f.widgets.cc:294 msgid "Reduce Chromatic Abberation" msgstr "Chromatische Aberration reduzieren" #: f.widgets.cc:295 msgid "Remove unwanted objects" msgstr "Unerwünschte Gegenstände entfernen" #: f.widgets.cc:296 msgid "Add a brightness/color ramp across the image" msgstr "Helligkeit/Farbe Verlauf quer über das Bild addieren" #: f.widgets.cc:297 msgid "Remove dust spots from scanned slides" msgstr "Staubflecken von gescannten Dias entfernen" #: f.widgets.cc:298 msgid "Smoothen edges with jaggies" msgstr "Ränder mit Treppeneffekt glätten" #: f.widgets.cc:299 msgid "Erase known hot and dark pixels" msgstr "Bekannte heiße und dunkle Pixeln löschen" #: f.widgets.cc:300 msgid "Paint image transparency using the mouse" msgstr "Bild-Transparenz mit der Maus malen" #: f.widgets.cc:301 msgid "Add image transparency based on image attributes" msgstr "Transparenz addieren basierend auf Bildeigenschaften" #: f.widgets.cc:304 msgid "Remove curvature, esp. panoramas" msgstr "Entkrümmen, besonders bei Panoramen" #: f.widgets.cc:305 msgid "Straighten objects seen from an angle" msgstr "Aus einem Winkel gesehene Objekte gerade machen" #: f.widgets.cc:306 msgid "Distort image areas using the mouse" msgstr "Bild-Ausschnitt mit der Maus krümmen" #: f.widgets.cc:307 msgid "Unwarp closeup face photo to remove distortion" msgstr "Verkrümmte Gesichts-Großaufnahme verbessern" #: f.widgets.cc:308 f.widgets.cc:309 f.widgets.cc:310 msgid "Distort the whole image using the mouse" msgstr "Ganzes Bild mit der Maus krümmen" #: f.widgets.cc:311 msgid "Flatten a photographed book page" msgstr "Foto von einer Buchseite flachmachen" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "Sphärische Projektion vom Bild erzeugen" #: f.widgets.cc:313 msgid "Rescale image outside selected areas" msgstr "Bild ausserhalb ausgewählter Ausschnitte skalieren" #: f.widgets.cc:314 msgid "Warp an image with a wave pattern" msgstr "Bild mit Wellenmuster verformen" #: f.widgets.cc:315 msgid "Twist image centered at mouse position" msgstr "Bild verdrehen um Maus-Position" #: f.widgets.cc:318 msgid "Convert to simulated sketch" msgstr "In eine simulierten Zeichnung verwandeln" #: f.widgets.cc:319 msgid "Convert image into a cartoon drawing" msgstr "Bild in ein Cartoon-Zeichnung wandeln" #: f.widgets.cc:320 msgid "Convert to line drawing (edge detection)" msgstr "In eine Stift-Zeichnung verwandeln" #: f.widgets.cc:321 msgid "Convert to solid color drawing" msgstr "In eine Farb-Zeichnung verwandeln" #: f.widgets.cc:322 msgid "Graduated Blur depending on contrast" msgstr "Gestaffelte Verwischung je nach dem Kontrast" #: f.widgets.cc:323 msgid "Create an embossed or 3D appearance" msgstr "In eine Prägung mit 3D Wirkung verwandeln" #: f.widgets.cc:324 msgid "Convert to square tiles" msgstr "In Kacheln verwandeln" #: f.widgets.cc:325 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "In Rasterpunkte verwandeln (Roy Lichtenstein Effekt)" #: f.widgets.cc:326 msgid "Convert into a simulated painting" msgstr "In ein simuliertes Gemälde verwandeln" #: f.widgets.cc:327 msgid "Change brightness or color radially" msgstr "Helligkeit oder Farbwert radial ändern" #: f.widgets.cc:328 msgid "Add texture to an image" msgstr "Textur auf Bild zufügen" #: f.widgets.cc:329 msgid "Tile image with a repeating pattern" msgstr "Bild mit sich wiederholenden Muster fliesen" #: f.widgets.cc:330 msgid "Create a mosaic with tiles made from all images" msgstr "Mosaik mit Kacheln aus allen Bilder erstellen" #: f.widgets.cc:331 msgid "Process an image using a custom kernel" msgstr "Bild mit benutzerspezifischem Kernel bearbeiten" #: f.widgets.cc:332 msgid "Blur an image in the direction of mouse drags" msgstr "Bild verwischen in Maus-Schlepp-Richtung" #: f.widgets.cc:333 msgid "Increasing blur with distance from selected areas" msgstr "Wachsende Unschärfe mit Abstand vom Ausschnitt" #: f.widgets.cc:334 msgid "Change color hue using an algorithm" msgstr "Farbton ändern mittles Algorithmus" #: f.widgets.cc:337 msgid "Combine bright/dark images for better detail" msgstr "Hell- und Dunkel-Bilder kombinieren um Details zu verbessern" #: f.widgets.cc:338 msgid "Combine near/far focus images for deeper focus" msgstr "Bilder mit näher/ferner Fokus kombinieren für mehr Schärfentiefe" #: f.widgets.cc:339 msgid "Combine images to erase passing people, etc." msgstr "Bilder kombinieren um vorbeigehende Menschen, usw. zu entfernen" #: f.widgets.cc:340 msgid "Combine noisy images into a low-noise image" msgstr "Rauschige Bilder zu einem Niedrig-Rausch Bild kombinieren" #: f.widgets.cc:341 msgid "Combine images into a panorama" msgstr "Bilder zu einem Panorama-Bild kombinieren" #: f.widgets.cc:342 msgid "Combine images into a vertical panorama" msgstr "Bilder zu einem Vertikal-Panorama kombinieren" #: f.widgets.cc:343 msgid "Combine images into a panorama (panorama tools)" msgstr "Bilder zu einem Panorama-Bild kombinieren (panorama tools)" #: f.widgets.cc:344 msgid "Arrange images and text in a layout (montage)" msgstr "Bilder und Texte auf Lageplan ordnen (Montage)" #: f.widgets.cc:345 msgid "Combine images into a montage of images" msgstr "Bilder kombinieren in ein Bildmontage" #: f.widgets.cc:348 msgid "Rename/convert/resize/move multiple files" msgstr "Bilddateien umbenennen/konvertieren/skalieren/versetzen" #: f.widgets.cc:349 msgid "Upright multiple rotated image files" msgstr "Mehrere gedrehte Bilddateien aufrichten" #: f.widgets.cc:350 msgid "Delete or Trash multiple files" msgstr "Mehrere Dateien Löschen oder in Papierkorb" #: f.widgets.cc:351 msgid "Convert camera RAW files using libraw or Raw Therapee" msgstr "Stapel RAW-Dateien konvertieren mit libraw oder Raw Therapee" #: f.widgets.cc:352 msgid "Build and run edit script files" msgstr "Bearbeitungs-Skriptdatei bauen und laufen lassen" #: f.widgets.cc:353 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Gewählte Bilddateien zu DVD/BlueRay Disc brennen" #: f.widgets.cc:354 msgid "Search all image files and report duplicates" msgstr "Alle Bilddateien durchsuchen und Duplikate anzeigen" #: f.widgets.cc:356 msgid "Export selected image files to a directory" msgstr "Ausgewählte Bilddatei zu einem Verzeichnis exportieren" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Tags in mehreren Bilder zuweisen/entfernen" #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Tagnamen convertieren auf alle Bilder" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "Foto Datum/Zeit ändern oder verschieben" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Metadata addieren/ändern/löschen auf mehreren Bilder" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Metadaten für mehreren Bilder auflisten" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Geotags addieren/ändern auf mehreren Bilder" #: f.widgets.cc:363 msgid "Find all images for a location [date]" msgstr "Alle Bilder für einen Ort [Datum] finden" #: f.widgets.cc:364 msgid "Show image counts by month, select and report" msgstr "Bildzahl pro Monat anzeigen, selektieren und berichten" #: f.widgets.cc:365 msgid "Find images meeting select criteria" msgstr "Bilder nach Auswahlkriterien finden" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Neue Dateien indizieren und Thumbnails erstellen" #: f.widgets.cc:369 msgid "Change user preferences" msgstr "Benutzereinstellungen ändern" #: f.widgets.cc:370 msgid "Change Keyboard Shortcut Keys" msgstr "Tastatur-Schnelltasten bearbeiten" #: f.widgets.cc:371 msgid "Show a brightness distribution graph" msgstr "Helligkeitsverteilungs-Grafik darstellen" #: f.widgets.cc:372 msgid "Show or revise grid lines" msgstr "Gitterlinien zeigen oder ändern" #: f.widgets.cc:373 msgid "Change color of foreground lines" msgstr "Vordergrund Linienfarbe ändern" #: f.widgets.cc:374 msgid "Show RGB colors at mouse click" msgstr "RGB-Werte auf Maus-klick zeigen" #: f.widgets.cc:375 msgid "Magnify image around the mouse position" msgstr "Bild um Maus-Position herum vergrößern" #: f.widgets.cc:376 msgid "Highlight darkest and brightest pixels" msgstr "Dunkelste und hellste Pixeln hervorheben" #: f.widgets.cc:377 msgid "Chart to adjust monitor color" msgstr "Grafik um Monitor-Farben einzustellen" #: f.widgets.cc:378 msgid "Chart to adjust monitor gamma" msgstr "Grafik um Monitor-Gamma einzustellen" #: f.widgets.cc:379 msgid "Change the GUI language" msgstr "GUI Sprache wechseln" #: f.widgets.cc:380 msgid "Report missing translations" msgstr "Fehlende Übersetzungen auflisten" #: f.widgets.cc:381 msgid "Convert to another color profile" msgstr "In einen anderen Farbraum umwandeln" #: f.widgets.cc:382 msgid "Calibrate printer colors" msgstr "Druckerfarben calibrieren" #: f.widgets.cc:383 msgid "Completely uninstall AppImage package" msgstr "AppImage Paket vollständig entfernen" #: f.widgets.cc:384 msgid "Memory and CPU (to terminal/logfile)" msgstr "Speicher und CPU (zu Terminal/Logdatei)" #: f.widgets.cc:387 msgid "Quick Start mini-guide" msgstr "Schnell-Start Mini-Anweisung" #: f.widgets.cc:388 msgid "Read the user guide" msgstr "Benutzeranleitung lesen" #: f.widgets.cc:389 msgid "Recent user guide changes" msgstr "Neueste Benutzeranleitungs-Änderungen" #: f.widgets.cc:390 msgid "Technical installation notes" msgstr "Technische Installierungsnotizen" #: f.widgets.cc:391 msgid "List updates by Fotoxx version" msgstr "Änderungen nach Fotoxxversion auflisten" #: f.widgets.cc:392 msgid "View the log file and error messages" msgstr "Logdatei und Fehlermeldungen auflisten" #: f.widgets.cc:393 msgid "How to do Fotoxx translations" msgstr "Wie eine Fotoxx Übersetzung erstellt wird" #: f.widgets.cc:394 msgid "Show the Fotoxx web page" msgstr "Fotoxx Webseite zeigen" #: f.widgets.cc:395 msgid "Version, license, contact, credits" msgstr "Version, Lizens, Kontakt, Mitwirkende" #: f.widgets.cc:398 msgid "list all directories, click any for gallery view" msgstr "Alle Verzeichnisse zeigen, eines anklicken für Galerie-Ansicht" #: f.widgets.cc:399 msgid "Set gallery from current image file" msgstr "Galerie vom aktuellen Bild herbeiholen" #: f.widgets.cc:400 msgid "Organize images into albums" msgstr "Bilder in Alben organisieren" #: f.widgets.cc:401 msgid "Update album files to latest version" msgstr "Album Dateien zu neuesten Dateiversionen aktualisieren" #: f.widgets.cc:402 msgid "Replace album file with another file" msgstr "Album Datei mit einer anderen Datei ersetzen" #: f.widgets.cc:403 msgid "Start a slide show" msgstr "Dia-Show starten" #: f.widgets.cc:425 msgid "New Window" msgstr "Neues Fenster" #: f.widgets.cc:426 f.widgets.cc:610 fotoxx-18.01.cc:1821 msgid "Sync Gallery" msgstr "Sync Galerie" #: f.widgets.cc:427 msgid "Recently Seen Images" msgstr "kürzlich gesehene Bilder" #: f.widgets.cc:428 msgid "Newest Images" msgstr "Neueste Bilder" #: f.widgets.cc:433 msgid "View 360° Panorama" msgstr "360° Panorama-Ansicht" #: f.widgets.cc:434 msgid "New Blank Image" msgstr "Neues Leerbild" #: f.widgets.cc:437 f.widgets.cc:758 msgid "Copy/Move to Location" msgstr "Zum Verzeichnis kopieren oder versetzen" #: f.widgets.cc:438 f.widgets.cc:759 msgid "Copy to Desktop" msgstr "zu Arbeitsoberfläche kopieren" #: f.widgets.cc:439 f.widgets.cc:760 msgid "Copy to Clipboard" msgstr "Auf Zwischenablage kopieren" #: f.widgets.cc:440 f.widgets.cc:764 msgid "Copy to Image Cache" msgstr "zu Bildercache kopieren" #: f.widgets.cc:441 f.widgets.cc:778 msgid "Show on Net Map" msgstr "am Net-Karte zeigen" #: f.widgets.cc:443 msgid "Print Image" msgstr "Bilddatei drucken" #: f.widgets.cc:444 msgid "Print Calibrated Image" msgstr "Kalibrierte Bilddatei drucken" #: f.widgets.cc:448 msgid "View Metadata (short)" msgstr "Metadaten ansehen (kurz)" #: f.widgets.cc:449 msgid "View Metadata (long)" msgstr "Metadaten ansehen (lang)" #: f.widgets.cc:453 msgid "Show Captions on Image" msgstr "Titel am Bild zeigen" #: f.widgets.cc:456 f.widgets.cc:776 msgid "Select Area" msgstr "Ausschnitt auswählen" #: f.widgets.cc:457 msgid "Find Area Gap" msgstr "Lücke in Ausschnitt finden" #: f.widgets.cc:459 msgid "Show Area" msgstr "Ausschnitt zeigen" #: f.widgets.cc:460 msgid "Hide Area" msgstr "Ausschnitt ausblenden" #: f.widgets.cc:461 msgid "Enable Area" msgstr "Ausschnitt aktivieren" #: f.widgets.cc:462 msgid "Disable Area" msgstr "Ausschnitt deaktivieren" #: f.widgets.cc:463 msgid "Invert Area" msgstr "Ausschnitt invertieren" #: f.widgets.cc:464 msgid "Unselect Area" msgstr "Ausschnitt löschen" #: f.widgets.cc:466 msgid "Paste Area" msgstr "Ausschnitt einfügen" #: f.widgets.cc:467 msgid "Open Area File" msgstr "Ausschnitt von Datei lesen" #: f.widgets.cc:474 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:475 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:477 f.widgets.cc:773 msgid "Edit Brightness" msgstr "Helligkeit verarbeiten" #: f.widgets.cc:478 f.widgets.cc:775 msgid "Gradients" msgstr "Graduierung" #: f.widgets.cc:482 msgid "Paint Image" msgstr "Bild bemalen" #: f.widgets.cc:489 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:494 msgid "Denoise" msgstr "Entrauschen" #: f.widgets.cc:495 msgid "Red Eyes" msgstr "Rote Augen" #: f.widgets.cc:497 msgid "Color Depth" msgstr "Farbtiefe" #: f.widgets.cc:500 msgid "Adjust RGB/CMY" msgstr "RGB/CMY justieren" #: f.widgets.cc:501 msgid "Adjust HSL" msgstr "HSL justieren" #: f.widgets.cc:502 msgid "Zonal Colors" msgstr "Zonale Farben" #: f.widgets.cc:503 msgid "Match Colors" msgstr "Farben anpassen" #: f.widgets.cc:509 msgid "Anti-Alias" msgstr "Kantenglättung" #: f.widgets.cc:516 msgid "Fix Perspective" msgstr "Perspective verbessern" #: f.widgets.cc:526 msgid "Twist Image" msgstr "Bild verdrehen" #: f.widgets.cc:529 msgid "Sketch" msgstr "Skizzieren" #: f.widgets.cc:530 msgid "Cartoon" msgstr "Cartoon" #: f.widgets.cc:534 msgid "Embossing" msgstr "Prägung" #: f.widgets.cc:536 msgid "Dots" msgstr "Rasterpunkte" #: f.widgets.cc:537 msgid "Painting" msgstr "Gemälde" #: f.widgets.cc:539 msgid "Texture" msgstr "Textur" #: f.widgets.cc:541 msgid "Mosaic" msgstr "Mosaik" #: f.widgets.cc:548 msgid "High Dynamic Range" msgstr "Hoher Dynamikbereich" #: f.widgets.cc:549 msgid "High Depth of Field" msgstr "Hohe Schärfentiefe" #: f.widgets.cc:550 msgid "Stack/Paint" msgstr "Stapeln/malen" #: f.widgets.cc:551 msgid "Stack/Noise" msgstr "Stapeln/Rauschen" #: f.widgets.cc:552 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:553 msgid "Vertical Panorama" msgstr "Vertikales Panorama" #: f.widgets.cc:554 msgid "PT Panorama" msgstr "PT Panorama" #: f.widgets.cc:555 msgid "Montage" msgstr "Montage" #: f.widgets.cc:556 msgid "Mashup" msgstr "Mashup" #: f.widgets.cc:562 msgid "Batch RAW" msgstr "Stapel RAW" #: f.widgets.cc:563 msgid "Script Files" msgstr "Skriptdateien" #: f.widgets.cc:566 msgid "Export File List" msgstr "Datei Liste exportieren" #: f.widgets.cc:574 msgid "Image Locations/Dates" msgstr "Bild-Orte/Daten" #: f.widgets.cc:575 msgid "Image Timeline" msgstr "Bild Zeitleiste" #: f.widgets.cc:576 msgid "Search Images" msgstr "Bilddatei durchsuchen" #: f.widgets.cc:582 msgid "Brightness Graph" msgstr "Helligkeitsverteilung" #: f.widgets.cc:587 msgid "Dark/Bright Pixels" msgstr "Dunkel/hell Pixeln" #: f.widgets.cc:588 msgid "Monitor Color" msgstr "Monitor Farbgrafik" #: f.widgets.cc:590 msgid "Change Language" msgstr "Sprache wechseln" #: f.widgets.cc:591 msgid "Missing Translations" msgstr "Fehlende Übersetzungen" #: f.widgets.cc:592 msgid "Color Profile" msgstr "Farbraum" #: f.widgets.cc:594 msgid "Uninstall AppImage" msgstr "AppImage deinstallieren" #: f.widgets.cc:611 msgid "All Directories" msgstr "Alle Verzeichnisse" #: f.widgets.cc:612 msgid "Bookmarks" msgstr "Lesezeichnen" #: f.widgets.cc:621 f.widgets.cc:646 f.widgets.cc:670 f.widgets.cc:683 msgid "Gallery" msgstr "Bildergalerie" #: f.widgets.cc:622 f.widgets.cc:647 f.widgets.cc:671 f.widgets.cc:684 msgid "World Maps" msgstr "Weltkarten" #: f.widgets.cc:623 f.widgets.cc:648 f.widgets.cc:672 f.widgets.cc:685 msgid "Net Maps" msgstr "Net-Karten" #: f.widgets.cc:625 f.widgets.cc:650 f.widgets.cc:1066 msgid "Favorites" msgstr "Favoriten" #: f.widgets.cc:627 msgid "Save File" msgstr "Datei speichern" #: f.widgets.cc:628 msgid "Prev/Next" msgstr "Vorig/Nächst" #: f.widgets.cc:630 msgid "Areas" msgstr "Ausschnitt" #: f.widgets.cc:631 msgid "Undo/Redo" msgstr "Undo/Redo" #: f.widgets.cc:632 fotoxx.h:1208 msgid "Edit" msgstr "Bearbeiten" #: f.widgets.cc:633 msgid "Repair" msgstr "Ausbessern" #: f.widgets.cc:635 msgid "Effects" msgstr "Effekte" #: f.widgets.cc:636 f.widgets.cc:660 msgid "Combine" msgstr "Verbund" #: f.widgets.cc:637 f.widgets.cc:661 msgid "Process" msgstr "Prozess" #: f.widgets.cc:638 f.widgets.cc:662 msgid "Tools" msgstr "Werkzeuge" #: f.widgets.cc:674 msgid "Choose Map" msgstr "Karte wählen" #: f.widgets.cc:675 f.widgets.cc:688 msgid "Map Markers" msgstr "Karten-Markierungen" #: f.widgets.cc:687 msgid "Map Source" msgstr "Karten-Quelle" #: f.widgets.cc:689 msgid "Map Locations" msgstr "Karten-Orte" #: f.widgets.cc:753 msgid "Popup Image" msgstr "Pop-up Bild" #: f.widgets.cc:757 fotoxx.h:1265 msgid "Rename" msgstr "Umbenennen" #: f.widgets.cc:761 msgid "Remove from Album" msgstr "Von Album entfernen" #: f.widgets.cc:763 msgid "Cut to Image Cache" msgstr "zu Bildercache ausschneiden" #: f.widgets.cc:765 msgid "Paste Image Cache Here (clear)" msgstr "Bildercache hier einfügen (löschen)" #: f.widgets.cc:766 msgid "Paste Image Cache Here (keep)" msgstr "Bildercache hier einfügen (behalten)" #: f.widgets.cc:779 msgid "Delete/Trash ..." msgstr "Löschen/Abfall ..." #: f.widgets.cc:1092 msgid "menu name is invalid" msgstr "Menüname ist ungültig" #: fotoxx-18.01.cc:434 msgid "Please install missing programs:" msgstr "Bitte fehlende Programme installieren:" #: fotoxx-18.01.cc:706 #, c-format msgid "Kill active dialog? %s" msgstr "Aktiven Dialog abbrechen? %s" #: fotoxx-18.01.cc:801 msgid "(reduced)" msgstr "(verkleinert) " #: fotoxx-18.01.cc:802 msgid "area active" msgstr "Ausschnitt aktiv" #: fotoxx-18.01.cc:803 msgid "dialog open" msgstr "Dialog aktiv" #: fotoxx-18.01.cc:804 msgid "blocked" msgstr "Gesperrt" #: fotoxx-18.01.cc:866 msgid "edits" msgstr "edits" #: fotoxx-18.01.cc:1801 msgid "File View" msgstr "Bilddatei Ansicht" #: fotoxx-18.01.cc:1806 msgid "Gallery View" msgstr "Gallerie Ansicht" #: fotoxx-18.01.cc:1811 msgid "World Map View" msgstr "Weltkarte Ansicht" #: fotoxx-18.01.cc:1816 msgid "Net Map View" msgstr "Net-Weltkarte Ansicht" #: fotoxx-18.01.cc:1831 msgid "Show Hidden" msgstr "Versteckte zeigen" #: fotoxx-18.01.cc:3023 msgid "Exceed 50 anchor points" msgstr "50 Ankerpunkte überschritten" #: fotoxx-18.01.cc:3250 msgid "load curve from a file" msgstr "Kurven-Datei öffnen" #: fotoxx-18.01.cc:3316 msgid "curve file is invalid" msgstr "Kurvendatei ist ungültig" #: fotoxx-18.01.cc:3330 msgid "save curve to a file" msgstr "Kurven-Datei speichern" #: fotoxx-18.01.cc:3399 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "Datei kann nicht bearbeitet werden \n" " %s" #: fotoxx-18.01.cc:3412 msgid "Too many edits, please save image" msgstr "Zuviele Änderungen, bitte Bild speichern" #: fotoxx-18.01.cc:3417 msgid "this function cannot be scripted" msgstr "diese Funktion darf nicht in einer Skriptdatei" #: fotoxx-18.01.cc:3434 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Ausschnit nicht aktiviert.\n" "Fortfahren?" #: fotoxx-18.01.cc:3749 msgid "file data does not fit dialog" msgstr "Datei stimmt nicht mit dem Dialog überein" #: fotoxx-18.01.cc:3759 msgid "Save settings to a file" msgstr "Einstellungen in Datei speichern" #: fotoxx-18.01.cc:4140 msgid "This action will discard changes to current image" msgstr "Dieses Vorgehen wird Änderungen im aktuellen Bild verwerfen" #: fotoxx-18.01.cc:4141 msgid "prior function still active" msgstr "Vorherige Funktion noch aktiv" #: fotoxx-18.01.cc:4142 fotoxx.h:1229 msgid "Keep" msgstr "Behalten" #: fotoxx-18.01.cc:4143 msgid "Discard" msgstr "Verwerfen" #: fotoxx.h:1177 msgid "Add" msgstr "Hinzufügen" #: fotoxx.h:1178 msgid "Add All" msgstr "Alle einfügen" #: fotoxx.h:1180 msgid "Amount" msgstr "Wert" #: fotoxx.h:1181 msgid "Angle" msgstr "Winkel" #: fotoxx.h:1182 zfuncs.cc:6782 msgid "Apply" msgstr "Anwenden" #: fotoxx.h:1183 msgid "Auto" msgstr "Auto" #: fotoxx.h:1184 msgid "Black" msgstr "Schwarz" #: fotoxx.h:1185 msgid "Blend Width" msgstr "Mischbreite" #: fotoxx.h:1187 zfuncs.cc:11368 msgid "Bottom" msgstr "Unten" #: fotoxx.h:1189 zfuncs.cc:6796 msgid "Browse" msgstr "Durchsuchen" #: fotoxx.h:1190 zfuncs.cc:6782 msgid "Cancel" msgstr "Abbrechen" #: fotoxx.h:1191 msgid "Center" msgstr "Mittig" #: fotoxx.h:1192 msgid "Choose" msgstr "Wählen" #: fotoxx.h:1193 msgid "Clear" msgstr "Aufräumen" #: fotoxx.h:1194 msgid "Close" msgstr "Beenden" #: fotoxx.h:1195 msgid "Color" msgstr "Farbe" #: fotoxx.h:1196 msgid "COMPLETED" msgstr "FERTIG" #: fotoxx.h:1197 msgid "continue" msgstr "fortsetzen" #: fotoxx.h:1199 msgid "Copy" msgstr "Kopieren" #: fotoxx.h:1200 msgid "Create" msgstr "Erstellen" #: fotoxx.h:1201 msgid "Curve File:" msgstr "Kurven-Datei" #: fotoxx.h:1202 msgid "Cut" msgstr "Ausschneiden" #: fotoxx.h:1203 msgid "Deband" msgstr "Entstreifen" #: fotoxx.h:1204 zfuncs.cc:6782 msgid "Delete" msgstr "Löschen" #: fotoxx.h:1205 msgid "Disable" msgstr "Ausschalten" #: fotoxx.h:1206 msgid "Done" msgstr "Fertig" #: fotoxx.h:1207 msgid "edge" msgstr "Rand" #: fotoxx.h:1209 msgid "Enable" msgstr "Aktivieren" #: fotoxx.h:1210 msgid "Erase" msgstr "Löschen" #: fotoxx.h:1211 msgid "Fetch" msgstr "Holen" #: fotoxx.h:1212 msgid "output file already exists" msgstr "Ausgabedatei existiert schon" #: fotoxx.h:1213 msgid "file not found" msgstr "Datei nicht gefunden" #: fotoxx.h:1214 #, c-format msgid "file not found: %s" msgstr "Datei nicht gefunden: %s" #: fotoxx.h:1215 #, c-format msgid "%d image files selected" msgstr "%d Bilddateien ausgewählt" #: fotoxx.h:1216 msgid "Find" msgstr "Suchen" #: fotoxx.h:1217 msgid "Finish" msgstr "Fertigstellen" #: fotoxx.h:1219 msgid "Font" msgstr "Font" #: fotoxx.h:1221 msgid "Grid" msgstr "Gitter" #: fotoxx.h:1222 zfuncs.cc:11398 msgid "Height" msgstr "Höhe" #: fotoxx.h:1224 msgid "Hide" msgstr "Ausblenden" #: fotoxx.h:1225 zfuncs.cc:11390 msgid "Image" msgstr "Bild" #: fotoxx.h:1226 msgid "Images" msgstr "Bilder" #: fotoxx.h:1227 msgid "Insert" msgstr "Einfügen" #: fotoxx.h:1228 msgid "Invert" msgstr "Invertieren" #: fotoxx.h:1230 zfuncs.cc:11372 msgid "Left" msgstr "Links" #: fotoxx.h:1231 msgid "limit" msgstr "Limit" #: fotoxx.h:1232 msgid "Load" msgstr "Laden" #: fotoxx.h:1233 msgid "Make" msgstr "Erstellen" #: fotoxx.h:1235 msgid "Map" msgstr "Karte" #: fotoxx.h:1236 msgid "Match Level:" msgstr "Farbübereinstimmung:" #: fotoxx.h:1237 msgid "Max" msgstr "Max" #: fotoxx.h:1238 msgid "mouse radius" msgstr "Mausradius" #: fotoxx.h:1239 msgid "Negative" msgstr "Negativ" #: fotoxx.h:1240 msgid "New" msgstr "Neu" #: fotoxx.h:1241 msgid "Next" msgstr "Nächst" #: fotoxx.h:1242 zfuncs.cc:10365 msgid "No" msgstr "Nein" #: fotoxx.h:1243 msgid "no images" msgstr "Keine Bilder" #: fotoxx.h:1244 msgid "" "image index disabled \n" " Enable?" msgstr "" "Bild-Indizieren gesperrt \n" " Aufsperren?" #: fotoxx.h:1245 msgid "no image files selected" msgstr "keine Bilddateien ausgewählt" #: fotoxx.h:1246 msgid "None" msgstr "Keine" #: fotoxx.h:1247 msgid "no selection" msgstr "Keine Auswahl" #: fotoxx.h:1248 msgid "OK" msgstr "OK" #: fotoxx.h:1249 msgid "image index not updated" msgstr "Bild-Index nicht aktualisiert" #: fotoxx.h:1251 msgid "Paint Radius" msgstr "Mal-Radius" #: fotoxx.h:1252 msgid "Paste" msgstr "Einfügen" #: fotoxx.h:1253 msgid "Pause" msgstr "Unterbrechen" #: fotoxx.h:1254 msgid "Percent" msgstr "Prozent" #: fotoxx.h:1256 msgid "Presets" msgstr "Voreinstellungen" #: fotoxx.h:1257 msgid "Prev" msgstr "Vorig" #: fotoxx.h:1258 msgid "Proceed" msgstr "Weiter" #: fotoxx.h:1260 msgid "range" msgstr "Wertbereich" #: fotoxx.h:1262 msgid "Redo" msgstr "Redo" #: fotoxx.h:1263 msgid "Reduce" msgstr "Vermindern" #: fotoxx.h:1264 msgid "Remove" msgstr "Entfernen" #: fotoxx.h:1266 msgid "Replace" msgstr "Ersetzen" #: fotoxx.h:1267 msgid "Reserved" msgstr "Reserviert" #: fotoxx.h:1268 msgid "Reset" msgstr "Zurücksetzen" #: fotoxx.h:1269 zfuncs.cc:11376 msgid "Right" msgstr "Rechts" #: fotoxx.h:1270 msgid "Rotate" msgstr "Drehen" #: fotoxx.h:1271 msgid "Run" msgstr "Laufen lassen" #: fotoxx.h:1272 msgid "Save" msgstr "Speichern" #: fotoxx.h:1273 msgid "Search" msgstr "Durchsuchen" #: fotoxx.h:1274 msgid "Seconds" msgstr "Sekunden" #: fotoxx.h:1275 msgid "Select" msgstr "Auswählen" #: fotoxx.h:1277 msgid "Show" msgstr "Zeigen" #: fotoxx.h:1278 msgid "Size" msgstr "Große" #: fotoxx.h:1279 msgid "Start" msgstr "Starten" #: fotoxx.h:1280 msgid "Stop" msgstr "Stop" #: fotoxx.h:1281 msgid "Strength" msgstr "Stärke" #: fotoxx.h:1282 msgid "Threshold" msgstr "Schwelle" #: fotoxx.h:1283 #, c-format msgid "exceed %d files" msgstr "%d Dateien überschritten" #: fotoxx.h:1284 zfuncs.cc:11364 msgid "Top" msgstr "Oben" #: fotoxx.h:1286 msgid "Trash" msgstr "Abfall" #: fotoxx.h:1287 msgid "Trim" msgstr "Schneiden" #: fotoxx.h:1288 msgid "Undo All" msgstr "Alles rückgängig" #: fotoxx.h:1289 msgid "Undo Last" msgstr "Letztes rückgängig" #: fotoxx.h:1290 msgid "Undo" msgstr "Undo" #: fotoxx.h:1291 msgid "Unfinish" msgstr "Unfertigstellen" #: fotoxx.h:1292 msgid "Unselect" msgstr "Deselektieren" #: fotoxx.h:1293 msgid "View" msgstr "Ansehen" #: fotoxx.h:1294 msgid "Web" msgstr "Web" #: fotoxx.h:1295 msgid "White" msgstr "Weiß" #: fotoxx.h:1296 zfuncs.cc:11394 msgid "Width" msgstr "Breite" #: fotoxx.h:1297 msgid "x-offset" msgstr "x-Verschiebung" #: fotoxx.h:1298 msgid "y-offset" msgstr "y-Verschiebung" #: fotoxx.h:1299 zfuncs.cc:10365 msgid "Yes" msgstr "Ja" #: zfuncs.cc:305 msgid "LOW MEMORY - performance may be poor" msgstr "Speicherplatz knapp - Leistung könnte leiden" #: zfuncs.cc:1722 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "Verzeichnis erstellen? \n" " %s" #: zfuncs.cc:4878 msgid "user guide not found" msgstr "Benutzeranweisung nicht gefunden" #: zfuncs.cc:5742 #, c-format msgid "cannot open file %s" msgstr "Kann Datei nicht öffnen %s" #: zfuncs.cc:5765 msgid "save text to file" msgstr "Text in Datei speichern" #: zfuncs.cc:6786 msgid "menu text" msgstr "Menü-Text" #: zfuncs.cc:6787 msgid "menu func" msgstr "Menü Funktion" #: zfuncs.cc:6788 msgid "menu icon" msgstr "Menü-Icon" #: zfuncs.cc:6789 msgid "icon size" msgstr "Icon-Größe" #: zfuncs.cc:6792 msgid "Bold" msgstr "Fett" #: zfuncs.cc:6799 msgid "close window" msgstr "Fenster schließen" #: zfuncs.cc:10488 zfuncs.cc:11021 zfuncs.cc:11351 msgid "cancel" msgstr "Abbrechen" #: zfuncs.cc:10982 msgid "choose file" msgstr "Datei wählen" #: zfuncs.cc:10987 msgid "choose files" msgstr "Dateien wählen" #: zfuncs.cc:10992 msgid "save" msgstr "Speichern" #: zfuncs.cc:10998 msgid "choose folder" msgstr "Verzeichnis wählen" #: zfuncs.cc:11003 msgid "choose folders" msgstr "Verzeichnise wählen" #: zfuncs.cc:11008 msgid "create folder" msgstr "Ordner erstellen" #: zfuncs.cc:11015 msgid "hidden" msgstr "Versteckt" #: zfuncs.cc:11351 msgid "done" msgstr "fertig" #: zfuncs.cc:11381 msgid "image scale" msgstr "Bildgröße" #: zfuncs.cc:11383 msgid "percent" msgstr "Prozent" #~ msgid "Retinex" #~ msgstr "Retinex" fotoxx-18.01.1/locales/translate-fr.po0000644000175000017500000043301413222767271016253 0ustar micomico# French translations for home package. # Copyright (C) 2018 THE home's COPYRIGHT HOLDER # This file is distributed under the same license as the home package. # mico , 2011. # # msgid "" msgstr "" "Project-Id-Version: home 2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-12-29 09:03+0100\n" "PO-Revision-Date: 2011-01-01 11:29+0100\n" "Last-Translator: mico \n" "Language-Team: English\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: f.albums.cc:89 msgid "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." msgstr "" "Clic droit sur une vignette d'album pour couper/copier \n" "vers le cache, insérer depuis le cache, ou enlever." #: f.albums.cc:91 msgid "" "Right-click left/right side of album \n" "thumbnail to insert cached images \n" "before/after the thumbnail." msgstr "" "Clic droit sur le côté gauche/droit d'une vignette \n" "dans un album pour insérer les images en cache \n" "avant/après cette vignette." #: f.albums.cc:94 msgid "Drag album thumbnail to new position." msgstr "Tirer une vignette d'album vers sa nouvelle position." #: f.albums.cc:128 f.widgets.cc:613 msgid "Manage Albums" msgstr "Gérer les albums" #: f.albums.cc:140 f.albums.cc:275 msgid "Create or replace an album" msgstr "Créer ou remplacer un album" #: f.albums.cc:142 msgid "Album to view or edit" msgstr "Album à visualiser ou à éditer" #: f.albums.cc:144 msgid "Select images, add to cache" msgstr "Sélectionner des images, ajouter au cache" #: f.albums.cc:146 msgid "Clear image cache" msgstr "Mettre à zéro le cache des images" #: f.albums.cc:148 f.albums.cc:170 msgid "Delete an album" msgstr "Effacer un album" #: f.albums.cc:169 msgid "Choose Album" msgstr "Choisir un album" #: f.albums.cc:171 #, c-format msgid "Image cache has %d images" msgstr "Le cache des images comprend %d images" #: f.albums.cc:172 msgid "cache added to empty album" msgstr "cache ajouté à un album vide" #: f.albums.cc:228 #, c-format msgid "delete %s ?" msgstr "supprimer %s ?" #: f.albums.cc:259 #, c-format msgid "fill from image cache (%d images)" msgstr "remplir depuis le cache des images (%d images)" #: f.albums.cc:277 f.albums.cc:310 msgid "Album Name" msgstr "Nom de l'album" #: f.albums.cc:283 msgid "make an initially empty album" msgstr "créer un album initialement vide" #: f.albums.cc:285 msgid "fill from current gallery" msgstr "remplir depuis la galerie actuelle" #: f.albums.cc:324 msgid "enter an album name" msgstr "entrer un nom pour l'album" #: f.albums.cc:352 msgid "new album created" msgstr "nouvel album créé" #: f.albums.cc:368 msgid "gallery is empty" msgstr "la galerie est vide" #: f.albums.cc:876 f.widgets.cc:614 msgid "Update Album Files" msgstr "Mise à jour des fichiers album" #: f.albums.cc:878 f.albums.cc:938 f.albums.cc:1007 f.albums.cc:1147 msgid "Choose Albums" msgstr "Choisir les albums" #: f.albums.cc:1005 f.widgets.cc:615 f.widgets.cc:762 msgid "Replace Album File" msgstr "Remplacer le fichier album" #: f.albums.cc:1009 msgid "All Albums" msgstr "Tous les albums" #: f.albums.cc:1012 msgid "old file" msgstr "ancien fichier" #: f.albums.cc:1016 msgid "new file" msgstr "nouveau fichier" #: f.albums.cc:1020 msgid "replace old" msgstr "remplacer l'ancien" #: f.albums.cc:1021 msgid "add after old" msgstr "ajouter après l'ancien" #: f.albums.cc:1081 msgid "no albums chosen" msgstr "aucun album n'a été sélectionné" #: f.albums.cc:1162 msgid "ALL albums chosen" msgstr "TOUS les albums ont été sélectionnés" #: f.albums.cc:1346 msgid "instant" msgstr "instantané" #: f.albums.cc:1347 msgid "fade-in" msgstr "fondu" #: f.albums.cc:1348 msgid "roll-right" msgstr "enroulement vers la droite" #: f.albums.cc:1349 msgid "roll-down" msgstr "enroulement vers le bas" #: f.albums.cc:1350 msgid "venetian" msgstr "store vénitien" #: f.albums.cc:1351 msgid "grate" msgstr "grillage" #: f.albums.cc:1352 msgid "rectangle" msgstr "rectangle" #: f.albums.cc:1353 msgid "implode" msgstr "implose" #: f.albums.cc:1354 msgid "explode" msgstr "explose" #: f.albums.cc:1355 msgid "radar" msgstr "radar" #: f.albums.cc:1356 msgid "spiral" msgstr "spirale" #: f.albums.cc:1357 msgid "Japan-fan" msgstr "éventail japonais" #: f.albums.cc:1358 msgid "jaws" msgstr "mâchoires" #: f.albums.cc:1359 msgid "ellipse" msgstr "ellipse" #: f.albums.cc:1360 msgid "raindrops" msgstr "gouttes de pluie" #: f.albums.cc:1361 msgid "doubledoor" msgstr "double porte" #: f.albums.cc:1362 msgid "rotate" msgstr "rotation" #: f.albums.cc:1363 msgid "fallover" msgstr "chute" #: f.albums.cc:1364 msgid "spheroid" msgstr "spheroïde" #: f.albums.cc:1365 msgid "turn-page" msgstr "tourner une page" #: f.albums.cc:1366 msgid "french-door" msgstr "porte française" #: f.albums.cc:1367 msgid "turn-cube" msgstr "cube rotatif" #: f.albums.cc:1368 msgid "windmill" msgstr "moulin à vent" #: f.albums.cc:1369 msgid "pixelize" msgstr "pixelisation" #: f.albums.cc:1370 msgid "twist" msgstr "torsion" #: f.albums.cc:1372 msgid "squish" msgstr "écraser" #: f.albums.cc:1399 f.widgets.cc:616 msgid "Slide Show" msgstr "Diaporama" #: f.albums.cc:1405 msgid "use gallery" msgstr "utiliser la galerie" #: f.albums.cc:1411 msgid "Clip Limit" msgstr "Limite de troncature" #: f.albums.cc:1415 msgid "Music File" msgstr "Fichier musical" #: f.albums.cc:1420 msgid "Full Screen" msgstr "Plein écran" #: f.albums.cc:1421 msgid "Auto-replay" msgstr "Jouer en boucle" #: f.albums.cc:1424 msgid "Customize:" msgstr "Personnaliser:" #: f.albums.cc:1425 msgid "transitions" msgstr "transitions" #: f.albums.cc:1426 msgid "image files" msgstr "fichiers image" #: f.albums.cc:1427 msgid "KB controls" msgstr "Contrôles du clavier" #: f.albums.cc:1441 f.albums.cc:1509 f.albums.cc:1732 f.albums.cc:2007 #: f.albums.cc:2292 f.albums.cc:2309 f.albums.cc:2526 msgid "invalid album" msgstr "album non valide" #: f.albums.cc:1540 msgid "open album" msgstr "ouvrir l'album" #: f.albums.cc:1556 msgid "Select music file" msgstr "Selectionner un fichier musical" #: f.albums.cc:1587 #, c-format msgid "%d images" msgstr "%d images" #: f.albums.cc:1614 msgid "arrow keys show previous or next image instantly" msgstr "" "les touches fléchées affichent instantanément l'image précédente ou suivante" #: f.albums.cc:1632 msgid "Keyboard Preferences" msgstr "Préférences du clavier" #: f.albums.cc:1635 msgid "blank or unblank window" msgstr "vider ou remplir la fenêtre" #: f.albums.cc:1638 msgid "show next image, with transition" msgstr "afficher l'image suivante, avec transition" #: f.albums.cc:1641 msgid "pause or resume slide show" msgstr "suspendre ou reprendre le diaporama" #: f.albums.cc:1644 msgid "magnify image (loupe tool)" msgstr "agrandir l'image (outil loupe)" #: f.albums.cc:1711 msgid "random sequence" msgstr "succession aléatoire" #: f.albums.cc:1736 msgid "Transition Preferences" msgstr "Préférences de transition" #: f.albums.cc:1738 msgid "Transitions File" msgstr "Fichier de transitions" #: f.albums.cc:1756 f.albums.cc:1760 msgid "transition" msgstr "transition" #: f.albums.cc:1757 f.albums.cc:1761 msgid "use" msgstr "utiliser" #: f.albums.cc:1758 f.albums.cc:1762 msgid "slow" msgstr "lente" #: f.albums.cc:1759 f.albums.cc:1763 msgid "pref" msgstr "préf." #: f.albums.cc:1862 f.albums.cc:1901 msgid "invalid file" msgstr "fichier non valide" #: f.albums.cc:1954 f.albums.cc:2511 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "erreur de format de fichier: \n" " %s" #: f.albums.cc:2013 msgid "Image Preferences" msgstr "Préférences d'image" #: f.albums.cc:2017 f.edit.cc:1405 f.file.cc:1577 f.file.cc:1883 f.meta.cc:820 #: f.meta.cc:1588 f.meta.cc:1780 msgid "Image File:" msgstr "Fichier image:" #: f.albums.cc:2021 msgid "Play tone when image shows" msgstr "Jouer un son quand l'image s'affiche" #: f.albums.cc:2024 msgid "Show image caption" msgstr "Afficher la légende de l'image" #: f.albums.cc:2029 msgid "Show image comments" msgstr "Afficher les annotations de l'image" #: f.albums.cc:2034 msgid "Wait before zoom" msgstr "Attente avant le zoom" #: f.albums.cc:2039 msgid "Zoom type:" msgstr "Type de zoom:" #: f.albums.cc:2041 msgid "zoom-in" msgstr "zoom avant" #: f.albums.cc:2042 msgid "zoom-out" msgstr "zoom arrière" #: f.albums.cc:2045 msgid "Zoom size (x)" msgstr "Taux de zoom (x)" #: f.albums.cc:2048 msgid "Steps" msgstr "Étapes" #: f.albums.cc:2052 msgid "Zoom Center" msgstr "Centre du zoom" #: f.albums.cc:2056 msgid "Wait after zoom" msgstr "Attente après le zoom" #: f.albums.cc:2061 msgid "Transition to next image" msgstr "Transition vers l'image suivante" #: f.albums.cc:2064 f.albums.cc:2167 msgid "next" msgstr "suivante" #: f.albums.cc:2157 msgid "click on thumbnail to set zoom center" msgstr "cliquer sur la vignette pour centrer le zoom" #: f.area.cc:81 msgid "Select Area for Edits" msgstr "Sélectionner une zone à éditer" #: f.area.cc:82 f.area.cc:1855 f.edit.cc:9094 msgid "Press F1 for help" msgstr "Presser F1 pour l'aide" #: f.area.cc:91 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Sélectionner une zone n'est pas supporté \n" "par cette fonction d'édition" #: f.area.cc:120 msgid "select rectangle" msgstr "sélectionner un rectangle" #: f.area.cc:121 msgid "select ellipse" msgstr "sélectionner une ellipse" #: f.area.cc:124 msgid "freehand draw" msgstr "dessin à main levée" #: f.area.cc:125 msgid "follow edge" msgstr "suivre le bord" #: f.area.cc:126 msgid "draw/replace" msgstr "dessiner/remplacer" #: f.area.cc:129 msgid "select area within mouse circle" msgstr "sélectionner une zone à l'intérieur du cercle de la souris" #: f.area.cc:132 msgid "select one matching color within mouse circle:" msgstr "sélectionner une couleur à l'intérieur du cercle de la souris:" #: f.area.cc:134 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "sélectionner d'abord la case à cocher, puis \n" "maj+clic sur l'image pour choisir la couleur" #: f.area.cc:138 msgid "select all matching colors within mouse circle" msgstr "" "sélectionner toutes les couleurs présentes à l'intérieur du cercle de la " "souris" #: f.area.cc:144 msgid "match level %" msgstr "niveau de correspondance %" #: f.area.cc:147 msgid "search range" msgstr "plage de recherche" #: f.area.cc:151 msgid "Area Edge Blend Width" msgstr "Largeur du fondu des bords de la zone" #: f.area.cc:154 msgid "Edge Creep" msgstr "Fluage du bord" #: f.area.cc:159 msgid "Line Color:" msgstr "Couleur de la ligne:" #: f.area.cc:179 f.area.cc:180 msgid "area edits fade away within edge distance" msgstr "" "les modifications apportées à la zone s'atténuent avec la distance aux bords" #: f.area.cc:351 f.area.cc:517 #, c-format msgid "exceed %d edits" msgstr "excède %d modifications" #: f.area.cc:1333 msgid "" "Click one time inside each enclosed area. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Cliquer une fois à l'intérieur de chaque zone concernée. \n" "De possibles discontinuités dans les contours seront détectées. \n" "Presser F1 pour l'aide." #: f.area.cc:1360 msgid "finish area" msgstr "finir la zone" #: f.area.cc:1389 f.area.cc:1534 #, c-format msgid "found %d pixels" msgstr "trouvé %d pixels" #: f.area.cc:1639 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Cliquer à proximité de n'importe quelle position sur le contour de la " "zone. \n" "D'éventuelles lacunes dans le contour seront repérées. \n" "Presser F1 pour l'aide." #: f.area.cc:1658 msgid "find outline gap" msgstr "trouver une discontinuité du contour" #: f.area.cc:1748 msgid "cannot find area outline" msgstr "impossible de trouver le contour de la zone" #: f.area.cc:1854 f.widgets.cc:458 msgid "Select Hairy" msgstr "Sélection plumeuse" #: f.area.cc:1862 msgid "select the area first" msgstr "sélectionner d'abord la zone" #: f.area.cc:1867 f.area.cc:2489 f.area.cc:2507 f.area.cc:2527 f.area.cc:2851 #: f.area.cc:2971 msgid "the area is not finished" msgstr "la zone n'est pas finie" #: f.area.cc:1940 msgid "select" msgstr "sélectionner" #: f.area.cc:1946 msgid "deselect" msgstr "désélectionner" #: f.area.cc:2616 msgid "Edge calculation in progress" msgstr "Calcul du bord en progrès" #: f.area.cc:2625 msgid "Area Edge Calc" msgstr "Calcul du bord de la zone" #: f.area.cc:2957 f.area.cc:3029 f.widgets.cc:465 msgid "Copy Area" msgstr "Copier la zone" #: f.area.cc:2960 f.widgets.cc:468 msgid "Save Area File" msgstr "Enregistrer un fichier de zone" #: f.area.cc:3035 msgid "save area as a PNG file" msgstr "enregistrer la zone comme fichier PNG" #: f.area.cc:3159 msgid "position with mouse click/drag" msgstr "position avec cliquer/tirer de la souris" #: f.area.cc:3219 msgid "Paste Image" msgstr "Coller une image" #: f.area.cc:3224 f.process.cc:1311 msgid "resize" msgstr "redimensionner" #: f.combine.cc:169 f.combine.cc:815 f.combine.cc:1416 f.combine.cc:2039 msgid "Select 2 to 9 files" msgstr "Sélectionner de 2 à 9 fichiers" #: f.combine.cc:191 f.combine.cc:837 f.combine.cc:1438 f.combine.cc:2061 msgid "Images are not all the same size" msgstr "Les images n'ont pas toutes la même taille" #: f.combine.cc:550 msgid "Adjust Image Contributions" msgstr "Ajuster la contribution respective de chaque image" #: f.combine.cc:554 msgid "dark pixels" msgstr "pixels sombres" #: f.combine.cc:556 msgid "light pixels" msgstr "pixels clairs" #: f.combine.cc:558 msgid "file:" msgstr "fichier:" #: f.combine.cc:1018 msgid "Paint and Warp Image" msgstr "Peindre et déformer l'image" #: f.combine.cc:1026 f.edit.cc:5686 msgid "paint" msgstr "peindre" #: f.combine.cc:1027 msgid "warp" msgstr "déformer" #: f.combine.cc:1623 msgid "Select and Paint Image" msgstr "Sélectionner et peindre l'image" #: f.combine.cc:1631 msgid "Transient Objects" msgstr "Objets transitoires" #: f.combine.cc:2232 msgid "Adjust Pixel Composition" msgstr "Ajuster la composition de pixels" #: f.combine.cc:2234 msgid "use average" msgstr "utiliser la moyenne" #: f.combine.cc:2235 msgid "use median" msgstr "utiliser la médiane" #: f.combine.cc:2237 msgid "omit low pixel" msgstr "omettre les pixels bas" #: f.combine.cc:2238 msgid "omit high pixel" msgstr "omettre les pixels hauts" #: f.combine.cc:2502 f.combine.cc:3653 msgid "Select 2 to 4 files" msgstr "Sélectionner de 2 à 4 fichiers" #: f.combine.cc:2573 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Tirer les images en un alignement approximatif.\n" "Pour les faire tourner, tirer depuis le bord inférieur." #: f.combine.cc:2575 f.combine.cc:3726 msgid "no curve (scanned image)" msgstr "pas de courbe (image scannée)" #: f.combine.cc:2576 f.combine.cc:3727 msgid "Search for lens mm" msgstr "Chercher l'objectif mm" #: f.combine.cc:2577 f.combine.cc:3728 msgid "Save lens mm → image EXIF" msgstr "Enregistrer objectif mm → image EXIF" #: f.combine.cc:2639 f.combine.cc:3790 msgid "Pre-align Images" msgstr "Pré-aligner les images" #: f.combine.cc:2643 f.combine.cc:3794 msgid "lens mm" msgstr "objectif mm" #: f.combine.cc:2648 f.combine.cc:3799 msgid "no auto warp" msgstr "pas de déformation automatique" #: f.combine.cc:2650 f.combine.cc:3801 msgid "manual align" msgstr "alignement manuel" #: f.combine.cc:2652 f.combine.cc:3803 f.widgets.cc:473 f.widgets.cc:768 msgid "Resize" msgstr "Redimensionner" #: f.combine.cc:2653 f.combine.cc:3804 msgid "resize window" msgstr "redimensionner la fenêtre" #: f.combine.cc:2661 f.combine.cc:3812 msgid "do not warp images during auto-alignment" msgstr "ne pas déformer les images pendant l'auto-alignement" #: f.combine.cc:2707 f.combine.cc:3858 msgid "use two images only" msgstr "n'utiliser que deux images" #: f.combine.cc:2735 f.combine.cc:2939 f.combine.cc:3127 f.combine.cc:3884 #: f.combine.cc:4088 f.combine.cc:4276 msgid "Too little overlap, cannot align" msgstr "Trop peu de recouvrement, impossible d'aligner" #: f.combine.cc:3205 f.combine.cc:4354 msgid "Match Brightness and Color" msgstr "Correspondance luminosité et couleur" #: f.combine.cc:3251 msgid "Select image" msgstr "Sélectionner une image" #: f.combine.cc:3266 f.combine.cc:4415 msgid "auto color" msgstr "couleur automatique" #: f.combine.cc:3267 f.combine.cc:4416 msgid "file color" msgstr "couleur du fichier" #: f.combine.cc:3273 f.combine.cc:4422 msgid "mouse warp" msgstr "déformation avec la souris" #: f.combine.cc:3275 f.combine.cc:4424 msgid "flatten image" msgstr "aplatir l'image" #: f.combine.cc:3724 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Tirer les images en un alignement approximatif.\n" "Pour les faire tourner, tirer depuis le bord droit." #: f.combine.cc:4684 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) n'est pas installé" #: f.combine.cc:4693 msgid "Select at least 2 files" msgstr "Sélectionner au moins 2 fichiers" #: f.edit.cc:98 msgid "Trim: drag middle to move, drag corners to resize" msgstr "" "Rogner: tirer le milieu pour déplacer, tirer les coins pour redimensionner" #: f.edit.cc:99 msgid "Minor rotate: drag right edge with mouse" msgstr "Rotation de faible amplitude: tirer le bord droit avec la souris" #: f.edit.cc:180 f.widgets.cc:471 f.widgets.cc:767 msgid "Trim/Rotate" msgstr "Rognage/Rotation" #: f.edit.cc:192 f.edit.cc:1306 msgid "ratio" msgstr "proportions" #: f.edit.cc:196 msgid "trim size:" msgstr "taille du rognage:" #: f.edit.cc:201 msgid "Lock Ratio" msgstr "Verrouillage des proportions" #: f.edit.cc:203 msgid "maximize trim box" msgstr "maximiser la boîte englobante" #: f.edit.cc:204 msgid "use previous size" msgstr "utiliser la taille précédente" #: f.edit.cc:205 msgid "invert width/height" msgstr "inverser la largeur et la hauteur" #: f.edit.cc:206 msgid "trim transparent edges" msgstr "rogner les bords transparents" #: f.edit.cc:207 msgid "lock width/height ratio" msgstr "verrouiller le rapport largeur/hauteur" #: f.edit.cc:212 msgid "Customize" msgstr "Personnaliser" #: f.edit.cc:218 msgid "Level" msgstr "Niveau" #: f.edit.cc:219 msgid "use EXIF data if available" msgstr "utiliser les données EXIF si disponibles" #: f.edit.cc:222 msgid "Rotate: degrees" msgstr "Rotation: degrés" #: f.edit.cc:1302 msgid "Trim Buttons" msgstr "Boutons de rognage" #: f.edit.cc:1305 msgid "label" msgstr "étiquette" #: f.edit.cc:1402 msgid "Upright Image" msgstr "Redresser l'image" #: f.edit.cc:1408 f.process.cc:168 f.process.cc:755 f.widgets.cc:472 #: f.widgets.cc:769 msgid "Upright" msgstr "Redresser" #: f.edit.cc:1461 msgid "rotation unknown" msgstr "rotation inconnue" #: f.edit.cc:1548 msgid "Resize Image" msgstr "Redimensionner l'image" #: f.edit.cc:1573 msgid "W/H Ratio:" msgstr "Rapport largeur/hauteur:" #: f.edit.cc:1575 msgid "Lock" msgstr "Verrouiller" #: f.edit.cc:1577 msgid "use previous settings" msgstr "utiliser les paramètres précédents" #: f.edit.cc:2298 f.widgets.cc:476 f.widgets.cc:772 msgid "Retouch Combo" msgstr "Combo de retouche" #: f.edit.cc:2319 msgid "Amplifier" msgstr "Amplificateur" #: f.edit.cc:2320 fotoxx.h:1188 msgid "Brightness" msgstr "Luminosité" #: f.edit.cc:2321 f.effects.cc:3681 fotoxx.h:1198 msgid "Contrast" msgstr "Contraste" #: f.edit.cc:2322 msgid "Low Color" msgstr "Couleur bas" #: f.edit.cc:2323 msgid "Warmer" msgstr "Plus chaud" #: f.edit.cc:2324 f.effects.cc:1332 msgid "Dark Areas" msgstr "Zones sombres" #: f.edit.cc:2333 msgid "Max." msgstr "Max." #: f.edit.cc:2334 f.edit.cc:2335 f.edit.cc:2336 msgid "High" msgstr "Haut" #: f.edit.cc:2337 msgid "Cooler" msgstr "Plus froid" #: f.edit.cc:2338 msgid "Bright" msgstr "Zones claires" #: f.edit.cc:2341 f.tools.cc:2134 msgid "Brightness Distribution" msgstr "Distribution de la luminosité" #: f.edit.cc:2344 msgid "Click for white balance" msgstr "Cliquer pour la balance des blancs" #: f.edit.cc:2345 msgid "Click for black level" msgstr "Cliquer pour le niveau de noir" #: f.edit.cc:2348 msgid "Settings File" msgstr "Fichier de paramètres" #: f.edit.cc:2352 msgid "recall previous settings used" msgstr "rappeler les paramètres précédement utilisés" #: f.edit.cc:3004 msgid "Adjust Brightness Distribution" msgstr "Ajuster la distribution de la luminosité" #: f.edit.cc:3043 msgid "Low Cutoff" msgstr "Seuil bas" #: f.edit.cc:3044 msgid "High Cutoff" msgstr "Seuil haut" #: f.edit.cc:3045 msgid "Low Flatten" msgstr "Aplatissement bas" #: f.edit.cc:3046 msgid "Mid Flatten" msgstr "Aplatissement moyen" #: f.edit.cc:3047 msgid "High Flatten" msgstr "Aplatissement haut" #: f.edit.cc:3048 msgid "Low Stretch" msgstr "Extension basse" #: f.edit.cc:3049 msgid "Mid Stretch" msgstr "Extension moyenne" #: f.edit.cc:3050 msgid "High Stretch" msgstr "Extension haute" #: f.edit.cc:3383 msgid "Magnify Gradients" msgstr "Amplifier les gradients" #: f.edit.cc:3415 msgid "low" msgstr "bas" #: f.edit.cc:3417 msgid "high" msgstr "haut" #: f.edit.cc:3420 msgid "Amplify" msgstr "Amplifier" #: f.edit.cc:3813 msgid "Flatten Brightness" msgstr "Aplatissement de la luminosité" #: f.edit.cc:3864 msgid "Zones" msgstr "Zones" #: f.edit.cc:3871 msgid "Deband Dark" msgstr "Restaurer les valeurs sombres" #: f.edit.cc:3874 msgid "Deband Bright" msgstr "Restaurer les valeurs claires" #: f.edit.cc:4386 msgid "Dark Point" msgstr "Point noir" #: f.edit.cc:4387 msgid "Bright Point" msgstr "Point blanc" #: f.edit.cc:4388 msgid "Multiplyer" msgstr "Multiplicateur" #: f.edit.cc:4412 msgid "brightness rescale" msgstr "rééchelonnement de la luminosité" #: f.edit.cc:4415 msgid "click bright point" msgstr "cliquer sur le point blanc" #: f.edit.cc:4416 msgid "click dark point" msgstr "cliquer sur le point noir" #: f.edit.cc:4427 msgid "blend" msgstr "fusionner" #: f.edit.cc:4430 msgid "reduce bright" msgstr "réduire pour les zones brillantes" #: f.edit.cc:5468 f.widgets.cc:481 msgid "Mirror Image" msgstr "Image en miroir" #: f.edit.cc:5472 f.warp.cc:3300 msgid "horizontal" msgstr "horizontal" #: f.edit.cc:5473 f.warp.cc:3304 msgid "vertical" msgstr "vertical" #: f.edit.cc:5620 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "maj+clic gauche: choisir une couleur dans l'image \n" "cliquer-tirer gauche: peindre l'image avec cette couleur \n" "cliquer-tirer droit: restaurer l'image originale" #: f.edit.cc:5656 msgid "Paint on Image" msgstr "Peindre sur l'image" #: f.edit.cc:5662 msgid "paint color" msgstr "couleur de la peinture" #: f.edit.cc:5673 f.edit.cc:6640 msgid "brush size" msgstr "taille de la brosse" #: f.edit.cc:5674 f.edit.cc:6641 msgid "opacity center" msgstr "centre de l'opacité" #: f.edit.cc:5675 f.edit.cc:6642 msgid "opacity edge" msgstr "bord de l'opacité" #: f.edit.cc:5687 msgid "erase" msgstr "gommer" #: f.edit.cc:5688 msgid "include transparent areas" msgstr "inclure les zones transparentes" #: f.edit.cc:5695 msgid "drag image" msgstr "tirer l'image" #: f.edit.cc:5697 msgid "zoom image" msgstr "zoomer sur l'image" #: f.edit.cc:6221 msgid "Color Palette" msgstr "Palette de couleurs" #: f.edit.cc:6225 msgid "click on image to choose color" msgstr "cliquer sur l'image pour choisir la couleur" #: f.edit.cc:6415 f.repair.cc:3799 msgid "Color Hue" msgstr "Teinte de la couleur" #: f.edit.cc:6416 f.repair.cc:3768 f.repair.cc:3800 msgid "Saturation" msgstr "Saturation" #: f.edit.cc:6417 f.repair.cc:3769 f.repair.cc:3801 msgid "Lightness" msgstr "Valeur" #: f.edit.cc:6599 msgid "" "shift + left click: pick image position to copy \n" "left drag: copy image to mouse position \n" "right drag: restore image" msgstr "" "maj + clic gauche: choisir la région de l'image à copier \n" "Cliquer-tirer gauche: copier l'image à la position de la souris \n" "Cliquer-tirer droit: restaurer l'image" #: f.edit.cc:6630 f.widgets.cc:483 msgid "Clone Image" msgstr "Cloner l'image" #: f.edit.cc:6649 msgid "paint over transparent areas" msgstr "peindre sur des zones transparentes" #: f.edit.cc:7098 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "cliquer-tirer gauche: fusionner des pixels de l'image \n" "cliquer-tirer droit: restaurer l'image" #: f.edit.cc:7126 f.widgets.cc:484 msgid "Blend Image" msgstr "Fusionner des pixels de l'image" #: f.edit.cc:7134 f.repair.cc:7663 msgid "strength center" msgstr "force au centre" #: f.edit.cc:7135 f.repair.cc:7664 msgid "strength edge" msgstr "force au bord" #: f.edit.cc:7359 msgid "+Version" msgstr "+Version" #: f.edit.cc:7382 f.widgets.cc:275 msgid "Add Text to Image" msgstr "Ajouter du texte à l'image" #: f.edit.cc:7383 msgid "Enter text, click/drag on image, right click to remove" msgstr "Entrer un texte, cliquer/tirer sur l'image, clic droit pour annuler" #: f.edit.cc:7432 f.edit.cc:8310 msgid "Use settings file" msgstr "Utiliser un fichier de paramètres" #: f.edit.cc:7437 f.mashup.cc:2455 msgid "Text" msgstr "Texte" #: f.edit.cc:7442 msgid "Use metadata key" msgstr "Utiliser une clé de métadonnées" #: f.edit.cc:7461 f.mashup.cc:2473 msgid "text" msgstr "texte" #: f.edit.cc:7462 f.edit.cc:8336 f.mashup.cc:2474 f.mashup.cc:2812 msgid "backing" msgstr "arrière-plan" #: f.edit.cc:7463 f.edit.cc:8337 f.mashup.cc:2475 f.mashup.cc:2813 msgid "outline" msgstr "contour" #: f.edit.cc:7464 f.edit.cc:8338 f.mashup.cc:2476 f.mashup.cc:2814 msgid "shadow" msgstr "ombre" #: f.edit.cc:7490 msgid "save to current file" msgstr "enregistrer vers le fichier courant" #: f.edit.cc:7491 f.file.cc:2247 msgid "save as new file version" msgstr "enregistrer sous une nouvelle version" #: f.edit.cc:7492 msgid "" "save to current file \n" "open next file with same text" msgstr "" "enregistrer sous le fichier courant \n" "ouvrir le fichier suivant avec le même texte" #: f.edit.cc:7652 f.mashup.cc:2580 f.tools.cc:1464 msgid "select font" msgstr "sélectionner une police" #: f.edit.cc:7928 f.edit.cc:8766 msgid "text file is defective" msgstr "le fichier texte est défectueux" #: f.edit.cc:8268 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Entrer les propriétés de la ligne ou de la flèche dans le dialogue, \n" "cliquer et tirer sur l'image, clic droit pour enlever" #: f.edit.cc:8302 f.widgets.cc:276 msgid "Add lines or arrows to an image" msgstr "Ajouter des lignes ou des flèches à une image" #: f.edit.cc:8315 f.mashup.cc:2791 msgid "Line length" msgstr "Longueur de la ligne" #: f.edit.cc:8322 f.mashup.cc:2798 msgid "Arrow head" msgstr "Extrémité de la flèche" #: f.edit.cc:8335 f.mashup.cc:2811 msgid "line" msgstr "ligne" #: f.edit.cc:8364 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "fixer la ligne ou flèche dans la composition \n" " commencer une nouvelle ligne ou flèche" #: f.edit.cc:9093 f.widgets.cc:487 msgid "Paint Edits" msgstr "Peinture de fonctions d'édition" #: f.edit.cc:9100 f.edit.cc:9323 fotoxx-18.01.cc:3426 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "La zone sélectionnée ne peut être conservée.\n" "Continuer?" #: f.edit.cc:9108 f.edit.cc:9331 f.tools.cc:2666 msgid "Edit function must be active" msgstr "Une fonction d'édition doit être active" #: f.edit.cc:9113 msgid "Cannot use Paint Edits" msgstr "Impossible d'utiliser la peinture de fonctions d'édition" #: f.edit.cc:9140 msgid "power: center" msgstr "puissance: centre" #: f.edit.cc:9145 msgid "reset area" msgstr "réinitialiser la zone" #: f.edit.cc:9316 f.widgets.cc:488 msgid "Leverage Edits" msgstr "Effet de levier sur les fonctions d'édition" #: f.edit.cc:9317 msgid "Edit Function Amplifier" msgstr "Édition de l'amplificateur d'effet de la fonction" #: f.edit.cc:9336 msgid "Cannot use Leverage Edits" msgstr "Impossible d'utiliser l'effet de levier sur les fonctions d'édition" #: f.edit.cc:9365 msgid "minimum" msgstr "minimum" #: f.edit.cc:9367 msgid "maximum" msgstr "maximum" #: f.edit.cc:9642 f.edit.cc:9686 msgid "Edit Plugins" msgstr "Éditer les greffons" #: f.edit.cc:9643 msgid "Edit plugins menu" msgstr "Éditer le menu des greffons" #: f.edit.cc:9651 msgid "Run as Fotoxx edit function" msgstr "Lancer comme une fonction d'édition de Fotoxx" #: f.edit.cc:9688 msgid "menu name" msgstr "nom du menu" #: f.edit.cc:9691 msgid "command" msgstr "commande" #: f.edit.cc:9888 msgid "Plugin working ..." msgstr "Le greffon est en cours ..." #: f.edit.cc:9897 msgid "plugin failed" msgstr "le greffon a échoué" #: f.effects.cc:79 msgid "Convert to Sketch" msgstr "Convertir en un croquis" #: f.effects.cc:157 msgid "Clip Level" msgstr "Niveau de troncature" #: f.effects.cc:161 msgid "Algorithm" msgstr "Algorithme" #: f.effects.cc:166 msgid "Foreground" msgstr "Avant-plan" #: f.effects.cc:169 f.tools.cc:1275 msgid "Background" msgstr "Arrière-plan" #: f.effects.cc:1054 f.widgets.cc:531 msgid "Line Drawing" msgstr "Dessin au trait" #: f.effects.cc:1067 msgid "black/white" msgstr "noir/blanc" #: f.effects.cc:1326 f.widgets.cc:532 msgid "Color Drawing" msgstr "Dessin en couleurs" #: f.effects.cc:1333 msgid "Bright Areas" msgstr "Valeurs claires" #: f.effects.cc:1575 f.widgets.cc:533 msgid "Graduated Blur" msgstr "Flou graduel" #: f.effects.cc:1579 msgid "Contrast Limit" msgstr "Limite du contraste" #: f.effects.cc:1582 f.repair.cc:929 msgid "Blur Radius" msgstr "Rayon du flou" #: f.effects.cc:1799 msgid "Simulate Embossing" msgstr "Simuler un gaufrage" #: f.effects.cc:1820 msgid "depth" msgstr "profondeur" #: f.effects.cc:2029 msgid "Simulate Tiles" msgstr "Simuler des tuiles" #: f.effects.cc:2033 msgid "tile size" msgstr "taille d'une tuile" #: f.effects.cc:2036 msgid "tile gap" msgstr "interstice entre les tuiles" #: f.effects.cc:2039 msgid "3D depth" msgstr "profondeur 3D" #: f.effects.cc:2258 msgid "Convert Image to Dots" msgstr "Convertir une image en une trame de points" #: f.effects.cc:2262 msgid "dot size" msgstr "taille du point" #: f.effects.cc:2484 msgid "Simulate Painting" msgstr "Simuler une peinture" #: f.effects.cc:2502 msgid "color depth" msgstr "profondeur de couleur" #: f.effects.cc:2506 msgid "patch area goal" msgstr "taille d'une zone colorée" #: f.effects.cc:2510 msgid "req. color match" msgstr "correspondance de couleur requise" #: f.effects.cc:2514 msgid "borders" msgstr "limites" #: f.effects.cc:3081 f.widgets.cc:538 msgid "Vignette" msgstr "Vignette" #: f.effects.cc:3430 msgid "Add Texture" msgstr "Ajout de texture" #: f.effects.cc:3646 msgid "Background Pattern" msgstr "Motif d'arrière-plan" #: f.effects.cc:3650 msgid "Pattern File:" msgstr "Fichier de motif:" #: f.effects.cc:3655 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3658 msgid "Geometry" msgstr "Géometrie" #: f.effects.cc:3659 msgid "Calculate" msgstr "Calculer" #: f.effects.cc:3662 f.widgets.cc:540 msgid "Pattern" msgstr "Motif" #: f.effects.cc:3670 msgid "Overlap" msgstr "Recouvrement" #: f.effects.cc:3678 msgid "Opacity" msgstr "Opacité" #: f.effects.cc:4124 msgid "Create Mosaic" msgstr "Créer une mosaïque" #: f.effects.cc:4170 msgid "Tile" msgstr "Tuile" #: f.effects.cc:4178 f.widgets.cc:535 msgid "Tiles" msgstr "Tuiles" #: f.effects.cc:4184 msgid "Tile blending" msgstr "Fusion des tuiles" #: f.effects.cc:4265 #, c-format msgid "exceeded max. tiles: %d" msgstr "max. de tuiles dépassé: %d" #: f.effects.cc:4272 #, c-format msgid "only %d tile images found" msgstr "trouvé seulement %d images de tuilage" #: f.effects.cc:4756 f.widgets.cc:542 msgid "Custom Kernel" msgstr "Matrice personnalisée" #: f.effects.cc:4760 msgid "Kernel size" msgstr "Taille de la matrice" #: f.effects.cc:4777 msgid "multiply" msgstr "multiplier" #: f.effects.cc:4780 msgid "add" msgstr "additionner" #: f.effects.cc:4784 msgid "Data file" msgstr "Fichier de données" #: f.effects.cc:4804 fotoxx-18.01.cc:3690 msgid "Load settings from file" msgstr "Charger les paramètres depuis un fichier" #: f.effects.cc:5046 msgid "Pull image using the mouse." msgstr "Tirer l'image avec la souris." #: f.effects.cc:5067 f.widgets.cc:543 msgid "Directed Blur" msgstr "Flou dirigé" #: f.effects.cc:5071 msgid "blur span" msgstr "portée du flou" #: f.effects.cc:5074 msgid "intensity" msgstr "intensité" #: f.effects.cc:5276 f.widgets.cc:544 msgid "Blur Background" msgstr "Flouter l'arrière-plan" #: f.effects.cc:5280 msgid "constant blur" msgstr "flou constant" #: f.effects.cc:5283 msgid "increase blur with distance" msgstr "augmenter le flou avec la distance" #: f.effects.cc:5285 msgid "min. blur radius" msgstr "rayon min. du flou" #: f.effects.cc:5288 msgid "max. blur radius" msgstr "rayon max. du flou" #: f.effects.cc:5322 f.warp.cc:819 f.warp.cc:1252 msgid "no active Select Area" msgstr "aucune fontion de sélection de zone n'est active" #: f.effects.cc:5515 f.widgets.cc:545 msgid "Alien Colors" msgstr "Couleurs exotiques" #: f.effects.cc:5518 msgid "blocksize" msgstr "taille d'un bloc" #: f.effects.cc:5521 f.warp.cc:3298 msgid "amplitude" msgstr "amplitude" #: f.file.cc:190 msgid "" "Use EXIF photo date or \n" " file modification date?" msgstr "" "Utiliser la date EXIF de la photo ou \n" " la date de modification du fichier?" #: f.file.cc:204 f.widgets.cc:626 msgid "File" msgstr "Fichier" #: f.file.cc:378 f.process.cc:1345 msgid "Raw Therapee not installed" msgstr "RawTherapee n'est pas installé" #: f.file.cc:396 f.widgets.cc:432 f.widgets.cc:777 msgid "Open RAW (Raw Therapee)" msgstr "Ouvrir un fichier RAW (RawTherapee)" #: f.file.cc:401 msgid "RAW type not registered in User Settings" msgstr "Type RAW non répertorié dans les paramètres utilisateur" #: f.file.cc:416 msgid "Raw Therapee produced no tif file" msgstr "RawTherapee n'a pas créé de fichier TIFF" #: f.file.cc:822 f.widgets.cc:429 msgid "Open Image File" msgstr "Ouvrir un fichier image" #: f.file.cc:841 f.process.cc:597 msgid "unknown file type" msgstr "type de fichier inconnu" #: f.file.cc:1019 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Début de la galerie, galerie précédente: %s" #: f.file.cc:1020 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Fin de la galerie, galerie suivante: %s" #: f.file.cc:1128 msgid "Create Blank Image" msgstr "Créer une image vide" #: f.file.cc:1130 msgid "file name" msgstr "nom du fichier" #: f.file.cc:1164 msgid "supply a file name" msgstr "fournir un nom de fichier" #: f.file.cc:1334 f.widgets.cc:436 msgid "Rename Image File" msgstr "Renommer un fichier image" #: f.file.cc:1341 msgid "Old Name" msgstr "ancien nom" #: f.file.cc:1342 f.process.cc:130 msgid "New Name" msgstr "Nouveau nom" #: f.file.cc:1351 msgid "previous name" msgstr "nom précédent" #: f.file.cc:1352 msgid "Add 1" msgstr "Ajouter 1" #: f.file.cc:1355 f.file.cc:1591 f.file.cc:1886 msgid "keep this dialog open" msgstr "conserver ce dialogue ouvert" #: f.file.cc:1540 msgid "Copy or Move Image File" msgstr "Copier ou déplacer un fichier image" #: f.file.cc:1582 msgid "New Location:" msgstr "Nouvel emplacement:" #: f.file.cc:1587 msgid "copy (duplicate file)" msgstr "copier (dupliquer le fichier)" #: f.file.cc:1588 msgid "move (remove original)" msgstr "déplacer (enlever l'original)" #: f.file.cc:1629 f.process.cc:573 f.process.cc:1503 f.process.cc:2625 msgid "Select directory" msgstr "Sélectionner un répertoire" #: f.file.cc:1667 msgid "new location is not a directory" msgstr "le nouvel emplacement n'est pas un répertoire" #: f.file.cc:1708 f.file.cc:1962 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "la suppression a échoué: \n" " %s" #: f.file.cc:1847 f.widgets.cc:442 msgid "Delete/Trash Image File" msgstr "Supprimer/Transférer vers la corbeille un fichier image" #: f.file.cc:1916 msgid "GTK g_file_trash() function failed" msgstr "la fonction GTK g_file_trash() a échoué" #: f.file.cc:1935 msgid "not a known image file" msgstr "n'est pas un fichier image connu" #: f.file.cc:1941 msgid "Delete read-only file?" msgstr "Supprimer le fichier en lecture seule?" #: f.file.cc:1943 msgid "Trash read-only file?" msgstr "Mettre à la corbeille le fichier en lecture seule?" #: f.file.cc:1991 msgid "no more images" msgstr "plus d'images" #: f.file.cc:2229 msgid "Save Image File" msgstr "Enregistrer le fichier image" #: f.file.cc:2239 msgid "new version" msgstr "nouvelle version" #: f.file.cc:2240 msgid "new file ..." msgstr "nouveau fichier ..." #: f.file.cc:2241 msgid "replace file" msgstr "remplacer le fichier" #: f.file.cc:2248 f.file.cc:2820 msgid "save as new file name or type" msgstr "enregistrer sous un nouveau nom ou type de fichier" #: f.file.cc:2250 msgid "replace old file (OVERWRITE)" msgstr "remplacer l'ancien fichier (ÉCRASER)" #: f.file.cc:2287 f.file.cc:2578 msgid "cannot save as RAW type" msgstr "impossible d'enregistrer sous un format RAW" #: f.file.cc:2395 msgid "too many file versions: 99" msgstr "trop de versions du fichier: 99" #: f.file.cc:2572 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "La transparence va être perdue.\n" "enregistrer au format PNG pour la conserver." #: f.file.cc:2657 msgid "save anyway" msgstr "enregistrer de toute façon" #: f.file.cc:2726 msgid "Unable to copy EXIF/IPTC data" msgstr "Impossible de copier les données EXIF/IPTC" #: f.file.cc:2838 f.file.cc:2841 msgid "make current" msgstr "rendre actuel" #: f.file.cc:2839 msgid "(new file becomes current file)" msgstr "(le nouveau fichier devient le fichier actuel)" #: f.file.cc:2952 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Écraser le fichier? \n" " %s" #: f.file.cc:3147 f.widgets.cc:599 msgid "Quick Start" msgstr "Démarrage rapide" #: f.file.cc:3150 f.widgets.cc:600 msgid "User Guide" msgstr "Guide de l'utilisateur" #: f.file.cc:3153 f.widgets.cc:601 msgid "User Guide Changes" msgstr "Changements au guide de l'utilisateur" #: f.file.cc:3156 f.widgets.cc:602 msgid "README" msgstr "LISEZ-MOI" #: f.file.cc:3159 f.widgets.cc:603 msgid "Change Log" msgstr "Journal des changements" #: f.file.cc:3162 f.widgets.cc:604 msgid "Log File" msgstr "Fichier log" #: f.file.cc:3165 f.widgets.cc:605 msgid "Translations" msgstr "Traductions" #: f.file.cc:3168 f.widgets.cc:606 msgid "Home Page" msgstr "Page d'accueil" #: f.file.cc:3171 f.widgets.cc:607 msgid "About" msgstr "À propos" #: f.file.cc:3177 f.widgets.cc:639 f.widgets.cc:663 f.widgets.cc:676 #: f.widgets.cc:690 fotoxx.h:1223 msgid "Help" msgstr "Aide" #: f.file.cc:3382 f.file.cc:3387 f.file.cc:3457 #, c-format msgid "file type not supported: %s" msgstr "format de fichier non supporté: %s" #: f.gallery.cc:1221 msgid "GoTo" msgstr "Aller à" #: f.gallery.cc:1227 f.widgets.cc:655 msgid "Sort" msgstr "Trier" #: f.gallery.cc:1233 f.widgets.cc:653 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:1243 f.widgets.cc:654 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:1255 msgid "Row Up" msgstr "Rangée Haut" #: f.gallery.cc:1263 msgid "Row Down" msgstr "Rangée Bas" #: f.gallery.cc:1271 f.widgets.cc:658 msgid "Page Up" msgstr "Page Haut" #: f.gallery.cc:1279 f.widgets.cc:659 msgid "Page Down" msgstr "Page Bas" #: f.gallery.cc:1287 f.widgets.cc:656 msgid "First" msgstr "Premier" #: f.gallery.cc:1294 f.widgets.cc:657 msgid "Last" msgstr "Dernier" #: f.gallery.cc:1425 f.gallery.cc:1445 msgid "recent images" msgstr "images récentes" #: f.gallery.cc:1426 f.gallery.cc:1450 msgid "newest images" msgstr "images nouvelles" #: f.gallery.cc:1504 msgid "no albums found" msgstr "aucun album trouvé" #: f.gallery.cc:1541 msgid "Albums cannot be sorted" msgstr "Les albums ne peuvent pas être triés" #: f.gallery.cc:1545 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" " Réinitialiser toutes les galeries\n" " au tri par nom de fichier ascendant" #: f.gallery.cc:1564 msgid "Gallery Sort" msgstr "Tri de la galerie" #: f.gallery.cc:1568 msgid "File Name" msgstr "Nom de fichier" #: f.gallery.cc:1569 msgid "File Mod Date/Time" msgstr "Date/heure de modification du fichier" #: f.gallery.cc:1570 msgid "Photo Date/Time (EXIF)" msgstr "Date/heure de la photo (EXIF)" #: f.gallery.cc:1572 msgid "ascending" msgstr "ascendant" #: f.gallery.cc:1573 msgid "descending" msgstr "descendant" #: f.gallery.cc:2925 f.gallery.cc:2929 msgid "Image File" msgstr "Fichier image" #: f.gallery.cc:2927 msgid "select thumbnail" msgstr "sélectionner une vignette" #: f.gallery.cc:3037 fotoxx.h:1276 msgid "Select Files" msgstr "Sélectionner des fichiers" #: f.gallery.cc:3097 #, c-format msgid "exceed %d selected files" msgstr "excède %d fichiers sélectionnés" #: f.gallery.cc:3457 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Cliquer sur une position dans la liste. Cliquer une vignette dans une " "galerie pour créer le nouveau signet.\n" "Le signet pour la vignette sera ajouté. Changer le nom et presser [Renommer]." #: f.gallery.cc:3484 f.gallery.cc:3734 msgid "Edit Bookmarks" msgstr "Modifier les signets" #: f.gallery.cc:3662 msgid "unable to save bookmarks file" msgstr "impossible d'enregistrer le fichier de signets" #: f.gallery.cc:3734 msgid "Go To Bookmark" msgstr "Aller au signet" #: f.mashup.cc:170 msgid "Mashup layout and background image" msgstr "" "Composition de l'agencement d'objets graphiques et image d'arrière-plan" #: f.mashup.cc:213 msgid "choose an image file" msgstr "choisir un fichier image" #: f.mashup.cc:214 msgid "use current image file" msgstr "utiliser le fichier image courant" #: f.mashup.cc:215 msgid "specify layout size and color" msgstr "spécifier la taille de la mise en page et la couleur" #: f.mashup.cc:216 msgid "open a Mashup project file" msgstr "ouvrir un fichier de projet d'agencement d'objets graphiques" #: f.mashup.cc:259 msgid "no current file" msgstr "pas de fichier courant" #: f.mashup.cc:291 msgid "Make Layout Image" msgstr "Créer l'image contenant la composition" #: f.mashup.cc:293 msgid "project name" msgstr "nom du projet" #: f.mashup.cc:319 msgid "supply a project name" msgstr "fournir un nom de projet" #: f.mashup.cc:411 msgid "Edit Images" msgstr "Édition des images" #: f.mashup.cc:412 f.mashup.cc:2450 msgid "Edit Text" msgstr "Éditer le texte" #: f.mashup.cc:413 msgid "Edit Line" msgstr "Éditer la ligne" #: f.mashup.cc:414 msgid "Rescale" msgstr "Changer l'échelle" #: f.mashup.cc:419 msgid "add or edit images" msgstr "ajouter ou éditer des images" #: f.mashup.cc:421 msgid "add or edit text" msgstr "ajouter ou éditer du texte" #: f.mashup.cc:423 msgid "add or edit lines/arrows" msgstr "ajout ou édition de lignes/de flèches" #: f.mashup.cc:425 msgid "change project scale" msgstr "changer l'échelle du projet" #: f.mashup.cc:427 msgid "project complete" msgstr "projet achevé" #: f.mashup.cc:429 msgid "cancel project" msgstr "annuler le projet" #: f.mashup.cc:447 msgid "rescale project" msgstr "changer l'échelle du projet" #: f.mashup.cc:506 msgid "save Mashup output file" msgstr "enregistrer le fichier de sortie d'agencement" #: f.mashup.cc:526 msgid "save Mashup project file?" msgstr "enregistrer le fichier de projet d'agencement?" #: f.mashup.cc:538 msgid "delete Mashup project file?" msgstr "supprimer le fichier de projet d'agencement?" #: f.mashup.cc:597 msgid "Open Project" msgstr "Ouvrir un projet" #: f.mashup.cc:626 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "le fichier image de la mise en page manque: \n" " %s" #: f.mashup.cc:683 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "le fichier image de recouvrement manque: \n" " %s" #: f.mashup.cc:989 msgid "project file is defective" msgstr "le fichier de projet est défectueux" #: f.mashup.cc:1019 msgid "Save Project" msgstr "Enregistrer le projet" #: f.mashup.cc:1160 msgid "layout exceeds 2 gigabytes" msgstr "la composition dépasse 2 giga-octets" #: f.mashup.cc:1234 msgid "Click image to select, drag image to move." msgstr "Cliquer sur l'image pour sélectionner, tirer l'image pour la déplacer." #: f.mashup.cc:1235 msgid "Make black margins transparent" msgstr "Rendre les marges noires transparentes" #: f.mashup.cc:1274 msgid "Current image:" msgstr "Image courante:" #: f.mashup.cc:1278 msgid "Cycle through images:" msgstr "Passer en revue les images:" #: f.mashup.cc:1285 msgid "Scale" msgstr "Échelle" #: f.mashup.cc:1294 msgid "Stacking Order" msgstr "Ordre d'empilement" #: f.mashup.cc:1295 msgid "Raise" msgstr "Monter" #: f.mashup.cc:1296 msgid "Lower" msgstr "Descendre" #: f.mashup.cc:1299 msgid "Base Transparency" msgstr "Transparence de base" #: f.mashup.cc:1303 msgid "Var. Transparency" msgstr "Transparence variable" #: f.mashup.cc:1304 msgid "Paint" msgstr "Peindre" #: f.mashup.cc:1307 msgid "Bend and fine-align" msgstr "Courbure et alignement fin" #: f.mashup.cc:1308 f.widgets.cc:634 msgid "Warp" msgstr "Déformer" #: f.mashup.cc:1317 zfuncs.cc:11351 zfuncs.cc:11360 msgid "Margins" msgstr "Marges" #: f.mashup.cc:1318 msgid "Hard" msgstr "Dur" #: f.mashup.cc:1319 f.repair.cc:4410 msgid "Blend" msgstr "Fusion" #: f.mashup.cc:1333 msgid "add images to layout" msgstr "ajouter des images à la composition" #: f.mashup.cc:1570 msgid "Paint Image Transparencies" msgstr "Peindre les transparences de l'image" #: f.mashup.cc:1588 msgid "Gradual" msgstr "Graduel" #: f.mashup.cc:1590 fotoxx.h:1255 msgid "Power" msgstr "Puissance" #: f.mashup.cc:1823 msgid "Pull on the image with the mouse." msgstr "Tirer sur l'image avec la souris." #: f.mashup.cc:1839 msgid "Warp Image" msgstr "Déformer l'image" #: f.mashup.cc:1845 f.warp.cc:1383 msgid "warp span" msgstr "étendue de la déformation" #: f.mashup.cc:2281 #, c-format msgid "exceeded %d images" msgstr "excède %d images" #: f.mashup.cc:2423 msgid "Enter text, [Add] to layout, edit properties." msgstr "Entrer le texte, [Ajout] à la mise en page, éditer les propriétés." #: f.mashup.cc:2503 msgid "Text File:" msgstr "Fichier texte:" #: f.mashup.cc:2507 msgid "add entered text to layout" msgstr "ajouter le texte entré à la mise en page" #: f.mashup.cc:2641 msgid "click position to add text" msgstr "cliquer à une position pour ajouter du texte" #: f.mashup.cc:2647 #, c-format msgid "exceeded %d text entries" msgstr "excède %d entrées de texte" #: f.mashup.cc:2652 f.widgets.cc:485 msgid "Add Text" msgstr "Ajouter du texte" #: f.mashup.cc:2761 msgid "Set line properties, [Add] to layout, edit." msgstr "" "Paramétrer les propriétés de la ligne, [Ajout] à la mise en page, éditer." #: f.mashup.cc:2785 msgid "Edit Line/Arrow" msgstr "Éditer la ligne/la flèche" #: f.mashup.cc:2840 msgid "add line/arrow to layout" msgstr "ajouter une ligne/une flèche à la mise en page" #: f.mashup.cc:2931 msgid "click position to add line" msgstr "cliquer à une position pour ajouter une ligne" #: f.mashup.cc:2937 #, c-format msgid "exceeded %d line entries" msgstr "excède %d lignes" #: f.mashup.cc:2942 f.widgets.cc:486 msgid "Add Line" msgstr "Ajouter une ligne" #: f.mashup.cc:4189 msgid "Image Montage" msgstr "Montage d'images" #: f.mashup.cc:4198 msgid "Frame Width" msgstr "Largeur de l'image de sortie" #: f.mashup.cc:4201 f.mashup.cc:4209 msgid "Margin" msgstr "Marge" #: f.mashup.cc:4206 msgid "Image Columns" msgstr "Colonnes d'images" #: f.mashup.cc:4223 #, c-format msgid "exceed %d rows" msgstr "dépasse %d rangées" #: f.mashup.cc:4319 msgid "Optimize" msgstr "Optimiser" #: f.mashup.cc:4327 #, c-format msgid "column difference: %d pixels" msgstr "différence entre les colonnes: %d pixels" #: f.mashup.cc:4385 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "différence entre les colonnes: %d pixels \n" "Rendre les colonnes égales?" #: f.mashup.cc:4407 msgid "Save with unique montage name" msgstr "Enregistrer sous un nom de montage unique" #: f.mashup.cc:4409 msgid "unique name:" msgstr "nom unique" #: f.mashup.cc:4411 msgid "create image map" msgstr "créer une image cliquable" #: f.mashup.cc:4421 f.meta.cc:8637 msgid "supply a reasonable name" msgstr "fournir un nom raisonnable" #: f.mashup.cc:4432 msgid "save montage" msgstr "enregistrer le montage" #: f.mashup.cc:4452 #, c-format msgid "map file saved: %s" msgstr "fichier de modèle enregistré: %s" #: f.mashup.cc:4492 #, c-format msgid "%d max images exceeded" msgstr "la limite de %d images est dépassée" #: f.mashup.cc:4566 f.mashup.cc:4572 #, c-format msgid "image frame is too large: %d x %d" msgstr "le cadre de l'image est trop important: %d x %d" #: f.mashup.cc:4873 #, c-format msgid "montage map file not found: %s" msgstr "le fichier de paramètres du montage est introuvable: %s" #: f.mashup.cc:4877 #, c-format msgid "montage map file invalid: %s" msgstr "le fichier de paramètres du montage n'est pas valide: %s" #: f.meta.cc:242 msgid "Add Metadata Items" msgstr "Ajout d'éléments de métadonnées" #: f.meta.cc:246 f.meta.cc:1583 f.meta.cc:3104 msgid "click to select" msgstr "cliquer pour sélectionner" #: f.meta.cc:251 msgid "click to unselect" msgstr "cliquer pour désélectionner" #: f.meta.cc:475 msgid "Extras" msgstr "Extras" #: f.meta.cc:475 f.meta.cc:655 f.widgets.cc:754 msgid "View Metadata" msgstr "Visualiser les métadonnées" #: f.meta.cc:813 f.widgets.cc:450 f.widgets.cc:755 msgid "Edit Metadata" msgstr "Modifier les métadonnées" #: f.meta.cc:816 msgid "save metadata to file" msgstr "enregistrer les métadonnées dans le fichier" #: f.meta.cc:825 msgid "Image Date" msgstr "Date de l'image" #: f.meta.cc:828 msgid "Time" msgstr "Heure" #: f.meta.cc:836 msgid "Rating (stars):" msgstr "Évaluation (étoiles):" #: f.meta.cc:848 msgid "Caption" msgstr "Légende" #: f.meta.cc:853 msgid "Comments" msgstr "Annotations" #: f.meta.cc:860 msgid "Location" msgstr "Localisation" #: f.meta.cc:863 msgid "Country" msgstr "Pays" #: f.meta.cc:886 msgid "Image Tags" msgstr "Étiquettes de l'image" #: f.meta.cc:891 f.meta.cc:1992 msgid "Recent Tags" msgstr "Étiquettes récentes" #: f.meta.cc:896 f.meta.cc:1998 msgid "Enter New Tag" msgstr "Entrer une nouvelle étiquette" #: f.meta.cc:902 f.meta.cc:2004 f.meta.cc:4565 msgid "Matching Tags" msgstr "Étiquettes correspondantes" #: f.meta.cc:909 f.meta.cc:2013 f.meta.cc:2511 f.meta.cc:4571 msgid "Defined Tags Category" msgstr "Catégories d'étiquettes définies" #: f.meta.cc:916 msgid "search known locations" msgstr "chercher dans les localisations connues" #: f.meta.cc:917 msgid "search using web service" msgstr "chercher en utilisant le service web" #: f.meta.cc:918 f.meta.cc:2020 msgid "create tag categories and tags" msgstr "créer des catégories d'étiquettes et des étiquettes" #: f.meta.cc:1368 f.meta.cc:3673 f.meta.cc:7231 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "mauvaise latitude/longitude: %s %s" #: f.meta.cc:1423 fotoxx.h:1234 msgid "Manage Tags" msgstr "Gérer les étiquettes" #: f.meta.cc:1423 msgid "orphan tags" msgstr "étiquettes orphelines" #: f.meta.cc:1427 msgid "category" msgstr "categorie" #: f.meta.cc:1430 msgid "tag" msgstr "étiquette" #: f.meta.cc:1437 msgid "Defined Tags:" msgstr "Étiquettes définies:" #: f.meta.cc:1579 f.widgets.cc:451 f.widgets.cc:756 msgid "Edit Any Metadata" msgstr "Modifier n'importe quelle métadonnée" #: f.meta.cc:1579 f.meta.cc:3099 msgid "Full List" msgstr "Liste complète" #: f.meta.cc:1592 f.meta.cc:3115 msgid "key name" msgstr "nom de la clé" #: f.meta.cc:1595 f.meta.cc:3116 msgid "key value" msgstr "valeur de la clé" #: f.meta.cc:1677 f.meta.cc:3259 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" "La commande: $ man Image::ExifTool::TagNames \n" "affichera plus de 15000 noms d'étiquette \"standard\"" #: f.meta.cc:1777 f.widgets.cc:452 msgid "Delete Metadata" msgstr "Supprimer des métadonnées" #: f.meta.cc:1783 fotoxx.h:1179 msgid "All" msgstr "Tout" #: f.meta.cc:1784 msgid "One Key:" msgstr "Une clé:" #: f.meta.cc:1968 f.widgets.cc:568 msgid "Batch Add/Remove Tags" msgstr "Ajout/suppression de métadonnées par lot" #: f.meta.cc:1981 msgid "tags to add" msgstr "étiquettes à ajouter" #: f.meta.cc:1982 msgid "tags to remove" msgstr "étiquettes à supprimer" #: f.meta.cc:2095 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " trop d'étiquettes" #: f.meta.cc:2108 msgid "repeat with same files?" msgstr "répéter avec les mêmes fichiers?" #: f.meta.cc:2290 msgid "specify files and tags" msgstr "specifier fichiers et étiquettes" #: f.meta.cc:2490 f.widgets.cc:569 msgid "Batch Rename Tags" msgstr "Renommer des étiquettes par lot" #: f.meta.cc:2500 msgid "(click defined tag)" msgstr "(cliquer sur une étiquette définie)" #: f.meta.cc:2502 f.process.cc:749 msgid "Rename to" msgstr "Renommer vers" #: f.meta.cc:2519 msgid "old tag name >> new tag name" msgstr "ancien nom d'étiquette >> nouveau nom d'étiquette" #: f.meta.cc:2576 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" "%d étiquettes à renommer \n" "dans %d fichiers image. \n" "Continuer?" #: f.meta.cc:2720 msgid "max tags exceeded" msgstr "nombre maximum d'étiquettes dépassé" #: f.meta.cc:2782 f.widgets.cc:570 msgid "Batch Photo Date/Time" msgstr "Mise à jour de la date/de l'heure des photos par lot" #: f.meta.cc:2790 msgid "set a new date/time:" msgstr "entrer une nouvelle date/une nouvelle heure" #: f.meta.cc:2798 msgid "shift existing date/time:" msgstr "décaler la date/l'heure existantes" #: f.meta.cc:2824 msgid "test: show changes, do not update files" msgstr "test: afficher les changements, ne pas mettre à jour les fichiers" #: f.meta.cc:2850 f.meta.cc:3190 f.meta.cc:3368 msgid "no files selected" msgstr "aucun fichier sélectionné" #: f.meta.cc:2880 f.meta.cc:2888 f.meta.cc:2894 msgid "invalid date/time format" msgstr "format de date/d'heure non valide" #: f.meta.cc:3099 f.widgets.cc:571 msgid "Batch Add/Change Metadata" msgstr "Ajouter/Modifier des métadonnées par lot" #: f.meta.cc:3184 msgid "enter key names" msgstr "entrer les noms de clé" #: f.meta.cc:3354 f.widgets.cc:572 msgid "Batch Report Metadata" msgstr "État des métadonnées par lot" #: f.meta.cc:3498 f.widgets.cc:573 msgid "Batch Geotags" msgstr "Ajout de balises de géolocalisation par lot" #: f.meta.cc:3528 msgid "location" msgstr "localisation" #: f.meta.cc:3531 msgid "country" msgstr "pays" #: f.meta.cc:3659 msgid "" "data is incomplete \n" " proceed?" msgstr "" "les données sont incomplètes \n" " continuer?" #: f.meta.cc:3745 msgid "Report Image Locations" msgstr "Afficher un état des localisations des images" #: f.meta.cc:3746 msgid "Group by country" msgstr "Grouper par pays" #: f.meta.cc:3747 msgid "Group by country/location" msgstr "Grouper par pays/localisation" #: f.meta.cc:3748 msgid "Group by country/location/date" msgstr "Grouper par pays/localisation/date" #: f.meta.cc:3749 msgid "Group by date/country/location" msgstr "Grouper par date/pays/localisation" #: f.meta.cc:3752 msgid "Combine within" msgstr "Combiner" #: f.meta.cc:3754 msgid "days" msgstr "jours" #: f.meta.cc:3860 f.widgets.cc:1026 f.widgets.cc:1048 msgid "Image Locations" msgstr "Localisations des images" #: f.meta.cc:4137 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Jan Fév Mar Avr Mai Jui Jul Aoû Sep Oct Nov Déc" #: f.meta.cc:4489 msgid "Search Image Metadata" msgstr "Chercher dans les métadonnées des images" #: f.meta.cc:4493 msgid "images to search:" msgstr "images dans lesquelles chercher:" #: f.meta.cc:4494 msgid "all" msgstr "tout" #: f.meta.cc:4495 msgid "current set only" msgstr "sous-ensemble courant seulement" #: f.meta.cc:4498 msgid "matching images:" msgstr "images correspondantes:" #: f.meta.cc:4499 msgid "new set" msgstr "nouveau sous-ensemble" #: f.meta.cc:4500 msgid "add to set" msgstr "ajout au sous-ensemble" #: f.meta.cc:4501 msgid "remove" msgstr "enlever" #: f.meta.cc:4504 msgid "report type:" msgstr "type d'état:" #: f.meta.cc:4505 msgid "gallery" msgstr "galerie" #: f.meta.cc:4509 msgid "date range" msgstr "plage de dates" #: f.meta.cc:4514 msgid "photo date" msgstr "date de la photo" #: f.meta.cc:4515 msgid "file date" msgstr "date du fichier" #: f.meta.cc:4516 msgid "(yyyy-mm-dd)" msgstr "(aaaa-mm-jj)" #: f.meta.cc:4519 msgid "stars range" msgstr "plage d'étoiles" #: f.meta.cc:4522 msgid "last version only" msgstr "dernière version seulement" #: f.meta.cc:4524 msgid "all/any" msgstr "tout/n'importe lequel" #: f.meta.cc:4527 msgid "search tags" msgstr "rechercher les étiquettes" #: f.meta.cc:4533 msgid "search text" msgstr "rechercher le texte" #: f.meta.cc:4539 msgid "search files" msgstr "rechercher les fichiers" #: f.meta.cc:4545 msgid "search locations" msgstr "rechercher les localisations" #: f.meta.cc:4549 msgid "enter cities, countries" msgstr "saisir les villes et/ou les pays" #: f.meta.cc:4554 msgid "search other metadata" msgstr "rechercher d'autres métadonnées" #: f.meta.cc:4561 msgid "Enter Search Tag" msgstr "Entrer l'étiquette" #: f.meta.cc:4837 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "pour enlever des images du sous-ensemble courant, \n" "rechercher dans celui-ci" #: f.meta.cc:4844 msgid "" "to add images to current set, \n" "search all images" msgstr "" "pour ajouter des images au sous-ensemble courant, \n" "rechercher dans toutes les images" #: f.meta.cc:4890 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "plage de dates irraisonnable \n" " %s %s" #: f.meta.cc:4915 msgid "stars range not reasonable" msgstr "plage d'étoiles irraisonnable" #: f.meta.cc:5129 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "images ajoutées: %d enlevées: %d nouveau décompte: %d" #: f.meta.cc:5132 msgid "no changes made" msgstr "aucun changement effectué" #: f.meta.cc:5303 msgid "" "These items are always reported: \n" "date, stars, tags, caption, comment" msgstr "" "Ces éléments sont toujours inclus dans l'état: \n" "date, étoiles, étiquettes, légende, annotations" #: f.meta.cc:5328 msgid "Additional Items for Report" msgstr "Éléments supplémentaires à inclure dans l'état" #: f.meta.cc:5335 msgid "Keyword" msgstr "Mot-clé" #: f.meta.cc:5349 msgid "Match Criteria" msgstr "Critère de recherche" #: f.meta.cc:5455 #, c-format msgid "bad number: %s" msgstr "nombre non valide: %s" #: f.meta.cc:5712 msgid "date format is YYYY-MM-DD" msgstr "le format de la date est AAAA-MM-JJ" #: f.meta.cc:5716 msgid "date is invalid" msgstr "date non valide" #: f.meta.cc:5750 msgid "time format is HH:MM [:SS]" msgstr "le format de l'heure est HH:MM [:SS]" #: f.meta.cc:5754 msgid "time is invalid" msgstr "heure non valide" #: f.meta.cc:6851 msgid "not found" msgstr "non trouvé" #: f.meta.cc:6852 msgid "location and country required" msgstr "la localisation et le pays sont requis" #: f.meta.cc:7111 msgid "choose location" msgstr "choisir une localisation" #: f.meta.cc:7408 msgid "Set Map Markers" msgstr "Positionner les repères sur la carte" #: f.meta.cc:7409 msgid "mark all image files" msgstr "marquer tous les fichiers image" #: f.meta.cc:7410 msgid "mark current gallery" msgstr "marquer la galerie courante" #: f.meta.cc:7510 msgid "choose map file" msgstr "choisir un fichier de carte" #: f.meta.cc:7652 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "le paquet fotoxx-maps n'est pas installé \n" "(voir https://kornelix.net)" #: f.meta.cc:7657 #, c-format msgid "map file %s is missing" msgstr "le fichier de carte %s est absent" #: f.meta.cc:7661 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" "les données de latitude/longitude sont irraisonnables \n" " %.3f %.3f %.3f %.3f" #: f.meta.cc:7981 f.meta.cc:8508 msgid "No matching images found" msgstr "Aucune image correspondante n'a été trouvée" #: f.meta.cc:8073 msgid "Set Map Source" msgstr "Sélectionner une source pour les cartes" #: f.meta.cc:8584 msgid "Net Map Locations" msgstr "Localisations des cartes Internet" #: f.meta.cc:8589 msgid "map location:" msgstr "localisation de la carte:" #: f.process.cc:123 f.widgets.cc:559 msgid "Batch Convert" msgstr "Conversion par lot" #: f.process.cc:135 msgid "Sequence Numbers" msgstr "Numéros de séquence" #: f.process.cc:137 msgid "base" msgstr "base" #: f.process.cc:140 msgid "adder" msgstr "incrément" #: f.process.cc:145 msgid "New Location" msgstr "Nouvel emplacement" #: f.process.cc:150 msgid "New File Type" msgstr "Nouveau type de fichier" #: f.process.cc:154 f.process.cc:162 f.process.cc:2538 msgid "no change" msgstr "pas de changement" #: f.process.cc:157 f.process.cc:2533 msgid "max. Width" msgstr "Largeur max." #: f.process.cc:166 msgid "Delete Originals" msgstr "Effacer les originaux" #: f.process.cc:167 f.process.cc:754 msgid "Copy Metadata" msgstr "Copier les métadonnées" #: f.process.cc:171 f.process.cc:756 f.process.cc:1320 f.repair.cc:121 #: f.widgets.cc:492 msgid "Sharpen" msgstr "Accentuer la netteté" #: f.process.cc:181 f.process.cc:757 msgid "Overlay Image" msgstr "Image de recouvrement" #: f.process.cc:184 msgid "Width %" msgstr "Largeur %" #: f.process.cc:189 msgid "Position" msgstr "Position" #: f.process.cc:201 msgid "Make constant size for screen:" msgstr "Rendre la taille constante pour l'écran:" #: f.process.cc:209 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "extensions: (année mois jour anciennom séquence) $yyyy $mm $dd $oldname " "$s" #: f.process.cc:331 #, c-format msgid "file type not supported: %s \n" msgstr "type de fichier non supporté: %s \n" #: f.process.cc:471 f.process.cc:2588 msgid "cannot create new file" msgstr "ne peut pas créer de nouveau fichier" #: f.process.cc:517 msgid "updating albums ..." msgstr "mise à jour des albums ..." #: f.process.cc:658 #, c-format msgid "invalid plugin: %s" msgstr "extension non valide: %s" #: f.process.cc:665 msgid "you must use either $s or $oldname" msgstr "vous devez utiliser soit $s soit $oldname" #: f.process.cc:670 msgid "$s plugin needs base and adder" msgstr "l'extension $s a besoin d'une base et d'un incrément" #: f.process.cc:675 msgid "base and adder need $s plugin" msgstr "la base et l'incrément nécessitent l'extension $s" #: f.process.cc:697 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "la taille max. %d x %d n'est pas dans des limites raisonnables" #: f.process.cc:704 msgid "specify overlay image file" msgstr "specifier l'image de recouvrement" #: f.process.cc:720 msgid "specify overlay position" msgstr "specifier la position de l'image de recouvrement" #: f.process.cc:748 #, c-format msgid "Convert %d image files" msgstr "Convertir %d fichiers image" #: f.process.cc:750 msgid "Convert to" msgstr "Convertir vers" #: f.process.cc:751 msgid "Resize within" msgstr "Redimensionner à l'intérieur de" #: f.process.cc:752 msgid "Output to" msgstr "Sortie vers" #: f.process.cc:758 msgid "*** Delete Originals ***" msgstr "*** Effacer les originaux ***" #: f.process.cc:759 msgid "*** Replace Originals ***" msgstr "*** Remplacer les originaux ***" #: f.process.cc:760 msgid "PROCEED?" msgstr "POURSUIVRE?" #: f.process.cc:913 f.widgets.cc:560 msgid "Batch Upright" msgstr "Redressement par lot" #: f.process.cc:919 msgid "Survey all files" msgstr "Examiner tous les fichiers" #: f.process.cc:956 msgid "file cannot be read" msgstr "le fichier est illisible" #: f.process.cc:1072 msgid "cannot select both options" msgstr "impossible de sélectionner les deux options simultanément" #: f.process.cc:1114 f.widgets.cc:561 msgid "Batch Delete/Trash" msgstr "Effacement / Mise à la corbeille par lot" #: f.process.cc:1119 msgid "delete" msgstr "supprimer" #: f.process.cc:1122 msgid "trash" msgstr "corbeille" #: f.process.cc:1260 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Conversion par lot de fichiers RAW (RawTherapee)" #: f.process.cc:1290 msgid "use libraw" msgstr "utiliser libraw" #: f.process.cc:1291 msgid "use Raw Therapee" msgstr "utiliser RawTherapee" #: f.process.cc:1298 msgid "output location" msgstr "emplacement de la sortie" #: f.process.cc:1303 msgid "output file type" msgstr "type du fichier de sortie" #: f.process.cc:1322 msgid "amount" msgstr "quantité" #: f.process.cc:1325 msgid "threshold" msgstr "seuil" #: f.process.cc:1614 msgid "script file" msgstr "fichier de script" #: f.process.cc:1627 msgid "begin making a script file" msgstr "début de la création d'un fichier de script" #: f.process.cc:1630 msgid "finish making a script file" msgstr "fin de la création d'un fichier de script" #: f.process.cc:1636 msgid "execute a script file" msgstr "exécuter un fichier de script" #: f.process.cc:1696 msgid "script already started" msgstr "script déjà démarré" #: f.process.cc:1700 msgid "start a new script file" msgstr "commencer un nouveau fichier de script" #: f.process.cc:1723 msgid "perform edits to be included in the script file" msgstr "" "effectuer les opérations d'édition devant être incluses dans le fichier de " "script" #: f.process.cc:1741 msgid "script file error" msgstr "erreur dans le fichier de script" #: f.process.cc:1746 #, c-format msgid "%s added to script" msgstr "%s ajouté au script" #: f.process.cc:1759 msgid "no script file was started" msgstr "aucun fichier de script n'a été démarré" #: f.process.cc:1767 msgid "script file closed" msgstr "fichier de script fermé" #: f.process.cc:1825 msgid "open script file" msgstr "ouverture d'un fichier de script" #: f.process.cc:1833 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "fichier de script: %s \n" " %s" #: f.process.cc:1855 f.process.cc:1904 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "échec de l'ouverture: %s \n" " %s" #: f.process.cc:1876 #, c-format msgid "unknown edit function: %s" msgstr "fonction d'édition inconnue: %s" #: f.process.cc:1885 #, c-format msgid "load widgets failed: %s" msgstr "le chargement des widgets a échoué: %s" #: f.process.cc:1913 #, c-format msgid "script file format error: %s" msgstr "erreur de format du fichier de script: %s" #: f.process.cc:1949 msgid "growisofs not installed" msgstr "growisofs n'est pas installé" #: f.process.cc:2001 msgid "no DVD/BlueRay device found" msgstr "aucun périphérique DVD/BluRay n'a été trouvé" #: f.process.cc:2024 f.widgets.cc:564 msgid "Burn Images to DVD/BlueRay" msgstr "Graver des images vers un DVD/BluRay" #: f.process.cc:2029 msgid "Select device" msgstr "Sélectionner le périphérique" #: f.process.cc:2136 f.widgets.cc:565 msgid "Find Duplicate Images" msgstr "Trouver les images dupliquées" #: f.process.cc:2138 msgid "Thumbnail size" msgstr "Taille des vignettes" #: f.process.cc:2142 msgid "pixel difference" msgstr "différence des pixels" #: f.process.cc:2145 msgid "pixel count" msgstr "nombre de pixels" #: f.process.cc:2152 msgid "Duplicates:" msgstr "Duplications:" #: f.process.cc:2170 #, c-format msgid "only %d image thumbnails found" msgstr "seulement %d vignettes d'image ont été trouvées" #: f.process.cc:2374 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Créer un fichier liste à partir des fichiers image sélectionnés" #: f.process.cc:2398 f.process.cc:2467 msgid "Output File" msgstr "Fichier de sortie" #: f.process.cc:2418 msgid "no input files selected" msgstr "aucun fichier d'entrée n'est sélectionné" #: f.process.cc:2424 msgid "no output file selected" msgstr "aucun fichier de sortie n'est sélectionné" #: f.process.cc:2523 f.widgets.cc:567 msgid "Export Image Files" msgstr "Exporter des fichiers image" #: f.process.cc:2528 msgid "To Location" msgstr "Vers l'emplacement" #: f.process.cc:2563 msgid "file type not supported" msgstr "type de fichier non supporté" #: f.process.cc:2643 msgid "location is not a directory" msgstr "l'emplacement n'est pas un répertoire" #: f.repair.cc:164 msgid "dark" msgstr "sombre" #: f.repair.cc:165 msgid "light" msgstr "clair" #: f.repair.cc:1193 msgid "Apply repeatedly while watching the image." msgstr "Appliquer de façon répétée tout en observant l'image." #: f.repair.cc:1194 msgid "Measure" msgstr "Mesure" #: f.repair.cc:1228 msgid "Noise Reduction" msgstr "Réduction du bruit" #: f.repair.cc:1247 f.widgets.cc:479 f.widgets.cc:774 fotoxx.h:1218 msgid "Flatten" msgstr "Aplatir" #: f.repair.cc:1248 msgid "Median" msgstr "Médiane" #: f.repair.cc:1263 msgid "dark areas" msgstr "zones sombres" #: f.repair.cc:1265 msgid "all areas" msgstr "toutes les zones" #: f.repair.cc:1856 msgid "Measure Noise" msgstr "Mesure du bruit" #: f.repair.cc:1857 msgid "Move mouse in a monotone image area." msgstr "Passer la souris au-dessus d'une zone monotone de l'image." #: f.repair.cc:2165 msgid "" "Method 1:\n" " Left-click on red-eye to darken.\n" "Method 2:\n" " Drag down and right to enclose red-eye.\n" " Left-click on red-eye to darken.\n" "Undo red-eye:\n" " Right-click on red-eye." msgstr "" "Méthode 1:\n" " Clic gauche sur l'oeil rouge pour assombrir.\n" "Méthode 2:\n" " Cliquer-tirer vers le bas et la droite pour circonscrire l'oeil rouge.\n" " Cic gauche sur l'oeil rouge pour assombrir.\n" "Défaire la fonction oeil rouge:\n" " Clic droit sur l'oeil rouge." #: f.repair.cc:2181 msgid "Red Eye Reduction" msgstr "Réduction des yeux rouges" #: f.repair.cc:2642 f.widgets.cc:496 msgid "Color Mode" msgstr "Mode de couleur" #: f.repair.cc:2645 msgid "reset" msgstr "réinitialiser" #: f.repair.cc:2646 msgid "black/white positive" msgstr "positif noir et blanc" #: f.repair.cc:2647 msgid "black/white negative" msgstr "négatif noir et blanc" #: f.repair.cc:2648 msgid "color negative" msgstr "négatif couleur" #: f.repair.cc:2649 msgid "RGB -> GBR" msgstr "RVB -> VBR" #: f.repair.cc:2650 msgid "sepia" msgstr "sépia" #: f.repair.cc:2861 msgid "Set color depth to 1-16 bits" msgstr "Déterminer une profondeur de couleur entre 1 et 16 bits" #: f.repair.cc:2875 msgid "Set Color Depth" msgstr "Déterminer une profondeur de couleur" #: f.repair.cc:3025 f.widgets.cc:498 msgid "Shift Colors" msgstr "Décaler les couleurs" #: f.repair.cc:3259 f.widgets.cc:499 msgid "Color Saturation" msgstr "Saturation de la couleur" #: f.repair.cc:3448 msgid "+Brightness" msgstr "+Luminosité" #: f.repair.cc:3449 msgid "+Red -Cyan" msgstr "+Rouge -Cyan" #: f.repair.cc:3450 msgid "+Green -Magenta" msgstr "+Vert -Magenta" #: f.repair.cc:3451 msgid "+Blue -Yellow" msgstr "+Bleu -Jaune" #: f.repair.cc:3457 fotoxx.h:1261 msgid "Red" msgstr "Rouge" #: f.repair.cc:3458 fotoxx.h:1220 msgid "Green" msgstr "Vert" #: f.repair.cc:3459 fotoxx.h:1186 msgid "Blue" msgstr "Bleu" #: f.repair.cc:3761 msgid "Input color to match and adjust:" msgstr "Couleur d'entrée à faire coïncider et à ajuster:" #: f.repair.cc:3763 msgid "shift+click on image to select color" msgstr "maj+clic sur l'image pour sélectionner une couleur" #: f.repair.cc:3766 f.repair.cc:7937 msgid "Match using:" msgstr "Correspondance utilisant:" #: f.repair.cc:3767 msgid "Hue" msgstr "Teinte" #: f.repair.cc:3779 msgid "Output Color" msgstr "Couleur de sortie" #: f.repair.cc:3802 msgid "Adjustment" msgstr "Ajustement" #: f.repair.cc:4320 msgid "Click image to select areas." msgstr "Cliquer sur l'image pour sélectionner des zones" #: f.repair.cc:4352 msgid "Local Color" msgstr "Couleur locale" #: f.repair.cc:4363 msgid "Metric:" msgstr "Métrique:" #: f.repair.cc:4786 msgid "Color Match Images" msgstr "Faire coïncider les couleurs des images" #: f.repair.cc:4812 msgid "mouse radius for color sample" msgstr "rayon de la souris pour l'échantillon de couleur" #: f.repair.cc:4814 f.repair.cc:4819 fotoxx.h:1250 msgid "Open" msgstr "Ouvrir" #: f.repair.cc:4815 msgid "image for source color" msgstr "l'image pour la couleur source" #: f.repair.cc:4817 msgid "click on image to get source color" msgstr "cliquer sur l'image pour échantillonner la couleur source" #: f.repair.cc:4820 msgid "image to set matching color" msgstr "l'image pour choisir la couleur en correspondance" #: f.repair.cc:4822 msgid "click on image to set matching color" msgstr "cliquer sur l'image pour choisir la couleur en correspondance" #: f.repair.cc:4881 msgid "select source image color first" msgstr "sélectionner l'image et la couleur source en premier" #: f.repair.cc:5059 msgid "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " msgstr "" " Ajuster chaque couleur RVB pour minimiser \n" " les franges colorées aux bords extrêmes de l'image. " #: f.repair.cc:5082 f.widgets.cc:504 msgid "Color Fringes" msgstr "Franges colorées" #: f.repair.cc:5240 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Tirer la souris pour sélectionner. \n" "2. Effacer. 3. Répéter. " #: f.repair.cc:5262 f.widgets.cc:505 msgid "Smart Erase" msgstr "Effacement intelligent" #: f.repair.cc:5267 fotoxx.h:1259 msgid "Radius" msgstr "Rayon" #: f.repair.cc:5269 f.widgets.cc:493 msgid "Blur" msgstr "Flou" #: f.repair.cc:5272 msgid "New Area" msgstr "Nouvelle zone" #: f.repair.cc:5617 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Tracer une ligne au travers de l'image dans la \n" "direction de la modification de la luminosité" #: f.repair.cc:5656 f.widgets.cc:506 msgid "Brightness Ramp" msgstr "Rampe de la luminosité" #: f.repair.cc:6182 f.widgets.cc:507 msgid "Remove Dust" msgstr "Enlever la poussière" #: f.repair.cc:6186 msgid "spot size limit" msgstr "taille limite du point" #: f.repair.cc:6189 msgid "max. brightness" msgstr "luminosité maximale" #: f.repair.cc:6192 msgid "min. contrast" msgstr "contraste minimal" #: f.repair.cc:6999 f.widgets.cc:510 msgid "Stuck Pixels" msgstr "Pixels bloqués" #: f.repair.cc:7005 msgid "pixel group" msgstr "groupe de pixels" #: f.repair.cc:7013 f.repair.cc:7085 msgid "stuck pixels:" msgstr "pixels bloqués:" #: f.repair.cc:7113 msgid "Load Stuck Pixels" msgstr "Charger les pixels bloqués" #: f.repair.cc:7115 msgid "File:" msgstr "Fichier:" #: f.repair.cc:7139 f.repair.cc:7196 msgid "Stuck Pixels file" msgstr "Fichier des pixels bloqués" #: f.repair.cc:7176 f.tools.cc:4220 msgid "file format error" msgstr "erreur de format de fichier" #: f.repair.cc:7622 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "cliquer-tirer gauche: ajouter de la transparence \n" "cliquer-tirer droit: ajouter de l'opacité" #: f.repair.cc:7654 f.widgets.cc:511 msgid "Paint Transparency" msgstr "Peindre avec de la transparence" #: f.repair.cc:7662 msgid "paintbrush radius" msgstr "rayon de la brosse" #: f.repair.cc:7669 msgid "gradual paint" msgstr "peinture graduelle" #: f.repair.cc:7923 f.widgets.cc:512 msgid "Add Transparency" msgstr "Ajouter de la transparence" #: f.repair.cc:7927 msgid "Match Brightness" msgstr "Correspondance de la luminosité" #: f.repair.cc:7932 msgid "Match Color" msgstr "Correspondance de la couleur" #: f.repair.cc:7934 msgid "click on image to select color" msgstr "cliquer sur l'image pour sélectionner une couleur" #: f.repair.cc:7950 msgid "Invert Match" msgstr "Inverser la correspondance" #: f.repair.cc:7953 fotoxx.h:1285 msgid "Transparency" msgstr "Transparence" #: f.tools.cc:86 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" "Sélectionner les répertoires contenant les fichiers image \n" "(les sous-répertoires seront inclus automatiquement)." #: f.tools.cc:88 msgid "Select to add, click on X to delete." msgstr "Sélectionner pour ajouter, cliquer sur X pour supprimer." #: f.tools.cc:89 msgid "directory for thumbnails" msgstr "répertoire pour les vignettes" #: f.tools.cc:90 msgid "extra metadata items to include in index" msgstr "métadonnées supplémentaires à inclure dans l'index" #: f.tools.cc:91 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "La fonction d'index est terminée. \n" "L'indexation est requise pour les recherches, les cartes \n" "et pour rendre les pages de galeries suffisamment rapides." #: f.tools.cc:126 f.widgets.cc:579 msgid "Index Image Files" msgstr "Index des fichiers image" #: f.tools.cc:298 msgid "Choose top image directories" msgstr "Choisir les répertoires de plus haut niveau pour les images" #: f.tools.cc:299 msgid "Choose thumbnail directory" msgstr "Choisir le répertoire des vignettes" #: f.tools.cc:300 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Tous les fichiers image vont être réindexés. \n" " Continuer?" #: f.tools.cc:372 msgid "" "No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files." msgstr "" "Aucun index des fichiers image n'a été trouvé.\n" "Un index des fichiers image va être créé.\n" "Vos fichiers image ne seront pas modifiés.\n" "Ceci peut prendre un temps considérable si vous \n" "avez plusieurs milliers de fichiers image." #: f.tools.cc:378 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Répertoire non valide: \n" " %s \n" "Veuillez l'enlever." #: f.tools.cc:381 #, c-format msgid "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" msgstr "" "Le répertoire des vignettes: \n" " %s \n" "doit être nommé .../thumbnails" #: f.tools.cc:384 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Répertoire dupliqué: \n" " %s \n" "Veuillez l'enlever." #: f.tools.cc:443 msgid "specify 1 thumbnail directory" msgstr "spécifier 1 répertoire pour les vignettes" #: f.tools.cc:628 msgid "Top directories have no images" msgstr "Les répertoires de plus haut niveau ne contiennent pas d'images" #: f.tools.cc:1075 msgid "Cancel image index function?" msgstr "Annuler la fonction d'indexation des images?" #: f.tools.cc:1195 msgid "Recent Files Gallery" msgstr "Galerie des fichiers récents" #: f.tools.cc:1196 msgid "Newest Files Gallery" msgstr "Galerie des nouveaux fichiers" #: f.tools.cc:1197 msgid "Specific Gallery" msgstr "Galerie spécifique" #: f.tools.cc:1198 msgid "Album Gallery" msgstr "Galerie d'album" #: f.tools.cc:1199 msgid "Previous Gallery" msgstr "Galerie précédente" #: f.tools.cc:1200 msgid "Previous Image File" msgstr "Dernier fichier image" #: f.tools.cc:1201 msgid "Specific Image File" msgstr "Fichier image spécifique" #: f.tools.cc:1202 f.widgets.cc:435 msgid "Blank Window" msgstr "Fenêtre vide" #: f.tools.cc:1250 f.widgets.cc:580 msgid "User Settings" msgstr "Paramétrages utilisateur" #: f.tools.cc:1253 msgid "Startup Display" msgstr "Affichage au démarrage" #: f.tools.cc:1264 msgid "Background color:" msgstr "Couleur d'arrière-plan" #: f.tools.cc:1265 msgid "F-View" msgstr "Vue-F" #: f.tools.cc:1268 msgid "G-View" msgstr "Vue-G" #: f.tools.cc:1272 msgid "Menu Text" msgstr "Texte du menu" #: f.tools.cc:1279 msgid "Menu Style" msgstr "Style de menu" #: f.tools.cc:1280 msgid "Icons" msgstr "Icônes" #: f.tools.cc:1281 msgid "Icons + Text" msgstr "Icônes + texte" #: f.tools.cc:1283 msgid "Icon size" msgstr "Taille des icônes" #: f.tools.cc:1287 msgid "Dialog font" msgstr "Police des dialogues" #: f.tools.cc:1292 msgid "Zoomed Image:" msgstr "Image zoomée:" #: f.tools.cc:1301 msgid "JPEG save quality" msgstr "Qualité d'enregistrement JPEG" #: f.tools.cc:1305 msgid "Curve node capture distance" msgstr "Distance de capture des noeuds de courbe" #: f.tools.cc:1309 msgid "Map marker size" msgstr "Taille du marqueur sur les cartes" #: f.tools.cc:1313 msgid "show last file version only" msgstr "afficher seulement la dernière version du fichier" #: f.tools.cc:1316 msgid "shift image to right margin" msgstr "décaler l'image vers la marge droite" #: f.tools.cc:1319 f.tools.cc:1324 msgid "image index level" msgstr "niveau d'indexation des images" #: f.tools.cc:1321 msgid "Fotoxx started directly" msgstr "Fotoxx démarré directement" #: f.tools.cc:1326 msgid "Fotoxx started by file manager" msgstr "Fotoxx démarré par un gestionnaire de fichiers" #: f.tools.cc:1329 msgid "RAW file types" msgstr "types de fichier RAW" #: f.tools.cc:1333 msgid "video file types" msgstr "types de fichiers vidéo" #: f.tools.cc:1440 msgid "Select startup directory" msgstr "Sélectionner le répertoire de démarrage" #: f.tools.cc:1447 msgid "Select startup image file" msgstr "Sélectionner le fichier image de démarrage" #: f.tools.cc:1454 msgid "Select startup album" msgstr "Sélectionner un album de démarrage" #: f.tools.cc:1497 msgid "startup directory is invalid" msgstr "répertoire de démarrage non valide" #: f.tools.cc:1508 msgid "startup file is invalid" msgstr "fichier de démarrage non valide" #: f.tools.cc:1683 f.widgets.cc:581 msgid "Keyboard Shortcuts" msgstr "Raccourcis clavier" #: f.tools.cc:1690 msgid "Reserved Shortcuts \n" msgstr "Raccourcis clavier réservés \n" #: f.tools.cc:1691 msgid " F1 User Guide for current function \n" msgstr " F1 Guide de l'utilisateur pour la fonction actuelle \n" #: f.tools.cc:1692 msgid " F10 Full Screen with menus \n" msgstr " F10 Vue plein écran avec menus \n" #: f.tools.cc:1693 msgid " F11 Full Screen without menus \n" msgstr " F11 Vue plein écran sans menus \n" #: f.tools.cc:1694 msgid " Escape Quit dialog; Quit Fotoxx \n" msgstr " Échap Quitter Fotoxx \n" #: f.tools.cc:1695 msgid " Ctrl+H Show hidden files in Gallery mode \n" msgstr " Ctrl+H Afficher les fichiers cachés en mode Galerie \n" #: f.tools.cc:1696 msgid " Delete Delete/Trash dialog \n" msgstr " Suppr Dialogue Supprimer/Corbeille \n" #: f.tools.cc:1697 msgid " Arrows Previous/Next Image or Gallery page \n" msgstr " Flèches Image ou page de Galerie précédente/suivante \n" #: f.tools.cc:1757 msgid "Edit KB Shortcuts" msgstr "Éditer les raccourcis clavier" #: f.tools.cc:1766 msgid "shortcut key:" msgstr "touche de raccourci clavier:" #: f.tools.cc:1767 msgid "(enter key)" msgstr "(entrer une touche)" #: f.tools.cc:1768 f.tools.cc:1911 f.tools.cc:2015 msgid "(no selection)" msgstr "(pas de sélection)" #: f.tools.cc:1905 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" est réservé, ne peut être utilisé" #: f.tools.cc:2036 msgid "unable to save KB-shortcuts file" msgstr "impossible d'enregistrer le fichier de raccourcis clavier" #: f.tools.cc:2356 f.widgets.cc:583 msgid "Grid Lines" msgstr "Lignes de grille" #: f.tools.cc:2365 msgid "x-spacing" msgstr "espacement-x" #: f.tools.cc:2366 msgid "x-count" msgstr "nombre-x" #: f.tools.cc:2367 msgid "x-enable" msgstr "activation-x" #: f.tools.cc:2373 msgid "y-spacing" msgstr "espacement-y" #: f.tools.cc:2374 msgid "y-count" msgstr "nombre-y" #: f.tools.cc:2375 msgid "y-enable" msgstr "activation-y" #: f.tools.cc:2495 f.widgets.cc:584 msgid "Line Color" msgstr "Couleur de la ligne" #: f.tools.cc:2560 msgid "Click image to select pixels." msgstr "Cliquer sur l'image pour sélectionner des pixels." #: f.tools.cc:2595 f.widgets.cc:585 msgid "Show RGB" msgstr "Afficher RVB" #: f.tools.cc:2890 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key X to toggle dialog." msgstr "" "Tirer la souris sur l'image. \n" "Clic gauche pour annuler. \n" "Touche X pour afficher/masquer le dialogue." #: f.tools.cc:2918 f.widgets.cc:586 msgid "Magnify Image" msgstr "Agrandir l'image" #: f.tools.cc:2927 msgid "X-size" msgstr "Taille X" #: f.tools.cc:3172 msgid "Darkest and Brightest Pixels" msgstr "Pixels les plus sombres et les plus clairs" #: f.tools.cc:3195 msgid "Dark Limit" msgstr "Limite valeurs sombres" #: f.tools.cc:3196 msgid "Bright Limit" msgstr "Limite valeurs claires" #: f.tools.cc:3285 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "La luminosité doit s'afficher selon \n" "une rampe graduelle de bord à bord." #: f.tools.cc:3376 f.widgets.cc:589 msgid "Monitor Gamma" msgstr "Gamma du moniteur" #: f.tools.cc:3443 msgid "Available Translations" msgstr "Traductions disponibles" #: f.tools.cc:3447 msgid "Set Language" msgstr "Choisir le langage" #: f.tools.cc:3573 msgid "Change Color Profile" msgstr "Changer le profil de couleur" #: f.tools.cc:3577 msgid "input profile" msgstr "profil d'entrée" #: f.tools.cc:3581 msgid "output profile" msgstr "profil de sortie" #: f.tools.cc:3602 msgid "Unable to change EXIF color profile" msgstr "Impossible de modifier le profil de couleur EXIF" #: f.tools.cc:3604 msgid "automatic new version created" msgstr "nouvelle version automatique créée" #: f.tools.cc:3613 msgid "color profile" msgstr "profil de couleur" #: f.tools.cc:3661 f.tools.cc:3667 #, c-format msgid "unknown cms profile %s" msgstr "profil cms inconnu %s" #: f.tools.cc:3770 f.widgets.cc:593 msgid "Calibrate Printer" msgstr "Calibrer l'imprimante" #: f.tools.cc:3795 msgid "print color chart" msgstr "imprimer une charte de couleurs" #: f.tools.cc:3796 msgid "scan and save color chart" msgstr "scanner et enregistrer la charte de couleurs" #: f.tools.cc:3797 msgid "align and trim color chart" msgstr "aligner et rogner la charte de couleurs" #: f.tools.cc:3798 msgid "open and process color chart" msgstr "ouvrir et traiter la charte de couleurs" #: f.tools.cc:3799 msgid "print image with revised colors" msgstr "imprimer l'image avec les couleurs calibrées" #: f.tools.cc:3940 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Scanner la charte de couleurs imprimée. \n" "La rangée la plus sombre est en haut. \n" "Enregistrer sous %s/" #: f.tools.cc:3955 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Ouvrir et éditer le fichier de charte de couleurs scannée. \n" "Corriger toute distortion ou rotation dûe au scanner. \n" "(Utiliser l'outil Corriger la perspective pour ceci). \n" "Éliminer la fine marge verte PRÉCISÉMENT." #: f.tools.cc:3987 msgid "Open the trimmed color chart file" msgstr "Ouvrir le fichier de charte de couleurs rognée" #: f.tools.cc:4120 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" "Entrer le nom du fichier qui contiendra les données de calibration " "produites \n" "[votre nom de fichier de calibration].dat" #: f.tools.cc:4160 msgid "Color map file to use" msgstr "Fichier de calibration à utiliser" #: f.tools.cc:4179 msgid "Select the image file to print." msgstr "Sélectionner le fichier image à imprimer." #: f.tools.cc:4244 msgid "converting colors..." msgstr "conversion des couleurs..." #: f.tools.cc:4344 msgid "Image colors are converted for printing." msgstr "Les couleurs de l'image sont converties pour l'impression." #: f.tools.cc:4467 msgid "This function is for AppImage package only" msgstr "Cette fonction est valide uniquement pour le fichier AppImage" #: f.tools.cc:4471 msgid "" "Completely remove the AppImage package \n" "Press F1 for more information. \n" "Continue?" msgstr "" "Enlever complètement le paquet AppImage \n" "Presser F1 pour plus d'informations. \n" "Continuer?" #: f.warp.cc:94 f.widgets.cc:515 msgid "Unbend" msgstr "Corriger la courbure" #: f.warp.cc:371 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Cliquer aux 4 coins d'un quadrilatère. Presser [appliquer]. \n" " L'image est déformée pour faire du quadrilatère un rectangle." #: f.warp.cc:388 msgid "Perspective Correction" msgstr "Correction de la perspective" #: f.warp.cc:621 msgid "must have 4 corners" msgstr "il faut 4 coins" #: f.warp.cc:745 msgid "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." msgstr "" " Sélectionner une zone à déformer avec la fonction de sélection de zone. \n" " Presser [démarrer déformation] et tirer la zone avec la souris. \n" " Faire plusieurs tirés jusqu'à satisfaction. \n" " Une fois achevé, sélectionner une autre zone ou presser [terminé]." #: f.warp.cc:758 f.widgets.cc:517 msgid "Warp area" msgstr "Déformer une zone" #: f.warp.cc:763 msgid "start warp" msgstr "démarrer la déformation" #: f.warp.cc:1149 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" "Utiliser Sélectionner une zone pour délimiter un visage. \n" "Cliquer sur le centre de la distorsion. \n" "Déplacer le curseur. \n" #: f.warp.cc:1176 f.widgets.cc:518 msgid "Unwarp Closeup" msgstr "Corriger la déformation d'un gros plan" #: f.warp.cc:1356 f.warp.cc:1671 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Tirer une position sur l'image en utilisant la souris. \n" " Faire plusieurs tirés jusqu'à satisfaction. \n" " Une fois achevé, presser [terminé]." #: f.warp.cc:1374 f.widgets.cc:519 msgid "Warp curved" msgstr "Déformation courbe" #: f.warp.cc:1689 f.widgets.cc:520 msgid "Warp linear" msgstr "Déformation linéaire" #: f.warp.cc:2005 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Tirer un coin de l'image avec la souris. \n" " Faire plusieurs tirés jusqu'à satisfaction. \n" " Une fois achevé, presser [terminé]." #: f.warp.cc:2021 f.widgets.cc:521 msgid "Warp affine" msgstr "Déformation affine" #: f.warp.cc:2368 f.widgets.cc:522 msgid "Flatten Book Page" msgstr "Aplatir une page de livre" #: f.warp.cc:2369 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Rogner l'image pour isoler une page. \n" "Définir les bords supérieur et inférieur avec \n" "4 clics ou plus de la souris, puis aplatir: " #: f.warp.cc:2372 msgid "Stretch curved-down surfaces:" msgstr "Aplatir les surfaces courbées:" #: f.warp.cc:2427 msgid "Top:" msgstr "en Haut:" #: f.warp.cc:2430 msgid "Bottom:" msgstr "en Bas:" #: f.warp.cc:2811 f.widgets.cc:523 msgid "Spherical Projection" msgstr "Projection sphérique" #: f.warp.cc:2855 msgid "Magnify" msgstr "Agrandir" #: f.warp.cc:3029 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" " Sélectionner les zones qui resteront inchangées. \n" " Tirer l'image depuis le coin supérieur gauche. \n" " Une fois achevé, presser [terminé]." #: f.warp.cc:3045 f.widgets.cc:524 msgid "Selective Rescale" msgstr "Changement d'échelle sélectif" #: f.warp.cc:3068 msgid "select areas first" msgstr "sélectionner les zone en premier lieu" #: f.warp.cc:3290 f.widgets.cc:525 msgid "Make Waves" msgstr "Créer des vagues" #: f.warp.cc:3297 msgid "wavelength" msgstr "longueur d'une vague" #: f.warp.cc:3299 msgid "variance" msgstr "variation" #: f.warp.cc:3310 msgid "perspective" msgstr "perspective" #: f.warp.cc:3484 f.warp.cc:3515 msgid "Twist" msgstr "Torsion" #: f.warp.cc:3512 msgid "drag mouse to set center" msgstr "tirer la souris pour positionner le centre" #: f.widgets.cc:103 msgid "Album" msgstr "Album" #: f.widgets.cc:105 msgid "TOP" msgstr "HAUT" #: f.widgets.cc:171 msgid "Current Image File (key F)" msgstr "Fichier image courant (touche F)" #: f.widgets.cc:172 msgid "Thumbnail Gallery (key G)" msgstr "Galerie de vignettes (touche G)" #: f.widgets.cc:173 msgid "World Maps (key W)" msgstr "Cartes du monde (touche W)" #: f.widgets.cc:174 msgid "Net Maps (key M)" msgstr "Cartes Internet (touche M)" #: f.widgets.cc:177 msgid "Favorite Functions" msgstr "Fonctions favorites" #: f.widgets.cc:178 msgid "File: Open, RAW, Rename, Delete, Print" msgstr "Fichier: ouvrir, RAW, renommer, supprimer, imprimer" #: f.widgets.cc:179 msgid "Save modified image file to disk" msgstr "Enregistrer sur disque le fichier image modifié" #: f.widgets.cc:180 msgid "Open previous or next file (left/right mouse click)" msgstr "Ouvrir le fichier précédent ou suivant (clic gauche/droit)" #: f.widgets.cc:181 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "" "Métadonnées: légendes, étiquettes, évaluations, balises de géolocalisation, " "recherche ... " #: f.widgets.cc:182 msgid "Areas: Select areas to edit, copy and paste" msgstr "Zones: sélectionner les zones à éditer, copier et coller" #: f.widgets.cc:183 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "" "Édition: rognage, rotation, redimensionnement, luminosité, contraste, " "texte ..." #: f.widgets.cc:184 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Réparer: netteté, bruit, yeux rouges, couleur, peinture, clonage ..." #: f.widgets.cc:185 msgid "Warp: Fix Perspective, Warp or unwarp image ..." msgstr "" "Distorsion: Corriger la perspective, Distordre une image ou en corriger la " "déformation ..." #: f.widgets.cc:186 msgid "Effects: Special Effects, Arty Transforms" msgstr "Effets: effets spéciaux, transformations artistiques" #: f.widgets.cc:187 msgid "" "left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "Clic gauche/droit: défaire/refaire 1 opération d'édition \n" " avec la touche A: défaire/refaire toutes les opérations d'édition \n" " clic milieu: aller à n'importe quelle opération d'édition" #: f.widgets.cc:190 msgid "Tools: Index, Settings, Shortcuts, Magnify ..." msgstr "Outils: index, paramétrages, raccourcis, agrandir ..." #: f.widgets.cc:191 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "" "Aide: démarrage rapide, guide de l'utilisateur, changements récents ..." #: f.widgets.cc:194 msgid "Sync Gallery, Albums, Slide Show" msgstr "Sync. galerie, albums, diaporama" #: f.widgets.cc:195 msgid "set and recall bookmarked image locations" msgstr "positionner et rappeler les images repérées par un signet" #: f.widgets.cc:196 msgid "increase thumbnail size" msgstr "augmenter la taille des vignettes" #: f.widgets.cc:197 msgid "reduce thumbnail size" msgstr "réduire la taille des vignettes" #: f.widgets.cc:198 msgid "change sort order" msgstr "changer l'ordre de tri" #: f.widgets.cc:199 msgid "jump to beginning (top)" msgstr "sauter au début (sommet)" #: f.widgets.cc:200 msgid "jump to end (bottom)" msgstr "sauter à la fin (bas)" #: f.widgets.cc:201 msgid "previous page" msgstr "page précédente" #: f.widgets.cc:202 msgid "next page" msgstr "page suivante" #: f.widgets.cc:203 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "" "Combiner: Image à large gamme dynamique (HDR), à forte profondeur de champ " "(HDF), panorama, pile, agencement d'objets" #: f.widgets.cc:204 msgid "Process: convert, export, metadata, search ..." msgstr "Traitements: conversion, export, métadonnées, recherche ..." #: f.widgets.cc:207 msgid "Choose a map file" msgstr "Choisir un fichier de carte local" #: f.widgets.cc:208 f.widgets.cc:212 msgid "Mark all images or current gallery" msgstr "Marquer toutes les images ou bien la galerie courante" #: f.widgets.cc:211 msgid "Choose Internet map source" msgstr "Choisir un serveur de cartes sur Internet" #: f.widgets.cc:213 msgid "Save and recall named map locations" msgstr "Enregistrer et rappeler les cartes nommées" #: f.widgets.cc:216 msgid "Open another window" msgstr "Ouvrir une autre fenêtre" #: f.widgets.cc:217 msgid "Open a new image file" msgstr "Ouvrir un nouveau fichier image" #: f.widgets.cc:218 f.widgets.cc:430 msgid "Cycle 2 Previous Files" msgstr "Alterne les 2 fichiers précédents" #: f.widgets.cc:219 f.widgets.cc:431 msgid "Cycle 3 Previous Files" msgstr "Alterne les 3 fichiers précédents" #: f.widgets.cc:220 msgid "Open a recently seen file" msgstr "Ouvrir un fichier récemment visualisé" #: f.widgets.cc:221 msgid "Open a newly added file" msgstr "Ouvrir un fichier nouvellement ajouté" #: f.widgets.cc:222 msgid "Open and edit a camera RAW file" msgstr "Ouvrir et traiter un fichier RAW" #: f.widgets.cc:223 msgid "View a 360 degree panorama image file" msgstr "Visualiser un fichier image contenant un panorama à 360 degrés" #: f.widgets.cc:224 msgid "Change the image file name" msgstr "Changer le nom du fichier image" #: f.widgets.cc:225 msgid "Copy or Move an image file to a new location" msgstr "Copier ou transférer un fichier image vers un nouvel emplacement" #: f.widgets.cc:226 msgid "Copy an image file to the desktop" msgstr "Copier un fichier image vers le bureau" #: f.widgets.cc:227 msgid "Copy image file to the clipboard" msgstr "Copier le fichier image vers le presse-papiers" #: f.widgets.cc:228 msgid "Copy image file to the image cache" msgstr "Copier le fichier image vers le cache d'images" #: f.widgets.cc:229 msgid "Show location on Interet map" msgstr "Afficher la localisation sur une carte Internet" #: f.widgets.cc:230 msgid "Create a blank image" msgstr "Créer une image vide" #: f.widgets.cc:231 msgid "toggle - blank or restore window" msgstr "bascule - vide ou restaure la fenêtre" #: f.widgets.cc:232 msgid "Delete or trash image file" msgstr "Supprimer ou mettre à la corbeille le fichier image" #: f.widgets.cc:233 msgid "Print the current image" msgstr "Imprimer l'image courante" #: f.widgets.cc:234 msgid "Print current image with adjusted colors" msgstr "Imprimer l'image courante avec des couleurs calibrées" #: f.widgets.cc:235 f.widgets.cc:445 msgid "Quit Fotoxx" msgstr "Quitter Fotoxx" #: f.widgets.cc:238 msgid "List a few key metadata items" msgstr "Lister un certain nombre d'éléments clé de métadonnées" #: f.widgets.cc:239 msgid "List all metadata items" msgstr "Lister tous les éléments de métadonnées" #: f.widgets.cc:240 msgid "Edit image tags/geotags/caption/rating ..." msgstr "" "Modifier les étiquettes/balises de géolocalisation/légendes/évaluations ..." #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Modifier n'importe quelle métadonnée" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Enlever les métadonnées de l'image sélectionnée" #: f.widgets.cc:243 msgid "(Toggle) show captions and comments" msgstr "(Bascule) affiche les légendes et les annotations" #: f.widgets.cc:246 msgid "Select object or area for editing" msgstr "Sélectionner un objet ou une zone pour édition" #: f.widgets.cc:247 msgid "Find a gap in an area outline" msgstr "Détecter une discontinuité dans le contour d'une zone" #: f.widgets.cc:248 msgid "Select hairy or irregular edge" msgstr "Sélectionner un bord plumeux ou irrégulier" #: f.widgets.cc:249 msgid "Show (outline) existing area" msgstr "Affiche les contours d'une zone existante" #: f.widgets.cc:250 msgid "Hide existing area" msgstr "Masquer les contours d'une zone existante" #: f.widgets.cc:251 msgid "Enable area for editing" msgstr "Activer une zone existante pour l'édition" #: f.widgets.cc:252 msgid "Disable area for editing" msgstr "Désactiver une zone existante" #: f.widgets.cc:253 msgid "Reverse existing area" msgstr "Inverser une zone existante" #: f.widgets.cc:254 msgid "Erase existing area" msgstr "Effacer une zone existante" #: f.widgets.cc:255 msgid "Copy area for later pasting into image" msgstr "Copier une zone pour collage ultérieur vers une image" #: f.widgets.cc:256 msgid "Save area to a file with transparency" msgstr "Enregistrer une zone dans un fichier avec la transparence" #: f.widgets.cc:257 msgid "Open a file and paste as area into image" msgstr "Ouvrir un fichier et le coller comme une zone dans une image" #: f.widgets.cc:258 msgid "Paste previously copied area into image" msgstr "Coller une zone précédemment copiée dans une image" #: f.widgets.cc:261 msgid "Trim/Crop margins and/or Rotate" msgstr "Rognage des marges et/ou rotation" #: f.widgets.cc:262 msgid "Upright a rotated image" msgstr "Redresser une image retournée" #: f.widgets.cc:263 msgid "Change pixel dimensions" msgstr "Changer la dimension en pixels" #: f.widgets.cc:264 f.widgets.cc:265 msgid "Fast auto enhance that may work OK" msgstr "Amélioration rapide pouvant se révéler intéressante" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Ajuster la luminosité, le contraste, la couleur" #: f.widgets.cc:267 msgid "Edit brightness distribution" msgstr "Modifier la distribution de la luminosité" #: f.widgets.cc:268 msgid "Magnify brightness gradients to enhance details" msgstr "Amplifier les gradients de luminosité pour améliorer les détails" #: f.widgets.cc:269 msgid "Flatten brightness distribution" msgstr "Aplatir la distribution de la luminosité" #: f.widgets.cc:270 msgid "Rescale brightness - reduce color caste and fog/haze" msgstr "" "Rééchelonner la luminosité - réduire la dominante colorée et la brume ou le " "voile" #: f.widgets.cc:271 msgid "Mirror image horizontally or vertically" msgstr "Refléter une image horizontalement ou verticalement" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Peindre des pixels dans l'image en utilisant la souris" #: f.widgets.cc:273 msgid "Clone image pixels using the mouse" msgstr "Cloner des pixels de l'image en utilisant la souris" #: f.widgets.cc:274 msgid "Blend image pixels using the mouse" msgstr "Fusionner des pixels de l'image en utilisant la souris" #: f.widgets.cc:277 msgid "Paint edit function gradually with mouse" msgstr "Peindre une fonction d'édition graduellement avec la souris" #: f.widgets.cc:278 msgid "Leverage edits by brightness or color" msgstr "" "Augmenter l'effet d'une fonction d'édition par la luminosité ou la couleur" #: f.widgets.cc:279 msgid "Edit plugins menu or run a plugin function" msgstr "Éditer le menu des greffons ou lancer un greffon en tant que fonction" #: f.widgets.cc:282 msgid "Make the image look sharper" msgstr "Rendre l'image plus nette" #: f.widgets.cc:283 msgid "Make the image look fuzzy" msgstr "Rendre l'image floue" #: f.widgets.cc:284 msgid "Filter noise from low-light photos" msgstr "Filtrer le bruit d'une image faiblement éclairée" #: f.widgets.cc:285 msgid "Fix red-eyes from electronic flash" msgstr "Corriger l'effet yeux rouges dû au flash" #: f.widgets.cc:286 msgid "Make BW/color, negative/positive, sepia" msgstr "Rendu en NB/couleur, négatif/positif, sépia" #: f.widgets.cc:287 msgid "Reduce color depth (posterize)" msgstr "Réduire la profondeur de couleur (postérisation)" #: f.widgets.cc:288 msgid "Shift/convert colors into other colors" msgstr "Décaler/convertir les couleurs en d'autres couleurs" #: f.widgets.cc:289 msgid "Adjust color intensity (saturation)" msgstr "Ajuster l'intensité de la couleur (saturation)" #: f.widgets.cc:290 msgid "Adjust color using RGB or CMY colors" msgstr "Ajuster la couleur en utilisant RVB ou CMJ" #: f.widgets.cc:291 msgid "Adjust color using HSL colors" msgstr "Ajuster la couleur en utilisant TSV" #: f.widgets.cc:292 msgid "Adjust color in selected image areas" msgstr "Ajuster la couleur dans des zones sélectionnées de l'image" #: f.widgets.cc:293 msgid "Match colors on one image with another" msgstr "Faire se correspondre les couleurs d'une image avec celles d'une autre" #: f.widgets.cc:294 msgid "Reduce Chromatic Abberation" msgstr "Réduire l'aberration chromatique" #: f.widgets.cc:295 msgid "Remove unwanted objects" msgstr "Enlever les objets indésirables" #: f.widgets.cc:296 msgid "Add a brightness/color ramp across the image" msgstr "Ajouter une rampe de luminosité/couleur au travers de l'image" #: f.widgets.cc:297 msgid "Remove dust spots from scanned slides" msgstr "Enlever les points de poussière d'images scannées" #: f.widgets.cc:298 msgid "Smoothen edges with jaggies" msgstr "Adoucir les bords en escalier" #: f.widgets.cc:299 msgid "Erase known hot and dark pixels" msgstr "Effacer les pixels chauds et sombres connus" #: f.widgets.cc:300 msgid "Paint image transparency using the mouse" msgstr "Peindre la transparence de l'image avec la souris" #: f.widgets.cc:301 msgid "Add image transparency based on image attributes" msgstr "" "Ajouter une couche transparente à l'image, basée sur les attributs de l'image" #: f.widgets.cc:304 msgid "Remove curvature, esp. panoramas" msgstr "Effacer la courbure, spécialement dans les panoramas" #: f.widgets.cc:305 msgid "Straighten objects seen from an angle" msgstr "Redresser la perspective d'objets vus sous un certain angle" #: f.widgets.cc:306 msgid "Distort image areas using the mouse" msgstr "Distordre des zones de l'image avec la souris" #: f.widgets.cc:307 msgid "Unwarp closeup face photo to remove distortion" msgstr "" "Corriger la déformation d'une photo de visage en gros plan pour en éliminer " "la distorsion" #: f.widgets.cc:308 f.widgets.cc:309 f.widgets.cc:310 msgid "Distort the whole image using the mouse" msgstr "Distordre l'image entière avec la souris" #: f.widgets.cc:311 msgid "Flatten a photographed book page" msgstr "Aplatir une page de livre photographiée" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "Créer une projection sphérique d'une image" #: f.widgets.cc:313 msgid "Rescale image outside selected areas" msgstr "Changer l'échelle de l'image à l'extérieur des zones sélectionnées" #: f.widgets.cc:314 msgid "Warp an image with a wave pattern" msgstr "Déformer une image avec un motif de vague" #: f.widgets.cc:315 msgid "Twist image centered at mouse position" msgstr "Tordre l'image autour de son centre à la position de la souris" #: f.widgets.cc:318 msgid "Convert to simulated sketch" msgstr "Convertir en une imitation de croquis" #: f.widgets.cc:319 msgid "Convert image into a cartoon drawing" msgstr "Convertir l'image en un cartoon" #: f.widgets.cc:320 msgid "Convert to line drawing (edge detection)" msgstr "Convertir en un dessin au trait (detection des bords)" #: f.widgets.cc:321 msgid "Convert to solid color drawing" msgstr "Convertir en un dessin en couleurs" #: f.widgets.cc:322 msgid "Graduated Blur depending on contrast" msgstr "Flou graduel dépendant du contraste" #: f.widgets.cc:323 msgid "Create an embossed or 3D appearance" msgstr "Créer une apparence gaufrée ou 3D" #: f.widgets.cc:324 msgid "Convert to square tiles" msgstr "Convertir en tuiles carrées" #: f.widgets.cc:325 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Convertir en points (effet Roy Lichtenstein)" #: f.widgets.cc:326 msgid "Convert into a simulated painting" msgstr "Convertir en une peinture" #: f.widgets.cc:327 msgid "Change brightness or color radially" msgstr "Changer la luminosité ou la couleur radialement" #: f.widgets.cc:328 msgid "Add texture to an image" msgstr "Ajouter une texture à une image" #: f.widgets.cc:329 msgid "Tile image with a repeating pattern" msgstr "Transformer l'image en un motif de tuiles répété" #: f.widgets.cc:330 msgid "Create a mosaic with tiles made from all images" msgstr "Créer une mosaïque avec des tuiles provenant de toutes les images" #: f.widgets.cc:331 msgid "Process an image using a custom kernel" msgstr "Traiter une image en utilisant une matrice personnalisée" #: f.widgets.cc:332 msgid "Blur an image in the direction of mouse drags" msgstr "Flouter une image dans la direction des tirés de souris" #: f.widgets.cc:333 msgid "Increasing blur with distance from selected areas" msgstr "Flou accru avec la distance, depuis des zones sélectionnées" #: f.widgets.cc:334 msgid "Change color hue using an algorithm" msgstr "Modifier la teinte de la couleur en utilisant un algorithme" #: f.widgets.cc:337 msgid "Combine bright/dark images for better detail" msgstr "Combiner des images claires/sombres pour accentuer les détails" #: f.widgets.cc:338 msgid "Combine near/far focus images for deeper focus" msgstr "" "Créer une grande profondeur de champ en combinant des images différemment " "focalisées" #: f.widgets.cc:339 msgid "Combine images to erase passing people, etc." msgstr "Combiner des images pour effacer les gens, etc." #: f.widgets.cc:340 msgid "Combine noisy images into a low-noise image" msgstr "Combiner des images fortement grainées en une image à faible grain" #: f.widgets.cc:341 msgid "Combine images into a panorama" msgstr "Combiner des images en un panorama" #: f.widgets.cc:342 msgid "Combine images into a vertical panorama" msgstr "Combiner des images en un panorama vertical" #: f.widgets.cc:343 msgid "Combine images into a panorama (panorama tools)" msgstr "Combiner des images en un panorama (panorama tools)" #: f.widgets.cc:344 msgid "Arrange images and text in a layout (montage)" msgstr "Créer un agencement d'éléments d'images et de texte (mise en page)" #: f.widgets.cc:345 msgid "Combine images into a montage of images" msgstr "Combiner des images en un montage" #: f.widgets.cc:348 msgid "Rename/convert/resize/move multiple files" msgstr "Renommer/convertir/redimensionner/transférer de multiples fichiers" #: f.widgets.cc:349 msgid "Upright multiple rotated image files" msgstr "Redresser de multiples fichiers image" #: f.widgets.cc:350 msgid "Delete or Trash multiple files" msgstr "Supprimer ou mettre à la corbeille de multiples fichiers" #: f.widgets.cc:351 msgid "Convert camera RAW files using libraw or Raw Therapee" msgstr "Convertir des fichiers RAW en utilisant libraw ou RawTherapee" #: f.widgets.cc:352 msgid "Build and run edit script files" msgstr "Construire et modifier des fichiers script" #: f.widgets.cc:353 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Graver les fichiers image sélectionnés sur un disque DVD/BluRay" #: f.widgets.cc:354 msgid "Search all image files and report duplicates" msgstr "Chercher dans tous les fichiers image et afficher les doublons" #: f.widgets.cc:356 msgid "Export selected image files to a directory" msgstr "Exporter les fichiers image sélectionnés vers un répertoire" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Ajouter/effacer les légendes pour de multiples images" #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Convertir le nom des étiquettes pour toutes les images" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "mise à jour ou décalage de la date/de l'heure de photos" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Ajouter/modifier/supprimer des métadonnées pour de multiples images" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Afficher un état des métadonnées pour de multiples images" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Ajouter/revoir les balises de géolocalisation pour de multiples images" #: f.widgets.cc:363 msgid "Find all images for a location [date]" msgstr "Trouver toutes les images pour une localisation donnée [date]" #: f.widgets.cc:364 msgid "Show image counts by month, select and report" msgstr "" "Afficher le nombre d'images par mois, sélectionner et afficher les résultats " "de la recherche" #: f.widgets.cc:365 msgid "Find images meeting select criteria" msgstr "Trouver les images répondant au critère sélectionné" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Indexer les nouveaux fichiers et créer les vignettes" #: f.widgets.cc:369 msgid "Change user preferences" msgstr "Changer le paramétrage utilisateur" #: f.widgets.cc:370 msgid "Change Keyboard Shortcut Keys" msgstr "Changer les touches des raccourcis clavier" #: f.widgets.cc:371 msgid "Show a brightness distribution graph" msgstr "Afficher un graphe de la distribution de la luminosité" #: f.widgets.cc:372 msgid "Show or revise grid lines" msgstr "Afficher ou paramétrer les lignes de la grille" #: f.widgets.cc:373 msgid "Change color of foreground lines" msgstr "Changer la couleur des lignes de premier plan" #: f.widgets.cc:374 msgid "Show RGB colors at mouse click" msgstr "Afficher les couleurs RVB au clic de la souris" #: f.widgets.cc:375 msgid "Magnify image around the mouse position" msgstr "Agrandir l'image autour de la position de la souris" #: f.widgets.cc:376 msgid "Highlight darkest and brightest pixels" msgstr "Mettre en surbrillance les pixels les plus sombres et les plus clairs" #: f.widgets.cc:377 msgid "Chart to adjust monitor color" msgstr "Charte pour ajuster la couleur du moniteur" #: f.widgets.cc:378 msgid "Chart to adjust monitor gamma" msgstr "Charte pour ajuster le gamma du moniteur" #: f.widgets.cc:379 msgid "Change the GUI language" msgstr "Changer la langue de l'interface graphique" #: f.widgets.cc:380 msgid "Report missing translations" msgstr "Lister les traductions manquantes" #: f.widgets.cc:381 msgid "Convert to another color profile" msgstr "Convertir vers un autre profil de couleur" #: f.widgets.cc:382 msgid "Calibrate printer colors" msgstr "Calibrer les couleurs de l'imprimante" #: f.widgets.cc:383 msgid "Completely uninstall AppImage package" msgstr "Désinstaller complètement le paquet AppImage" #: f.widgets.cc:384 msgid "Memory and CPU (to terminal/logfile)" msgstr "Mémoire et processeur (vers le terminal/un fichier log)" #: f.widgets.cc:387 msgid "Quick Start mini-guide" msgstr "Mini-guide de démarrage rapide" #: f.widgets.cc:388 msgid "Read the user guide" msgstr "Lire le guide utilisateur" #: f.widgets.cc:389 msgid "Recent user guide changes" msgstr "Changements récents dans le guide de l'utilisateur" #: f.widgets.cc:390 msgid "Technical installation notes" msgstr "Notes d'installation" #: f.widgets.cc:391 msgid "List updates by Fotoxx version" msgstr "Liste des changements par version de Fotoxx" #: f.widgets.cc:392 msgid "View the log file and error messages" msgstr "Visualiser le fichier log et les messages d'erreur" #: f.widgets.cc:393 msgid "How to do Fotoxx translations" msgstr "Comment traduire Fotoxx" #: f.widgets.cc:394 msgid "Show the Fotoxx web page" msgstr "Affiche la page web de Fotoxx" #: f.widgets.cc:395 msgid "Version, license, contact, credits" msgstr "Version, licence, contact, crédits" #: f.widgets.cc:398 msgid "list all directories, click any for gallery view" msgstr "" "lister tous les répertoires, cliquer sur l'un d'entre eux pour en afficher " "la vue en galerie" #: f.widgets.cc:399 msgid "Set gallery from current image file" msgstr "La galerie du fichier actuel devient la galerie actuelle" #: f.widgets.cc:400 msgid "Organize images into albums" msgstr "Organiser les images en albums" #: f.widgets.cc:401 msgid "Update album files to latest version" msgstr "Mise à jour des fichiers album vers la version la plus récente" #: f.widgets.cc:402 msgid "Replace album file with another file" msgstr "Remplacer le fichier album par un autre fichier" #: f.widgets.cc:403 msgid "Start a slide show" msgstr "Démarrer un diaporama" #: f.widgets.cc:425 msgid "New Window" msgstr "Nouvelle fenêtre" #: f.widgets.cc:426 f.widgets.cc:610 fotoxx-18.01.cc:1821 msgid "Sync Gallery" msgstr "Sync. galerie" #: f.widgets.cc:427 msgid "Recently Seen Images" msgstr "Images récemment visionnées" #: f.widgets.cc:428 msgid "Newest Images" msgstr "Images nouvelles" #: f.widgets.cc:433 msgid "View 360° Panorama" msgstr "Visualiser un panorama à 360°" #: f.widgets.cc:434 msgid "New Blank Image" msgstr "Nouvelle image vide" #: f.widgets.cc:437 f.widgets.cc:758 msgid "Copy/Move to Location" msgstr "Copier/Transférer vers un emplacement donné" #: f.widgets.cc:438 f.widgets.cc:759 msgid "Copy to Desktop" msgstr "Copier vers le Bureau" #: f.widgets.cc:439 f.widgets.cc:760 msgid "Copy to Clipboard" msgstr "Copier vers le presse-papiers" #: f.widgets.cc:440 f.widgets.cc:764 msgid "Copy to Image Cache" msgstr "Copier vers le cache d'image" #: f.widgets.cc:441 f.widgets.cc:778 msgid "Show on Net Map" msgstr "Afficher sur une carte Internet" #: f.widgets.cc:443 msgid "Print Image" msgstr "Imprimer l'image" #: f.widgets.cc:444 msgid "Print Calibrated Image" msgstr "Imprimer l'image avec des couleurs calibrées" #: f.widgets.cc:448 msgid "View Metadata (short)" msgstr "Visualiser les métadonnées (version courte)" #: f.widgets.cc:449 msgid "View Metadata (long)" msgstr "Visualiser les métadonnées (version longue)" #: f.widgets.cc:453 msgid "Show Captions on Image" msgstr "Montrer les légendes de l'image" #: f.widgets.cc:456 f.widgets.cc:776 msgid "Select Area" msgstr "Sélectionner une zone" #: f.widgets.cc:457 msgid "Find Area Gap" msgstr "Trouver une discontinuité dans la zone" #: f.widgets.cc:459 msgid "Show Area" msgstr "Montrer la zone" #: f.widgets.cc:460 msgid "Hide Area" msgstr "Cacher la zone" #: f.widgets.cc:461 msgid "Enable Area" msgstr "Activer la zone" #: f.widgets.cc:462 msgid "Disable Area" msgstr "Désactiver la zone" #: f.widgets.cc:463 msgid "Invert Area" msgstr "Inverser la zone" #: f.widgets.cc:464 msgid "Unselect Area" msgstr "Désélectionner la zone" #: f.widgets.cc:466 msgid "Paste Area" msgstr "Coller la zone" #: f.widgets.cc:467 msgid "Open Area File" msgstr "Ouvrir un fichier de zone" #: f.widgets.cc:474 msgid "Voodoo 1" msgstr "Vaudou 1" #: f.widgets.cc:475 msgid "Voodoo 2" msgstr "Vaudou 2" #: f.widgets.cc:477 f.widgets.cc:773 msgid "Edit Brightness" msgstr "Modifier la luminosité" #: f.widgets.cc:478 f.widgets.cc:775 msgid "Gradients" msgstr "Gradients" #: f.widgets.cc:482 msgid "Paint Image" msgstr "Peindre l'image" #: f.widgets.cc:489 msgid "Plugins" msgstr "Greffons" #: f.widgets.cc:494 msgid "Denoise" msgstr "Réduction du bruit" #: f.widgets.cc:495 msgid "Red Eyes" msgstr "Yeux rouges" #: f.widgets.cc:497 msgid "Color Depth" msgstr "Profondeur de couleur" #: f.widgets.cc:500 msgid "Adjust RGB/CMY" msgstr "Ajuster RVB/CMJ" #: f.widgets.cc:501 msgid "Adjust HSL" msgstr "Ajuster TSV" #: f.widgets.cc:502 msgid "Zonal Colors" msgstr "Couleurs zonales" #: f.widgets.cc:503 msgid "Match Colors" msgstr "Faire se correspondre des couleurs" #: f.widgets.cc:509 msgid "Anti-Alias" msgstr "Anti-alias" #: f.widgets.cc:516 msgid "Fix Perspective" msgstr "Corriger la perspective" #: f.widgets.cc:526 msgid "Twist Image" msgstr "Tordre l'image" #: f.widgets.cc:529 msgid "Sketch" msgstr "Croquis" #: f.widgets.cc:530 msgid "Cartoon" msgstr "Cartoon" #: f.widgets.cc:534 msgid "Embossing" msgstr "Gaufrage" #: f.widgets.cc:536 msgid "Dots" msgstr "Points" #: f.widgets.cc:537 msgid "Painting" msgstr "Peinture" #: f.widgets.cc:539 msgid "Texture" msgstr "Texture" #: f.widgets.cc:541 msgid "Mosaic" msgstr "Mosaïque" #: f.widgets.cc:548 msgid "High Dynamic Range" msgstr "High Dynamic Range (HDR)" #: f.widgets.cc:549 msgid "High Depth of Field" msgstr "High Depth of Field (HDF)" #: f.widgets.cc:550 msgid "Stack/Paint" msgstr "Empilement/peinture" #: f.widgets.cc:551 msgid "Stack/Noise" msgstr "Empilement/débruitage" #: f.widgets.cc:552 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:553 msgid "Vertical Panorama" msgstr "Panorama vertical" #: f.widgets.cc:554 msgid "PT Panorama" msgstr "PT Panorama" #: f.widgets.cc:555 msgid "Montage" msgstr "Montage" #: f.widgets.cc:556 msgid "Mashup" msgstr "Agencement d'objets graphiques" #: f.widgets.cc:562 msgid "Batch RAW" msgstr "Traitement RAW par lot" #: f.widgets.cc:563 msgid "Script Files" msgstr "Fichiers script" #: f.widgets.cc:566 msgid "Export File List" msgstr "Exporter une liste de fichiers" #: f.widgets.cc:574 msgid "Image Locations/Dates" msgstr "Localisations/dates des images" #: f.widgets.cc:575 msgid "Image Timeline" msgstr "Calendrier des images" #: f.widgets.cc:576 msgid "Search Images" msgstr "Rechercher des images" #: f.widgets.cc:582 msgid "Brightness Graph" msgstr "Graphe de la luminosité" #: f.widgets.cc:587 msgid "Dark/Bright Pixels" msgstr "Pixels sombres/brillants" #: f.widgets.cc:588 msgid "Monitor Color" msgstr "Couleur du moniteur" #: f.widgets.cc:590 msgid "Change Language" msgstr "Changer la langue" #: f.widgets.cc:591 msgid "Missing Translations" msgstr "Traductions manquantes" #: f.widgets.cc:592 msgid "Color Profile" msgstr "Profil de couleur" #: f.widgets.cc:594 msgid "Uninstall AppImage" msgstr "Désinstaller AppImage" #: f.widgets.cc:611 msgid "All Directories" msgstr "Tous les répertoires" #: f.widgets.cc:612 msgid "Bookmarks" msgstr "Signets" #: f.widgets.cc:621 f.widgets.cc:646 f.widgets.cc:670 f.widgets.cc:683 msgid "Gallery" msgstr "Galerie" #: f.widgets.cc:622 f.widgets.cc:647 f.widgets.cc:671 f.widgets.cc:684 msgid "World Maps" msgstr "Cartes du monde" #: f.widgets.cc:623 f.widgets.cc:648 f.widgets.cc:672 f.widgets.cc:685 msgid "Net Maps" msgstr "Cartes Internet" #: f.widgets.cc:625 f.widgets.cc:650 f.widgets.cc:1066 msgid "Favorites" msgstr "Favoris" #: f.widgets.cc:627 msgid "Save File" msgstr "Enregistrer le fichier" #: f.widgets.cc:628 msgid "Prev/Next" msgstr "Préc./Suiv." #: f.widgets.cc:630 msgid "Areas" msgstr "Zones" #: f.widgets.cc:631 msgid "Undo/Redo" msgstr "Défaire/Refaire" #: f.widgets.cc:632 fotoxx.h:1208 msgid "Edit" msgstr "Édition" #: f.widgets.cc:633 msgid "Repair" msgstr "Réparer" #: f.widgets.cc:635 msgid "Effects" msgstr "Effets" #: f.widgets.cc:636 f.widgets.cc:660 msgid "Combine" msgstr "Combiner" #: f.widgets.cc:637 f.widgets.cc:661 msgid "Process" msgstr "Traitements" #: f.widgets.cc:638 f.widgets.cc:662 msgid "Tools" msgstr "Outils" #: f.widgets.cc:674 msgid "Choose Map" msgstr "Choisir une carte" #: f.widgets.cc:675 f.widgets.cc:688 msgid "Map Markers" msgstr "Marqueurs sur les cartes" #: f.widgets.cc:687 msgid "Map Source" msgstr "Source des cartes" #: f.widgets.cc:689 msgid "Map Locations" msgstr "Emplacement des cartes" #: f.widgets.cc:753 msgid "Popup Image" msgstr "Image contextuelle" #: f.widgets.cc:757 fotoxx.h:1265 msgid "Rename" msgstr "Renommer" #: f.widgets.cc:761 msgid "Remove from Album" msgstr "Enlever de l'album" #: f.widgets.cc:763 msgid "Cut to Image Cache" msgstr "Couper vers le cache d'image" #: f.widgets.cc:765 msgid "Paste Image Cache Here (clear)" msgstr "Coller le cache d'image ici (enlever)" #: f.widgets.cc:766 msgid "Paste Image Cache Here (keep)" msgstr "Coller le cache d'image ici (conserver)" #: f.widgets.cc:779 msgid "Delete/Trash ..." msgstr "Supprimer/Corbeille ..." #: f.widgets.cc:1092 msgid "menu name is invalid" msgstr "nom de menu non valide" #: fotoxx-18.01.cc:434 msgid "Please install missing programs:" msgstr "Veuillez installer les applications manquantes:" #: fotoxx-18.01.cc:706 #, c-format msgid "Kill active dialog? %s" msgstr "Tuer le dialogue actif? %s" #: fotoxx-18.01.cc:801 msgid "(reduced)" msgstr "(réduit)" #: fotoxx-18.01.cc:802 msgid "area active" msgstr "zone active" #: fotoxx-18.01.cc:803 msgid "dialog open" msgstr "dialogue ouvert" #: fotoxx-18.01.cc:804 msgid "blocked" msgstr "bloqué" #: fotoxx-18.01.cc:866 msgid "edits" msgstr "opérations d'édition" #: fotoxx-18.01.cc:1801 msgid "File View" msgstr "Vue par fichier image" #: fotoxx-18.01.cc:1806 msgid "Gallery View" msgstr "Vue en galerie" #: fotoxx-18.01.cc:1811 msgid "World Map View" msgstr "Vue Carte du Monde" #: fotoxx-18.01.cc:1816 msgid "Net Map View" msgstr "Vue Carte Internet" #: fotoxx-18.01.cc:1831 msgid "Show Hidden" msgstr "Montrer les fichiers cachés" #: fotoxx-18.01.cc:3023 msgid "Exceed 50 anchor points" msgstr "Excède 50 points d'ancrage" #: fotoxx-18.01.cc:3250 msgid "load curve from a file" msgstr "charger une courbe depuis un fichier" #: fotoxx-18.01.cc:3316 msgid "curve file is invalid" msgstr "fichier de courbe non valide" #: fotoxx-18.01.cc:3330 msgid "save curve to a file" msgstr "enregistrer une courbe dans un fichier" #: fotoxx-18.01.cc:3399 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "Le fichier ne peut pas être modifié \n" " %s" #: fotoxx-18.01.cc:3412 msgid "Too many edits, please save image" msgstr "Trop d'opérations d'édition, veuillez enregistrer l'image" #: fotoxx-18.01.cc:3417 msgid "this function cannot be scripted" msgstr "cette fonction ne peut pas être incluse dans un script" #: fotoxx-18.01.cc:3434 msgid "" "Select area not active.\n" "Continue?" msgstr "" "La zone sélectionnée n'est pas active.\n" "Continuer?" #: fotoxx-18.01.cc:3749 msgid "file data does not fit dialog" msgstr "les données du fichier n'entrent pas dans le dialogue" #: fotoxx-18.01.cc:3759 msgid "Save settings to a file" msgstr "Enregistrer les paramètres dans un fichier" #: fotoxx-18.01.cc:4140 msgid "This action will discard changes to current image" msgstr "Les modifications faites à l'image courante seront perdues" #: fotoxx-18.01.cc:4141 msgid "prior function still active" msgstr "la fonction précédente est toujours active" #: fotoxx-18.01.cc:4142 fotoxx.h:1229 msgid "Keep" msgstr "Conserver" #: fotoxx-18.01.cc:4143 msgid "Discard" msgstr "Abandonner" #: fotoxx.h:1177 msgid "Add" msgstr "Ajouter" #: fotoxx.h:1178 msgid "Add All" msgstr "Ajouter tout" #: fotoxx.h:1180 msgid "Amount" msgstr "Quantité" #: fotoxx.h:1181 msgid "Angle" msgstr "Angle" #: fotoxx.h:1182 zfuncs.cc:6782 msgid "Apply" msgstr "Appliquer" #: fotoxx.h:1183 msgid "Auto" msgstr "Auto" #: fotoxx.h:1184 msgid "Black" msgstr "Noir" #: fotoxx.h:1185 msgid "Blend Width" msgstr "Blend Width" #: fotoxx.h:1187 zfuncs.cc:11368 msgid "Bottom" msgstr "Bas" #: fotoxx.h:1189 zfuncs.cc:6796 msgid "Browse" msgstr "Parcourir" #: fotoxx.h:1190 zfuncs.cc:6782 msgid "Cancel" msgstr "Annuler" #: fotoxx.h:1191 msgid "Center" msgstr "Centrer" #: fotoxx.h:1192 msgid "Choose" msgstr "Choisir" #: fotoxx.h:1193 msgid "Clear" msgstr "Effacement" #: fotoxx.h:1194 msgid "Close" msgstr "Fermer" #: fotoxx.h:1195 msgid "Color" msgstr "Couleur" #: fotoxx.h:1196 msgid "COMPLETED" msgstr "TERMINÉ" #: fotoxx.h:1197 msgid "continue" msgstr "continuer" #: fotoxx.h:1199 msgid "Copy" msgstr "Copier" #: fotoxx.h:1200 msgid "Create" msgstr "Créer" #: fotoxx.h:1201 msgid "Curve File:" msgstr "Fichier de courbe:" #: fotoxx.h:1202 msgid "Cut" msgstr "Couper" #: fotoxx.h:1203 msgid "Deband" msgstr "Détendre" #: fotoxx.h:1204 zfuncs.cc:6782 msgid "Delete" msgstr "Supprimer" #: fotoxx.h:1205 msgid "Disable" msgstr "Désactiver" #: fotoxx.h:1206 msgid "Done" msgstr "Terminé" #: fotoxx.h:1207 msgid "edge" msgstr "bord" #: fotoxx.h:1209 msgid "Enable" msgstr "Activer" #: fotoxx.h:1210 msgid "Erase" msgstr "Effacer" #: fotoxx.h:1211 msgid "Fetch" msgstr "Aller chercher" #: fotoxx.h:1212 msgid "output file already exists" msgstr "le fichier de sortie existe déjà" #: fotoxx.h:1213 msgid "file not found" msgstr "fichier non trouvé" #: fotoxx.h:1214 #, c-format msgid "file not found: %s" msgstr "fichier non trouvé: %s" #: fotoxx.h:1215 #, c-format msgid "%d image files selected" msgstr "%d fichiers image sélectionnés" #: fotoxx.h:1216 msgid "Find" msgstr "Trouver" #: fotoxx.h:1217 msgid "Finish" msgstr "Finir" #: fotoxx.h:1219 msgid "Font" msgstr "Police" #: fotoxx.h:1221 msgid "Grid" msgstr "Grille" #: fotoxx.h:1222 zfuncs.cc:11398 msgid "Height" msgstr "Hauteur" #: fotoxx.h:1224 msgid "Hide" msgstr "Masquer" #: fotoxx.h:1225 zfuncs.cc:11390 msgid "Image" msgstr "Image" #: fotoxx.h:1226 msgid "Images" msgstr "Images" #: fotoxx.h:1227 msgid "Insert" msgstr "Insérer" #: fotoxx.h:1228 msgid "Invert" msgstr "Inverser" #: fotoxx.h:1230 zfuncs.cc:11372 msgid "Left" msgstr "Gauche" #: fotoxx.h:1231 msgid "limit" msgstr "limite" #: fotoxx.h:1232 msgid "Load" msgstr "Charger" #: fotoxx.h:1233 msgid "Make" msgstr "Fabricant" #: fotoxx.h:1235 msgid "Map" msgstr "Carte" #: fotoxx.h:1236 msgid "Match Level:" msgstr "Niveau de correspondance:" #: fotoxx.h:1237 msgid "Max" msgstr "Max" #: fotoxx.h:1238 msgid "mouse radius" msgstr "rayon de la souris" #: fotoxx.h:1239 msgid "Negative" msgstr "Negatif" #: fotoxx.h:1240 msgid "New" msgstr "Nouveau" #: fotoxx.h:1241 msgid "Next" msgstr "Prochain" #: fotoxx.h:1242 zfuncs.cc:10365 msgid "No" msgstr "Non" #: fotoxx.h:1243 msgid "no images" msgstr "pas d'images" #: fotoxx.h:1244 msgid "" "image index disabled \n" " Enable?" msgstr "" "l'indexation des images est désactivée \n" " Activer?" #: fotoxx.h:1245 msgid "no image files selected" msgstr "aucun fichier image sélectionné" #: fotoxx.h:1246 msgid "None" msgstr "Aucune" #: fotoxx.h:1247 msgid "no selection" msgstr "pas de sélection" #: fotoxx.h:1248 msgid "OK" msgstr "OK" #: fotoxx.h:1249 msgid "image index not updated" msgstr "l'index des images n'a pas été mis à jour" #: fotoxx.h:1251 msgid "Paint Radius" msgstr "Rayon de la peinture" #: fotoxx.h:1252 msgid "Paste" msgstr "Coller" #: fotoxx.h:1253 msgid "Pause" msgstr "Pause" #: fotoxx.h:1254 msgid "Percent" msgstr "Pourcent" #: fotoxx.h:1256 msgid "Presets" msgstr "Pré-réglages" #: fotoxx.h:1257 msgid "Prev" msgstr "Précédent" #: fotoxx.h:1258 msgid "Proceed" msgstr "Poursuivre" #: fotoxx.h:1260 msgid "range" msgstr "plage" #: fotoxx.h:1262 msgid "Redo" msgstr "Refaire" #: fotoxx.h:1263 msgid "Reduce" msgstr "Réduire" #: fotoxx.h:1264 msgid "Remove" msgstr "Enlever" #: fotoxx.h:1266 msgid "Replace" msgstr "Remplacer" #: fotoxx.h:1267 msgid "Reserved" msgstr "Réservé" #: fotoxx.h:1268 msgid "Reset" msgstr "Réinitialiser" #: fotoxx.h:1269 zfuncs.cc:11376 msgid "Right" msgstr "Droit" #: fotoxx.h:1270 msgid "Rotate" msgstr "Rotation" #: fotoxx.h:1271 msgid "Run" msgstr "Lancer" #: fotoxx.h:1272 msgid "Save" msgstr "Enregistrer" #: fotoxx.h:1273 msgid "Search" msgstr "Rechercher" #: fotoxx.h:1274 msgid "Seconds" msgstr "Secondes" #: fotoxx.h:1275 msgid "Select" msgstr "Sélectionner" #: fotoxx.h:1277 msgid "Show" msgstr "Afficher" #: fotoxx.h:1278 msgid "Size" msgstr "Taille" #: fotoxx.h:1279 msgid "Start" msgstr "Départ" #: fotoxx.h:1280 msgid "Stop" msgstr "Arrêt" #: fotoxx.h:1281 msgid "Strength" msgstr "Force" #: fotoxx.h:1282 msgid "Threshold" msgstr "Seuil" #: fotoxx.h:1283 #, c-format msgid "exceed %d files" msgstr "excède %d fichiers" #: fotoxx.h:1284 zfuncs.cc:11364 msgid "Top" msgstr "Sommet" #: fotoxx.h:1286 msgid "Trash" msgstr "Corbeille" #: fotoxx.h:1287 msgid "Trim" msgstr "Rogner" #: fotoxx.h:1288 msgid "Undo All" msgstr "Défaire tout" #: fotoxx.h:1289 msgid "Undo Last" msgstr "Défaire le dernier" #: fotoxx.h:1290 msgid "Undo" msgstr "Défaire" #: fotoxx.h:1291 msgid "Unfinish" msgstr "Annuler le bouclage" #: fotoxx.h:1292 msgid "Unselect" msgstr "Désélectionner" #: fotoxx.h:1293 msgid "View" msgstr "Vue" #: fotoxx.h:1294 msgid "Web" msgstr "Web" #: fotoxx.h:1295 msgid "White" msgstr "Blanc" #: fotoxx.h:1296 zfuncs.cc:11394 msgid "Width" msgstr "Largeur" #: fotoxx.h:1297 msgid "x-offset" msgstr "décalage x" #: fotoxx.h:1298 msgid "y-offset" msgstr "décalage y" #: fotoxx.h:1299 zfuncs.cc:10365 msgid "Yes" msgstr "Oui" #: zfuncs.cc:305 msgid "LOW MEMORY - performance may be poor" msgstr "MEMOIRE BASSE - les performances seront mauvaises" #: zfuncs.cc:1722 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "créer le répertoire? \n" " %s" #: zfuncs.cc:4878 msgid "user guide not found" msgstr "guide de l'utilisateur non trouvé" #: zfuncs.cc:5742 #, c-format msgid "cannot open file %s" msgstr "impossible d'ouvrir le fichier %s" #: zfuncs.cc:5765 msgid "save text to file" msgstr "enregistrer le texte dans un fichier" #: zfuncs.cc:6786 msgid "menu text" msgstr "texte du menu" #: zfuncs.cc:6787 msgid "menu func" msgstr "fonction de menu" #: zfuncs.cc:6788 msgid "menu icon" msgstr "icône du menu" #: zfuncs.cc:6789 msgid "icon size" msgstr "taille de l'icône" #: zfuncs.cc:6792 msgid "Bold" msgstr "Gras" #: zfuncs.cc:6799 msgid "close window" msgstr "fermer la fenêtre" #: zfuncs.cc:10488 zfuncs.cc:11021 zfuncs.cc:11351 msgid "cancel" msgstr "annuler" #: zfuncs.cc:10982 msgid "choose file" msgstr "choisir un fichier" #: zfuncs.cc:10987 msgid "choose files" msgstr "choisir les fichiers" #: zfuncs.cc:10992 msgid "save" msgstr "enregistrer" #: zfuncs.cc:10998 msgid "choose folder" msgstr "choisir un répertoire" #: zfuncs.cc:11003 msgid "choose folders" msgstr "choisir les répertoires" #: zfuncs.cc:11008 msgid "create folder" msgstr "créer un répertoire" #: zfuncs.cc:11015 msgid "hidden" msgstr "caché" #: zfuncs.cc:11351 msgid "done" msgstr "terminé" #: zfuncs.cc:11381 msgid "image scale" msgstr "échelle de l'image" #: zfuncs.cc:11383 msgid "percent" msgstr "pourcent" #~ msgid "Select images to remove" #~ msgstr "Sélectionner les images à enlever" #~ msgid "remove %d album files?" #~ msgstr "enlever %d fichiers de l'album?" #~ msgid "KB functions" #~ msgstr "Liste des raccourcis clavier" #~ msgid "both" #~ msgstr "les deux" #~ msgid "Click for white balance or black level" #~ msgstr "Cliquer pour la balance des blancs ou le niveau de noir" #~ msgid "" #~ "check the box, then click on white or dark \n" #~ "area to calibrate image white or black color" #~ msgstr "" #~ "Sélectionner la case à cocher, puis cliquer sur une zone blanche \n" #~ "ou sombre pour calibrer le blanc de l'image ou son noir" #~ msgid "Zonal Flatten Brightness" #~ msgstr "Aplatissement zonal de la luminosité" #~ msgid "Tone Mapping" #~ msgstr "Mappage de tons" #~ msgid "Flip" #~ msgstr "Retournement" #~ msgid "Write Text on Image" #~ msgstr "Écrire un texte sur une image" #~ msgid "Write Line or Arrow on Image" #~ msgstr "Tracer une ligne ou une flèche sur l'image" #~ msgid "Row↑" #~ msgstr "Rangée↑" #~ msgid "Row↓" #~ msgstr "Rangée↓" #~ msgid "Page↑" #~ msgstr "Page↑" #~ msgid "Page↓" #~ msgstr "Page↓" #~ msgid "a sorted album cannot be edited" #~ msgstr "un album trié ne peut pas être édité" #~ msgid "Click list position. Click thumbnail to add." #~ msgstr "" #~ "Cliquer à la position dans la liste. Clic sur la vignette pour ajouter." #~ msgid "(yyyymmdd)" #~ msgstr "(aaaammjj)" #~ msgid "Batch Convert RAW Files" #~ msgstr "Conversion par lot de fichiers RAW" #~ msgid "Images:" #~ msgstr "Images:" #~ msgid "searching ..." #~ msgstr "recherche en cours ..." #~ msgid "" #~ "Draw a line across the image in \n" #~ "direction of brightness gradient." #~ msgstr "" #~ "Dessiner une ligne au travers de l'image dans \n" #~ "la direction du gradient de luminosité." #~ msgid "Brightness Gradient" #~ msgstr "Gradient de luminosité" #~ msgid "Select directory for thumbnails." #~ msgstr "Sélectionner le répertoire où iront les vignettes." #~ msgid "no thumbnails directory defined" #~ msgstr "aucun répertoire pour les vignettes n'a été défini" #~ msgid "Dialog font and size" #~ msgstr "Police et corps des dialogues" #~ msgid "Open the previously seen file" #~ msgstr "Ouvrir le dernier fichier visualisé" #~ msgid "Add local contrast, enhance details" #~ msgstr "Ajouter du contraste local, rehausser les détails" #~ msgid "Flatten zonal brightness distribution" #~ msgstr "Aplatir la distribution zonale de la luminosité" #~ msgid "Write text on image" #~ msgstr "Écrire du texte sur une image" #~ msgid "Write lines or arrows on image" #~ msgstr "Tracer des lignes ou des flèches sur une image" #~ msgid "Add a brightness/color gradient across the image" #~ msgstr "Ajouter un gradient de luminosité/couleur au travers de l'image" #~ msgid "Convert camera RAW files using libraw" #~ msgstr "Convertir des fichiers RAW d'un appareil-photo en utilisant LibRaw" #~ msgid "Convert camera RAW files using Raw Therapee" #~ msgstr "" #~ "Convertir des fichiers RAW d'un appareil-photo en utilisant RawTherapee" #~ msgid "Open Previous File" #~ msgstr "Ouvrir le fichier précédent" #~ msgid "Find Gap" #~ msgstr "Trouver une discontinuité" #~ msgid "Zonal Flatten" #~ msgstr "Aplatissement zonal" #~ msgid "Add Lines/Arrows" #~ msgstr "Ajout de lignes/de flèches" #~ msgid "Batch Raw Therapee" #~ msgstr "Traitement RAW par lot avec RawTherapee" #~ msgid "Resources" #~ msgstr "Ressources" #~ msgid "Kill active dialog?" #~ msgstr "Tuer le dialogue actif?" #~ msgid "save screen to file" #~ msgstr "enregistrer l'écran dans un fichier" #~ msgid "" #~ "shift + left click: pick image position to copy \n" #~ "left click or drag: copy image to mouse position \n" #~ "right click or drag: restore image" #~ msgstr "" #~ "maj + clic gauche: choisir la région de l'image à copier \n" #~ "Clic gauche ou tirer: copier l'image à la position de la souris \n" #~ "clic droit ou tirer: restaurer l'image" #~ msgid "Scroll" #~ msgstr "Défilement" #~ msgid "Choose image directory" #~ msgstr "Choisir le répertoire de l'image" #~ msgid "Choose album" #~ msgstr "Choisir un album" #~ msgid "Click on a monotone image area." #~ msgstr "Cliquer sur une zone uniforme de l'image." #~ msgid "not a defined tag: %s" #~ msgstr "étiquette indéfinie: %s" #~ msgid "open new script file" #~ msgstr "ouvrir un nouveau fichier de script" #~ msgid "script file is not closed" #~ msgstr "le fichier de script n'est pas fermé" #~ msgid "select script file to run" #~ msgstr "sélectionner le fichier de script à lancer" #~ msgid "unknown script file" #~ msgstr "fichier de script inconnu" #~ msgid "select image files to be processed" #~ msgstr "sélectionner les fichiers image à traiter" #~ msgid "" #~ "shift + left click: pick color or image position \n" #~ "left click or drag: paint color or copy image \n" #~ "right click or drag: remove color or image" #~ msgstr "" #~ "maj+clic gauche: choisir une couleur ou une position dans l'image \n" #~ "clic gauche ou tirer: paint color or copy image \n" #~ "clic droit ou tirer: remove color or image" #~ msgid "Paint/Clone" #~ msgstr "Peindre/Cloner" #~ msgid "copy from image" #~ msgstr "copier depuis l'image" #~ msgid "transparency center" #~ msgstr "centre de la transparence" #~ msgid "transparency edge" #~ msgstr "bord de la transparence" #~ msgid "Directory" #~ msgstr "Répertoire" #~ msgid "Paint or clone image pixels using the mouse" #~ msgstr "Peindre ou cloner des pixels de l'image en utilisant la souris" #~ msgid "Set desktop wallpaper from current Fotoxx image" #~ msgstr "Changer le papier peint du bureau à partir de l'image courante" #~ msgid "Cycle desktop wallpaper from a Fotoxx album" #~ msgstr "" #~ "Afficher et changer le papier peint du bureau à partir d'un album de " #~ "Fotoxx" #~ msgid "Set Desktop Wallpaper" #~ msgstr "Choisir un papier peint pour le bureau" #~ msgid "Cycle Desktop Wallpaper" #~ msgstr "Changer le papier peint du bureau" #~ msgid "%d files selected" #~ msgstr "%d fichiers sélectionnés" #~ msgid "Save File Version" #~ msgstr "Enregistrer une version d'un fichier" #~ msgid "slow scroll" #~ msgstr "défilement lent" #~ msgid "Area Color" #~ msgstr "Couleur de la zone" #~ msgid "Match Level" #~ msgstr "Niveau de correspondance" #~ msgid "Upload image files to Flickr web service" #~ msgstr "Exporter (téléverser) des fichiers image vers le service web Flickr" #~ msgid "Summary of image edit functions" #~ msgstr "Résumé des fonctions d'édition d'images" #~ msgid "Set map markers from current gallery" #~ msgstr "Positionner les repères sur la carte depuis la galerie courante" #~ msgid "Set all images or current gallery" #~ msgstr "Choisir toutes les images ou seulement la galerie courante" #~ msgid "go to bookmarked image" #~ msgstr "aller à l'image repérée par un signet" #~ msgid "Sync Gallery, Export, Flickr, Albums, Slide Show" #~ msgstr "Sync galerie, Export, Flickr, Albums, Diaporama" #~ msgid "Process: batch convert, batch metadata, image search ..." #~ msgstr "" #~ "Traitement: conversion par lot, métadonnées par lot, recherche " #~ "d'images ..." #~ msgid "Flatten Book Page Photo" #~ msgstr "Aplatir la photo d'une page de livre" #~ msgid "" #~ "appimage file moved to %s \n" #~ "desktop file created at %s \n" #~ "\n" #~ msgstr "" #~ "le fichier appimage a été déplacé vers %s \n" #~ "le fichier desktop a été créé dans %s \n" #~ "\n" #~ msgid "Select Album" #~ msgstr "Sélectionner un album" #~ msgid "Delete present thumbnails to make effective" #~ msgstr "Supprimer les vignettes actuelles pour valider la modification" #~ msgid "Zooms for 2x" #~ msgstr "Zooms pour 2x" #~ msgid "Image Pan/scroll:" #~ msgstr "Défilement de l'image:" #~ msgid "Directory Gallery" #~ msgstr "Galerie arbitraire" #~ msgid "Previous Image" #~ msgstr "Image précédente" #~ msgid "Select File" #~ msgstr "Sélectionner un fichier de palette" #~ msgid "Batch Change Metadata" #~ msgstr "Modification de métadonnées par lot" #~ msgid "Geocoding service by MapQuest" #~ msgstr "Service de geocoding par MapQuest" #~ msgid "choose layout file" #~ msgstr "choisir le fichier de mise en page" #~ msgid "preference" #~ msgstr "préférence" #~ msgid "slowdown" #~ msgstr "ralentissement" #~ msgid "enabled" #~ msgstr "activé" #~ msgid "select random (if 2+ enabled)" #~ msgstr "sélectionner aléatoirement (si 2 ou + activés)" #~ msgid "Upload to Flickr" #~ msgstr "Exporter vers Flickr" #~ msgid "reverse upload sequence" #~ msgstr "inverser la séquence d'exportation" #~ msgid "%d files uploaded" #~ msgstr "%d fichiers téléversés" #~ msgid "Edit Functions Summary" #~ msgstr "Résumé des fonctions d'édition" #~ msgid "Delete or Trash Image File" #~ msgstr "Supprimer ou mettre à la corbeille un fichier image" #~ msgid "Open RAW file (Raw Therapee)" #~ msgstr "Ouvrir un fichier RAW (RawTherapee)" #~ msgid "Divisor" #~ msgstr "Diviseur" #~ msgid "choose pattern tile" #~ msgstr "choisir l'élément de base du motif" #~ msgid "curved image" #~ msgstr "image courbée" #~ msgid "load area from a file" #~ msgstr "charger une zone depuis un fichier" #~ msgid "" #~ "Click on colors to select and deselect. \n" #~ "Set threshold levels for color matching. \n" #~ "Left drag to select or deselect pixels. \n" #~ "Right drag to restore original pixels. \n" #~ "When done, copy or save the finished area \n" #~ "for subsequent pasting into an image. \n" #~ msgstr "" #~ "Cliquer sur les couleurs de l'image à sélectionner et à désélectionner. \n" #~ "Paramétrer le seuil de coïncidence des couleurs. \n" #~ "Cliquer-tirer de la touche gauche pour sélectionner ou désélectionner des " #~ "pixels. \n" #~ "Cliquer-tirer de la touche droite pour restaurer les pixels d'origine. \n" #~ "Une fois le processus terminé, copier ou enregistrer la zone finie \n" #~ "pour collage ultérieur dans une image. \n" #~ msgid "Select Hairy Edge" #~ msgstr "Sélectionner un bord plumeux" #~ msgid "Geocoding by MapQuest" #~ msgstr "Géocodage par MapQuest" #~ msgid "END (press Escape to exit)" #~ msgstr "FIN (presser Échap pour sortir)" #~ msgid "" #~ "Metadata Search report cannot be re-sorted. \n" #~ "Check User Guide for a way to do this." #~ msgstr "" #~ "Les résultats de la recherche sur les métadonnées ne peuvent pas être re-" #~ "triés. \n" #~ "Voir le Guide de l'utilisateur pour une manière de faire ceci." #~ msgid "Japanese-fan" #~ msgstr "éventail japonais" #~ msgid "Show Resources" #~ msgstr "Afficher les ressources" #~ msgid "Retinex" #~ msgstr "Retinex" #~ msgid "Open with EOG" #~ msgstr "Ouvrir avec EOG" #~ msgid "Convert to pencil sketch" #~ msgstr "Convertir en un croquis au crayon" #~ msgid "View an image file with Gnome EOG" #~ msgstr "Visionner un fichier image avec Gnome EOG" #~ msgid "Select images to add" #~ msgstr "Sélectionner les images à ajouter" fotoxx-18.01.1/locales/translate-es.po0000644000175000017500000041774313222767271016266 0ustar micomico# Traducción al castellano para el paquete Fotoxx. # Copyright (C) 2018 THE home's COPYRIGHT HOLDER # Este archivo está distribuido bajo la misma licencia que el paquete principal. # Miguel Anxo Bouzada , 2010-2012 # Josep Antoni Miralles Puignau 2012-2017 # msgid "" msgstr "" "Project-Id-Version: home 2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-12-26 22:25+0100\n" "PO-Revision-Date: 2017-01-20 19:42+0200\n" "Last-Translator: Josep Antoni Miralles Puignau \n" "Language-Team: nLanguage: es\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: f.albums.cc:89 msgid "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." msgstr "" "Clic derecho en miniatura dl álbum para cortar/copiar \n" "a la caché, insertar desde la caché, o eliminar" #: f.albums.cc:91 msgid "" "Right-click left/right side of album \n" "thumbnail to insert cached images \n" "before/after the thumbnail." msgstr "" "Clic derecho en el lado izq/der del álbum \n" "de miniaturas para insertar imágenes del caché \n" "antes/después de la miniatura." #: f.albums.cc:94 msgid "Drag album thumbnail to new position." msgstr "Arrastrar miniatura del álbum a la nueva posición" #: f.albums.cc:128 f.widgets.cc:613 msgid "Manage Albums" msgstr "Gestionar álbum" #: f.albums.cc:140 f.albums.cc:275 msgid "Create or replace an album" msgstr "Crear o reemplazar un álbum" #: f.albums.cc:142 msgid "Album to view or edit" msgstr "Álbum para ver o editar" #: f.albums.cc:144 msgid "Select images, add to cache" msgstr "Seleccionar imágenes para añadir al caché" #: f.albums.cc:146 msgid "Clear image cache" msgstr "Limpiar caché de imagen" #: f.albums.cc:148 f.albums.cc:170 msgid "Delete an album" msgstr "Borrar un álbum" #: f.albums.cc:169 msgid "Choose Album" msgstr "Elegir álbum" #: f.albums.cc:171 #, c-format msgid "Image cache has %d images" msgstr "Caché de imágenes tiene %d imágenes" #: f.albums.cc:172 msgid "cache added to empty album" msgstr "caché añadida a álbum vacío" #: f.albums.cc:228 #, c-format msgid "delete %s ?" msgstr "¿borrar %s ?" #: f.albums.cc:259 #, c-format msgid "fill from image cache (%d images)" msgstr "llenar desde caché (%d imágenes)" #: f.albums.cc:277 f.albums.cc:310 msgid "Album Name" msgstr "Nombre del álbum" #: f.albums.cc:283 msgid "make an initially empty album" msgstr "crear un álbum vacío" #: f.albums.cc:285 msgid "fill from current gallery" msgstr "llenar desde la galería actual" #: f.albums.cc:324 msgid "enter an album name" msgstr "introducir un nombre de álbum" #: f.albums.cc:352 msgid "new album created" msgstr "nuevo álbum creado" #: f.albums.cc:368 msgid "gallery is empty" msgstr "la galería está vacía" #: f.albums.cc:876 f.widgets.cc:614 msgid "Update Album Files" msgstr "Actualizar álbumes" #: f.albums.cc:878 f.albums.cc:938 f.albums.cc:1007 f.albums.cc:1147 msgid "Choose Albums" msgstr "Elegir álbumes" #: f.albums.cc:1005 f.widgets.cc:615 f.widgets.cc:762 msgid "Replace Album File" msgstr "Reemplazar álbum" #: f.albums.cc:1009 msgid "All Albums" msgstr "Todos los álbumes" #: f.albums.cc:1012 msgid "old file" msgstr "archivo antiguo" #: f.albums.cc:1016 msgid "new file" msgstr "archivo nuevo" #: f.albums.cc:1020 msgid "replace old" msgstr "reemplazar antiguo" #: f.albums.cc:1021 msgid "add after old" msgstr "añadir después antiguo" #: f.albums.cc:1081 msgid "no albums chosen" msgstr "no se han elegido álbumes" #: f.albums.cc:1162 msgid "ALL albums chosen" msgstr "elegidos todos los álbumes" #: f.albums.cc:1346 msgid "instant" msgstr "instantánea" #: f.albums.cc:1347 msgid "fade-in" msgstr "fundido" #: f.albums.cc:1348 msgid "roll-right" msgstr "desenrollar hacia la derecha" #: f.albums.cc:1349 msgid "roll-down" msgstr "desenrollar hacia abajo" #: f.albums.cc:1350 msgid "venetian" msgstr "persiana" #: f.albums.cc:1351 msgid "grate" msgstr "enrejado" #: f.albums.cc:1352 msgid "rectangle" msgstr "rectángulo" #: f.albums.cc:1353 msgid "implode" msgstr "implosión" #: f.albums.cc:1354 msgid "explode" msgstr "explosión" #: f.albums.cc:1355 msgid "radar" msgstr "radar" #: f.albums.cc:1356 msgid "spiral" msgstr "espiral" #: f.albums.cc:1357 msgid "Japan-fan" msgstr "Abanico japonés" #: f.albums.cc:1358 msgid "jaws" msgstr "dientes" #: f.albums.cc:1359 msgid "ellipse" msgstr "elipse" #: f.albums.cc:1360 msgid "raindrops" msgstr "gotas de lluvia" #: f.albums.cc:1361 msgid "doubledoor" msgstr "doble puerta" #: f.albums.cc:1362 msgid "rotate" msgstr "girar" #: f.albums.cc:1363 msgid "fallover" msgstr "hacia abajo" #: f.albums.cc:1364 msgid "spheroid" msgstr "esferoide" #: f.albums.cc:1365 msgid "turn-page" msgstr "girar página" #: f.albums.cc:1366 msgid "french-door" msgstr "puertas batientes" #: f.albums.cc:1367 msgid "turn-cube" msgstr "girar cubo" #: f.albums.cc:1368 msgid "windmill" msgstr "molino" #: f.albums.cc:1369 msgid "pixelize" msgstr "pixelizar" #: f.albums.cc:1370 msgid "twist" msgstr "girar" #: f.albums.cc:1372 msgid "squish" msgstr "expansión" #: f.albums.cc:1399 f.widgets.cc:616 msgid "Slide Show" msgstr "Diaporama" #: f.albums.cc:1405 msgid "use gallery" msgstr "usar galeria" #: f.albums.cc:1411 msgid "Clip Limit" msgstr "Límite de secuencia" #: f.albums.cc:1415 msgid "Music File" msgstr "Archivo de Música" #: f.albums.cc:1420 msgid "Full Screen" msgstr "Pantalla completa" #: f.albums.cc:1421 msgid "Auto-replay" msgstr "Auto reiniciar" #: f.albums.cc:1424 msgid "Customize:" msgstr "Personalizar:" #: f.albums.cc:1425 msgid "transitions" msgstr "transiciones" #: f.albums.cc:1426 msgid "image files" msgstr "archivos de imagen" #: f.albums.cc:1427 msgid "KB controls" msgstr "controles de atajos" #: f.albums.cc:1441 f.albums.cc:1509 f.albums.cc:1732 f.albums.cc:2007 #: f.albums.cc:2292 f.albums.cc:2309 f.albums.cc:2526 msgid "invalid album" msgstr "álbum inválido" #: f.albums.cc:1540 msgid "open album" msgstr "abrir álbum" #: f.albums.cc:1556 msgid "Select music file" msgstr "Seleccione archivo de música" #: f.albums.cc:1587 #, c-format msgid "%d images" msgstr "%d imágenes" #: f.albums.cc:1614 msgid "arrow keys show previous or next image instantly" msgstr "" "las teclas de flecha muestran instantáneamente la imagen previa o siguiente" #: f.albums.cc:1632 msgid "Keyboard Preferences" msgstr "Preferencias de atajos" #: f.albums.cc:1635 msgid "blank or unblank window" msgstr "pantalla vacía o llena" #: f.albums.cc:1638 msgid "show next image, with transition" msgstr "mostrar imagen siguiente, con transición" #: f.albums.cc:1641 msgid "pause or resume slide show" msgstr "pausar o reiniciar el diaporama" #: f.albums.cc:1644 msgid "magnify image (loupe tool)" msgstr "aumentar imagen (herramienta lupa)" #: f.albums.cc:1711 msgid "random sequence" msgstr "sacuencia aleatoria" #: f.albums.cc:1736 msgid "Transition Preferences" msgstr "Preferencias de transición" #: f.albums.cc:1738 msgid "Transitions File" msgstr "Archivo de transiciones" #: f.albums.cc:1756 f.albums.cc:1760 msgid "transition" msgstr "transición" #: f.albums.cc:1757 f.albums.cc:1761 msgid "use" msgstr "usar" #: f.albums.cc:1758 f.albums.cc:1762 msgid "slow" msgstr "lento" #: f.albums.cc:1759 f.albums.cc:1763 msgid "pref" msgstr "pref." #: f.albums.cc:1862 f.albums.cc:1901 msgid "invalid file" msgstr "archivo no válido" #: f.albums.cc:1954 f.albums.cc:2511 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "error de formato de archivo: \n" " %s" #: f.albums.cc:2013 msgid "Image Preferences" msgstr "Preferencias de imagen" #: f.albums.cc:2017 f.edit.cc:1405 f.file.cc:1577 f.file.cc:1883 f.meta.cc:820 #: f.meta.cc:1588 f.meta.cc:1780 msgid "Image File:" msgstr "Imagen:" #: f.albums.cc:2021 msgid "Play tone when image shows" msgstr "Reproducir música mientras se ve la imagen" #: f.albums.cc:2024 msgid "Show image caption" msgstr "Mostrar título del diaporama" #: f.albums.cc:2029 msgid "Show image comments" msgstr "Mostrar comentarios de la imagen" #: f.albums.cc:2034 msgid "Wait before zoom" msgstr "Espera antes del zoom" #: f.albums.cc:2039 msgid "Zoom type:" msgstr "Tipo de zoom:" #: f.albums.cc:2041 msgid "zoom-in" msgstr "acercar" #: f.albums.cc:2042 msgid "zoom-out" msgstr "alejar" #: f.albums.cc:2045 msgid "Zoom size (x)" msgstr "Tamaño de zoom (x)" #: f.albums.cc:2048 msgid "Steps" msgstr "Pasos" #: f.albums.cc:2052 msgid "Zoom Center" msgstr "Centrar zoom" #: f.albums.cc:2056 msgid "Wait after zoom" msgstr "Espera después del zoom" #: f.albums.cc:2061 msgid "Transition to next image" msgstr "Transición a la próxima imagen" #: f.albums.cc:2064 f.albums.cc:2167 msgid "next" msgstr "siguiente" #: f.albums.cc:2157 msgid "click on thumbnail to set zoom center" msgstr "clic en la miniatura para situar el centro de zoom" #: f.area.cc:81 msgid "Select Area for Edits" msgstr "Seleccionar área a editar" #: f.area.cc:82 f.area.cc:1855 f.edit.cc:9094 msgid "Press F1 for help" msgstr "Pulsar F1 para ayuda" #: f.area.cc:91 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Selección de área no admitida \n" "por esta función de edición" #: f.area.cc:120 msgid "select rectangle" msgstr "seleccionar rectángulo" #: f.area.cc:121 msgid "select ellipse" msgstr "seleccionar elipse" #: f.area.cc:124 msgid "freehand draw" msgstr "dibujar a mano alzada" #: f.area.cc:125 msgid "follow edge" msgstr "seguir contorno" #: f.area.cc:126 msgid "draw/replace" msgstr "dibujar/reemplazar" #: f.area.cc:129 msgid "select area within mouse circle" msgstr "seleccionar área interior del cursor" #: f.area.cc:132 msgid "select one matching color within mouse circle:" msgstr "seleccionar un color coincidente con el interior del cursor" #: f.area.cc:134 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "primero seleccion la caja de selección, después \n" "mayús+clic en la imagen para ajustar el color" #: f.area.cc:138 msgid "select all matching colors within mouse circle" msgstr "seleccione todos los colores coincidentes con el interior del cursor" #: f.area.cc:144 msgid "match level %" msgstr "nivel de coincidencia %" #: f.area.cc:147 msgid "search range" msgstr "rango de búsqueda" #: f.area.cc:151 msgid "Area Edge Blend Width" msgstr "Ancho de mezcla de bordes de área" #: f.area.cc:154 msgid "Edge Creep" msgstr "Borde progresivo" #: f.area.cc:159 msgid "Line Color:" msgstr "Color de línea:" #: f.area.cc:179 f.area.cc:180 msgid "area edits fade away within edge distance" msgstr "las ediciones de área se desvanecen en los bordes" #: f.area.cc:351 f.area.cc:517 #, c-format msgid "exceed %d edits" msgstr "excedido en %d ediciones" #: f.area.cc:1333 msgid "" "Click one time inside each enclosed area. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Clicar una vez en el ineterior de cada área cerrada. \n" "Se encontrarán posibles separaciones en el contorno. \n" "Presionar F1 para ayuda." #: f.area.cc:1360 msgid "finish area" msgstr "terminar área" #: f.area.cc:1389 f.area.cc:1534 #, c-format msgid "found %d pixels" msgstr "encontrados %d pixels" #: f.area.cc:1639 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Clic cerca de cualquier posición en el contorno del área. \n" "Se encontrarán posibles separaciones en el contorno. \n" "Presionar F1 para ayuda." #: f.area.cc:1658 msgid "find outline gap" msgstr "encontrar separación en el contorno" #: f.area.cc:1748 msgid "cannot find area outline" msgstr "no se encuentra contorno de área" #: f.area.cc:1854 f.widgets.cc:458 msgid "Select Hairy" msgstr "Selección irregular" #: f.area.cc:1862 msgid "select the area first" msgstr "en primer lugar sleccionar el área" #: f.area.cc:1867 f.area.cc:2489 f.area.cc:2507 f.area.cc:2527 f.area.cc:2851 #: f.area.cc:2971 msgid "the area is not finished" msgstr "el área no está cerrada" #: f.area.cc:1940 msgid "select" msgstr "seleccionar" #: f.area.cc:1946 msgid "deselect" msgstr "deshacer selección" #: f.area.cc:2616 msgid "Edge calculation in progress" msgstr "Cálculo del contorno en progreso" #: f.area.cc:2625 msgid "Area Edge Calc" msgstr "Cálculo del contorno del área" #: f.area.cc:2957 f.area.cc:3029 f.widgets.cc:465 msgid "Copy Area" msgstr "Copiar área" #: f.area.cc:2960 f.widgets.cc:468 msgid "Save Area File" msgstr "Guardar archivo de área" #: f.area.cc:3035 msgid "save area as a PNG file" msgstr "guardar área como un archivo PNG" #: f.area.cc:3159 msgid "position with mouse click/drag" msgstr "posición con el clic/arrastre del ratón" #: f.area.cc:3219 msgid "Paste Image" msgstr "Pegar imagen" #: f.area.cc:3224 f.process.cc:1311 msgid "resize" msgstr "redimensionar" #: f.combine.cc:169 f.combine.cc:815 f.combine.cc:1416 f.combine.cc:2039 msgid "Select 2 to 9 files" msgstr "Seleccione de 2 a 9 imágenes" #: f.combine.cc:191 f.combine.cc:837 f.combine.cc:1438 f.combine.cc:2061 msgid "Images are not all the same size" msgstr "No todas las imágenes son del mismo tamaño" #: f.combine.cc:550 msgid "Adjust Image Contributions" msgstr "Ajustar aportes de imagen" #: f.combine.cc:554 msgid "dark pixels" msgstr "sombras" #: f.combine.cc:556 msgid "light pixels" msgstr "luces" #: f.combine.cc:558 msgid "file:" msgstr "archivo:" #: f.combine.cc:1018 msgid "Paint and Warp Image" msgstr "Pintar y deformar imagen" #: f.combine.cc:1026 f.edit.cc:5686 msgid "paint" msgstr "pintar" #: f.combine.cc:1027 msgid "warp" msgstr "deformar" #: f.combine.cc:1623 msgid "Select and Paint Image" msgstr "Seleccionar y pintar imagen" #: f.combine.cc:1631 msgid "Transient Objects" msgstr "Objetos transitorios" #: f.combine.cc:2232 msgid "Adjust Pixel Composition" msgstr "Ajustar composición de píxeles" #: f.combine.cc:2234 msgid "use average" msgstr "usar media" #: f.combine.cc:2235 msgid "use median" msgstr "usar mediana" #: f.combine.cc:2237 msgid "omit low pixel" msgstr "omitir píxeles bajos" #: f.combine.cc:2238 msgid "omit high pixel" msgstr "omitir píxeles altos" #: f.combine.cc:2502 f.combine.cc:3653 msgid "Select 2 to 4 files" msgstr "Seleccionar de 2 a 4 imagenes" #: f.combine.cc:2573 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Arrastre las imágenes para un alineado aproximado.\n" "Para girar, arrastre desde el borde inferior." #: f.combine.cc:2575 f.combine.cc:3726 msgid "no curve (scanned image)" msgstr "sin curvatura (imagen escaneada)" #: f.combine.cc:2576 f.combine.cc:3727 msgid "Search for lens mm" msgstr "Buscar por longitud focal (mm)" #: f.combine.cc:2577 f.combine.cc:3728 msgid "Save lens mm → image EXIF" msgstr "Guardar focal en EXIF" #: f.combine.cc:2639 f.combine.cc:3790 msgid "Pre-align Images" msgstr "Pre-alinear imágenes" #: f.combine.cc:2643 f.combine.cc:3794 msgid "lens mm" msgstr "longitud focal (mm)" #: f.combine.cc:2648 f.combine.cc:3799 msgid "no auto warp" msgstr "no auto corrección" #: f.combine.cc:2650 f.combine.cc:3801 msgid "manual align" msgstr "alineación manual" #: f.combine.cc:2652 f.combine.cc:3803 f.widgets.cc:473 f.widgets.cc:768 msgid "Resize" msgstr "Redimensionar" #: f.combine.cc:2653 f.combine.cc:3804 msgid "resize window" msgstr "redimensionar ventana" #: f.combine.cc:2661 f.combine.cc:3812 msgid "do not warp images during auto-alignment" msgstr "no deformar imágenes durante el auto alineado" #: f.combine.cc:2707 f.combine.cc:3858 msgid "use two images only" msgstr "usar sólo dos imágenes" #: f.combine.cc:2735 f.combine.cc:2939 f.combine.cc:3127 f.combine.cc:3884 #: f.combine.cc:4088 f.combine.cc:4276 msgid "Too little overlap, cannot align" msgstr "Solapamiento demasiado pequeño, no puedo alinear" #: f.combine.cc:3205 f.combine.cc:4354 msgid "Match Brightness and Color" msgstr "Concordancia de brillo y color" #: f.combine.cc:3251 msgid "Select image" msgstr "Seleccionar imagen" #: f.combine.cc:3266 f.combine.cc:4415 msgid "auto color" msgstr "color automático" #: f.combine.cc:3267 f.combine.cc:4416 msgid "file color" msgstr "archivo de color" #: f.combine.cc:3273 f.combine.cc:4422 msgid "mouse warp" msgstr "corregir con ratón" #: f.combine.cc:3275 f.combine.cc:4424 msgid "flatten image" msgstr "aplanar imagen" #: f.combine.cc:3724 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Arrastre las imágenes para un alineado aproximado.\n" "Para girar, arrastre desde el borde derecho." #: f.combine.cc:4684 msgid "pano tools (hugin) not installed" msgstr "Pano Tools (Hugin) no instalado" #: f.combine.cc:4693 msgid "Select at least 2 files" msgstr "Seleccione el menos 2 archivos" #: f.edit.cc:98 msgid "Trim: drag middle to move, drag corners to resize" msgstr "" "Recortar : arrastrar centro para mover, arrastrar esquinas para redimensionar" #: f.edit.cc:99 msgid "Minor rotate: drag right edge with mouse" msgstr "Giro : arrastrar al lado derecho con el ratón" #: f.edit.cc:180 f.widgets.cc:471 f.widgets.cc:767 msgid "Trim/Rotate" msgstr "Recortar/Girar" #: f.edit.cc:192 f.edit.cc:1306 msgid "ratio" msgstr "relación" #: f.edit.cc:196 msgid "trim size:" msgstr "tamaño de recorte:" #: f.edit.cc:201 msgid "Lock Ratio" msgstr "Bloquear la relación" #: f.edit.cc:203 msgid "maximize trim box" msgstr "maximizar caja de recorte" #: f.edit.cc:204 msgid "use previous size" msgstr "usar tamaño previo" #: f.edit.cc:205 msgid "invert width/height" msgstr "invertit ancho/alto" #: f.edit.cc:206 msgid "trim transparent edges" msgstr "recortar bordes transparentes" #: f.edit.cc:207 msgid "lock width/height ratio" msgstr "bloquear proporción ancho/alto" #: f.edit.cc:212 msgid "Customize" msgstr "Personalizar" #: f.edit.cc:218 msgid "Level" msgstr "Nivel" #: f.edit.cc:219 msgid "use EXIF data if available" msgstr "usar datos EXIF si están disponibles" #: f.edit.cc:222 msgid "Rotate: degrees" msgstr "Girar : grados" #: f.edit.cc:1302 msgid "Trim Buttons" msgstr "Botones de recorte" #: f.edit.cc:1305 msgid "label" msgstr "etiqueta" #: f.edit.cc:1402 msgid "Upright Image" msgstr "Enderezar imagen" #: f.edit.cc:1408 f.process.cc:168 f.process.cc:755 f.widgets.cc:472 #: f.widgets.cc:769 msgid "Upright" msgstr "Enderezar" #: f.edit.cc:1461 msgid "rotation unknown" msgstr "giro desconocido" #: f.edit.cc:1548 msgid "Resize Image" msgstr "Redimensionar imagen" #: f.edit.cc:1573 msgid "W/H Ratio:" msgstr "Relación ancho/alto" #: f.edit.cc:1575 msgid "Lock" msgstr "Bloquear" #: f.edit.cc:1577 msgid "use previous settings" msgstr "usar ajustes anteriores" #: f.edit.cc:2298 f.widgets.cc:476 f.widgets.cc:772 msgid "Retouch Combo" msgstr "Retocar luz y color" #: f.edit.cc:2319 msgid "Amplifier" msgstr "Amplificar" #: f.edit.cc:2320 fotoxx.h:1188 msgid "Brightness" msgstr "Brillo" #: f.edit.cc:2321 f.effects.cc:3681 fotoxx.h:1198 msgid "Contrast" msgstr "Contraste" #: f.edit.cc:2322 msgid "Low Color" msgstr "Color" #: f.edit.cc:2323 msgid "Warmer" msgstr "Calidez" #: f.edit.cc:2324 f.effects.cc:1332 msgid "Dark Areas" msgstr "Sombras" #: f.edit.cc:2333 msgid "Max." msgstr "Max" #: f.edit.cc:2334 f.edit.cc:2335 f.edit.cc:2336 msgid "High" msgstr "+" #: f.edit.cc:2337 msgid "Cooler" msgstr "Frio" #: f.edit.cc:2338 msgid "Bright" msgstr "Luces" #: f.edit.cc:2341 f.tools.cc:2134 msgid "Brightness Distribution" msgstr "Distribución del brillo" #: f.edit.cc:2344 msgid "Click for white balance" msgstr "Clic para balance de blancos" #: f.edit.cc:2345 msgid "Click for black level" msgstr "Clic para nivel de negro" #: f.edit.cc:2348 msgid "Settings File" msgstr "Ajustes" #: f.edit.cc:2352 msgid "recall previous settings used" msgstr "recuperar ajustes previamente usados" #: f.edit.cc:3004 msgid "Adjust Brightness Distribution" msgstr "Ajustar histograma" #: f.edit.cc:3043 msgid "Low Cutoff" msgstr "Corte bajo" #: f.edit.cc:3044 msgid "High Cutoff" msgstr "Corte alto" #: f.edit.cc:3045 msgid "Low Flatten" msgstr "Aplanado bajo" #: f.edit.cc:3046 msgid "Mid Flatten" msgstr "Aplanado medio" #: f.edit.cc:3047 msgid "High Flatten" msgstr "Aplanado alto" #: f.edit.cc:3048 msgid "Low Stretch" msgstr "Tramo bajo" #: f.edit.cc:3049 msgid "Mid Stretch" msgstr "Tramo medio" #: f.edit.cc:3050 msgid "High Stretch" msgstr "Tramo alto" #: f.edit.cc:3383 msgid "Magnify Gradients" msgstr "Aumentar gradientes" #: f.edit.cc:3415 msgid "low" msgstr "bajo" #: f.edit.cc:3417 msgid "high" msgstr "alto" #: f.edit.cc:3420 msgid "Amplify" msgstr "Ampliar" #: f.edit.cc:3813 msgid "Flatten Brightness" msgstr "Aplanar brillo" #: f.edit.cc:3864 msgid "Zones" msgstr "Zonas" #: f.edit.cc:3871 msgid "Deband Dark" msgstr "Eliminar bandas oscuras" #: f.edit.cc:3874 msgid "Deband Bright" msgstr "Eliminar bandas brillantes" #: f.edit.cc:4386 msgid "Dark Point" msgstr "Punto negro" #: f.edit.cc:4387 msgid "Bright Point" msgstr "Punto de brillo" #: f.edit.cc:4388 msgid "Multiplyer" msgstr "Multiplicador" #: f.edit.cc:4412 msgid "brightness rescale" msgstr "reecalar brillo" #: f.edit.cc:4415 msgid "click bright point" msgstr "clic en punto brillante" #: f.edit.cc:4416 msgid "click dark point" msgstr "clic en punto oscuro" #: f.edit.cc:4427 msgid "blend" msgstr "mezclar" #: f.edit.cc:4430 msgid "reduce bright" msgstr "reducir brillo" #: f.edit.cc:5468 f.widgets.cc:481 msgid "Mirror Image" msgstr "Espejar imagen" #: f.edit.cc:5472 f.warp.cc:3300 msgid "horizontal" msgstr "horizontal" #: f.edit.cc:5473 f.warp.cc:3304 msgid "vertical" msgstr "vertical" #: f.edit.cc:5620 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "mayúsc + clic izquierdo: tomar color de una imagen \n" "arrastre izquierdo: pintar color en la imagen \n" "arrastre derecho: restaurar imagen original" #: f.edit.cc:5656 msgid "Paint on Image" msgstr "Pintar en la imagen" #: f.edit.cc:5662 msgid "paint color" msgstr "color de la pintura" #: f.edit.cc:5673 f.edit.cc:6640 msgid "brush size" msgstr "tamaño de brocha" #: f.edit.cc:5674 f.edit.cc:6641 msgid "opacity center" msgstr "opacidad del centro" #: f.edit.cc:5675 f.edit.cc:6642 msgid "opacity edge" msgstr "opacidad del borde" #: f.edit.cc:5687 msgid "erase" msgstr "borrar" #: f.edit.cc:5688 msgid "include transparent areas" msgstr "incluir áreas transparentes" #: f.edit.cc:5695 msgid "drag image" msgstr "arratrar imagen" #: f.edit.cc:5697 msgid "zoom image" msgstr "zoom de imagen" #: f.edit.cc:6221 msgid "Color Palette" msgstr "Palte de color" #: f.edit.cc:6225 msgid "click on image to choose color" msgstr "clic en le imagen para escoger color" #: f.edit.cc:6415 f.repair.cc:3799 msgid "Color Hue" msgstr "Tono de color" #: f.edit.cc:6416 f.repair.cc:3768 f.repair.cc:3800 msgid "Saturation" msgstr "Saturación" #: f.edit.cc:6417 f.repair.cc:3769 f.repair.cc:3801 msgid "Lightness" msgstr "Luminosidad" #: f.edit.cc:6599 msgid "" "shift + left click: pick image position to copy \n" "left drag: copy image to mouse position \n" "right drag: restore image" msgstr "" "mayúsc + clic izq.: señalar posición de imagen a copiar nbotón izquierdo y " "arrastrar : copiar imagen en la posición del ratón \n" "botón derecho y arrastrar: restaurar imagen" #: f.edit.cc:6630 f.widgets.cc:483 msgid "Clone Image" msgstr "Clonar imagen" #: f.edit.cc:6649 msgid "paint over transparent areas" msgstr "pintar sobre áreas transparentes" #: f.edit.cc:7098 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "arrastre izquierdo: mezclar imagen \n" "arrastre derecho: reinicializar imagen" #: f.edit.cc:7126 f.widgets.cc:484 msgid "Blend Image" msgstr "Mezclar imagen" #: f.edit.cc:7134 f.repair.cc:7663 msgid "strength center" msgstr "fuerza en el centro" #: f.edit.cc:7135 f.repair.cc:7664 msgid "strength edge" msgstr "fuerza en bordes" #: f.edit.cc:7359 msgid "+Version" msgstr "+Version" #: f.edit.cc:7382 f.widgets.cc:275 msgid "Add Text to Image" msgstr "Añadir texto a la imagen" #: f.edit.cc:7383 msgid "Enter text, click/drag on image, right click to remove" msgstr "" "Escriba el texto, pulse/arrastre sobre la imagen, pulse con el botón derecho " "para eliminar" #: f.edit.cc:7432 f.edit.cc:8310 msgid "Use settings file" msgstr "Usar archivo de ajustes" #: f.edit.cc:7437 f.mashup.cc:2455 msgid "Text" msgstr "Texto" #: f.edit.cc:7442 msgid "Use metadata key" msgstr "Usar clave de metadatos" #: f.edit.cc:7461 f.mashup.cc:2473 msgid "text" msgstr "texto" #: f.edit.cc:7462 f.edit.cc:8336 f.mashup.cc:2474 f.mashup.cc:2812 msgid "backing" msgstr "fondo" #: f.edit.cc:7463 f.edit.cc:8337 f.mashup.cc:2475 f.mashup.cc:2813 msgid "outline" msgstr "contorno" #: f.edit.cc:7464 f.edit.cc:8338 f.mashup.cc:2476 f.mashup.cc:2814 msgid "shadow" msgstr "sombra" #: f.edit.cc:7490 msgid "save to current file" msgstr "guardar en archivo actual" #: f.edit.cc:7491 f.file.cc:2247 msgid "save as new file version" msgstr "guardar como una nueva versión de archivo" #: f.edit.cc:7492 msgid "" "save to current file \n" "open next file with same text" msgstr "" "guardar en archivo actual \n" "abrir próximo archivo con el mismo texto" #: f.edit.cc:7652 f.mashup.cc:2580 f.tools.cc:1464 msgid "select font" msgstr "seleccionar tipo de letra" #: f.edit.cc:7928 f.edit.cc:8766 msgid "text file is defective" msgstr "archivo de texto defectuoso" #: f.edit.cc:8268 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Introducir propiedades de línea/flecha en el diálogo. \n" "clic/arrastrar en la imagen, clic derecho para eliminar" #: f.edit.cc:8302 f.widgets.cc:276 msgid "Add lines or arrows to an image" msgstr "Añadir líneas o flechas a una imagen" #: f.edit.cc:8315 f.mashup.cc:2791 msgid "Line length" msgstr "Longitud de línea" #: f.edit.cc:8322 f.mashup.cc:2798 msgid "Arrow head" msgstr "Punta de flecha" #: f.edit.cc:8335 f.mashup.cc:2811 msgid "line" msgstr "linea" #: f.edit.cc:8364 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "corregir línea/flecha en la capa \n" "iniciar nueva línea/flecha" #: f.edit.cc:9093 f.widgets.cc:487 msgid "Paint Edits" msgstr "Editar pintando" #: f.edit.cc:9100 f.edit.cc:9323 fotoxx-18.01.cc:3426 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "No se puede conservar la selección de área.\n" "¿Continuar" #: f.edit.cc:9108 f.edit.cc:9331 f.tools.cc:2666 msgid "Edit function must be active" msgstr "la función de edición debe estar activa" #: f.edit.cc:9113 msgid "Cannot use Paint Edits" msgstr "No se puede usar Editar pintando" #: f.edit.cc:9140 msgid "power: center" msgstr "fuerza: centro" #: f.edit.cc:9145 msgid "reset area" msgstr "restablecer área" #: f.edit.cc:9316 f.widgets.cc:488 msgid "Leverage Edits" msgstr "Editar por niveles" #: f.edit.cc:9317 msgid "Edit Function Amplifier" msgstr "Amplificador de la función de edición" #: f.edit.cc:9336 msgid "Cannot use Leverage Edits" msgstr "No se puede usar Edición por niveles" #: f.edit.cc:9365 msgid "minimum" msgstr "sombras" #: f.edit.cc:9367 msgid "maximum" msgstr "luces" #: f.edit.cc:9642 f.edit.cc:9686 msgid "Edit Plugins" msgstr "Editar complementos (plugins)" #: f.edit.cc:9643 msgid "Edit plugins menu" msgstr "Editar menú de plugins" #: f.edit.cc:9651 msgid "Run as Fotoxx edit function" msgstr "Abrir como una función de edición de Fotoxx" #: f.edit.cc:9688 msgid "menu name" msgstr "nombre de menú" #: f.edit.cc:9691 msgid "command" msgstr "comando" #: f.edit.cc:9888 msgid "Plugin working ..." msgstr "Plugin trabajando ..." #: f.edit.cc:9897 msgid "plugin failed" msgstr "falló el complemento" #: f.effects.cc:79 msgid "Convert to Sketch" msgstr "Convertir a esbozo" #: f.effects.cc:157 msgid "Clip Level" msgstr "Nivel de corte" #: f.effects.cc:161 msgid "Algorithm" msgstr "Algoritmo" #: f.effects.cc:166 msgid "Foreground" msgstr "Primer plano" #: f.effects.cc:169 f.tools.cc:1275 msgid "Background" msgstr "Fondo" #: f.effects.cc:1054 f.widgets.cc:531 msgid "Line Drawing" msgstr "Dibujo con líneas" #: f.effects.cc:1067 msgid "black/white" msgstr " negro/blanco" #: f.effects.cc:1326 f.widgets.cc:532 msgid "Color Drawing" msgstr "Dibujo con colores" #: f.effects.cc:1333 msgid "Bright Areas" msgstr "Luces" #: f.effects.cc:1575 f.widgets.cc:533 msgid "Graduated Blur" msgstr "Desenfoque graduado" #: f.effects.cc:1579 msgid "Contrast Limit" msgstr "Límite de contraste" #: f.effects.cc:1582 f.repair.cc:929 msgid "Blur Radius" msgstr "Radio de desenfoque" #: f.effects.cc:1799 msgid "Simulate Embossing" msgstr "Simular relieve" #: f.effects.cc:1820 msgid "depth" msgstr "profundidad" #: f.effects.cc:2029 msgid "Simulate Tiles" msgstr "Simular un mosaico" #: f.effects.cc:2033 msgid "tile size" msgstr "tamaño de baldosas" #: f.effects.cc:2036 msgid "tile gap" msgstr "separación de baldosas" #: f.effects.cc:2039 msgid "3D depth" msgstr "profundidad 3D" #: f.effects.cc:2258 msgid "Convert Image to Dots" msgstr "Convertir imagen a puntos" #: f.effects.cc:2262 msgid "dot size" msgstr "tamaño del punto" #: f.effects.cc:2484 msgid "Simulate Painting" msgstr "Simular una pintura" #: f.effects.cc:2502 msgid "color depth" msgstr "profundidad de color" #: f.effects.cc:2506 msgid "patch area goal" msgstr "tamaño del área de color " #: f.effects.cc:2510 msgid "req. color match" msgstr "correspondencia de color requerida" #: f.effects.cc:2514 msgid "borders" msgstr "bordes" #: f.effects.cc:3081 f.widgets.cc:538 msgid "Vignette" msgstr "Viñetado" #: f.effects.cc:3430 msgid "Add Texture" msgstr "Añadir textura" #: f.effects.cc:3646 msgid "Background Pattern" msgstr "Patrón de fondo" #: f.effects.cc:3650 msgid "Pattern File:" msgstr "Archivo de patrón" #: f.effects.cc:3655 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3658 msgid "Geometry" msgstr "Geometría" #: f.effects.cc:3659 msgid "Calculate" msgstr "Calcular" #: f.effects.cc:3662 f.widgets.cc:540 msgid "Pattern" msgstr "Patrón" #: f.effects.cc:3670 msgid "Overlap" msgstr "Solape" #: f.effects.cc:3678 msgid "Opacity" msgstr "Opacidad" #: f.effects.cc:4124 msgid "Create Mosaic" msgstr "Crear mosaico" #: f.effects.cc:4170 msgid "Tile" msgstr "Baldosa" #: f.effects.cc:4178 f.widgets.cc:535 msgid "Tiles" msgstr "Baldosas" #: f.effects.cc:4184 msgid "Tile blending" msgstr "Mezcla de baldosas" #: f.effects.cc:4265 #, c-format msgid "exceeded max. tiles: %d" msgstr "excedido max. baldosas: %d" #: f.effects.cc:4272 #, c-format msgid "only %d tile images found" msgstr "encontradas sólo %d imágenes de baldosas" #: f.effects.cc:4756 f.widgets.cc:542 msgid "Custom Kernel" msgstr "Matriz de convolución" #: f.effects.cc:4760 msgid "Kernel size" msgstr "Tamaño de matriz" #: f.effects.cc:4777 msgid "multiply" msgstr "multiplicar" #: f.effects.cc:4780 msgid "add" msgstr "añadir" #: f.effects.cc:4784 msgid "Data file" msgstr "Archivo de datos" #: f.effects.cc:4804 fotoxx-18.01.cc:3690 msgid "Load settings from file" msgstr "Cargar ajustes desde un archivo" #: f.effects.cc:5046 msgid "Pull image using the mouse." msgstr "Empujar imagen usando el ratón" #: f.effects.cc:5067 f.widgets.cc:543 msgid "Directed Blur" msgstr "Desenfoque direccional" #: f.effects.cc:5071 msgid "blur span" msgstr "cantidad de desnfoque" #: f.effects.cc:5074 msgid "intensity" msgstr "intensidad" #: f.effects.cc:5276 f.widgets.cc:544 msgid "Blur Background" msgstr "Desenfocar fondo" #: f.effects.cc:5280 msgid "constant blur" msgstr "desenfoque constante" #: f.effects.cc:5283 msgid "increase blur with distance" msgstr "incrementar desenfoque con la distancia" #: f.effects.cc:5285 msgid "min. blur radius" msgstr "mín. radio de desenfoque" #: f.effects.cc:5288 msgid "max. blur radius" msgstr "máx. radio de desenfoque" #: f.effects.cc:5322 f.warp.cc:819 f.warp.cc:1252 msgid "no active Select Area" msgstr "selección de área no activada" #: f.effects.cc:5515 f.widgets.cc:545 msgid "Alien Colors" msgstr "Colores alienígenas" #: f.effects.cc:5518 msgid "blocksize" msgstr "tamaño de bloque" #: f.effects.cc:5521 f.warp.cc:3298 msgid "amplitude" msgstr "amplitud" #: f.file.cc:190 msgid "" "Use EXIF photo date or \n" " file modification date?" msgstr "" "Usar fecha EXIF de la foto o \n" "facha de modificación del archivo?" #: f.file.cc:204 f.widgets.cc:626 msgid "File" msgstr "Archivo" #: f.file.cc:378 f.process.cc:1345 msgid "Raw Therapee not installed" msgstr "RawTherapee no instalado" #: f.file.cc:396 f.widgets.cc:432 f.widgets.cc:777 msgid "Open RAW (Raw Therapee)" msgstr "Abrir RAW (Raw Therapee)" #: f.file.cc:401 msgid "RAW type not registered in User Settings" msgstr "Formato RAW no registrado en las preferencias de usuario" #: f.file.cc:416 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee no produjo archivo tif" #: f.file.cc:822 f.widgets.cc:429 msgid "Open Image File" msgstr "Abrir archivo de imagen" #: f.file.cc:841 f.process.cc:597 msgid "unknown file type" msgstr "tipo de archivo desconocido" #: f.file.cc:1019 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Inicio de galería, precedente: %s" #: f.file.cc:1020 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Fin de galería. siguiente: %s" #: f.file.cc:1128 msgid "Create Blank Image" msgstr "Crear una imagen vacía" #: f.file.cc:1130 msgid "file name" msgstr "nombre de archivo" #: f.file.cc:1164 msgid "supply a file name" msgstr "proporcionar un nombre de archivo" #: f.file.cc:1334 f.widgets.cc:436 msgid "Rename Image File" msgstr "Renombrar archivo de imagen" #: f.file.cc:1341 msgid "Old Name" msgstr "nombre antiguo" #: f.file.cc:1342 f.process.cc:130 msgid "New Name" msgstr "nombre nuevo" #: f.file.cc:1351 msgid "previous name" msgstr "nombre anterior" #: f.file.cc:1352 msgid "Add 1" msgstr "Añadir 1" #: f.file.cc:1355 f.file.cc:1591 f.file.cc:1886 msgid "keep this dialog open" msgstr "mantener este diálogo abierto" #: f.file.cc:1540 msgid "Copy or Move Image File" msgstr "Copiar o mover archivo" #: f.file.cc:1582 msgid "New Location:" msgstr "Nueva ubicación" #: f.file.cc:1587 msgid "copy (duplicate file)" msgstr "copiar (duplicar archivo)" #: f.file.cc:1588 msgid "move (remove original)" msgstr "mover (quitar original)" #: f.file.cc:1629 f.process.cc:573 f.process.cc:1503 f.process.cc:2625 msgid "Select directory" msgstr "seleccionar directorio" #: f.file.cc:1667 msgid "new location is not a directory" msgstr "la nueva ubicación no es un directorio" #: f.file.cc:1708 f.file.cc:1962 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "fallo al borrar: \n" " %s" #: f.file.cc:1847 f.widgets.cc:442 msgid "Delete/Trash Image File" msgstr "Eliminar/borrar archivo de imagen" #: f.file.cc:1916 msgid "GTK g_file_trash() function failed" msgstr "Ha fallado la función GTK g_file_trash()" #: f.file.cc:1935 msgid "not a known image file" msgstr "no es un archivo de imagen conocido" #: f.file.cc:1941 msgid "Delete read-only file?" msgstr "¿Borrar archivo de sólo lectura?" #: f.file.cc:1943 msgid "Trash read-only file?" msgstr "¿Archivo de papelera de sólo lectura?" #: f.file.cc:1991 msgid "no more images" msgstr "no hay más imágenes" #: f.file.cc:2229 msgid "Save Image File" msgstr "Guardar imagen" #: f.file.cc:2239 msgid "new version" msgstr "nueva versión" #: f.file.cc:2240 msgid "new file ..." msgstr "nuevo archivo ..." #: f.file.cc:2241 msgid "replace file" msgstr "reemplazar archivo" #: f.file.cc:2248 f.file.cc:2820 msgid "save as new file name or type" msgstr "guardar como un nuevo nombre o tipo de archivo" #: f.file.cc:2250 msgid "replace old file (OVERWRITE)" msgstr "reemplazar archivo antiguo (SOBREESCRIBIR)" #: f.file.cc:2287 f.file.cc:2578 msgid "cannot save as RAW type" msgstr "no se puede guardar como RAW" #: f.file.cc:2395 msgid "too many file versions: 99" msgstr "demasiadas versiones del archivo: 99" #: f.file.cc:2572 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "Se perderá el mapa de transparencia.\n" "guardar en un archivo PNG para conservarlo" #: f.file.cc:2657 msgid "save anyway" msgstr "guardar de todos modos" #: f.file.cc:2726 msgid "Unable to copy EXIF/IPTC data" msgstr "No se pueden copiar los datos EXIF/IPTC" #: f.file.cc:2838 f.file.cc:2841 msgid "make current" msgstr "hacer actual" #: f.file.cc:2839 msgid "(new file becomes current file)" msgstr "(nuevo archivo será el archivo actual)" #: f.file.cc:2952 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "¿Sobreescribir archivo? \n" " %s" #: f.file.cc:3147 f.widgets.cc:599 msgid "Quick Start" msgstr "Referencia rápida" #: f.file.cc:3150 f.widgets.cc:600 msgid "User Guide" msgstr "Guia de usuario" #: f.file.cc:3153 f.widgets.cc:601 msgid "User Guide Changes" msgstr "Cambios en la guía de usuario" #: f.file.cc:3156 f.widgets.cc:602 msgid "README" msgstr "LÉAME" #: f.file.cc:3159 f.widgets.cc:603 msgid "Change Log" msgstr "Registro de cambios" #: f.file.cc:3162 f.widgets.cc:604 msgid "Log File" msgstr "Archivo de registro" #: f.file.cc:3165 f.widgets.cc:605 msgid "Translations" msgstr "Traducciones" #: f.file.cc:3168 f.widgets.cc:606 msgid "Home Page" msgstr "Página web" #: f.file.cc:3171 f.widgets.cc:607 msgid "About" msgstr "Acerca de" #: f.file.cc:3177 f.widgets.cc:639 f.widgets.cc:663 f.widgets.cc:676 #: f.widgets.cc:690 fotoxx.h:1223 msgid "Help" msgstr "Ayuda" #: f.file.cc:3382 f.file.cc:3387 f.file.cc:3457 #, c-format msgid "file type not supported: %s" msgstr "tipo de archivo no soportado: %s" #: f.gallery.cc:1221 msgid "GoTo" msgstr "Ir a" #: f.gallery.cc:1227 f.widgets.cc:655 msgid "Sort" msgstr "Ordenar" #: f.gallery.cc:1233 f.widgets.cc:653 msgid "Zoom+" msgstr "Zoom +" #: f.gallery.cc:1243 f.widgets.cc:654 msgid "Zoom-" msgstr "Zoom -" #: f.gallery.cc:1255 msgid "Row Up" msgstr "Fila arriba" #: f.gallery.cc:1263 msgid "Row Down" msgstr "Fila abajo" #: f.gallery.cc:1271 f.widgets.cc:658 msgid "Page Up" msgstr "Página arriba" #: f.gallery.cc:1279 f.widgets.cc:659 msgid "Page Down" msgstr "Página abajo" #: f.gallery.cc:1287 f.widgets.cc:656 msgid "First" msgstr "Primera" #: f.gallery.cc:1294 f.widgets.cc:657 msgid "Last" msgstr "Última" #: f.gallery.cc:1425 f.gallery.cc:1445 msgid "recent images" msgstr "imágenes recientes" #: f.gallery.cc:1426 f.gallery.cc:1450 msgid "newest images" msgstr "imágenes más recientes" #: f.gallery.cc:1504 msgid "no albums found" msgstr "no se han encontrado albums" #: f.gallery.cc:1541 msgid "Albums cannot be sorted" msgstr "Los álbumes no pueden ser ordenados" #: f.gallery.cc:1545 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" "Reiniciar todas las galerías\n" "a nombres de archivo ascendentes" #: f.gallery.cc:1564 msgid "Gallery Sort" msgstr "Ordenar galería " #: f.gallery.cc:1568 msgid "File Name" msgstr "Nombre de archivo" #: f.gallery.cc:1569 msgid "File Mod Date/Time" msgstr "Modo de archivo Fecha/Hora" #: f.gallery.cc:1570 msgid "Photo Date/Time (EXIF)" msgstr "Fecha/Hora de la foto (EXIF)" #: f.gallery.cc:1572 msgid "ascending" msgstr "ascendente" #: f.gallery.cc:1573 msgid "descending" msgstr "descendente" #: f.gallery.cc:2925 f.gallery.cc:2929 msgid "Image File" msgstr "Archivo de imagen" #: f.gallery.cc:2927 msgid "select thumbnail" msgstr "seleccionar miniatura" #: f.gallery.cc:3037 fotoxx.h:1276 msgid "Select Files" msgstr "Seleccionar archivos" #: f.gallery.cc:3097 #, c-format msgid "exceed %d selected files" msgstr "excedidos %d archivos seleccionados" #: f.gallery.cc:3457 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Clic en una posición de la lista. Clic en una miniatura de galería para " "nueva etiqueta.\n" "Serán añadidas etiquetas para miniaturas. Cambiar el nombre y pulsar " "[Renombrar]" #: f.gallery.cc:3484 f.gallery.cc:3734 msgid "Edit Bookmarks" msgstr "Añadir marcadores" #: f.gallery.cc:3662 msgid "unable to save bookmarks file" msgstr "incapaz de guardar archivo de marcadores" #: f.gallery.cc:3734 msgid "Go To Bookmark" msgstr "Ir a un marcador" #: f.mashup.cc:170 msgid "Mashup layout and background image" msgstr "Capa de montaje e imagen de fondo" #: f.mashup.cc:213 msgid "choose an image file" msgstr "escoger una imagen" #: f.mashup.cc:214 msgid "use current image file" msgstr "usar imagen actual" #: f.mashup.cc:215 msgid "specify layout size and color" msgstr "especificar tamaño y color de la capa" #: f.mashup.cc:216 msgid "open a Mashup project file" msgstr "abrir un proyecto de fotomontaje" #: f.mashup.cc:259 msgid "no current file" msgstr "no hay archivo activo" #: f.mashup.cc:291 msgid "Make Layout Image" msgstr "Hacer composición de imágenes" #: f.mashup.cc:293 msgid "project name" msgstr "nombre del proyecto" #: f.mashup.cc:319 msgid "supply a project name" msgstr "proporcione un nombre de proyecto" #: f.mashup.cc:411 msgid "Edit Images" msgstr "Editar imágenes" #: f.mashup.cc:412 f.mashup.cc:2450 msgid "Edit Text" msgstr "Editar texto" #: f.mashup.cc:413 msgid "Edit Line" msgstr "Editar línea" #: f.mashup.cc:414 msgid "Rescale" msgstr "Reescalar" #: f.mashup.cc:419 msgid "add or edit images" msgstr "añadir o editar imágenes" #: f.mashup.cc:421 msgid "add or edit text" msgstr "añadir o editar texto" #: f.mashup.cc:423 msgid "add or edit lines/arrows" msgstr "añadir o editar líneas/flechas" #: f.mashup.cc:425 msgid "change project scale" msgstr "cambiar escala del proyecto" #: f.mashup.cc:427 msgid "project complete" msgstr "proyecto completo" #: f.mashup.cc:429 msgid "cancel project" msgstr "cancelar proyecto" #: f.mashup.cc:447 msgid "rescale project" msgstr "reescalar proyecto" #: f.mashup.cc:506 msgid "save Mashup output file" msgstr "guardar archivo de salida del fotomontaje" #: f.mashup.cc:526 msgid "save Mashup project file?" msgstr "guardar archivo de proyecto del fotomontaje" #: f.mashup.cc:538 msgid "delete Mashup project file?" msgstr "¿borrar archivo de proyecto del fotomontaje?" #: f.mashup.cc:597 msgid "Open Project" msgstr "Abrir proyecto" #: f.mashup.cc:626 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "necesaria imagen de capa \n" " %s" #: f.mashup.cc:683 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "necesaria imagen superpuesta \n" " %s" #: f.mashup.cc:989 msgid "project file is defective" msgstr "archivo de proyecto defectuoso" #: f.mashup.cc:1019 msgid "Save Project" msgstr "Guardar proyecto" #: f.mashup.cc:1160 msgid "layout exceeds 2 gigabytes" msgstr "la composición excede de 2 gigabytes" #: f.mashup.cc:1234 msgid "Click image to select, drag image to move." msgstr "Clic en imagen para seleccionar, arrastrar para mover" #: f.mashup.cc:1235 msgid "Make black margins transparent" msgstr "Hacer transparentes los márgenes negros" #: f.mashup.cc:1274 msgid "Current image:" msgstr "Imagen activa" #: f.mashup.cc:1278 msgid "Cycle through images:" msgstr "Recorrido por las imágenes" #: f.mashup.cc:1285 msgid "Scale" msgstr "Escalar" #: f.mashup.cc:1294 msgid "Stacking Order" msgstr "Orden de pila" #: f.mashup.cc:1295 msgid "Raise" msgstr "Subir" #: f.mashup.cc:1296 msgid "Lower" msgstr "Bajar" #: f.mashup.cc:1299 msgid "Base Transparency" msgstr "Transparencia" #: f.mashup.cc:1303 msgid "Var. Transparency" msgstr "Variar transparencia" #: f.mashup.cc:1304 msgid "Paint" msgstr "Pintar" #: f.mashup.cc:1307 msgid "Bend and fine-align" msgstr "Mezclado y alineado fino" #: f.mashup.cc:1308 f.widgets.cc:634 msgid "Warp" msgstr "Deformación" #: f.mashup.cc:1317 zfuncs.cc:11351 zfuncs.cc:11360 msgid "Margins" msgstr "Márgenes" #: f.mashup.cc:1318 msgid "Hard" msgstr "Fuerte" #: f.mashup.cc:1319 f.repair.cc:4410 msgid "Blend" msgstr "Mezclar" #: f.mashup.cc:1333 msgid "add images to layout" msgstr "añadir imágenes a la capa" #: f.mashup.cc:1570 msgid "Paint Image Transparencies" msgstr "Pintar transparencias de la imagen" #: f.mashup.cc:1588 msgid "Gradual" msgstr "Gradual" #: f.mashup.cc:1590 fotoxx.h:1255 msgid "Power" msgstr "Fuerza" #: f.mashup.cc:1823 msgid "Pull on the image with the mouse." msgstr "Empujar la imagen con el ratón" #: f.mashup.cc:1839 msgid "Warp Image" msgstr "Deformar imagen" #: f.mashup.cc:1845 f.warp.cc:1383 msgid "warp span" msgstr "intervalo de deformación" #: f.mashup.cc:2281 #, c-format msgid "exceeded %d images" msgstr "excedidas en %d imágenes" #: f.mashup.cc:2423 msgid "Enter text, [Add] to layout, edit properties." msgstr "Introducir texto, [Añadir] a la capa, editar propiedades" #: f.mashup.cc:2503 msgid "Text File:" msgstr "Archivo de texto:" #: f.mashup.cc:2507 msgid "add entered text to layout" msgstr "añadir texto a la capa" #: f.mashup.cc:2641 msgid "click position to add text" msgstr "clicar en la posición para añadir texto" #: f.mashup.cc:2647 #, c-format msgid "exceeded %d text entries" msgstr "Excedidas en %d entradas de texto" #: f.mashup.cc:2652 f.widgets.cc:485 msgid "Add Text" msgstr "Añadir texto" #: f.mashup.cc:2761 msgid "Set line properties, [Add] to layout, edit." msgstr "Ajustar propiedades de línea, [Añadir] a la capa, editar." #: f.mashup.cc:2785 msgid "Edit Line/Arrow" msgstr "Editar línea/flecha" #: f.mashup.cc:2840 msgid "add line/arrow to layout" msgstr "añadir línea/flecha a la capa" #: f.mashup.cc:2931 msgid "click position to add line" msgstr "clic en la posició per afegir línia" #: f.mashup.cc:2937 #, c-format msgid "exceeded %d line entries" msgstr "excedidas %d entradas de línea" #: f.mashup.cc:2942 f.widgets.cc:486 msgid "Add Line" msgstr "Añadir línea" #: f.mashup.cc:4189 msgid "Image Montage" msgstr "Montaje de imágenes" #: f.mashup.cc:4198 msgid "Frame Width" msgstr "Ancho de marco" #: f.mashup.cc:4201 f.mashup.cc:4209 msgid "Margin" msgstr "Margen" #: f.mashup.cc:4206 msgid "Image Columns" msgstr "Columnas de imagen" #: f.mashup.cc:4223 #, c-format msgid "exceed %d rows" msgstr "excedido en columnas %d" #: f.mashup.cc:4319 msgid "Optimize" msgstr "Optimizar" #: f.mashup.cc:4327 #, c-format msgid "column difference: %d pixels" msgstr "diferencia de columnas : %d píxeles" #: f.mashup.cc:4385 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "diferencia de columnas : %d píxeles \n" "¿Hacer columnas?" #: f.mashup.cc:4407 msgid "Save with unique montage name" msgstr "Guardar con nombtre d emontaje único" #: f.mashup.cc:4409 msgid "unique name:" msgstr "nombre único" #: f.mashup.cc:4411 msgid "create image map" msgstr "crear mapa de imágenes" #: f.mashup.cc:4421 f.meta.cc:8637 msgid "supply a reasonable name" msgstr "dar un nombre razonable" #: f.mashup.cc:4432 msgid "save montage" msgstr "guardar montaje" #: f.mashup.cc:4452 #, c-format msgid "map file saved: %s" msgstr "mapa de imágenes guardado: %s" #: f.mashup.cc:4492 #, c-format msgid "%d max images exceeded" msgstr "%d máximo de imágenes excedido" #: f.mashup.cc:4566 f.mashup.cc:4572 #, c-format msgid "image frame is too large: %d x %d" msgstr "marco de imagen demasiado ancho: %d x %d" #: f.mashup.cc:4873 #, c-format msgid "montage map file not found: %s" msgstr "archivo de mapa de imágenes no encontrado: %s" #: f.mashup.cc:4877 #, c-format msgid "montage map file invalid: %s" msgstr "archivo de montaje de imágenes inválido: %s" #: f.meta.cc:242 msgid "Add Metadata Items" msgstr "Añadir items de metadatos" #: f.meta.cc:246 f.meta.cc:1583 f.meta.cc:3104 msgid "click to select" msgstr "clic para seleccionar" #: f.meta.cc:251 msgid "click to unselect" msgstr "clic para deselecionar" #: f.meta.cc:475 msgid "Extras" msgstr "Extras" #: f.meta.cc:475 f.meta.cc:655 f.widgets.cc:754 msgid "View Metadata" msgstr "Ver metadatos" #: f.meta.cc:813 f.widgets.cc:450 f.widgets.cc:755 msgid "Edit Metadata" msgstr "Editar metadatos" #: f.meta.cc:816 msgid "save metadata to file" msgstr "guardar metadatos en el archivo" #: f.meta.cc:825 msgid "Image Date" msgstr "Fecha de la imagen" #: f.meta.cc:828 msgid "Time" msgstr "Hora" #: f.meta.cc:836 msgid "Rating (stars):" msgstr "Calificación (estrellas)" #: f.meta.cc:848 msgid "Caption" msgstr "Título" #: f.meta.cc:853 msgid "Comments" msgstr "Comentarios" #: f.meta.cc:860 msgid "Location" msgstr "Ubicación" #: f.meta.cc:863 msgid "Country" msgstr "Pais" #: f.meta.cc:886 msgid "Image Tags" msgstr "Etiquetas de la imagen" #: f.meta.cc:891 f.meta.cc:1992 msgid "Recent Tags" msgstr "etiquetas recientes" #: f.meta.cc:896 f.meta.cc:1998 msgid "Enter New Tag" msgstr "Introduzca nueva etiqueta" #: f.meta.cc:902 f.meta.cc:2004 f.meta.cc:4565 msgid "Matching Tags" msgstr "Construyendo etiquetas" #: f.meta.cc:909 f.meta.cc:2013 f.meta.cc:2511 f.meta.cc:4571 msgid "Defined Tags Category" msgstr "Categoría de etiquetas definida" #: f.meta.cc:916 msgid "search known locations" msgstr "buscar localidades conocidas" #: f.meta.cc:917 msgid "search using web service" msgstr "buscar usando servicio web" #: f.meta.cc:918 f.meta.cc:2020 msgid "create tag categories and tags" msgstr "crear etiquetas y categorías de etiquetas" #: f.meta.cc:1368 f.meta.cc:3673 f.meta.cc:7231 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "latitud/longitud incorrecta: %s %s" #: f.meta.cc:1423 fotoxx.h:1234 msgid "Manage Tags" msgstr "Administrar etiquetas" #: f.meta.cc:1423 msgid "orphan tags" msgstr "etiquetas huérfanas" #: f.meta.cc:1427 msgid "category" msgstr "categoria" #: f.meta.cc:1430 msgid "tag" msgstr "etiqueta" #: f.meta.cc:1437 msgid "Defined Tags:" msgstr "etiquetas definidas:" #: f.meta.cc:1579 f.widgets.cc:451 f.widgets.cc:756 msgid "Edit Any Metadata" msgstr "Editar cualquier metadato" #: f.meta.cc:1579 f.meta.cc:3099 msgid "Full List" msgstr "Lista completa" #: f.meta.cc:1592 f.meta.cc:3115 msgid "key name" msgstr "nombre clave" #: f.meta.cc:1595 f.meta.cc:3116 msgid "key value" msgstr "valor clave" #: f.meta.cc:1677 f.meta.cc:3259 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" "El comando: $ man Image::ExifTool::TagNames \n" "mostrará hasta 15000 \"standard\" nombres de etiqueta" #: f.meta.cc:1777 f.widgets.cc:452 msgid "Delete Metadata" msgstr "Borrar metadatos" #: f.meta.cc:1783 fotoxx.h:1179 msgid "All" msgstr "Todo" #: f.meta.cc:1784 msgid "One Key:" msgstr "Una clave:" #: f.meta.cc:1968 f.widgets.cc:568 msgid "Batch Add/Remove Tags" msgstr "Añadir/eliminar etiquetas en lote" #: f.meta.cc:1981 msgid "tags to add" msgstr "etiquetas para añadir" #: f.meta.cc:1982 msgid "tags to remove" msgstr "etquetas para eliminar" #: f.meta.cc:2095 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " demasiadas etiquetas" #: f.meta.cc:2108 msgid "repeat with same files?" msgstr "¿repetir con los miosmos archivos?" #: f.meta.cc:2290 msgid "specify files and tags" msgstr "especificar archivos y etiquetas" #: f.meta.cc:2490 f.widgets.cc:569 msgid "Batch Rename Tags" msgstr "Renombrar etiquetas en lote" #: f.meta.cc:2500 msgid "(click defined tag)" msgstr "(clic etiquetas definidas)" #: f.meta.cc:2502 f.process.cc:749 msgid "Rename to" msgstr "Renombrar como" #: f.meta.cc:2519 msgid "old tag name >> new tag name" msgstr "antiguo nombre de etiqueta >> nuevo nombre de etiqueta" #: f.meta.cc:2576 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" "%d etiquetas para renombrar \n" "en %d imágenes. \n" "¿Proceder?" #: f.meta.cc:2720 msgid "max tags exceeded" msgstr "Máximo de etiquetas excedido" #: f.meta.cc:2782 f.widgets.cc:570 msgid "Batch Photo Date/Time" msgstr "Fecha/Hora de fotos en lote" #: f.meta.cc:2790 msgid "set a new date/time:" msgstr "establecer nuevos fecha/hora" #: f.meta.cc:2798 msgid "shift existing date/time:" msgstr "cambiar fecha/hora existente" #: f.meta.cc:2824 msgid "test: show changes, do not update files" msgstr "probar: mostrar cambios, no actualizar archivos" #: f.meta.cc:2850 f.meta.cc:3190 f.meta.cc:3368 msgid "no files selected" msgstr "no hay archivos seleccionados" #: f.meta.cc:2880 f.meta.cc:2888 f.meta.cc:2894 msgid "invalid date/time format" msgstr "formato inválido de fecha/hora" #: f.meta.cc:3099 f.widgets.cc:571 msgid "Batch Add/Change Metadata" msgstr "Añadir/cambiar metadatos en lote" #: f.meta.cc:3184 msgid "enter key names" msgstr "entrar nombres clave" #: f.meta.cc:3354 f.widgets.cc:572 msgid "Batch Report Metadata" msgstr "Informe de metadatos en lote" #: f.meta.cc:3498 f.widgets.cc:573 msgid "Batch Geotags" msgstr "Geoetiquetas en lote" #: f.meta.cc:3528 msgid "location" msgstr "ubicación" #: f.meta.cc:3531 msgid "country" msgstr "pais" #: f.meta.cc:3659 msgid "" "data is incomplete \n" " proceed?" msgstr "" "los datos están incompletos \n" " ¿proceder?" #: f.meta.cc:3745 msgid "Report Image Locations" msgstr "informe de localizaciones de imagen" #: f.meta.cc:3746 msgid "Group by country" msgstr "Agrupar por país" #: f.meta.cc:3747 msgid "Group by country/location" msgstr "Agrupar por país/ubicación" #: f.meta.cc:3748 msgid "Group by country/location/date" msgstr "Agrupar por país/ubicación/fecha" #: f.meta.cc:3749 msgid "Group by date/country/location" msgstr "Agrupar por fecha/país/ubicación" #: f.meta.cc:3752 msgid "Combine within" msgstr "Combinar dentro" #: f.meta.cc:3754 msgid "days" msgstr "dias" #: f.meta.cc:3860 f.widgets.cc:1026 f.widgets.cc:1048 msgid "Image Locations" msgstr "Ubicación de imágenes" #: f.meta.cc:4137 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Ene Feb Mar Abr May Jun Jul Ago Sep Oct Nov Dic" #: f.meta.cc:4489 msgid "Search Image Metadata" msgstr "Buscar los metadatos de la imagen" #: f.meta.cc:4493 msgid "images to search:" msgstr "imágenes a buscar:" #: f.meta.cc:4494 msgid "all" msgstr "todo" #: f.meta.cc:4495 msgid "current set only" msgstr "sólo selección actual" #: f.meta.cc:4498 msgid "matching images:" msgstr "imágenes coincidentes" #: f.meta.cc:4499 msgid "new set" msgstr "nueva selección" #: f.meta.cc:4500 msgid "add to set" msgstr "añadir a la selección" #: f.meta.cc:4501 msgid "remove" msgstr "eliminar" #: f.meta.cc:4504 msgid "report type:" msgstr "tipo de imforme:" #: f.meta.cc:4505 msgid "gallery" msgstr "galería" #: f.meta.cc:4509 msgid "date range" msgstr "rango de fechas" #: f.meta.cc:4514 msgid "photo date" msgstr "fecha de la foto" #: f.meta.cc:4515 msgid "file date" msgstr "fecha del archivo" #: f.meta.cc:4516 msgid "(yyyy-mm-dd)" msgstr "(aaaa-mm-dd)" #: f.meta.cc:4519 msgid "stars range" msgstr "rango de estrellas" #: f.meta.cc:4522 msgid "last version only" msgstr "sólo última versión" #: f.meta.cc:4524 msgid "all/any" msgstr "todo/cualquiera" #: f.meta.cc:4527 msgid "search tags" msgstr "buscar etiquetas" #: f.meta.cc:4533 msgid "search text" msgstr "buscar texto" #: f.meta.cc:4539 msgid "search files" msgstr "buscar archivos" #: f.meta.cc:4545 msgid "search locations" msgstr "buscar localidades" #: f.meta.cc:4549 msgid "enter cities, countries" msgstr "introducir ciudades, paises" #: f.meta.cc:4554 msgid "search other metadata" msgstr "buscar otros metadatos" #: f.meta.cc:4561 msgid "Enter Search Tag" msgstr "Introducir etiqueta de búsqueda" #: f.meta.cc:4837 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "para eliminar imágenes de la selección actual, \n" "buscar en la selección actual" #: f.meta.cc:4844 msgid "" "to add images to current set, \n" "search all images" msgstr "" "para añadir imágenes a la selección actual, \n" "buscar en todas las imágenes" #: f.meta.cc:4890 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "búsqueda de datos no razonable \n" " %s %s" #: f.meta.cc:4915 msgid "stars range not reasonable" msgstr "rango de estrellas no razonable" #: f.meta.cc:5129 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "imágenes añadidas: %d eliminadas: %d nueva cantidad: %d" #: f.meta.cc:5132 msgid "no changes made" msgstr "no se han efectuado cambios" #: f.meta.cc:5303 msgid "" "These items are always reported: \n" "date, stars, tags, caption, comment" msgstr "" "Siempre se informa de estos elementos: \n" "fecha, estrellas, etiquetas, leyenda, comentario" #: f.meta.cc:5328 msgid "Additional Items for Report" msgstr "Elementos adicionales para el informe" #: f.meta.cc:5335 msgid "Keyword" msgstr "Palabra clave" #: f.meta.cc:5349 msgid "Match Criteria" msgstr "Criterio de coincidencia" #: f.meta.cc:5455 #, c-format msgid "bad number: %s" msgstr "número erróneo: %s" #: f.meta.cc:5712 msgid "date format is YYYY-MM-DD" msgstr "Formato de fecha es AAAA-MM-DD" #: f.meta.cc:5716 msgid "date is invalid" msgstr "fecha no válida" #: f.meta.cc:5750 msgid "time format is HH:MM [:SS]" msgstr "formato de hora es HH:MM [:SS]" #: f.meta.cc:5754 msgid "time is invalid" msgstr "hora no válida" #: f.meta.cc:6851 msgid "not found" msgstr "no encontrada" #: f.meta.cc:6852 msgid "location and country required" msgstr "ubicación y pais requeridos" #: f.meta.cc:7111 msgid "choose location" msgstr "escoger ubicación" #: f.meta.cc:7408 msgid "Set Map Markers" msgstr "Establecer marcadores de mapa" #: f.meta.cc:7409 msgid "mark all image files" msgstr "marcar todas las imágenes" #: f.meta.cc:7410 msgid "mark current gallery" msgstr "marcar galería actual" #: f.meta.cc:7510 msgid "choose map file" msgstr "elegir archivo de mapas" #: f.meta.cc:7652 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "fotoxx-maps package not instalado \n" "(ver https://kornelix.net)" #: f.meta.cc:7657 #, c-format msgid "map file %s is missing" msgstr "necesario archivo de mapas %s" #: f.meta.cc:7661 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" "datos de latitud/longitud no consistentes \n" " %.3f %.3f %.3f %.3f" #: f.meta.cc:7981 f.meta.cc:8508 msgid "No matching images found" msgstr "No encontradas imagenes coincidentes" #: f.meta.cc:8073 msgid "Set Map Source" msgstr "Establecer origen de mapas" #: f.meta.cc:8584 msgid "Net Map Locations" msgstr "Ubicaciones Net Map" #: f.meta.cc:8589 msgid "map location:" msgstr "ubicación en mapa" #: f.process.cc:123 f.widgets.cc:559 msgid "Batch Convert" msgstr "Convertir en lote" #: f.process.cc:135 msgid "Sequence Numbers" msgstr "Números secuenciales" #: f.process.cc:137 msgid "base" msgstr "inicio" #: f.process.cc:140 msgid "adder" msgstr "incremento" #: f.process.cc:145 msgid "New Location" msgstr "Nueva ubicación" #: f.process.cc:150 msgid "New File Type" msgstr "Nuevo tipo de archivo" #: f.process.cc:154 f.process.cc:162 f.process.cc:2538 msgid "no change" msgstr "sin cambios" #: f.process.cc:157 f.process.cc:2533 msgid "max. Width" msgstr "Ancho máx." #: f.process.cc:166 msgid "Delete Originals" msgstr "Borrar originales" #: f.process.cc:167 f.process.cc:754 msgid "Copy Metadata" msgstr "Copiar metadatos" #: f.process.cc:171 f.process.cc:756 f.process.cc:1320 f.repair.cc:121 #: f.widgets.cc:492 msgid "Sharpen" msgstr "Enfocar" #: f.process.cc:181 f.process.cc:757 msgid "Overlay Image" msgstr "Imagen de capa" #: f.process.cc:184 msgid "Width %" msgstr "Ancho %" #: f.process.cc:189 msgid "Position" msgstr "Posición" #: f.process.cc:201 msgid "Make constant size for screen:" msgstr "Tamaño constante para la pantalla" #: f.process.cc:209 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "plugins: (año mes día nombre-antiguo secuencia) $aaaa $mm $dd $nombreantiguo " "$s" #: f.process.cc:331 #, c-format msgid "file type not supported: %s \n" msgstr "tipo de archivo no soportado: %s \n" #: f.process.cc:471 f.process.cc:2588 msgid "cannot create new file" msgstr "no se puede crear el nuevo archivo" #: f.process.cc:517 msgid "updating albums ..." msgstr "actualizando álbums ..." #: f.process.cc:658 #, c-format msgid "invalid plugin: %s" msgstr "plugin no válido: %s" #: f.process.cc:665 msgid "you must use either $s or $oldname" msgstr "debe utilizar $s o $nombreantiguo" #: f.process.cc:670 msgid "$s plugin needs base and adder" msgstr "el plugin $s necesita una base y un incremento" #: f.process.cc:675 msgid "base and adder need $s plugin" msgstr "la base y el incremento necesitan $s plugin" #: f.process.cc:697 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "el tamaño máximo %d x %d no es razonable" #: f.process.cc:704 msgid "specify overlay image file" msgstr "especificar imagen de capa" #: f.process.cc:720 msgid "specify overlay position" msgstr "especificar posición de capa" #: f.process.cc:748 #, c-format msgid "Convert %d image files" msgstr "Convertit %d imágenes" #: f.process.cc:750 msgid "Convert to" msgstr "Convertir a" #: f.process.cc:751 msgid "Resize within" msgstr "Redimensionar dentro de " #: f.process.cc:752 msgid "Output to" msgstr "Salida como" #: f.process.cc:758 msgid "*** Delete Originals ***" msgstr "*** Borrar originales ***" #: f.process.cc:759 msgid "*** Replace Originals ***" msgstr "*** Reemplazar originales ***" #: f.process.cc:760 msgid "PROCEED?" msgstr "PROCEDER?" #: f.process.cc:913 f.widgets.cc:560 msgid "Batch Upright" msgstr "Enderezar en lote" #: f.process.cc:919 msgid "Survey all files" msgstr "Examinar todos los archivos" #: f.process.cc:956 msgid "file cannot be read" msgstr "no se puede leer el archivo" #: f.process.cc:1072 msgid "cannot select both options" msgstr "no se pueden seleccionar ambas opciones" #: f.process.cc:1114 f.widgets.cc:561 msgid "Batch Delete/Trash" msgstr "Borrar/papelera en lote" #: f.process.cc:1119 msgid "delete" msgstr "borrar" #: f.process.cc:1122 msgid "trash" msgstr "papelera" #: f.process.cc:1260 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Convertir en lote archivos RAW (RawTherapee)" #: f.process.cc:1290 msgid "use libraw" msgstr "usar libraw" #: f.process.cc:1291 msgid "use Raw Therapee" msgstr "usar Raw Therapee" #: f.process.cc:1298 msgid "output location" msgstr "ubicación exterior" #: f.process.cc:1303 msgid "output file type" msgstr "tipo de archivo de salida" #: f.process.cc:1322 msgid "amount" msgstr "cantidad" #: f.process.cc:1325 msgid "threshold" msgstr "umbral" #: f.process.cc:1614 msgid "script file" msgstr "archivo de script" #: f.process.cc:1627 msgid "begin making a script file" msgstr "iniciando construcción de un script" #: f.process.cc:1630 msgid "finish making a script file" msgstr "acabando construcción de un script" #: f.process.cc:1636 msgid "execute a script file" msgstr "ejecutar un script" #: f.process.cc:1696 msgid "script already started" msgstr "script iniciado" #: f.process.cc:1700 msgid "start a new script file" msgstr "iniciar un nuevo script" #: f.process.cc:1723 msgid "perform edits to be included in the script file" msgstr "performar ediciones a ser incluidas en el script" #: f.process.cc:1741 msgid "script file error" msgstr "error de script" #: f.process.cc:1746 #, c-format msgid "%s added to script" msgstr "%s añadido al script" #: f.process.cc:1759 msgid "no script file was started" msgstr "no se ha iniciado el script" #: f.process.cc:1767 msgid "script file closed" msgstr "script cerrado" #: f.process.cc:1825 msgid "open script file" msgstr "abrir script" #: f.process.cc:1833 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "script: %s \n" " %s" #: f.process.cc:1855 f.process.cc:1904 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "fallo de apertura: %s \n" " %s" #: f.process.cc:1876 #, c-format msgid "unknown edit function: %s" msgstr "función de edición desconocida: %s" #: f.process.cc:1885 #, c-format msgid "load widgets failed: %s" msgstr "cargar complementos fallados: %s" #: f.process.cc:1913 #, c-format msgid "script file format error: %s" msgstr "error de formato de script: %s" #: f.process.cc:1949 msgid "growisofs not installed" msgstr "growisofs no instalado" #: f.process.cc:2001 msgid "no DVD/BlueRay device found" msgstr "No encontrado dispositivo DVDE/Blue Ray" #: f.process.cc:2024 f.widgets.cc:564 msgid "Burn Images to DVD/BlueRay" msgstr "Grabar imágenes en un DVD/Blue Ray" #: f.process.cc:2029 msgid "Select device" msgstr "Seleccionar dispositivo" #: f.process.cc:2136 f.widgets.cc:565 msgid "Find Duplicate Images" msgstr "Buscar imágenes duplicadas" #: f.process.cc:2138 msgid "Thumbnail size" msgstr "Tamaño de miniatura" #: f.process.cc:2142 msgid "pixel difference" msgstr "diferencia de píxels" #: f.process.cc:2145 msgid "pixel count" msgstr "conteo de píxels" #: f.process.cc:2152 msgid "Duplicates:" msgstr "Duplicados:" #: f.process.cc:2170 #, c-format msgid "only %d image thumbnails found" msgstr "ercontradas sólo %d miniaturas de imágenes" #: f.process.cc:2374 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Crear un listado de las imágenes seleccionadas" #: f.process.cc:2398 f.process.cc:2467 msgid "Output File" msgstr "Archivo de salida" #: f.process.cc:2418 msgid "no input files selected" msgstr "No seleccionados archivos de entrada" #: f.process.cc:2424 msgid "no output file selected" msgstr "No seleccionado archivo de salida" #: f.process.cc:2523 f.widgets.cc:567 msgid "Export Image Files" msgstr "Exportar imágenes" #: f.process.cc:2528 msgid "To Location" msgstr "A ubicación" #: f.process.cc:2563 msgid "file type not supported" msgstr "tipo de archivo no soportado" #: f.process.cc:2643 msgid "location is not a directory" msgstr "La ubicación no es un directorio" #: f.repair.cc:164 msgid "dark" msgstr "oscuro" #: f.repair.cc:165 msgid "light" msgstr "claro" #: f.repair.cc:1193 msgid "Apply repeatedly while watching the image." msgstr "Aplicar repetidamente mientras se observe la imagen" #: f.repair.cc:1194 msgid "Measure" msgstr "Medida" #: f.repair.cc:1228 msgid "Noise Reduction" msgstr "Reducción de ruido" #: f.repair.cc:1247 f.widgets.cc:479 f.widgets.cc:774 fotoxx.h:1218 msgid "Flatten" msgstr "Aplanar" #: f.repair.cc:1248 msgid "Median" msgstr "Mediana" #: f.repair.cc:1263 msgid "dark areas" msgstr "áreas oscuras" #: f.repair.cc:1265 msgid "all areas" msgstr "todas las áreas" #: f.repair.cc:1856 msgid "Measure Noise" msgstr "Medir ruído" #: f.repair.cc:1857 msgid "Move mouse in a monotone image area." msgstr "Mover el ratón en un área de color uniforme de la imagen." #: f.repair.cc:2165 msgid "" "Method 1:\n" " Left-click on red-eye to darken.\n" "Method 2:\n" " Drag down and right to enclose red-eye.\n" " Left-click on red-eye to darken.\n" "Undo red-eye:\n" " Right-click on red-eye." msgstr "" "Método 1:\n" " Clic-izquierdo en el ojo rojo para oscurecer.\n" "Método 2:\n" " Picar y arrastrar a la derecha para delimitar el ojo rojo.\n" " Clic-izquierdo para oscurecer el ojo rojo.\n" "Deshacer ojos rojos:\n" " Clic-derecho en el ojo rojo." #: f.repair.cc:2181 msgid "Red Eye Reduction" msgstr "Reducción de ojos rojos" #: f.repair.cc:2642 f.widgets.cc:496 msgid "Color Mode" msgstr "Modo de color" #: f.repair.cc:2645 msgid "reset" msgstr "reinicializar" #: f.repair.cc:2646 msgid "black/white positive" msgstr "positivo en blanco y negro" #: f.repair.cc:2647 msgid "black/white negative" msgstr "negativo en blanco y negro" #: f.repair.cc:2648 msgid "color negative" msgstr "Negativo en color" #: f.repair.cc:2649 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.repair.cc:2650 msgid "sepia" msgstr "sepia" #: f.repair.cc:2861 msgid "Set color depth to 1-16 bits" msgstr "Establecer profundidad de color 1-16 bits" #: f.repair.cc:2875 msgid "Set Color Depth" msgstr "Ajustar profundidad de color" #: f.repair.cc:3025 f.widgets.cc:498 msgid "Shift Colors" msgstr "Modificar colores" #: f.repair.cc:3259 f.widgets.cc:499 msgid "Color Saturation" msgstr "Saturación de color" #: f.repair.cc:3448 msgid "+Brightness" msgstr "Brillo" #: f.repair.cc:3449 msgid "+Red -Cyan" msgstr "+Rojo -Cyan" #: f.repair.cc:3450 msgid "+Green -Magenta" msgstr "+Verde -Magenta" #: f.repair.cc:3451 msgid "+Blue -Yellow" msgstr "+Azul -Amarillo" #: f.repair.cc:3457 fotoxx.h:1261 msgid "Red" msgstr "R" #: f.repair.cc:3458 fotoxx.h:1220 msgid "Green" msgstr "V" #: f.repair.cc:3459 fotoxx.h:1186 msgid "Blue" msgstr "A" #: f.repair.cc:3761 msgid "Input color to match and adjust:" msgstr "Entrar color para coincidir y ajustar" #: f.repair.cc:3763 msgid "shift+click on image to select color" msgstr "Mayús+clic en la imagen para seleccionar color" #: f.repair.cc:3766 f.repair.cc:7937 msgid "Match using:" msgstr "Usando coincidencia:" #: f.repair.cc:3767 msgid "Hue" msgstr "Tono" #: f.repair.cc:3779 msgid "Output Color" msgstr "Color de salida" #: f.repair.cc:3802 msgid "Adjustment" msgstr "Ajuste" #: f.repair.cc:4320 msgid "Click image to select areas." msgstr "Clic en la imagen para seleccionar área" #: f.repair.cc:4352 msgid "Local Color" msgstr "Color local" #: f.repair.cc:4363 msgid "Metric:" msgstr "Métrica:" #: f.repair.cc:4786 msgid "Color Match Images" msgstr "Concordar color de imágenes" #: f.repair.cc:4812 msgid "mouse radius for color sample" msgstr "radio del ratón para muestra de color" #: f.repair.cc:4814 f.repair.cc:4819 fotoxx.h:1250 msgid "Open" msgstr "Abrir" #: f.repair.cc:4815 msgid "image for source color" msgstr "imagen para el color de origen" #: f.repair.cc:4817 msgid "click on image to get source color" msgstr "Pulse en la imagen para obtener color de origen" #: f.repair.cc:4820 msgid "image to set matching color" msgstr "imagen para establecer concordancia de color" #: f.repair.cc:4822 msgid "click on image to set matching color" msgstr "Pulse en la imagen para establecer la concordancia de color" #: f.repair.cc:4881 msgid "select source image color first" msgstr "seleccionar primero el color de la imagen de origen" #: f.repair.cc:5059 msgid "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " msgstr "" " Ajustar cada color RGB para minimizar \n" " franjas de color en los extremos de la imagen. " #: f.repair.cc:5082 f.widgets.cc:504 msgid "Color Fringes" msgstr "Bandas de color" #: f.repair.cc:5240 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Arrastre el ratón para seleccionar. \n" "2. Borrar. 3. Repetir." #: f.repair.cc:5262 f.widgets.cc:505 msgid "Smart Erase" msgstr "Borrado inteligente" #: f.repair.cc:5267 fotoxx.h:1259 msgid "Radius" msgstr "Radio" #: f.repair.cc:5269 f.widgets.cc:493 msgid "Blur" msgstr "Desenfocar" #: f.repair.cc:5272 msgid "New Area" msgstr "Nueva área" #: f.repair.cc:5617 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Trazar una línea en la imagen en \n" "la dirección del cambio de brillo." #: f.repair.cc:5656 f.widgets.cc:506 msgid "Brightness Ramp" msgstr "Rampa de brillo" #: f.repair.cc:6182 f.widgets.cc:507 msgid "Remove Dust" msgstr "Retirar polvo" #: f.repair.cc:6186 msgid "spot size limit" msgstr "límite de tamaño de punto" #: f.repair.cc:6189 msgid "max. brightness" msgstr "máximo brillo" #: f.repair.cc:6192 msgid "min. contrast" msgstr "mínimo contraste" #: f.repair.cc:6999 f.widgets.cc:510 msgid "Stuck Pixels" msgstr "Píxeles retenidos" #: f.repair.cc:7005 msgid "pixel group" msgstr "agrupar píxeles" #: f.repair.cc:7013 f.repair.cc:7085 msgid "stuck pixels:" msgstr "píxeles defectuosos" #: f.repair.cc:7113 msgid "Load Stuck Pixels" msgstr "Cargar píxeles defectuosos" #: f.repair.cc:7115 msgid "File:" msgstr "Archivo:" #: f.repair.cc:7139 f.repair.cc:7196 msgid "Stuck Pixels file" msgstr "Archivo de píxeles defectuosos" #: f.repair.cc:7176 f.tools.cc:4220 msgid "file format error" msgstr "error de formato de archivo" #: f.repair.cc:7622 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "arrastre izquierdo: añadir transparencia \n" "arrastre derecho: añadir opacidad" #: f.repair.cc:7654 f.widgets.cc:511 msgid "Paint Transparency" msgstr "Pintar transparencia" #: f.repair.cc:7662 msgid "paintbrush radius" msgstr "radio del pincel" #: f.repair.cc:7669 msgid "gradual paint" msgstr "pintado gradual" #: f.repair.cc:7923 f.widgets.cc:512 msgid "Add Transparency" msgstr "Añadir transparencia" #: f.repair.cc:7927 msgid "Match Brightness" msgstr "Coincidencia de brillo" #: f.repair.cc:7932 msgid "Match Color" msgstr "Coincidencia de color" #: f.repair.cc:7934 msgid "click on image to select color" msgstr "Clic en la imagen para seleccionar color" #: f.repair.cc:7950 msgid "Invert Match" msgstr "Invertir coincidencia" #: f.repair.cc:7953 fotoxx.h:1285 msgid "Transparency" msgstr "Transparencia" #: f.tools.cc:86 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" "Seleccionar directorios conteniendo imágenes \n" "(los subdirectorios serán incluidos automáticamente)." #: f.tools.cc:88 msgid "Select to add, click on X to delete." msgstr "Seleccionar para añadir, clic en X para borrar." #: f.tools.cc:89 msgid "directory for thumbnails" msgstr "directorio para las miniaturas" #: f.tools.cc:90 msgid "extra metadata items to include in index" msgstr "ítems extra de metadatos para incluir en el índice" #: f.tools.cc:91 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Función de indexado terminada. \n" "Se requiere el indexado para las búsquedas y funciones de mapa \n" "y para hacer las páginas de galería aceptablemente rápidas." #: f.tools.cc:126 f.widgets.cc:579 msgid "Index Image Files" msgstr "Indexar imágenes" #: f.tools.cc:298 msgid "Choose top image directories" msgstr "Seleccionar el directorio raiz de imágenes" #: f.tools.cc:299 msgid "Choose thumbnail directory" msgstr "Elegir directorio de miniaturas" #: f.tools.cc:300 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Todos los archivos de imagen serán reindexados. \n" " ¿Continuar?" #: f.tools.cc:372 msgid "" "No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files." msgstr "" "No se encontró índice de imágenes.\n" "Se creará un índice de imágenes.\n" "Sus imágenes no serán modificadas.\n" "Esto puede llevar un tiempo considerable si \n" "tiene muchos miles de imágenes" #: f.tools.cc:378 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Directorio inválido : \n" " %s \n" "Por favor borrar." #: f.tools.cc:381 #, c-format msgid "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" msgstr "" "Directorio de miniaturas: \n" " %s \n" "debe llamarse .../thumbnails" #: f.tools.cc:384 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Directorio duplicado: \n" " %s \n" "Por favor borrar" #: f.tools.cc:443 msgid "specify 1 thumbnail directory" msgstr "Especificar un directorio de miniaturas" #: f.tools.cc:628 msgid "Top directories have no images" msgstr "Directorios superiores no tienen imágenes" #: f.tools.cc:1075 msgid "Cancel image index function?" msgstr "Cancelar función de indexado de imágenes" #: f.tools.cc:1195 msgid "Recent Files Gallery" msgstr "Galería de archivos recientes" #: f.tools.cc:1196 msgid "Newest Files Gallery" msgstr "Galería de archivos más recientes" #: f.tools.cc:1197 msgid "Specific Gallery" msgstr "Galería específica" #: f.tools.cc:1198 msgid "Album Gallery" msgstr "Galería de álbumes" #: f.tools.cc:1199 msgid "Previous Gallery" msgstr "Galería previa" #: f.tools.cc:1200 msgid "Previous Image File" msgstr "Imagen anterior" #: f.tools.cc:1201 msgid "Specific Image File" msgstr "Imagen específica" #: f.tools.cc:1202 f.widgets.cc:435 msgid "Blank Window" msgstr "Ventana vacía" #: f.tools.cc:1250 f.widgets.cc:580 msgid "User Settings" msgstr "Ajustes de usuario" #: f.tools.cc:1253 msgid "Startup Display" msgstr "Pantalla de inicio" #: f.tools.cc:1264 msgid "Background color:" msgstr "Color de fondo" #: f.tools.cc:1265 msgid "F-View" msgstr "Vista de archivos" #: f.tools.cc:1268 msgid "G-View" msgstr "Vista de Galería" #: f.tools.cc:1272 msgid "Menu Text" msgstr "Texto del menú" #: f.tools.cc:1279 msgid "Menu Style" msgstr "Estilo de menú" #: f.tools.cc:1280 msgid "Icons" msgstr "Iconos" #: f.tools.cc:1281 msgid "Icons + Text" msgstr "Iconos + texto" #: f.tools.cc:1283 msgid "Icon size" msgstr "Tamaño de icono" #: f.tools.cc:1287 msgid "Dialog font" msgstr "Fuente del diálogo" #: f.tools.cc:1292 msgid "Zoomed Image:" msgstr "Imagen aumentada:" #: f.tools.cc:1301 msgid "JPEG save quality" msgstr "calidad de guardado JPEG" #: f.tools.cc:1305 msgid "Curve node capture distance" msgstr "distancia de captura de nodo de curva" #: f.tools.cc:1309 msgid "Map marker size" msgstr "Tamaño del marcador de mapa" #: f.tools.cc:1313 msgid "show last file version only" msgstr "mostrar sólo última versión de archivo" #: f.tools.cc:1316 msgid "shift image to right margin" msgstr "cambiar imagen al margen derecho" #: f.tools.cc:1319 f.tools.cc:1324 msgid "image index level" msgstr "nivel de índice de imagen" #: f.tools.cc:1321 msgid "Fotoxx started directly" msgstr "Fotoxx iniciado directamente" #: f.tools.cc:1326 msgid "Fotoxx started by file manager" msgstr "Fotoxx iniciado por el explorador de archivos" #: f.tools.cc:1329 msgid "RAW file types" msgstr "Tipos de archivos RAW" #: f.tools.cc:1333 msgid "video file types" msgstr "tipos de archivos de vídeo" #: f.tools.cc:1440 msgid "Select startup directory" msgstr "Seleccionar el directorio de inicio" #: f.tools.cc:1447 msgid "Select startup image file" msgstr "Seleccionar el archivo de imagen de inicio" #: f.tools.cc:1454 msgid "Select startup album" msgstr "Seleccionar álbum de inicio" #: f.tools.cc:1497 msgid "startup directory is invalid" msgstr "El directorio de inicio no es correcto" #: f.tools.cc:1508 msgid "startup file is invalid" msgstr "El archivo de inicio no es correcto" #: f.tools.cc:1683 f.widgets.cc:581 msgid "Keyboard Shortcuts" msgstr "Atajos de teclado" #: f.tools.cc:1690 msgid "Reserved Shortcuts \n" msgstr "Atajos de teclado reservado \n" #: f.tools.cc:1691 msgid " F1 User Guide for current function \n" msgstr " F1 guía de usuario para la función actual \n" #: f.tools.cc:1692 msgid " F10 Full Screen with menus \n" msgstr " F10 Pantalla completa con menús \n" #: f.tools.cc:1693 msgid " F11 Full Screen without menus \n" msgstr " F11 Pantalla completa sin menús \n" #: f.tools.cc:1694 msgid " Escape Quit dialog; Quit Fotoxx \n" msgstr " Esc salir del diálogo, salir de Fotoxx \n" #: f.tools.cc:1695 msgid " Ctrl+H Show hidden files in Gallery mode \n" msgstr " Ctrl+H muestra archivos ocultos en el modo Galería \n" #: f.tools.cc:1696 msgid " Delete Delete/Trash dialog \n" msgstr " Supr borrar/eliminar diálogo \n" #: f.tools.cc:1697 msgid " Arrows Previous/Next Image or Gallery page \n" msgstr "Flechas Imagen Anterior/Siguiente en la páginan de galería \n" #: f.tools.cc:1757 msgid "Edit KB Shortcuts" msgstr "Editar atajos de teclado" #: f.tools.cc:1766 msgid "shortcut key:" msgstr "atajo de teclado" #: f.tools.cc:1767 msgid "(enter key)" msgstr "(entrar tecla)" #: f.tools.cc:1768 f.tools.cc:1911 f.tools.cc:2015 msgid "(no selection)" msgstr "(nada seleccionado)" #: f.tools.cc:1905 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reservada, no se puede utilizar" #: f.tools.cc:2036 msgid "unable to save KB-shortcuts file" msgstr "imposible guardar archivos de atajos de teclado" #: f.tools.cc:2356 f.widgets.cc:583 msgid "Grid Lines" msgstr "Lineas de rejilla" #: f.tools.cc:2365 msgid "x-spacing" msgstr "espaciado X" #: f.tools.cc:2366 msgid "x-count" msgstr "conteo X" #: f.tools.cc:2367 msgid "x-enable" msgstr "activar X" #: f.tools.cc:2373 msgid "y-spacing" msgstr "espaciado Y" #: f.tools.cc:2374 msgid "y-count" msgstr "conteo Y" #: f.tools.cc:2375 msgid "y-enable" msgstr "activar Y" #: f.tools.cc:2495 f.widgets.cc:584 msgid "Line Color" msgstr "Color de línea" #: f.tools.cc:2560 msgid "Click image to select pixels." msgstr "Pulsar en la imagen para seleccionar los píxeles." #: f.tools.cc:2595 f.widgets.cc:585 msgid "Show RGB" msgstr "Mostrar RGB" #: f.tools.cc:2890 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key X to toggle dialog." msgstr "" "Arrastrar el ratón sobre la imagen. \n" "Clic izquierdo para cancelar. \n" "Tecla X para esconder diálogo." #: f.tools.cc:2918 f.widgets.cc:586 msgid "Magnify Image" msgstr "Aumentar imagen" #: f.tools.cc:2927 msgid "X-size" msgstr "Lupa" #: f.tools.cc:3172 msgid "Darkest and Brightest Pixels" msgstr "Píxeles más oscuros y más brillantes" #: f.tools.cc:3195 msgid "Dark Limit" msgstr "Sombra" #: f.tools.cc:3196 msgid "Bright Limit" msgstr "Luz" #: f.tools.cc:3285 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "El brillo debe mostrar una rampa gradual \n" "Extender hasta el final de los bordes." #: f.tools.cc:3376 f.widgets.cc:589 msgid "Monitor Gamma" msgstr "Gamma del monitor" #: f.tools.cc:3443 msgid "Available Translations" msgstr "Traducciones disponibles" #: f.tools.cc:3447 msgid "Set Language" msgstr "Seleccionar el idioma" #: f.tools.cc:3573 msgid "Change Color Profile" msgstr "Cambiar perfil de color" #: f.tools.cc:3577 msgid "input profile" msgstr "perfil de entrada" #: f.tools.cc:3581 msgid "output profile" msgstr "perfil de salida" #: f.tools.cc:3602 msgid "Unable to change EXIF color profile" msgstr "No se puede cambiar el perfil de color EXIF" #: f.tools.cc:3604 msgid "automatic new version created" msgstr "nueva versión creada automáticamente" #: f.tools.cc:3613 msgid "color profile" msgstr "perfil de color" #: f.tools.cc:3661 f.tools.cc:3667 #, c-format msgid "unknown cms profile %s" msgstr "perfil cms desconocido %s" #: f.tools.cc:3770 f.widgets.cc:593 msgid "Calibrate Printer" msgstr "Calibrar impresora" #: f.tools.cc:3795 msgid "print color chart" msgstr "imprimir carta de colores" #: f.tools.cc:3796 msgid "scan and save color chart" msgstr "escanear y guardar carta de colores" #: f.tools.cc:3797 msgid "align and trim color chart" msgstr "alinear y recortar carta de colores" #: f.tools.cc:3798 msgid "open and process color chart" msgstr "abrir y procesar carta de colores" #: f.tools.cc:3799 msgid "print image with revised colors" msgstr "imprimir imagen con colores revisados" #: f.tools.cc:3940 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Escanear la carta de color impresa. \n" "La fila más oscura arriba. \n" "Guaradr en %s/" #: f.tools.cc:3955 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Abrir y editar la carta de color escaneada. \n" "Eliminar cualquier giro o rotación del escaneado. \n" "(Usar la función Corregir perspectiva para ésto). \n" "Recortar CUIDADOSAMENTE el delgado margen verde." #: f.tools.cc:3987 msgid "Open the trimmed color chart file" msgstr "Abrir la carta de colores recortada" #: f.tools.cc:4120 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" "Establecer nombre parael archivo de calibración de salida \n" "[su nombre para la calibración].dat" #: f.tools.cc:4160 msgid "Color map file to use" msgstr "Archivo de mapa de color para usar" #: f.tools.cc:4179 msgid "Select the image file to print." msgstr "Seleccionar la imagen a imprimir" #: f.tools.cc:4244 msgid "converting colors..." msgstr "convirtiendo colores ..." #: f.tools.cc:4344 msgid "Image colors are converted for printing." msgstr "Colores de la imagen se han convertido para imprimir" #: f.tools.cc:4467 msgid "This function is for AppImage package only" msgstr "Esta función sólo para paquete Appimage" #: f.tools.cc:4471 msgid "" "Completely remove the AppImage package \n" "Press F1 for more information. \n" "Continue?" msgstr "" "Borrar completamente el paquete Appimagen \n" "Preesionar F1 para más información. \n" "¿Continuar?" #: f.warp.cc:94 f.widgets.cc:515 msgid "Unbend" msgstr "Enderezar" #: f.warp.cc:371 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Pulsar en las cuatro esquinas del área tetragonal. Pulsar [Aplicar]. \n" " La imagen será deformada para hacer que el tetrágono sea un rectángulo." #: f.warp.cc:388 msgid "Perspective Correction" msgstr "Corregir perspectiva" #: f.warp.cc:621 msgid "must have 4 corners" msgstr "debe tener 4 esquinas" #: f.warp.cc:745 msgid "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." msgstr "" " Seleccionar un área para deformar utilizando el botón [Seleccionar]. \n" " Pulsar [Comenzar deformación] y tirar del área con el ratón. \n" " Dar varios tirones con el ratón hasta que quede satisfecho. \n" " Cuando esté terminado, seleccionar otra área o pulsar [Hecho]." #: f.warp.cc:758 f.widgets.cc:517 msgid "Warp area" msgstr "Deformar área" #: f.warp.cc:763 msgid "start warp" msgstr "comenzar deformación" #: f.warp.cc:1149 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" "Usar Seleccionar área para seleccionar una cara. \n" "Clic en el centro de la distorsión. \n" "Mover el deslizador. \n" #: f.warp.cc:1176 f.widgets.cc:518 msgid "Unwarp Closeup" msgstr "Distorsión de acercamiento" #: f.warp.cc:1356 f.warp.cc:1671 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Tirar de una posición en la imagen empleando el ratón. \n" " Dar varios tirones con el ratón hasta que quede satisfecho. \n" " Cuando haya terminado, pulsar [Hecho]." #: f.warp.cc:1374 f.widgets.cc:519 msgid "Warp curved" msgstr "Deformación curva" #: f.warp.cc:1689 f.widgets.cc:520 msgid "Warp linear" msgstr "Deformación lineal" #: f.warp.cc:2005 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Tirar de un borde de la imagen utilizando el ratón. \n" " Dar varios tirones con el ratón hasta que quede satisfecho. \n" " Cuando haya terminado, pulse [Hecho]." #: f.warp.cc:2021 f.widgets.cc:521 msgid "Warp affine" msgstr "Deformación afín" #: f.warp.cc:2368 f.widgets.cc:522 msgid "Flatten Book Page" msgstr "Aplanar página impresa" #: f.warp.cc:2369 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Cortar imagen para aislar una página. \n" "Señalar bordes superiores e inferiores con \n" "más de 4 clics de ratón, después aplanar : " #: f.warp.cc:2372 msgid "Stretch curved-down surfaces:" msgstr "Enderezar superficies curvadas por abajo" #: f.warp.cc:2427 msgid "Top:" msgstr "Arriba:" #: f.warp.cc:2430 msgid "Bottom:" msgstr "Abajo" #: f.warp.cc:2811 f.widgets.cc:523 msgid "Spherical Projection" msgstr "Proyección esférica" #: f.warp.cc:2855 msgid "Magnify" msgstr "Aumentar" #: f.warp.cc:3029 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" "Seleccionar áreas que permanecerán sin cambios.\n" "Arrastrar imagen desde esquina superior izquierda. \n" "Al acabar, presionar [Hecho]." #: f.warp.cc:3045 f.widgets.cc:524 msgid "Selective Rescale" msgstr "Reescalado selectivo" #: f.warp.cc:3068 msgid "select areas first" msgstr "primero seleccione área" #: f.warp.cc:3290 f.widgets.cc:525 msgid "Make Waves" msgstr "Ondas" #: f.warp.cc:3297 msgid "wavelength" msgstr "longitud de onda" #: f.warp.cc:3299 msgid "variance" msgstr "varianza" #: f.warp.cc:3310 msgid "perspective" msgstr "perspectiva" #: f.warp.cc:3484 f.warp.cc:3515 msgid "Twist" msgstr "Girar" #: f.warp.cc:3512 msgid "drag mouse to set center" msgstr "mover ratón para situar centro" #: f.widgets.cc:103 msgid "Album" msgstr "Álbum" #: f.widgets.cc:105 msgid "TOP" msgstr "ARRIBA" #: f.widgets.cc:171 msgid "Current Image File (key F)" msgstr "Archivo de imagen actual (tecla F)" #: f.widgets.cc:172 msgid "Thumbnail Gallery (key G)" msgstr "Galería de mi​niaturas (tecla G)" #: f.widgets.cc:173 msgid "World Maps (key W)" msgstr "Mapa mundial (tecla W)​" #: f.widgets.cc:174 msgid "Net Maps (key M)" msgstr "Mapas en red (tecla M)" #: f.widgets.cc:177 msgid "Favorite Functions" msgstr "Funciones favoritas" #: f.widgets.cc:178 msgid "File: Open, RAW, Rename, Delete, Print" msgstr "Archivo : Abrir, RAW, Renombrar, Borrar, Imprimir" #: f.widgets.cc:179 msgid "Save modified image file to disk" msgstr "Guardar imagen modificada en el disco" #: f.widgets.cc:180 msgid "Open previous or next file (left/right mouse click)" msgstr "Abrir archivo anterior o siguiente ​(clic izquierdo/derecho del ratón)" #: f.widgets.cc:181 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "" "Metadatos: leyendas, Etiquetas, Valoraciones, Geoetiquetas, Búsqueda ..." #: f.widgets.cc:182 msgid "Areas: Select areas to edit, copy and paste" msgstr "Areas: Seleccionar áreas para editar, copiar y pegar" #: f.widgets.cc:183 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "Editar: Recortar, Girar,​ Brillo, Contraste, Texto ..." #: f.widgets.cc:184 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Reparar: Enfocar, Ruido, Ojos rojos, Color, Pintar, Clonar ..." #: f.widgets.cc:185 msgid "Warp: Fix Perspective, Warp or unwarp image ..." msgstr "Deformar: Corregir perspectiva, deformar imagen ..." #: f.widgets.cc:186 msgid "Effects: Special Effects, Arty Transforms" msgstr "Efectos: Efectos especiales, Transformaciones artísticas" #: f.widgets.cc:187 msgid "" "left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "clic izq./der. deshacer/rehacer 1 edición \n" " con la tecla A: deshacer/rehacer todas las ediciones \n" " clic central: ir a una edición anterior" #: f.widgets.cc:190 msgid "Tools: Index, Settings, Shortcuts, Magnify ..." msgstr "Herramientas: índice, ajustes, atajos, aumentar ..." #: f.widgets.cc:191 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Ayuda: Inicio rápido, Guía de usuario, Cambios recientes ..." #: f.widgets.cc:194 msgid "Sync Gallery, Albums, Slide Show" msgstr "Sinctonizar Galería, Álbumes, Diaporama" #: f.widgets.cc:195 msgid "set and recall bookmarked image locations" msgstr "establecer y recuperar ubicaciones de imágenes marcadas" #: f.widgets.cc:196 msgid "increase thumbnail size" msgstr "aumentar tamaño de miniatura" #: f.widgets.cc:197 msgid "reduce thumbnail size" msgstr "reducir tamaño de la miniatura" #: f.widgets.cc:198 msgid "change sort order" msgstr "cambiar criterio de ordenación" #: f.widgets.cc:199 msgid "jump to beginning (top)" msgstr "ir al principio (arriba)" #: f.widgets.cc:200 msgid "jump to end (bottom)" msgstr "ir al final (abajo)" #: f.widgets.cc:201 msgid "previous page" msgstr "página anterior" #: f.widgets.cc:202 msgid "next page" msgstr "página siguiente" #: f.widgets.cc:203 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combinar: HDR, HDF, Panorama, Pila, Montaje" #: f.widgets.cc:204 msgid "Process: convert, export, metadata, search ..." msgstr "Proceso: convertir, exportar, metadatos, buscar ..." #: f.widgets.cc:207 msgid "Choose a map file" msgstr "Elegir un archivo de mapas" #: f.widgets.cc:208 f.widgets.cc:212 msgid "Mark all images or current gallery" msgstr "Marcar todas las imágenes o la galería actual" #: f.widgets.cc:211 msgid "Choose Internet map source" msgstr "Elegir origen del mapa de internet" #: f.widgets.cc:213 msgid "Save and recall named map locations" msgstr "Guardar y recuperar ubicaciones" #: f.widgets.cc:216 msgid "Open another window" msgstr "Abrir otra ventana" #: f.widgets.cc:217 msgid "Open a new image file" msgstr "Abrir nueva imagen" #: f.widgets.cc:218 f.widgets.cc:430 msgid "Cycle 2 Previous Files" msgstr "Ciclo de 2 archivos anteriores" #: f.widgets.cc:219 f.widgets.cc:431 msgid "Cycle 3 Previous Files" msgstr "Ciclo de 3 archivos anteriores" #: f.widgets.cc:220 msgid "Open a recently seen file" msgstr "Abrir archivo recientemente visto" #: f.widgets.cc:221 msgid "Open a newly added file" msgstr "Abrir un archivo añadido nuevo" #: f.widgets.cc:222 msgid "Open and edit a camera RAW file" msgstr "Abrir y editar un archivo de cámara RAW" #: f.widgets.cc:223 msgid "View a 360 degree panorama image file" msgstr "Ver una imagen panorámica de 360 grados" #: f.widgets.cc:224 msgid "Change the image file name" msgstr "Cambiar el nombre de la imagen" #: f.widgets.cc:225 msgid "Copy or Move an image file to a new location" msgstr "Copiar o mover una imagen a una nueva ubicación" #: f.widgets.cc:226 msgid "Copy an image file to the desktop" msgstr "Copiar una imagen al esritorio" #: f.widgets.cc:227 msgid "Copy image file to the clipboard" msgstr "Copiar imagen al portapapeles" #: f.widgets.cc:228 msgid "Copy image file to the image cache" msgstr "Copiuar imagen al caché de imágenes" #: f.widgets.cc:229 msgid "Show location on Interet map" msgstr "Mostrar localizacion en mapa Interet" #: f.widgets.cc:230 msgid "Create a blank image" msgstr "Crear una imagen en blanco" #: f.widgets.cc:231 msgid "toggle - blank or restore window" msgstr "ocultar o restaurar ventana" #: f.widgets.cc:232 msgid "Delete or trash image file" msgstr "Borrar imagen" #: f.widgets.cc:233 msgid "Print the current image" msgstr "Imprimir la imagen actual" #: f.widgets.cc:234 msgid "Print current image with adjusted colors" msgstr "Imprimir imagen actual con los colores ajustados" #: f.widgets.cc:235 f.widgets.cc:445 msgid "Quit Fotoxx" msgstr "Salir de Fotoxx" #: f.widgets.cc:238 msgid "List a few key metadata items" msgstr "Listar algunos datos clave de metadatos" #: f.widgets.cc:239 msgid "List all metadata items" msgstr "Listar todos los items de metadatos" #: f.widgets.cc:240 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Editr etiquetas/geoetiquetas/título/valoración ..." #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Editar cualquier metadato de la imagen" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Eliminar metadatos de las imágenes seleccionadas" #: f.widgets.cc:243 msgid "(Toggle) show captions and comments" msgstr "(Cambiar) muestra leyendas y comentarios" #: f.widgets.cc:246 msgid "Select object or area for editing" msgstr "Seleccionar objeto o área para editar" #: f.widgets.cc:247 msgid "Find a gap in an area outline" msgstr "Encontrar separación en el contorno de área" #: f.widgets.cc:248 msgid "Select hairy or irregular edge" msgstr "Seleccionar borde deshilachado o irregular" #: f.widgets.cc:249 msgid "Show (outline) existing area" msgstr "Mostrar un área existente (contorno)" #: f.widgets.cc:250 msgid "Hide existing area" msgstr "Ocultar áreas existentes" #: f.widgets.cc:251 msgid "Enable area for editing" msgstr "activar áre para editar" #: f.widgets.cc:252 msgid "Disable area for editing" msgstr "Desactivar área para editar" #: f.widgets.cc:253 msgid "Reverse existing area" msgstr "Invertir área existente" #: f.widgets.cc:254 msgid "Erase existing area" msgstr "Borrar área existente" #: f.widgets.cc:255 msgid "Copy area for later pasting into image" msgstr "Copiar área para posterior pegado en una imagen" #: f.widgets.cc:256 msgid "Save area to a file with transparency" msgstr "Guardar área como un archivo con transparencia" #: f.widgets.cc:257 msgid "Open a file and paste as area into image" msgstr "Abrir un archivo y pegar como área en la imagen" #: f.widgets.cc:258 msgid "Paste previously copied area into image" msgstr "Pegar en la imagen àrea previamente copiada" #: f.widgets.cc:261 msgid "Trim/Crop margins and/or Rotate" msgstr "Recortar/cortar márgenes y/o girar" #: f.widgets.cc:262 msgid "Upright a rotated image" msgstr "Enderezar una imagen girada" #: f.widgets.cc:263 msgid "Change pixel dimensions" msgstr "Cambiar dimensiones en píxeles" #: f.widgets.cc:264 f.widgets.cc:265 msgid "Fast auto enhance that may work OK" msgstr "Mejora automática rápida" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Ajustar brillo, contraste, color" #: f.widgets.cc:267 msgid "Edit brightness distribution" msgstr "Editar distribución de brillo" #: f.widgets.cc:268 msgid "Magnify brightness gradients to enhance details" msgstr "Aumentar gradientes de brillo para mejorar detalles" #: f.widgets.cc:269 msgid "Flatten brightness distribution" msgstr "Aplanar histograma" #: f.widgets.cc:270 msgid "Rescale brightness - reduce color caste and fog/haze" msgstr "Reescalar brillo - reduce dominante de color y la neblina" #: f.widgets.cc:271 msgid "Mirror image horizontally or vertically" msgstr "Reflejar horizontal o verticalmente la imagen" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Pintar píxeles de imagen usando el ratón" #: f.widgets.cc:273 msgid "Clone image pixels using the mouse" msgstr "Clonar píxeles de imagen usando el ratón" #: f.widgets.cc:274 msgid "Blend image pixels using the mouse" msgstr "Mezclar píxeles usando el rató" #: f.widgets.cc:277 msgid "Paint edit function gradually with mouse" msgstr "Función de edición pintando gradualmente con el ratón" #: f.widgets.cc:278 msgid "Leverage edits by brightness or color" msgstr "Edición por niveles de brillo o color" #: f.widgets.cc:279 msgid "Edit plugins menu or run a plugin function" msgstr "Editar men​​ú de plugins o ejecutar una función de plugin" #: f.widgets.cc:282 msgid "Make the image look sharper" msgstr "Hacer que la imagen se vea más enfocada" #: f.widgets.cc:283 msgid "Make the image look fuzzy" msgstr "Hacer que la imagen se vea desenfocada" #: f.widgets.cc:284 msgid "Filter noise from low-light photos" msgstr "Filtrar ruido de fotos hechas con poca luz" #: f.widgets.cc:285 msgid "Fix red-eyes from electronic flash" msgstr "Corregir ojos rojos producidos por un flash electrónico" #: f.widgets.cc:286 msgid "Make BW/color, negative/positive, sepia" msgstr "Cambiar blanco y negro/color, negativo/positivo, sepia" #: f.widgets.cc:287 msgid "Reduce color depth (posterize)" msgstr "Reducir profundidad de color (posterizar)" #: f.widgets.cc:288 msgid "Shift/convert colors into other colors" msgstr "Cambiar/convertir colores en otros" #: f.widgets.cc:289 msgid "Adjust color intensity (saturation)" msgstr "Ajustar intensidad de color (saturación)" #: f.widgets.cc:290 msgid "Adjust color using RGB or CMY colors" msgstr "Ajustar colores usando sistema RGB o CMY" #: f.widgets.cc:291 msgid "Adjust color using HSL colors" msgstr "Ajustar color usando sistema HSL" #: f.widgets.cc:292 msgid "Adjust color in selected image areas" msgstr "Ajustar color en áreas seleccionadas de la imagen" #: f.widgets.cc:293 msgid "Match colors on one image with another" msgstr "Concordar colores de una imagen con los de otra" #: f.widgets.cc:294 msgid "Reduce Chromatic Abberation" msgstr "Reducir aberración cromática" #: f.widgets.cc:295 msgid "Remove unwanted objects" msgstr "Eliminar objetos no deseados" #: f.widgets.cc:296 msgid "Add a brightness/color ramp across the image" msgstr "Añadir una rampa de brillo/color a la imagen" #: f.widgets.cc:297 msgid "Remove dust spots from scanned slides" msgstr "Eliminar motas de polvo de diapositivas escaneadas" #: f.widgets.cc:298 msgid "Smoothen edges with jaggies" msgstr "Suavizar bordes con dientes de sierra" #: f.widgets.cc:299 msgid "Erase known hot and dark pixels" msgstr "Eliminar píxeles calientes y negros conocidos" #: f.widgets.cc:300 msgid "Paint image transparency using the mouse" msgstr "Pintar transparencia de la imagen con el ratón" #: f.widgets.cc:301 msgid "Add image transparency based on image attributes" msgstr "Añadir transparencia a la imagen basada en los atributos de la imagen" #: f.widgets.cc:304 msgid "Remove curvature, esp. panoramas" msgstr "Eliminar curvatura, esp. panoramas" #: f.widgets.cc:305 msgid "Straighten objects seen from an angle" msgstr "Enderezar objetos vistos desde un ángulo" #: f.widgets.cc:306 msgid "Distort image areas using the mouse" msgstr "Deformar áreas de la imagen usando el ratón" #: f.widgets.cc:307 msgid "Unwarp closeup face photo to remove distortion" msgstr "" "Eliminatr deformación de acercamiento en un retrato para eliminar distorsión" #: f.widgets.cc:308 f.widgets.cc:309 f.widgets.cc:310 msgid "Distort the whole image using the mouse" msgstr "Deformar toda la imagen usando el ratón" #: f.widgets.cc:311 msgid "Flatten a photographed book page" msgstr "Aplanar foto de una página impresa " #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "Hacer una proyección esférica de una imagen" #: f.widgets.cc:313 msgid "Rescale image outside selected areas" msgstr "Reescalar imagen por fuera de las áreas seleccionadas" #: f.widgets.cc:314 msgid "Warp an image with a wave pattern" msgstr "Deformar una imagencon un patrón de ondas" #: f.widgets.cc:315 msgid "Twist image centered at mouse position" msgstr "Girar la imagen centrada en la posición del ratón" #: f.widgets.cc:318 msgid "Convert to simulated sketch" msgstr "Convertir a simulación de boceto" #: f.widgets.cc:319 msgid "Convert image into a cartoon drawing" msgstr "Convertir la imagen en un dibujo a tintas planas" #: f.widgets.cc:320 msgid "Convert to line drawing (edge detection)" msgstr "Convertir a un dibujo de líneas (detección de bordes)" #: f.widgets.cc:321 msgid "Convert to solid color drawing" msgstr "Convertir a dibujo de colores sólidos" #: f.widgets.cc:322 msgid "Graduated Blur depending on contrast" msgstr "Desenfoque graduado dependiendo del contraste" #: f.widgets.cc:323 msgid "Create an embossed or 3D appearance" msgstr "Crear un relieve o apariencia 3D" #: f.widgets.cc:324 msgid "Convert to square tiles" msgstr "Convertir a baldosas cuadradas" #: f.widgets.cc:325 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Convertir a puntos (efecto Roy Lichtenstein)" #: f.widgets.cc:326 msgid "Convert into a simulated painting" msgstr "Convertir a pintura simulada" #: f.widgets.cc:327 msgid "Change brightness or color radially" msgstr "Cambiar radialmente el brillo o el color" #: f.widgets.cc:328 msgid "Add texture to an image" msgstr "Añadir textura a una imagen" #: f.widgets.cc:329 msgid "Tile image with a repeating pattern" msgstr "Embaldosar imagen con un patrón repetitivo" #: f.widgets.cc:330 msgid "Create a mosaic with tiles made from all images" msgstr "Crear un mosaico con baldosas hechas de todas las imágenes" #: f.widgets.cc:331 msgid "Process an image using a custom kernel" msgstr "Procesar una imagen usando una matriz personalizada" #: f.widgets.cc:332 msgid "Blur an image in the direction of mouse drags" msgstr "Desenfocar una imagen en la dirección del movimiento del ratón" #: f.widgets.cc:333 msgid "Increasing blur with distance from selected areas" msgstr "Incrementar desenfoque con distancia desde áreas seleccionadas" #: f.widgets.cc:334 msgid "Change color hue using an algorithm" msgstr "Cambiar tono de color usando un algoritmo" #: f.widgets.cc:337 msgid "Combine bright/dark images for better detail" msgstr "Combinar imágenes brillantes/oscuras para mejorar detalles" #: f.widgets.cc:338 msgid "Combine near/far focus images for deeper focus" msgstr "" "Combinar imágenes enfocadas cerca/lejos para mayor profundidad de campo" #: f.widgets.cc:339 msgid "Combine images to erase passing people, etc." msgstr "Combinar imágenes para borrar gente, coches, etc" #: f.widgets.cc:340 msgid "Combine noisy images into a low-noise image" msgstr "Combinaar imágenes ruidosas en una imagen de bajo ruido" #: f.widgets.cc:341 msgid "Combine images into a panorama" msgstr "Combinar imágenes en una panorámica" #: f.widgets.cc:342 msgid "Combine images into a vertical panorama" msgstr "Combinar imágenes en un panorama vertical" #: f.widgets.cc:343 msgid "Combine images into a panorama (panorama tools)" msgstr "Combinar imágenes en una panorámica (panorama tools)" #: f.widgets.cc:344 msgid "Arrange images and text in a layout (montage)" msgstr "Ordenar imágenes y texto en una capa (montaje)" #: f.widgets.cc:345 msgid "Combine images into a montage of images" msgstr "Combinar imágenes en un montaje" #: f.widgets.cc:348 msgid "Rename/convert/resize/move multiple files" msgstr "Renombrar/convertir/redimensionar/mover múltiples archivos" #: f.widgets.cc:349 msgid "Upright multiple rotated image files" msgstr "Enderezar múltiples imagenes giradas" #: f.widgets.cc:350 msgid "Delete or Trash multiple files" msgstr "Borrar o eliminar múltiples imágenes" #: f.widgets.cc:351 msgid "Convert camera RAW files using libraw or Raw Therapee" msgstr "Convertir archivos RAW usando libraw o Raw Therapee" #: f.widgets.cc:352 msgid "Build and run edit script files" msgstr "Escribir y ejecutar scripts editados" #: f.widgets.cc:353 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Grabar las imágenes seleccionadas en un DVD o disco BlueRay" #: f.widgets.cc:354 msgid "Search all image files and report duplicates" msgstr "Buscar en todas las imágenes e informar de duplicados" #: f.widgets.cc:356 msgid "Export selected image files to a directory" msgstr "Exportar imágenes seleccionadas a un directorio" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Añadir/eliminar etiquetas para múltiples imágenes " #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Convertir nombres de etiquetas para todas las imágenes" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "cambiar fechas/horas de las fotos" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Añadir/cambiar/borrar metadatos en múltiples imágenes" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Informe de metadatos para múltiples imágenes" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Añadir/revisar geoetiquetas en múltiples imágenes" #: f.widgets.cc:363 msgid "Find all images for a location [date]" msgstr "Encontrar todas las imágenes para una ubicación (fecha)" #: f.widgets.cc:364 msgid "Show image counts by month, select and report" msgstr "Mostrar cuenta de imágenes por meses, seleccionar e informar" #: f.widgets.cc:365 msgid "Find images meeting select criteria" msgstr "Encontrar imágenes que cumplan un criterio de selección" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Indexar nuevos archivos y crear miniaturas" #: f.widgets.cc:369 msgid "Change user preferences" msgstr "Cambiar preferencias del usuario" #: f.widgets.cc:370 msgid "Change Keyboard Shortcut Keys" msgstr "Canmbiar teclas de atajos de teclado" #: f.widgets.cc:371 msgid "Show a brightness distribution graph" msgstr "Mostrar histograma (distribución del brillo)" #: f.widgets.cc:372 msgid "Show or revise grid lines" msgstr "Mostrar o revisar líneas de rejilla" #: f.widgets.cc:373 msgid "Change color of foreground lines" msgstr "Cambiar color de líneas de contorno" #: f.widgets.cc:374 msgid "Show RGB colors at mouse click" msgstr "Mostrar colores RGB con un clic del ratón" #: f.widgets.cc:375 msgid "Magnify image around the mouse position" msgstr "Aumenta la imagen alrededor de la posición del ratón" #: f.widgets.cc:376 msgid "Highlight darkest and brightest pixels" msgstr "Resaltar píxeles más brillantes o más oscuros" #: f.widgets.cc:377 msgid "Chart to adjust monitor color" msgstr "Carta para ajustar el color del monitor" #: f.widgets.cc:378 msgid "Chart to adjust monitor gamma" msgstr "Carta para ajustar la gamma del monitor" #: f.widgets.cc:379 msgid "Change the GUI language" msgstr "cambiar el idioma de la interfaz de usuario" #: f.widgets.cc:380 msgid "Report missing translations" msgstr "Informe de traducciones que faltan" #: f.widgets.cc:381 msgid "Convert to another color profile" msgstr "Convertir a otro perfil de color" #: f.widgets.cc:382 msgid "Calibrate printer colors" msgstr "Calibrar colores de la impresora" #: f.widgets.cc:383 msgid "Completely uninstall AppImage package" msgstr "Desinstar completamente paquete Appimage" #: f.widgets.cc:384 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memoria y CPU (al terminal/archivo de registro)" #: f.widgets.cc:387 msgid "Quick Start mini-guide" msgstr "Mini guía de referencia rápida" #: f.widgets.cc:388 msgid "Read the user guide" msgstr "Leer la guía de usuario" #: f.widgets.cc:389 msgid "Recent user guide changes" msgstr "Cambios recientes en la guia de usuario" #: f.widgets.cc:390 msgid "Technical installation notes" msgstr "Notas técnicas de instalación" #: f.widgets.cc:391 msgid "List updates by Fotoxx version" msgstr "Listar actualizaciones por versión de Fotoxx" #: f.widgets.cc:392 msgid "View the log file and error messages" msgstr "Ver el archivo de registro y mensajes de error" #: f.widgets.cc:393 msgid "How to do Fotoxx translations" msgstr "Cómo hacer traducciones de Fotoxx" #: f.widgets.cc:394 msgid "Show the Fotoxx web page" msgstr "Muestra la página web de Fotoxx" #: f.widgets.cc:395 msgid "Version, license, contact, credits" msgstr "Versión, licencia, contacto, créditos" #: f.widgets.cc:398 msgid "list all directories, click any for gallery view" msgstr "listar todos los directorios, clic en uno para vista de galería" #: f.widgets.cc:399 msgid "Set gallery from current image file" msgstr "Establecer galería desde la imagen actual" #: f.widgets.cc:400 msgid "Organize images into albums" msgstr "Organizar imágenes en álbumes" #: f.widgets.cc:401 msgid "Update album files to latest version" msgstr "Actualizar archivos de álbum a la última versión" #: f.widgets.cc:402 msgid "Replace album file with another file" msgstr "Reemplazar archivo de álbum con otro archivo" #: f.widgets.cc:403 msgid "Start a slide show" msgstr "Iniciar un diaporama" #: f.widgets.cc:425 msgid "New Window" msgstr "Nueva ventana" #: f.widgets.cc:426 f.widgets.cc:610 fotoxx-18.01.cc:1821 msgid "Sync Gallery" msgstr "Sincronizar galería" #: f.widgets.cc:427 msgid "Recently Seen Images" msgstr "Imágenes vistas recientemente " #: f.widgets.cc:428 msgid "Newest Images" msgstr "Imágenes más recientes" #: f.widgets.cc:433 msgid "View 360° Panorama" msgstr "Ver panorama 360º" #: f.widgets.cc:434 msgid "New Blank Image" msgstr "Nueva imagen en blanco" #: f.widgets.cc:437 f.widgets.cc:758 msgid "Copy/Move to Location" msgstr "Copiar/mover a una ubicación" #: f.widgets.cc:438 f.widgets.cc:759 msgid "Copy to Desktop" msgstr "Copiar al escritorio" #: f.widgets.cc:439 f.widgets.cc:760 msgid "Copy to Clipboard" msgstr "Copiar al portapapeles" #: f.widgets.cc:440 f.widgets.cc:764 msgid "Copy to Image Cache" msgstr "Copiar al caché de imagen " #: f.widgets.cc:441 f.widgets.cc:778 msgid "Show on Net Map" msgstr "Mostrar en mapa de red" #: f.widgets.cc:443 msgid "Print Image" msgstr "Imprimir imagen" #: f.widgets.cc:444 msgid "Print Calibrated Image" msgstr "Imprimir imagen calibrada" #: f.widgets.cc:448 msgid "View Metadata (short)" msgstr "Ver metadatos (corto)" #: f.widgets.cc:449 msgid "View Metadata (long)" msgstr "Ver metadatos (largo)" #: f.widgets.cc:453 msgid "Show Captions on Image" msgstr "Mostrar leyendas en la imagen" #: f.widgets.cc:456 f.widgets.cc:776 msgid "Select Area" msgstr "Seleccionar área" #: f.widgets.cc:457 msgid "Find Area Gap" msgstr "Encontrar separación del área" #: f.widgets.cc:459 msgid "Show Area" msgstr "Mostrar área" #: f.widgets.cc:460 msgid "Hide Area" msgstr "Ocultar área" #: f.widgets.cc:461 msgid "Enable Area" msgstr "Habilitar área" #: f.widgets.cc:462 msgid "Disable Area" msgstr "Deshabiulitar área" #: f.widgets.cc:463 msgid "Invert Area" msgstr "Invertitr área" #: f.widgets.cc:464 msgid "Unselect Area" msgstr "Deseleccionar área" #: f.widgets.cc:466 msgid "Paste Area" msgstr "Pegar área" #: f.widgets.cc:467 msgid "Open Area File" msgstr "Abrir archivo de área" #: f.widgets.cc:474 msgid "Voodoo 1" msgstr "Automejora 1" #: f.widgets.cc:475 msgid "Voodoo 2" msgstr "Automejora 2" #: f.widgets.cc:477 f.widgets.cc:773 msgid "Edit Brightness" msgstr "Editar brillo" #: f.widgets.cc:478 f.widgets.cc:775 msgid "Gradients" msgstr "Gradientes" #: f.widgets.cc:482 msgid "Paint Image" msgstr "Pintar en la imagen" #: f.widgets.cc:489 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:494 msgid "Denoise" msgstr "Reducir ruido" #: f.widgets.cc:495 msgid "Red Eyes" msgstr "Ojos rojos" #: f.widgets.cc:497 msgid "Color Depth" msgstr "Profundidad de color" #: f.widgets.cc:500 msgid "Adjust RGB/CMY" msgstr "Ajustar RGB/CMY" #: f.widgets.cc:501 msgid "Adjust HSL" msgstr "Ajustar HSL" #: f.widgets.cc:502 msgid "Zonal Colors" msgstr "Colores zonales" #: f.widgets.cc:503 msgid "Match Colors" msgstr "Concordar colores" #: f.widgets.cc:509 msgid "Anti-Alias" msgstr "Suavizar bordes" #: f.widgets.cc:516 msgid "Fix Perspective" msgstr "Corregir perspectiva" #: f.widgets.cc:526 msgid "Twist Image" msgstr "Girar imagen" #: f.widgets.cc:529 msgid "Sketch" msgstr "Esbozo" #: f.widgets.cc:530 msgid "Cartoon" msgstr "Tintas planas" #: f.widgets.cc:534 msgid "Embossing" msgstr "Relieve" #: f.widgets.cc:536 msgid "Dots" msgstr "Puntos" #: f.widgets.cc:537 msgid "Painting" msgstr "Pintura" #: f.widgets.cc:539 msgid "Texture" msgstr "Textura" #: f.widgets.cc:541 msgid "Mosaic" msgstr "Mosaico" #: f.widgets.cc:548 msgid "High Dynamic Range" msgstr "HDR alto rango dinámico" #: f.widgets.cc:549 msgid "High Depth of Field" msgstr "HDF alta profundidad de campo" #: f.widgets.cc:550 msgid "Stack/Paint" msgstr "Apilar/pintar" #: f.widgets.cc:551 msgid "Stack/Noise" msgstr "Apilar/ruido" #: f.widgets.cc:552 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:553 msgid "Vertical Panorama" msgstr "Panorama vertical " #: f.widgets.cc:554 msgid "PT Panorama" msgstr "PT panorama" #: f.widgets.cc:555 msgid "Montage" msgstr "Montaje" #: f.widgets.cc:556 msgid "Mashup" msgstr "Fotomontaje" #: f.widgets.cc:562 msgid "Batch RAW" msgstr "Convertir RAW en lote" #: f.widgets.cc:563 msgid "Script Files" msgstr "Archivo de comandos" #: f.widgets.cc:566 msgid "Export File List" msgstr "Exportar lista de archivos" #: f.widgets.cc:574 msgid "Image Locations/Dates" msgstr "Localizaciones/fechas de imágenes" #: f.widgets.cc:575 msgid "Image Timeline" msgstr "Linea de tiempo de imágenes" #: f.widgets.cc:576 msgid "Search Images" msgstr "Buscar imágenes" #: f.widgets.cc:582 msgid "Brightness Graph" msgstr "Gráfico de brillo" #: f.widgets.cc:587 msgid "Dark/Bright Pixels" msgstr "Sobre/subexposición" #: f.widgets.cc:588 msgid "Monitor Color" msgstr "Color del monitor" #: f.widgets.cc:590 msgid "Change Language" msgstr "Cambiar idioma" #: f.widgets.cc:591 msgid "Missing Translations" msgstr "Traducciones que faltan" #: f.widgets.cc:592 msgid "Color Profile" msgstr "perfil de color" #: f.widgets.cc:594 msgid "Uninstall AppImage" msgstr "Desinstalar Appimage" #: f.widgets.cc:611 msgid "All Directories" msgstr "Todos los directorios" #: f.widgets.cc:612 msgid "Bookmarks" msgstr "Marcas" #: f.widgets.cc:621 f.widgets.cc:646 f.widgets.cc:670 f.widgets.cc:683 msgid "Gallery" msgstr "Galeria" #: f.widgets.cc:622 f.widgets.cc:647 f.widgets.cc:671 f.widgets.cc:684 msgid "World Maps" msgstr "Mapas mundiales" #: f.widgets.cc:623 f.widgets.cc:648 f.widgets.cc:672 f.widgets.cc:685 msgid "Net Maps" msgstr "Mapas de la red" #: f.widgets.cc:625 f.widgets.cc:650 f.widgets.cc:1066 msgid "Favorites" msgstr "Favoritos" #: f.widgets.cc:627 msgid "Save File" msgstr "Guardar archivo" #: f.widgets.cc:628 msgid "Prev/Next" msgstr "Ant/Sig" #: f.widgets.cc:630 msgid "Areas" msgstr "Areas" #: f.widgets.cc:631 msgid "Undo/Redo" msgstr "Desh/Reh" #: f.widgets.cc:632 fotoxx.h:1208 msgid "Edit" msgstr "Editar" #: f.widgets.cc:633 msgid "Repair" msgstr "Reparar" #: f.widgets.cc:635 msgid "Effects" msgstr "Efectos" #: f.widgets.cc:636 f.widgets.cc:660 msgid "Combine" msgstr "Combinar" #: f.widgets.cc:637 f.widgets.cc:661 msgid "Process" msgstr "Proceso" #: f.widgets.cc:638 f.widgets.cc:662 msgid "Tools" msgstr "Herramientas" #: f.widgets.cc:674 msgid "Choose Map" msgstr "Elegir mapa" #: f.widgets.cc:675 f.widgets.cc:688 msgid "Map Markers" msgstr "Marcadores de mapa" #: f.widgets.cc:687 msgid "Map Source" msgstr "Fuente de mapa" #: f.widgets.cc:689 msgid "Map Locations" msgstr "Ubicaciones de mapa" #: f.widgets.cc:753 msgid "Popup Image" msgstr "Imagen emergente" #: f.widgets.cc:757 fotoxx.h:1265 msgid "Rename" msgstr "Renombrar" #: f.widgets.cc:761 msgid "Remove from Album" msgstr "Eliminar del álbum" #: f.widgets.cc:763 msgid "Cut to Image Cache" msgstr " Cortar al caché de imagen" #: f.widgets.cc:765 msgid "Paste Image Cache Here (clear)" msgstr "Pegar caché de imagen aquí (borrar)" #: f.widgets.cc:766 msgid "Paste Image Cache Here (keep)" msgstr "Pegar caché de imagen aquí (guardar)" #: f.widgets.cc:779 msgid "Delete/Trash ..." msgstr "Eliminar/borrar ..." #: f.widgets.cc:1092 msgid "menu name is invalid" msgstr "el nombre de menú no es válido" #: fotoxx-18.01.cc:434 msgid "Please install missing programs:" msgstr "Por favor instalar los programas necesarios:" #: fotoxx-18.01.cc:706 #, c-format msgid "Kill active dialog? %s" msgstr "¿Matar diálogo activo? %s" #: fotoxx-18.01.cc:801 msgid "(reduced)" msgstr "(reducido)" #: fotoxx-18.01.cc:802 msgid "area active" msgstr "área activa" #: fotoxx-18.01.cc:803 msgid "dialog open" msgstr "diálogo abierto" #: fotoxx-18.01.cc:804 msgid "blocked" msgstr "bloqueado" #: fotoxx-18.01.cc:866 msgid "edits" msgstr "ediciones" #: fotoxx-18.01.cc:1801 msgid "File View" msgstr "Vista de archivo" #: fotoxx-18.01.cc:1806 msgid "Gallery View" msgstr "Vista de galería" #: fotoxx-18.01.cc:1811 msgid "World Map View" msgstr "Vista mapa mundial" #: fotoxx-18.01.cc:1816 msgid "Net Map View" msgstr "Vista de mnapa en red" #: fotoxx-18.01.cc:1831 msgid "Show Hidden" msgstr "Mostrar ocultos" #: fotoxx-18.01.cc:3023 msgid "Exceed 50 anchor points" msgstr "Excedidos 50 puntos de anclaje" #: fotoxx-18.01.cc:3250 msgid "load curve from a file" msgstr "Cargar curva desde un archivo" #: fotoxx-18.01.cc:3316 msgid "curve file is invalid" msgstr "Archivo de curva no válido" #: fotoxx-18.01.cc:3330 msgid "save curve to a file" msgstr "Guardar la curva como un archivo" #: fotoxx-18.01.cc:3399 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "El archivo no puede ser editado \n" " %s" #: fotoxx-18.01.cc:3412 msgid "Too many edits, please save image" msgstr "Demasiadas ediciones, por favcor guarde la imagen" #: fotoxx-18.01.cc:3417 msgid "this function cannot be scripted" msgstr "esta función no puede ser guionada" #: fotoxx-18.01.cc:3434 msgid "" "Select area not active.\n" "Continue?" msgstr "" "La selección de área no está activada.\n" "¿Continuar?" #: fotoxx-18.01.cc:3749 msgid "file data does not fit dialog" msgstr "los datos del archivo no se ajustan al diálogo" #: fotoxx-18.01.cc:3759 msgid "Save settings to a file" msgstr "Guardar ajustes en un archivo" #: fotoxx-18.01.cc:4140 msgid "This action will discard changes to current image" msgstr "Esta acción descartará los cambios a la imagen actual" #: fotoxx-18.01.cc:4141 msgid "prior function still active" msgstr "función anterior todavía activa" #: fotoxx-18.01.cc:4142 fotoxx.h:1229 msgid "Keep" msgstr "Guardar" #: fotoxx-18.01.cc:4143 msgid "Discard" msgstr "Descartar" #: fotoxx.h:1177 msgid "Add" msgstr "Añadir" #: fotoxx.h:1178 msgid "Add All" msgstr "Añadir todo" #: fotoxx.h:1180 msgid "Amount" msgstr "Cantidad" #: fotoxx.h:1181 msgid "Angle" msgstr "Ángulo" #: fotoxx.h:1182 zfuncs.cc:6782 msgid "Apply" msgstr "Aplicar" #: fotoxx.h:1183 msgid "Auto" msgstr "Auto" #: fotoxx.h:1184 msgid "Black" msgstr "Negro" #: fotoxx.h:1185 msgid "Blend Width" msgstr "Ancho de mezclado" #: fotoxx.h:1187 zfuncs.cc:11368 msgid "Bottom" msgstr "Abajo" #: fotoxx.h:1189 zfuncs.cc:6796 msgid "Browse" msgstr "Examinar" #: fotoxx.h:1190 zfuncs.cc:6782 msgid "Cancel" msgstr "Cancelar" #: fotoxx.h:1191 msgid "Center" msgstr "centro" #: fotoxx.h:1192 msgid "Choose" msgstr "Elegir" #: fotoxx.h:1193 msgid "Clear" msgstr "Limpiar" #: fotoxx.h:1194 msgid "Close" msgstr "Cerrar" #: fotoxx.h:1195 msgid "Color" msgstr "Color" #: fotoxx.h:1196 msgid "COMPLETED" msgstr "COMPLETADO" #: fotoxx.h:1197 msgid "continue" msgstr "continuar" #: fotoxx.h:1199 msgid "Copy" msgstr "Copiar" #: fotoxx.h:1200 msgid "Create" msgstr "Crear" #: fotoxx.h:1201 msgid "Curve File:" msgstr "Archivo de curva:" #: fotoxx.h:1202 msgid "Cut" msgstr "Cortar" #: fotoxx.h:1203 msgid "Deband" msgstr "Eliminar bandas" #: fotoxx.h:1204 zfuncs.cc:6782 msgid "Delete" msgstr "Borrar" #: fotoxx.h:1205 msgid "Disable" msgstr "Desactivar" #: fotoxx.h:1206 msgid "Done" msgstr "Hecho" #: fotoxx.h:1207 msgid "edge" msgstr "borde" #: fotoxx.h:1209 msgid "Enable" msgstr "Activar" #: fotoxx.h:1210 msgid "Erase" msgstr "Borrar" #: fotoxx.h:1211 msgid "Fetch" msgstr "Extraer" #: fotoxx.h:1212 msgid "output file already exists" msgstr "el archivo de salida ya existe" #: fotoxx.h:1213 msgid "file not found" msgstr "archivo no encontrado" #: fotoxx.h:1214 #, c-format msgid "file not found: %s" msgstr "archivo no encontrado: %s" #: fotoxx.h:1215 #, c-format msgid "%d image files selected" msgstr "% imágenes seleccionadas" #: fotoxx.h:1216 msgid "Find" msgstr "Encontrar" #: fotoxx.h:1217 msgid "Finish" msgstr "Terminar" #: fotoxx.h:1219 msgid "Font" msgstr "Tipo de letra" #: fotoxx.h:1221 msgid "Grid" msgstr "Rejilla" #: fotoxx.h:1222 zfuncs.cc:11398 msgid "Height" msgstr "Alto" #: fotoxx.h:1224 msgid "Hide" msgstr "Ocultar" #: fotoxx.h:1225 zfuncs.cc:11390 msgid "Image" msgstr "Imagen" #: fotoxx.h:1226 msgid "Images" msgstr " Imágenes" #: fotoxx.h:1227 msgid "Insert" msgstr "Insertar" #: fotoxx.h:1228 msgid "Invert" msgstr "Invertir" #: fotoxx.h:1230 zfuncs.cc:11372 msgid "Left" msgstr "Izquierda" #: fotoxx.h:1231 msgid "limit" msgstr "límite" #: fotoxx.h:1232 msgid "Load" msgstr "Cargar" #: fotoxx.h:1233 msgid "Make" msgstr "Hacer" #: fotoxx.h:1235 msgid "Map" msgstr "Mapa" #: fotoxx.h:1236 msgid "Match Level:" msgstr "Nivel de coincidencia:" #: fotoxx.h:1237 msgid "Max" msgstr "Máx" #: fotoxx.h:1238 msgid "mouse radius" msgstr "radio del cursor" #: fotoxx.h:1239 msgid "Negative" msgstr "Negativo" #: fotoxx.h:1240 msgid "New" msgstr "Nuevo" #: fotoxx.h:1241 msgid "Next" msgstr "Siguiente" #: fotoxx.h:1242 zfuncs.cc:10365 msgid "No" msgstr "No" #: fotoxx.h:1243 msgid "no images" msgstr "sin imágenes" #: fotoxx.h:1244 msgid "" "image index disabled \n" " Enable?" msgstr "" "índice de imagen deshabilitdo \n" " habilitado?" #: fotoxx.h:1245 msgid "no image files selected" msgstr "no hay imágenes seleccionadas" #: fotoxx.h:1246 msgid "None" msgstr "Ninguno" #: fotoxx.h:1247 msgid "no selection" msgstr "sin selección" #: fotoxx.h:1248 msgid "OK" msgstr "OK" #: fotoxx.h:1249 msgid "image index not updated" msgstr "índice de imágenes no actualizado" #: fotoxx.h:1251 msgid "Paint Radius" msgstr "Radio a pintar" #: fotoxx.h:1252 msgid "Paste" msgstr "Pegar" #: fotoxx.h:1253 msgid "Pause" msgstr "Pausa" #: fotoxx.h:1254 msgid "Percent" msgstr "Porcentaje" #: fotoxx.h:1256 msgid "Presets" msgstr "Predefinidos" #: fotoxx.h:1257 msgid "Prev" msgstr "Anterior" #: fotoxx.h:1258 msgid "Proceed" msgstr "Proceder" #: fotoxx.h:1260 msgid "range" msgstr "rango" #: fotoxx.h:1262 msgid "Redo" msgstr "Rehacer" #: fotoxx.h:1263 msgid "Reduce" msgstr "Reducir" #: fotoxx.h:1264 msgid "Remove" msgstr "Eliminar" #: fotoxx.h:1266 msgid "Replace" msgstr "Reemplazar" #: fotoxx.h:1267 msgid "Reserved" msgstr "Reservado" #: fotoxx.h:1268 msgid "Reset" msgstr "Restablecer" #: fotoxx.h:1269 zfuncs.cc:11376 msgid "Right" msgstr "Derecha" #: fotoxx.h:1270 msgid "Rotate" msgstr "Rotar" #: fotoxx.h:1271 msgid "Run" msgstr "Ejecutar" #: fotoxx.h:1272 msgid "Save" msgstr "Guardar" #: fotoxx.h:1273 msgid "Search" msgstr "Buscar" #: fotoxx.h:1274 msgid "Seconds" msgstr "Segundos" #: fotoxx.h:1275 msgid "Select" msgstr "Seleccionar" #: fotoxx.h:1277 msgid "Show" msgstr "Mostrar" #: fotoxx.h:1278 msgid "Size" msgstr "Tamaño" #: fotoxx.h:1279 msgid "Start" msgstr "Comenzar" #: fotoxx.h:1280 msgid "Stop" msgstr "Parar" #: fotoxx.h:1281 msgid "Strength" msgstr "Fuerza" #: fotoxx.h:1282 msgid "Threshold" msgstr "Umbral" #: fotoxx.h:1283 #, c-format msgid "exceed %d files" msgstr "se excedió en %d archivos" #: fotoxx.h:1284 zfuncs.cc:11364 msgid "Top" msgstr "Arriba" #: fotoxx.h:1286 msgid "Trash" msgstr "Papelera" #: fotoxx.h:1287 msgid "Trim" msgstr "Recortar" #: fotoxx.h:1288 msgid "Undo All" msgstr "Deshacer todo" #: fotoxx.h:1289 msgid "Undo Last" msgstr "Deshacer el último" #: fotoxx.h:1290 msgid "Undo" msgstr "Deshacer" #: fotoxx.h:1291 msgid "Unfinish" msgstr "Sin cerrar" #: fotoxx.h:1292 msgid "Unselect" msgstr "Deseleccionar" #: fotoxx.h:1293 msgid "View" msgstr "Ver" #: fotoxx.h:1294 msgid "Web" msgstr "Web" #: fotoxx.h:1295 msgid "White" msgstr "Blanco" #: fotoxx.h:1296 zfuncs.cc:11394 msgid "Width" msgstr "Ancho" #: fotoxx.h:1297 msgid "x-offset" msgstr "desplazamiento X" #: fotoxx.h:1298 msgid "y-offset" msgstr "desplazamiento Y" #: fotoxx.h:1299 zfuncs.cc:10365 msgid "Yes" msgstr "Sí" #: zfuncs.cc:305 msgid "LOW MEMORY - performance may be poor" msgstr "MEMORIA BAJA - el rendimiento puede ser bajo" #: zfuncs.cc:1722 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "¿Crear directorio? \n" " %s" #: zfuncs.cc:4878 msgid "user guide not found" msgstr "guia de usuario no encontrada" #: zfuncs.cc:5742 #, c-format msgid "cannot open file %s" msgstr "No puedo abrir el archivo %s" #: zfuncs.cc:5765 msgid "save text to file" msgstr "guardar texto en archivo" #: zfuncs.cc:6786 msgid "menu text" msgstr "texto del menú" #: zfuncs.cc:6787 msgid "menu func" msgstr "función del menú" #: zfuncs.cc:6788 msgid "menu icon" msgstr "icono del menú" #: zfuncs.cc:6789 msgid "icon size" msgstr "tamaño de icono" #: zfuncs.cc:6792 msgid "Bold" msgstr "Negrita" #: zfuncs.cc:6799 msgid "close window" msgstr "cerrar ventana" #: zfuncs.cc:10488 zfuncs.cc:11021 zfuncs.cc:11351 msgid "cancel" msgstr "cancelar" #: zfuncs.cc:10982 msgid "choose file" msgstr "elegir archivo" #: zfuncs.cc:10987 msgid "choose files" msgstr "elegir archivos" #: zfuncs.cc:10992 msgid "save" msgstr "guardar" #: zfuncs.cc:10998 msgid "choose folder" msgstr "elegir carpeta" #: zfuncs.cc:11003 msgid "choose folders" msgstr "elegir carpetas" #: zfuncs.cc:11008 msgid "create folder" msgstr "crear carpeta" #: zfuncs.cc:11015 msgid "hidden" msgstr "ocultar" #: zfuncs.cc:11351 msgid "done" msgstr "hecho" #: zfuncs.cc:11381 msgid "image scale" msgstr "escalar imagen" #: zfuncs.cc:11383 msgid "percent" msgstr "porcentual" #~ msgid "Select images to remove" #~ msgstr "Seleccionar imágenes a eliminar" #~ msgid "remove %d album files?" #~ msgstr "¿eliminar %d álbumes?" #~ msgid "KB functions" #~ msgstr "Funciones de teclado" #~ msgid "both" #~ msgstr "ambos" #~ msgid "Click for white balance or black level" #~ msgstr "Clic para balance de blancos o punto negro" #~ msgid "" #~ "check the box, then click on white or dark \n" #~ "area to calibrate image white or black color" #~ msgstr "" #~ "chequear la caja, después clic en área blanca o negra \n" #~ "para calibrar el color blanco o negro en la imagen" #~ msgid "Zonal Flatten Brightness" #~ msgstr "Aplanar brillo zonal" #~ msgid "Tone Mapping" #~ msgstr "Mapeo de tonos" #~ msgid "Flip" #~ msgstr "Voltear" #~ msgid "Write Text on Image" #~ msgstr "Escribir un texto sobre la imagen" #~ msgid "Write Line or Arrow on Image" #~ msgstr "Dibujar línea o flecha en la imagen" #~ msgid "Row↑" #~ msgstr "Fila ↑" #~ msgid "Row↓" #~ msgstr "Fila ↓" #~ msgid "Page↑" #~ msgstr "Página ↑" #~ msgid "Page↓" #~ msgstr "Página ↓" #~ msgid "a sorted album cannot be edited" #~ msgstr "un álbum ordenado no se puede editar" #~ msgid "Click list position. Click thumbnail to add." #~ msgstr "Clic en una posición de la lista. Clic en la miniatura para añadir" #~ msgid "(yyyymmdd)" #~ msgstr "(aaaammdd)" #~ msgid "Batch Convert RAW Files" #~ msgstr "Convertir en lote archivos RAW" #~ msgid "Images:" #~ msgstr "Imágenes:" #~ msgid "searching ..." #~ msgstr "buscando ..." #~ msgid "" #~ "Draw a line across the image in \n" #~ "direction of brightness gradient." #~ msgstr "" #~ "Dibujar una línea a través de la imagen en \n" #~ "la dirección del gradiente de brillo" #~ msgid "Brightness Gradient" #~ msgstr "Gradiente de brillo" #~ msgid "Select directory for thumbnails." #~ msgstr "Seleccionar directorio para las miniaturas." #~ msgid "no thumbnails directory defined" #~ msgstr "no definido directorio de miniaturas" #~ msgid "Dialog font and size" #~ msgstr "Diálogo fuente y tamaño" #~ msgid "Open the previously seen file" #~ msgstr "Abrir archivo previamente visto" #~ msgid "Add local contrast, enhance details" #~ msgstr "Añadir contraste local, mejorar detalles" #~ msgid "Flatten zonal brightness distribution" #~ msgstr "Aplanar histograma zonal" #~ msgid "Write text on image" #~ msgstr "Escribir un texto sobre la imagen" #~ msgid "Write lines or arrows on image" #~ msgstr "Dibujar líneas o flechas en la imagen" #~ msgid "Add a brightness/color gradient across the image" #~ msgstr "Añadir un gradiente de brillo/color a la imagen" #~ msgid "Convert camera RAW files using libraw" #~ msgstr "Convertir archivos RAW usando libraw" #~ msgid "Convert camera RAW files using Raw Therapee" #~ msgstr "Convertir archivos RAW usando RawTherapee" #~ msgid "Open Previous File" #~ msgstr "Abrir archivo anterior" #~ msgid "Find Gap" #~ msgstr "Encontrar separación" #~ msgid "Zonal Flatten" #~ msgstr "Aplanado zonal" #~ msgid "Add Lines/Arrows" #~ msgstr "Añadir línes/flechas" #~ msgid "Batch Raw Therapee" #~ msgstr "Convertir RAW en lote (Raw Therapee)" #~ msgid "Resources" #~ msgstr "Recursos" #~ msgid "Kill active dialog?" #~ msgstr "¿Cerrar diálogo activo?" #~ msgid "save screen to file" #~ msgstr "guardar pantalla a archivo" #~ msgid "END (press Escape to exit)" #~ msgstr "FIN (pulsar ESC para salir" #~ msgid "Geocoding by MapQuest" #~ msgstr "Geocodificar desde Mapquest" #~ msgid "Select Hairy Edge" #~ msgstr "Seleccionar borde deshilachado" #~ msgid "" #~ "Click on colors to select and deselect. \n" #~ "Set threshold levels for color matching. \n" #~ "Left drag to select or deselect pixels. \n" #~ "Right drag to restore original pixels. \n" #~ "When done, copy or save the finished area \n" #~ "for subsequent pasting into an image. \n" #~ msgstr "" #~ "Click en los colores para seleccionar o deseleccionar. \n" #~ "Establecer niveles para coincidencia de colores. \n" #~ "Arrastre izquierdo para seleccionar o deseleccionar píxeles. \n" #~ "Arrastre derecho para restituir los píxeles originales. \n" #~ "Cuando haya terminado, copie o guarde el área terminada \n" #~ "para pegado posterior en una imagen. \n" #~ msgid "load area from a file" #~ msgstr "Cargar área desde un archivo" #~ msgid "curved image" #~ msgstr "imagen curva" #~ msgid "choose pattern tile" #~ msgstr "elegir archivo patrón" #~ msgid "Divisor" #~ msgstr "Divisor" #~ msgid "Open RAW file (Raw Therapee)" #~ msgstr "Abrir archivo RAW con RawTherapee" #~ msgid "Delete or Trash Image File" #~ msgstr "Borrar o enviar a la papelera la imagen" #~ msgid "Edit Functions Summary" #~ msgstr "Resumen de las funciones de edición" #~ msgid "%d files uploaded" #~ msgstr "%d archivos actualizados" #~ msgid "reverse upload sequence" #~ msgstr "revertir secuencia de subida" #~ msgid "Upload to Flickr" #~ msgstr "Subir a Flickr" #~ msgid "select random (if 2+ enabled)" #~ msgstr "selección aleatoria (si activado +2)" #~ msgid "enabled" #~ msgstr "activado" #~ msgid "slowdown" #~ msgstr "ralentizar" #~ msgid "preference" #~ msgstr "preferencia" #~ msgid "choose layout file" #~ msgstr "escoger archivo de capa" #~ msgid "Geocoding service by MapQuest" #~ msgstr "Servicio de geocodificación de MapQuest" #~ msgid "Batch Change Metadata" #~ msgstr "Cambiar metadatos en lote" #~ msgid "Select File" #~ msgstr "Seleccionar archivo" #~ msgid "Previous Image" #~ msgstr "Imagen previa" #~ msgid "Directory Gallery" #~ msgstr "Galería de directorio" #~ msgid "Image Pan/scroll:" #~ msgstr "Fijar/deslizar imagen" #~ msgid "Zooms for 2x" #~ msgstr "Zooms x2" #~ msgid "Delete present thumbnails to make effective" #~ msgstr "Borrar la diapositiva actual para hacerlo efectivo" #~ msgid "Select Album" #~ msgstr "Seleccionar álbum" #~ msgid "" #~ "appimage file moved to %s \n" #~ "desktop file created at %s \n" #~ "\n" #~ msgstr "" #~ "archivo appimagen app movido a %s \n" #~ "archivo de escritorio creado en %s \n" #~ "\n" #~ msgid "Flatten Book Page Photo" #~ msgstr "Aplanar foto de página impresa" #~ msgid "Process: batch convert, batch metadata, image search ..." #~ msgstr "Proceso: conversión en lote, metadatos en lote, buscar imagen ..." #~ msgid "Sync Gallery, Export, Flickr, Albums, Slide Show" #~ msgstr "Sincronizar galeria, exportar, Flickr, álbumes, diaporama" #~ msgid "go to bookmarked image" #~ msgstr "ir a una imagen marcada" #~ msgid "Set all images or current gallery" #~ msgstr "Todas las imágenes o galería actual" #~ msgid "Set map markers from current gallery" #~ msgstr "Esrablecer marcadores de mapa desde la galría actual" #~ msgid "Summary of image edit functions" #~ msgstr "Resumen de las funciones de edición de imágenes" #~ msgid "Upload image files to Flickr web service" #~ msgstr "Subir imágenes al servicio web de Flickr" #~ msgid "Match Level" #~ msgstr "Nivel de coincidencia" #~ msgid "slow scroll" #~ msgstr "desplazamiento lento" #~ msgid "Save File Version" #~ msgstr "Guardar versión de archivo" #~ msgid "%d files selected" #~ msgstr "%d archivos seleccionados" #~ msgid "Cycle Desktop Wallpaper" #~ msgstr "Fondo de escritorio cíclico" #~ msgid "Set Desktop Wallpaper" #~ msgstr "Establecer fondo de escritorio" #~ msgid "Cycle desktop wallpaper from a Fotoxx album" #~ msgstr "Fondo de escritorio cíclico desde un álbum de Fotoxx" #~ msgid "Set desktop wallpaper from current Fotoxx image" #~ msgstr "Establecer fondo de escritorio desde la imagen de Fotoxx actual" #~ msgid "Paint or clone image pixels using the mouse" #~ msgstr "Pintar o clonar píxels usando el ratón" #~ msgid "Directory" #~ msgstr "Directorio" #~ msgid "transparency edge" #~ msgstr "transparencia de los bordes" #~ msgid "transparency center" #~ msgstr "transparencia del centro" #~ msgid "copy from image" #~ msgstr "copiar desde la imagen" #~ msgid "Paint/Clone" #~ msgstr "Pintar/Clonar" #~ msgid "" #~ "shift + left click: pick color or image position \n" #~ "left click or drag: paint color or copy image \n" #~ "right click or drag: remove color or image" #~ msgstr "" #~ "Mayúsc + clic izquierdo: tomar color o posición de imagen \n" #~ "clic izquierdo o arrastrar: pintar color o copiar imagen \n" #~ "clic derecho o arrastrar: borrar color o imagen" #~ msgid "select image files to be processed" #~ msgstr "seleccionaar imágenes a procesar" #~ msgid "unknown script file" #~ msgstr "script desconocido" #~ msgid "select script file to run" #~ msgstr "seleccionar script a ejecutar" #~ msgid "script file is not closed" #~ msgstr "el script no se ha cerrado" #~ msgid "open new script file" #~ msgstr "abrir nuevo script" #~ msgid "not a defined tag: %s" #~ msgstr "no está definida la etiqueta: %s" #~ msgid "Area Color" #~ msgstr "Color de área" #~ msgid "" #~ "shift + left click: pick image position to copy \n" #~ "left click or drag: copy image to mouse position \n" #~ "right click or drag: restore image" #~ msgstr "" #~ "mayúsc + clic izq.: señalar posición de imagen a copiar nbotón izquierdo " #~ "y arrastrar : copiar imagen en la posición del ratón \n" #~ "botón derecho y arrastrar: restaurar imagen" #~ msgid "Click on a monotone image area." #~ msgstr "Clic en un área monotono de la imagen" #~ msgid "Choose album" #~ msgstr "Elegir álbum" #~ msgid "Choose image directory" #~ msgstr "Elegir directorio de imagen" #~ msgid "Scroll" #~ msgstr "Desplazar" #~ msgid "" #~ "Metadata Search report cannot be re-sorted. \n" #~ "Check User Guide for a way to do this." #~ msgstr "" #~ "Informe de búsqueda de metadatos no puede ser re-ordenado. nVer guía de " #~ "usuario para ver cómo hacerlo" #~ msgid "Japanese-fan" #~ msgstr "abanico japonés" #~ msgid "Show Resources" #~ msgstr "Mostrar recursos" #~ msgid "Retinex" #~ msgstr "Retinex" #~ msgid "Open with EOG" #~ msgstr "Abrir con EOG" #~ msgid "Convert to pencil sketch" #~ msgstr "Convertir a un esbozo a lápiz" #~ msgid "View an image file with Gnome EOG" #~ msgstr "Ver una imagen con Eye of Gnome EOG" #~ msgid "Cycle 3 most recent files" #~ msgstr "3 archivos más recientes en ciclo" #~ msgid "Cycle 2 most recent files" #~ msgstr "2 archivos más recientes en ciclo" #~ msgid "Select images to add" #~ msgstr "Seleccionar imágenes para añadir" fotoxx-18.01.1/locales/translate-ca.po0000644000175000017500000041450313222767271016231 0ustar micomico# Traducció al català del paquet Fotoxx # Copyright (C) 2018 THE home's COPYRIGHT HOLDER # Aquest fitxer es distribueix Igual que el paquet original. # Josep Antoni Miralles Puignau 2012-2017 # msgid "" msgstr "" "Project-Id-Version: home 2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-12-26 22:25+0100\n" "PO-Revision-Date: 2017-01-20 21:58+0200\n" "Last-Translator: Josep Antoni Miralles Puignau \n" "Language-Team: Català, Valencià <>\n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" #: f.albums.cc:89 msgid "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." msgstr "" "Ciic dret en la miniatura de l'àlbum per tallar/copiar \n" "a la caché, inserir des de la caché, o eliminar" #: f.albums.cc:91 msgid "" "Right-click left/right side of album \n" "thumbnail to insert cached images \n" "before/after the thumbnail." msgstr "" "Clic dret en el costat esq/dret de l'àlbum \n" "de miniatures per inserir imatges del caché \n" "abans/després de la miniatura." #: f.albums.cc:94 msgid "Drag album thumbnail to new position." msgstr "Arrossegar miniatura de l'àlbum a la nova posició" #: f.albums.cc:128 f.widgets.cc:613 msgid "Manage Albums" msgstr "Gestionar àlbums" #: f.albums.cc:140 f.albums.cc:275 msgid "Create or replace an album" msgstr "Crear o substituir un àlbum" #: f.albums.cc:142 msgid "Album to view or edit" msgstr "Àlbum per veure o editar" #: f.albums.cc:144 msgid "Select images, add to cache" msgstr "Seleccionar imatges per afegir al caché" #: f.albums.cc:146 msgid "Clear image cache" msgstr "Netejar cache d'imatge" #: f.albums.cc:148 f.albums.cc:170 msgid "Delete an album" msgstr "Esborrar un àlbum" #: f.albums.cc:169 msgid "Choose Album" msgstr "Escollir un àlbum" #: f.albums.cc:171 #, c-format msgid "Image cache has %d images" msgstr "Cachè d'imatges té %d imatges" #: f.albums.cc:172 msgid "cache added to empty album" msgstr "caché afegida a un àlbum buit" #: f.albums.cc:228 #, c-format msgid "delete %s ?" msgstr "esborrar %s ?" #: f.albums.cc:259 #, c-format msgid "fill from image cache (%d images)" msgstr "emplenar des del cachè (%d imatges)" #: f.albums.cc:277 f.albums.cc:310 msgid "Album Name" msgstr "Nom de l'àlbum" #: f.albums.cc:283 msgid "make an initially empty album" msgstr "crear un àlbun buit" #: f.albums.cc:285 msgid "fill from current gallery" msgstr "emplenar des de la galeria actual" #: f.albums.cc:324 msgid "enter an album name" msgstr "entrar un nom d'àlbum" #: f.albums.cc:352 msgid "new album created" msgstr "creat nou àlbum" #: f.albums.cc:368 msgid "gallery is empty" msgstr "la galeria està buida" #: f.albums.cc:876 f.widgets.cc:614 msgid "Update Album Files" msgstr "Actualitzar arxius de l'àlbum" #: f.albums.cc:878 f.albums.cc:938 f.albums.cc:1007 f.albums.cc:1147 msgid "Choose Albums" msgstr "Escollir àlbums" #: f.albums.cc:1005 f.widgets.cc:615 f.widgets.cc:762 msgid "Replace Album File" msgstr "Substituir àlbum" #: f.albums.cc:1009 msgid "All Albums" msgstr "Tots els àlbums" #: f.albums.cc:1012 msgid "old file" msgstr "arxiu antic" #: f.albums.cc:1016 msgid "new file" msgstr "nou arxiu" #: f.albums.cc:1020 msgid "replace old" msgstr "substituir antic" #: f.albums.cc:1021 msgid "add after old" msgstr "afegir desprès de l'antic" #: f.albums.cc:1081 msgid "no albums chosen" msgstr "No heu escollit àlbums" #: f.albums.cc:1162 msgid "ALL albums chosen" msgstr "Tots els àlbums ewscollits" #: f.albums.cc:1346 msgid "instant" msgstr "instantània" #: f.albums.cc:1347 msgid "fade-in" msgstr "fusió endins" #: f.albums.cc:1348 msgid "roll-right" msgstr "enrotllar a la dreta" #: f.albums.cc:1349 msgid "roll-down" msgstr "enrotllar abaix" #: f.albums.cc:1350 msgid "venetian" msgstr "persiana" #: f.albums.cc:1351 msgid "grate" msgstr "enreixat" #: f.albums.cc:1352 msgid "rectangle" msgstr "rectangle" #: f.albums.cc:1353 msgid "implode" msgstr "implosionar" #: f.albums.cc:1354 msgid "explode" msgstr "explotar" #: f.albums.cc:1355 msgid "radar" msgstr "radar" #: f.albums.cc:1356 msgid "spiral" msgstr "espiral" #: f.albums.cc:1357 msgid "Japan-fan" msgstr "Ventall japonés" #: f.albums.cc:1358 msgid "jaws" msgstr "dents" #: f.albums.cc:1359 msgid "ellipse" msgstr "elipsi" #: f.albums.cc:1360 msgid "raindrops" msgstr "gotes de pluja" #: f.albums.cc:1361 msgid "doubledoor" msgstr "doble porta" #: f.albums.cc:1362 msgid "rotate" msgstr "girar" #: f.albums.cc:1363 msgid "fallover" msgstr "cap abaix" #: f.albums.cc:1364 msgid "spheroid" msgstr "esferoide" #: f.albums.cc:1365 msgid "turn-page" msgstr "girar pàgina" #: f.albums.cc:1366 msgid "french-door" msgstr "porta batents" #: f.albums.cc:1367 msgid "turn-cube" msgstr "cub giratori" #: f.albums.cc:1368 msgid "windmill" msgstr "remolí" #: f.albums.cc:1369 msgid "pixelize" msgstr "pixelitzar" #: f.albums.cc:1370 msgid "twist" msgstr "girar" #: f.albums.cc:1372 msgid "squish" msgstr "Expansió" #: f.albums.cc:1399 f.widgets.cc:616 msgid "Slide Show" msgstr "Diaporama" #: f.albums.cc:1405 msgid "use gallery" msgstr "utilitzar galeria" #: f.albums.cc:1411 msgid "Clip Limit" msgstr "Límit de sequencia" #: f.albums.cc:1415 msgid "Music File" msgstr "Arxiu de Música" #: f.albums.cc:1420 msgid "Full Screen" msgstr "A tota pantalla" #: f.albums.cc:1421 msgid "Auto-replay" msgstr "Auto reiniciar" #: f.albums.cc:1424 msgid "Customize:" msgstr "Personalitzar" #: f.albums.cc:1425 msgid "transitions" msgstr "transicions" #: f.albums.cc:1426 msgid "image files" msgstr "archivos de imagen" #: f.albums.cc:1427 msgid "KB controls" msgstr "Controls de tecles dreceres" #: f.albums.cc:1441 f.albums.cc:1509 f.albums.cc:1732 f.albums.cc:2007 #: f.albums.cc:2292 f.albums.cc:2309 f.albums.cc:2526 msgid "invalid album" msgstr "àlbum invàlid" #: f.albums.cc:1540 msgid "open album" msgstr "obrir àlbum" #: f.albums.cc:1556 msgid "Select music file" msgstr "Seleccioneu arxiu de música" #: f.albums.cc:1587 #, c-format msgid "%d images" msgstr "%d imatges" #: f.albums.cc:1614 msgid "arrow keys show previous or next image instantly" msgstr "les tecles de fletxes mostren imatge prèvia o següent imstantàniament" #: f.albums.cc:1632 msgid "Keyboard Preferences" msgstr "Preferències de teclat" #: f.albums.cc:1635 msgid "blank or unblank window" msgstr "pantalla buïda o plena" #: f.albums.cc:1638 msgid "show next image, with transition" msgstr "mostrar la següent imatge, amb transició" #: f.albums.cc:1641 msgid "pause or resume slide show" msgstr "pausar o reiniciar diaporama" #: f.albums.cc:1644 msgid "magnify image (loupe tool)" msgstr "aumenta imatga (eina de lupa)" #: f.albums.cc:1711 msgid "random sequence" msgstr "seqüència aleatòria" #: f.albums.cc:1736 msgid "Transition Preferences" msgstr "Preferències de transició" #: f.albums.cc:1738 msgid "Transitions File" msgstr "Arxiu de transicions" #: f.albums.cc:1756 f.albums.cc:1760 msgid "transition" msgstr "transició" #: f.albums.cc:1757 f.albums.cc:1761 msgid "use" msgstr "utilitzar" #: f.albums.cc:1758 f.albums.cc:1762 msgid "slow" msgstr "lent" #: f.albums.cc:1759 f.albums.cc:1763 msgid "pref" msgstr "pref." #: f.albums.cc:1862 f.albums.cc:1901 msgid "invalid file" msgstr "Arxiu no vàlid" #: f.albums.cc:1954 f.albums.cc:2511 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "error de format d'arxiu: \n" " %s" #: f.albums.cc:2013 msgid "Image Preferences" msgstr "Preferències d'imatge" #: f.albums.cc:2017 f.edit.cc:1405 f.file.cc:1577 f.file.cc:1883 f.meta.cc:820 #: f.meta.cc:1588 f.meta.cc:1780 msgid "Image File:" msgstr "Imatge:" #: f.albums.cc:2021 msgid "Play tone when image shows" msgstr "Reproduïr música mentre es veu la imatge" #: f.albums.cc:2024 msgid "Show image caption" msgstr "Mostrar títul del diaporama" #: f.albums.cc:2029 msgid "Show image comments" msgstr "Mostrar comentaris de la imatge" #: f.albums.cc:2034 msgid "Wait before zoom" msgstr "Espera abans del zoom" #: f.albums.cc:2039 msgid "Zoom type:" msgstr "Tipus de zoom" #: f.albums.cc:2041 msgid "zoom-in" msgstr "aproximar" #: f.albums.cc:2042 msgid "zoom-out" msgstr "allunyar" #: f.albums.cc:2045 msgid "Zoom size (x)" msgstr "Tamany de zoon (x)" #: f.albums.cc:2048 msgid "Steps" msgstr "Pasos" #: f.albums.cc:2052 msgid "Zoom Center" msgstr "Centrar zoom" #: f.albums.cc:2056 msgid "Wait after zoom" msgstr "Espera desprès del zoom" #: f.albums.cc:2061 msgid "Transition to next image" msgstr "Transiciò a la pròxima imatge" #: f.albums.cc:2064 f.albums.cc:2167 msgid "next" msgstr "següent" #: f.albums.cc:2157 msgid "click on thumbnail to set zoom center" msgstr "clic en la miniatura per situar el centre del zoom" #: f.area.cc:81 msgid "Select Area for Edits" msgstr "Seleccionar àrea per editar" #: f.area.cc:82 f.area.cc:1855 f.edit.cc:9094 msgid "Press F1 for help" msgstr "Prèmer F1 per ajuda" #: f.area.cc:91 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Seleccionar àrea no suportat \n" "Per aquesta funció d'edició" #: f.area.cc:120 msgid "select rectangle" msgstr "seleccionar rectangle" #: f.area.cc:121 msgid "select ellipse" msgstr "seleccionar ellipse" #: f.area.cc:124 msgid "freehand draw" msgstr "dibuixar a mà alçada" #: f.area.cc:125 msgid "follow edge" msgstr "seguir contorn" #: f.area.cc:126 msgid "draw/replace" msgstr "dibuixar/substituïr" #: f.area.cc:129 msgid "select area within mouse circle" msgstr "sel·leccionar àrea interior del cursor" #: f.area.cc:132 msgid "select one matching color within mouse circle:" msgstr "sel·leccionar un color coincident amb el cursor" #: f.area.cc:134 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "primer sel.leccioneu la casella, desprès \n" "majúsc+clic en la imatge per ajustar el color" #: f.area.cc:138 msgid "select all matching colors within mouse circle" msgstr "sel·leccionar tots els colors coincidents amb el cursor" #: f.area.cc:144 msgid "match level %" msgstr "nivell de coincidència %" #: f.area.cc:147 msgid "search range" msgstr "rang de cerca" #: f.area.cc:151 msgid "Area Edge Blend Width" msgstr "Ample de barreja dels línits de l'àrea" #: f.area.cc:154 msgid "Edge Creep" msgstr "Límit progressiu" #: f.area.cc:159 msgid "Line Color:" msgstr "Color de línia" #: f.area.cc:179 f.area.cc:180 msgid "area edits fade away within edge distance" msgstr "les edicions d'àrea s'esvaeixen a les vores" #: f.area.cc:351 f.area.cc:517 #, c-format msgid "exceed %d edits" msgstr "excedit %d edicions" #: f.area.cc:1333 msgid "" "Click one time inside each enclosed area. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Feu clic una vegada a l'interior de cada àrea delimitada.\n" "Es poden trobar possibles forats en el contorn.\n" "Premeu F1 per obtenir ajuda." #: f.area.cc:1360 msgid "finish area" msgstr "acabar àrea" #: f.area.cc:1389 f.area.cc:1534 #, c-format msgid "found %d pixels" msgstr "trobats %d pixels" #: f.area.cc:1639 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Clic en una posició en l'interior de l'àrea. \n" "Es trobaràn possibles separacions en el contorn. \n" "Prèmer F1 per ajuda." #: f.area.cc:1658 msgid "find outline gap" msgstr "trobar separació en contorn" #: f.area.cc:1748 msgid "cannot find area outline" msgstr "no es pot trobar contorn d'àrea" #: f.area.cc:1854 f.widgets.cc:458 msgid "Select Hairy" msgstr "Seleccionar irregular" #: f.area.cc:1862 msgid "select the area first" msgstr "Primer seleccioneu l'àrea" #: f.area.cc:1867 f.area.cc:2489 f.area.cc:2507 f.area.cc:2527 f.area.cc:2851 #: f.area.cc:2971 msgid "the area is not finished" msgstr "l'àrea no està acabada" #: f.area.cc:1940 msgid "select" msgstr "Seleccionar" #: f.area.cc:1946 msgid "deselect" msgstr "anul·lar la selecció" #: f.area.cc:2616 msgid "Edge calculation in progress" msgstr "Càlcul del límit en progrés" #: f.area.cc:2625 msgid "Area Edge Calc" msgstr "Càlcul del límit de l'àrea" #: f.area.cc:2957 f.area.cc:3029 f.widgets.cc:465 msgid "Copy Area" msgstr "Copiar àrea" #: f.area.cc:2960 f.widgets.cc:468 msgid "Save Area File" msgstr "Desar arxiu d'àrea" #: f.area.cc:3035 msgid "save area as a PNG file" msgstr "desar àrea com un arxiu PNG" #: f.area.cc:3159 msgid "position with mouse click/drag" msgstr "posició amb el clic del ratolí/arrossegament" #: f.area.cc:3219 msgid "Paste Image" msgstr "Enganxar imatge" #: f.area.cc:3224 f.process.cc:1311 msgid "resize" msgstr "redimensionar" #: f.combine.cc:169 f.combine.cc:815 f.combine.cc:1416 f.combine.cc:2039 msgid "Select 2 to 9 files" msgstr "Seleccionar de 2 a 9 fitxers" #: f.combine.cc:191 f.combine.cc:837 f.combine.cc:1438 f.combine.cc:2061 msgid "Images are not all the same size" msgstr "Les imatges no són totes de la mateixa mida" #: f.combine.cc:550 msgid "Adjust Image Contributions" msgstr "Ajustar contribucions de cada imatge" #: f.combine.cc:554 msgid "dark pixels" msgstr "ombres" #: f.combine.cc:556 msgid "light pixels" msgstr "llums" #: f.combine.cc:558 msgid "file:" msgstr "fitxer:" #: f.combine.cc:1018 msgid "Paint and Warp Image" msgstr "Pintar i deformar imatge" #: f.combine.cc:1026 f.edit.cc:5686 msgid "paint" msgstr "pintar" #: f.combine.cc:1027 msgid "warp" msgstr "deformar" #: f.combine.cc:1623 msgid "Select and Paint Image" msgstr "Seleccionar i pintar imatge" #: f.combine.cc:1631 msgid "Transient Objects" msgstr "Objectes transitoris" #: f.combine.cc:2232 msgid "Adjust Pixel Composition" msgstr "Ajustar la composició de pixels" #: f.combine.cc:2234 msgid "use average" msgstr "usar la mitja" #: f.combine.cc:2235 msgid "use median" msgstr "usar la mediana" #: f.combine.cc:2237 msgid "omit low pixel" msgstr "omitir píxel baix" #: f.combine.cc:2238 msgid "omit high pixel" msgstr "omitir píxel alt" #: f.combine.cc:2502 f.combine.cc:3653 msgid "Select 2 to 4 files" msgstr "Seleccionar de 2 a 4 fitxers" #: f.combine.cc:2573 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Arrossegar imatges a una alineació aproximada.\n" "Per girar, arrossegar des de la vora inferior." #: f.combine.cc:2575 f.combine.cc:3726 msgid "no curve (scanned image)" msgstr "no hi ha corba (imatge escanejada)" #: f.combine.cc:2576 f.combine.cc:3727 msgid "Search for lens mm" msgstr "Cerca mm de la lent" #: f.combine.cc:2577 f.combine.cc:3728 msgid "Save lens mm → image EXIF" msgstr "Desar focal en EXIF" #: f.combine.cc:2639 f.combine.cc:3790 msgid "Pre-align Images" msgstr "Pre-alinear imatges" #: f.combine.cc:2643 f.combine.cc:3794 msgid "lens mm" msgstr "mm de la lent" #: f.combine.cc:2648 f.combine.cc:3799 msgid "no auto warp" msgstr "no auto correcció" #: f.combine.cc:2650 f.combine.cc:3801 msgid "manual align" msgstr "alineació manual" #: f.combine.cc:2652 f.combine.cc:3803 f.widgets.cc:473 f.widgets.cc:768 msgid "Resize" msgstr "Redimensionar" #: f.combine.cc:2653 f.combine.cc:3804 msgid "resize window" msgstr "redimensionar finestra" #: f.combine.cc:2661 f.combine.cc:3812 msgid "do not warp images during auto-alignment" msgstr "no deformar imatges mentre s'auto-alineen" #: f.combine.cc:2707 f.combine.cc:3858 msgid "use two images only" msgstr "utilitzar només dues imatges" #: f.combine.cc:2735 f.combine.cc:2939 f.combine.cc:3127 f.combine.cc:3884 #: f.combine.cc:4088 f.combine.cc:4276 msgid "Too little overlap, cannot align" msgstr "molt poc solpament, no es pot alinear" #: f.combine.cc:3205 f.combine.cc:4354 msgid "Match Brightness and Color" msgstr "Coincidència de brillantor i color" #: f.combine.cc:3251 msgid "Select image" msgstr "Sele ccionar imatge" #: f.combine.cc:3266 f.combine.cc:4415 msgid "auto color" msgstr "auto color" #: f.combine.cc:3267 f.combine.cc:4416 msgid "file color" msgstr "fitxer de color" #: f.combine.cc:3273 f.combine.cc:4422 msgid "mouse warp" msgstr "corregir amb el ratolí" #: f.combine.cc:3275 f.combine.cc:4424 msgid "flatten image" msgstr "aplanar imatge" #: f.combine.cc:3724 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Arrossegar imatges a una alineació aproximada.\n" "Per girar, arrossegar des de la vora dreta." #: f.combine.cc:4684 msgid "pano tools (hugin) not installed" msgstr "Pano Tools (Hugin) no installat" #: f.combine.cc:4693 msgid "Select at least 2 files" msgstr "Seleccioneu almenys 2 arxius" #: f.edit.cc:98 msgid "Trim: drag middle to move, drag corners to resize" msgstr "" "Retallar : arrossegar pel mig per moure, arrossegar cantons per redimensionar" #: f.edit.cc:99 msgid "Minor rotate: drag right edge with mouse" msgstr "Girar : arrossegar cantó dret amb el ratolí " #: f.edit.cc:180 f.widgets.cc:471 f.widgets.cc:767 msgid "Trim/Rotate" msgstr "Retallar/Girar" #: f.edit.cc:192 f.edit.cc:1306 msgid "ratio" msgstr "proporció" #: f.edit.cc:196 msgid "trim size:" msgstr "mida de retallat" #: f.edit.cc:201 msgid "Lock Ratio" msgstr "Bloquejar proporció" #: f.edit.cc:203 msgid "maximize trim box" msgstr "maximitzar caixa de retallada" #: f.edit.cc:204 msgid "use previous size" msgstr "usar tamany previ" #: f.edit.cc:205 msgid "invert width/height" msgstr "invertir ample/alçada" #: f.edit.cc:206 msgid "trim transparent edges" msgstr "retallar contorns transparents" #: f.edit.cc:207 msgid "lock width/height ratio" msgstr "bloquejar proporció amplada/alçada" #: f.edit.cc:212 msgid "Customize" msgstr "Personalitzar" #: f.edit.cc:218 msgid "Level" msgstr "Nivel​l" #: f.edit.cc:219 msgid "use EXIF data if available" msgstr "usar dades EXIF si estàn disponibles" #: f.edit.cc:222 msgid "Rotate: degrees" msgstr "Girar : graus" #: f.edit.cc:1302 msgid "Trim Buttons" msgstr "Botons de retallar" #: f.edit.cc:1305 msgid "label" msgstr "etiqueta" #: f.edit.cc:1402 msgid "Upright Image" msgstr "Adressar imatge" #: f.edit.cc:1408 f.process.cc:168 f.process.cc:755 f.widgets.cc:472 #: f.widgets.cc:769 msgid "Upright" msgstr "Adressar" #: f.edit.cc:1461 msgid "rotation unknown" msgstr "ratació desconeguda" #: f.edit.cc:1548 msgid "Resize Image" msgstr "Redimensionar imatge" #: f.edit.cc:1573 msgid "W/H Ratio:" msgstr "Relació amplada/alçada:" #: f.edit.cc:1575 msgid "Lock" msgstr "Bloquejar" #: f.edit.cc:1577 msgid "use previous settings" msgstr "etilitzar especificacions anteriors" #: f.edit.cc:2298 f.widgets.cc:476 f.widgets.cc:772 msgid "Retouch Combo" msgstr "Retocar llum i color " #: f.edit.cc:2319 msgid "Amplifier" msgstr "Amplificar" #: f.edit.cc:2320 fotoxx.h:1188 msgid "Brightness" msgstr "Brillantor" #: f.edit.cc:2321 f.effects.cc:3681 fotoxx.h:1198 msgid "Contrast" msgstr "Contrast" #: f.edit.cc:2322 msgid "Low Color" msgstr "Color -" #: f.edit.cc:2323 msgid "Warmer" msgstr "Càlid" #: f.edit.cc:2324 f.effects.cc:1332 msgid "Dark Areas" msgstr "Ombres" #: f.edit.cc:2333 msgid "Max." msgstr "Max" #: f.edit.cc:2334 f.edit.cc:2335 f.edit.cc:2336 msgid "High" msgstr "+" #: f.edit.cc:2337 msgid "Cooler" msgstr "Fred" #: f.edit.cc:2338 msgid "Bright" msgstr "Llums" #: f.edit.cc:2341 f.tools.cc:2134 msgid "Brightness Distribution" msgstr "Distribució de la brillantor" #: f.edit.cc:2344 msgid "Click for white balance" msgstr "Clc per balanç de blancs" #: f.edit.cc:2345 msgid "Click for black level" msgstr "Clic per nivell de negre" #: f.edit.cc:2348 msgid "Settings File" msgstr "Ajustaments" #: f.edit.cc:2352 msgid "recall previous settings used" msgstr "recuperar ajustaments usats anteriorment" #: f.edit.cc:3004 msgid "Adjust Brightness Distribution" msgstr "Ajustar histograma" #: f.edit.cc:3043 msgid "Low Cutoff" msgstr "Tall baix" #: f.edit.cc:3044 msgid "High Cutoff" msgstr "Tall alt" #: f.edit.cc:3045 msgid "Low Flatten" msgstr "Aplanat baix" #: f.edit.cc:3046 msgid "Mid Flatten" msgstr "Aplanat mitjà" #: f.edit.cc:3047 msgid "High Flatten" msgstr "Aplanat alt" #: f.edit.cc:3048 msgid "Low Stretch" msgstr "Tram baix" #: f.edit.cc:3049 msgid "Mid Stretch" msgstr "Tram mitjà" #: f.edit.cc:3050 msgid "High Stretch" msgstr "Tram alt" #: f.edit.cc:3383 msgid "Magnify Gradients" msgstr "Augmentar gradients" #: f.edit.cc:3415 msgid "low" msgstr "baix" #: f.edit.cc:3417 msgid "high" msgstr "alt" #: f.edit.cc:3420 msgid "Amplify" msgstr "Ampliar" #: f.edit.cc:3813 msgid "Flatten Brightness" msgstr "Aplanar brillantor" #: f.edit.cc:3864 msgid "Zones" msgstr "Zones" #: f.edit.cc:3871 msgid "Deband Dark" msgstr "Eliminar bandes fosques" #: f.edit.cc:3874 msgid "Deband Bright" msgstr "Eliminar bandes lluminoses" #: f.edit.cc:4386 msgid "Dark Point" msgstr "Punt negre" #: f.edit.cc:4387 msgid "Bright Point" msgstr "Punt brillant" #: f.edit.cc:4388 msgid "Multiplyer" msgstr "Multiplicador" #: f.edit.cc:4412 msgid "brightness rescale" msgstr "re-esclar brillantor" #: f.edit.cc:4415 msgid "click bright point" msgstr "clic en el punt brillant" #: f.edit.cc:4416 msgid "click dark point" msgstr "clic en el punt fosc" #: f.edit.cc:4427 msgid "blend" msgstr "barrejar" #: f.edit.cc:4430 msgid "reduce bright" msgstr "reduïr brillantor" #: f.edit.cc:5468 f.widgets.cc:481 msgid "Mirror Image" msgstr "Enmirallar imatge" #: f.edit.cc:5472 f.warp.cc:3300 msgid "horizontal" msgstr "horitzontal" #: f.edit.cc:5473 f.warp.cc:3304 msgid "vertical" msgstr "vertical" #: f.edit.cc:5620 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "Majúsc + clic esq.: agafar color des de la imatge \n" "arrossegar esq.: pintar color en la imatge \n" "arrossegar dret: reaturar imatge original" #: f.edit.cc:5656 msgid "Paint on Image" msgstr "Pintar en la imatge" #: f.edit.cc:5662 msgid "paint color" msgstr "pintar color" #: f.edit.cc:5673 f.edit.cc:6640 msgid "brush size" msgstr "tamany de pinzell" #: f.edit.cc:5674 f.edit.cc:6641 msgid "opacity center" msgstr "opacitat del centre" #: f.edit.cc:5675 f.edit.cc:6642 msgid "opacity edge" msgstr "opacitat del contorn" #: f.edit.cc:5687 msgid "erase" msgstr "esborrar" #: f.edit.cc:5688 msgid "include transparent areas" msgstr "incluïr àrees transparents" #: f.edit.cc:5695 msgid "drag image" msgstr "arrossegar imatge" #: f.edit.cc:5697 msgid "zoom image" msgstr "zoom imatge" #: f.edit.cc:6221 msgid "Color Palette" msgstr "Palta de color" #: f.edit.cc:6225 msgid "click on image to choose color" msgstr "clic en la imatge per escollir color" #: f.edit.cc:6415 f.repair.cc:3799 msgid "Color Hue" msgstr "To de color" #: f.edit.cc:6416 f.repair.cc:3768 f.repair.cc:3800 msgid "Saturation" msgstr "Saturació" #: f.edit.cc:6417 f.repair.cc:3769 f.repair.cc:3801 msgid "Lightness" msgstr "Brillantor" #: f.edit.cc:6599 msgid "" "shift + left click: pick image position to copy \n" "left drag: copy image to mouse position \n" "right drag: restore image" msgstr "" "majúsc + clic esq.: assenyalar posició d'imatge per copiar \n" "botó equerra i arrossegar: copiar imatge en la posició del ratolí \n" "botó dret i arrossegar: restaurar imatge" #: f.edit.cc:6630 f.widgets.cc:483 msgid "Clone Image" msgstr "Clonar imatge" #: f.edit.cc:6649 msgid "paint over transparent areas" msgstr "pintar sobre àrees transparents" #: f.edit.cc:7098 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "arrossegar esquerra: barrejar imatge \n" "arrossegar dret: reinicialitzar imatge" #: f.edit.cc:7126 f.widgets.cc:484 msgid "Blend Image" msgstr "Barrejar imatge" #: f.edit.cc:7134 f.repair.cc:7663 msgid "strength center" msgstr "força en el centre" #: f.edit.cc:7135 f.repair.cc:7664 msgid "strength edge" msgstr "força en els extrems" #: f.edit.cc:7359 msgid "+Version" msgstr "+Version" #: f.edit.cc:7382 f.widgets.cc:275 msgid "Add Text to Image" msgstr "Afegir text a la imatge" #: f.edit.cc:7383 msgid "Enter text, click/drag on image, right click to remove" msgstr "" "Escriure el text, clic/arrossegar sobre la imnatge, clic dret per eliminar" #: f.edit.cc:7432 f.edit.cc:8310 msgid "Use settings file" msgstr "Utilitzar " #: f.edit.cc:7437 f.mashup.cc:2455 msgid "Text" msgstr "Text" #: f.edit.cc:7442 msgid "Use metadata key" msgstr "Usar clau de metadades" #: f.edit.cc:7461 f.mashup.cc:2473 msgid "text" msgstr "text" #: f.edit.cc:7462 f.edit.cc:8336 f.mashup.cc:2474 f.mashup.cc:2812 msgid "backing" msgstr "fons" #: f.edit.cc:7463 f.edit.cc:8337 f.mashup.cc:2475 f.mashup.cc:2813 msgid "outline" msgstr "contorn" #: f.edit.cc:7464 f.edit.cc:8338 f.mashup.cc:2476 f.mashup.cc:2814 msgid "shadow" msgstr "ombra" #: f.edit.cc:7490 msgid "save to current file" msgstr "desar en l'arxiu actual" #: f.edit.cc:7491 f.file.cc:2247 msgid "save as new file version" msgstr "desar com una nova versió" #: f.edit.cc:7492 msgid "" "save to current file \n" "open next file with same text" msgstr "" "desar en l'arxiu actual \n" "obrir següent arxiu amb el mateix text" #: f.edit.cc:7652 f.mashup.cc:2580 f.tools.cc:1464 msgid "select font" msgstr "seleccionar tipus de lletra" #: f.edit.cc:7928 f.edit.cc:8766 msgid "text file is defective" msgstr "arxiu de text és defectuós" #: f.edit.cc:8268 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Introduïr propietats de línia o fletxa en el dialogclic/arrossegar en la " "imatge, clic dret per eliminar" #: f.edit.cc:8302 f.widgets.cc:276 msgid "Add lines or arrows to an image" msgstr "Afegir línies o fltxes a una imatge" #: f.edit.cc:8315 f.mashup.cc:2791 msgid "Line length" msgstr "Longitut de línia" #: f.edit.cc:8322 f.mashup.cc:2798 msgid "Arrow head" msgstr "Punta de fletxa" #: f.edit.cc:8335 f.mashup.cc:2811 msgid "line" msgstr "linea" #: f.edit.cc:8364 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "corretgir línia/fletxa a la capa​ \n" "iniciar nova línia/fletxa" #: f.edit.cc:9093 f.widgets.cc:487 msgid "Paint Edits" msgstr "Editar pintant" #: f.edit.cc:9100 f.edit.cc:9323 fotoxx-18.01.cc:3426 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Àrea seleccionada no pot ser guardada.\n" "Continuar?" #: f.edit.cc:9108 f.edit.cc:9331 f.tools.cc:2666 msgid "Edit function must be active" msgstr "La funció d'editar pintant ha d'estar activada" #: f.edit.cc:9113 msgid "Cannot use Paint Edits" msgstr "No es pot usar Editar pintant" #: f.edit.cc:9140 msgid "power: center" msgstr "força: centre" #: f.edit.cc:9145 msgid "reset area" msgstr "reiniciar àrea" #: f.edit.cc:9316 f.widgets.cc:488 msgid "Leverage Edits" msgstr "Edició per nivells" #: f.edit.cc:9317 msgid "Edit Function Amplifier" msgstr "Amplificador de la funció d'edició" #: f.edit.cc:9336 msgid "Cannot use Leverage Edits" msgstr "No es pot usar Edició per nivells" #: f.edit.cc:9365 msgid "minimum" msgstr "ombres" #: f.edit.cc:9367 msgid "maximum" msgstr "llums" #: f.edit.cc:9642 f.edit.cc:9686 msgid "Edit Plugins" msgstr "Editar complements (Plugins)" #: f.edit.cc:9643 msgid "Edit plugins menu" msgstr "Editar menú de plugins" #: f.edit.cc:9651 msgid "Run as Fotoxx edit function" msgstr "Obrir com una funció d'edició de Fotoxx" #: f.edit.cc:9688 msgid "menu name" msgstr "nom de menú" #: f.edit.cc:9691 msgid "command" msgstr "instrucció" #: f.edit.cc:9888 msgid "Plugin working ..." msgstr "Plugin treballant ..." #: f.edit.cc:9897 msgid "plugin failed" msgstr "errada de complement" #: f.effects.cc:79 msgid "Convert to Sketch" msgstr "Convertir a esbós" #: f.effects.cc:157 msgid "Clip Level" msgstr "Nivell de tall" #: f.effects.cc:161 msgid "Algorithm" msgstr "Algoritme" #: f.effects.cc:166 msgid "Foreground" msgstr "Primer pla" #: f.effects.cc:169 f.tools.cc:1275 msgid "Background" msgstr "Fons" #: f.effects.cc:1054 f.widgets.cc:531 msgid "Line Drawing" msgstr "Dibuix amb línees" #: f.effects.cc:1067 msgid "black/white" msgstr " negre/blanc" #: f.effects.cc:1326 f.widgets.cc:532 msgid "Color Drawing" msgstr "Dibuix amb colors" #: f.effects.cc:1333 msgid "Bright Areas" msgstr "Llums" #: f.effects.cc:1575 f.widgets.cc:533 msgid "Graduated Blur" msgstr "Desenfocament graduat" #: f.effects.cc:1579 msgid "Contrast Limit" msgstr "Límit de contrast" #: f.effects.cc:1582 f.repair.cc:929 msgid "Blur Radius" msgstr "Radi de desenfocament" #: f.effects.cc:1799 msgid "Simulate Embossing" msgstr "Simular un relleu" #: f.effects.cc:1820 msgid "depth" msgstr "profunditat" #: f.effects.cc:2029 msgid "Simulate Tiles" msgstr "Simular un mosaic" #: f.effects.cc:2033 msgid "tile size" msgstr "mida de la rajola" #: f.effects.cc:2036 msgid "tile gap" msgstr "separació de lles rajoles" #: f.effects.cc:2039 msgid "3D depth" msgstr "profunditat 3D" #: f.effects.cc:2258 msgid "Convert Image to Dots" msgstr "Convertrr la imatge a punts" #: f.effects.cc:2262 msgid "dot size" msgstr "mida de punt" #: f.effects.cc:2484 msgid "Simulate Painting" msgstr "Simular una Pintura" #: f.effects.cc:2502 msgid "color depth" msgstr "profunditat de color" #: f.effects.cc:2506 msgid "patch area goal" msgstr "mida de l'àrea de color" #: f.effects.cc:2510 msgid "req. color match" msgstr "correspondència de color requerida" #: f.effects.cc:2514 msgid "borders" msgstr "vores" #: f.effects.cc:3081 f.widgets.cc:538 msgid "Vignette" msgstr "Vinyetat" #: f.effects.cc:3430 msgid "Add Texture" msgstr "Afegir textura" #: f.effects.cc:3646 msgid "Background Pattern" msgstr "Patrò de fons" #: f.effects.cc:3650 msgid "Pattern File:" msgstr "Arxiu de patrò" #: f.effects.cc:3655 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3658 msgid "Geometry" msgstr "Geometria" #: f.effects.cc:3659 msgid "Calculate" msgstr "Calcular" #: f.effects.cc:3662 f.widgets.cc:540 msgid "Pattern" msgstr "Patrò" #: f.effects.cc:3670 msgid "Overlap" msgstr "Superposició" #: f.effects.cc:3678 msgid "Opacity" msgstr "Opacitat" #: f.effects.cc:4124 msgid "Create Mosaic" msgstr "Crear mosaïc" #: f.effects.cc:4170 msgid "Tile" msgstr "Rajola" #: f.effects.cc:4178 f.widgets.cc:535 msgid "Tiles" msgstr "Rajoles" #: f.effects.cc:4184 msgid "Tile blending" msgstr "Mescla de rajoles" #: f.effects.cc:4265 #, c-format msgid "exceeded max. tiles: %d" msgstr "excedit max. arxius: %d" #: f.effects.cc:4272 #, c-format msgid "only %d tile images found" msgstr "trobades sols %d imatges" #: f.effects.cc:4756 f.widgets.cc:542 msgid "Custom Kernel" msgstr "Matriu de convolució" #: f.effects.cc:4760 msgid "Kernel size" msgstr "Tamany de matriu" #: f.effects.cc:4777 msgid "multiply" msgstr "multiplicar" #: f.effects.cc:4780 msgid "add" msgstr "afegir" #: f.effects.cc:4784 msgid "Data file" msgstr "Arxiu de dades" #: f.effects.cc:4804 fotoxx-18.01.cc:3690 msgid "Load settings from file" msgstr "Carregar ajustaments des d'un arxiu" #: f.effects.cc:5046 msgid "Pull image using the mouse." msgstr "Empènyer la imatge usant el ratolí" #: f.effects.cc:5067 f.widgets.cc:543 msgid "Directed Blur" msgstr "Desenfocament direccional" #: f.effects.cc:5071 msgid "blur span" msgstr "quantitat de desenfocament" #: f.effects.cc:5074 msgid "intensity" msgstr "intensitat" #: f.effects.cc:5276 f.widgets.cc:544 msgid "Blur Background" msgstr "Desenfocar fons" #: f.effects.cc:5280 msgid "constant blur" msgstr "desenfocament constant" #: f.effects.cc:5283 msgid "increase blur with distance" msgstr "incrementar desenfocament amb la distància" #: f.effects.cc:5285 msgid "min. blur radius" msgstr "mín. radi de desenfocament" #: f.effects.cc:5288 msgid "max. blur radius" msgstr "màx. radi de desenfocament" #: f.effects.cc:5322 f.warp.cc:819 f.warp.cc:1252 msgid "no active Select Area" msgstr "Seleccionar àrea no actiu" #: f.effects.cc:5515 f.widgets.cc:545 msgid "Alien Colors" msgstr "Colors alienígens" #: f.effects.cc:5518 msgid "blocksize" msgstr "tamany de bloc" #: f.effects.cc:5521 f.warp.cc:3298 msgid "amplitude" msgstr "amplitut" #: f.file.cc:190 msgid "" "Use EXIF photo date or \n" " file modification date?" msgstr "" "Utilitzar data EXIF de la foto o \n" "data de modificació de l'arxiu?" #: f.file.cc:204 f.widgets.cc:626 msgid "File" msgstr "Arxiu" #: f.file.cc:378 f.process.cc:1345 msgid "Raw Therapee not installed" msgstr "RawTherapee no instal.lat" #: f.file.cc:396 f.widgets.cc:432 f.widgets.cc:777 msgid "Open RAW (Raw Therapee)" msgstr "Obrir RAW (Raw Therapee)" #: f.file.cc:401 msgid "RAW type not registered in User Settings" msgstr "tipus RAW no registrat en Configuració d'usuari" #: f.file.cc:416 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee no ha produït un arxiu tiff" #: f.file.cc:822 f.widgets.cc:429 msgid "Open Image File" msgstr "Obrir arxiu d'imatge" #: f.file.cc:841 f.process.cc:597 msgid "unknown file type" msgstr "tipus d'arxiu desconegut" #: f.file.cc:1019 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Inici de galeria, precedent : %s" #: f.file.cc:1020 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Fi de galeria, següent : %s" #: f.file.cc:1128 msgid "Create Blank Image" msgstr "Crear imatge buida" #: f.file.cc:1130 msgid "file name" msgstr "nom d'arxiu" #: f.file.cc:1164 msgid "supply a file name" msgstr "proporcionar nom d'àlbum" #: f.file.cc:1334 f.widgets.cc:436 msgid "Rename Image File" msgstr "Re-anomenar arxiu d'imatge" #: f.file.cc:1341 msgid "Old Name" msgstr "antic nom" #: f.file.cc:1342 f.process.cc:130 msgid "New Name" msgstr "nou nom" #: f.file.cc:1351 msgid "previous name" msgstr "nom anterior" #: f.file.cc:1352 msgid "Add 1" msgstr "Afegir 1" #: f.file.cc:1355 f.file.cc:1591 f.file.cc:1886 msgid "keep this dialog open" msgstr "mantenir aquest diàleg obert" #: f.file.cc:1540 msgid "Copy or Move Image File" msgstr "Copiar o moure arxiu d'imatge" #: f.file.cc:1582 msgid "New Location:" msgstr "Nova ubicació" #: f.file.cc:1587 msgid "copy (duplicate file)" msgstr "copiar (duplicar arxiu)" #: f.file.cc:1588 msgid "move (remove original)" msgstr "moure (moure original)" #: f.file.cc:1629 f.process.cc:573 f.process.cc:1503 f.process.cc:2625 msgid "Select directory" msgstr "Seleccionar directori" #: f.file.cc:1667 msgid "new location is not a directory" msgstr "la nova ubicació no és un directori" #: f.file.cc:1708 f.file.cc:1962 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "l'esborrat ha fallat: \n" " %s" #: f.file.cc:1847 f.widgets.cc:442 msgid "Delete/Trash Image File" msgstr "Eliminar/esborrar una imatge" #: f.file.cc:1916 msgid "GTK g_file_trash() function failed" msgstr "Ha fallat la funció GTK g_file_trash()" #: f.file.cc:1935 msgid "not a known image file" msgstr "no és un arxiu d'imatge conegut" #: f.file.cc:1941 msgid "Delete read-only file?" msgstr "Esborrar arxius de només lectura?" #: f.file.cc:1943 msgid "Trash read-only file?" msgstr "Arxiu de paperera nomès de lectura?" #: f.file.cc:1991 msgid "no more images" msgstr "no hi ha més imatges" #: f.file.cc:2229 msgid "Save Image File" msgstr "Desar imatge" #: f.file.cc:2239 msgid "new version" msgstr "nova versió" #: f.file.cc:2240 msgid "new file ..." msgstr "nou arxiu ..." #: f.file.cc:2241 msgid "replace file" msgstr "substituïr arxiu" #: f.file.cc:2248 f.file.cc:2820 msgid "save as new file name or type" msgstr "desar com un nou nom o tipus d'arxiu" #: f.file.cc:2250 msgid "replace old file (OVERWRITE)" msgstr "substituïr arxiu anterior (SOBRE-ESCRIURE)" #: f.file.cc:2287 f.file.cc:2578 msgid "cannot save as RAW type" msgstr "no es pot desar com a RAW" #: f.file.cc:2395 msgid "too many file versions: 99" msgstr "massa versions de l'arxiu: 99" #: f.file.cc:2572 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "Es perdrà el mapa de transparència.\n" "desar com arxiu .PNG per conservar-lo" #: f.file.cc:2657 msgid "save anyway" msgstr "desar de totes maneres" #: f.file.cc:2726 msgid "Unable to copy EXIF/IPTC data" msgstr "No puc copiar dades EXIF/IPTC" #: f.file.cc:2838 f.file.cc:2841 msgid "make current" msgstr "fer actual" #: f.file.cc:2839 msgid "(new file becomes current file)" msgstr "(el nou arxiu serà l'arxiu actual)" #: f.file.cc:2952 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Sobre-escriure arxiu? \n" " %s" #: f.file.cc:3147 f.widgets.cc:599 msgid "Quick Start" msgstr "Inici ràpid" #: f.file.cc:3150 f.widgets.cc:600 msgid "User Guide" msgstr "Guia d'usuari" #: f.file.cc:3153 f.widgets.cc:601 msgid "User Guide Changes" msgstr "Canvis a la guia d'usuari" #: f.file.cc:3156 f.widgets.cc:602 msgid "README" msgstr "LLEGEIX-ME" #: f.file.cc:3159 f.widgets.cc:603 msgid "Change Log" msgstr "Registre de canvis" #: f.file.cc:3162 f.widgets.cc:604 msgid "Log File" msgstr "Arxiu de registre" #: f.file.cc:3165 f.widgets.cc:605 msgid "Translations" msgstr "Traduccions" #: f.file.cc:3168 f.widgets.cc:606 msgid "Home Page" msgstr "Pàgina web de Fotoxx" #: f.file.cc:3171 f.widgets.cc:607 msgid "About" msgstr "Sobre Fotoxx " #: f.file.cc:3177 f.widgets.cc:639 f.widgets.cc:663 f.widgets.cc:676 #: f.widgets.cc:690 fotoxx.h:1223 msgid "Help" msgstr "Ajuda" #: f.file.cc:3382 f.file.cc:3387 f.file.cc:3457 #, c-format msgid "file type not supported: %s" msgstr "tipus d'arxiu no suportaat: %s" #: f.gallery.cc:1221 msgid "GoTo" msgstr "Anar a" #: f.gallery.cc:1227 f.widgets.cc:655 msgid "Sort" msgstr "Ordenar" #: f.gallery.cc:1233 f.widgets.cc:653 msgid "Zoom+" msgstr "Zoom +" #: f.gallery.cc:1243 f.widgets.cc:654 msgid "Zoom-" msgstr "Zoom -" #: f.gallery.cc:1255 msgid "Row Up" msgstr "Fila amunt" #: f.gallery.cc:1263 msgid "Row Down" msgstr "Fila avall" #: f.gallery.cc:1271 f.widgets.cc:658 msgid "Page Up" msgstr "Pàgina amunt" #: f.gallery.cc:1279 f.widgets.cc:659 msgid "Page Down" msgstr "Pàgina avall" #: f.gallery.cc:1287 f.widgets.cc:656 msgid "First" msgstr "Primera" #: f.gallery.cc:1294 f.widgets.cc:657 msgid "Last" msgstr "Última" #: f.gallery.cc:1425 f.gallery.cc:1445 msgid "recent images" msgstr "imatges recents" #: f.gallery.cc:1426 f.gallery.cc:1450 msgid "newest images" msgstr "imatge més recents" #: f.gallery.cc:1504 msgid "no albums found" msgstr "No trobats àlbums" #: f.gallery.cc:1541 msgid "Albums cannot be sorted" msgstr "Ela àlbums no poden ser ordenats" #: f.gallery.cc:1545 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" "Re-inicir totes les galeries\n" "a nom d'arxiu ascendent" #: f.gallery.cc:1564 msgid "Gallery Sort" msgstr "Ordenar galeria" #: f.gallery.cc:1568 msgid "File Name" msgstr "Nom d'arxiu" #: f.gallery.cc:1569 msgid "File Mod Date/Time" msgstr "Mode d'arxiu date/hora" #: f.gallery.cc:1570 msgid "Photo Date/Time (EXIF)" msgstr "Data/hora de la foto (EXIF)" #: f.gallery.cc:1572 msgid "ascending" msgstr "ascendent" #: f.gallery.cc:1573 msgid "descending" msgstr " descendent" #: f.gallery.cc:2925 f.gallery.cc:2929 msgid "Image File" msgstr "Arxiu d'imatges" #: f.gallery.cc:2927 msgid "select thumbnail" msgstr "sel·leccionar miniatures" #: f.gallery.cc:3037 fotoxx.h:1276 msgid "Select Files" msgstr "Seleccionar arxius" #: f.gallery.cc:3097 #, c-format msgid "exceed %d selected files" msgstr "excedits %d arxius seleccionats" #: f.gallery.cc:3457 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Clic en una posició de la llista. Clic en una miniatuira de galeria per nou " "marcador.\n" "S'afegirà un ou marcador per diapositiva. Canviar el nom y pulsat [Re-" "anomenar]." #: f.gallery.cc:3484 f.gallery.cc:3734 msgid "Edit Bookmarks" msgstr "Editar marcadors" #: f.gallery.cc:3662 msgid "unable to save bookmarks file" msgstr "no puc desar arxiu de marcadors" #: f.gallery.cc:3734 msgid "Go To Bookmark" msgstr "Anar a un marcador" #: f.mashup.cc:170 msgid "Mashup layout and background image" msgstr "Capa de muntatge i imatge de fons" #: f.mashup.cc:213 msgid "choose an image file" msgstr "escollir una imatge" #: f.mashup.cc:214 msgid "use current image file" msgstr "utilitzar imatge actual" #: f.mashup.cc:215 msgid "specify layout size and color" msgstr "especificar mida i color de la capa" #: f.mashup.cc:216 msgid "open a Mashup project file" msgstr "Obrir un projecte de muntatge" #: f.mashup.cc:259 msgid "no current file" msgstr "no hi ha arxiu actiu" #: f.mashup.cc:291 msgid "Make Layout Image" msgstr "Fer una composició d'imatges" #: f.mashup.cc:293 msgid "project name" msgstr "nom de projecte" #: f.mashup.cc:319 msgid "supply a project name" msgstr "proporcioni un nom de projecte" #: f.mashup.cc:411 msgid "Edit Images" msgstr "Editar imatges" #: f.mashup.cc:412 f.mashup.cc:2450 msgid "Edit Text" msgstr "Editar text" #: f.mashup.cc:413 msgid "Edit Line" msgstr "Editar text" #: f.mashup.cc:414 msgid "Rescale" msgstr "Re-escalar" #: f.mashup.cc:419 msgid "add or edit images" msgstr "afegir o editar imatges" #: f.mashup.cc:421 msgid "add or edit text" msgstr "afegir o editar text" #: f.mashup.cc:423 msgid "add or edit lines/arrows" msgstr "afegir o editar línees/fletxes" #: f.mashup.cc:425 msgid "change project scale" msgstr "canviar escala del projecte" #: f.mashup.cc:427 msgid "project complete" msgstr "projecte complert" #: f.mashup.cc:429 msgid "cancel project" msgstr "cancel.lar projecte" #: f.mashup.cc:447 msgid "rescale project" msgstr "re-escalar projecte" #: f.mashup.cc:506 msgid "save Mashup output file" msgstr "desar arxiu de sortida del fotomuntatge" #: f.mashup.cc:526 msgid "save Mashup project file?" msgstr "desar arxiu de projecte del fotomuntatge" #: f.mashup.cc:538 msgid "delete Mashup project file?" msgstr "esborrar arxiu projecte del fotomuntatge?" #: f.mashup.cc:597 msgid "Open Project" msgstr "Obrir projecte" #: f.mashup.cc:626 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "requerida imatge de capa \n" " %s" #: f.mashup.cc:683 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "requerida imatge sobreposada \n" " %s" #: f.mashup.cc:989 msgid "project file is defective" msgstr "arxiu de projecte defectuós" #: f.mashup.cc:1019 msgid "Save Project" msgstr "Desar projecte" #: f.mashup.cc:1160 msgid "layout exceeds 2 gigabytes" msgstr "la composició excedeix 2 gigabytes" #: f.mashup.cc:1234 msgid "Click image to select, drag image to move." msgstr "Clic en la imatge per seleccionar, arrossegar imatge per moure-la" #: f.mashup.cc:1235 msgid "Make black margins transparent" msgstr "Fer transparents els marges negres" #: f.mashup.cc:1274 msgid "Current image:" msgstr "Imatge actual:" #: f.mashup.cc:1278 msgid "Cycle through images:" msgstr "Recorregut per les imatges:" #: f.mashup.cc:1285 msgid "Scale" msgstr "Escalar" #: f.mashup.cc:1294 msgid "Stacking Order" msgstr "Ordre de pila" #: f.mashup.cc:1295 msgid "Raise" msgstr "Pujar" #: f.mashup.cc:1296 msgid "Lower" msgstr "Baixar" #: f.mashup.cc:1299 msgid "Base Transparency" msgstr "Transparència" #: f.mashup.cc:1303 msgid "Var. Transparency" msgstr "Variar transparència" #: f.mashup.cc:1304 msgid "Paint" msgstr "Pintar" #: f.mashup.cc:1307 msgid "Bend and fine-align" msgstr "Combinar i alineació fina" #: f.mashup.cc:1308 f.widgets.cc:634 msgid "Warp" msgstr "Deformar" #: f.mashup.cc:1317 zfuncs.cc:11351 zfuncs.cc:11360 msgid "Margins" msgstr "Marges" #: f.mashup.cc:1318 msgid "Hard" msgstr "Fort" #: f.mashup.cc:1319 f.repair.cc:4410 msgid "Blend" msgstr "Barrejar" #: f.mashup.cc:1333 msgid "add images to layout" msgstr "afegir imatges a la capa" #: f.mashup.cc:1570 msgid "Paint Image Transparencies" msgstr "Pintar transparències de la imatge" #: f.mashup.cc:1588 msgid "Gradual" msgstr "Gradual" #: f.mashup.cc:1590 fotoxx.h:1255 msgid "Power" msgstr "Força" #: f.mashup.cc:1823 msgid "Pull on the image with the mouse." msgstr "Empènyer la imatge amb el ratolí" #: f.mashup.cc:1839 msgid "Warp Image" msgstr "Deformar imatge" #: f.mashup.cc:1845 f.warp.cc:1383 msgid "warp span" msgstr "intèrval de deformació" #: f.mashup.cc:2281 #, c-format msgid "exceeded %d images" msgstr "excedit %d imatges" #: f.mashup.cc:2423 msgid "Enter text, [Add] to layout, edit properties." msgstr "Afegir text, [Afegir] a la capa, editar propietats" #: f.mashup.cc:2503 msgid "Text File:" msgstr "arxiu de text:" #: f.mashup.cc:2507 msgid "add entered text to layout" msgstr "afegir text a la capa" #: f.mashup.cc:2641 msgid "click position to add text" msgstr "clic en la posició per afegir text" #: f.mashup.cc:2647 #, c-format msgid "exceeded %d text entries" msgstr "excedit%d entrades de text" #: f.mashup.cc:2652 f.widgets.cc:485 msgid "Add Text" msgstr "Afegir text" #: f.mashup.cc:2761 msgid "Set line properties, [Add] to layout, edit." msgstr "Ajustar propietats de línia, [Afegir] a la capa, editar." #: f.mashup.cc:2785 msgid "Edit Line/Arrow" msgstr "Editar linea/fletxa" #: f.mashup.cc:2840 msgid "add line/arrow to layout" msgstr "afegir línia/fletxa a la capa" #: f.mashup.cc:2931 msgid "click position to add line" msgstr "Clic en la posició per afegir línia" #: f.mashup.cc:2937 #, c-format msgid "exceeded %d line entries" msgstr "excedides %d entrades de línia" #: f.mashup.cc:2942 f.widgets.cc:486 msgid "Add Line" msgstr "Afegir línia" #: f.mashup.cc:4189 msgid "Image Montage" msgstr "Montatge d'imatges" #: f.mashup.cc:4198 msgid "Frame Width" msgstr "Ample del marc" #: f.mashup.cc:4201 f.mashup.cc:4209 msgid "Margin" msgstr "Marge" #: f.mashup.cc:4206 msgid "Image Columns" msgstr "Columnes d'imatge" #: f.mashup.cc:4223 #, c-format msgid "exceed %d rows" msgstr "excedides %d columnes" #: f.mashup.cc:4319 msgid "Optimize" msgstr "Optimitzar" #: f.mashup.cc:4327 #, c-format msgid "column difference: %d pixels" msgstr "diferència de columnes: %d píxels" #: f.mashup.cc:4385 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "deiferència de columnes: %d píxels \n" "Fer columnes igualment?" #: f.mashup.cc:4407 msgid "Save with unique montage name" msgstr "Guardar amb un únic nom de muntatge" #: f.mashup.cc:4409 msgid "unique name:" msgstr "nom únic" #: f.mashup.cc:4411 msgid "create image map" msgstr "crear mapa d'imatge" #: f.mashup.cc:4421 f.meta.cc:8637 msgid "supply a reasonable name" msgstr "donar un nom raonable" #: f.mashup.cc:4432 msgid "save montage" msgstr "guaradr muntatge" #: f.mashup.cc:4452 #, c-format msgid "map file saved: %s" msgstr "arxiu de mapa guardat: %s" #: f.mashup.cc:4492 #, c-format msgid "%d max images exceeded" msgstr "%d màxim d'imatges excedit" #: f.mashup.cc:4566 f.mashup.cc:4572 #, c-format msgid "image frame is too large: %d x %d" msgstr "marc d'imatge és massa ample: %d x %d" #: f.mashup.cc:4873 #, c-format msgid "montage map file not found: %s" msgstr "arxiu mapa de muntatge no trobat: %s" #: f.mashup.cc:4877 #, c-format msgid "montage map file invalid: %s" msgstr "arxiu de mapoa de muntatge invàlid: %s" #: f.meta.cc:242 msgid "Add Metadata Items" msgstr "Afegir ítems de metadades" #: f.meta.cc:246 f.meta.cc:1583 f.meta.cc:3104 msgid "click to select" msgstr "clic per seleccionar" #: f.meta.cc:251 msgid "click to unselect" msgstr "clic per deseleccionar" #: f.meta.cc:475 msgid "Extras" msgstr "Extres" #: f.meta.cc:475 f.meta.cc:655 f.widgets.cc:754 msgid "View Metadata" msgstr "Veure metadades" #: f.meta.cc:813 f.widgets.cc:450 f.widgets.cc:755 msgid "Edit Metadata" msgstr "Editar metadades" #: f.meta.cc:816 msgid "save metadata to file" msgstr "desar metadades en l'arxiu" #: f.meta.cc:825 msgid "Image Date" msgstr "Data de la imatge" #: f.meta.cc:828 msgid "Time" msgstr "Hora" #: f.meta.cc:836 msgid "Rating (stars):" msgstr "Calificació (estrellas)" #: f.meta.cc:848 msgid "Caption" msgstr "Títol" #: f.meta.cc:853 msgid "Comments" msgstr "Comentaris" #: f.meta.cc:860 msgid "Location" msgstr "Ubicació" #: f.meta.cc:863 msgid "Country" msgstr "Pais" #: f.meta.cc:886 msgid "Image Tags" msgstr "Etiquetes d'imatge" #: f.meta.cc:891 f.meta.cc:1992 msgid "Recent Tags" msgstr "etiquetes recents" #: f.meta.cc:896 f.meta.cc:1998 msgid "Enter New Tag" msgstr "Introduïr nova etiqueta" #: f.meta.cc:902 f.meta.cc:2004 f.meta.cc:4565 msgid "Matching Tags" msgstr "Construïnt etiquetes" #: f.meta.cc:909 f.meta.cc:2013 f.meta.cc:2511 f.meta.cc:4571 msgid "Defined Tags Category" msgstr "Categoria d'etiquetes definida" #: f.meta.cc:916 msgid "search known locations" msgstr "mostrar localitats conegudes" #: f.meta.cc:917 msgid "search using web service" msgstr "buscar utilitzan servei web" #: f.meta.cc:918 f.meta.cc:2020 msgid "create tag categories and tags" msgstr "crear etiquetes i categories d'etiquetes" #: f.meta.cc:1368 f.meta.cc:3673 f.meta.cc:7231 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "latitud/longitud errònia: %s %s" #: f.meta.cc:1423 fotoxx.h:1234 msgid "Manage Tags" msgstr "Gestionar etiquetes" #: f.meta.cc:1423 msgid "orphan tags" msgstr "etiquetes òrfenes" #: f.meta.cc:1427 msgid "category" msgstr "categoria" #: f.meta.cc:1430 msgid "tag" msgstr "etiqueta" #: f.meta.cc:1437 msgid "Defined Tags:" msgstr "etiquetes definides:" #: f.meta.cc:1579 f.widgets.cc:451 f.widgets.cc:756 msgid "Edit Any Metadata" msgstr "Editar qualsevol metadada" #: f.meta.cc:1579 f.meta.cc:3099 msgid "Full List" msgstr "Llista completa" #: f.meta.cc:1592 f.meta.cc:3115 msgid "key name" msgstr "nom de clau" #: f.meta.cc:1595 f.meta.cc:3116 msgid "key value" msgstr "valor de clau" #: f.meta.cc:1677 f.meta.cc:3259 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" "El comando: $ man Image::ExifTool::TagNames \n" "mostrará fins a 15000 \"standard\" noms d'etiqueta" #: f.meta.cc:1777 f.widgets.cc:452 msgid "Delete Metadata" msgstr "Esborrar metadades" #: f.meta.cc:1783 fotoxx.h:1179 msgid "All" msgstr "Tot" #: f.meta.cc:1784 msgid "One Key:" msgstr "Una clau:" #: f.meta.cc:1968 f.widgets.cc:568 msgid "Batch Add/Remove Tags" msgstr "Afegir/eliminar etiquetes en lot" #: f.meta.cc:1981 msgid "tags to add" msgstr "Etiquetes per afegir" #: f.meta.cc:1982 msgid "tags to remove" msgstr "etiquetes per eliminar" #: f.meta.cc:2095 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " massa etiquetes" #: f.meta.cc:2108 msgid "repeat with same files?" msgstr "Repetir amb els mateixos arxius" #: f.meta.cc:2290 msgid "specify files and tags" msgstr "especificar arxius i etiquetes" #: f.meta.cc:2490 f.widgets.cc:569 msgid "Batch Rename Tags" msgstr "Reanomenar etiquetes en lot" #: f.meta.cc:2500 msgid "(click defined tag)" msgstr "(clic en etiqueta definida)" #: f.meta.cc:2502 f.process.cc:749 msgid "Rename to" msgstr "Re-anomenar com" #: f.meta.cc:2519 msgid "old tag name >> new tag name" msgstr "anis¡c nom d'etiqueta >> nou nom d'etiqueta" #: f.meta.cc:2576 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" "%d etiquetes per reanomenar \n" "en %d imatges. \n" "procedir?" #: f.meta.cc:2720 msgid "max tags exceeded" msgstr "máxim d'etiquetes excedit" #: f.meta.cc:2782 f.widgets.cc:570 msgid "Batch Photo Date/Time" msgstr "Data/hora de les fotos en lot" #: f.meta.cc:2790 msgid "set a new date/time:" msgstr "establir un ana nova data/hora" #: f.meta.cc:2798 msgid "shift existing date/time:" msgstr "canviar data/hora existents" #: f.meta.cc:2824 msgid "test: show changes, do not update files" msgstr "prova: mostra canvis sense modificar arxius" #: f.meta.cc:2850 f.meta.cc:3190 f.meta.cc:3368 msgid "no files selected" msgstr "no hi han arxius seleccionats" #: f.meta.cc:2880 f.meta.cc:2888 f.meta.cc:2894 msgid "invalid date/time format" msgstr "format de data/hora no vàlid" #: f.meta.cc:3099 f.widgets.cc:571 msgid "Batch Add/Change Metadata" msgstr "Afegir/canviar metadades en lot" #: f.meta.cc:3184 msgid "enter key names" msgstr "introduïr noms clau" #: f.meta.cc:3354 f.widgets.cc:572 msgid "Batch Report Metadata" msgstr "Informe de metada​des en lot" #: f.meta.cc:3498 f.widgets.cc:573 msgid "Batch Geotags" msgstr "Geo-etiquetes en lot" #: f.meta.cc:3528 msgid "location" msgstr "ubicació" #: f.meta.cc:3531 msgid "country" msgstr "pais" #: f.meta.cc:3659 msgid "" "data is incomplete \n" " proceed?" msgstr "" "les dades són incompletas \n" " procedir?" #: f.meta.cc:3745 msgid "Report Image Locations" msgstr "Informe de localitzacions d'imatges" #: f.meta.cc:3746 msgid "Group by country" msgstr "Agrupar per pais" #: f.meta.cc:3747 msgid "Group by country/location" msgstr "Agrupar per pais/ubicació" #: f.meta.cc:3748 msgid "Group by country/location/date" msgstr "Agrupar pais/ubicació/data" #: f.meta.cc:3749 msgid "Group by date/country/location" msgstr "Agrupar data/pais/ubicació" #: f.meta.cc:3752 msgid "Combine within" msgstr "Conbinar dins de" #: f.meta.cc:3754 msgid "days" msgstr "dies" #: f.meta.cc:3860 f.widgets.cc:1026 f.widgets.cc:1048 msgid "Image Locations" msgstr "Ubicació d'imatge" #: f.meta.cc:4137 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" #: f.meta.cc:4489 msgid "Search Image Metadata" msgstr "Buscar metadades de la imatge" #: f.meta.cc:4493 msgid "images to search:" msgstr "imatges a buscar:" #: f.meta.cc:4494 msgid "all" msgstr "tot" #: f.meta.cc:4495 msgid "current set only" msgstr "omés selecció actual" #: f.meta.cc:4498 msgid "matching images:" msgstr "imatges coïncidents" #: f.meta.cc:4499 msgid "new set" msgstr "nova selecció" #: f.meta.cc:4500 msgid "add to set" msgstr "afegir a la selecció" #: f.meta.cc:4501 msgid "remove" msgstr "eliminar" #: f.meta.cc:4504 msgid "report type:" msgstr "tipus d'informe" #: f.meta.cc:4505 msgid "gallery" msgstr "galeria" #: f.meta.cc:4509 msgid "date range" msgstr "rand de dates" #: f.meta.cc:4514 msgid "photo date" msgstr "data de la foto" #: f.meta.cc:4515 msgid "file date" msgstr "data d'arxiu" #: f.meta.cc:4516 msgid "(yyyy-mm-dd)" msgstr "(aaaa-mm-dd" #: f.meta.cc:4519 msgid "stars range" msgstr "rang d'estrelles" #: f.meta.cc:4522 msgid "last version only" msgstr "només última versió" #: f.meta.cc:4524 msgid "all/any" msgstr "tot/qualsevol" #: f.meta.cc:4527 msgid "search tags" msgstr "buscar etiquetes" #: f.meta.cc:4533 msgid "search text" msgstr "buscar text" #: f.meta.cc:4539 msgid "search files" msgstr "buscar arxius" #: f.meta.cc:4545 msgid "search locations" msgstr "buscar localitats" #: f.meta.cc:4549 msgid "enter cities, countries" msgstr "introduïr ciutats, països" #: f.meta.cc:4554 msgid "search other metadata" msgstr "buscar altres metadades" #: f.meta.cc:4561 msgid "Enter Search Tag" msgstr "Introduïr etiquetes de cerca" #: f.meta.cc:4837 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "per eliminar imatges de laa selecció actual, \n" "buscar en la selecció actual" #: f.meta.cc:4844 msgid "" "to add images to current set, \n" "search all images" msgstr "" "per afegir imatges a la selecció actual, \n" "buscar en totes les imatges" #: f.meta.cc:4890 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "cerca de dades no raonable \n" " %s %s" #: f.meta.cc:4915 msgid "stars range not reasonable" msgstr "rang d'estrellas no raonable" #: f.meta.cc:5129 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "imatges afegides: %d eliminades: %d nova quantitat: %d" #: f.meta.cc:5132 msgid "no changes made" msgstr "no s'han fet canvis" #: f.meta.cc:5303 msgid "" "These items are always reported: \n" "date, stars, tags, caption, comment" msgstr "" "Sempre s'informa d'aquests detalls: \n" "data, estrelles, etiquetes, llegenda, comentari" #: f.meta.cc:5328 msgid "Additional Items for Report" msgstr "Detalls addicionals per a l'informe" #: f.meta.cc:5335 msgid "Keyword" msgstr "Paraula clau" #: f.meta.cc:5349 msgid "Match Criteria" msgstr "Criteri de coincidència" #: f.meta.cc:5455 #, c-format msgid "bad number: %s" msgstr "nombre erroni: %s" #: f.meta.cc:5712 msgid "date format is YYYY-MM-DD" msgstr "format de data es AAAA-MM-DD" #: f.meta.cc:5716 msgid "date is invalid" msgstr "data no vàlida" #: f.meta.cc:5750 msgid "time format is HH:MM [:SS]" msgstr "fomat d'hora es HH:MM [:SS]" #: f.meta.cc:5754 msgid "time is invalid" msgstr "hora no vàlida" #: f.meta.cc:6851 msgid "not found" msgstr "no trobada" #: f.meta.cc:6852 msgid "location and country required" msgstr "és necessari ubicació i pais" #: f.meta.cc:7111 msgid "choose location" msgstr "escollir ubicació" #: f.meta.cc:7408 msgid "Set Map Markers" msgstr "Establir marcadors de mapa" #: f.meta.cc:7409 msgid "mark all image files" msgstr "marcar totes les imatges" #: f.meta.cc:7410 msgid "mark current gallery" msgstr "marcar galeria actual" #: f.meta.cc:7510 msgid "choose map file" msgstr "escollir arxiu de mapes" #: f.meta.cc:7652 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "paquet fotoxx-maps no instal·lat \n" "(veure https://kornelix.net)" #: f.meta.cc:7657 #, c-format msgid "map file %s is missing" msgstr "es necessita arxiu de mapes %s" #: f.meta.cc:7661 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" "dades de latitut/longitut inconsistents \n" " %.3f %.3f %.3f %.3f" #: f.meta.cc:7981 f.meta.cc:8508 msgid "No matching images found" msgstr "No s'han trobat imatges coincidents" #: f.meta.cc:8073 msgid "Set Map Source" msgstr "Establir origen de mapas" #: f.meta.cc:8584 msgid "Net Map Locations" msgstr "Ubicacions Net Map" #: f.meta.cc:8589 msgid "map location:" msgstr "ubicació de mapa" #: f.process.cc:123 f.widgets.cc:559 msgid "Batch Convert" msgstr "Convertir en lot" #: f.process.cc:135 msgid "Sequence Numbers" msgstr "Números seqüencials" #: f.process.cc:137 msgid "base" msgstr "inici" #: f.process.cc:140 msgid "adder" msgstr "increment" #: f.process.cc:145 msgid "New Location" msgstr "Nova ubicació" #: f.process.cc:150 msgid "New File Type" msgstr "Nou tipus d'arxiu" #: f.process.cc:154 f.process.cc:162 f.process.cc:2538 msgid "no change" msgstr "sense canvis" #: f.process.cc:157 f.process.cc:2533 msgid "max. Width" msgstr "Ample màx." #: f.process.cc:166 msgid "Delete Originals" msgstr "Esborrar originals" #: f.process.cc:167 f.process.cc:754 msgid "Copy Metadata" msgstr "Copiar metadades" #: f.process.cc:171 f.process.cc:756 f.process.cc:1320 f.repair.cc:121 #: f.widgets.cc:492 msgid "Sharpen" msgstr "Enfocar" #: f.process.cc:181 f.process.cc:757 msgid "Overlay Image" msgstr "Imatge de capa" #: f.process.cc:184 msgid "Width %" msgstr "Amplada %" #: f.process.cc:189 msgid "Position" msgstr "Posició" #: f.process.cc:201 msgid "Make constant size for screen:" msgstr "Mida constant per a la pantalla" #: f.process.cc:209 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "plugins: (any mes dia nom-antic seqüència) $aaaa $mm $dd $nomantic $s" #: f.process.cc:331 #, c-format msgid "file type not supported: %s \n" msgstr "tipus d'arxiu no suportat: %s \n" #: f.process.cc:471 f.process.cc:2588 msgid "cannot create new file" msgstr "no es pot crear un nou arxiu" #: f.process.cc:517 msgid "updating albums ..." msgstr "actualitzant àlbums ..." #: f.process.cc:658 #, c-format msgid "invalid plugin: %s" msgstr "plugin invàlid: %s" #: f.process.cc:665 msgid "you must use either $s or $oldname" msgstr "ha d'utilitzar $s o $nomantic" #: f.process.cc:670 msgid "$s plugin needs base and adder" msgstr "plugin $s necessita una base i un increment" #: f.process.cc:675 msgid "base and adder need $s plugin" msgstr "base i increment necessiten plugin $s" #: f.process.cc:697 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "max. mida %d x %d no és raonable" #: f.process.cc:704 msgid "specify overlay image file" msgstr "especificar imatge de capa" #: f.process.cc:720 msgid "specify overlay position" msgstr "especificar posició de capa" #: f.process.cc:748 #, c-format msgid "Convert %d image files" msgstr "Convertir %d imatges" #: f.process.cc:750 msgid "Convert to" msgstr "Convertir a" #: f.process.cc:751 msgid "Resize within" msgstr "Redimmensionar dins de" #: f.process.cc:752 msgid "Output to" msgstr "Sortida com" #: f.process.cc:758 msgid "*** Delete Originals ***" msgstr "*** Esborrar originals ***" #: f.process.cc:759 msgid "*** Replace Originals ***" msgstr "*** Substituir originals ***" #: f.process.cc:760 msgid "PROCEED?" msgstr "PROCEDIR?" #: f.process.cc:913 f.widgets.cc:560 msgid "Batch Upright" msgstr "Adressar en lot" #: f.process.cc:919 msgid "Survey all files" msgstr "Examinar tots els arxius" #: f.process.cc:956 msgid "file cannot be read" msgstr "l'arxiu no pot ser llegit" #: f.process.cc:1072 msgid "cannot select both options" msgstr "no es poden sel·leccionar ambdues opcions" #: f.process.cc:1114 f.widgets.cc:561 msgid "Batch Delete/Trash" msgstr "Esborrar/paperera en lot" #: f.process.cc:1119 msgid "delete" msgstr "esborrar" #: f.process.cc:1122 msgid "trash" msgstr "paperera" #: f.process.cc:1260 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Convertir en lot arxius RAW (RawTherapee)" #: f.process.cc:1290 msgid "use libraw" msgstr "utilitzar libraw" #: f.process.cc:1291 msgid "use Raw Therapee" msgstr "utilitzar Raw Therapee" #: f.process.cc:1298 msgid "output location" msgstr "ubicació exterior" #: f.process.cc:1303 msgid "output file type" msgstr "tipus d'arxiu de sortida" #: f.process.cc:1322 msgid "amount" msgstr "quantitat" #: f.process.cc:1325 msgid "threshold" msgstr "umbral" #: f.process.cc:1614 msgid "script file" msgstr "arxiu de script" #: f.process.cc:1627 msgid "begin making a script file" msgstr "iniciar construcció d'un script" #: f.process.cc:1630 msgid "finish making a script file" msgstr "acabant construcció d'un script" #: f.process.cc:1636 msgid "execute a script file" msgstr "executar un script" #: f.process.cc:1696 msgid "script already started" msgstr "script iniciat" #: f.process.cc:1700 msgid "start a new script file" msgstr "iniciar un nou script" #: f.process.cc:1723 msgid "perform edits to be included in the script file" msgstr "performar edicions per ser inloses en l'script" #: f.process.cc:1741 msgid "script file error" msgstr "error d'script" #: f.process.cc:1746 #, c-format msgid "%s added to script" msgstr "%s afegit a l'script" #: f.process.cc:1759 msgid "no script file was started" msgstr "no s'ha iniciat l'script" #: f.process.cc:1767 msgid "script file closed" msgstr "script tancat" #: f.process.cc:1825 msgid "open script file" msgstr "obrir script" #: f.process.cc:1833 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "script: %s \n" " %s" #: f.process.cc:1855 f.process.cc:1904 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "error d'apertura: %s \n" " %s" #: f.process.cc:1876 #, c-format msgid "unknown edit function: %s" msgstr "funció d'edició desconeguda: %s" #: f.process.cc:1885 #, c-format msgid "load widgets failed: %s" msgstr "carregar complements que han fallat: %s" #: f.process.cc:1913 #, c-format msgid "script file format error: %s" msgstr "error de format d'script: %s" #: f.process.cc:1949 msgid "growisofs not installed" msgstr "growisofs no instal·lat" #: f.process.cc:2001 msgid "no DVD/BlueRay device found" msgstr "No es troba dispositiu DVD/Blue Ray" #: f.process.cc:2024 f.widgets.cc:564 msgid "Burn Images to DVD/BlueRay" msgstr "Gravar imatges en un DVD/Blue Ray" #: f.process.cc:2029 msgid "Select device" msgstr "Sel·leccionar dispositiu" #: f.process.cc:2136 f.widgets.cc:565 msgid "Find Duplicate Images" msgstr "Trobar imatges duplicades" #: f.process.cc:2138 msgid "Thumbnail size" msgstr "Tamany de miniatura" #: f.process.cc:2142 msgid "pixel difference" msgstr "diferència de píxels" #: f.process.cc:2145 msgid "pixel count" msgstr "conteig de píxels" #: f.process.cc:2152 msgid "Duplicates:" msgstr "Duplicats:" #: f.process.cc:2170 #, c-format msgid "only %d image thumbnails found" msgstr "trobades tant sols % imatges" #: f.process.cc:2374 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Crear un arxiu de les imatges sel·leccionades" #: f.process.cc:2398 f.process.cc:2467 msgid "Output File" msgstr "Arxiu de sortida" #: f.process.cc:2418 msgid "no input files selected" msgstr "No sel·leccionats arxius d'entrada" #: f.process.cc:2424 msgid "no output file selected" msgstr "No sel·leccionat arxiu de sortida" #: f.process.cc:2523 f.widgets.cc:567 msgid "Export Image Files" msgstr "Exportar imatges" #: f.process.cc:2528 msgid "To Location" msgstr "A ubicació" #: f.process.cc:2563 msgid "file type not supported" msgstr "tipus d'arxiu no suportat" #: f.process.cc:2643 msgid "location is not a directory" msgstr "la ubicació no és al directori" #: f.repair.cc:164 msgid "dark" msgstr "fosc" #: f.repair.cc:165 msgid "light" msgstr "clar" #: f.repair.cc:1193 msgid "Apply repeatedly while watching the image." msgstr "Aplicar repetidament mentre es visualitza la imatge" #: f.repair.cc:1194 msgid "Measure" msgstr "Mida" #: f.repair.cc:1228 msgid "Noise Reduction" msgstr "Reducció de soroll" #: f.repair.cc:1247 f.widgets.cc:479 f.widgets.cc:774 fotoxx.h:1218 msgid "Flatten" msgstr "Aplanar" #: f.repair.cc:1248 msgid "Median" msgstr "Mediana" #: f.repair.cc:1263 msgid "dark areas" msgstr "àrees fosques" #: f.repair.cc:1265 msgid "all areas" msgstr "totes les àrees" #: f.repair.cc:1856 msgid "Measure Noise" msgstr "Mida de soroll" #: f.repair.cc:1857 msgid "Move mouse in a monotone image area." msgstr "Moure ratolí en un àrea de color uniforme de la imatge." #: f.repair.cc:2165 msgid "" "Method 1:\n" " Left-click on red-eye to darken.\n" "Method 2:\n" " Drag down and right to enclose red-eye.\n" " Left-click on red-eye to darken.\n" "Undo red-eye:\n" " Right-click on red-eye." msgstr "" "Mètode 1:\n" " Clic esquerra en l'ull vermell.\n" "Mètode 2:\n" " Arrossegar abaix i a la dretaq per incloure l'ull vermell.\n" " Clic esquerra en l'ull vermell.\n" "Desfer ulls vermells:\n" " Clic dret en l'ull vermell." #: f.repair.cc:2181 msgid "Red Eye Reduction" msgstr "Reducció d'ulls vermells" #: f.repair.cc:2642 f.widgets.cc:496 msgid "Color Mode" msgstr "Mode de color" #: f.repair.cc:2645 msgid "reset" msgstr "reinicialitzar" #: f.repair.cc:2646 msgid "black/white positive" msgstr "positiu blanc i negre" #: f.repair.cc:2647 msgid "black/white negative" msgstr "negatiu blanc i negre" #: f.repair.cc:2648 msgid "color negative" msgstr "negatiu color" #: f.repair.cc:2649 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.repair.cc:2650 msgid "sepia" msgstr "sèpia" #: f.repair.cc:2861 msgid "Set color depth to 1-16 bits" msgstr "Ajustar la profunditat de color a 1-16 bits" #: f.repair.cc:2875 msgid "Set Color Depth" msgstr "Ajustar la profunditat de color" #: f.repair.cc:3025 f.widgets.cc:498 msgid "Shift Colors" msgstr "Modificar colors" #: f.repair.cc:3259 f.widgets.cc:499 msgid "Color Saturation" msgstr "Saturació de color" #: f.repair.cc:3448 msgid "+Brightness" msgstr "Brillantor" #: f.repair.cc:3449 msgid "+Red -Cyan" msgstr "+Vermell -Cian" #: f.repair.cc:3450 msgid "+Green -Magenta" msgstr "+Verd -Magenta" #: f.repair.cc:3451 msgid "+Blue -Yellow" msgstr "+Blae -Groc" #: f.repair.cc:3457 fotoxx.h:1261 msgid "Red" msgstr "Vermell" #: f.repair.cc:3458 fotoxx.h:1220 msgid "Green" msgstr "Verd" #: f.repair.cc:3459 fotoxx.h:1186 msgid "Blue" msgstr "Blau" #: f.repair.cc:3761 msgid "Input color to match and adjust:" msgstr "Entar color per coincidir i ajustar" #: f.repair.cc:3763 msgid "shift+click on image to select color" msgstr "Majúss+clic en la imatge per a sel·leccionar color" #: f.repair.cc:3766 f.repair.cc:7937 msgid "Match using:" msgstr "Usan​t coincidèencia:" #: f.repair.cc:3767 msgid "Hue" msgstr "Tonalitat" #: f.repair.cc:3779 msgid "Output Color" msgstr "Color de sortida" #: f.repair.cc:3802 msgid "Adjustment" msgstr "Ajustament" #: f.repair.cc:4320 msgid "Click image to select areas." msgstr "Clic en la imatge per seleccionar àrees" #: f.repair.cc:4352 msgid "Local Color" msgstr "Colr local" #: f.repair.cc:4363 msgid "Metric:" msgstr "Mètrica:" #: f.repair.cc:4786 msgid "Color Match Images" msgstr "Concordar color d'imatges" #: f.repair.cc:4812 msgid "mouse radius for color sample" msgstr "radi del ratolí per mostra de color" #: f.repair.cc:4814 f.repair.cc:4819 fotoxx.h:1250 msgid "Open" msgstr "Obrir" #: f.repair.cc:4815 msgid "image for source color" msgstr "imatge per el color d'origen" #: f.repair.cc:4817 msgid "click on image to get source color" msgstr "clic en la imatge per obtenir color d'origen" #: f.repair.cc:4820 msgid "image to set matching color" msgstr "imatge per establir concordància de color" #: f.repair.cc:4822 msgid "click on image to set matching color" msgstr "click on image to set matching color" #: f.repair.cc:4881 msgid "select source image color first" msgstr "seleccionar primer el color de la imatge d'origen" #: f.repair.cc:5059 msgid "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " msgstr "" " Ajustar cada color RGB per minimitzar \n" " les franges de color en els extrems de la imatge. " #: f.repair.cc:5082 f.widgets.cc:504 msgid "Color Fringes" msgstr "Franges de color" #: f.repair.cc:5240 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Arrossegar el ratolí per seleccionar. \n" "2. Esborrar. 3. Repetir. " #: f.repair.cc:5262 f.widgets.cc:505 msgid "Smart Erase" msgstr "Esborrat intel.ligent" #: f.repair.cc:5267 fotoxx.h:1259 msgid "Radius" msgstr "Radi" #: f.repair.cc:5269 f.widgets.cc:493 msgid "Blur" msgstr "Desenfocar" #: f.repair.cc:5272 msgid "New Area" msgstr "Nova àrea" #: f.repair.cc:5617 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Trazar una línea en la imagen en \n" "la dirección del cambio de brillo." #: f.repair.cc:5656 f.widgets.cc:506 msgid "Brightness Ramp" msgstr "Rampa de brillantor" #: f.repair.cc:6182 f.widgets.cc:507 msgid "Remove Dust" msgstr "Retirar pols" #: f.repair.cc:6186 msgid "spot size limit" msgstr "límit de mida del punt" #: f.repair.cc:6189 msgid "max. brightness" msgstr "max. brillantor" #: f.repair.cc:6192 msgid "min. contrast" msgstr "min. contrast" #: f.repair.cc:6999 f.widgets.cc:510 msgid "Stuck Pixels" msgstr "Píxels retinguts" #: f.repair.cc:7005 msgid "pixel group" msgstr "agrupar píxels" #: f.repair.cc:7013 f.repair.cc:7085 msgid "stuck pixels:" msgstr "píxels defectuosos" #: f.repair.cc:7113 msgid "Load Stuck Pixels" msgstr "Carregar píxels defectuosos" #: f.repair.cc:7115 msgid "File:" msgstr "Arxiu:" #: f.repair.cc:7139 f.repair.cc:7196 msgid "Stuck Pixels file" msgstr "Arxiu de píxels defectuosos" #: f.repair.cc:7176 f.tools.cc:4220 msgid "file format error" msgstr "error de format d'arxiu" #: f.repair.cc:7622 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "arrossegar esquerra: afegir transparència \n" "arrossegar dret: afegir opacitat" #: f.repair.cc:7654 f.widgets.cc:511 msgid "Paint Transparency" msgstr "Pintar transparència" #: f.repair.cc:7662 msgid "paintbrush radius" msgstr "radi del pincell" #: f.repair.cc:7669 msgid "gradual paint" msgstr "pintat gradual" #: f.repair.cc:7923 f.widgets.cc:512 msgid "Add Transparency" msgstr "Afegir transparència" #: f.repair.cc:7927 msgid "Match Brightness" msgstr "Coincidència de brillantor" #: f.repair.cc:7932 msgid "Match Color" msgstr "Coincidència de color" #: f.repair.cc:7934 msgid "click on image to select color" msgstr "clic en la imatge per sel·leccionar color" #: f.repair.cc:7950 msgid "Invert Match" msgstr "Invertir coincidència" #: f.repair.cc:7953 fotoxx.h:1285 msgid "Transparency" msgstr "Transparència" #: f.tools.cc:86 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" "Sel·leccionar directoris contenint imatges \n" "(s'inclouràn automàticament els subdirectoris)." #: f.tools.cc:88 msgid "Select to add, click on X to delete." msgstr "Sel·leccionar per afegir, cili la X per esborrar" #: f.tools.cc:89 msgid "directory for thumbnails" msgstr "directori per miniatures" #: f.tools.cc:90 msgid "extra metadata items to include in index" msgstr "ítems de metadades extra no inclosos en l'índex" #: f.tools.cc:91 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Acabada la funció d'indexat. \n" "L'indexat és requerit per cercar i funcions de mapa \n" "i oer fer les pàgines de galeria acceptablement ràpides." #: f.tools.cc:126 f.widgets.cc:579 msgid "Index Image Files" msgstr "Arxiu índex d'imatges" #: f.tools.cc:298 msgid "Choose top image directories" msgstr "Seleccionar el dirctori d'imatges principal" #: f.tools.cc:299 msgid "Choose thumbnail directory" msgstr "Escollir directori de miniatures" #: f.tools.cc:300 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Tots els arxiusd'imatge seràn re-indexats. \n" " Continuar?" #: f.tools.cc:372 msgid "" "No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files." msgstr "" "No s'ha trobat arxiu índex.\n" "Es crearà un arxiu índex.\n" "Les imatges no es modificaràn\n" "Això pot necessitar un temps considerable\n" "si te milers d'imatges." #: f.tools.cc:378 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Directori invàlid: \n" " %s \n" "Si us plau esborrar." #: f.tools.cc:381 #, c-format msgid "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" msgstr "" "El directori de miniatures: \n" " %s \n" "s'ha d'anomenar .../thumbnails" #: f.tools.cc:384 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Directori duplicat: \n" " %s \n" "Si us plau esborrar." #: f.tools.cc:443 msgid "specify 1 thumbnail directory" msgstr "especificar un directori de miniatures" #: f.tools.cc:628 msgid "Top directories have no images" msgstr "Directoris superiors no tenen im​at​ges" #: f.tools.cc:1075 msgid "Cancel image index function?" msgstr "Cancel·lar funció d'indexat d'imatge?" #: f.tools.cc:1195 msgid "Recent Files Gallery" msgstr "Galeria d'arxius recents" #: f.tools.cc:1196 msgid "Newest Files Gallery" msgstr "Galeria d'arxius nous" #: f.tools.cc:1197 msgid "Specific Gallery" msgstr "Galeria específica" #: f.tools.cc:1198 msgid "Album Gallery" msgstr "Galeria d'àlbums" #: f.tools.cc:1199 msgid "Previous Gallery" msgstr "Galeria prèvia" #: f.tools.cc:1200 msgid "Previous Image File" msgstr "Imatge prèvia" #: f.tools.cc:1201 msgid "Specific Image File" msgstr "Imatge específica" #: f.tools.cc:1202 f.widgets.cc:435 msgid "Blank Window" msgstr "Finestra buida" #: f.tools.cc:1250 f.widgets.cc:580 msgid "User Settings" msgstr "Ajustaments de l'usuari" #: f.tools.cc:1253 msgid "Startup Display" msgstr "Pantalla d'inici" #: f.tools.cc:1264 msgid "Background color:" msgstr "Color de fons" #: f.tools.cc:1265 msgid "F-View" msgstr "Vista d'arxius" #: f.tools.cc:1268 msgid "G-View" msgstr "Vista de galeries" #: f.tools.cc:1272 msgid "Menu Text" msgstr "Text del menú" #: f.tools.cc:1279 msgid "Menu Style" msgstr "Estil de menú" #: f.tools.cc:1280 msgid "Icons" msgstr "Icones" #: f.tools.cc:1281 msgid "Icons + Text" msgstr "Icones + text" #: f.tools.cc:1283 msgid "Icon size" msgstr "tamany d'icona" #: f.tools.cc:1287 msgid "Dialog font" msgstr "Font del diàleg" #: f.tools.cc:1292 msgid "Zoomed Image:" msgstr "Imatge augmentada" #: f.tools.cc:1301 msgid "JPEG save quality" msgstr "qualitat de l'arxiu JPEG" #: f.tools.cc:1305 msgid "Curve node capture distance" msgstr "Dist'ancia de captura de nodus de corba" #: f.tools.cc:1309 msgid "Map marker size" msgstr "Mida del marcador de mapa" #: f.tools.cc:1313 msgid "show last file version only" msgstr "mostrar nomès última versió d'arxiu" #: f.tools.cc:1316 msgid "shift image to right margin" msgstr "canviar imatge al marge dret" #: f.tools.cc:1319 f.tools.cc:1324 msgid "image index level" msgstr "nivell d'index d'imatge" #: f.tools.cc:1321 msgid "Fotoxx started directly" msgstr "Fotoxx iniciat directament" #: f.tools.cc:1326 msgid "Fotoxx started by file manager" msgstr "Fotoxx iniciat per explorador d'arxius" #: f.tools.cc:1329 msgid "RAW file types" msgstr "Tipus d'arxius RAW" #: f.tools.cc:1333 msgid "video file types" msgstr "tipus d'arxius de vídeo" #: f.tools.cc:1440 msgid "Select startup directory" msgstr "Seleccionar directori d'inici" #: f.tools.cc:1447 msgid "Select startup image file" msgstr "Seleccionarimatge d'inici" #: f.tools.cc:1454 msgid "Select startup album" msgstr "Sel·leccionar àlbum inicial" #: f.tools.cc:1497 msgid "startup directory is invalid" msgstr "directori d'inici no és vàlid" #: f.tools.cc:1508 msgid "startup file is invalid" msgstr "arxiu d'inici no és vàlid" #: f.tools.cc:1683 f.widgets.cc:581 msgid "Keyboard Shortcuts" msgstr "Tecles drecera" #: f.tools.cc:1690 msgid "Reserved Shortcuts \n" msgstr "Dreceres de teclat reservades \n" #: f.tools.cc:1691 msgid " F1 User Guide for current function \n" msgstr "F1 Guia d'usuari per la funció actual \n" #: f.tools.cc:1692 msgid " F10 Full Screen with menus \n" msgstr "F10 Pantalla complerta amb menús \n" #: f.tools.cc:1693 msgid " F11 Full Screen without menus \n" msgstr "F11 Pantalla complerta sense menús \n" #: f.tools.cc:1694 msgid " Escape Quit dialog; Quit Fotoxx \n" msgstr "Esc Diàleg de sortida: Sortir de Fotoxx \n" #: f.tools.cc:1695 msgid " Ctrl+H Show hidden files in Gallery mode \n" msgstr "Ctrl+H Mostrar arxius ocults en mode galeria \n" #: f.tools.cc:1696 msgid " Delete Delete/Trash dialog \n" msgstr "Supr Esborrar/Eliminar diàleg \n" #: f.tools.cc:1697 msgid " Arrows Previous/Next Image or Gallery page \n" msgstr "Fletxes Imatge \n" #: f.tools.cc:1757 msgid "Edit KB Shortcuts" msgstr "Editar tecles drecera" #: f.tools.cc:1766 msgid "shortcut key:" msgstr "tecla drecera" #: f.tools.cc:1767 msgid "(enter key)" msgstr "(entra tecla)" #: f.tools.cc:1768 f.tools.cc:1911 f.tools.cc:2015 msgid "(no selection)" msgstr "(cap selecció)" #: f.tools.cc:1905 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reservada, no es pot utilitzar" #: f.tools.cc:2036 msgid "unable to save KB-shortcuts file" msgstr "no es pot desar arxiu de tecles drecera" #: f.tools.cc:2356 f.widgets.cc:583 msgid "Grid Lines" msgstr "Línies de graella" #: f.tools.cc:2365 msgid "x-spacing" msgstr "espaiat X" #: f.tools.cc:2366 msgid "x-count" msgstr "compte X" #: f.tools.cc:2367 msgid "x-enable" msgstr "habilitar X" #: f.tools.cc:2373 msgid "y-spacing" msgstr "espaiat Y" #: f.tools.cc:2374 msgid "y-count" msgstr "compta Y" #: f.tools.cc:2375 msgid "y-enable" msgstr "habilitar Y" #: f.tools.cc:2495 f.widgets.cc:584 msgid "Line Color" msgstr "Color de línees" #: f.tools.cc:2560 msgid "Click image to select pixels." msgstr "Clic en la imatge per seleccionar píxels." #: f.tools.cc:2595 f.widgets.cc:585 msgid "Show RGB" msgstr "Mostrar RGB" #: f.tools.cc:2890 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key X to toggle dialog." msgstr "" "Arrossegar ratolí sobre la imatge. \n" "clic esquerra per cancel.lar. \n" "Tecla X per amagar diàleg." #: f.tools.cc:2918 f.widgets.cc:586 msgid "Magnify Image" msgstr "Augmentar imaten" #: f.tools.cc:2927 msgid "X-size" msgstr "Lupa" #: f.tools.cc:3172 msgid "Darkest and Brightest Pixels" msgstr "Píxels més foscos i més brillants" #: f.tools.cc:3195 msgid "Dark Limit" msgstr "Ombra" #: f.tools.cc:3196 msgid "Bright Limit" msgstr "Llum" #: f.tools.cc:3285 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "la brillantor ha de mostrar una rampa gradual \n" "extenent-se fina a les vores." #: f.tools.cc:3376 f.widgets.cc:589 msgid "Monitor Gamma" msgstr "Gamma del monitor" #: f.tools.cc:3443 msgid "Available Translations" msgstr "Traduccions disponibles" #: f.tools.cc:3447 msgid "Set Language" msgstr "Sel.leccionar idioma" #: f.tools.cc:3573 msgid "Change Color Profile" msgstr "Canviar perfil de color" #: f.tools.cc:3577 msgid "input profile" msgstr "perfil d'entrada" #: f.tools.cc:3581 msgid "output profile" msgstr "perfil de sortida" #: f.tools.cc:3602 msgid "Unable to change EXIF color profile" msgstr "No es pot canviar el perfil de color EXIF" #: f.tools.cc:3604 msgid "automatic new version created" msgstr "creada nova versió automàtica" #: f.tools.cc:3613 msgid "color profile" msgstr "perfil de color" #: f.tools.cc:3661 f.tools.cc:3667 #, c-format msgid "unknown cms profile %s" msgstr "perfil cms desconegut %s" #: f.tools.cc:3770 f.widgets.cc:593 msgid "Calibrate Printer" msgstr "Calibrar impresora" #: f.tools.cc:3795 msgid "print color chart" msgstr "imprimir carta de colors" #: f.tools.cc:3796 msgid "scan and save color chart" msgstr "escanejar y desar carta de colors" #: f.tools.cc:3797 msgid "align and trim color chart" msgstr "adressar i retalla carta de colors" #: f.tools.cc:3798 msgid "open and process color chart" msgstr "obrir i processar carta de colors" #: f.tools.cc:3799 msgid "print image with revised colors" msgstr "imprimir imatge amb colors revisats" #: f.tools.cc:3940 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Escanejar la carta de color impresa. \n" "La columna mès fosca a dalt. \n" "Desar com %s/" #: f.tools.cc:3955 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Obrir i editar la carta de color escanejada. \n" "Eliminar qualsevol gir o rotació de l'escanejat. \n" "Retallar AMB MOLTA CURA el prim marge verd." #: f.tools.cc:3987 msgid "Open the trimmed color chart file" msgstr "Obrir la carta de colors retallada" #: f.tools.cc:4120 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" "Establir el nom per l'arxiu de calibració de sortida \n" "[el seu nom per la calibració].dat" #: f.tools.cc:4160 msgid "Color map file to use" msgstr "Arxiu de mapa de color per utilitzar" #: f.tools.cc:4179 msgid "Select the image file to print." msgstr "Sel·leccionar imatge a imprimir" #: f.tools.cc:4244 msgid "converting colors..." msgstr "convertint colors" #: f.tools.cc:4344 msgid "Image colors are converted for printing." msgstr "Colors de la imatge convertits per imprimir" #: f.tools.cc:4467 msgid "This function is for AppImage package only" msgstr "Aquesta funció és només per el paquet Appimage" #: f.tools.cc:4471 msgid "" "Completely remove the AppImage package \n" "Press F1 for more information. \n" "Continue?" msgstr "" "Eliminar completament el paquet Appimage \n" "Presionar F1 pwer a més informació. \n" "Continuar?" #: f.warp.cc:94 f.widgets.cc:515 msgid "Unbend" msgstr "Redreçar" #: f.warp.cc:371 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Clic en les quatre cantonades d'un àrea tetragonal. Polsar [Aplicar]. \n" " La imatge és deformada per que el tetràgon sigui un rectangle." #: f.warp.cc:388 msgid "Perspective Correction" msgstr "Corregir perspectiva" #: f.warp.cc:621 msgid "must have 4 corners" msgstr "ha de tenir 4 cantonades" #: f.warp.cc:745 msgid "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." msgstr "" " Seleccionar un àrea per deformar utilitzan la funció de selecció d'àrea. \n" " Prèmer [Iniciar deformació] i estirar l'àrea amb el ratolí. \n" " Múltiples estirades amb el ratolí fins a quedar satisfet. \n" " Quan hagi acabat, seleccionar un altre àrea o prèmer [Fet]." #: f.warp.cc:758 f.widgets.cc:517 msgid "Warp area" msgstr "Deformar àrea" #: f.warp.cc:763 msgid "start warp" msgstr "iniciar deformació" #: f.warp.cc:1149 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" "Usar Seleccionar àrea per sel·leccionar una cara. \n" "Clic en el centre de distorsió. \n" "Moure el control. \n" #: f.warp.cc:1176 f.widgets.cc:518 msgid "Unwarp Closeup" msgstr "Desfer deformció d'aproximació" #: f.warp.cc:1356 f.warp.cc:1671 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Empènyer una posició de imatge utilitzan el ratolí. \n" " Múltiples empentes del ratolí fins a quedar satisfet. \n" " Quan hagi acabat, polsar [Fet]." #: f.warp.cc:1374 f.widgets.cc:519 msgid "Warp curved" msgstr "Deformació corba" #: f.warp.cc:1689 f.widgets.cc:520 msgid "Warp linear" msgstr "Deformació lineal" #: f.warp.cc:2005 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Empènyer en una cantonada de la imatge utilitzan el ratolí. \n" " Múltiples empentes del ratolí fins a quedar satisfet. \n" " Quan hagi acabat, polsar [Fet]." #: f.warp.cc:2021 f.widgets.cc:521 msgid "Warp affine" msgstr "Deformació afí" #: f.warp.cc:2368 f.widgets.cc:522 msgid "Flatten Book Page" msgstr "Aplanar una pàgina impresa" #: f.warp.cc:2369 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Tallar la imatge per aïllar una pàgina \n" "Assenyalar les cantonades superior i inferior \n" "amb més de 4 clics del ratolí, després aplanar : " #: f.warp.cc:2372 msgid "Stretch curved-down surfaces:" msgstr "Adreçar superfícies curvades per sota" #: f.warp.cc:2427 msgid "Top:" msgstr "Adalt :" #: f.warp.cc:2430 msgid "Bottom:" msgstr "Abaix :" #: f.warp.cc:2811 f.widgets.cc:523 msgid "Spherical Projection" msgstr "Projecció esfèrica" #: f.warp.cc:2855 msgid "Magnify" msgstr "Augmentar" #: f.warp.cc:3029 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" " Sel·leccionar àrees que no han de canviar. \n" " Arrossegar imatge des de la cantonada superior esquerra. \n" " Quan acabeu, premeu [Fet]." #: f.warp.cc:3045 f.widgets.cc:524 msgid "Selective Rescale" msgstr "Re-escalat sel·lectiu" #: f.warp.cc:3068 msgid "select areas first" msgstr "primer sel·leccionar àrees" #: f.warp.cc:3290 f.widgets.cc:525 msgid "Make Waves" msgstr "Ones" #: f.warp.cc:3297 msgid "wavelength" msgstr "longitut d'ona" #: f.warp.cc:3299 msgid "variance" msgstr "variança" #: f.warp.cc:3310 msgid "perspective" msgstr "prespectiva​" #: f.warp.cc:3484 f.warp.cc:3515 msgid "Twist" msgstr "Girar" #: f.warp.cc:3512 msgid "drag mouse to set center" msgstr "moure ratolí per establir centre" #: f.widgets.cc:103 msgid "Album" msgstr "Àlbum" #: f.widgets.cc:105 msgid "TOP" msgstr "DALT" #: f.widgets.cc:171 msgid "Current Image File (key F)" msgstr "Arxiu d'imatge actual (tecla F)" #: f.widgets.cc:172 msgid "Thumbnail Gallery (key G)" msgstr "Galería de miniatures (tecla G)" #: f.widgets.cc:173 msgid "World Maps (key W)" msgstr "Mapa mundial (tecla W)" #: f.widgets.cc:174 msgid "Net Maps (key M)" msgstr "Maps de la xaexa (tecla M)" #: f.widgets.cc:177 msgid "Favorite Functions" msgstr "Funcions favorites" #: f.widgets.cc:178 msgid "File: Open, RAW, Rename, Delete, Print" msgstr "Arxiu : Obrir, RAW, Re-anomenar, Esborrar, Imprimir" #: f.widgets.cc:179 msgid "Save modified image file to disk" msgstr "Desar imatge modificada en el disc" #: f.widgets.cc:180 msgid "Open previous or next file (left/right mouse click)" msgstr "Obrir arxiu anterior o sugüent (clic esq/dret)" #: f.widgets.cc:181 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadades: Titols, Etiquetes, Valoracions, Geoetiquetes, Buscar ..." #: f.widgets.cc:182 msgid "Areas: Select areas to edit, copy and paste" msgstr "Arees: Sel.leccionar àrees per editar, copiar i enganxar" #: f.widgets.cc:183 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "Editar: Retallar, Girar, Redimensionar, Brillantor, Contrast, Text ..." #: f.widgets.cc:184 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Reparar: Enfocar, Soroll, Ulls vermells, Color, Pintar, Clonar ..." #: f.widgets.cc:185 msgid "Warp: Fix Perspective, Warp or unwarp image ..." msgstr "Deformar: coegir perspectiva, deformar una imatge ..." #: f.widgets.cc:186 msgid "Effects: Special Effects, Arty Transforms" msgstr "Efectes: Efectes especials, Transformacions artístiques" #: f.widgets.cc:187 msgid "" "left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "clic esq./dret.: desfer/refer 1 edició \n" " amb la tecla A: defer/refer totes les edicions \n" " clic central: anar a una edició anterior" #: f.widgets.cc:190 msgid "Tools: Index, Settings, Shortcuts, Magnify ..." msgstr "Eines: Indexar, Ajustaments, Dreceres, Augmentar ..." #: f.widgets.cc:191 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Ajuda: Inici ràpid, Guia d'usuari, Canvis recents" #: f.widgets.cc:194 msgid "Sync Gallery, Albums, Slide Show" msgstr "Sincronitzar Galeria, Àlbums, Diaporames" #: f.widgets.cc:195 msgid "set and recall bookmarked image locations" msgstr "establir i recuperar imatges ambs ubicacions marcades" #: f.widgets.cc:196 msgid "increase thumbnail size" msgstr "Augmentar mida de miniatura" #: f.widgets.cc:197 msgid "reduce thumbnail size" msgstr "reduir mida de miniatura" #: f.widgets.cc:198 msgid "change sort order" msgstr "canviar criteri d'ordenació" #: f.widgets.cc:199 msgid "jump to beginning (top)" msgstr "anar al principi (a dalt)" #: f.widgets.cc:200 msgid "jump to end (bottom)" msgstr "anar al final (a baix)" #: f.widgets.cc:201 msgid "previous page" msgstr "pàgina anterior" #: f.widgets.cc:202 msgid "next page" msgstr "pàgina següent" #: f.widgets.cc:203 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combinar: HDR, HDF, Panorama, Apilar, Muntatge" #: f.widgets.cc:204 msgid "Process: convert, export, metadata, search ..." msgstr "Procés: convertir, exportar, metadades, cercar ..." #: f.widgets.cc:207 msgid "Choose a map file" msgstr "Escollir un arxiu de mapa" #: f.widgets.cc:208 f.widgets.cc:212 msgid "Mark all images or current gallery" msgstr "Marcar totes les imatges o la galeria actual" #: f.widgets.cc:211 msgid "Choose Internet map source" msgstr "Escollir font del mapa d'internet" #: f.widgets.cc:213 msgid "Save and recall named map locations" msgstr "Desar i recuperar ubicacions marcades en el mapa" #: f.widgets.cc:216 msgid "Open another window" msgstr "Obrir una altre finestra" #: f.widgets.cc:217 msgid "Open a new image file" msgstr "Obrir una nova imatge" #: f.widgets.cc:218 f.widgets.cc:430 msgid "Cycle 2 Previous Files" msgstr "2 imatges prèvias en cicle" #: f.widgets.cc:219 f.widgets.cc:431 msgid "Cycle 3 Previous Files" msgstr "3 imatges prèvias en cicle" #: f.widgets.cc:220 msgid "Open a recently seen file" msgstr "Obrir arxiu vist recentment" #: f.widgets.cc:221 msgid "Open a newly added file" msgstr "Obrir un nou arxiu afegit" #: f.widgets.cc:222 msgid "Open and edit a camera RAW file" msgstr "Obrir i editar un arxiu de càmera RAW" #: f.widgets.cc:223 msgid "View a 360 degree panorama image file" msgstr "Veure una imatge panoràmica de 360 graus" #: f.widgets.cc:224 msgid "Change the image file name" msgstr "Canvia nom de la imatge" #: f.widgets.cc:225 msgid "Copy or Move an image file to a new location" msgstr "Copiar o moure un arxiu a una nova ubicació" #: f.widgets.cc:226 msgid "Copy an image file to the desktop" msgstr "Copiar una imatge a l'escriptori" #: f.widgets.cc:227 msgid "Copy image file to the clipboard" msgstr "Copiar imatge al portapapers" #: f.widgets.cc:228 msgid "Copy image file to the image cache" msgstr "Copiar imatges al caché d'imatges" #: f.widgets.cc:229 msgid "Show location on Interet map" msgstr "Mostrar localitat en mapa Interet" #: f.widgets.cc:230 msgid "Create a blank image" msgstr "Crear una imatge buida" #: f.widgets.cc:231 msgid "toggle - blank or restore window" msgstr "canviar a blanc o restaurar imatge" #: f.widgets.cc:232 msgid "Delete or trash image file" msgstr "Eliminar o esborrar una imatge" #: f.widgets.cc:233 msgid "Print the current image" msgstr "Impprimir la imatge actual" #: f.widgets.cc:234 msgid "Print current image with adjusted colors" msgstr "Imprimir imatge actual amb els colors ajustats" #: f.widgets.cc:235 f.widgets.cc:445 msgid "Quit Fotoxx" msgstr "Sortir de Fotoxx" #: f.widgets.cc:238 msgid "List a few key metadata items" msgstr "Llistar algunes dades clau de metadades" #: f.widgets.cc:239 msgid "List all metadata items" msgstr "Listar totes les metadades" #: f.widgets.cc:240 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Editar etiquetes/geoetiquetes/títol/valoració ..." #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Editar qualsevol metadada de la imatge" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Eliminar les metadades seleccionades" #: f.widgets.cc:243 msgid "(Toggle) show captions and comments" msgstr "(Canviar) mostra llegendes i comentaris" #: f.widgets.cc:246 msgid "Select object or area for editing" msgstr "Seleccionar objecte o àrea per editar" #: f.widgets.cc:247 msgid "Find a gap in an area outline" msgstr "Trobar separació en un contorn d'àrea" #: f.widgets.cc:248 msgid "Select hairy or irregular edge" msgstr "Seleccioneu una vora esfilagarsada o irregular" #: f.widgets.cc:249 msgid "Show (outline) existing area" msgstr "Mostrar un àrea existent (contorn)" #: f.widgets.cc:250 msgid "Hide existing area" msgstr "Ocultar àrea existent" #: f.widgets.cc:251 msgid "Enable area for editing" msgstr "Activar àrea per editar" #: f.widgets.cc:252 msgid "Disable area for editing" msgstr "Desactivat àrea per editar" #: f.widgets.cc:253 msgid "Reverse existing area" msgstr "Invertir àrea existent" #: f.widgets.cc:254 msgid "Erase existing area" msgstr "Esborrar àrea existent" #: f.widgets.cc:255 msgid "Copy area for later pasting into image" msgstr "Copiar àrea per posterior enganxat en una imatge" #: f.widgets.cc:256 msgid "Save area to a file with transparency" msgstr "Desar àrea en un arxiu amb tranparència" #: f.widgets.cc:257 msgid "Open a file and paste as area into image" msgstr "Obrir un arxiu i exnganxar-lo com un àrea en la imatge" #: f.widgets.cc:258 msgid "Paste previously copied area into image" msgstr "Enganxar en la imatge àrea previament copiada" #: f.widgets.cc:261 msgid "Trim/Crop margins and/or Rotate" msgstr "Retallar/tallar marges i/o girar" #: f.widgets.cc:262 msgid "Upright a rotated image" msgstr "Adressar una imatge girada" #: f.widgets.cc:263 msgid "Change pixel dimensions" msgstr "Canviar dimensions de píxels" #: f.widgets.cc:264 f.widgets.cc:265 msgid "Fast auto enhance that may work OK" msgstr "Auto-millora ràpida" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Ajustar brillantor, contrast, color" #: f.widgets.cc:267 msgid "Edit brightness distribution" msgstr "Editar distribució de brillantor" #: f.widgets.cc:268 msgid "Magnify brightness gradients to enhance details" msgstr "Augmentar gradients de brillantor per millorar detalls" #: f.widgets.cc:269 msgid "Flatten brightness distribution" msgstr "Aplanarhistograma" #: f.widgets.cc:270 msgid "Rescale brightness - reduce color caste and fog/haze" msgstr "Re-escalar brillantor - reduïr dominant de color y la boira o boirina" #: f.widgets.cc:271 msgid "Mirror image horizontally or vertically" msgstr "Enmirallar la imatge horizontalment o verticalment" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Pinta pixles utilitzant el ratolí" #: f.widgets.cc:273 msgid "Clone image pixels using the mouse" msgstr "Clonar píxels utilitzant el ratolí" #: f.widgets.cc:274 msgid "Blend image pixels using the mouse" msgstr "Barrejar imatge usant el ratolí" #: f.widgets.cc:277 msgid "Paint edit function gradually with mouse" msgstr "Funció d'edició pintant gradualment amb el ratolí" #: f.widgets.cc:278 msgid "Leverage edits by brightness or color" msgstr "Edició per nivells de brillantor o color" #: f.widgets.cc:279 msgid "Edit plugins menu or run a plugin function" msgstr "Editar menú de plugins o executar una funció de plugin" #: f.widgets.cc:282 msgid "Make the image look sharper" msgstr "Fer que la imatge es vegi més enfocada" #: f.widgets.cc:283 msgid "Make the image look fuzzy" msgstr "Fer que la imatge es vegi desenfocada" #: f.widgets.cc:284 msgid "Filter noise from low-light photos" msgstr "Filtrar soroll de fotografíes fetes amb poca llum" #: f.widgets.cc:285 msgid "Fix red-eyes from electronic flash" msgstr "Corretgir ulls vermells produïts per un flash electrònic" #: f.widgets.cc:286 msgid "Make BW/color, negative/positive, sepia" msgstr "Blanc i negre/color, negatiu/positiu, sèpia" #: f.widgets.cc:287 msgid "Reduce color depth (posterize)" msgstr "Reduïr profunditat de color (posteritzar)" #: f.widgets.cc:288 msgid "Shift/convert colors into other colors" msgstr "Canviar/convertir uns colors en altres" #: f.widgets.cc:289 msgid "Adjust color intensity (saturation)" msgstr "Ajustar intensitat de color (saturació)" #: f.widgets.cc:290 msgid "Adjust color using RGB or CMY colors" msgstr "Ajustar coilor usant sistema RGB o CMY" #: f.widgets.cc:291 msgid "Adjust color using HSL colors" msgstr "Ajustar color usant sistema HSL" #: f.widgets.cc:292 msgid "Adjust color in selected image areas" msgstr "Ajustar color en una àrea seleccionada de una imatge" #: f.widgets.cc:293 msgid "Match colors on one image with another" msgstr "Fer concordar colors d'una imatge amb els d'altra" #: f.widgets.cc:294 msgid "Reduce Chromatic Abberation" msgstr "Reduïr l'aberració cromàtica" #: f.widgets.cc:295 msgid "Remove unwanted objects" msgstr "Eliminar objectes no desitjats" #: f.widgets.cc:296 msgid "Add a brightness/color ramp across the image" msgstr "Afegir una rampa de broillantor o color a través de la imatge" #: f.widgets.cc:297 msgid "Remove dust spots from scanned slides" msgstr "Eliminar pols de diapositives escanejades" #: f.widgets.cc:298 msgid "Smoothen edges with jaggies" msgstr "Suavitzar vores amb dents de serr" #: f.widgets.cc:299 msgid "Erase known hot and dark pixels" msgstr "Esborrar píxels calents i foscos coneguts" #: f.widgets.cc:300 msgid "Paint image transparency using the mouse" msgstr "Pintar transparència d'imatge amb el ratolí" #: f.widgets.cc:301 msgid "Add image transparency based on image attributes" msgstr "Afegir transparència a la imatge basada en els atributs de la imatge" #: f.widgets.cc:304 msgid "Remove curvature, esp. panoramas" msgstr "Eliminar corbatura, especialment d'un panorama" #: f.widgets.cc:305 msgid "Straighten objects seen from an angle" msgstr "Redreçar objectes vistos des d'un angle" #: f.widgets.cc:306 msgid "Distort image areas using the mouse" msgstr "Distorsionar una àrea de una imatge utilitzant el ratolí" #: f.widgets.cc:307 msgid "Unwarp closeup face photo to remove distortion" msgstr "Desfer deformació d'aproximació per desfer la distorsió" #: f.widgets.cc:308 f.widgets.cc:309 f.widgets.cc:310 msgid "Distort the whole image using the mouse" msgstr "Distorsionar tota la imatge utilitzant el ratolí" #: f.widgets.cc:311 msgid "Flatten a photographed book page" msgstr "Aplanar una pàgina impresa fotografiada" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "Fer una projecció esfèrica de una imatge" #: f.widgets.cc:313 msgid "Rescale image outside selected areas" msgstr "Re-escalar imatge per fora de les àrees sel·leccionades" #: f.widgets.cc:314 msgid "Warp an image with a wave pattern" msgstr "Deformar una imatge amb ​un patró d'ones" #: f.widgets.cc:315 msgid "Twist image centered at mouse position" msgstr "Girar imatge centrada en la posició del ratolí" #: f.widgets.cc:318 msgid "Convert to simulated sketch" msgstr "Convertir a simulació de'esbós" #: f.widgets.cc:319 msgid "Convert image into a cartoon drawing" msgstr "Convertir imatge a un dibuix a tintes planes" #: f.widgets.cc:320 msgid "Convert to line drawing (edge detection)" msgstr "Convertir a un dibuix de línees (detecció de vores)" #: f.widgets.cc:321 msgid "Convert to solid color drawing" msgstr "Convertir a un dibuix de colors sòlids" #: f.widgets.cc:322 msgid "Graduated Blur depending on contrast" msgstr "Desenfocament graduat depenent del contrast" #: f.widgets.cc:323 msgid "Create an embossed or 3D appearance" msgstr "Crear un relleu o aparença 3D" #: f.widgets.cc:324 msgid "Convert to square tiles" msgstr "Convertir a rajoles quadrades" #: f.widgets.cc:325 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Convertir a punts (efecte Roy Lichtenstein" #: f.widgets.cc:326 msgid "Convert into a simulated painting" msgstr "Convertir a una pintura simulada" #: f.widgets.cc:327 msgid "Change brightness or color radially" msgstr "Canviar brillantor o color radialment" #: f.widgets.cc:328 msgid "Add texture to an image" msgstr "Afegir textura a una imatge" #: f.widgets.cc:329 msgid "Tile image with a repeating pattern" msgstr "Enrajolñar imatge amb un patrò repetitiu" #: f.widgets.cc:330 msgid "Create a mosaic with tiles made from all images" msgstr "Crear un mosaïc amb rajoles fetes de totes les imatges" #: f.widgets.cc:331 msgid "Process an image using a custom kernel" msgstr "Processar una imatge fent servir una matriu personalitzada" #: f.widgets.cc:332 msgid "Blur an image in the direction of mouse drags" msgstr "Desenfocar una imatge en la direcció del moviment del ratolí" #: f.widgets.cc:333 msgid "Increasing blur with distance from selected areas" msgstr "" "Incrementar desenfocament amb la distància des de les àrees sel·leccionades" #: f.widgets.cc:334 msgid "Change color hue using an algorithm" msgstr "Canviar to de color utilitzant el retolí" #: f.widgets.cc:337 msgid "Combine bright/dark images for better detail" msgstr "Combinar imatges fosques/il.luminadas per millorar detalls" #: f.widgets.cc:338 msgid "Combine near/far focus images for deeper focus" msgstr "" "Combinar imatges enfocades aprop/lluny per obtenir major profunditat de camp" #: f.widgets.cc:339 msgid "Combine images to erase passing people, etc." msgstr "Combinar imatges per esborrar vianants, cotxes, etc" #: f.widgets.cc:340 msgid "Combine noisy images into a low-noise image" msgstr "Combinar imatges amb soroll en imatges amb poc soroll" #: f.widgets.cc:341 msgid "Combine images into a panorama" msgstr "Combinar imatges en un panorama" #: f.widgets.cc:342 msgid "Combine images into a vertical panorama" msgstr "Combinar imatges en un panorama vertical" #: f.widgets.cc:343 msgid "Combine images into a panorama (panorama tools)" msgstr "Combinar imatges en un panorama (panorama tools)" #: f.widgets.cc:344 msgid "Arrange images and text in a layout (montage)" msgstr "Ordenar imatges i text en una capa (muntatge)" #: f.widgets.cc:345 msgid "Combine images into a montage of images" msgstr "Combinar imatges en un muntatge d'imatges" #: f.widgets.cc:348 msgid "Rename/convert/resize/move multiple files" msgstr "Reanomenar/convertir/redimensionar/moure múltiples arxius" #: f.widgets.cc:349 msgid "Upright multiple rotated image files" msgstr "Adressar múltiples imatges girades" #: f.widgets.cc:350 msgid "Delete or Trash multiple files" msgstr "Esborrar/eliminar múltiples arxius" #: f.widgets.cc:351 msgid "Convert camera RAW files using libraw or Raw Therapee" msgstr "Convertir arxius RAW utilitzan libraw o Raw Therapee" #: f.widgets.cc:352 msgid "Build and run edit script files" msgstr "Escriure y executar scripts editats" #: f.widgets.cc:353 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Gravar imatge sel·leccionada a un DVD o Blue Ray" #: f.widgets.cc:354 msgid "Search all image files and report duplicates" msgstr "Buscar totes les imatges i informar dels duplicats" #: f.widgets.cc:356 msgid "Export selected image files to a directory" msgstr "Exportar les imatges sel·leccionades a un directori" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Afegir/eliminar etiquetes per múltiples imatges " #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Convertir noms d'etiqueta per totes les imatges" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "canviar data/hora de la foto" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Afegir/canviar/esborrar metadades a múltiples imatges" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Informe de m​eatada​des per múltiples imatges" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Afegir/revisar geo-etiquetes a múltiples imatges" #: f.widgets.cc:363 msgid "Find all images for a location [date]" msgstr "Trobar totes les imatges per una ubicació [data]" #: f.widgets.cc:364 msgid "Show image counts by month, select and report" msgstr "Mostar quantitat d'imatges per mes,seleccionar i fer informe" #: f.widgets.cc:365 msgid "Find images meeting select criteria" msgstr "Trobar imatges que compleixin un criteri de seleccio" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Indexar nous arxius i fer miniatures" #: f.widgets.cc:369 msgid "Change user preferences" msgstr "Canviar preferències d'usuari" #: f.widgets.cc:370 msgid "Change Keyboard Shortcut Keys" msgstr "Canviar tecles drecera" #: f.widgets.cc:371 msgid "Show a brightness distribution graph" msgstr "Mostrar histograma (distribució de la brillantor)" #: f.widgets.cc:372 msgid "Show or revise grid lines" msgstr "Mostrar o amagar línies de graella" #: f.widgets.cc:373 msgid "Change color of foreground lines" msgstr "Canviar color a línies de contorn" #: f.widgets.cc:374 msgid "Show RGB colors at mouse click" msgstr "Mostrar colors RGB amb un clic de ratolí" #: f.widgets.cc:375 msgid "Magnify image around the mouse position" msgstr "Augmenta la imatge al voltant de la posició del ratolí" #: f.widgets.cc:376 msgid "Highlight darkest and brightest pixels" msgstr "Resaltar píxels més foscos o més brillants" #: f.widgets.cc:377 msgid "Chart to adjust monitor color" msgstr "Carta per ajustar el color del monitor" #: f.widgets.cc:378 msgid "Chart to adjust monitor gamma" msgstr "Carta per ajustar la gamma del monitor" #: f.widgets.cc:379 msgid "Change the GUI language" msgstr "Canviar idioma de la interfície d'usuari" #: f.widgets.cc:380 msgid "Report missing translations" msgstr "Informe de traduccions que falten" #: f.widgets.cc:381 msgid "Convert to another color profile" msgstr "Convertir a un perfil de color" #: f.widgets.cc:382 msgid "Calibrate printer colors" msgstr "Calibrar colors d'impresora" #: f.widgets.cc:383 msgid "Completely uninstall AppImage package" msgstr "Desinstal·lar completament el paquet Appimage" #: f.widgets.cc:384 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memoria i CPU (al terminal/arxiu de registre)" #: f.widgets.cc:387 msgid "Quick Start mini-guide" msgstr "Minni guia d'inici ràpid" #: f.widgets.cc:388 msgid "Read the user guide" msgstr "Llegir la guia d'usuari" #: f.widgets.cc:389 msgid "Recent user guide changes" msgstr "Canvis recents a la guia d'usuari" #: f.widgets.cc:390 msgid "Technical installation notes" msgstr "Notes tècniques d'instal.lació " #: f.widgets.cc:391 msgid "List updates by Fotoxx version" msgstr "Llistar actualitzacions per versió de Fotoxx" #: f.widgets.cc:392 msgid "View the log file and error messages" msgstr "Veure l'arxiu de registre i missatges d'error" #: f.widgets.cc:393 msgid "How to do Fotoxx translations" msgstr "Com fer traduccións de Fotoxx" #: f.widgets.cc:394 msgid "Show the Fotoxx web page" msgstr "Mostrar la pàgina web de Fotoxx" #: f.widgets.cc:395 msgid "Version, license, contact, credits" msgstr "Versió, llicència, contacte, crèdits" #: f.widgets.cc:398 msgid "list all directories, click any for gallery view" msgstr "listar tots els directoris, clic en qualsevol per vista de galeria" #: f.widgets.cc:399 msgid "Set gallery from current image file" msgstr "Establir galeria des de l'imatge actual" #: f.widgets.cc:400 msgid "Organize images into albums" msgstr "Organitzar imatges en àlbums" #: f.widgets.cc:401 msgid "Update album files to latest version" msgstr "Actualitzar arxius d'album a la darrera versió" #: f.widgets.cc:402 msgid "Replace album file with another file" msgstr "Substituïr arxiu d'àlbum amb un altre arxiu" #: f.widgets.cc:403 msgid "Start a slide show" msgstr "Iniciar un diaporama" #: f.widgets.cc:425 msgid "New Window" msgstr "Nova finestra" #: f.widgets.cc:426 f.widgets.cc:610 fotoxx-18.01.cc:1821 msgid "Sync Gallery" msgstr "Sincronitzar galería" #: f.widgets.cc:427 msgid "Recently Seen Images" msgstr "Imatges vistes recentment " #: f.widgets.cc:428 msgid "Newest Images" msgstr "Imatges més recents" #: f.widgets.cc:433 msgid "View 360° Panorama" msgstr "Veure panorama 360º" #: f.widgets.cc:434 msgid "New Blank Image" msgstr "Nova imatge buida" #: f.widgets.cc:437 f.widgets.cc:758 msgid "Copy/Move to Location" msgstr "Copiar/moure a una ubicació" #: f.widgets.cc:438 f.widgets.cc:759 msgid "Copy to Desktop" msgstr "Copiar a l'escriptori" #: f.widgets.cc:439 f.widgets.cc:760 msgid "Copy to Clipboard" msgstr "Copiar al portapapers" #: f.widgets.cc:440 f.widgets.cc:764 msgid "Copy to Image Cache" msgstr "Copiar a la cache d'imatge " #: f.widgets.cc:441 f.widgets.cc:778 msgid "Show on Net Map" msgstr "Mostrar en mapa en xarxa" #: f.widgets.cc:443 msgid "Print Image" msgstr "Imprimir imatge" #: f.widgets.cc:444 msgid "Print Calibrated Image" msgstr "Imprimir imatge calibrada" #: f.widgets.cc:448 msgid "View Metadata (short)" msgstr "Veure metadades (curt)" #: f.widgets.cc:449 msgid "View Metadata (long)" msgstr "Veure metadades (llarg)" #: f.widgets.cc:453 msgid "Show Captions on Image" msgstr "Mostrar llegendes a la imatge" #: f.widgets.cc:456 f.widgets.cc:776 msgid "Select Area" msgstr "Select Area" #: f.widgets.cc:457 msgid "Find Area Gap" msgstr "Trobar separació d'àrea" #: f.widgets.cc:459 msgid "Show Area" msgstr "Mostrar àrea" #: f.widgets.cc:460 msgid "Hide Area" msgstr "Amagar àrrea" #: f.widgets.cc:461 msgid "Enable Area" msgstr "Habilitar àrea" #: f.widgets.cc:462 msgid "Disable Area" msgstr "Deshabilitar àrea" #: f.widgets.cc:463 msgid "Invert Area" msgstr "Invertir àrea" #: f.widgets.cc:464 msgid "Unselect Area" msgstr "Deselecionar àrea" #: f.widgets.cc:466 msgid "Paste Area" msgstr "Enganxar àrea" #: f.widgets.cc:467 msgid "Open Area File" msgstr "Obrir arxiu d'àrea" #: f.widgets.cc:474 msgid "Voodoo 1" msgstr "Auto-millora 1" #: f.widgets.cc:475 msgid "Voodoo 2" msgstr "Auto-millora 2" #: f.widgets.cc:477 f.widgets.cc:773 msgid "Edit Brightness" msgstr "Editar brillantor" #: f.widgets.cc:478 f.widgets.cc:775 msgid "Gradients" msgstr "Gradients" #: f.widgets.cc:482 msgid "Paint Image" msgstr "Pintar imatge" #: f.widgets.cc:489 msgid "Plugins" msgstr "Plugins" #: f.widgets.cc:494 msgid "Denoise" msgstr "Reduïr soroll" #: f.widgets.cc:495 msgid "Red Eyes" msgstr "Ulls vermells" #: f.widgets.cc:497 msgid "Color Depth" msgstr "Profunditat de color" #: f.widgets.cc:500 msgid "Adjust RGB/CMY" msgstr "Ajustar RGB/CMY" #: f.widgets.cc:501 msgid "Adjust HSL" msgstr "Ajustar HSL" #: f.widgets.cc:502 msgid "Zonal Colors" msgstr "Colors zonals" #: f.widgets.cc:503 msgid "Match Colors" msgstr "Concordar colors" #: f.widgets.cc:509 msgid "Anti-Alias" msgstr "Suavitzar contorns" #: f.widgets.cc:516 msgid "Fix Perspective" msgstr "Corregir perspectiva" #: f.widgets.cc:526 msgid "Twist Image" msgstr "Girar imatge" #: f.widgets.cc:529 msgid "Sketch" msgstr " Esbós" #: f.widgets.cc:530 msgid "Cartoon" msgstr "Tintes planes" #: f.widgets.cc:534 msgid "Embossing" msgstr "Relleu" #: f.widgets.cc:536 msgid "Dots" msgstr "Punts" #: f.widgets.cc:537 msgid "Painting" msgstr "Pintura" #: f.widgets.cc:539 msgid "Texture" msgstr "Textura" #: f.widgets.cc:541 msgid "Mosaic" msgstr "Mosaïc" #: f.widgets.cc:548 msgid "High Dynamic Range" msgstr "HDR alt rang dinàmic" #: f.widgets.cc:549 msgid "High Depth of Field" msgstr "HDF alta profunditat de camp" #: f.widgets.cc:550 msgid "Stack/Paint" msgstr "Apilar/Pintar" #: f.widgets.cc:551 msgid "Stack/Noise" msgstr "Apilar/Soroll" #: f.widgets.cc:552 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:553 msgid "Vertical Panorama" msgstr "Panorama vertical" #: f.widgets.cc:554 msgid "PT Panorama" msgstr "PT panorama" #: f.widgets.cc:555 msgid "Montage" msgstr "Muntatge" #: f.widgets.cc:556 msgid "Mashup" msgstr "Fotomuntatge" #: f.widgets.cc:562 msgid "Batch RAW" msgstr "Convertir Raw en lot" #: f.widgets.cc:563 msgid "Script Files" msgstr "Scripts" #: f.widgets.cc:566 msgid "Export File List" msgstr "Exportar llista d'arxius" #: f.widgets.cc:574 msgid "Image Locations/Dates" msgstr "Localitzacions/dates de les imatges" #: f.widgets.cc:575 msgid "Image Timeline" msgstr "Línia de temps de les imatges" #: f.widgets.cc:576 msgid "Search Images" msgstr "Buscar imatges" #: f.widgets.cc:582 msgid "Brightness Graph" msgstr "Gráfic de brillantor" #: f.widgets.cc:587 msgid "Dark/Bright Pixels" msgstr "Sobre/subexposició" #: f.widgets.cc:588 msgid "Monitor Color" msgstr "Color del monitor" #: f.widgets.cc:590 msgid "Change Language" msgstr "Canviar idioma" #: f.widgets.cc:591 msgid "Missing Translations" msgstr "Traduccions que falten" #: f.widgets.cc:592 msgid "Color Profile" msgstr "Perfil de color" #: f.widgets.cc:594 msgid "Uninstall AppImage" msgstr "Desinstal·lar Appimage" #: f.widgets.cc:611 msgid "All Directories" msgstr "Tots els directoris" #: f.widgets.cc:612 msgid "Bookmarks" msgstr "Marques" #: f.widgets.cc:621 f.widgets.cc:646 f.widgets.cc:670 f.widgets.cc:683 msgid "Gallery" msgstr "Galeria" #: f.widgets.cc:622 f.widgets.cc:647 f.widgets.cc:671 f.widgets.cc:684 msgid "World Maps" msgstr "Mapes mundials" #: f.widgets.cc:623 f.widgets.cc:648 f.widgets.cc:672 f.widgets.cc:685 msgid "Net Maps" msgstr "Mapes en xarxa" #: f.widgets.cc:625 f.widgets.cc:650 f.widgets.cc:1066 msgid "Favorites" msgstr "Favorits" #: f.widgets.cc:627 msgid "Save File" msgstr "Desar arxiu" #: f.widgets.cc:628 msgid "Prev/Next" msgstr "Ant/Seg" #: f.widgets.cc:630 msgid "Areas" msgstr "Àrees" #: f.widgets.cc:631 msgid "Undo/Redo" msgstr "Desfer/Refer" #: f.widgets.cc:632 fotoxx.h:1208 msgid "Edit" msgstr "Editar" #: f.widgets.cc:633 msgid "Repair" msgstr "Reparar" #: f.widgets.cc:635 msgid "Effects" msgstr "Efectes" #: f.widgets.cc:636 f.widgets.cc:660 msgid "Combine" msgstr "Combinar" #: f.widgets.cc:637 f.widgets.cc:661 msgid "Process" msgstr "Procés" #: f.widgets.cc:638 f.widgets.cc:662 msgid "Tools" msgstr "Eines" #: f.widgets.cc:674 msgid "Choose Map" msgstr "Escollir mapa" #: f.widgets.cc:675 f.widgets.cc:688 msgid "Map Markers" msgstr "Marcadors de mapa" #: f.widgets.cc:687 msgid "Map Source" msgstr "Orígen de mapa" #: f.widgets.cc:689 msgid "Map Locations" msgstr "Ubicacions de mapa" #: f.widgets.cc:753 msgid "Popup Image" msgstr "Imatge emergent" #: f.widgets.cc:757 fotoxx.h:1265 msgid "Rename" msgstr "Canviar el nom" #: f.widgets.cc:761 msgid "Remove from Album" msgstr "Eliminar de l'àlbum" #: f.widgets.cc:763 msgid "Cut to Image Cache" msgstr " Tallar a la cache d'imatge" #: f.widgets.cc:765 msgid "Paste Image Cache Here (clear)" msgstr "Enganxar aquí cache d'imatge (esborrar) " #: f.widgets.cc:766 msgid "Paste Image Cache Here (keep)" msgstr "Enganxar aquí cache d'imatge (desar) " #: f.widgets.cc:779 msgid "Delete/Trash ..." msgstr "Eliminar/paperera ..." #: f.widgets.cc:1092 msgid "menu name is invalid" msgstr "nom de menú invàlid" #: fotoxx-18.01.cc:434 msgid "Please install missing programs:" msgstr "Si us plau instal.leu programas requerits:" #: fotoxx-18.01.cc:706 #, c-format msgid "Kill active dialog? %s" msgstr "Tancar el diàleg actiu? %s" #: fotoxx-18.01.cc:801 msgid "(reduced)" msgstr "(reduït)" #: fotoxx-18.01.cc:802 msgid "area active" msgstr "àrea activa" #: fotoxx-18.01.cc:803 msgid "dialog open" msgstr "dialog obert" #: fotoxx-18.01.cc:804 msgid "blocked" msgstr "bloquejat" #: fotoxx-18.01.cc:866 msgid "edits" msgstr "edicions" #: fotoxx-18.01.cc:1801 msgid "File View" msgstr "Vista d'arxiu" #: fotoxx-18.01.cc:1806 msgid "Gallery View" msgstr "Vista de galeria" #: fotoxx-18.01.cc:1811 msgid "World Map View" msgstr "Vista de mapa mumndial" #: fotoxx-18.01.cc:1816 msgid "Net Map View" msgstr "Vista de mapa d'Internet" #: fotoxx-18.01.cc:1831 msgid "Show Hidden" msgstr "Mostrar ocults" #: fotoxx-18.01.cc:3023 msgid "Exceed 50 anchor points" msgstr "Excedits punts d'anclatge" #: fotoxx-18.01.cc:3250 msgid "load curve from a file" msgstr "carregar corba des d'un arxiu" #: fotoxx-18.01.cc:3316 msgid "curve file is invalid" msgstr "Atxiu de corba no vàlid" #: fotoxx-18.01.cc:3330 msgid "save curve to a file" msgstr "desar corba en un arxiu" #: fotoxx-18.01.cc:3399 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "L'arxiu no por ser editat \n" " %s" #: fotoxx-18.01.cc:3412 msgid "Too many edits, please save image" msgstr "Massa edicions, si us plau guardeu la imatge" #: fotoxx-18.01.cc:3417 msgid "this function cannot be scripted" msgstr "aquesta fiunció no pot ser guionada" #: fotoxx-18.01.cc:3434 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Àrea seleccionada no activa.\n" "Continuar?" #: fotoxx-18.01.cc:3749 msgid "file data does not fit dialog" msgstr "les dades d'arxiu no s'ajusten al diàleg" #: fotoxx-18.01.cc:3759 msgid "Save settings to a file" msgstr "Desar ajustos en un arxiu" #: fotoxx-18.01.cc:4140 msgid "This action will discard changes to current image" msgstr "Aquesta acció descxartarà els canvis a la imatge actual" #: fotoxx-18.01.cc:4141 msgid "prior function still active" msgstr "funció anterior encara activa" #: fotoxx-18.01.cc:4142 fotoxx.h:1229 msgid "Keep" msgstr "Desar" #: fotoxx-18.01.cc:4143 msgid "Discard" msgstr "Descartar" #: fotoxx.h:1177 msgid "Add" msgstr "Afegir" #: fotoxx.h:1178 msgid "Add All" msgstr "Afegir tot" #: fotoxx.h:1180 msgid "Amount" msgstr "Quantitat" #: fotoxx.h:1181 msgid "Angle" msgstr "Angle" #: fotoxx.h:1182 zfuncs.cc:6782 msgid "Apply" msgstr "Aplicar" #: fotoxx.h:1183 msgid "Auto" msgstr "Auto" #: fotoxx.h:1184 msgid "Black" msgstr "Negre" #: fotoxx.h:1185 msgid "Blend Width" msgstr "Barrejar per ample" #: fotoxx.h:1187 zfuncs.cc:11368 msgid "Bottom" msgstr "a Baix" #: fotoxx.h:1189 zfuncs.cc:6796 msgid "Browse" msgstr "Examinar" #: fotoxx.h:1190 zfuncs.cc:6782 msgid "Cancel" msgstr "Cancel.lar" #: fotoxx.h:1191 msgid "Center" msgstr "centre" #: fotoxx.h:1192 msgid "Choose" msgstr "Escollir" #: fotoxx.h:1193 msgid "Clear" msgstr "Esborrar" #: fotoxx.h:1194 msgid "Close" msgstr "Tancar" #: fotoxx.h:1195 msgid "Color" msgstr "Color" #: fotoxx.h:1196 msgid "COMPLETED" msgstr "COMPLETAT" #: fotoxx.h:1197 msgid "continue" msgstr "continuar" #: fotoxx.h:1199 msgid "Copy" msgstr "Copiar" #: fotoxx.h:1200 msgid "Create" msgstr "Crear" #: fotoxx.h:1201 msgid "Curve File:" msgstr "Arxiu de corba:" #: fotoxx.h:1202 msgid "Cut" msgstr "Tallar" #: fotoxx.h:1203 msgid "Deband" msgstr "Eliminar bandes" #: fotoxx.h:1204 zfuncs.cc:6782 msgid "Delete" msgstr "Esborrar" #: fotoxx.h:1205 msgid "Disable" msgstr "Desactivar" #: fotoxx.h:1206 msgid "Done" msgstr "Fet" #: fotoxx.h:1207 msgid "edge" msgstr "vora" #: fotoxx.h:1209 msgid "Enable" msgstr "Activar" #: fotoxx.h:1210 msgid "Erase" msgstr "Esborrar" #: fotoxx.h:1211 msgid "Fetch" msgstr "Extreure" #: fotoxx.h:1212 msgid "output file already exists" msgstr "l'arxiu de sortida ja existeix" #: fotoxx.h:1213 msgid "file not found" msgstr "arxiu no trobat" #: fotoxx.h:1214 #, c-format msgid "file not found: %s" msgstr "arxiu no trobat: %s" #: fotoxx.h:1215 #, c-format msgid "%d image files selected" msgstr "%d imatges seleccionades" #: fotoxx.h:1216 msgid "Find" msgstr "Trobar" #: fotoxx.h:1217 msgid "Finish" msgstr "Acabar" #: fotoxx.h:1219 msgid "Font" msgstr "Tipus de lletra" #: fotoxx.h:1221 msgid "Grid" msgstr "Graella" #: fotoxx.h:1222 zfuncs.cc:11398 msgid "Height" msgstr "Alçada" #: fotoxx.h:1224 msgid "Hide" msgstr "Ocultar" #: fotoxx.h:1225 zfuncs.cc:11390 msgid "Image" msgstr "Imatge" #: fotoxx.h:1226 msgid "Images" msgstr " imatges" #: fotoxx.h:1227 msgid "Insert" msgstr "Insertar" #: fotoxx.h:1228 msgid "Invert" msgstr "Invertir" #: fotoxx.h:1230 zfuncs.cc:11372 msgid "Left" msgstr "Esquerra" #: fotoxx.h:1231 msgid "limit" msgstr "limit" #: fotoxx.h:1232 msgid "Load" msgstr "Carregar" #: fotoxx.h:1233 msgid "Make" msgstr "Fer" #: fotoxx.h:1235 msgid "Map" msgstr "Mapa" #: fotoxx.h:1236 msgid "Match Level:" msgstr "Nivel​l​ de coincidència:" #: fotoxx.h:1237 msgid "Max" msgstr "Max" #: fotoxx.h:1238 msgid "mouse radius" msgstr "radi del ratolí" #: fotoxx.h:1239 msgid "Negative" msgstr "Negatiu" #: fotoxx.h:1240 msgid "New" msgstr "Nou" #: fotoxx.h:1241 msgid "Next" msgstr "Següent" #: fotoxx.h:1242 zfuncs.cc:10365 msgid "No" msgstr "No" #: fotoxx.h:1243 msgid "no images" msgstr "sense imatges" #: fotoxx.h:1244 msgid "" "image index disabled \n" " Enable?" msgstr "" "índex d'imatges deshabilitat \n" " habilitat?" #: fotoxx.h:1245 msgid "no image files selected" msgstr "no hi han imatges seleccionades" #: fotoxx.h:1246 msgid "None" msgstr "Cap" #: fotoxx.h:1247 msgid "no selection" msgstr "sense selecció" #: fotoxx.h:1248 msgid "OK" msgstr "OK" #: fotoxx.h:1249 msgid "image index not updated" msgstr "índex d'imatge no actualitzat" #: fotoxx.h:1251 msgid "Paint Radius" msgstr "Radi a pintar" #: fotoxx.h:1252 msgid "Paste" msgstr "Enganxar" #: fotoxx.h:1253 msgid "Pause" msgstr "Pausa" #: fotoxx.h:1254 msgid "Percent" msgstr "Percentatge" #: fotoxx.h:1256 msgid "Presets" msgstr "Predefinits" #: fotoxx.h:1257 msgid "Prev" msgstr "Anterior" #: fotoxx.h:1258 msgid "Proceed" msgstr "Procedir" #: fotoxx.h:1260 msgid "range" msgstr "rang" #: fotoxx.h:1262 msgid "Redo" msgstr "Refer" #: fotoxx.h:1263 msgid "Reduce" msgstr "Reduïr" #: fotoxx.h:1264 msgid "Remove" msgstr "Eliminar" #: fotoxx.h:1266 msgid "Replace" msgstr "Substituir" #: fotoxx.h:1267 msgid "Reserved" msgstr "Reservat" #: fotoxx.h:1268 msgid "Reset" msgstr "Restablir" #: fotoxx.h:1269 zfuncs.cc:11376 msgid "Right" msgstr "Dreta" #: fotoxx.h:1270 msgid "Rotate" msgstr "Rotar" #: fotoxx.h:1271 msgid "Run" msgstr "Executar" #: fotoxx.h:1272 msgid "Save" msgstr "Desar" #: fotoxx.h:1273 msgid "Search" msgstr "Buscar" #: fotoxx.h:1274 msgid "Seconds" msgstr "Segons" #: fotoxx.h:1275 msgid "Select" msgstr "Seleccionar" #: fotoxx.h:1277 msgid "Show" msgstr "Mostrar" #: fotoxx.h:1278 msgid "Size" msgstr "Tamany" #: fotoxx.h:1279 msgid "Start" msgstr "Començar" #: fotoxx.h:1280 msgid "Stop" msgstr "Parar" #: fotoxx.h:1281 msgid "Strength" msgstr "Força" #: fotoxx.h:1282 msgid "Threshold" msgstr "Llindar" #: fotoxx.h:1283 #, c-format msgid "exceed %d files" msgstr "excedeix %d arxius" #: fotoxx.h:1284 zfuncs.cc:11364 msgid "Top" msgstr "Adalt" #: fotoxx.h:1286 msgid "Trash" msgstr "Eliminar" #: fotoxx.h:1287 msgid "Trim" msgstr "Retallar" #: fotoxx.h:1288 msgid "Undo All" msgstr "Desfer-ho tot" #: fotoxx.h:1289 msgid "Undo Last" msgstr "Desfer l'últim" #: fotoxx.h:1290 msgid "Undo" msgstr "Desfer" #: fotoxx.h:1291 msgid "Unfinish" msgstr "No acabat" #: fotoxx.h:1292 msgid "Unselect" msgstr "Deseleccionar" #: fotoxx.h:1293 msgid "View" msgstr "Veure" #: fotoxx.h:1294 msgid "Web" msgstr "Web" #: fotoxx.h:1295 msgid "White" msgstr "Blanc" #: fotoxx.h:1296 zfuncs.cc:11394 msgid "Width" msgstr "Ample" #: fotoxx.h:1297 msgid "x-offset" msgstr "desplaçament X" #: fotoxx.h:1298 msgid "y-offset" msgstr "desplaçament Y" #: fotoxx.h:1299 zfuncs.cc:10365 msgid "Yes" msgstr "Si" #: zfuncs.cc:305 msgid "LOW MEMORY - performance may be poor" msgstr "MEMORIA BAIXA - el rendiment pot ser baix" #: zfuncs.cc:1722 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "Crear directori? \n" " %s" #: zfuncs.cc:4878 msgid "user guide not found" msgstr "no trobada la guia d'usuari" #: zfuncs.cc:5742 #, c-format msgid "cannot open file %s" msgstr "no es pot obrir l'arxiu %s" #: zfuncs.cc:5765 msgid "save text to file" msgstr "desar text a l'arxiu" #: zfuncs.cc:6786 msgid "menu text" msgstr "text del menú" #: zfuncs.cc:6787 msgid "menu func" msgstr "funció del menú" #: zfuncs.cc:6788 msgid "menu icon" msgstr "icona del menú" #: zfuncs.cc:6789 msgid "icon size" msgstr "tamany de la icona" #: zfuncs.cc:6792 msgid "Bold" msgstr "Negreta" #: zfuncs.cc:6799 msgid "close window" msgstr "tancar finestra" #: zfuncs.cc:10488 zfuncs.cc:11021 zfuncs.cc:11351 msgid "cancel" msgstr "cancel.lar" #: zfuncs.cc:10982 msgid "choose file" msgstr "escollir arxiu" #: zfuncs.cc:10987 msgid "choose files" msgstr "escollir arxius" #: zfuncs.cc:10992 msgid "save" msgstr "desar" #: zfuncs.cc:10998 msgid "choose folder" msgstr "escollir carpeta" #: zfuncs.cc:11003 msgid "choose folders" msgstr "escollir carpetes" #: zfuncs.cc:11008 msgid "create folder" msgstr "crear carpeta" #: zfuncs.cc:11015 msgid "hidden" msgstr "ocult" #: zfuncs.cc:11351 msgid "done" msgstr "fet" #: zfuncs.cc:11381 msgid "image scale" msgstr "escala de l'imatge" #: zfuncs.cc:11383 msgid "percent" msgstr "percentatge" #~ msgid "Select images to remove" #~ msgstr "Seleccionar imatges per eliminar" #~ msgid "remove %d album files?" #~ msgstr "eliminar %d àlbums?" #~ msgid "KB functions" #~ msgstr "Funcions de teclat" #~ msgid "both" #~ msgstr "tot" #~ msgid "Click for white balance or black level" #~ msgstr "Clic per balanç de blanc o punt negre" #~ msgid "" #~ "check the box, then click on white or dark \n" #~ "area to calibrate image white or black color" #~ msgstr "" #~ "marcar la casella, després clic en una àrea blanca o negra \n" #~ "per cal·librar el color blanc o negre a la imatge" #~ msgid "Zonal Flatten Brightness" #~ msgstr "Aplanarn brillantor zonal" #~ msgid "Tone Mapping" #~ msgstr "Mapa tonal" #~ msgid "Flip" #~ msgstr "Voltejar" #~ msgid "Write Text on Image" #~ msgstr "Escriure text en una imatge" #~ msgid "Write Line or Arrow on Image" #~ msgstr "Dibuixar línia o fletxa en una imatge" #~ msgid "Row↑" #~ msgstr "Fila ↑" #~ msgid "Row↓" #~ msgstr "Fila ↓" #~ msgid "Page↑" #~ msgstr "Pàgina ↑" #~ msgid "Page↓" #~ msgstr "Pàgina ↓" #~ msgid "a sorted album cannot be edited" #~ msgstr "un àlbum ordenat no pot ser editat" #~ msgid "Click list position. Click thumbnail to add." #~ msgstr "Clic una posició de la llista. Clic una miniatura per afegir" #~ msgid "(yyyymmdd)" #~ msgstr "(aaaammdd)" #~ msgid "Batch Convert RAW Files" #~ msgstr "Convertir en lot arxius RAW" #~ msgid "Images:" #~ msgstr "Imatges:" #~ msgid "searching ..." #~ msgstr "buscant ..." #~ msgid "" #~ "Draw a line across the image in \n" #~ "direction of brightness gradient." #~ msgstr "" #~ "Dibuixar una línia creuan la imatge \n" #~ "en la direcció del gradient de brillantor." #~ msgid "Brightness Gradient" #~ msgstr "Gradient de brillantor" #~ msgid "Select directory for thumbnails." #~ msgstr "Sel·leccionar directori per miniatures." #~ msgid "no thumbnails directory defined" #~ msgstr "directori de miniatures no definit" #~ msgid "Dialog font and size" #~ msgstr "Diàleg font i mida" #~ msgid "Open the previously seen file" #~ msgstr "Obrir arxiu vist previament" #~ msgid "Add local contrast, enhance details" #~ msgstr "Afegir contrast local, millorar detalls" #~ msgid "Flatten zonal brightness distribution" #~ msgstr "Aplanar zones de l'histograma" #~ msgid "Write text on image" #~ msgstr "Escriure un text en la imatge" #~ msgid "Write lines or arrows on image" #~ msgstr "Dibuixar línees o fletxes en la imatge" #~ msgid "Add a brightness/color gradient across the image" #~ msgstr "Afegir un gradient de brillantor/color a la imatge" #~ msgid "Convert camera RAW files using libraw" #~ msgstr "Convertir arxius RAW usant libraw" #~ msgid "Convert camera RAW files using Raw Therapee" #~ msgstr "Convertir arxius RAW usant RawTherapee" #~ msgid "Open Previous File" #~ msgstr "Obrir arxiu anterior" #~ msgid "Find Gap" #~ msgstr "Trobar separació" #~ msgid "Zonal Flatten" #~ msgstr "Aplanat zonañl" #~ msgid "Add Lines/Arrows" #~ msgstr "Afegri línees/fletxes" #~ msgid "Batch Raw Therapee" #~ msgstr "Convertir Raw en lot (Raw Therapee)" #~ msgid "Resources" #~ msgstr "Recursos" #~ msgid "Kill active dialog?" #~ msgstr "Tancar diàleg actiu?" #~ msgid "save screen to file" #~ msgstr "desar la pantalla en un arxiu" #~ msgid "slow scroll" #~ msgstr "desplaçament lent" #~ msgid "Save File Version" #~ msgstr "G​uardar​ versión d​'arxiu" #~ msgid "%d files selected" #~ msgstr "%d arxius seleccionats" #~ msgid "Cycle Desktop Wallpaper" #~ msgstr "Fons d'escritori cíclic" #~ msgid "Set Desktop Wallpaper" #~ msgstr "Establir fons d'escriptori" #~ msgid "Cycle desktop wallpaper from a Fotoxx album" #~ msgstr "Fons d'escritori cíclic des d'un àlbum de Fotoxx" #~ msgid "Set desktop wallpaper from current Fotoxx image" #~ msgstr "Establir fons d'escriptori des de la imatge de Fotoxx actual" #~ msgid "Paint or clone image pixels using the mouse" #~ msgstr "Pintar o clonar píxels usant el ratolí" #~ msgid "Directory" #~ msgstr "Directori" #~ msgid "transparency edge" #~ msgstr "transparència de les vores" #~ msgid "transparency center" #~ msgstr "transparència del centre" #~ msgid "copy from image" #~ msgstr "copiar des de imatge" #~ msgid "Paint/Clone" #~ msgstr "Pintar/Clonar" #~ msgid "" #~ "shift + left click: pick color or image position \n" #~ "left click or drag: paint color or copy image \n" #~ "right click or drag: remove color or image" #~ msgstr "" #~ "Majúc + click esquerra: agafar color o posició d'imatge \n" #~ "clic esquerra o arrossegar:pintar amb color o copiar de la imatge \n" #~ "clic dret o arrossegar: eliminar color o imatge" #~ msgid "select image files to be processed" #~ msgstr "sel·leccionar imatges a processar" #~ msgid "unknown script file" #~ msgstr "script desconegut" #~ msgid "select script file to run" #~ msgstr "sel·leccionar scrit a executar" #~ msgid "script file is not closed" #~ msgstr "no s'ha tancat l'script" #~ msgid "open new script file" #~ msgstr "obrir nou script" #~ msgid "not a defined tag: %s" #~ msgstr "no és una etiqueta definida: %s" #~ msgid "Area Color" #~ msgstr "Color de l'àrea" #~ msgid "" #~ "shift + left click: pick image position to copy \n" #~ "left click or drag: copy image to mouse position \n" #~ "right click or drag: restore image" #~ msgstr "" #~ "majúsc + clic esq.: assenyalar posició d'imatge per copiar \n" #~ "botó equerra i arrossegar: copiar imatge en la posició del ratolí \n" #~ "botó dret i arrossegar: restaurar imatge" #~ msgid "Click on a monotone image area." #~ msgstr "Clic en una àrea de la imatge monotonal" #~ msgid "Choose album" #~ msgstr "Escollir àlbums" #~ msgid "Choose image directory" #~ msgstr "Escollir directori d'imatge" #~ msgid "Scroll" #~ msgstr "Desplaçar" #~ msgid "Match Level" #~ msgstr "Nivell de coincidència" #~ msgid "Upload image files to Flickr web service" #~ msgstr "Pujar imatges el servei web de Flickr" #~ msgid "Summary of image edit functions" #~ msgstr "Resum de les funcions d'edició d'imatge" #~ msgid "Set map markers from current gallery" #~ msgstr "Establir marcadors de mapas des de la galeria actual" #~ msgid "Set all images or current gallery" #~ msgstr "Establir totes les imatges o la galeria actual" #~ msgid "go to bookmarked image" #~ msgstr "anar a imatge marcada" #~ msgid "Sync Gallery, Export, Flickr, Albums, Slide Show" #~ msgstr "Sincronitzar galeria, Exportar, Flickr, Albums, Diaporama" #~ msgid "Process: batch convert, batch metadata, image search ..." #~ msgstr "Procés: convertir en lot, metaddes en lot, cerca d'imatges ..." #~ msgid "Flatten Book Page Photo" #~ msgstr "Aplanar foto d'una pàgina d'un llibre " #~ msgid "" #~ "appimage file moved to %s \n" #~ "desktop file created at %s \n" #~ "\n" #~ msgstr "" #~ "Arxiu Appimage magut a %s \n" #~ "arxiu escriptori creat a %s \n" #~ "\n" #~ msgid "Select Album" #~ msgstr "Seleccionar àlbum" #~ msgid "Delete present thumbnails to make effective" #~ msgstr "Esborrar miniatura actual per fer-ho efectiu" #~ msgid "Zooms for 2x" #~ msgstr "Zooms x2" #~ msgid "Image Pan/scroll:" #~ msgstr "Fixar/desplaçar imatge" #~ msgid "Directory Gallery" #~ msgstr "Galeria de directoris" #~ msgid "Previous Image" #~ msgstr "Imatge prèvia" #~ msgid "Select File" #~ msgstr "sel·lecionar arxiu" #~ msgid "Batch Change Metadata" #~ msgstr "Ca​nviar metadades en lot" #~ msgid "Geocoding service by MapQuest" #~ msgstr "Servei de geo-codificació de MapQuest" #~ msgid "choose layout file" #~ msgstr "escollir arxiu de capa" #~ msgid "preference" #~ msgstr "preferència" #~ msgid "slowdown" #~ msgstr "ralentitzar" #~ msgid "enabled" #~ msgstr "activada" #~ msgid "select random (if 2+ enabled)" #~ msgstr "Selecció aleatòria (si +2 activat)" #~ msgid "Upload to Flickr" #~ msgstr "Pujar a Flickr" #~ msgid "reverse upload sequence" #~ msgstr "revertir sequècia de pujada" #~ msgid "%d files uploaded" #~ msgstr "%d arxius pujats" #~ msgid "Edit Functions Summary" #~ msgstr "Resum de les funcions d'edició" #~ msgid "Delete or Trash Image File" #~ msgstr "eliminar/esborrar imatge" #~ msgid "Open RAW file (Raw Therapee)" #~ msgstr "Obrir arxiu RAW amb RawTherapee" #~ msgid "Divisor" #~ msgstr "Divisor" #~ msgid "choose pattern tile" #~ msgstr "Escollir arxiu patrò" #~ msgid "curved image" #~ msgstr "imatge corba" #~ msgid "load area from a file" #~ msgstr "Carregar àrea des d'una imatge" #~ msgid "" #~ "Click on colors to select and deselect. \n" #~ "Set threshold levels for color matching. \n" #~ "Left drag to select or deselect pixels. \n" #~ "Right drag to restore original pixels. \n" #~ "When done, copy or save the finished area \n" #~ "for subsequent pasting into an image. \n" #~ msgstr "" #~ "Feu clic en els colors per seleccionar i deseleccionar. \n" #~ "Establir els nivells de coincidència de colors. \n" #~ "Arrossegar esquerra per seleccionar o deseleccionar píxels. \n" #~ "Arrossegar dreta per restaurar píxels originals. \n" #~ "En acabar, copiar o guardar l'àrea acabada \n" #~ "per enganxat posterior en una imatge. \n" #~ msgid "Select Hairy Edge" #~ msgstr "Seleccionar vores esfilagarsades" #~ msgid "Geocoding by MapQuest" #~ msgstr "Geo-codificar des de MapQuest" #~ msgid "END (press Escape to exit)" #~ msgstr "FI (prèmer Esc per sortir)" #~ msgid "" #~ "Metadata Search report cannot be re-sorted. \n" #~ "Check User Guide for a way to do this." #~ msgstr "" #~ "Informe de cer5ca de metadades no port ser re-ordenat. \n" #~ "Veure guia d'usuari per veure com fer-*ho." #~ msgid "Japanese-fan" #~ msgstr "ventall japonés" #~ msgid "Show Resources" #~ msgstr "Mostrar recursos" #~ msgid "Retinex" #~ msgstr "Retinex" #~ msgid "Open with EOG" #~ msgstr "Obrir amb EOG" #~ msgid "Convert to pencil sketch" #~ msgstr "Convertir en un esbós a llapis" #~ msgid "View an image file with Gnome EOG" #~ msgstr "Veure una imatge amb EOG (Eye of Gnome)" #~ msgid "Cycle 3 most recent files" #~ msgstr "Els 3 arxius més recents en cicle" #~ msgid "Cycle 2 most recent files" #~ msgstr "Els 2 arxius més recents en cicle" #~ msgid "Select images to add" #~ msgstr "Seleccionar imatges per afegir" fotoxx-18.01.1/locales/translate-it.po0000644000175000017500000040725113222767271016264 0ustar micomico# translation of fotoxx.po to italian # Eugenio Baldi , 2009. # (revisione) Doriano Blengino , 2011-2017 # #-#-#-#-# fotoxx.po (messages) #-#-#-#-# # Italian translations for home package. # Copyright (C) 2018 THE home's COPYRIGHT HOLDER # This file is distributed under the same license as the home package. msgid "" msgstr "" "Project-Id-Version: fotoxx\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-12-25 12:47+0100\n" "PO-Revision-Date: 2010-06-03 16:14+0100\n" "Language-Team: italiano \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: f.albums.cc:89 msgid "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." msgstr "" "Cliccare col destro su icona album per tagliare/copiare \n" "sul deposito, inserire da, o eliminare." #: f.albums.cc:91 msgid "" "Right-click left/right side of album \n" "thumbnail to insert cached images \n" "before/after the thumbnail." msgstr "" #: f.albums.cc:94 msgid "Drag album thumbnail to new position." msgstr "Trascinare l'icona dell'album in nuova posizione" #: f.albums.cc:128 f.widgets.cc:613 msgid "Manage Albums" msgstr "Gestisci gli Album" #: f.albums.cc:140 f.albums.cc:275 msgid "Create or replace an album" msgstr "Crea o rimpiazza un album" #: f.albums.cc:142 msgid "Album to view or edit" msgstr "Album da vedere o modificare" #: f.albums.cc:144 msgid "Select images, add to cache" msgstr "" #: f.albums.cc:146 msgid "Clear image cache" msgstr "Azzera il deposito immagini" #: f.albums.cc:148 f.albums.cc:170 msgid "Delete an album" msgstr "Elimina un album" #: f.albums.cc:169 msgid "Choose Album" msgstr "Scegli un album" #: f.albums.cc:171 #, c-format msgid "Image cache has %d images" msgstr "La cache contiene %d immagini" #: f.albums.cc:172 msgid "cache added to empty album" msgstr "Il deposito è stato aggiunto a un album vuoto" #: f.albums.cc:228 #, c-format msgid "delete %s ?" msgstr "Eliminare %s?" #: f.albums.cc:259 #, c-format msgid "fill from image cache (%d images)" msgstr "Riempire da deposito immagini (%d immagini)" #: f.albums.cc:277 f.albums.cc:310 msgid "Album Name" msgstr "Nome dell'album" #: f.albums.cc:283 msgid "make an initially empty album" msgstr "Crea un album vuoto" #: f.albums.cc:285 msgid "fill from current gallery" msgstr "Riempire con la galleria corrente" #: f.albums.cc:324 msgid "enter an album name" msgstr "Immettere un nome per l'album" #: f.albums.cc:352 msgid "new album created" msgstr "Creato un nuovo album" #: f.albums.cc:368 msgid "gallery is empty" msgstr "La galleria è vuota" #: f.albums.cc:876 f.widgets.cc:614 msgid "Update Album Files" msgstr "Aggiorna i file di album" #: f.albums.cc:878 f.albums.cc:938 f.albums.cc:1007 f.albums.cc:1147 msgid "Choose Albums" msgstr "Scegli album" #: f.albums.cc:1005 f.widgets.cc:615 f.widgets.cc:762 msgid "Replace Album File" msgstr "Rimpiazza i file di album" #: f.albums.cc:1009 msgid "All Albums" msgstr "" #: f.albums.cc:1012 msgid "old file" msgstr "Vecchio file" #: f.albums.cc:1016 msgid "new file" msgstr "Nuovo file" #: f.albums.cc:1020 msgid "replace old" msgstr "Rimpiazza il vecchio file" #: f.albums.cc:1021 msgid "add after old" msgstr "Aggiungi dopo il vecchio" #: f.albums.cc:1081 msgid "no albums chosen" msgstr "" #: f.albums.cc:1162 msgid "ALL albums chosen" msgstr "" #: f.albums.cc:1346 msgid "instant" msgstr "istantanea" #: f.albums.cc:1347 msgid "fade-in" msgstr "dissolvenza" #: f.albums.cc:1348 msgid "roll-right" msgstr "tenda-sx-dx" #: f.albums.cc:1349 msgid "roll-down" msgstr "tenda-su-giu" #: f.albums.cc:1350 msgid "venetian" msgstr "Veneziana" #: f.albums.cc:1351 msgid "grate" msgstr "quadrettoni" #: f.albums.cc:1352 msgid "rectangle" msgstr "rettangolo" #: f.albums.cc:1353 msgid "implode" msgstr "implodi" #: f.albums.cc:1354 msgid "explode" msgstr "esplodi" #: f.albums.cc:1355 msgid "radar" msgstr "radar" #: f.albums.cc:1356 msgid "spiral" msgstr "spirale" #: f.albums.cc:1357 msgid "Japan-fan" msgstr "" #: f.albums.cc:1358 msgid "jaws" msgstr "ganasce" #: f.albums.cc:1359 msgid "ellipse" msgstr "ellisse" #: f.albums.cc:1360 msgid "raindrops" msgstr "gocce" #: f.albums.cc:1361 msgid "doubledoor" msgstr "Finestra-doppia" #: f.albums.cc:1362 msgid "rotate" msgstr "ruotare" #: f.albums.cc:1363 msgid "fallover" msgstr "pioggia" #: f.albums.cc:1364 msgid "spheroid" msgstr "Sferoide" #: f.albums.cc:1365 msgid "turn-page" msgstr "Volta pagina" #: f.albums.cc:1366 msgid "french-door" msgstr "pannelli" #: f.albums.cc:1367 msgid "turn-cube" msgstr "ruotacubo" #: f.albums.cc:1368 msgid "windmill" msgstr "mulinello" #: f.albums.cc:1369 msgid "pixelize" msgstr "traspixel" #: f.albums.cc:1370 msgid "twist" msgstr "Intreccia" #: f.albums.cc:1372 msgid "squish" msgstr "" #: f.albums.cc:1399 f.widgets.cc:616 msgid "Slide Show" msgstr "Slideshow" #: f.albums.cc:1405 msgid "use gallery" msgstr "Prendi le immagini dalla galleria corrente" #: f.albums.cc:1411 msgid "Clip Limit" msgstr "Limite dimensione" #: f.albums.cc:1415 msgid "Music File" msgstr "File musicale" #: f.albums.cc:1420 msgid "Full Screen" msgstr "Pieno schermo" #: f.albums.cc:1421 msgid "Auto-replay" msgstr "Riparte automaticamente" #: f.albums.cc:1424 msgid "Customize:" msgstr "Personalizza:" #: f.albums.cc:1425 msgid "transitions" msgstr "transizioni" #: f.albums.cc:1426 msgid "image files" msgstr "immagini" #: f.albums.cc:1427 msgid "KB controls" msgstr "" #: f.albums.cc:1441 f.albums.cc:1509 f.albums.cc:1732 f.albums.cc:2007 #: f.albums.cc:2292 f.albums.cc:2309 f.albums.cc:2526 msgid "invalid album" msgstr "album non valido" #: f.albums.cc:1540 msgid "open album" msgstr "apri album" #: f.albums.cc:1556 msgid "Select music file" msgstr "Seleziona file musicale" #: f.albums.cc:1587 #, c-format msgid "%d images" msgstr "%d immagini" #: f.albums.cc:1614 msgid "arrow keys show previous or next image instantly" msgstr "" #: f.albums.cc:1632 msgid "Keyboard Preferences" msgstr "" #: f.albums.cc:1635 msgid "blank or unblank window" msgstr "" #: f.albums.cc:1638 msgid "show next image, with transition" msgstr "" #: f.albums.cc:1641 msgid "pause or resume slide show" msgstr "" #: f.albums.cc:1644 msgid "magnify image (loupe tool)" msgstr "" #: f.albums.cc:1711 msgid "random sequence" msgstr "Transizione casuale" #: f.albums.cc:1736 msgid "Transition Preferences" msgstr "Preferenze di transizione" #: f.albums.cc:1738 msgid "Transitions File" msgstr "File per le transizioni" #: f.albums.cc:1756 f.albums.cc:1760 msgid "transition" msgstr "Effetto di transizione" #: f.albums.cc:1757 f.albums.cc:1761 msgid "use" msgstr "Abilitata" #: f.albums.cc:1758 f.albums.cc:1762 msgid "slow" msgstr "Rallentamento" #: f.albums.cc:1759 f.albums.cc:1763 msgid "pref" msgstr "Preferenza" #: f.albums.cc:1862 f.albums.cc:1901 msgid "invalid file" msgstr "File non valido" #: f.albums.cc:1954 f.albums.cc:2511 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "formato file errato: \n" " %s" #: f.albums.cc:2013 msgid "Image Preferences" msgstr "Preferenze d'immagine" #: f.albums.cc:2017 f.edit.cc:1405 f.file.cc:1557 f.file.cc:1863 f.meta.cc:820 #: f.meta.cc:1588 f.meta.cc:1780 msgid "Image File:" msgstr "File immagine:" #: f.albums.cc:2021 msgid "Play tone when image shows" msgstr "Suona quando l'immagine appare" #: f.albums.cc:2024 msgid "Show image caption" msgstr "Mostra titolo" #: f.albums.cc:2029 msgid "Show image comments" msgstr "Mostra commenti" #: f.albums.cc:2034 msgid "Wait before zoom" msgstr "Attendi prima d'ingrandire" #: f.albums.cc:2039 msgid "Zoom type:" msgstr "Tipo di zoom:" #: f.albums.cc:2041 msgid "zoom-in" msgstr "ingrandisci" #: f.albums.cc:2042 msgid "zoom-out" msgstr "riduci" #: f.albums.cc:2045 msgid "Zoom size (x)" msgstr "Ingrandimento" #: f.albums.cc:2048 msgid "Steps" msgstr "N. passi" #: f.albums.cc:2052 msgid "Zoom Center" msgstr "Centro per zoom" #: f.albums.cc:2056 msgid "Wait after zoom" msgstr "Attendi dopo zoom" #: f.albums.cc:2061 msgid "Transition to next image" msgstr "Transizione alla prossima immagine" #: f.albums.cc:2064 f.albums.cc:2167 msgid "next" msgstr "prossima" #: f.albums.cc:2157 msgid "click on thumbnail to set zoom center" msgstr "Clicca sulla miniatura per impostare il centro dello zoom" #: f.area.cc:81 msgid "Select Area for Edits" msgstr "Seleziona area per modifiche" #: f.area.cc:82 f.area.cc:1855 f.edit.cc:9094 msgid "Press F1 for help" msgstr "Premi F1 per l'aiuto" #: f.area.cc:91 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Questa funzione non supporta \n" "aree di selezione." #: f.area.cc:120 msgid "select rectangle" msgstr "Sel. rettangolo" #: f.area.cc:121 msgid "select ellipse" msgstr "Sel. ellisse" #: f.area.cc:124 msgid "freehand draw" msgstr "Mano libera" #: f.area.cc:125 msgid "follow edge" msgstr "Insegui contorno" #: f.area.cc:126 msgid "draw/replace" msgstr "Disegna e ritocca" #: f.area.cc:129 msgid "select area within mouse circle" msgstr "Seleziona l'area entro il circolo del mouse" #: f.area.cc:132 msgid "select one matching color within mouse circle:" msgstr "Seleziona un colore corrispondente entro il circolo del mouse:" #: f.area.cc:134 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "Prima selezionare la casella di spunta, poi \n" "Maiusc+Clic nell'immagine per impostare il colore" #: f.area.cc:138 msgid "select all matching colors within mouse circle" msgstr "Seleziona tutti i colori corrispondenti entro il circolo del mouse" #: f.area.cc:144 msgid "match level %" msgstr "Similarità %" #: f.area.cc:147 msgid "search range" msgstr "Raggio di ricerca" #: f.area.cc:151 msgid "Area Edge Blend Width" msgstr "Larghezza sfumatura dell'area" #: f.area.cc:154 msgid "Edge Creep" msgstr "Ritocco lati (dentro/fuori)" #: f.area.cc:159 msgid "Line Color:" msgstr "Colore linea:" #: f.area.cc:179 f.area.cc:180 msgid "area edits fade away within edge distance" msgstr "Le modifiche sfumano entro la distanza dai bordi" #: f.area.cc:351 f.area.cc:517 #, c-format msgid "exceed %d edits" msgstr "superate %d modifiche" #: f.area.cc:1333 msgid "" "Click one time inside each enclosed area. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Cliccare una volta dentro ogni area chiusa. \n" "Eventuali interruzioni nel contorno verranno rilevate. \n" "Usare F1 per l'aiuto." #: f.area.cc:1360 msgid "finish area" msgstr "Finisci area" #: f.area.cc:1389 f.area.cc:1534 #, c-format msgid "found %d pixels" msgstr "trovati %d pixel" #: f.area.cc:1639 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Cliccare vicino al contorno dell'area. \n" "Possibili interruzioni del contorno verranno individuate. \n" "Premere F1 per l'aiuto" #: f.area.cc:1658 msgid "find outline gap" msgstr "trova sconnessione nel contorno" #: f.area.cc:1748 msgid "cannot find area outline" msgstr "impossibile trovare contorno dell'area" #: f.area.cc:1854 f.widgets.cc:458 msgid "Select Hairy" msgstr "Seleziona frastagliature" #: f.area.cc:1862 msgid "select the area first" msgstr "prima selezionare l'area" #: f.area.cc:1867 f.area.cc:2489 f.area.cc:2507 f.area.cc:2527 f.area.cc:2851 #: f.area.cc:2971 msgid "the area is not finished" msgstr "L'area non è chiusa" #: f.area.cc:1940 msgid "select" msgstr "seleziona" #: f.area.cc:1946 msgid "deselect" msgstr "deseleziona" #: f.area.cc:2616 msgid "Edge calculation in progress" msgstr "Ricalcolo bordo in corso" #: f.area.cc:2625 msgid "Area Edge Calc" msgstr "Calcolo area delimitata" #: f.area.cc:2957 f.area.cc:3029 f.widgets.cc:465 msgid "Copy Area" msgstr "Copia area" #: f.area.cc:2960 f.widgets.cc:468 msgid "Save Area File" msgstr "Salva file di area" #: f.area.cc:3035 msgid "save area as a PNG file" msgstr "salva area come file PNG" #: f.area.cc:3159 msgid "position with mouse click/drag" msgstr "posizione con clic o trascinamento" #: f.area.cc:3219 msgid "Paste Image" msgstr "Incolla immagine" #: f.area.cc:3224 f.process.cc:1311 msgid "resize" msgstr "ridimensiona" #: f.combine.cc:169 f.combine.cc:815 f.combine.cc:1416 f.combine.cc:2039 msgid "Select 2 to 9 files" msgstr "Seleziona da 2 a 9 file" #: f.combine.cc:191 f.combine.cc:837 f.combine.cc:1438 f.combine.cc:2061 msgid "Images are not all the same size" msgstr "Le immagini non hanno la stessa dimensione" #: f.combine.cc:550 msgid "Adjust Image Contributions" msgstr "Regola contributi immagine" #: f.combine.cc:554 msgid "dark pixels" msgstr "Pixel scuri" #: f.combine.cc:556 msgid "light pixels" msgstr "Pixel luminosi" #: f.combine.cc:558 msgid "file:" msgstr "File:" #: f.combine.cc:1018 msgid "Paint and Warp Image" msgstr "Dipingi e deforma immagine" #: f.combine.cc:1026 f.edit.cc:5686 msgid "paint" msgstr "Dipingi" #: f.combine.cc:1027 msgid "warp" msgstr "Deforma" #: f.combine.cc:1623 msgid "Select and Paint Image" msgstr "Seleziona e dipingi" #: f.combine.cc:1631 msgid "Transient Objects" msgstr "Soggetti in movimento" #: f.combine.cc:2232 msgid "Adjust Pixel Composition" msgstr "Regola la composizione dei pixel" #: f.combine.cc:2234 msgid "use average" msgstr "Usa media" #: f.combine.cc:2235 msgid "use median" msgstr "Usa mediano" #: f.combine.cc:2237 msgid "omit low pixel" msgstr "ometti pixel bassi" #: f.combine.cc:2238 msgid "omit high pixel" msgstr "ometti pixel alti" #: f.combine.cc:2502 f.combine.cc:3653 msgid "Select 2 to 4 files" msgstr "Seleziona da 2 a 4 file" #: f.combine.cc:2573 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Trascina le immagini a un allineamento approssimativo.\n" "Per ruotarle, trascina il bordo inferiore." #: f.combine.cc:2575 f.combine.cc:3726 msgid "no curve (scanned image)" msgstr "nessuna curva (immagine scansionata)" #: f.combine.cc:2576 f.combine.cc:3727 msgid "Search for lens mm" msgstr "Ricerca per mm lenti" #: f.combine.cc:2577 f.combine.cc:3728 msgid "Save lens mm → image EXIF" msgstr "Salva mm della lente nelle informazioni EXIF" #: f.combine.cc:2639 f.combine.cc:3790 msgid "Pre-align Images" msgstr "Pre-allineamento immagini" #: f.combine.cc:2643 f.combine.cc:3794 msgid "lens mm" msgstr "mm lente" #: f.combine.cc:2648 f.combine.cc:3799 msgid "no auto warp" msgstr "Deformazione automatica esclusa" #: f.combine.cc:2650 f.combine.cc:3801 msgid "manual align" msgstr "Allineamento manuale" #: f.combine.cc:2652 f.combine.cc:3803 f.widgets.cc:473 f.widgets.cc:768 msgid "Resize" msgstr "Ridimensiona" #: f.combine.cc:2653 f.combine.cc:3804 msgid "resize window" msgstr "Ridimensiona finestra" #: f.combine.cc:2661 f.combine.cc:3812 msgid "do not warp images during auto-alignment" msgstr "Non deformare immagini durante l'auto allineamento" #: f.combine.cc:2707 f.combine.cc:3858 msgid "use two images only" msgstr "Utilizza solo due immagini" #: f.combine.cc:2735 f.combine.cc:2939 f.combine.cc:3127 f.combine.cc:3884 #: f.combine.cc:4088 f.combine.cc:4276 msgid "Too little overlap, cannot align" msgstr "Troppo poca sovrapposizione, impossibile allineare" #: f.combine.cc:3205 f.combine.cc:4354 msgid "Match Brightness and Color" msgstr "Allinea luminosità e colore" #: f.combine.cc:3251 msgid "Select image" msgstr "Selezionare immagine" #: f.combine.cc:3266 f.combine.cc:4415 msgid "auto color" msgstr "colore automatico" #: f.combine.cc:3267 f.combine.cc:4416 msgid "file color" msgstr "file colore" #: f.combine.cc:3273 f.combine.cc:4422 msgid "mouse warp" msgstr "Distorcere col mouse" #: f.combine.cc:3275 f.combine.cc:4424 msgid "flatten image" msgstr "Appiattisci immagine" #: f.combine.cc:3724 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Trascina le immagini a un allineamento approssimativo.\n" "Per ruotarle, trascina il bordo destro." #: f.combine.cc:4684 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) non è installato" #: f.combine.cc:4693 msgid "Select at least 2 files" msgstr "Selezionare almeno 2 file" #: f.edit.cc:98 msgid "Trim: drag middle to move, drag corners to resize" msgstr "" "Ritaglia: trascina il centro per spostare, gli angoli per ridimensionare" #: f.edit.cc:99 msgid "Minor rotate: drag right edge with mouse" msgstr "Rotazione precisa: trascina il lato destro" #: f.edit.cc:180 f.widgets.cc:471 f.widgets.cc:767 msgid "Trim/Rotate" msgstr "Ritaglia/Ruota" #: f.edit.cc:192 f.edit.cc:1306 msgid "ratio" msgstr "Rapporto:" #: f.edit.cc:196 msgid "trim size:" msgstr "Dimensioni del ritaglio:" #: f.edit.cc:201 msgid "Lock Ratio" msgstr "Blocca proporzioni" #: f.edit.cc:203 msgid "maximize trim box" msgstr "Massimizza la casella di ritaglio" #: f.edit.cc:204 msgid "use previous size" msgstr "Usa dimensioni precedenti" #: f.edit.cc:205 msgid "invert width/height" msgstr "Scambia larghezza e altezza" #: f.edit.cc:206 msgid "trim transparent edges" msgstr "Ritaglia i lati trasparenti" #: f.edit.cc:207 msgid "lock width/height ratio" msgstr "Blocca il rapporto tra larghezza e altezza" #: f.edit.cc:212 msgid "Customize" msgstr "Personalizza" #: f.edit.cc:218 msgid "Level" msgstr "Livello" #: f.edit.cc:219 msgid "use EXIF data if available" msgstr "Utilizza i dati EXIF se disponibili" #: f.edit.cc:222 msgid "Rotate: degrees" msgstr "Ruota: gradi" #: f.edit.cc:1302 msgid "Trim Buttons" msgstr "Pulsanti di taglio" #: f.edit.cc:1305 msgid "label" msgstr "Etichetta" #: f.edit.cc:1402 msgid "Upright Image" msgstr "Raddrizza immagine" #: f.edit.cc:1408 f.process.cc:168 f.process.cc:755 f.widgets.cc:472 #: f.widgets.cc:769 msgid "Upright" msgstr "Raddrizza" #: f.edit.cc:1461 msgid "rotation unknown" msgstr "Rotazione sconosciuta" #: f.edit.cc:1548 msgid "Resize Image" msgstr "Ridimensiona" #: f.edit.cc:1573 msgid "W/H Ratio:" msgstr "Rapporto Larg/Alt" #: f.edit.cc:1575 msgid "Lock" msgstr "Blocca" #: f.edit.cc:1577 msgid "use previous settings" msgstr "Usa impostazioni esistenti" #: f.edit.cc:2298 f.widgets.cc:476 f.widgets.cc:772 msgid "Retouch Combo" msgstr "Regola i colori" #: f.edit.cc:2319 msgid "Amplifier" msgstr "Amplificatore" #: f.edit.cc:2320 fotoxx.h:1188 msgid "Brightness" msgstr "Luminosità" #: f.edit.cc:2321 f.effects.cc:3681 fotoxx.h:1198 msgid "Contrast" msgstr "Contrasto" #: f.edit.cc:2322 msgid "Low Color" msgstr "Poco colore" #: f.edit.cc:2323 msgid "Warmer" msgstr "Caldo" #: f.edit.cc:2324 f.effects.cc:1332 msgid "Dark Areas" msgstr "Aree scure" #: f.edit.cc:2333 msgid "Max." msgstr "Max" #: f.edit.cc:2334 f.edit.cc:2335 f.edit.cc:2336 msgid "High" msgstr "Alto" #: f.edit.cc:2337 msgid "Cooler" msgstr "Freddo" #: f.edit.cc:2338 msgid "Bright" msgstr "Luminose" #: f.edit.cc:2341 f.tools.cc:2134 msgid "Brightness Distribution" msgstr "Distribuzione della luminosità" #: f.edit.cc:2344 msgid "Click for white balance" msgstr "" #: f.edit.cc:2345 msgid "Click for black level" msgstr "" #: f.edit.cc:2348 msgid "Settings File" msgstr "File di impostazioni" #: f.edit.cc:2352 msgid "recall previous settings used" msgstr "Richiama impostazioni usate in precedenza" #: f.edit.cc:3004 msgid "Adjust Brightness Distribution" msgstr "Regola la distribuzione di luminosità" #: f.edit.cc:3043 msgid "Low Cutoff" msgstr "Più scuri" #: f.edit.cc:3044 msgid "High Cutoff" msgstr "Più chiari" #: f.edit.cc:3045 msgid "Low Flatten" msgstr "Meno scuri" #: f.edit.cc:3046 msgid "Mid Flatten" msgstr "Più medi" #: f.edit.cc:3047 msgid "High Flatten" msgstr "Più chiari" #: f.edit.cc:3048 msgid "Low Stretch" msgstr "Comprimi scuri" #: f.edit.cc:3049 msgid "Mid Stretch" msgstr "Comprimi medi" #: f.edit.cc:3050 msgid "High Stretch" msgstr "Comprimi chiari" #: f.edit.cc:3383 msgid "Magnify Gradients" msgstr "" #: f.edit.cc:3415 msgid "low" msgstr "basso" #: f.edit.cc:3417 msgid "high" msgstr "alto" #: f.edit.cc:3420 msgid "Amplify" msgstr "Amplificazione" #: f.edit.cc:3813 msgid "Flatten Brightness" msgstr "" #: f.edit.cc:3864 msgid "Zones" msgstr "Zone" #: f.edit.cc:3871 msgid "Deband Dark" msgstr "Escludi scuri" #: f.edit.cc:3874 msgid "Deband Bright" msgstr "Escludi chiari" #: f.edit.cc:4386 msgid "Dark Point" msgstr "" #: f.edit.cc:4387 msgid "Bright Point" msgstr "" #: f.edit.cc:4388 msgid "Multiplyer" msgstr "" #: f.edit.cc:4412 msgid "brightness rescale" msgstr "" #: f.edit.cc:4415 msgid "click bright point" msgstr "" #: f.edit.cc:4416 msgid "click dark point" msgstr "" #: f.edit.cc:4427 msgid "blend" msgstr "" #: f.edit.cc:4430 msgid "reduce bright" msgstr "" #: f.edit.cc:5468 f.widgets.cc:481 msgid "Mirror Image" msgstr "" #: f.edit.cc:5472 f.warp.cc:3300 msgid "horizontal" msgstr "Orizzontale" #: f.edit.cc:5473 f.warp.cc:3304 msgid "vertical" msgstr "Verticale" #: f.edit.cc:5620 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "Maiusc + clic sinistro: preleva colore dall'immagine \n" "Trascina col sinistro: dipinge sull'immagine \n" "Trascina col destro: ripristina l'originale" #: f.edit.cc:5656 msgid "Paint on Image" msgstr "Dipingi sull'immagine" #: f.edit.cc:5662 msgid "paint color" msgstr "Colore pennello" #: f.edit.cc:5673 f.edit.cc:6640 msgid "brush size" msgstr "Dimensione pennello" #: f.edit.cc:5674 f.edit.cc:6641 msgid "opacity center" msgstr "Opacità al centro" #: f.edit.cc:5675 f.edit.cc:6642 msgid "opacity edge" msgstr "Opacità dei bordi" #: f.edit.cc:5687 msgid "erase" msgstr "Cancella" #: f.edit.cc:5688 msgid "include transparent areas" msgstr "Anche aree trasparenti" #: f.edit.cc:5695 msgid "drag image" msgstr "Sposta immagine senza dipingere" #: f.edit.cc:5697 msgid "zoom image" msgstr "Ingrandimento immagine" #: f.edit.cc:6221 msgid "Color Palette" msgstr "Tavolozza dei colori" #: f.edit.cc:6225 msgid "click on image to choose color" msgstr "Cliccare sull'immagine per scegliere il colore" #: f.edit.cc:6415 f.repair.cc:3799 msgid "Color Hue" msgstr "Tono del colore" #: f.edit.cc:6416 f.repair.cc:3768 f.repair.cc:3800 msgid "Saturation" msgstr "Saturazione" #: f.edit.cc:6417 f.repair.cc:3769 f.repair.cc:3801 msgid "Lightness" msgstr "Lucentezza" #: f.edit.cc:6599 msgid "" "shift + left click: pick image position to copy \n" "left drag: copy image to mouse position \n" "right drag: restore image" msgstr "" "Maiusc+clic sinistro: sceglie posizione da copiare \n" "Trascina sinistro: copia immagine nella nuova posizione \n" "Trascina destro: ripristina immagine" #: f.edit.cc:6630 f.widgets.cc:483 msgid "Clone Image" msgstr "Clona immagine" #: f.edit.cc:6649 msgid "paint over transparent areas" msgstr "pitturare su aree trasparenti" #: f.edit.cc:7098 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "Trascina col tasto sinistra: miscela \n" "Trascina col tasto destro: usa originale" #: f.edit.cc:7126 f.widgets.cc:484 msgid "Blend Image" msgstr "Miscela immagine" #: f.edit.cc:7134 f.repair.cc:7663 msgid "strength center" msgstr "Rafforza il centro" #: f.edit.cc:7135 f.repair.cc:7664 msgid "strength edge" msgstr "Rafforza lati" #: f.edit.cc:7359 msgid "+Version" msgstr "+Versione" #: f.edit.cc:7382 f.widgets.cc:275 msgid "Add Text to Image" msgstr "" #: f.edit.cc:7383 msgid "Enter text, click/drag on image, right click to remove" msgstr "" "Imposta il testo e clicca/trascina nell'immagine, clic destro per rimuovere" #: f.edit.cc:7432 f.edit.cc:8310 msgid "Use settings file" msgstr "Usa file d'impostazioni" #: f.edit.cc:7437 f.mashup.cc:2455 msgid "Text" msgstr "Testo" #: f.edit.cc:7442 msgid "Use metadata key" msgstr "Usa chiave metadati" #: f.edit.cc:7461 f.mashup.cc:2473 msgid "text" msgstr "Testo" #: f.edit.cc:7462 f.edit.cc:8336 f.mashup.cc:2474 f.mashup.cc:2812 msgid "backing" msgstr "Sfondo" #: f.edit.cc:7463 f.edit.cc:8337 f.mashup.cc:2475 f.mashup.cc:2813 msgid "outline" msgstr "Contorno" #: f.edit.cc:7464 f.edit.cc:8338 f.mashup.cc:2476 f.mashup.cc:2814 msgid "shadow" msgstr "Ombra" #: f.edit.cc:7490 msgid "save to current file" msgstr "Salve nel file corrente" #: f.edit.cc:7491 f.file.cc:2227 msgid "save as new file version" msgstr "salva come nuova versione" #: f.edit.cc:7492 msgid "" "save to current file \n" "open next file with same text" msgstr "" "Salva nel file corrente \n" "apre il prossimo con lo stesso testo" #: f.edit.cc:7652 f.mashup.cc:2580 f.tools.cc:1464 msgid "select font" msgstr "Scegli il font" #: f.edit.cc:7928 f.edit.cc:8766 msgid "text file is defective" msgstr "file dei testi difettoso" #: f.edit.cc:8268 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Immettere dati linea/freccia nella finestrella,\n" "poi cliccare/trascinare; clic destro per eliminare" #: f.edit.cc:8302 f.widgets.cc:276 msgid "Add lines or arrows to an image" msgstr "" #: f.edit.cc:8315 f.mashup.cc:2791 msgid "Line length" msgstr "Lunghezza linea" #: f.edit.cc:8322 f.mashup.cc:2798 msgid "Arrow head" msgstr "Terminazione freccia" #: f.edit.cc:8335 f.mashup.cc:2811 msgid "line" msgstr "Linea" #: f.edit.cc:8364 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "Fissa linea/freccia\n" "inizia un'altra" #: f.edit.cc:9093 f.widgets.cc:487 msgid "Paint Edits" msgstr "Pennella funzione" #: f.edit.cc:9100 f.edit.cc:9323 fotoxx-18.01.cc:3426 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "L'area selezionata non può essere mantenuta.\n" "Continuare?" #: f.edit.cc:9108 f.edit.cc:9331 f.tools.cc:2666 msgid "Edit function must be active" msgstr "Una funzione di edit deve essere attiva" #: f.edit.cc:9113 msgid "Cannot use Paint Edits" msgstr "Impossibile usare Pennella Funzione" #: f.edit.cc:9140 msgid "power: center" msgstr "Forza: centro:" #: f.edit.cc:9145 msgid "reset area" msgstr "Azzera area" #: f.edit.cc:9316 f.widgets.cc:488 msgid "Leverage Edits" msgstr "Modula ritocco" #: f.edit.cc:9317 msgid "Edit Function Amplifier" msgstr "Regola amplificazione di funzione" #: f.edit.cc:9336 msgid "Cannot use Leverage Edits" msgstr "Impossibile usare Modula Ritocco" #: f.edit.cc:9365 msgid "minimum" msgstr "Minimo" #: f.edit.cc:9367 msgid "maximum" msgstr "Massimo" #: f.edit.cc:9642 f.edit.cc:9686 msgid "Edit Plugins" msgstr "Gestione plug-in" #: f.edit.cc:9643 msgid "Edit plugins menu" msgstr "Modifica menu plug-in" #: f.edit.cc:9651 msgid "Run as Fotoxx edit function" msgstr "Esegui come funzione di edit fotoxx" #: f.edit.cc:9688 msgid "menu name" msgstr "Nome nel menù" #: f.edit.cc:9691 msgid "command" msgstr "Comando" #: f.edit.cc:9888 msgid "Plugin working ..." msgstr "Esecuzione plugin..." #: f.edit.cc:9897 msgid "plugin failed" msgstr "plugin non trovato" #: f.effects.cc:79 msgid "Convert to Sketch" msgstr "Converti in schizzo" #: f.effects.cc:157 msgid "Clip Level" msgstr "Bianco" #: f.effects.cc:161 msgid "Algorithm" msgstr "Metodo" #: f.effects.cc:166 msgid "Foreground" msgstr "Colore" #: f.effects.cc:169 f.tools.cc:1275 msgid "Background" msgstr "Sfondo" #: f.effects.cc:1054 f.widgets.cc:531 msgid "Line Drawing" msgstr "Tracciamento linea" #: f.effects.cc:1067 msgid "black/white" msgstr "Bianco e nero" #: f.effects.cc:1326 f.widgets.cc:532 msgid "Color Drawing" msgstr "Tracciamento colori" #: f.effects.cc:1333 msgid "Bright Areas" msgstr "Aree luminose" #: f.effects.cc:1575 f.widgets.cc:533 msgid "Graduated Blur" msgstr "Sfumatura graduale" #: f.effects.cc:1579 msgid "Contrast Limit" msgstr "Limite contrasto" #: f.effects.cc:1582 f.repair.cc:929 msgid "Blur Radius" msgstr "Raggio di sfumatura" #: f.effects.cc:1799 msgid "Simulate Embossing" msgstr "Simula un bassorilievo" #: f.effects.cc:1820 msgid "depth" msgstr "Profondità" #: f.effects.cc:2029 msgid "Simulate Tiles" msgstr "Simula un mosaico" #: f.effects.cc:2033 msgid "tile size" msgstr "Dimensione tessere" #: f.effects.cc:2036 msgid "tile gap" msgstr "Distanza fra tessere" #: f.effects.cc:2039 msgid "3D depth" msgstr "Profondità 3D" #: f.effects.cc:2258 msgid "Convert Image to Dots" msgstr "Converti immagine in punti" #: f.effects.cc:2262 msgid "dot size" msgstr "Dimensione punto" #: f.effects.cc:2484 msgid "Simulate Painting" msgstr "Simula un dipinto" #: f.effects.cc:2502 msgid "color depth" msgstr "Profondità di colore" #: f.effects.cc:2506 msgid "patch area goal" msgstr "Fattore artistico" #: f.effects.cc:2510 msgid "req. color match" msgstr "Precisione colori" #: f.effects.cc:2514 msgid "borders" msgstr "Contorni" #: f.effects.cc:3081 f.widgets.cc:538 msgid "Vignette" msgstr "Effetto vignetta" #: f.effects.cc:3430 msgid "Add Texture" msgstr "Aggiungi disturbo" #: f.effects.cc:3646 msgid "Background Pattern" msgstr "Motivo di sfondo" #: f.effects.cc:3650 msgid "Pattern File:" msgstr "File motivo:" #: f.effects.cc:3655 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3658 msgid "Geometry" msgstr "Geometria" #: f.effects.cc:3659 msgid "Calculate" msgstr "Calcola" #: f.effects.cc:3662 f.widgets.cc:540 msgid "Pattern" msgstr "Motivo" #: f.effects.cc:3670 msgid "Overlap" msgstr "Sovrapposizione" #: f.effects.cc:3678 msgid "Opacity" msgstr "Opacità" #: f.effects.cc:4124 msgid "Create Mosaic" msgstr "Creazione Mosaico" #: f.effects.cc:4170 msgid "Tile" msgstr "Tessera" #: f.effects.cc:4178 f.widgets.cc:535 msgid "Tiles" msgstr "Tessere" #: f.effects.cc:4184 msgid "Tile blending" msgstr "Miscelazione tessere" #: f.effects.cc:4265 #, c-format msgid "exceeded max. tiles: %d" msgstr "Superato massimo tessere: %d" #: f.effects.cc:4272 #, c-format msgid "only %d tile images found" msgstr "Trovate solo %d tessere" #: f.effects.cc:4756 f.widgets.cc:542 msgid "Custom Kernel" msgstr "Matrice di trasformazione" #: f.effects.cc:4760 msgid "Kernel size" msgstr "Dimensione matrice" #: f.effects.cc:4777 msgid "multiply" msgstr "Moltiplica" #: f.effects.cc:4780 msgid "add" msgstr "Aggiungi" #: f.effects.cc:4784 msgid "Data file" msgstr "File di dati" #: f.effects.cc:4804 fotoxx-18.01.cc:3690 msgid "Load settings from file" msgstr "Carica impostazioni dal file" #: f.effects.cc:5046 msgid "Pull image using the mouse." msgstr "Tira l'immagine con il mouse" #: f.effects.cc:5067 f.widgets.cc:543 msgid "Directed Blur" msgstr "Sfumatura interattiva" #: f.effects.cc:5071 msgid "blur span" msgstr "Dimensione" #: f.effects.cc:5074 msgid "intensity" msgstr "Intensità" #: f.effects.cc:5276 f.widgets.cc:544 msgid "Blur Background" msgstr "Sfuma lo sfondo" #: f.effects.cc:5280 msgid "constant blur" msgstr "Sfumatura costante" #: f.effects.cc:5283 msgid "increase blur with distance" msgstr "Sfumatura maggiore con la distanza" #: f.effects.cc:5285 msgid "min. blur radius" msgstr "Raggio minimo" #: f.effects.cc:5288 msgid "max. blur radius" msgstr "Raggio massimo" #: f.effects.cc:5322 f.warp.cc:819 f.warp.cc:1252 msgid "no active Select Area" msgstr "Area di selezione non attiva" #: f.effects.cc:5515 f.widgets.cc:545 msgid "Alien Colors" msgstr "Effetto colori alieni" #: f.effects.cc:5518 msgid "blocksize" msgstr "Dimensione tessere" #: f.effects.cc:5521 f.warp.cc:3298 msgid "amplitude" msgstr "Ampiezza" #: f.file.cc:190 msgid "" "Use EXIF photo date or \n" " file modification date?" msgstr "" "Usare data EXIF o \n" " data del file?" #: f.file.cc:204 f.widgets.cc:626 msgid "File" msgstr "File" #: f.file.cc:378 f.process.cc:1345 msgid "Raw Therapee not installed" msgstr "Programma Raw Therapee non installato" #: f.file.cc:396 f.widgets.cc:432 f.widgets.cc:777 msgid "Open RAW (Raw Therapee)" msgstr "Apre file RAW (con Raw Therapee)" #: f.file.cc:401 msgid "RAW type not registered in User Settings" msgstr "Tipo RAW non registrato in preferenze utente" #: f.file.cc:416 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee non ha prodotto alcun file TIF" #: f.file.cc:802 f.widgets.cc:429 msgid "Open Image File" msgstr "Apri immagine" #: f.file.cc:821 f.process.cc:597 msgid "unknown file type" msgstr "tipo di file sconosciuto" #: f.file.cc:999 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Inizio galleria, nome della precedente: %s" #: f.file.cc:1000 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Fine galleria, nome della prossima: %s" #: f.file.cc:1108 msgid "Create Blank Image" msgstr "Crea immagine vuota" #: f.file.cc:1110 msgid "file name" msgstr "Nome file" #: f.file.cc:1144 msgid "supply a file name" msgstr "fornire un nome di file" #: f.file.cc:1314 f.widgets.cc:436 msgid "Rename Image File" msgstr "Rinomina immagine" #: f.file.cc:1321 msgid "Old Name" msgstr "Vecchio nome" #: f.file.cc:1322 f.process.cc:130 msgid "New Name" msgstr "Nuovo nome" #: f.file.cc:1331 msgid "previous name" msgstr "Nome precedente" #: f.file.cc:1332 msgid "Add 1" msgstr "Aggiungi 1" #: f.file.cc:1335 f.file.cc:1571 f.file.cc:1866 msgid "keep this dialog open" msgstr "Mantieni aperto questo dialogo" #: f.file.cc:1520 msgid "Copy or Move Image File" msgstr "Copia o sposta il file dell'immagine" #: f.file.cc:1562 msgid "New Location:" msgstr "Nuova posizione:" #: f.file.cc:1567 msgid "copy (duplicate file)" msgstr "Copia (duplica il file)" #: f.file.cc:1568 msgid "move (remove original)" msgstr "Sposta (cancella il file originale)" #: f.file.cc:1609 f.process.cc:573 f.process.cc:1503 f.process.cc:2625 msgid "Select directory" msgstr "Seleziona cartella" #: f.file.cc:1647 msgid "new location is not a directory" msgstr "la destinazione non è una cartella" #: f.file.cc:1688 f.file.cc:1942 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "errore in cancellazione: \n" " %s" #: f.file.cc:1827 f.widgets.cc:442 msgid "Delete/Trash Image File" msgstr "Elimina/cestina il file dell'immagine" #: f.file.cc:1896 msgid "GTK g_file_trash() function failed" msgstr "La funzione GTK g_file_trash() è fallita" #: f.file.cc:1915 msgid "not a known image file" msgstr "Il file non contiene un'immagine riconosciuta" #: f.file.cc:1921 msgid "Delete read-only file?" msgstr "Eliminare il file di sola lettura?" #: f.file.cc:1923 msgid "Trash read-only file?" msgstr "Cestinare un file di sola lettura?" #: f.file.cc:1971 msgid "no more images" msgstr "fine delle immagini" #: f.file.cc:2209 msgid "Save Image File" msgstr "Salva file immagine" #: f.file.cc:2219 msgid "new version" msgstr "nuova versione" #: f.file.cc:2220 msgid "new file ..." msgstr "Nuovo file ..." #: f.file.cc:2221 msgid "replace file" msgstr "rimpiazza file" #: f.file.cc:2228 f.file.cc:2800 msgid "save as new file name or type" msgstr "salva come nuovo nome o diverso tipo" #: f.file.cc:2230 msgid "replace old file (OVERWRITE)" msgstr "rimpiazza il vecchio file (SOVRASCRIVE)" #: f.file.cc:2267 f.file.cc:2558 msgid "cannot save as RAW type" msgstr "impossibile salvare come tipo RAW" #: f.file.cc:2375 msgid "too many file versions: 99" msgstr "Troppe versioni del file: 99" #: f.file.cc:2552 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "Le informazioni di trasparenza saranno perse.\n" "Salvare come PNG per mantenerle." #: f.file.cc:2637 msgid "save anyway" msgstr "Salva comunque" #: f.file.cc:2706 msgid "Unable to copy EXIF/IPTC data" msgstr "Non riesco a copiare i dati EXIF/IPTC" #: f.file.cc:2818 f.file.cc:2821 msgid "make current" msgstr "Rendi corrente" #: f.file.cc:2819 msgid "(new file becomes current file)" msgstr "(il nuovo file diventa quello corrente)" #: f.file.cc:2932 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Sovrascrivere il file? \n" " %s" #: f.file.cc:3127 f.widgets.cc:599 msgid "Quick Start" msgstr "Introduzione rapida" #: f.file.cc:3130 f.widgets.cc:600 msgid "User Guide" msgstr "Guida utente" #: f.file.cc:3133 f.widgets.cc:601 msgid "User Guide Changes" msgstr "Cronistoria della guida utente" #: f.file.cc:3136 f.widgets.cc:602 msgid "README" msgstr "Read-me" #: f.file.cc:3139 f.widgets.cc:603 msgid "Change Log" msgstr "Cronistoria delle modifiche" #: f.file.cc:3142 f.widgets.cc:604 msgid "Log File" msgstr "Mostra file di log" #: f.file.cc:3145 f.widgets.cc:605 msgid "Translations" msgstr "Come tradurre" #: f.file.cc:3148 f.widgets.cc:606 msgid "Home Page" msgstr "Sito WEB" #: f.file.cc:3151 f.widgets.cc:607 msgid "About" msgstr "Info" #: f.file.cc:3157 f.widgets.cc:639 f.widgets.cc:663 f.widgets.cc:676 #: f.widgets.cc:690 fotoxx.h:1223 msgid "Help" msgstr "Aiuto" #: f.file.cc:3362 f.file.cc:3367 f.file.cc:3437 #, c-format msgid "file type not supported: %s" msgstr "tipo di file non supportato: %s" #: f.gallery.cc:1221 msgid "GoTo" msgstr "Vai a..." #: f.gallery.cc:1227 f.widgets.cc:655 msgid "Sort" msgstr "Riordina" #: f.gallery.cc:1233 f.widgets.cc:653 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:1243 f.widgets.cc:654 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:1255 msgid "Row Up" msgstr "" #: f.gallery.cc:1263 msgid "Row Down" msgstr "" #: f.gallery.cc:1271 f.widgets.cc:658 msgid "Page Up" msgstr "" #: f.gallery.cc:1279 f.widgets.cc:659 msgid "Page Down" msgstr "" #: f.gallery.cc:1287 f.widgets.cc:656 msgid "First" msgstr "Prima" #: f.gallery.cc:1294 f.widgets.cc:657 msgid "Last" msgstr "Ultima" #: f.gallery.cc:1425 f.gallery.cc:1445 msgid "recent images" msgstr "Immagini recenti" #: f.gallery.cc:1426 f.gallery.cc:1450 msgid "newest images" msgstr "Immagini più nuove" #: f.gallery.cc:1504 msgid "no albums found" msgstr "nessun album trovato" #: f.gallery.cc:1541 msgid "Albums cannot be sorted" msgstr "Gli album non si possono riordinare" #: f.gallery.cc:1545 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" #: f.gallery.cc:1564 msgid "Gallery Sort" msgstr "Riordina galleria" #: f.gallery.cc:1568 msgid "File Name" msgstr "Nome file" #: f.gallery.cc:1569 msgid "File Mod Date/Time" msgstr "Data di modifica file" #: f.gallery.cc:1570 msgid "Photo Date/Time (EXIF)" msgstr "Data della foto (EXIF)" #: f.gallery.cc:1572 msgid "ascending" msgstr "ascendente" #: f.gallery.cc:1573 msgid "descending" msgstr "discendente" #: f.gallery.cc:2925 f.gallery.cc:2929 msgid "Image File" msgstr "File immagine" #: f.gallery.cc:2927 msgid "select thumbnail" msgstr "Selezione icona" #: f.gallery.cc:3037 fotoxx.h:1276 msgid "Select Files" msgstr "Seleziona i file" #: f.gallery.cc:3097 #, c-format msgid "exceed %d selected files" msgstr "selezionati troppi file, %d" #: f.gallery.cc:3457 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" #: f.gallery.cc:3484 f.gallery.cc:3734 msgid "Edit Bookmarks" msgstr "Gestisci segnalibri" #: f.gallery.cc:3662 msgid "unable to save bookmarks file" msgstr "Impossibile salvare i segnalibri" #: f.gallery.cc:3734 msgid "Go To Bookmark" msgstr "Va al segnalibro" #: f.mashup.cc:170 msgid "Mashup layout and background image" msgstr "Disposizione e sfondo del collage" #: f.mashup.cc:213 msgid "choose an image file" msgstr "Scegliere una immagine" #: f.mashup.cc:214 msgid "use current image file" msgstr "Usare l'immagine corrente" #: f.mashup.cc:215 msgid "specify layout size and color" msgstr "Specificare dimensioni e colore" #: f.mashup.cc:216 msgid "open a Mashup project file" msgstr "Aprire un file di progetto del collage" #: f.mashup.cc:259 msgid "no current file" msgstr "nessun file corrente" #: f.mashup.cc:291 msgid "Make Layout Image" msgstr "Crea immagine collage" #: f.mashup.cc:293 msgid "project name" msgstr "nome del progetto" #: f.mashup.cc:319 msgid "supply a project name" msgstr "fornire un nome del progetto" #: f.mashup.cc:411 msgid "Edit Images" msgstr "Modifica immagini" #: f.mashup.cc:412 f.mashup.cc:2450 msgid "Edit Text" msgstr "Modifica testi" #: f.mashup.cc:413 msgid "Edit Line" msgstr "Edit linea" #: f.mashup.cc:414 msgid "Rescale" msgstr "Riscala" #: f.mashup.cc:419 msgid "add or edit images" msgstr "Aggiungi o modifica immagini" #: f.mashup.cc:421 msgid "add or edit text" msgstr "Aggiungi o cambia testi" #: f.mashup.cc:423 msgid "add or edit lines/arrows" msgstr "Aggiungere o modificare linee/frecce" #: f.mashup.cc:425 msgid "change project scale" msgstr "cambia scala del progetto" #: f.mashup.cc:427 msgid "project complete" msgstr "Progetto completo" #: f.mashup.cc:429 msgid "cancel project" msgstr "Annulla progetto" #: f.mashup.cc:447 msgid "rescale project" msgstr "Riscala il progetto" #: f.mashup.cc:506 msgid "save Mashup output file" msgstr "Salva risultato del collage" #: f.mashup.cc:526 msgid "save Mashup project file?" msgstr "Salvo il progetto collage?" #: f.mashup.cc:538 msgid "delete Mashup project file?" msgstr "Elimino il progetto collage?" #: f.mashup.cc:597 msgid "Open Project" msgstr "Apri un progetto" #: f.mashup.cc:626 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "File immagine composizione mancante: \n" " %s" #: f.mashup.cc:683 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "File immagine di sfondo mancante: \n" " %s " #: f.mashup.cc:989 msgid "project file is defective" msgstr "Il file di progetto è difettoso" #: f.mashup.cc:1019 msgid "Save Project" msgstr "Salva il progetto" #: f.mashup.cc:1160 msgid "layout exceeds 2 gigabytes" msgstr "Il collage eccede 2 gigabyte" #: f.mashup.cc:1234 msgid "Click image to select, drag image to move." msgstr "Clicca immagine per selezionare, trascina per spostare." #: f.mashup.cc:1235 msgid "Make black margins transparent" msgstr "Rende trasparenti i margini neri" #: f.mashup.cc:1274 msgid "Current image:" msgstr "Immagine corrente: " #: f.mashup.cc:1278 msgid "Cycle through images:" msgstr "Cicla attraverso le immagini: " #: f.mashup.cc:1285 msgid "Scale" msgstr "Scala" #: f.mashup.cc:1294 msgid "Stacking Order" msgstr "Ordine di impilamento" #: f.mashup.cc:1295 msgid "Raise" msgstr "Solleva" #: f.mashup.cc:1296 msgid "Lower" msgstr "Abbassa" #: f.mashup.cc:1299 msgid "Base Transparency" msgstr "Trasparenza" #: f.mashup.cc:1303 msgid "Var. Transparency" msgstr "Trasparenza manuale" #: f.mashup.cc:1304 msgid "Paint" msgstr "Dipinto" #: f.mashup.cc:1307 msgid "Bend and fine-align" msgstr "Deforma e collima" #: f.mashup.cc:1308 f.widgets.cc:634 msgid "Warp" msgstr "Deforma" #: f.mashup.cc:1317 zfuncs.cc:11351 zfuncs.cc:11360 msgid "Margins" msgstr "Margini" #: f.mashup.cc:1318 msgid "Hard" msgstr "Forte" #: f.mashup.cc:1319 f.repair.cc:4410 msgid "Blend" msgstr "Mescola" #: f.mashup.cc:1333 msgid "add images to layout" msgstr "Aggiunge immagini alla composizione" #: f.mashup.cc:1570 msgid "Paint Image Transparencies" msgstr "Dipingi aree trasparenti" #: f.mashup.cc:1588 msgid "Gradual" msgstr "Graduale" #: f.mashup.cc:1590 fotoxx.h:1255 msgid "Power" msgstr "Forza" #: f.mashup.cc:1823 msgid "Pull on the image with the mouse." msgstr "Trascina il mouse sull'immagine" #: f.mashup.cc:1839 msgid "Warp Image" msgstr "Deforma immagine" #: f.mashup.cc:1845 f.warp.cc:1383 msgid "warp span" msgstr "Dimensione deformazione: " #: f.mashup.cc:2281 #, c-format msgid "exceeded %d images" msgstr "troppe immagini (massimo %d)" #: f.mashup.cc:2423 msgid "Enter text, [Add] to layout, edit properties." msgstr "Immetti il testo, [Aggiungi], modifica le opzioni" #: f.mashup.cc:2503 msgid "Text File:" msgstr "File di testo:" #: f.mashup.cc:2507 msgid "add entered text to layout" msgstr "Aggiunge testo alla composizione" #: f.mashup.cc:2641 msgid "click position to add text" msgstr "Clicca la posizione del testo" #: f.mashup.cc:2647 #, c-format msgid "exceeded %d text entries" msgstr "troppi testi (massimo %d)" #: f.mashup.cc:2652 f.widgets.cc:485 msgid "Add Text" msgstr "Aggiungi scritte" #: f.mashup.cc:2761 msgid "Set line properties, [Add] to layout, edit." msgstr "Imposta opzioni linea, [Aggiungi], modifica" #: f.mashup.cc:2785 msgid "Edit Line/Arrow" msgstr "Modifica linea/freccia" #: f.mashup.cc:2840 msgid "add line/arrow to layout" msgstr "Aggiunge linea/freccia alla composizione" #: f.mashup.cc:2931 msgid "click position to add line" msgstr "Clicca la posizione della linea" #: f.mashup.cc:2937 #, c-format msgid "exceeded %d line entries" msgstr "Immesse più di %d linee" #: f.mashup.cc:2942 f.widgets.cc:486 msgid "Add Line" msgstr "" #: f.mashup.cc:4189 msgid "Image Montage" msgstr "Esposizione d'immagini" #: f.mashup.cc:4198 msgid "Frame Width" msgstr "Larghezza cornice" #: f.mashup.cc:4201 f.mashup.cc:4209 msgid "Margin" msgstr "Spaziatura" #: f.mashup.cc:4206 msgid "Image Columns" msgstr "Numero di colonne" #: f.mashup.cc:4223 #, c-format msgid "exceed %d rows" msgstr "Troppe righe: %d" #: f.mashup.cc:4319 msgid "Optimize" msgstr "Ottimizza" #: f.mashup.cc:4327 #, c-format msgid "column difference: %d pixels" msgstr "Differenza tra colonne: %d pixel" #: f.mashup.cc:4385 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "Differenza tra colonne: %d pixel \n" "Pareggiare le colonne?" #: f.mashup.cc:4407 msgid "Save with unique montage name" msgstr "Salva con nome unico" #: f.mashup.cc:4409 msgid "unique name:" msgstr "Nome unico:" #: f.mashup.cc:4411 msgid "create image map" msgstr "Crea mappa dell'immagine" #: f.mashup.cc:4421 f.meta.cc:8637 msgid "supply a reasonable name" msgstr "Fornire un nome corretto" #: f.mashup.cc:4432 msgid "save montage" msgstr "Salva esposizione" #: f.mashup.cc:4452 #, c-format msgid "map file saved: %s" msgstr "File di mappa salvato: %s" #: f.mashup.cc:4492 #, c-format msgid "%d max images exceeded" msgstr "Troppe immagini: %d" #: f.mashup.cc:4566 f.mashup.cc:4572 #, c-format msgid "image frame is too large: %d x %d" msgstr "La dimensione della cornice è troppo grande: %d x %d" #: f.mashup.cc:4873 #, c-format msgid "montage map file not found: %s" msgstr "File mappa dell'immagine non trovato: %s" #: f.mashup.cc:4877 #, c-format msgid "montage map file invalid: %s" msgstr "File mappa dell'immagine non valido: %s" #: f.meta.cc:242 msgid "Add Metadata Items" msgstr "Aggiunta di voci di metadati" #: f.meta.cc:246 f.meta.cc:1583 f.meta.cc:3104 msgid "click to select" msgstr "cliccare per selezionare" #: f.meta.cc:251 msgid "click to unselect" msgstr "Cliccare per deselezionare" #: f.meta.cc:475 msgid "Extras" msgstr "Extra" #: f.meta.cc:475 f.meta.cc:655 f.widgets.cc:754 msgid "View Metadata" msgstr "Mostra i metadati" #: f.meta.cc:813 f.widgets.cc:450 f.widgets.cc:755 msgid "Edit Metadata" msgstr "Modifica metadati (normale)" #: f.meta.cc:816 msgid "save metadata to file" msgstr "Salva i metadati in un file" #: f.meta.cc:825 msgid "Image Date" msgstr "Data immagine:" #: f.meta.cc:828 msgid "Time" msgstr "Ora" #: f.meta.cc:836 msgid "Rating (stars):" msgstr "Punteggio (stelle):" #: f.meta.cc:848 msgid "Caption" msgstr "Titolo" #: f.meta.cc:853 msgid "Comments" msgstr "Commenti" #: f.meta.cc:860 msgid "Location" msgstr "Luogo" #: f.meta.cc:863 msgid "Country" msgstr "Nazione" #: f.meta.cc:886 msgid "Image Tags" msgstr "Etichette" #: f.meta.cc:891 f.meta.cc:1992 msgid "Recent Tags" msgstr "Etichette recenti:" #: f.meta.cc:896 f.meta.cc:1998 msgid "Enter New Tag" msgstr "Immetti nuova etichetta" #: f.meta.cc:902 f.meta.cc:2004 f.meta.cc:4565 msgid "Matching Tags" msgstr "Etichette corrispondenti" #: f.meta.cc:909 f.meta.cc:2013 f.meta.cc:2511 f.meta.cc:4571 msgid "Defined Tags Category" msgstr "Categorie conosciute" #: f.meta.cc:916 msgid "search known locations" msgstr "ricerca luoghi conosciuti" #: f.meta.cc:917 msgid "search using web service" msgstr "ricerca tramite servizio web" #: f.meta.cc:918 f.meta.cc:2020 msgid "create tag categories and tags" msgstr "Crea etichette e relative categorie" #: f.meta.cc:1368 f.meta.cc:3673 f.meta.cc:7231 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "Latitudine/Longitudine errati: %s %s" #: f.meta.cc:1423 fotoxx.h:1234 msgid "Manage Tags" msgstr "Gestione etichette" #: f.meta.cc:1423 msgid "orphan tags" msgstr "Etichette orfane" #: f.meta.cc:1427 msgid "category" msgstr "Categoria" #: f.meta.cc:1430 msgid "tag" msgstr "Etichetta" #: f.meta.cc:1437 msgid "Defined Tags:" msgstr "Etichette definite:" #: f.meta.cc:1579 f.widgets.cc:451 f.widgets.cc:756 msgid "Edit Any Metadata" msgstr "Modifica metadati (avanzato)" #: f.meta.cc:1579 f.meta.cc:3099 msgid "Full List" msgstr "Lista completa" #: f.meta.cc:1592 f.meta.cc:3115 msgid "key name" msgstr "Nome chiave" #: f.meta.cc:1595 f.meta.cc:3116 msgid "key value" msgstr "Valore chiave" #: f.meta.cc:1677 f.meta.cc:3259 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" "Il comando: $ man Image::ExifTool::TagNames \n" "mostrerà oltre 15.000 nomi di etichette \"standard\"" #: f.meta.cc:1777 f.widgets.cc:452 msgid "Delete Metadata" msgstr "Elimina metadati..." #: f.meta.cc:1783 fotoxx.h:1179 msgid "All" msgstr "Tutto" #: f.meta.cc:1784 msgid "One Key:" msgstr "Una chiave:" #: f.meta.cc:1968 f.widgets.cc:568 msgid "Batch Add/Remove Tags" msgstr "Aggiungi/elimina etichette in massa" #: f.meta.cc:1981 msgid "tags to add" msgstr "Etichette da aggiungere" #: f.meta.cc:1982 msgid "tags to remove" msgstr "Etichette da rimuovere" #: f.meta.cc:2095 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " troppe etichette" #: f.meta.cc:2108 msgid "repeat with same files?" msgstr "" #: f.meta.cc:2290 msgid "specify files and tags" msgstr "specificare file ed etichette" #: f.meta.cc:2490 f.widgets.cc:569 msgid "Batch Rename Tags" msgstr "Rinomina etichette in massa" #: f.meta.cc:2500 msgid "(click defined tag)" msgstr "(cliccare le etichette definite)" #: f.meta.cc:2502 f.process.cc:749 msgid "Rename to" msgstr "Rinomina come" #: f.meta.cc:2519 msgid "old tag name >> new tag name" msgstr "vecchia etichetta >> nuova etichetta" #: f.meta.cc:2576 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" "%d etichette da rinominare \n" "in %d immagini. \n" "Procedere?" #: f.meta.cc:2720 msgid "max tags exceeded" msgstr "Richieste troppe etichette" #: f.meta.cc:2782 f.widgets.cc:570 msgid "Batch Photo Date/Time" msgstr "" #: f.meta.cc:2790 msgid "set a new date/time:" msgstr "" #: f.meta.cc:2798 msgid "shift existing date/time:" msgstr "" #: f.meta.cc:2824 msgid "test: show changes, do not update files" msgstr "" #: f.meta.cc:2850 f.meta.cc:3190 f.meta.cc:3368 msgid "no files selected" msgstr "Nessun file selezionato" #: f.meta.cc:2880 f.meta.cc:2888 f.meta.cc:2894 msgid "invalid date/time format" msgstr "" #: f.meta.cc:3099 f.widgets.cc:571 msgid "Batch Add/Change Metadata" msgstr "Aggiunge/Modifica metadati in massa" #: f.meta.cc:3184 msgid "enter key names" msgstr "Immetti i nomi delle etichette" #: f.meta.cc:3354 f.widgets.cc:572 msgid "Batch Report Metadata" msgstr "Prospetto metadati multipli" #: f.meta.cc:3498 f.widgets.cc:573 msgid "Batch Geotags" msgstr "Applicazione geotag in massa" #: f.meta.cc:3528 msgid "location" msgstr "Luogo" #: f.meta.cc:3531 msgid "country" msgstr "Nazione" #: f.meta.cc:3659 msgid "" "data is incomplete \n" " proceed?" msgstr "" "I dati sono incompleti \n" " proseguire?" #: f.meta.cc:3745 msgid "Report Image Locations" msgstr "Cerca luoghi con immagini" #: f.meta.cc:3746 msgid "Group by country" msgstr "Raggruppa per nazione" #: f.meta.cc:3747 msgid "Group by country/location" msgstr "Raggruppa per nazione/luogo" #: f.meta.cc:3748 msgid "Group by country/location/date" msgstr "Raggruppa per nazione/luogo/data" #: f.meta.cc:3749 msgid "Group by date/country/location" msgstr "Raggruppa per date/nazione/luogo" #: f.meta.cc:3752 msgid "Combine within" msgstr "Combina entro" #: f.meta.cc:3754 msgid "days" msgstr "giorni" #: f.meta.cc:3860 f.widgets.cc:1026 f.widgets.cc:1048 msgid "Image Locations" msgstr "Locazioni delle immagini" #: f.meta.cc:4137 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Gen Feb Mar Apr Mag Giu Lug Ago Set Ott Nov Dic" #: f.meta.cc:4489 msgid "Search Image Metadata" msgstr "Cerca nei metadati dell'immagine" #: f.meta.cc:4493 msgid "images to search:" msgstr "immagini da cercare:" #: f.meta.cc:4494 msgid "all" msgstr "tutte" #: f.meta.cc:4495 msgid "current set only" msgstr "insieme corrente" #: f.meta.cc:4498 msgid "matching images:" msgstr "immagini corrispondenti:" #: f.meta.cc:4499 msgid "new set" msgstr "nuovo insieme" #: f.meta.cc:4500 msgid "add to set" msgstr "aggiungi all'insieme" #: f.meta.cc:4501 msgid "remove" msgstr "rimuovi" #: f.meta.cc:4504 msgid "report type:" msgstr "Tipo di risultato:" #: f.meta.cc:4505 msgid "gallery" msgstr "galleria" #: f.meta.cc:4509 msgid "date range" msgstr "Intervallo date: " #: f.meta.cc:4514 msgid "photo date" msgstr "" #: f.meta.cc:4515 msgid "file date" msgstr "" #: f.meta.cc:4516 msgid "(yyyy-mm-dd)" msgstr "" #: f.meta.cc:4519 msgid "stars range" msgstr "Voto da/a:" #: f.meta.cc:4522 msgid "last version only" msgstr "Solo ultime versioni" #: f.meta.cc:4524 msgid "all/any" msgstr "Tutte/Qualsiasi" #: f.meta.cc:4527 msgid "search tags" msgstr "Cerca etichette" #: f.meta.cc:4533 msgid "search text" msgstr "Cerca testo" #: f.meta.cc:4539 msgid "search files" msgstr "Cerca nei file" #: f.meta.cc:4545 msgid "search locations" msgstr "cerca nei luoghi" #: f.meta.cc:4549 msgid "enter cities, countries" msgstr "Immettere nomi di città o nazioni" #: f.meta.cc:4554 msgid "search other metadata" msgstr "cerca in altri metadati" #: f.meta.cc:4561 msgid "Enter Search Tag" msgstr "Immettere etichette di ricerca" #: f.meta.cc:4837 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "per rimuovere immagini dall'insieme corrente, \n" "cercare nell'insieme corrente" #: f.meta.cc:4844 msgid "" "to add images to current set, \n" "search all images" msgstr "" "per aggiungere immagini all'insieme corrente, \n" "cercare in tutte le immagini" #: f.meta.cc:4890 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "Date di ricerca non ragionevoli \n" " %s %s" #: f.meta.cc:4915 msgid "stars range not reasonable" msgstr "punteggio (stelle) non ammissibile" #: f.meta.cc:5129 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "Immagini aggiunte: %d; tolte: %d; totale corrente: %d" #: f.meta.cc:5132 msgid "no changes made" msgstr "nessun cambiamento fatto" #: f.meta.cc:5303 msgid "" "These items are always reported: \n" "date, stars, tags, caption, comment" msgstr "" "Questi elementi sono sempre mostrati: \n" "data, stelle, etichette, titolo, commenti" #: f.meta.cc:5328 msgid "Additional Items for Report" msgstr "Informazioni addizionali da riportare" #: f.meta.cc:5335 msgid "Keyword" msgstr "parola chiave" #: f.meta.cc:5349 msgid "Match Criteria" msgstr "criterio di corrispondenza" #: f.meta.cc:5455 #, c-format msgid "bad number: %s" msgstr "numero errato: %s" #: f.meta.cc:5712 msgid "date format is YYYY-MM-DD" msgstr "Il formato della data è AAAA-MM-GG" #: f.meta.cc:5716 msgid "date is invalid" msgstr "la data non è valida" #: f.meta.cc:5750 msgid "time format is HH:MM [:SS]" msgstr "Il formato dell'ora è HH:MM [:SS]" #: f.meta.cc:5754 msgid "time is invalid" msgstr "L'ora non è valida" #: f.meta.cc:6851 msgid "not found" msgstr "non trovata" #: f.meta.cc:6852 msgid "location and country required" msgstr "Indicare luogo e nazione" #: f.meta.cc:7111 msgid "choose location" msgstr "Scegli luogo" #: f.meta.cc:7408 msgid "Set Map Markers" msgstr "Imposta segnalini sulla mappa" #: f.meta.cc:7409 msgid "mark all image files" msgstr "Mostra foto inerenti alla mappa" #: f.meta.cc:7410 msgid "mark current gallery" msgstr "Considera solo la galleria corrente" #: f.meta.cc:7510 msgid "choose map file" msgstr "Scegliere file di mappa" #: f.meta.cc:7652 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "pacchetto fotoxx-maps non installato \n" "(vedi https://kornelix.net)" #: f.meta.cc:7657 #, c-format msgid "map file %s is missing" msgstr "File di mappa %s mancante" #: f.meta.cc:7661 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" "Latitudine/longitudine della mappa non validi:\n" " %.3f %.3f %.3f %.3f" #: f.meta.cc:7981 f.meta.cc:8508 msgid "No matching images found" msgstr "Nessuna immagine soddisfa la ricerca" #: f.meta.cc:8073 msgid "Set Map Source" msgstr "Selezionare fornitore mappa" #: f.meta.cc:8584 msgid "Net Map Locations" msgstr "Elenco dei luoghi della mappa" #: f.meta.cc:8589 msgid "map location:" msgstr "Punto della mappa:" #: f.process.cc:123 f.widgets.cc:559 msgid "Batch Convert" msgstr "Conversione in massa" #: f.process.cc:135 msgid "Sequence Numbers" msgstr "Numeri sequenziali per nome" #: f.process.cc:137 msgid "base" msgstr "comune" #: f.process.cc:140 msgid "adder" msgstr "addizione" #: f.process.cc:145 msgid "New Location" msgstr "Nuova destinazione:" #: f.process.cc:150 msgid "New File Type" msgstr "Nuovo tipo di file" #: f.process.cc:154 f.process.cc:162 f.process.cc:2538 msgid "no change" msgstr "NON modificare" #: f.process.cc:157 f.process.cc:2533 msgid "max. Width" msgstr "Larghezza max" #: f.process.cc:166 msgid "Delete Originals" msgstr "Elimina gli originali" #: f.process.cc:167 f.process.cc:754 msgid "Copy Metadata" msgstr "Copia i metadati" #: f.process.cc:171 f.process.cc:756 f.process.cc:1320 f.repair.cc:121 #: f.widgets.cc:492 msgid "Sharpen" msgstr "Contrasta" #: f.process.cc:181 f.process.cc:757 msgid "Overlay Image" msgstr "Immagine sovrimpressa" #: f.process.cc:184 msgid "Width %" msgstr "Larghezza %" #: f.process.cc:189 msgid "Position" msgstr "Posizione" #: f.process.cc:201 msgid "Make constant size for screen:" msgstr "Fissare dimensione per schermo:" #: f.process.cc:209 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "plugin (anno mese giorno vecchionome sequence) $yyyy $mm $dd $oldname $s" #: f.process.cc:331 #, c-format msgid "file type not supported: %s \n" msgstr "" #: f.process.cc:471 f.process.cc:2588 msgid "cannot create new file" msgstr "Errore in creazione del nuovo file" #: f.process.cc:517 msgid "updating albums ..." msgstr "aggiornamento degli album..." #: f.process.cc:658 #, c-format msgid "invalid plugin: %s" msgstr "plugin non valido: %s" #: f.process.cc:665 msgid "you must use either $s or $oldname" msgstr "occorre usare $s oppure $oldname" #: f.process.cc:670 msgid "$s plugin needs base and adder" msgstr "il $s richiede base e addizione" #: f.process.cc:675 msgid "base and adder need $s plugin" msgstr "per base e addizione serve plugin $s" #: f.process.cc:697 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "Dimensioni massime %d x %d non ragionevoli" #: f.process.cc:704 msgid "specify overlay image file" msgstr "Specifica file da sovraimprimere" #: f.process.cc:720 msgid "specify overlay position" msgstr "Specificare posizione per sovraimpressione" #: f.process.cc:748 #, c-format msgid "Convert %d image files" msgstr "Conversione %d file immagine" #: f.process.cc:750 msgid "Convert to" msgstr "Converti a" #: f.process.cc:751 msgid "Resize within" msgstr "Ridimensiona entro" #: f.process.cc:752 msgid "Output to" msgstr "Produci su" #: f.process.cc:758 msgid "*** Delete Originals ***" msgstr "*** ELIMINA gli originali ***" #: f.process.cc:759 msgid "*** Replace Originals ***" msgstr "*** RIMPIAZZA gli originali ***" #: f.process.cc:760 msgid "PROCEED?" msgstr "PROSEGUO?" #: f.process.cc:913 f.widgets.cc:560 msgid "Batch Upright" msgstr "Raddrizzare immagini in massa" #: f.process.cc:919 msgid "Survey all files" msgstr "Controlla tutti i file" #: f.process.cc:956 msgid "file cannot be read" msgstr "Impossibile leggere il file" #: f.process.cc:1072 msgid "cannot select both options" msgstr "Non selezionare entrambe le opzioni" #: f.process.cc:1114 f.widgets.cc:561 msgid "Batch Delete/Trash" msgstr "Elimina/cestina in massa" #: f.process.cc:1119 msgid "delete" msgstr "Elimina" #: f.process.cc:1122 msgid "trash" msgstr "Cestina" #: f.process.cc:1260 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Conversione in massa di file RAW (Raw Therapee)" #: f.process.cc:1290 msgid "use libraw" msgstr "" #: f.process.cc:1291 msgid "use Raw Therapee" msgstr "" #: f.process.cc:1298 msgid "output location" msgstr "Cartella o directory di uscita" #: f.process.cc:1303 msgid "output file type" msgstr "Tipo file d'uscita" #: f.process.cc:1322 msgid "amount" msgstr "Quantità" #: f.process.cc:1325 msgid "threshold" msgstr "Soglia" #: f.process.cc:1614 msgid "script file" msgstr "File di script" #: f.process.cc:1627 msgid "begin making a script file" msgstr "Inizia a comporre uno script" #: f.process.cc:1630 msgid "finish making a script file" msgstr "Termina la composizione dello script" #: f.process.cc:1636 msgid "execute a script file" msgstr "Esegui uno script da file" #: f.process.cc:1696 msgid "script already started" msgstr "Script già in esecuzione" #: f.process.cc:1700 msgid "start a new script file" msgstr "Crea nuovo file di script" #: f.process.cc:1723 msgid "perform edits to be included in the script file" msgstr "Eseguire le operazioni da registrare nello script" #: f.process.cc:1741 msgid "script file error" msgstr "Errore nel file script" #: f.process.cc:1746 #, c-format msgid "%s added to script" msgstr "%s aggiunto allo script" #: f.process.cc:1759 msgid "no script file was started" msgstr "Nessuno script è stato avviato" #: f.process.cc:1767 msgid "script file closed" msgstr "File script chiuso" #: f.process.cc:1825 msgid "open script file" msgstr "Aprire un file di script" #: f.process.cc:1833 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "File script: %s\n" " %s" #: f.process.cc:1855 f.process.cc:1904 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "Errore in apertura: %s \n" " %s" #: f.process.cc:1876 #, c-format msgid "unknown edit function: %s" msgstr "Funzione di edit sconosciuta: %s" #: f.process.cc:1885 #, c-format msgid "load widgets failed: %s" msgstr "Errore di caricamento widget: %s" #: f.process.cc:1913 #, c-format msgid "script file format error: %s" msgstr "Formato file script errato: %s" #: f.process.cc:1949 msgid "growisofs not installed" msgstr "growisofs non è installato" #: f.process.cc:2001 msgid "no DVD/BlueRay device found" msgstr "nessuna dispositivo DVD/Blueray trovato" #: f.process.cc:2024 f.widgets.cc:564 msgid "Burn Images to DVD/BlueRay" msgstr "Registrare immagini su DVD/Blueray" #: f.process.cc:2029 msgid "Select device" msgstr "Selezionare dispositivo" #: f.process.cc:2136 f.widgets.cc:565 msgid "Find Duplicate Images" msgstr "Trova immagini duplicate" #: f.process.cc:2138 msgid "Thumbnail size" msgstr "Dimensione miniatura" #: f.process.cc:2142 msgid "pixel difference" msgstr "Differenza di pixel" #: f.process.cc:2145 msgid "pixel count" msgstr "Conteggio pixel" #: f.process.cc:2152 msgid "Duplicates:" msgstr "Duplicate:" #: f.process.cc:2170 #, c-format msgid "only %d image thumbnails found" msgstr "Trovate solo %d miniature" #: f.process.cc:2374 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Crea un file con le immagini selezionate" #: f.process.cc:2398 f.process.cc:2467 msgid "Output File" msgstr "File d'uscita" #: f.process.cc:2418 msgid "no input files selected" msgstr "nessun file di ingresso selezionato" #: f.process.cc:2424 msgid "no output file selected" msgstr "nessun file d'uscita selezionato" #: f.process.cc:2523 f.widgets.cc:567 msgid "Export Image Files" msgstr "Esporta immagini" #: f.process.cc:2528 msgid "To Location" msgstr "Verso la cartella" #: f.process.cc:2563 msgid "file type not supported" msgstr "Tipo di file non supportato" #: f.process.cc:2643 msgid "location is not a directory" msgstr "La locazione immessa non è una cartella" #: f.repair.cc:164 msgid "dark" msgstr "scuro" #: f.repair.cc:165 msgid "light" msgstr "chiaro" #: f.repair.cc:1193 msgid "Apply repeatedly while watching the image." msgstr "Applica ripetutamente osservando l'immagine" #: f.repair.cc:1194 msgid "Measure" msgstr "Misura" #: f.repair.cc:1228 msgid "Noise Reduction" msgstr "Riduzione disturbo" #: f.repair.cc:1247 f.widgets.cc:479 f.widgets.cc:774 fotoxx.h:1218 msgid "Flatten" msgstr "Quantità" #: f.repair.cc:1248 msgid "Median" msgstr "Filtro mediano" #: f.repair.cc:1263 msgid "dark areas" msgstr "zone scure" #: f.repair.cc:1265 msgid "all areas" msgstr "tutte le zone" #: f.repair.cc:1856 msgid "Measure Noise" msgstr "Misura disturbo" #: f.repair.cc:1857 msgid "Move mouse in a monotone image area." msgstr "Spostare il mouse in una zona monotona/omogenea" #: f.repair.cc:2165 msgid "" "Method 1:\n" " Left-click on red-eye to darken.\n" "Method 2:\n" " Drag down and right to enclose red-eye.\n" " Left-click on red-eye to darken.\n" "Undo red-eye:\n" " Right-click on red-eye." msgstr "" "Metodo 1:\n" " - Clicca col pulsante sinistro sull'occhio rosso per scurirlo.\n" "Metodo 2:\n" " - Trascina verso destra e in basso per circondare l'occhio rosso. \n" " - Clicca col pulsante sinistro sull'occhio rosso per scurirlo. \n" "Per annullare:\n" " - Clicca col pulsante destro sull'occhio rosso." #: f.repair.cc:2181 msgid "Red Eye Reduction" msgstr "Riduzione occhi rossi" #: f.repair.cc:2642 f.widgets.cc:496 msgid "Color Mode" msgstr "Cambia i colori" #: f.repair.cc:2645 msgid "reset" msgstr "Azzera" #: f.repair.cc:2646 msgid "black/white positive" msgstr "Bianco e nero, positivo" #: f.repair.cc:2647 msgid "black/white negative" msgstr "Bianco e nero, negativo" #: f.repair.cc:2648 msgid "color negative" msgstr "A colori, negativo" #: f.repair.cc:2649 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.repair.cc:2650 msgid "sepia" msgstr "Seppia" #: f.repair.cc:2861 msgid "Set color depth to 1-16 bits" msgstr "Imposta la risoluzione del colore da 1 a 16 bit" #: f.repair.cc:2875 msgid "Set Color Depth" msgstr "Imposta la risoluzione di colore" #: f.repair.cc:3025 f.widgets.cc:498 msgid "Shift Colors" msgstr "Modula componenti" #: f.repair.cc:3259 f.widgets.cc:499 msgid "Color Saturation" msgstr "Saturazione del colore" #: f.repair.cc:3448 msgid "+Brightness" msgstr "+Luminosità" #: f.repair.cc:3449 msgid "+Red -Cyan" msgstr "+Rosso -Ciano" #: f.repair.cc:3450 msgid "+Green -Magenta" msgstr "+Verde -Magenta" #: f.repair.cc:3451 msgid "+Blue -Yellow" msgstr "+Blu -Giallo" #: f.repair.cc:3457 fotoxx.h:1261 msgid "Red" msgstr "Rosso" #: f.repair.cc:3458 fotoxx.h:1220 msgid "Green" msgstr "Verde" #: f.repair.cc:3459 fotoxx.h:1186 msgid "Blue" msgstr "Blu" #: f.repair.cc:3761 msgid "Input color to match and adjust:" msgstr "Scegliere il colore da confrontare e regolare:" #: f.repair.cc:3763 msgid "shift+click on image to select color" msgstr "Maiusc+clic sull'immagine per selezionare il colore" #: f.repair.cc:3766 f.repair.cc:7937 msgid "Match using:" msgstr "Metodo di contronto:" #: f.repair.cc:3767 msgid "Hue" msgstr "Tono" #: f.repair.cc:3779 msgid "Output Color" msgstr "Colore d'uscita" #: f.repair.cc:3802 msgid "Adjustment" msgstr "Regolazione" #: f.repair.cc:4320 msgid "Click image to select areas." msgstr "Clicca nell'immagine per selezionare le aree" #: f.repair.cc:4352 msgid "Local Color" msgstr "Colore locale" #: f.repair.cc:4363 msgid "Metric:" msgstr "Metrica" #: f.repair.cc:4786 msgid "Color Match Images" msgstr "Allinea colori" #: f.repair.cc:4812 msgid "mouse radius for color sample" msgstr "Raggio di prelievo del colore" #: f.repair.cc:4814 f.repair.cc:4819 fotoxx.h:1250 msgid "Open" msgstr "Apri" #: f.repair.cc:4815 msgid "image for source color" msgstr "l'immagine di riferimento" #: f.repair.cc:4817 msgid "click on image to get source color" msgstr "Clicca nell'immagine per prelevare il colore" #: f.repair.cc:4820 msgid "image to set matching color" msgstr "l'immagine a cui impostare il colore" #: f.repair.cc:4822 msgid "click on image to set matching color" msgstr "Clicca nell'immagine per allineare il colore" #: f.repair.cc:4881 msgid "select source image color first" msgstr "Seleziona prima l'immagine di riferimento" #: f.repair.cc:5059 msgid "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " msgstr "" " Regola ogni componente RGB per minimizzare \n" " le frange di colore agli estremi dell'immagine. " #: f.repair.cc:5082 f.widgets.cc:504 msgid "Color Fringes" msgstr "Frange di colore" #: f.repair.cc:5240 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Trascina per selezionare. \n" "2. Cancella. 3. Ripeti. " #: f.repair.cc:5262 f.widgets.cc:505 msgid "Smart Erase" msgstr "Cancellazione intelligente" #: f.repair.cc:5267 fotoxx.h:1259 msgid "Radius" msgstr "Raggio" #: f.repair.cc:5269 f.widgets.cc:493 msgid "Blur" msgstr "Sfuoca" #: f.repair.cc:5272 msgid "New Area" msgstr "Nuova area" #: f.repair.cc:5617 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" #: f.repair.cc:5656 f.widgets.cc:506 msgid "Brightness Ramp" msgstr "" #: f.repair.cc:6182 f.widgets.cc:507 msgid "Remove Dust" msgstr "Rimozione polvere" #: f.repair.cc:6186 msgid "spot size limit" msgstr "Raggio max della polvere" #: f.repair.cc:6189 msgid "max. brightness" msgstr "Luminosità massima" #: f.repair.cc:6192 msgid "min. contrast" msgstr "Contrasto minimo" #: f.repair.cc:6999 f.widgets.cc:510 msgid "Stuck Pixels" msgstr "Pixel rovinati (incollati)" #: f.repair.cc:7005 msgid "pixel group" msgstr "Gruppo di pixel:" #: f.repair.cc:7013 f.repair.cc:7085 msgid "stuck pixels:" msgstr "Pixel difettosi:" #: f.repair.cc:7113 msgid "Load Stuck Pixels" msgstr "Carica file pixel difettosi" #: f.repair.cc:7115 msgid "File:" msgstr "File:" #: f.repair.cc:7139 f.repair.cc:7196 msgid "Stuck Pixels file" msgstr "File di pixel difettosi" #: f.repair.cc:7176 f.tools.cc:4220 msgid "file format error" msgstr "Formato file errato" #: f.repair.cc:7622 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "trascina con sinistro: aggiunge trasparenza \n" "trascina con destro: aggiunge opacità" #: f.repair.cc:7654 f.widgets.cc:511 msgid "Paint Transparency" msgstr "Dipingi trasparenza" #: f.repair.cc:7662 msgid "paintbrush radius" msgstr "Raggio del pennello:" #: f.repair.cc:7669 msgid "gradual paint" msgstr "pittura graduale" #: f.repair.cc:7923 f.widgets.cc:512 msgid "Add Transparency" msgstr "Aggiungi trasparenza" #: f.repair.cc:7927 msgid "Match Brightness" msgstr "Regolato dalla luminosità" #: f.repair.cc:7932 msgid "Match Color" msgstr "Regolato dal colore" #: f.repair.cc:7934 msgid "click on image to select color" msgstr "Cliccare nell'immagine per scegliere il colore" #: f.repair.cc:7950 msgid "Invert Match" msgstr "Inverti la soglia" #: f.repair.cc:7953 fotoxx.h:1285 msgid "Transparency" msgstr "Trasparenza:" #: f.tools.cc:86 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" "Seleziona directory contenenti immagini \n" "(le sottodirectory sono incluse automaticamente)." #: f.tools.cc:88 msgid "Select to add, click on X to delete." msgstr "Seleziona per aggiungere, clicca X per eliminare." #: f.tools.cc:89 msgid "directory for thumbnails" msgstr "" #: f.tools.cc:90 msgid "extra metadata items to include in index" msgstr "" #: f.tools.cc:91 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Funzione indice terminata. \n" "L'indice è richiesto per le ricerche e le mappe \n" "e per generare le gallerie in modo rapido." #: f.tools.cc:126 f.widgets.cc:579 msgid "Index Image Files" msgstr "Gestione file indice" #: f.tools.cc:298 msgid "Choose top image directories" msgstr "Scegli la cartella principale delle immagini" #: f.tools.cc:299 msgid "Choose thumbnail directory" msgstr "Scegliere directory miniature" #: f.tools.cc:300 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" #: f.tools.cc:372 msgid "" "No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files." msgstr "" "Nessun indice d'immagini è stato trovato.\n" "Un indice verrà creato.\n" "Le immagini non saranno modificate.\n" "Può essere richiesto molto tempo se \n" "ci sono molte migliaia di immagini." #: f.tools.cc:378 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Directory non valida: \n" " %s \n" "Si prega di rimuoverla." #: f.tools.cc:381 #, c-format msgid "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" msgstr "" "La directory miniature: \n" " %s \n" "deve chiamarsi .../thumbnails" #: f.tools.cc:384 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Directory duplicata: \n" " %s \n" "Si prega di rimuoverla." #: f.tools.cc:443 msgid "specify 1 thumbnail directory" msgstr "" #: f.tools.cc:628 msgid "Top directories have no images" msgstr "Le directory principali non contengono immagini" #: f.tools.cc:1075 msgid "Cancel image index function?" msgstr "Interrompere la funzione di indicizzazione?" #: f.tools.cc:1195 msgid "Recent Files Gallery" msgstr "Galleria file recenti" #: f.tools.cc:1196 msgid "Newest Files Gallery" msgstr "Galleria delle immagini nuove" #: f.tools.cc:1197 msgid "Specific Gallery" msgstr "Galleria di cartella specifica" #: f.tools.cc:1198 msgid "Album Gallery" msgstr "Galleria di album specifico" #: f.tools.cc:1199 msgid "Previous Gallery" msgstr "Galleria precedente" #: f.tools.cc:1200 msgid "Previous Image File" msgstr "Ultima immagine aperta" #: f.tools.cc:1201 msgid "Specific Image File" msgstr "Immagine specifica" #: f.tools.cc:1202 f.widgets.cc:435 msgid "Blank Window" msgstr "Finestra vuota" #: f.tools.cc:1250 f.widgets.cc:580 msgid "User Settings" msgstr "Preferenze utente" #: f.tools.cc:1253 msgid "Startup Display" msgstr "Vista iniziale" #: f.tools.cc:1264 msgid "Background color:" msgstr "Colore di fondo per:" #: f.tools.cc:1265 msgid "F-View" msgstr "" #: f.tools.cc:1268 msgid "G-View" msgstr "" #: f.tools.cc:1272 msgid "Menu Text" msgstr "" #: f.tools.cc:1279 msgid "Menu Style" msgstr "Stile del menù" #: f.tools.cc:1280 msgid "Icons" msgstr "Icone" #: f.tools.cc:1281 msgid "Icons + Text" msgstr "Icone e testo" #: f.tools.cc:1283 msgid "Icon size" msgstr "Dimensione icone" #: f.tools.cc:1287 msgid "Dialog font" msgstr "" #: f.tools.cc:1292 msgid "Zoomed Image:" msgstr "Immagine ingrandita:" #: f.tools.cc:1301 msgid "JPEG save quality" msgstr "Qualità Jpeg" #: f.tools.cc:1305 msgid "Curve node capture distance" msgstr "Distanza di cattura dei nodi della curva" #: f.tools.cc:1309 msgid "Map marker size" msgstr "Dimensione del segnalino delle mappe" #: f.tools.cc:1313 msgid "show last file version only" msgstr "mostra solo l'ultima versione dei file" #: f.tools.cc:1316 msgid "shift image to right margin" msgstr "sposta immagine al margine destro" #: f.tools.cc:1319 f.tools.cc:1324 msgid "image index level" msgstr "Modo di indicizzazione" #: f.tools.cc:1321 msgid "Fotoxx started directly" msgstr "Fotoxx eseguito direttamente" #: f.tools.cc:1326 msgid "Fotoxx started by file manager" msgstr "Fotoxx invocato dal file manager" #: f.tools.cc:1329 msgid "RAW file types" msgstr "Tipi di file RAW" #: f.tools.cc:1333 msgid "video file types" msgstr "Tipi di file video" #: f.tools.cc:1440 msgid "Select startup directory" msgstr "Seleziona cartella d'avvio" #: f.tools.cc:1447 msgid "Select startup image file" msgstr "Seleziona file d'avvio" #: f.tools.cc:1454 msgid "Select startup album" msgstr "Seleziona album di avviamento" #: f.tools.cc:1497 msgid "startup directory is invalid" msgstr "Cartella iniziale non valida" #: f.tools.cc:1508 msgid "startup file is invalid" msgstr "File iniziale non valido" #: f.tools.cc:1683 f.widgets.cc:581 msgid "Keyboard Shortcuts" msgstr "Scorciatoie di tastiera" #: f.tools.cc:1690 msgid "Reserved Shortcuts \n" msgstr "" #: f.tools.cc:1691 msgid " F1 User Guide for current function \n" msgstr "" #: f.tools.cc:1692 msgid " F10 Full Screen with menus \n" msgstr "" #: f.tools.cc:1693 msgid " F11 Full Screen without menus \n" msgstr "" #: f.tools.cc:1694 msgid " Escape Quit dialog; Quit Fotoxx \n" msgstr "" #: f.tools.cc:1695 msgid " Ctrl+H Show hidden files in Gallery mode \n" msgstr "" #: f.tools.cc:1696 msgid " Delete Delete/Trash dialog \n" msgstr "" #: f.tools.cc:1697 msgid " Arrows Previous/Next Image or Gallery page \n" msgstr "" #: f.tools.cc:1757 msgid "Edit KB Shortcuts" msgstr "Gestisci le scorciatoie di tastiera" #: f.tools.cc:1766 msgid "shortcut key:" msgstr "Scorciatoia:" #: f.tools.cc:1767 msgid "(enter key)" msgstr "(premere il/i tasti)" #: f.tools.cc:1768 f.tools.cc:1911 f.tools.cc:2015 msgid "(no selection)" msgstr "" #: f.tools.cc:1905 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" è riservata - non si può usare" #: f.tools.cc:2036 msgid "unable to save KB-shortcuts file" msgstr "Non riesco a salvare le scorciatoie" #: f.tools.cc:2356 f.widgets.cc:583 msgid "Grid Lines" msgstr "Linee griglia" #: f.tools.cc:2365 msgid "x-spacing" msgstr "Passo X:" #: f.tools.cc:2366 msgid "x-count" msgstr "Num. barre X:" #: f.tools.cc:2367 msgid "x-enable" msgstr "Abilita X:" #: f.tools.cc:2373 msgid "y-spacing" msgstr "Passo Y:" #: f.tools.cc:2374 msgid "y-count" msgstr "Num. righe Y:" #: f.tools.cc:2375 msgid "y-enable" msgstr "Abilita Y:" #: f.tools.cc:2495 f.widgets.cc:584 msgid "Line Color" msgstr "Colore della linea" #: f.tools.cc:2560 msgid "Click image to select pixels." msgstr "Clicca l'immagine per selezionare i pixel" #: f.tools.cc:2595 f.widgets.cc:585 msgid "Show RGB" msgstr "Esplora componenti RGB" #: f.tools.cc:2890 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key X to toggle dialog." msgstr "" "Trascinare il mouse nell'immagine. \n" "Cliccare per annullare. \n" "Tasto X per mostrare/nascondere la finestra di dialogo" #: f.tools.cc:2918 f.widgets.cc:586 msgid "Magnify Image" msgstr "Lente d'ingrandimento" #: f.tools.cc:2927 msgid "X-size" msgstr "Ingrandimento" #: f.tools.cc:3172 msgid "Darkest and Brightest Pixels" msgstr "Cerca pixel scuri e chiari" #: f.tools.cc:3195 msgid "Dark Limit" msgstr "Più scuri di..." #: f.tools.cc:3196 msgid "Bright Limit" msgstr "Più chiari di..." #: f.tools.cc:3285 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "Le barre dovrebbero mostrare una gradualità\n" "in tutta la loro estensione." #: f.tools.cc:3376 f.widgets.cc:589 msgid "Monitor Gamma" msgstr "Gamma dello schermo" #: f.tools.cc:3443 msgid "Available Translations" msgstr "Traduzioni disponibili" #: f.tools.cc:3447 msgid "Set Language" msgstr "Cambia lingua" #: f.tools.cc:3573 msgid "Change Color Profile" msgstr "Cambia profilo di colore" #: f.tools.cc:3577 msgid "input profile" msgstr "Profilo in ingresso" #: f.tools.cc:3581 msgid "output profile" msgstr "Profilo in uscita" #: f.tools.cc:3602 msgid "Unable to change EXIF color profile" msgstr "Impossibile modificare il profilo di colore EXIF" #: f.tools.cc:3604 msgid "automatic new version created" msgstr "Versione nuova creata automaticamente" #: f.tools.cc:3613 msgid "color profile" msgstr "Profilo di colore" #: f.tools.cc:3661 f.tools.cc:3667 #, c-format msgid "unknown cms profile %s" msgstr "Profilo CMS sconosciuto %s" #: f.tools.cc:3770 f.widgets.cc:593 msgid "Calibrate Printer" msgstr "Calibrazione stampante" #: f.tools.cc:3795 msgid "print color chart" msgstr "Stampa l'immagine campione (mappa dei colori)" #: f.tools.cc:3796 msgid "scan and save color chart" msgstr "Acquisisci con lo scanner l'immagine stampata e salvala in un file" #: f.tools.cc:3797 msgid "align and trim color chart" msgstr "Raddrizza e ritaglia l'immagine acquisita" #: f.tools.cc:3798 msgid "open and process color chart" msgstr "Apri e processa l'immagine stampata, acquisita e regolata" #: f.tools.cc:3799 msgid "print image with revised colors" msgstr "Stampa la mappa dei colori con le correzioni attive" #: f.tools.cc:3940 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Acquisire da scanner il campione dei colori stampato. \n" "La linea più scura deve stare in alto. \n" "Salvare in %s/" #: f.tools.cc:3955 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Aprire ed editare l'immagine acquisita in precedenza. \n" "Raddrizzare pendenze o rotazioni dall'immagine acquisita. \n" "(Usare Correggi prospettiva). \n" "Ritagliare via accuratamente il bordo verde." #: f.tools.cc:3987 msgid "Open the trimmed color chart file" msgstr "Apri il file con l'immagine acquisita e ritagliata" #: f.tools.cc:4120 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" "Impostare il nome per il file di calibrazione \n" "[titolo di calibrazione].dat" #: f.tools.cc:4160 msgid "Color map file to use" msgstr "File della mappa colori da usare" #: f.tools.cc:4179 msgid "Select the image file to print." msgstr "Selezionare l'immagine da stampare." #: f.tools.cc:4244 msgid "converting colors..." msgstr "Conversione colori..." #: f.tools.cc:4344 msgid "Image colors are converted for printing." msgstr "Regolazione dei colori per la stampa." #: f.tools.cc:4467 msgid "This function is for AppImage package only" msgstr "Questa funzione è solo per i pacchetti AppImage" #: f.tools.cc:4471 msgid "" "Completely remove the AppImage package \n" "Press F1 for more information. \n" "Continue?" msgstr "" "Rimozione completa del pacchetto AppImage \n" "Premere F1 per maggiori informazioni. \n" "Proseguire?" #: f.warp.cc:94 f.widgets.cc:515 msgid "Unbend" msgstr "Correggi deformazioni" #: f.warp.cc:371 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Clicca gli angoli di un'area trapezoidale; poi clicca [Applica]. \n" " L'immagine si deformerà per trasformare il trapezio in un rettangolo." #: f.warp.cc:388 msgid "Perspective Correction" msgstr "Correzione di prospettiva" #: f.warp.cc:621 msgid "must have 4 corners" msgstr "Occorrono 4 angoli." #: f.warp.cc:745 msgid "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." msgstr "" "Seleziona un'area da deformare utilizzando la funzione del menu. \n" "Premi [Avvia deformazione] e trascina con il mouse.\n" "Trascina più volte se necessario.\n" "Quando terminato, seleziona un'altra zona o premi [Fatto]. " #: f.warp.cc:758 f.widgets.cc:517 msgid "Warp area" msgstr "Deforma area" #: f.warp.cc:763 msgid "start warp" msgstr "Avvia deformazione" #: f.warp.cc:1149 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" "Usare Selezione area per scegliere una faccia. \n" "Cliccare nel centro della distorsione. \n" "Regolare il cursore. \n" #: f.warp.cc:1176 f.widgets.cc:518 msgid "Unwarp Closeup" msgstr "Correzione del primo piano" #: f.warp.cc:1356 f.warp.cc:1671 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Trascina un punto dell'immagine con il mouse. \n" " Esegui diversi trascinamenti fino al completamento. \n" " Quando finito, premi [Fatto]." #: f.warp.cc:1374 f.widgets.cc:519 msgid "Warp curved" msgstr "Deforma a curva" #: f.warp.cc:1689 f.widgets.cc:520 msgid "Warp linear" msgstr "Deforma a linea" #: f.warp.cc:2005 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" "Trascina angoli o lati dell'immagine con il mouse. \n" "Ripeti l'operazione se richiesto.\n" "Una volta finito, premi [Fatto]." #: f.warp.cc:2021 f.widgets.cc:521 msgid "Warp affine" msgstr "Deforma specularmente" #: f.warp.cc:2368 f.widgets.cc:522 msgid "Flatten Book Page" msgstr "Appiattisci pagina" #: f.warp.cc:2369 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Ritaglia per isolare una pagina. \n" "Indica i lati in cima e in fondo con \n" "4 o più clic, poi appiattisci: " #: f.warp.cc:2372 msgid "Stretch curved-down surfaces:" msgstr "Deforma le superfici curve" #: f.warp.cc:2427 msgid "Top:" msgstr "Cima:" #: f.warp.cc:2430 msgid "Bottom:" msgstr "Fondo:" #: f.warp.cc:2811 f.widgets.cc:523 msgid "Spherical Projection" msgstr "Proiezione sferica" #: f.warp.cc:2855 msgid "Magnify" msgstr "Ingrandimento" #: f.warp.cc:3029 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" " Selezionare aree da lasciare invariate. \n" " Tirare l'immagine dall'angolo alto a sinistra. \n" " Quando finito, cliccare [Fatto]." #: f.warp.cc:3045 f.widgets.cc:524 msgid "Selective Rescale" msgstr "Scalatura selettiva" #: f.warp.cc:3068 msgid "select areas first" msgstr "Prima eseguire una selezione di aree" #: f.warp.cc:3290 f.widgets.cc:525 msgid "Make Waves" msgstr "Onde" #: f.warp.cc:3297 msgid "wavelength" msgstr "Lunghezza onda" #: f.warp.cc:3299 msgid "variance" msgstr "Varianza" #: f.warp.cc:3310 msgid "perspective" msgstr "Prospettiva" #: f.warp.cc:3484 f.warp.cc:3515 msgid "Twist" msgstr "Intreccia" #: f.warp.cc:3512 msgid "drag mouse to set center" msgstr "Trascinare il mouse per impostare il centro" #: f.widgets.cc:103 msgid "Album" msgstr "Album" #: f.widgets.cc:105 msgid "TOP" msgstr "CIMA" #: f.widgets.cc:171 msgid "Current Image File (key F)" msgstr "Immagine corrente (tasto F)" #: f.widgets.cc:172 msgid "Thumbnail Gallery (key G)" msgstr "Galleria miniature (tasto G)" #: f.widgets.cc:173 msgid "World Maps (key W)" msgstr "Mappa del mondo (tasto W)" #: f.widgets.cc:174 msgid "Net Maps (key M)" msgstr "Mappe da internet (tasto M)" #: f.widgets.cc:177 msgid "Favorite Functions" msgstr "Funzioni preferite" #: f.widgets.cc:178 msgid "File: Open, RAW, Rename, Delete, Print" msgstr "File: Apri, RAW, Rinomina, Elimina, Stampa" #: f.widgets.cc:179 msgid "Save modified image file to disk" msgstr "Salva su disco l'immagine modificata" #: f.widgets.cc:180 msgid "Open previous or next file (left/right mouse click)" msgstr "Aprire il file precedente o successivo (clic sinistro/destro)" #: f.widgets.cc:181 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadati: Titoli, Etichette, Punteggi, Geotags, Ricerca..." #: f.widgets.cc:182 msgid "Areas: Select areas to edit, copy and paste" msgstr "Aree: selezione aree per edit, copia e incolla" #: f.widgets.cc:183 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "Edit: Ritaglia, Ruota, Ridimensiona, Luminosità, Contrasto, Scritte..." #: f.widgets.cc:184 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Ripara: Nitidezza, Disturbo, Occhi rossi, Colore, Dipinto, Clona..." #: f.widgets.cc:185 msgid "Warp: Fix Perspective, Warp or unwarp image ..." msgstr "Deforma: corregge la prospettiva, deforma o corregge l'immagine..." #: f.widgets.cc:186 msgid "Effects: Special Effects, Arty Transforms" msgstr "Effetti: effetti speciali, trasformazioni artistiche" #: f.widgets.cc:187 msgid "" "left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "clic sx/dx: annulla/ripeti una modifica \n" " con tasto A: annulla/ripeti tutto\n" " clic centrale: va alla funzione precedente" #: f.widgets.cc:190 msgid "Tools: Index, Settings, Shortcuts, Magnify ..." msgstr "Strumenti: indice, preferenze, scorciatoie, lente ingrandente..." #: f.widgets.cc:191 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Aiuto: Guida rapida, Manuale utente, Ultime novità..." #: f.widgets.cc:194 msgid "Sync Gallery, Albums, Slide Show" msgstr "Aggiorna gallerie, album, slide show" #: f.widgets.cc:195 msgid "set and recall bookmarked image locations" msgstr "Imposta e richiama segnalibri di luoghi" #: f.widgets.cc:196 msgid "increase thumbnail size" msgstr "Aumenta la dimensione delle miniature" #: f.widgets.cc:197 msgid "reduce thumbnail size" msgstr "Riduci la dimensione delle miniature" #: f.widgets.cc:198 msgid "change sort order" msgstr "Cambia ordinamento" #: f.widgets.cc:199 msgid "jump to beginning (top)" msgstr "Salta alla cima" #: f.widgets.cc:200 msgid "jump to end (bottom)" msgstr "Salta al fondo" #: f.widgets.cc:201 msgid "previous page" msgstr "Pagina precedente" #: f.widgets.cc:202 msgid "next page" msgstr "Pagina successiva" #: f.widgets.cc:203 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combina: HDR, HDF, Panorama, Impila, Componi" #: f.widgets.cc:204 msgid "Process: convert, export, metadata, search ..." msgstr "Processa, converte, esporta, ricerca..." #: f.widgets.cc:207 msgid "Choose a map file" msgstr "Scegliere un file di mappa nel computer" #: f.widgets.cc:208 f.widgets.cc:212 msgid "Mark all images or current gallery" msgstr "Segna tutte le immagini di questa galleria" #: f.widgets.cc:211 msgid "Choose Internet map source" msgstr "Scegliere una mappa da internet" #: f.widgets.cc:213 msgid "Save and recall named map locations" msgstr "Salva e richiama luoghi della mappa" #: f.widgets.cc:216 msgid "Open another window" msgstr "Apri altra finestra" #: f.widgets.cc:217 msgid "Open a new image file" msgstr "Apri un ulteriore file" #: f.widgets.cc:218 f.widgets.cc:430 msgid "Cycle 2 Previous Files" msgstr "" #: f.widgets.cc:219 f.widgets.cc:431 msgid "Cycle 3 Previous Files" msgstr "" #: f.widgets.cc:220 msgid "Open a recently seen file" msgstr "Apri un file recente" #: f.widgets.cc:221 msgid "Open a newly added file" msgstr "Apri una immagine aggiunta" #: f.widgets.cc:222 msgid "Open and edit a camera RAW file" msgstr "Apri un file RAW" #: f.widgets.cc:223 msgid "View a 360 degree panorama image file" msgstr "" #: f.widgets.cc:224 msgid "Change the image file name" msgstr "Rinomina il file" #: f.widgets.cc:225 msgid "Copy or Move an image file to a new location" msgstr "Copia o sposta un file d'immagine in un'altra destinazione" #: f.widgets.cc:226 msgid "Copy an image file to the desktop" msgstr "Copia un'immagine sulla scrivania" #: f.widgets.cc:227 msgid "Copy image file to the clipboard" msgstr "" #: f.widgets.cc:228 msgid "Copy image file to the image cache" msgstr "" #: f.widgets.cc:229 msgid "Show location on Interet map" msgstr "" #: f.widgets.cc:230 msgid "Create a blank image" msgstr "Crea immagine nuova" #: f.widgets.cc:231 msgid "toggle - blank or restore window" msgstr "" #: f.widgets.cc:232 msgid "Delete or trash image file" msgstr "Elimina o cestina il file dell'immagine" #: f.widgets.cc:233 msgid "Print the current image" msgstr "Stampa l'immagine corrente" #: f.widgets.cc:234 msgid "Print current image with adjusted colors" msgstr "Stampa l'immagine corrente con i colori corretti" #: f.widgets.cc:235 f.widgets.cc:445 msgid "Quit Fotoxx" msgstr "Esci da Fotoxx" #: f.widgets.cc:238 msgid "List a few key metadata items" msgstr "Mostra alcuni metadati di base" #: f.widgets.cc:239 msgid "List all metadata items" msgstr "Mostra tutti i metadati" #: f.widgets.cc:240 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Gestisci etichette, geotag, titoli, punteggi..." #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Modifica qualsiasi metadato dell'immagine" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Elimina metadati dall'immagine selezionata" #: f.widgets.cc:243 msgid "(Toggle) show captions and comments" msgstr "Titoli e commenti sulla foto" #: f.widgets.cc:246 msgid "Select object or area for editing" msgstr "Seleziona oggetto o area da modificare" #: f.widgets.cc:247 msgid "Find a gap in an area outline" msgstr "Cerca interruzione nel contorno" #: f.widgets.cc:248 msgid "Select hairy or irregular edge" msgstr "Seleziona bordi irregolari/frastagliati" #: f.widgets.cc:249 msgid "Show (outline) existing area" msgstr "Mostra il contorno dell'area definita" #: f.widgets.cc:250 msgid "Hide existing area" msgstr "Nascondi l'area definita" #: f.widgets.cc:251 msgid "Enable area for editing" msgstr "Abilita area alle modifiche" #: f.widgets.cc:252 msgid "Disable area for editing" msgstr "Disabilita area per modifiche" #: f.widgets.cc:253 msgid "Reverse existing area" msgstr "Inverti l'area definita" #: f.widgets.cc:254 msgid "Erase existing area" msgstr "Cancella area" #: f.widgets.cc:255 msgid "Copy area for later pasting into image" msgstr "Copia area per incollarla poi in un'immagine" #: f.widgets.cc:256 msgid "Save area to a file with transparency" msgstr "Salva area in un file con trasparenza" #: f.widgets.cc:257 msgid "Open a file and paste as area into image" msgstr "Apre un file e incolla come area" #: f.widgets.cc:258 msgid "Paste previously copied area into image" msgstr "Incolla nell'immagine l'area precedentemente copiata" #: f.widgets.cc:261 msgid "Trim/Crop margins and/or Rotate" msgstr "Regola/ritaglia margini e/o ruota" #: f.widgets.cc:262 msgid "Upright a rotated image" msgstr "Raddrizza un'immagine (se ruotata)" #: f.widgets.cc:263 msgid "Change pixel dimensions" msgstr "Modifica le dimensioni in pixel" #: f.widgets.cc:264 f.widgets.cc:265 msgid "Fast auto enhance that may work OK" msgstr "Auto correzione che potrebbe anche funzionare bene..." #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Correggi luminosità contrasto e colore" #: f.widgets.cc:267 msgid "Edit brightness distribution" msgstr "Modifica la distribuzione di luminosità" #: f.widgets.cc:268 msgid "Magnify brightness gradients to enhance details" msgstr "" #: f.widgets.cc:269 msgid "Flatten brightness distribution" msgstr "" #: f.widgets.cc:270 msgid "Rescale brightness - reduce color caste and fog/haze" msgstr "" #: f.widgets.cc:271 msgid "Mirror image horizontally or vertically" msgstr "Rispecchia in orizzontale o verticale" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Dpingi sull'immagine col mouse" #: f.widgets.cc:273 msgid "Clone image pixels using the mouse" msgstr "Duplica pixel con il mouse" #: f.widgets.cc:274 msgid "Blend image pixels using the mouse" msgstr "Miscela i pixel con il mouse" #: f.widgets.cc:277 msgid "Paint edit function gradually with mouse" msgstr "Applica una funzione gradualmente col mouse" #: f.widgets.cc:278 msgid "Leverage edits by brightness or color" msgstr "Modula la funzione corrente secondo luminosità/colore" #: f.widgets.cc:279 msgid "Edit plugins menu or run a plugin function" msgstr "Modifica menu plugin o invoca una funzione" #: f.widgets.cc:282 msgid "Make the image look sharper" msgstr "Produci un'immagine più dettagliata" #: f.widgets.cc:283 msgid "Make the image look fuzzy" msgstr "Produci un'immagine più imprecisa" #: f.widgets.cc:284 msgid "Filter noise from low-light photos" msgstr "Elimina disturbi da foto con poca luce" #: f.widgets.cc:285 msgid "Fix red-eyes from electronic flash" msgstr "Correggi gli occhi rossi a causa del flash" #: f.widgets.cc:286 msgid "Make BW/color, negative/positive, sepia" msgstr "Trasforma in B/N o a colori, negativo, seppia..." #: f.widgets.cc:287 msgid "Reduce color depth (posterize)" msgstr "Riduce la risoluzione dei colori (poster)" #: f.widgets.cc:288 msgid "Shift/convert colors into other colors" msgstr "Regola i singoli componenti del colore" #: f.widgets.cc:289 msgid "Adjust color intensity (saturation)" msgstr "Regola l'intensità del colore (saturazione)" #: f.widgets.cc:290 msgid "Adjust color using RGB or CMY colors" msgstr "Regola colori usando RGB o CMY" #: f.widgets.cc:291 msgid "Adjust color using HSL colors" msgstr "Regola colori usando HSL" #: f.widgets.cc:292 msgid "Adjust color in selected image areas" msgstr "Regola il colore in aree selezionate" #: f.widgets.cc:293 msgid "Match colors on one image with another" msgstr "Allinea il colore di un'immagine con un'altra" #: f.widgets.cc:294 msgid "Reduce Chromatic Abberation" msgstr "Corregge le barre colorate estranee alla foto" #: f.widgets.cc:295 msgid "Remove unwanted objects" msgstr "Elimina oggetti non voluti" #: f.widgets.cc:296 msgid "Add a brightness/color ramp across the image" msgstr "" #: f.widgets.cc:297 msgid "Remove dust spots from scanned slides" msgstr "Elimina le macchie di polvere da diapositive scansionate" #: f.widgets.cc:298 msgid "Smoothen edges with jaggies" msgstr "Sfuoca bordi sgranati" #: f.widgets.cc:299 msgid "Erase known hot and dark pixels" msgstr "" "Cancella pixel difettosi della fotocamera, detti Hot-caldi o Dark-scuri " #: f.widgets.cc:300 msgid "Paint image transparency using the mouse" msgstr "Pennella la trasparenza con il mouse" #: f.widgets.cc:301 msgid "Add image transparency based on image attributes" msgstr "Aggiunge trasparenze basate su caratteristiche dell'immagine" #: f.widgets.cc:304 msgid "Remove curvature, esp. panoramas" msgstr "Elimina la curvatura, specialmente da panorama" #: f.widgets.cc:305 msgid "Straighten objects seen from an angle" msgstr "Raddrizza oggetti visti da un angolo" #: f.widgets.cc:306 msgid "Distort image areas using the mouse" msgstr "Distorci un'area usando il mouse" #: f.widgets.cc:307 msgid "Unwarp closeup face photo to remove distortion" msgstr "Rimuove la distorsione della foto di un viso in primo piano" #: f.widgets.cc:308 f.widgets.cc:309 f.widgets.cc:310 msgid "Distort the whole image using the mouse" msgstr "Distorci l'intera foto usando il mouse" #: f.widgets.cc:311 msgid "Flatten a photographed book page" msgstr "Appiattisce la foto della pagina di un libro" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "Produce una proiezione sferica di un'immagine" #: f.widgets.cc:313 msgid "Rescale image outside selected areas" msgstr "Scalatura escludendo parti selezionate dell'immagine" #: f.widgets.cc:314 msgid "Warp an image with a wave pattern" msgstr "Distorce immagine con motivo di onde" #: f.widgets.cc:315 msgid "Twist image centered at mouse position" msgstr "Intreccia l'immagine intorno alla posizione del mouse" #: f.widgets.cc:318 msgid "Convert to simulated sketch" msgstr "" #: f.widgets.cc:319 msgid "Convert image into a cartoon drawing" msgstr "Converte in disegno come un cartone animato" #: f.widgets.cc:320 msgid "Convert to line drawing (edge detection)" msgstr "Converte in disegno a linee (riconoscimento bordi)" #: f.widgets.cc:321 msgid "Convert to solid color drawing" msgstr "Converte in disegno con colori piatti" #: f.widgets.cc:322 msgid "Graduated Blur depending on contrast" msgstr "Sfumatura graduale basata sul contrasto" #: f.widgets.cc:323 msgid "Create an embossed or 3D appearance" msgstr "Crea un effetto scolpito o 3D" #: f.widgets.cc:324 msgid "Convert to square tiles" msgstr "Crea un effetto piastrellato" #: f.widgets.cc:325 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Crea un effetto puntinato (Roy Lichtenstein)" #: f.widgets.cc:326 msgid "Convert into a simulated painting" msgstr "Crea un effetto di dipinto a olio" #: f.widgets.cc:327 msgid "Change brightness or color radially" msgstr "Cambia luminosità o contrasto in modo radiale" #: f.widgets.cc:328 msgid "Add texture to an image" msgstr "Aggiungi un motivo (texture)" #: f.widgets.cc:329 msgid "Tile image with a repeating pattern" msgstr "Sovraimprime all'immagine un motivo ripetuto" #: f.widgets.cc:330 msgid "Create a mosaic with tiles made from all images" msgstr "Crea un mosaico con tessere da tutte le immagini" #: f.widgets.cc:331 msgid "Process an image using a custom kernel" msgstr "Elabora un'immagine applicando una matrice utente" #: f.widgets.cc:332 msgid "Blur an image in the direction of mouse drags" msgstr "Sfuma un'immagine in direzione dei trascinamenti col mouse" #: f.widgets.cc:333 msgid "Increasing blur with distance from selected areas" msgstr "Accentua la sfumatura con la distanza dalle aree selezionate" #: f.widgets.cc:334 msgid "Change color hue using an algorithm" msgstr "Cambia i colori tramite un algoritmo" #: f.widgets.cc:337 msgid "Combine bright/dark images for better detail" msgstr "Combina immagini chiare/scure per guadagnare in dettaglio" #: f.widgets.cc:338 msgid "Combine near/far focus images for deeper focus" msgstr "Combina immagini con profondità diverse per ampliare la messa a fuoco" #: f.widgets.cc:339 msgid "Combine images to erase passing people, etc." msgstr "Combina immagini per eliminare oggetti (es. passanti, automobili...)" #: f.widgets.cc:340 msgid "Combine noisy images into a low-noise image" msgstr "Combina immagini disturbate in una con minor disturbo" #: f.widgets.cc:341 msgid "Combine images into a panorama" msgstr "Combina immagini lato a lato per creare un panorama" #: f.widgets.cc:342 msgid "Combine images into a vertical panorama" msgstr "Combina immagini in un panorama verticale" #: f.widgets.cc:343 msgid "Combine images into a panorama (panorama tools)" msgstr "Combina immagini lato a lato per creare un panorama (panorama tools)" #: f.widgets.cc:344 msgid "Arrange images and text in a layout (montage)" msgstr "Dispone immagini e testi in un collage" #: f.widgets.cc:345 msgid "Combine images into a montage of images" msgstr "Combina diverse immagini in una esposizione" #: f.widgets.cc:348 msgid "Rename/convert/resize/move multiple files" msgstr "Rinomina/converte/ridimensiona/sposta file multipli" #: f.widgets.cc:349 msgid "Upright multiple rotated image files" msgstr "Raddrizza immagini in massa" #: f.widgets.cc:350 msgid "Delete or Trash multiple files" msgstr "Elimina o cestina più immagini" #: f.widgets.cc:351 msgid "Convert camera RAW files using libraw or Raw Therapee" msgstr "" #: f.widgets.cc:352 msgid "Build and run edit script files" msgstr "Compone ed esegue file script" #: f.widgets.cc:353 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Registra immagini selezionate su DVD/Blueray" #: f.widgets.cc:354 msgid "Search all image files and report duplicates" msgstr "Analizza tutte le immagini e segnala i duplicati" #: f.widgets.cc:356 msgid "Export selected image files to a directory" msgstr "Esporta in una cartella le immagini selezionate" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Aggiungi/togli etichette in massa" #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Converte nomi etichette per tutte le immagini" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Aggiungi/cambia/elimina metadati in diverse immagini " #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Elenca i metadati di immagini multiple" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Aggiungi/modifica geotag per diverse immagini" #: f.widgets.cc:363 msgid "Find all images for a location [date]" msgstr "Cerca tutte le immagini di un luogo/data" #: f.widgets.cc:364 msgid "Show image counts by month, select and report" msgstr "Mostra conteggi per mese, seleziona e riepiloga" #: f.widgets.cc:365 msgid "Find images meeting select criteria" msgstr "Cerca immagini con determinati criteri" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Aggiorna indici e miniature di nuovi file" #: f.widgets.cc:369 msgid "Change user preferences" msgstr "Modifica le preferenze" #: f.widgets.cc:370 msgid "Change Keyboard Shortcut Keys" msgstr "Modifica le scorciatoie da tastiera" #: f.widgets.cc:371 msgid "Show a brightness distribution graph" msgstr "Mostra il grafico di distribuzione della luminosità" #: f.widgets.cc:372 msgid "Show or revise grid lines" msgstr "Mostra o cambia la griglia" #: f.widgets.cc:373 msgid "Change color of foreground lines" msgstr "Cambia colore delle linee in primo piano" #: f.widgets.cc:374 msgid "Show RGB colors at mouse click" msgstr "Mostra i componenti colore RGB al clic" #: f.widgets.cc:375 msgid "Magnify image around the mouse position" msgstr "Ingrandisce temporaneamente una porzione d'immagine" #: f.widgets.cc:376 msgid "Highlight darkest and brightest pixels" msgstr "Individua i pixel di luminosità estrema" #: f.widgets.cc:377 msgid "Chart to adjust monitor color" msgstr "Grafico per regolare il colore dello schermo" #: f.widgets.cc:378 msgid "Chart to adjust monitor gamma" msgstr "Grafico per regolare la gamma dello schermo" #: f.widgets.cc:379 msgid "Change the GUI language" msgstr "Cambia la lingua dell'interfaccia" #: f.widgets.cc:380 msgid "Report missing translations" msgstr "Segnala traduzioni mancanti" #: f.widgets.cc:381 msgid "Convert to another color profile" msgstr "Converte a un altro profilo di colore" #: f.widgets.cc:382 msgid "Calibrate printer colors" msgstr "" "Esegue una procedura per regolare la stampante e migliorare la resa dei " "colori" #: f.widgets.cc:383 msgid "Completely uninstall AppImage package" msgstr "Disinstalla completamente un pacchetto AppImage" #: f.widgets.cc:384 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memoria e CPU (al terminale/giornale)" #: f.widgets.cc:387 msgid "Quick Start mini-guide" msgstr "Miniguida rapida" #: f.widgets.cc:388 msgid "Read the user guide" msgstr "Visualizza la guida utente" #: f.widgets.cc:389 msgid "Recent user guide changes" msgstr "Visualizza gli ultimi cambiamenti alla guida utente" #: f.widgets.cc:390 msgid "Technical installation notes" msgstr "Mostra note tecniche aggiuntive su Fotoxx" #: f.widgets.cc:391 msgid "List updates by Fotoxx version" msgstr "Mostra la storia delle versioni e loro contenuti" #: f.widgets.cc:392 msgid "View the log file and error messages" msgstr "Mostra il file di log: i messaggi generati durante l'avviamento" #: f.widgets.cc:393 msgid "How to do Fotoxx translations" msgstr "Mostra come fare la traduzione in altre lingue" #: f.widgets.cc:394 msgid "Show the Fotoxx web page" msgstr "Mostra la home page internet di fotoxx" #: f.widgets.cc:395 msgid "Version, license, contact, credits" msgstr "Versione, licenza, contatti, riconoscimenti" #: f.widgets.cc:398 msgid "list all directories, click any for gallery view" msgstr "Lista cartelle: cliccare su una per vederne la galleria" #: f.widgets.cc:399 msgid "Set gallery from current image file" msgstr "Apre la galleria dell'immagine corrente" #: f.widgets.cc:400 msgid "Organize images into albums" msgstr "Organizza immagini in album" #: f.widgets.cc:401 msgid "Update album files to latest version" msgstr "Aggiorna gli album all'ultima versione" #: f.widgets.cc:402 msgid "Replace album file with another file" msgstr "Rimpiazza l'album con un altro file" #: f.widgets.cc:403 msgid "Start a slide show" msgstr "Inizia uno slideshow " #: f.widgets.cc:425 msgid "New Window" msgstr "Nuova finestra" #: f.widgets.cc:426 f.widgets.cc:610 fotoxx-18.01.cc:1821 msgid "Sync Gallery" msgstr "Sincronizza galleria" #: f.widgets.cc:427 msgid "Recently Seen Images" msgstr "Immagini viste di recente" #: f.widgets.cc:428 msgid "Newest Images" msgstr "Immagini più nuove" #: f.widgets.cc:433 msgid "View 360° Panorama" msgstr "" #: f.widgets.cc:434 msgid "New Blank Image" msgstr "Nuova immagine" #: f.widgets.cc:437 f.widgets.cc:758 msgid "Copy/Move to Location" msgstr "Copia/sposta altrove" #: f.widgets.cc:438 f.widgets.cc:759 msgid "Copy to Desktop" msgstr "Copia sulla scrivania" #: f.widgets.cc:439 f.widgets.cc:760 msgid "Copy to Clipboard" msgstr "Copia negli appunti" #: f.widgets.cc:440 f.widgets.cc:764 msgid "Copy to Image Cache" msgstr "Copia nel deposito immagini" #: f.widgets.cc:441 f.widgets.cc:778 msgid "Show on Net Map" msgstr "Mostra nella mappa internet" #: f.widgets.cc:443 msgid "Print Image" msgstr "Stampa immagine" #: f.widgets.cc:444 msgid "Print Calibrated Image" msgstr "Stampa immagine calibrata" #: f.widgets.cc:448 msgid "View Metadata (short)" msgstr "Vedi metadati (breve)" #: f.widgets.cc:449 msgid "View Metadata (long)" msgstr "Vedi metadati (completo)" #: f.widgets.cc:453 msgid "Show Captions on Image" msgstr "Mostra/nasconde titoli e commenti in sovrimpressione" #: f.widgets.cc:456 f.widgets.cc:776 msgid "Select Area" msgstr "Seleziona area" #: f.widgets.cc:457 msgid "Find Area Gap" msgstr "" #: f.widgets.cc:459 msgid "Show Area" msgstr "" #: f.widgets.cc:460 msgid "Hide Area" msgstr "" #: f.widgets.cc:461 msgid "Enable Area" msgstr "" #: f.widgets.cc:462 msgid "Disable Area" msgstr "" #: f.widgets.cc:463 msgid "Invert Area" msgstr "" #: f.widgets.cc:464 msgid "Unselect Area" msgstr "" #: f.widgets.cc:466 msgid "Paste Area" msgstr "Incolla area" #: f.widgets.cc:467 msgid "Open Area File" msgstr "Apri file di area" #: f.widgets.cc:474 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:475 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:477 f.widgets.cc:773 msgid "Edit Brightness" msgstr "Distribuisci luminosità" #: f.widgets.cc:478 f.widgets.cc:775 msgid "Gradients" msgstr "" #: f.widgets.cc:480 msgid "Retinex" msgstr "" #: f.widgets.cc:482 msgid "Paint Image" msgstr "Dipingi" #: f.widgets.cc:489 msgid "Plugins" msgstr "Plugin" #: f.widgets.cc:494 msgid "Denoise" msgstr "Riduci disturbo" #: f.widgets.cc:495 msgid "Red Eyes" msgstr "Rimuovi occhi rossi" #: f.widgets.cc:497 msgid "Color Depth" msgstr "Risoluzione del colore" #: f.widgets.cc:500 msgid "Adjust RGB/CMY" msgstr "Regola RGB/CMY" #: f.widgets.cc:501 msgid "Adjust HSL" msgstr "Regola HSL" #: f.widgets.cc:502 msgid "Zonal Colors" msgstr "" #: f.widgets.cc:503 msgid "Match Colors" msgstr "Allinea colori" #: f.widgets.cc:509 msgid "Anti-Alias" msgstr "Anti-Alias" #: f.widgets.cc:516 msgid "Fix Perspective" msgstr "Correggi prospettiva" #: f.widgets.cc:526 msgid "Twist Image" msgstr "Intreccia" #: f.widgets.cc:529 msgid "Sketch" msgstr "Schizzo" #: f.widgets.cc:530 msgid "Cartoon" msgstr "Cartone animato" #: f.widgets.cc:534 msgid "Embossing" msgstr "Bassorilievo" #: f.widgets.cc:536 msgid "Dots" msgstr "Puntinismo" #: f.widgets.cc:537 msgid "Painting" msgstr "Dipinto" #: f.widgets.cc:539 msgid "Texture" msgstr "Aggiungi disturbo" #: f.widgets.cc:541 msgid "Mosaic" msgstr "Mosaico" #: f.widgets.cc:548 msgid "High Dynamic Range" msgstr "Combina immagini (alta dinamica - HDR)" #: f.widgets.cc:549 msgid "High Depth of Field" msgstr "Combina immagini (grande profondità - HDF)" #: f.widgets.cc:550 msgid "Stack/Paint" msgstr "Sovrapponi (interattivo con mouse)" #: f.widgets.cc:551 msgid "Stack/Noise" msgstr "Sovrapponi (rimozione disturbo)" #: f.widgets.cc:552 msgid "Panorama" msgstr "Crea un panorama" #: f.widgets.cc:553 msgid "Vertical Panorama" msgstr "Panorama verticale" #: f.widgets.cc:554 msgid "PT Panorama" msgstr "Panorama (usa pano tools)" #: f.widgets.cc:555 msgid "Montage" msgstr "Crea esposizione" #: f.widgets.cc:556 msgid "Mashup" msgstr "Collage" #: f.widgets.cc:562 msgid "Batch RAW" msgstr "Conversione RAW in massa" #: f.widgets.cc:563 msgid "Script Files" msgstr "File script" #: f.widgets.cc:566 msgid "Export File List" msgstr "Esporta lista di file" #: f.widgets.cc:574 msgid "Image Locations/Dates" msgstr "Luoghi e date delle immagini" #: f.widgets.cc:575 msgid "Image Timeline" msgstr "Cronistoria delle immagini" #: f.widgets.cc:576 msgid "Search Images" msgstr "Ricerca immagini" #: f.widgets.cc:582 msgid "Brightness Graph" msgstr "Grafico delle distribuzione della luminosità" #: f.widgets.cc:587 msgid "Dark/Bright Pixels" msgstr "Punti chiari/scuri" #: f.widgets.cc:588 msgid "Monitor Color" msgstr "Colore monitor" #: f.widgets.cc:590 msgid "Change Language" msgstr "Cambia lingua del programma" #: f.widgets.cc:591 msgid "Missing Translations" msgstr "Traduzioni mancanti" #: f.widgets.cc:592 msgid "Color Profile" msgstr "Profilo di colore" #: f.widgets.cc:594 msgid "Uninstall AppImage" msgstr "Disinstalla pacchetto AppImage" #: f.widgets.cc:611 msgid "All Directories" msgstr "Tutte le cartelle" #: f.widgets.cc:612 msgid "Bookmarks" msgstr "Segnalibri" #: f.widgets.cc:621 f.widgets.cc:646 f.widgets.cc:670 f.widgets.cc:683 msgid "Gallery" msgstr "Galleria" #: f.widgets.cc:622 f.widgets.cc:647 f.widgets.cc:671 f.widgets.cc:684 msgid "World Maps" msgstr "Mappe del mondo" #: f.widgets.cc:623 f.widgets.cc:648 f.widgets.cc:672 f.widgets.cc:685 msgid "Net Maps" msgstr "Mappe internet" #: f.widgets.cc:625 f.widgets.cc:650 f.widgets.cc:1066 msgid "Favorites" msgstr "Preferiti" #: f.widgets.cc:627 msgid "Save File" msgstr "" #: f.widgets.cc:628 msgid "Prev/Next" msgstr "Prec./Pross." #: f.widgets.cc:630 msgid "Areas" msgstr "Aree" #: f.widgets.cc:631 msgid "Undo/Redo" msgstr "Annulla/ripeti" #: f.widgets.cc:632 fotoxx.h:1208 msgid "Edit" msgstr "Modifica" #: f.widgets.cc:633 msgid "Repair" msgstr "Correggi" #: f.widgets.cc:635 msgid "Effects" msgstr "Effetti" #: f.widgets.cc:636 f.widgets.cc:660 msgid "Combine" msgstr "Combina" #: f.widgets.cc:637 f.widgets.cc:661 msgid "Process" msgstr "Elaborazioni in massa" #: f.widgets.cc:638 f.widgets.cc:662 msgid "Tools" msgstr "Strumenti" #: f.widgets.cc:674 msgid "Choose Map" msgstr "Scegliere mappa" #: f.widgets.cc:675 f.widgets.cc:688 msgid "Map Markers" msgstr "Segnalini della mappa" #: f.widgets.cc:687 msgid "Map Source" msgstr "Fornitore della mappa" #: f.widgets.cc:689 msgid "Map Locations" msgstr "Luoghi sulla mappa" #: f.widgets.cc:753 msgid "Popup Image" msgstr "Comparsa immagine" #: f.widgets.cc:757 fotoxx.h:1265 msgid "Rename" msgstr "Rinomina" #: f.widgets.cc:761 msgid "Remove from Album" msgstr "Rimuovere dall'album" #: f.widgets.cc:763 msgid "Cut to Image Cache" msgstr "Taglia verso verso il deposito immagini" #: f.widgets.cc:765 msgid "Paste Image Cache Here (clear)" msgstr "Incolla il deposito immagini qui (svuota)" #: f.widgets.cc:766 msgid "Paste Image Cache Here (keep)" msgstr "Incolla il deposito immagini qui (mantiene)" #: f.widgets.cc:779 msgid "Delete/Trash ..." msgstr "Elimina/cestina ..." #: f.widgets.cc:1092 msgid "menu name is invalid" msgstr "" #: fotoxx-18.01.cc:434 msgid "Please install missing programs:" msgstr "Installa i programmi mancanti:" #: fotoxx-18.01.cc:706 #, c-format msgid "Kill active dialog? %s" msgstr "" #: fotoxx-18.01.cc:801 msgid "(reduced)" msgstr "(ridotto)" #: fotoxx-18.01.cc:802 msgid "area active" msgstr "area attiva" #: fotoxx-18.01.cc:803 msgid "dialog open" msgstr "dialogo aperto" #: fotoxx-18.01.cc:804 msgid "blocked" msgstr "bloccato" #: fotoxx-18.01.cc:866 msgid "edits" msgstr "modifiche" #: fotoxx-18.01.cc:1801 msgid "File View" msgstr "Vista di un file" #: fotoxx-18.01.cc:1806 msgid "Gallery View" msgstr "Galleria" #: fotoxx-18.01.cc:1811 msgid "World Map View" msgstr "" #: fotoxx-18.01.cc:1816 msgid "Net Map View" msgstr "" #: fotoxx-18.01.cc:1831 msgid "Show Hidden" msgstr "" #: fotoxx-18.01.cc:3023 msgid "Exceed 50 anchor points" msgstr "Superati 50 punti di ancoraggio" #: fotoxx-18.01.cc:3250 msgid "load curve from a file" msgstr "carica curva da file" #: fotoxx-18.01.cc:3316 msgid "curve file is invalid" msgstr "File della curva invalido" #: fotoxx-18.01.cc:3330 msgid "save curve to a file" msgstr "salva curva in un file" #: fotoxx-18.01.cc:3399 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "Il file non può essere editato \n" " %s" #: fotoxx-18.01.cc:3412 msgid "Too many edits, please save image" msgstr "Troppe modifiche: salva l'immagine" #: fotoxx-18.01.cc:3417 msgid "this function cannot be scripted" msgstr "Questa funzione non può essere usata in uno script" #: fotoxx-18.01.cc:3434 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Selezione di area non attiva.\n" "Continuare?" #: fotoxx-18.01.cc:3749 msgid "file data does not fit dialog" msgstr "I dati del file non si adattano alla finestra" #: fotoxx-18.01.cc:3759 msgid "Save settings to a file" msgstr "Salva impostazioni in un file" #: fotoxx-18.01.cc:4140 msgid "This action will discard changes to current image" msgstr "Questa azione annullerà le modifiche all'immagine" #: fotoxx-18.01.cc:4141 msgid "prior function still active" msgstr "Funzione precedente ancora attiva" #: fotoxx-18.01.cc:4142 fotoxx.h:1229 msgid "Keep" msgstr "Mantieni modifiche" #: fotoxx-18.01.cc:4143 msgid "Discard" msgstr "Perdi modifiche" #: fotoxx.h:1177 msgid "Add" msgstr "Aggiungi" #: fotoxx.h:1178 msgid "Add All" msgstr "Aggiungi tutto" #: fotoxx.h:1180 msgid "Amount" msgstr "Quantità" #: fotoxx.h:1181 msgid "Angle" msgstr "Angolo:" #: fotoxx.h:1182 zfuncs.cc:6782 msgid "Apply" msgstr "Applica" #: fotoxx.h:1183 msgid "Auto" msgstr "Auto" #: fotoxx.h:1184 msgid "Black" msgstr "Nero" #: fotoxx.h:1185 msgid "Blend Width" msgstr "Larghezza di sfumatura" #: fotoxx.h:1187 zfuncs.cc:11368 msgid "Bottom" msgstr "Basso" #: fotoxx.h:1189 zfuncs.cc:6796 msgid "Browse" msgstr "Sfoglia" #: fotoxx.h:1190 zfuncs.cc:6782 msgid "Cancel" msgstr "Annulla" #: fotoxx.h:1191 msgid "Center" msgstr "centro" #: fotoxx.h:1192 msgid "Choose" msgstr "Scegli" #: fotoxx.h:1193 msgid "Clear" msgstr "Pulisci" #: fotoxx.h:1194 msgid "Close" msgstr "Chiudi" #: fotoxx.h:1195 msgid "Color" msgstr "Colore" #: fotoxx.h:1196 msgid "COMPLETED" msgstr "COMPLETATO" #: fotoxx.h:1197 msgid "continue" msgstr "continua" #: fotoxx.h:1199 msgid "Copy" msgstr "Copia" #: fotoxx.h:1200 msgid "Create" msgstr "Creare" #: fotoxx.h:1201 msgid "Curve File:" msgstr "File di curve:" #: fotoxx.h:1202 msgid "Cut" msgstr "Taglia" #: fotoxx.h:1203 msgid "Deband" msgstr "Elimina bande" #: fotoxx.h:1204 zfuncs.cc:6782 msgid "Delete" msgstr "Elimina" #: fotoxx.h:1205 msgid "Disable" msgstr "Disabilita" #: fotoxx.h:1206 msgid "Done" msgstr "Fatto" #: fotoxx.h:1207 msgid "edge" msgstr "bordo:" #: fotoxx.h:1209 msgid "Enable" msgstr "Abilita" #: fotoxx.h:1210 msgid "Erase" msgstr "Esegui cancellatura" #: fotoxx.h:1211 msgid "Fetch" msgstr "Prendi" #: fotoxx.h:1212 msgid "output file already exists" msgstr "Il file di uscita esiste già" #: fotoxx.h:1213 msgid "file not found" msgstr "File non trovato" #: fotoxx.h:1214 #, c-format msgid "file not found: %s" msgstr "file non trovato: %s" #: fotoxx.h:1215 #, c-format msgid "%d image files selected" msgstr "selezionate %d file d'immagine" #: fotoxx.h:1216 msgid "Find" msgstr "Cerca" #: fotoxx.h:1217 msgid "Finish" msgstr "Termina" #: fotoxx.h:1219 msgid "Font" msgstr "Font" #: fotoxx.h:1221 msgid "Grid" msgstr "Griglia" #: fotoxx.h:1222 zfuncs.cc:11398 msgid "Height" msgstr "Altezza" #: fotoxx.h:1224 msgid "Hide" msgstr "Nascondi" #: fotoxx.h:1225 zfuncs.cc:11390 msgid "Image" msgstr "Immagine" #: fotoxx.h:1226 msgid "Images" msgstr "Immagini" #: fotoxx.h:1227 msgid "Insert" msgstr "Incolla" #: fotoxx.h:1228 msgid "Invert" msgstr "Inverti" #: fotoxx.h:1230 zfuncs.cc:11372 msgid "Left" msgstr "Sinistra" #: fotoxx.h:1231 msgid "limit" msgstr "limite" #: fotoxx.h:1232 msgid "Load" msgstr "Carica" #: fotoxx.h:1233 msgid "Make" msgstr "Produci" #: fotoxx.h:1235 msgid "Map" msgstr "Mappa" #: fotoxx.h:1236 msgid "Match Level:" msgstr "Corrispondenza:" #: fotoxx.h:1237 msgid "Max" msgstr "Massimo" #: fotoxx.h:1238 msgid "mouse radius" msgstr "Raggio del mouse" #: fotoxx.h:1239 msgid "Negative" msgstr "Negativo" #: fotoxx.h:1240 msgid "New" msgstr "Nuovo" #: fotoxx.h:1241 msgid "Next" msgstr "Prossima" #: fotoxx.h:1242 zfuncs.cc:10365 msgid "No" msgstr "No" #: fotoxx.h:1243 msgid "no images" msgstr "nessuna immagine" #: fotoxx.h:1244 msgid "" "image index disabled \n" " Enable?" msgstr "" "l'indicizzazione è disabilitata \n" " abilitarla?" #: fotoxx.h:1245 msgid "no image files selected" msgstr "nessun file d'immagine selezionato" #: fotoxx.h:1246 msgid "None" msgstr "Nessuna" #: fotoxx.h:1247 msgid "no selection" msgstr "nessuna selezione" #: fotoxx.h:1248 msgid "OK" msgstr "Ok" #: fotoxx.h:1249 msgid "image index not updated" msgstr "l'indice delle immagini non è aggiornato" #: fotoxx.h:1251 msgid "Paint Radius" msgstr "Raggio del pennello" #: fotoxx.h:1252 msgid "Paste" msgstr "Incolla" #: fotoxx.h:1253 msgid "Pause" msgstr "Sospendi" #: fotoxx.h:1254 msgid "Percent" msgstr "Percentuale" #: fotoxx.h:1256 msgid "Presets" msgstr "Fisse: " #: fotoxx.h:1257 msgid "Prev" msgstr "Preced." #: fotoxx.h:1258 msgid "Proceed" msgstr "Prosegui" #: fotoxx.h:1260 msgid "range" msgstr "intervallo" #: fotoxx.h:1262 msgid "Redo" msgstr "Ripeti" #: fotoxx.h:1263 msgid "Reduce" msgstr "Riduci" #: fotoxx.h:1264 msgid "Remove" msgstr "Elimina" #: fotoxx.h:1266 msgid "Replace" msgstr "Rimpiazza" #: fotoxx.h:1267 msgid "Reserved" msgstr "Riservato" #: fotoxx.h:1268 msgid "Reset" msgstr "Azzera" #: fotoxx.h:1269 zfuncs.cc:11376 msgid "Right" msgstr "Destra" #: fotoxx.h:1270 msgid "Rotate" msgstr "Ruota" #: fotoxx.h:1271 msgid "Run" msgstr "Avvia" #: fotoxx.h:1272 msgid "Save" msgstr "Salva" #: fotoxx.h:1273 msgid "Search" msgstr "Ricerca" #: fotoxx.h:1274 msgid "Seconds" msgstr "Secondi" #: fotoxx.h:1275 msgid "Select" msgstr "Seleziona" #: fotoxx.h:1277 msgid "Show" msgstr "Visualizza" #: fotoxx.h:1278 msgid "Size" msgstr "Dimensione" #: fotoxx.h:1279 msgid "Start" msgstr "Inizio" #: fotoxx.h:1280 msgid "Stop" msgstr "Arresta" #: fotoxx.h:1281 msgid "Strength" msgstr "Intensità" #: fotoxx.h:1282 msgid "Threshold" msgstr "Soglia" #: fotoxx.h:1283 #, c-format msgid "exceed %d files" msgstr "più di %d file" #: fotoxx.h:1284 zfuncs.cc:11364 msgid "Top" msgstr "Alto" #: fotoxx.h:1286 msgid "Trash" msgstr "Cestina" #: fotoxx.h:1287 msgid "Trim" msgstr "Ritaglia" #: fotoxx.h:1288 msgid "Undo All" msgstr "Annulla tutto" #: fotoxx.h:1289 msgid "Undo Last" msgstr "Annulla ultimo" #: fotoxx.h:1290 msgid "Undo" msgstr "Annulla" #: fotoxx.h:1291 msgid "Unfinish" msgstr "Ricomincia" #: fotoxx.h:1292 msgid "Unselect" msgstr "Deseleziona" #: fotoxx.h:1293 msgid "View" msgstr "Vista" #: fotoxx.h:1294 msgid "Web" msgstr "Web" #: fotoxx.h:1295 msgid "White" msgstr "Bianco" #: fotoxx.h:1296 zfuncs.cc:11394 msgid "Width" msgstr "Larghezza" #: fotoxx.h:1297 msgid "x-offset" msgstr "Origine X:" #: fotoxx.h:1298 msgid "y-offset" msgstr "Origine Y:" #: fotoxx.h:1299 zfuncs.cc:10365 msgid "Yes" msgstr "Si" #: zfuncs.cc:305 msgid "LOW MEMORY - performance may be poor" msgstr "Poca memoria RAM - le prestazioni potrebbero essere scarse" #: zfuncs.cc:1722 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "creare directory? \n" " %s" #: zfuncs.cc:4878 msgid "user guide not found" msgstr "Guida utente non trovata" #: zfuncs.cc:5742 #, c-format msgid "cannot open file %s" msgstr "non riesco ad aprire il file %s" #: zfuncs.cc:5765 msgid "save text to file" msgstr "" #: zfuncs.cc:6786 msgid "menu text" msgstr "Nome nel menù" #: zfuncs.cc:6787 msgid "menu func" msgstr "Funzione" #: zfuncs.cc:6788 msgid "menu icon" msgstr "Icona" #: zfuncs.cc:6789 msgid "icon size" msgstr "Dimensione icona" #: zfuncs.cc:6792 msgid "Bold" msgstr "Grassetto" #: zfuncs.cc:6799 msgid "close window" msgstr "Chiudi finestra" #: zfuncs.cc:10488 zfuncs.cc:11021 zfuncs.cc:11351 msgid "cancel" msgstr "Annulla" #: zfuncs.cc:10982 msgid "choose file" msgstr "Scegli file" #: zfuncs.cc:10987 msgid "choose files" msgstr "Scegli i file" #: zfuncs.cc:10992 msgid "save" msgstr "Salva" #: zfuncs.cc:10998 msgid "choose folder" msgstr "Scegli cartella" #: zfuncs.cc:11003 msgid "choose folders" msgstr "Scegli cartelle" #: zfuncs.cc:11008 msgid "create folder" msgstr "Crea cartella" #: zfuncs.cc:11015 msgid "hidden" msgstr "Nascosto" #: zfuncs.cc:11351 msgid "done" msgstr "Fatto" #: zfuncs.cc:11381 msgid "image scale" msgstr "Riscala immagine" #: zfuncs.cc:11383 msgid "percent" msgstr "percento" #~ msgid "Select images to remove" #~ msgstr "Seleziona immagini da togliere" #~ msgid "remove %d album files?" #~ msgstr "Cancella %d file dell'album?" #~ msgid "KB functions" #~ msgstr "Tasti" #~ msgid "both" #~ msgstr "Entrambi" #~ msgid "Click for white balance or black level" #~ msgstr "Cliccare per bilanciamento del bianco/nero" #~ msgid "" #~ "check the box, then click on white or dark \n" #~ "area to calibrate image white or black color" #~ msgstr "" #~ "Seleziona la casella, poi clicca su chiaro o scuro \n" #~ "area per calibrare il bilanciamento del bianco o il livello di nero" #~ msgid "Zonal Flatten Brightness" #~ msgstr "Compressione luminosità a zone" #~ msgid "Tone Mapping" #~ msgstr "Mappa tonalità" #~ msgid "Flip" #~ msgstr "Rifletti/specchia" #~ msgid "Write Text on Image" #~ msgstr "Scrivi testo su immagine" #~ msgid "Write Line or Arrow on Image" #~ msgstr "Metti linea o freccia sull'immagine" #~ msgid "Row↑" #~ msgstr "Riga↑" #~ msgid "Row↓" #~ msgstr "Riga↓" #~ msgid "Page↑" #~ msgstr "Pagina↑" #~ msgid "Page↓" #~ msgstr "Pagina↓" #~ msgid "a sorted album cannot be edited" #~ msgstr "Un album ordinato non può essere manipolato" #~ msgid "Click list position. Click thumbnail to add." #~ msgstr "" #~ "Clicca la lista per indicare la posizione; clicca le miniature per " #~ "aggiungere." #~ msgid "(yyyymmdd)" #~ msgstr "(aaaammgg)" #~ msgid "Batch Convert RAW Files" #~ msgstr "Conversione in massa di file RAW" #~ msgid "Images:" #~ msgstr "Immagini:" #~ msgid "searching ..." #~ msgstr "Ricerca in corso..." #~ msgid "" #~ "Draw a line across the image in \n" #~ "direction of brightness gradient." #~ msgstr "" #~ "Tracciare una linea lungo l'immagine \n" #~ "in direzione della sfumatura voluta." #~ msgid "Brightness Gradient" #~ msgstr "Sfuma luminosità" #~ msgid "Select directory for thumbnails." #~ msgstr "Seleziona directory per miniature" #~ msgid "no thumbnails directory defined" #~ msgstr "nessuna directory miniature definita" #~ msgid "Dialog font and size" #~ msgstr "Impostazione font e dimensione" #~ msgid "Open the previously seen file" #~ msgstr "Apri il file precedente" #~ msgid "Add local contrast, enhance details" #~ msgstr "Aggiungi contrasto locale (più dettagli)" #~ msgid "Flatten zonal brightness distribution" #~ msgstr "Regola luminosità a zone" #~ msgid "Write text on image" #~ msgstr "Disegna scritte sull'immagine" #~ msgid "Write lines or arrows on image" #~ msgstr "Aggiunge linee o frecce sull'immagine" #~ msgid "Add a brightness/color gradient across the image" #~ msgstr "Aggiunge un gradiente di luminosità o colore lungo l'immagine" #~ msgid "Convert camera RAW files using libraw" #~ msgstr "Converte file RAW usando libraw" #~ msgid "Convert camera RAW files using Raw Therapee" #~ msgstr "Converte file RAW usando Raw Therapee" #~ msgid "Open Previous File" #~ msgstr "Apri l'immagine precedente" #~ msgid "Find Gap" #~ msgstr "Trova interruzione" #~ msgid "Zonal Flatten" #~ msgstr "Appiattimento a zone" #~ msgid "Add Lines/Arrows" #~ msgstr "Aggiungi linee/frecce" #~ msgid "Batch Raw Therapee" #~ msgstr "Conversione RAW in massa (Raw Therapee)" #~ msgid "Resources" #~ msgstr "Mostra risorse (nel terminale)" #~ msgid "Kill active dialog?" #~ msgstr "Terminare il dialogo corrente?" #~ msgid "save screen to file" #~ msgstr "Salva schermo in un file" #~ msgid "END (press Escape to exit)" #~ msgstr "FINE (premere Esc per uscire)" #~ msgid "Geocoding by MapQuest" #~ msgstr "Geocoding è di MapQuest" #~ msgid "Select Hairy Edge" #~ msgstr "Seleziona contorni frastagliati" #~ msgid "" #~ "Click on colors to select and deselect. \n" #~ "Set threshold levels for color matching. \n" #~ "Left drag to select or deselect pixels. \n" #~ "Right drag to restore original pixels. \n" #~ "When done, copy or save the finished area \n" #~ "for subsequent pasting into an image. \n" #~ msgstr "" #~ "Cliccare i colori per (de)selezionarli. \n" #~ "Impostarne le soglie di corrispondenza. \n" #~ "Trascinare col tasto sinistro per (de)selezionare i pixel. \n" #~ "Trascinare col destro per ripristinare i pixel originari. \n" #~ "Infine, copiare o salvare l'area finita \n" #~ "per poi incollarla dentro un'immagine. \n" #~ msgid "load area from a file" #~ msgstr "carica area da un file" #~ msgid "curved image" #~ msgstr "immagine curva" #~ msgid "choose pattern tile" #~ msgstr "Scelta tessera del motivo" #~ msgid "Divisor" #~ msgstr "Divisore" #~ msgid "Open RAW file (Raw Therapee)" #~ msgstr "Apri file RAW (Raw Therapee)" #~ msgid "Delete or Trash Image File" #~ msgstr "Elimina o cestina il file dell'immagine" #~ msgid "Edit Functions Summary" #~ msgstr "Sommario funzioni" #~ msgid "%d files uploaded" #~ msgstr "inviati %d file" #~ msgid "reverse upload sequence" #~ msgstr "Invia in ordine inverso" #~ msgid "Upload to Flickr" #~ msgstr "Invia a Flickr" #~ msgid "select random (if 2+ enabled)" #~ msgstr "casuale (se più di 2)" #~ msgid "enabled" #~ msgstr "Attiva" #~ msgid "slowdown" #~ msgstr "Rallenta" #~ msgid "preference" #~ msgstr "Preferenza" #~ msgid "choose layout file" #~ msgstr "Scegliere un file di disposizione" #~ msgid "Geocoding service by MapQuest" #~ msgstr "Servizio di codifica geo di MapQuest" #~ msgid "Batch Change Metadata" #~ msgstr "Cambio metadati in massa" #~ msgid "Select File" #~ msgstr "Seleziona file di tavolozza" #~ msgid "Previous Image" #~ msgstr "Immagine precedente" #~ msgid "Directory Gallery" #~ msgstr "Cartella iniziale della galleria" #~ msgid "Image Pan/scroll:" #~ msgstr "Scorrimento immagine a video:" #~ msgid "Zooms for 2x" #~ msgstr "Zoom per 2x" #~ msgid "Delete present thumbnails to make effective" #~ msgstr "Eliminare le miniature presenti per rendere effettivo" #~ msgid "Select Album" #~ msgstr "Album iniziale della galleria" #~ msgid "" #~ "appimage file moved to %s \n" #~ "desktop file created at %s \n" #~ "\n" #~ msgstr "" #~ "File AppImage spostato in %s \n" #~ "File del desktop creato come %s \n" #~ msgid "Flatten Book Page Photo" #~ msgstr "Appiattisci foto di pagina di libro" #~ msgid "Process: batch convert, batch metadata, image search ..." #~ msgstr "Conversioni, gestione metadati, ricerca..." #~ msgid "Sync Gallery, Export, Flickr, Albums, Slide Show" #~ msgstr "Aggiorna galleria, esporta, manda a Flickr, album, slideshow" #~ msgid "go to bookmarked image" #~ msgstr "Va a un segnalibro" #~ msgid "Set all images or current gallery" #~ msgstr "Segnala tutte le foto o quelle della galleria corrente" #~ msgid "Set map markers from current gallery" #~ msgstr "Mostra segnalini sulla mappa dalla galleria corrente" #~ msgid "Summary of image edit functions" #~ msgstr "Visualizza un sommario delle funzioni disponibili" #~ msgid "Upload image files to Flickr web service" #~ msgstr "Manda le immagini al servizio web Flicker" #~ msgid "Match Level" #~ msgstr "Soglia d'intervento" #~ msgid "Scroll" #~ msgstr "Scorri" #~ msgid "Choose image directory" #~ msgstr "Scegliere directory immagini" #~ msgid "Choose album" #~ msgstr "Scegliere album" #~ msgid "Click on a monotone image area." #~ msgstr "Cliccare una zona omogenea" #~ msgid "not a defined tag: %s" #~ msgstr "Etichetta sconosciuta: %s" #~ msgid "open new script file" #~ msgstr "Apri un file script" #~ msgid "script file is not closed" #~ msgstr "File di script non chiuso" #~ msgid "select script file to run" #~ msgstr "Selezionare il file script da eseguire" #~ msgid "unknown script file" #~ msgstr "File script sconosciuto" #~ msgid "select image files to be processed" #~ msgstr "Selezionare le immagini da processare" #~ msgid "" #~ "shift + left click: pick color or image position \n" #~ "left click or drag: paint color or copy image \n" #~ "right click or drag: remove color or image" #~ msgstr "" #~ "Maiusc + Clic sinistro: indica colore o posizione \n" #~ "Clic o trascina sinistro: dipinge o copia l'immagine \n" #~ "Clic o trascina destro: rimuove colore o immagine" #~ msgid "Paint/Clone" #~ msgstr "Dipingi pixel" #~ msgid "copy from image" #~ msgstr "Copia dall'immagine" #~ msgid "transparency center" #~ msgstr "Trasparenza al centro:" #~ msgid "transparency edge" #~ msgstr "Trasparenza al bordo:" #~ msgid "Directory" #~ msgstr "Directory" #~ msgid "Paint or clone image pixels using the mouse" #~ msgstr "Disegna o duplica pixel con il mouse" #~ msgid "Set desktop wallpaper from current Fotoxx image" #~ msgstr "Usa l'immagine corrente come sfondo del desktop" #~ msgid "Cycle desktop wallpaper from a Fotoxx album" #~ msgstr "Cicla sfondo del desktop da un album di Fotoxx" #~ msgid "Set Desktop Wallpaper" #~ msgstr "Imposta sfondo del desktop" #~ msgid "Cycle Desktop Wallpaper" #~ msgstr "Cicla sfondo del deskto" #~ msgid "%d files selected" #~ msgstr "%d file selezionati" #~ msgid "Save File Version" #~ msgstr "Salva file con versione" #~ msgid "slow scroll" #~ msgstr "Scorrimento lento" #~ msgid "Area Color" #~ msgstr "Colore dell'area" #~ msgid "" #~ "shift + left click: pick image position to copy \n" #~ "left click or drag: copy image to mouse position \n" #~ "right click or drag: restore image" #~ msgstr "" #~ "Maiusc + clic sinistro: sceglie la parte da copiare \n" #~ "Clic o trascinamento sinistro: copia nella posizione \n" #~ "Clic o trascinamento destro: ripristina" #~ msgid "" #~ "Metadata Search report cannot be re-sorted. \n" #~ "Check User Guide for a way to do this." #~ msgstr "" #~ "I risultati di una ricerca metadati non possono essere riordinati.\n" #~ "Consultare il manuale d'uso per istruzioni su come fare questo." #~ msgid "Japanese-fan" #~ msgstr "FanGiapponese" #~ msgid "Convert to pencil sketch" #~ msgstr "Converte in disegno a matita" #~ msgid "Select images to add" #~ msgstr "Seleziona immagini da aggiungere" fotoxx-18.01.1/locales/translate-pt.po0000644000175000017500000037371413222767271016301 0ustar micomico# Portuguese translations for home package. # Copyright (C) 2018 THE home'S COPYRIGHT HOLDER # This file is distributed under the same license as the home package. # msgid "" msgstr "" "Project-Id-Version: fotoxx-16.09\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-12-30 20:59+0100\n" "PO-Revision-Date: 2017-07-25 21:12+1200\n" "Last-Translator: André Campos Rodovalho \n" "Language-Team: Portuguese\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.8.7.1\n" #: f.albums.cc:89 msgid "" "Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove." msgstr "" "Clique com o botão direito do mouse na miniatura do álbum para \n" "recortar/colar na reserva, inserir a partir da reserva, ou remover." #: f.albums.cc:91 msgid "" "Right-click left/right side of album \n" "thumbnail to insert cached images \n" "before/after the thumbnail." msgstr "" "Clique com o botão direito do mouse no lado esquerdo/direito \n" "da miniatura do álbum para inserir imagens em cache \n" "antes/depois da miniatura." #: f.albums.cc:94 msgid "Drag album thumbnail to new position." msgstr "Arrastar miniatura de álbum" #: f.albums.cc:128 f.widgets.cc:613 msgid "Manage Albums" msgstr "Gerenciar Álbuns" #: f.albums.cc:140 f.albums.cc:275 msgid "Create or replace an album" msgstr "Criar ou substituir um álbum" #: f.albums.cc:142 msgid "Album to view or edit" msgstr "Álbum para visualizar ou editar" #: f.albums.cc:144 msgid "Select images, add to cache" msgstr "Selecionar imagens, adicionar ao cache" #: f.albums.cc:146 msgid "Clear image cache" msgstr "Limpar reserva de imagens" #: f.albums.cc:148 f.albums.cc:170 msgid "Delete an album" msgstr "Eliminar um álbum" #: f.albums.cc:169 msgid "Choose Album" msgstr "Escolher Álbum" #: f.albums.cc:171 #, c-format msgid "Image cache has %d images" msgstr "Cache de imagem tem %d imagens" #: f.albums.cc:172 msgid "cache added to empty album" msgstr "reservar adicionadas em um álbum vazio" #: f.albums.cc:228 #, c-format msgid "delete %s ?" msgstr "excluir %s ?" #: f.albums.cc:259 #, c-format msgid "fill from image cache (%d images)" msgstr "preencher com imagens reservadas (%d imagens)" #: f.albums.cc:277 f.albums.cc:310 msgid "Album Name" msgstr "Nome do Álbum" #: f.albums.cc:283 msgid "make an initially empty album" msgstr "criar um álbum inicialmente vazio" #: f.albums.cc:285 msgid "fill from current gallery" msgstr "preencher usando galeria atual" #: f.albums.cc:324 msgid "enter an album name" msgstr "inserir um nome para o álbum" #: f.albums.cc:352 msgid "new album created" msgstr "novo álbum criado" #: f.albums.cc:368 msgid "gallery is empty" msgstr "galeria vazia" #: f.albums.cc:876 f.widgets.cc:614 msgid "Update Album Files" msgstr "Atualizar Arquivos de Álbum" #: f.albums.cc:878 f.albums.cc:938 f.albums.cc:1007 f.albums.cc:1147 msgid "Choose Albums" msgstr "Escolher Álbuns" #: f.albums.cc:1005 f.widgets.cc:615 f.widgets.cc:762 msgid "Replace Album File" msgstr "Substituir Arquivo de Álbum" #: f.albums.cc:1009 msgid "All Albums" msgstr "Todos os álbuns" #: f.albums.cc:1012 msgid "old file" msgstr "arquivo antigo" #: f.albums.cc:1016 msgid "new file" msgstr "novo arquivo" #: f.albums.cc:1020 msgid "replace old" msgstr "substituir antigo" #: f.albums.cc:1021 msgid "add after old" msgstr "adicionar depois de antigo" #: f.albums.cc:1081 msgid "no albums chosen" msgstr "nenhum álbum escolhido" #: f.albums.cc:1162 msgid "ALL albums chosen" msgstr "TODOS os álbuns escolhidos" #: f.albums.cc:1346 msgid "instant" msgstr "imediato" #: f.albums.cc:1347 msgid "fade-in" msgstr "esmaecer" #: f.albums.cc:1348 msgid "roll-right" msgstr "rolar para direita" #: f.albums.cc:1349 msgid "roll-down" msgstr "rolar para baixo" #: f.albums.cc:1350 msgid "venetian" msgstr "veneziano" #: f.albums.cc:1351 msgid "grate" msgstr "grade" #: f.albums.cc:1352 msgid "rectangle" msgstr "retângulo" #: f.albums.cc:1353 msgid "implode" msgstr "implodir" #: f.albums.cc:1354 msgid "explode" msgstr "explodir" #: f.albums.cc:1355 msgid "radar" msgstr "radar" #: f.albums.cc:1356 msgid "spiral" msgstr "espiral" #: f.albums.cc:1357 msgid "Japan-fan" msgstr "Leque Japonês" #: f.albums.cc:1358 msgid "jaws" msgstr "maxilas" #: f.albums.cc:1359 msgid "ellipse" msgstr "elipse" #: f.albums.cc:1360 msgid "raindrops" msgstr "gotas de chuva" #: f.albums.cc:1361 msgid "doubledoor" msgstr "doubledoor" #: f.albums.cc:1362 msgid "rotate" msgstr "rotacionar" #: f.albums.cc:1363 msgid "fallover" msgstr "tombar" #: f.albums.cc:1364 msgid "spheroid" msgstr "esferóide" #: f.albums.cc:1365 msgid "turn-page" msgstr "vira-página" #: f.albums.cc:1366 msgid "french-door" msgstr "porta de entrada" #: f.albums.cc:1367 msgid "turn-cube" msgstr "cubo mágico" #: f.albums.cc:1368 msgid "windmill" msgstr "moinho de vento" #: f.albums.cc:1369 msgid "pixelize" msgstr "pixelizar" #: f.albums.cc:1370 msgid "twist" msgstr "torcer" #: f.albums.cc:1372 msgid "squish" msgstr "esmagar" #: f.albums.cc:1399 f.widgets.cc:616 msgid "Slide Show" msgstr "Apresentação" #: f.albums.cc:1405 msgid "use gallery" msgstr "usar galeria" #: f.albums.cc:1411 msgid "Clip Limit" msgstr "Limite de Corte" #: f.albums.cc:1415 msgid "Music File" msgstr "Arquivo de Música" #: f.albums.cc:1420 msgid "Full Screen" msgstr "Tela cheia" #: f.albums.cc:1421 msgid "Auto-replay" msgstr "Auto-repetição" #: f.albums.cc:1424 msgid "Customize:" msgstr "Customizar:" #: f.albums.cc:1425 msgid "transitions" msgstr "transições" #: f.albums.cc:1426 msgid "image files" msgstr "arquivos de imagem" #: f.albums.cc:1427 msgid "KB controls" msgstr "Teclas de Controle" #: f.albums.cc:1441 f.albums.cc:1509 f.albums.cc:1732 f.albums.cc:2007 #: f.albums.cc:2292 f.albums.cc:2309 f.albums.cc:2526 msgid "invalid album" msgstr "álbum inválido" #: f.albums.cc:1540 msgid "open album" msgstr "abrir álbum" #: f.albums.cc:1556 msgid "Select music file" msgstr "Selecionar arquivo de música" #: f.albums.cc:1587 #, c-format msgid "%d images" msgstr "%d imagens" #: f.albums.cc:1614 msgid "arrow keys show previous or next image instantly" msgstr "teclas seta mostram a imagem seguinte ou anterior instantaneamente" #: f.albums.cc:1632 msgid "Keyboard Preferences" msgstr "Preferências do teclado" #: f.albums.cc:1635 msgid "blank or unblank window" msgstr "branquear ou desbranquear tela" #: f.albums.cc:1638 msgid "show next image, with transition" msgstr "mostra a próxima imagem, com transição" #: f.albums.cc:1641 msgid "pause or resume slide show" msgstr "pausa ou retorna a apresentação de slides" #: f.albums.cc:1644 msgid "magnify image (loupe tool)" msgstr "ampliar imagem (ferramenta lupa)" #: f.albums.cc:1711 msgid "random sequence" msgstr "sequência randômica" #: f.albums.cc:1736 msgid "Transition Preferences" msgstr "Preferências de Transição" #: f.albums.cc:1738 msgid "Transitions File" msgstr "Arquivo de Transições" #: f.albums.cc:1756 f.albums.cc:1760 msgid "transition" msgstr "transição" #: f.albums.cc:1757 f.albums.cc:1761 msgid "use" msgstr "usar" #: f.albums.cc:1758 f.albums.cc:1762 msgid "slow" msgstr "lento" #: f.albums.cc:1759 f.albums.cc:1763 msgid "pref" msgstr "pref" #: f.albums.cc:1862 f.albums.cc:1901 msgid "invalid file" msgstr "arquivo inválido" #: f.albums.cc:1954 f.albums.cc:2511 #, c-format msgid "" "file format error: \n" " %s" msgstr "" "error no formato de arquivo: \n" " %s" #: f.albums.cc:2013 msgid "Image Preferences" msgstr "Preferências de Imagem" #: f.albums.cc:2017 f.edit.cc:1405 f.file.cc:1582 f.file.cc:1888 f.meta.cc:820 #: f.meta.cc:1588 f.meta.cc:1780 msgid "Image File:" msgstr "Arquivo de Imagem:" #: f.albums.cc:2021 msgid "Play tone when image shows" msgstr "Tocar áudio quando imagem aparecer" #: f.albums.cc:2024 msgid "Show image caption" msgstr "Mostrar legenda de imagem" #: f.albums.cc:2029 msgid "Show image comments" msgstr "Mostrar comentários de imagem" #: f.albums.cc:2034 msgid "Wait before zoom" msgstr "Atraso antes do Zoom" #: f.albums.cc:2039 msgid "Zoom type:" msgstr "Tipo de Zoom:" #: f.albums.cc:2041 msgid "zoom-in" msgstr "aproximar" #: f.albums.cc:2042 msgid "zoom-out" msgstr "afastar" #: f.albums.cc:2045 msgid "Zoom size (x)" msgstr "Tamanho do Zoom (x)" #: f.albums.cc:2048 msgid "Steps" msgstr "Passos" #: f.albums.cc:2052 msgid "Zoom Center" msgstr "Zoom Centralizado" #: f.albums.cc:2056 msgid "Wait after zoom" msgstr "Atraso depois do Zoom" #: f.albums.cc:2061 msgid "Transition to next image" msgstr "Transição para a próxima imagem" #: f.albums.cc:2064 f.albums.cc:2167 msgid "next" msgstr "próximo" #: f.albums.cc:2157 msgid "click on thumbnail to set zoom center" msgstr "clique na miniatura para configurar centralização e zoom" #: f.area.cc:81 msgid "Select Area for Edits" msgstr "Selecionar área para edições" #: f.area.cc:82 f.area.cc:1855 f.edit.cc:9094 msgid "Press F1 for help" msgstr "Pressione F1 para ajuda" #: f.area.cc:91 msgid "" "Select Area not supported \n" "by this edit function" msgstr "" "Seleção de Área não suportada \n" "por esta função de edição" #: f.area.cc:120 msgid "select rectangle" msgstr "seleção retangular" #: f.area.cc:121 msgid "select ellipse" msgstr "seleção elíptica" #: f.area.cc:124 msgid "freehand draw" msgstr "desenho a mão livre" #: f.area.cc:125 msgid "follow edge" msgstr "seguir borda" #: f.area.cc:126 msgid "draw/replace" msgstr "desenho/substituir" #: f.area.cc:129 msgid "select area within mouse circle" msgstr "selecionar a área dentro do círculo do mouse" #: f.area.cc:132 msgid "select one matching color within mouse circle:" msgstr "selecionar uma cor correspondente dentro do círculo do mouse:" #: f.area.cc:134 msgid "" "first select the checkbox, then \n" "shift+click on image to set the color" msgstr "" "primeiro marque na caixa de seleção, depois \n" "Shift+Clique na imagem para definir a cor" #: f.area.cc:138 msgid "select all matching colors within mouse circle" msgstr "selecionar todas cores correspondentes dentro do círculo do mouse:" #: f.area.cc:144 msgid "match level %" msgstr "nível de correspondência %" #: f.area.cc:147 msgid "search range" msgstr "intervalo de procura" #: f.area.cc:151 msgid "Area Edge Blend Width" msgstr "Largura da Área de Borda da Mistura " #: f.area.cc:154 msgid "Edge Creep" msgstr "Deslizamento de Borda" #: f.area.cc:159 msgid "Line Color:" msgstr "Cor de Linha:" #: f.area.cc:179 f.area.cc:180 msgid "area edits fade away within edge distance" msgstr "edições na área esmaecem dentre as distâncias de borda" #: f.area.cc:351 f.area.cc:517 #, c-format msgid "exceed %d edits" msgstr "excedeu %d edições" #: f.area.cc:1333 msgid "" "Click one time inside each enclosed area. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Clique uma vez dentro de cada área delimitada. \n" "Possíveis lacunas no contorno serão encontradas. \n" "Pressione F1 para acessar ajuda." #: f.area.cc:1360 msgid "finish area" msgstr "finalize a área" #: f.area.cc:1389 f.area.cc:1534 #, c-format msgid "found %d pixels" msgstr "encontrados %d pixels" #: f.area.cc:1639 msgid "" "Click near any position on the area outline. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help." msgstr "" "Clique próximo a qualquer posição do contorno da área. \n" "Possíveis lacunas no contorno serão encontradas. \n" "Pressione F1 para obter ajuda." #: f.area.cc:1658 msgid "find outline gap" msgstr "procurar lacuna de contorno" #: f.area.cc:1748 msgid "cannot find area outline" msgstr "não foi possível encontrar área de contorno" #: f.area.cc:1854 f.widgets.cc:458 msgid "Select Hairy" msgstr "Seleção Irregular" #: f.area.cc:1862 msgid "select the area first" msgstr "selecione a área primeiro" #: f.area.cc:1867 f.area.cc:2489 f.area.cc:2507 f.area.cc:2527 f.area.cc:2851 #: f.area.cc:2971 msgid "the area is not finished" msgstr "a área não está finalizada" #: f.area.cc:1940 msgid "select" msgstr "selecionar" #: f.area.cc:1946 msgid "deselect" msgstr "deselecionar" #: f.area.cc:2616 msgid "Edge calculation in progress" msgstr "Cálculo de borda em andamento" #: f.area.cc:2625 msgid "Area Edge Calc" msgstr "Calc. área de borda" #: f.area.cc:2957 f.area.cc:3029 f.widgets.cc:465 msgid "Copy Area" msgstr "Copiar Área" #: f.area.cc:2960 f.widgets.cc:468 msgid "Save Area File" msgstr "Salvar Arquivo de Área" #: f.area.cc:3035 msgid "save area as a PNG file" msgstr "salvar área como um arquivo PNG" #: f.area.cc:3159 msgid "position with mouse click/drag" msgstr "posicione o mouse clique/arraste" #: f.area.cc:3219 msgid "Paste Image" msgstr "Colar Imagem" #: f.area.cc:3224 f.process.cc:1311 msgid "resize" msgstr "redimensionar" #: f.combine.cc:169 f.combine.cc:815 f.combine.cc:1416 f.combine.cc:2039 msgid "Select 2 to 9 files" msgstr "Selecionar 2 a 9 arquivos" #: f.combine.cc:191 f.combine.cc:837 f.combine.cc:1438 f.combine.cc:2061 msgid "Images are not all the same size" msgstr "Imagens não são todas do mesmo tamanho" #: f.combine.cc:550 msgid "Adjust Image Contributions" msgstr "Ajustar contribuição de imagem" #: f.combine.cc:554 msgid "dark pixels" msgstr "pixels escuros" #: f.combine.cc:556 msgid "light pixels" msgstr "pixels claros" #: f.combine.cc:558 msgid "file:" msgstr "arquivo:" #: f.combine.cc:1018 msgid "Paint and Warp Image" msgstr "Pintar e distorcer imagem" #: f.combine.cc:1026 f.edit.cc:5686 msgid "paint" msgstr "pintar" #: f.combine.cc:1027 msgid "warp" msgstr "distorção" #: f.combine.cc:1623 msgid "Select and Paint Image" msgstr "Selecionar e pintar imagem" #: f.combine.cc:1631 msgid "Transient Objects" msgstr "Objetos Transientes" #: f.combine.cc:2232 msgid "Adjust Pixel Composition" msgstr "Ajustar Composição do Pixel" #: f.combine.cc:2234 msgid "use average" msgstr "usar média" #: f.combine.cc:2235 msgid "use median" msgstr "usar mediana" #: f.combine.cc:2237 msgid "omit low pixel" msgstr "omitir pixel baixos" #: f.combine.cc:2238 msgid "omit high pixel" msgstr "omitir pixels altos" #: f.combine.cc:2502 f.combine.cc:3653 msgid "Select 2 to 4 files" msgstr "Selecionar 2 a 4 arquivos" #: f.combine.cc:2573 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from lower edge." msgstr "" "Arraste as imagens para o alinhamento grosseiro.\n" "Para rotacionar, arraste da borda mais abaixo." #: f.combine.cc:2575 f.combine.cc:3726 msgid "no curve (scanned image)" msgstr "não vetorial (imagem digitalizada)" #: f.combine.cc:2576 f.combine.cc:3727 msgid "Search for lens mm" msgstr "Procurar por mm de lente" #: f.combine.cc:2577 f.combine.cc:3728 msgid "Save lens mm → image EXIF" msgstr "Salvar mm de lente → EXIF imagem" #: f.combine.cc:2639 f.combine.cc:3790 msgid "Pre-align Images" msgstr "Pré-alinhamento de imagens" #: f.combine.cc:2643 f.combine.cc:3794 msgid "lens mm" msgstr "mm de lente" #: f.combine.cc:2648 f.combine.cc:3799 msgid "no auto warp" msgstr "não auto distorcer" #: f.combine.cc:2650 f.combine.cc:3801 msgid "manual align" msgstr "alinhar manualmente" #: f.combine.cc:2652 f.combine.cc:3803 f.widgets.cc:473 f.widgets.cc:768 msgid "Resize" msgstr "Redimensionar" #: f.combine.cc:2653 f.combine.cc:3804 msgid "resize window" msgstr "redimensionar janela" #: f.combine.cc:2661 f.combine.cc:3812 msgid "do not warp images during auto-alignment" msgstr "não distorcer imagem durante auto-alinhamento" #: f.combine.cc:2707 f.combine.cc:3858 msgid "use two images only" msgstr "use duas imagens apenas" #: f.combine.cc:2735 f.combine.cc:2939 f.combine.cc:3127 f.combine.cc:3884 #: f.combine.cc:4088 f.combine.cc:4276 msgid "Too little overlap, cannot align" msgstr "Baixa sobreposição, não é possível alinhar" #: f.combine.cc:3205 f.combine.cc:4354 msgid "Match Brightness and Color" msgstr "Parear Brilho e Cor" #: f.combine.cc:3251 msgid "Select image" msgstr "Selecionar imagem" #: f.combine.cc:3266 f.combine.cc:4415 msgid "auto color" msgstr "cor automática" #: f.combine.cc:3267 f.combine.cc:4416 msgid "file color" msgstr "arquivo de cor" #: f.combine.cc:3273 f.combine.cc:4422 msgid "mouse warp" msgstr "distorcer com mouse" #: f.combine.cc:3275 f.combine.cc:4424 msgid "flatten image" msgstr "aplainar imagem" #: f.combine.cc:3724 msgid "" "Drag images into rough alignment.\n" "To rotate, drag from right edge." msgstr "" "Arraste as imagens para o alinhamento grosseiro.\n" "Para rotacionar, arraste pela borda a direita." #: f.combine.cc:4684 msgid "pano tools (hugin) not installed" msgstr "pano tools (hugin) não instalado" #: f.combine.cc:4693 msgid "Select at least 2 files" msgstr "Selecione pelo menos 2 arquivos" #: f.edit.cc:98 msgid "Trim: drag middle to move, drag corners to resize" msgstr "" "Recorte: arraste o centro para mover, arraste os cantos para redimensionar" #: f.edit.cc:99 msgid "Minor rotate: drag right edge with mouse" msgstr "Pequena rotação: arrastar margem direita com o mouse" #: f.edit.cc:180 f.widgets.cc:471 f.widgets.cc:767 msgid "Trim/Rotate" msgstr "Recortar/Rodar" #: f.edit.cc:192 f.edit.cc:1306 msgid "ratio" msgstr "taxa" #: f.edit.cc:196 msgid "trim size:" msgstr "tamanho do recorte:" #: f.edit.cc:201 msgid "Lock Ratio" msgstr "Travar taxa" #: f.edit.cc:203 msgid "maximize trim box" msgstr "maximizar caixa de recorte" #: f.edit.cc:204 msgid "use previous size" msgstr "usar tamanho anteior" #: f.edit.cc:205 msgid "invert width/height" msgstr "inverter altura/largura" #: f.edit.cc:206 msgid "trim transparent edges" msgstr "aparar bordas transparentes" #: f.edit.cc:207 msgid "lock width/height ratio" msgstr "travar proporção altura/largura" #: f.edit.cc:212 msgid "Customize" msgstr "Customizar" #: f.edit.cc:218 msgid "Level" msgstr "Nível" #: f.edit.cc:219 msgid "use EXIF data if available" msgstr "usar informação EXIF se disponível" #: f.edit.cc:222 msgid "Rotate: degrees" msgstr "Rotação: graus" #: f.edit.cc:1302 msgid "Trim Buttons" msgstr "Aparar rodapés" #: f.edit.cc:1305 msgid "label" msgstr "etiqueta" #: f.edit.cc:1402 msgid "Upright Image" msgstr "Orientar Imagem" #: f.edit.cc:1408 f.process.cc:168 f.process.cc:755 f.widgets.cc:472 #: f.widgets.cc:769 msgid "Upright" msgstr "Orientar" #: f.edit.cc:1461 msgid "rotation unknown" msgstr "rotação desconhecida" #: f.edit.cc:1548 msgid "Resize Image" msgstr "Redimensionar" #: f.edit.cc:1573 msgid "W/H Ratio:" msgstr "Relação L/A:" #: f.edit.cc:1575 msgid "Lock" msgstr "Travar" #: f.edit.cc:1577 msgid "use previous settings" msgstr "usar configurações prévias" #: f.edit.cc:2298 f.widgets.cc:476 f.widgets.cc:772 msgid "Retouch Combo" msgstr "Kit Retoque" #: f.edit.cc:2319 msgid "Amplifier" msgstr "Amplificador" #: f.edit.cc:2320 fotoxx.h:1188 msgid "Brightness" msgstr "Briho" #: f.edit.cc:2321 f.effects.cc:3681 fotoxx.h:1198 msgid "Contrast" msgstr "Contraste" #: f.edit.cc:2322 msgid "Low Color" msgstr "Cor baixa" #: f.edit.cc:2323 msgid "Warmer" msgstr "Mais quente" #: f.edit.cc:2324 f.effects.cc:1332 msgid "Dark Areas" msgstr "Áreas Escuras" #: f.edit.cc:2333 msgid "Max." msgstr "Máx." #: f.edit.cc:2334 f.edit.cc:2335 f.edit.cc:2336 msgid "High" msgstr "Alto" #: f.edit.cc:2337 msgid "Cooler" msgstr "Mais frio" #: f.edit.cc:2338 msgid "Bright" msgstr "Brilho" #: f.edit.cc:2341 f.tools.cc:2134 msgid "Brightness Distribution" msgstr "Distribuição de Brilho" #: f.edit.cc:2344 msgid "Click for white balance" msgstr "Clique para balanço de brancos" #: f.edit.cc:2345 msgid "Click for black level" msgstr "Clique para nível de preto" #: f.edit.cc:2348 msgid "Settings File" msgstr "Arquivos de Configuração" #: f.edit.cc:2352 msgid "recall previous settings used" msgstr "recolocar configurações prévias utilizadas" #: f.edit.cc:3004 msgid "Adjust Brightness Distribution" msgstr "Ajustar Distribuição de Brilho" #: f.edit.cc:3043 msgid "Low Cutoff" msgstr "Corte Baixo" #: f.edit.cc:3044 msgid "High Cutoff" msgstr "Corte Elevado" #: f.edit.cc:3045 msgid "Low Flatten" msgstr "Achatamento Baixo" #: f.edit.cc:3046 msgid "Mid Flatten" msgstr "Achatamento Médio" #: f.edit.cc:3047 msgid "High Flatten" msgstr "Achatamento Elevado" #: f.edit.cc:3048 msgid "Low Stretch" msgstr "Estiramento Baixo" #: f.edit.cc:3049 msgid "Mid Stretch" msgstr "Estiramento Médio" #: f.edit.cc:3050 msgid "High Stretch" msgstr "Estiramento Elevado" #: f.edit.cc:3383 msgid "Magnify Gradients" msgstr "Ampliar Gradientes" #: f.edit.cc:3415 msgid "low" msgstr "baixo" #: f.edit.cc:3417 msgid "high" msgstr "alto" #: f.edit.cc:3420 msgid "Amplify" msgstr "Ampliar" #: f.edit.cc:3813 msgid "Flatten Brightness" msgstr "Nivelamento de Brilho" #: f.edit.cc:3864 msgid "Zones" msgstr "Zonas" #: f.edit.cc:3871 msgid "Deband Dark" msgstr "Debandar Escuridão" #: f.edit.cc:3874 msgid "Deband Bright" msgstr "Debandar Claridade" #: f.edit.cc:4386 msgid "Dark Point" msgstr "Ponto Escuro" #: f.edit.cc:4387 msgid "Bright Point" msgstr "Ponto Claro" #: f.edit.cc:4388 msgid "Multiplyer" msgstr "Multiplicador" #: f.edit.cc:4412 msgid "brightness rescale" msgstr "reescalar brilho" #: f.edit.cc:4415 msgid "click bright point" msgstr "clique no ponto claro" #: f.edit.cc:4416 msgid "click dark point" msgstr "clique no ponto escuro" #: f.edit.cc:4427 msgid "blend" msgstr "mistura" #: f.edit.cc:4430 msgid "reduce bright" msgstr "reduzir claridade" #: f.edit.cc:5468 f.widgets.cc:481 msgid "Mirror Image" msgstr "Espelhar Imagem" #: f.edit.cc:5472 f.warp.cc:3300 msgid "horizontal" msgstr "horizontal" #: f.edit.cc:5473 f.warp.cc:3304 msgid "vertical" msgstr "vertical" #: f.edit.cc:5620 msgid "" "shift + left click: pick color from image \n" "left drag: paint color on image \n" "right drag: restore original image" msgstr "" "shift + clique esquerdo do mouse: captura a cor da imagem \n" "arrasto com botão esquerdo do mouse: pinta a cor na imagem \n" "arrasto com botão direito do mouse: restaura a imagem original" #: f.edit.cc:5656 msgid "Paint on Image" msgstr "Pintura em Imagem" #: f.edit.cc:5662 msgid "paint color" msgstr "cor da pintura" #: f.edit.cc:5673 f.edit.cc:6640 msgid "brush size" msgstr "tamanho do pincel" #: f.edit.cc:5674 f.edit.cc:6641 msgid "opacity center" msgstr "centro de opacidade" #: f.edit.cc:5675 f.edit.cc:6642 msgid "opacity edge" msgstr "borda de opacidade" #: f.edit.cc:5687 msgid "erase" msgstr "apagar" #: f.edit.cc:5688 msgid "include transparent areas" msgstr "incluir áreas transparentes" #: f.edit.cc:5695 msgid "drag image" msgstr "arrastar imagem" #: f.edit.cc:5697 msgid "zoom image" msgstr "ampliar imagem" #: f.edit.cc:6221 msgid "Color Palette" msgstr "Paleta de cores" #: f.edit.cc:6225 msgid "click on image to choose color" msgstr "clique na imagem para escolher a cor" #: f.edit.cc:6415 f.repair.cc:3799 msgid "Color Hue" msgstr "Matiz de Cor" #: f.edit.cc:6416 f.repair.cc:3768 f.repair.cc:3800 msgid "Saturation" msgstr "Saturação" #: f.edit.cc:6417 f.repair.cc:3769 f.repair.cc:3801 msgid "Lightness" msgstr "Luminosidade" #: f.edit.cc:6599 msgid "" "shift + left click: pick image position to copy \n" "left drag: copy image to mouse position \n" "right drag: restore image" msgstr "" "shift + clique com botão esquerdo do mouse: captura posição da imagem a " "copiar \n" "arrasto com botão esquerdo do mouse: copia imagem na posição do mouse \n" "arrasto com botão direito do mouse: restaura imagem original" #: f.edit.cc:6630 f.widgets.cc:483 msgid "Clone Image" msgstr "Clonar Imagem" #: f.edit.cc:6649 msgid "paint over transparent areas" msgstr "pintar sobre áreas transparentes" #: f.edit.cc:7098 msgid "" "left drag: blend image \n" "right drag: restore image" msgstr "" "arrasto com botão esquerdo: misturar image \n" "arrasto com botão direito: restaurar imagem" #: f.edit.cc:7126 f.widgets.cc:484 msgid "Blend Image" msgstr "Misturar Imagem" #: f.edit.cc:7134 f.repair.cc:7663 msgid "strength center" msgstr "fortalecer centro" #: f.edit.cc:7135 f.repair.cc:7664 msgid "strength edge" msgstr "fortalecer borda" #: f.edit.cc:7359 msgid "+Version" msgstr "+Versão" #: f.edit.cc:7382 f.widgets.cc:275 msgid "Add Text to Image" msgstr "Adicionar texto à Imagem" #: f.edit.cc:7383 msgid "Enter text, click/drag on image, right click to remove" msgstr "" "Insira um texto, clique/arraste na imagem, clique com o botão direito para " "remover" #: f.edit.cc:7432 f.edit.cc:8310 msgid "Use settings file" msgstr "Usas arquivo de configuração" #: f.edit.cc:7437 f.mashup.cc:2455 msgid "Text" msgstr "Texto" #: f.edit.cc:7442 msgid "Use metadata key" msgstr "Use chave de metadado" #: f.edit.cc:7461 f.mashup.cc:2473 msgid "text" msgstr "texto" #: f.edit.cc:7462 f.edit.cc:8336 f.mashup.cc:2474 f.mashup.cc:2812 msgid "backing" msgstr "fundo" #: f.edit.cc:7463 f.edit.cc:8337 f.mashup.cc:2475 f.mashup.cc:2813 msgid "outline" msgstr "contorno" #: f.edit.cc:7464 f.edit.cc:8338 f.mashup.cc:2476 f.mashup.cc:2814 msgid "shadow" msgstr "sombra" #: f.edit.cc:7490 msgid "save to current file" msgstr "salvar em arquivo atual" #: f.edit.cc:7491 f.file.cc:2252 msgid "save as new file version" msgstr "salvar como nova versão de arquivo" #: f.edit.cc:7492 msgid "" "save to current file \n" "open next file with same text" msgstr "" "salvar para arquivo atual \n" "abrir próximo arquivo com o mesmo texto" #: f.edit.cc:7652 f.mashup.cc:2580 f.tools.cc:1464 msgid "select font" msgstr "selecionar fonte" #: f.edit.cc:7928 f.edit.cc:8766 msgid "text file is defective" msgstr "arquivo de texto está defeituoso" #: f.edit.cc:8268 msgid "" "Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove" msgstr "" "Inserir propriedades de linha ou seta em caixa de diálogo, \n" "clique/arraste na imagem, clique com botão direito para remover" #: f.edit.cc:8302 f.widgets.cc:276 msgid "Add lines or arrows to an image" msgstr "Adicionar linhas ou setas a uma imagem" #: f.edit.cc:8315 f.mashup.cc:2791 msgid "Line length" msgstr "Comprimento de linha" #: f.edit.cc:8322 f.mashup.cc:2798 msgid "Arrow head" msgstr "Ponta de seta" #: f.edit.cc:8335 f.mashup.cc:2811 msgid "line" msgstr "linha" #: f.edit.cc:8364 msgid "" "fix line/arrow in layout \n" " start new line/arrow" msgstr "" "fixar linha/seta no leiaute \n" " começar nova linha/seta" #: f.edit.cc:9093 f.widgets.cc:487 msgid "Paint Edits" msgstr "Primir Edições" #: f.edit.cc:9100 f.edit.cc:9323 fotoxx-18.01.cc:3426 msgid "" "Select area cannot be kept.\n" "Continue?" msgstr "" "Área selecionada não pode ser capturada.\n" "Continuar?" #: f.edit.cc:9108 f.edit.cc:9331 f.tools.cc:2666 msgid "Edit function must be active" msgstr "Função Editar tem de estar ativada" #: f.edit.cc:9113 msgid "Cannot use Paint Edits" msgstr "Impossível usar Edições Aplicadas" #: f.edit.cc:9140 msgid "power: center" msgstr "alimentação: centro" #: f.edit.cc:9145 msgid "reset area" msgstr "redefinir área" #: f.edit.cc:9316 f.widgets.cc:488 msgid "Leverage Edits" msgstr "Influenciar Edições" #: f.edit.cc:9317 msgid "Edit Function Amplifier" msgstr "Editar função Amplificador" #: f.edit.cc:9336 msgid "Cannot use Leverage Edits" msgstr "Impossível usar Edições Influenciadas" #: f.edit.cc:9365 msgid "minimum" msgstr "mínimo" #: f.edit.cc:9367 msgid "maximum" msgstr "máximo" #: f.edit.cc:9642 f.edit.cc:9686 msgid "Edit Plugins" msgstr "Editar Extensões" #: f.edit.cc:9643 msgid "Edit plugins menu" msgstr "Editar menu de plugins" #: f.edit.cc:9651 msgid "Run as Fotoxx edit function" msgstr "Executar como função de edição do Fotoxx" #: f.edit.cc:9688 msgid "menu name" msgstr "nome do menu" #: f.edit.cc:9691 msgid "command" msgstr "comando" #: f.edit.cc:9888 msgid "Plugin working ..." msgstr "Plugin funcionando ..." #: f.edit.cc:9897 msgid "plugin failed" msgstr "extensão falhou" #: f.effects.cc:79 msgid "Convert to Sketch" msgstr "Converter em Esboço" #: f.effects.cc:157 msgid "Clip Level" msgstr "Grampear Nível" #: f.effects.cc:161 msgid "Algorithm" msgstr "Algoritmo" #: f.effects.cc:166 msgid "Foreground" msgstr "Primeiro Plano" #: f.effects.cc:169 f.tools.cc:1275 msgid "Background" msgstr "Plano de Fundo" #: f.effects.cc:1054 f.widgets.cc:531 msgid "Line Drawing" msgstr "Desenho de linha" #: f.effects.cc:1067 msgid "black/white" msgstr "preto/branco" #: f.effects.cc:1326 f.widgets.cc:532 msgid "Color Drawing" msgstr "Desenho de Cores" #: f.effects.cc:1333 msgid "Bright Areas" msgstr "Áreas claras" #: f.effects.cc:1575 f.widgets.cc:533 msgid "Graduated Blur" msgstr "Desfoque Graduado" #: f.effects.cc:1579 msgid "Contrast Limit" msgstr "Limite de Contraste" #: f.effects.cc:1582 f.repair.cc:929 msgid "Blur Radius" msgstr "Raio de Desfoque" #: f.effects.cc:1799 msgid "Simulate Embossing" msgstr "Simular Gravação em relevo" #: f.effects.cc:1820 msgid "depth" msgstr "profundidade" #: f.effects.cc:2029 msgid "Simulate Tiles" msgstr "Simular Ladrilho" #: f.effects.cc:2033 msgid "tile size" msgstr "tamanho do ladrilho" #: f.effects.cc:2036 msgid "tile gap" msgstr "espaçamento dos ladrilhos" #: f.effects.cc:2039 msgid "3D depth" msgstr "profundidade 3D" #: f.effects.cc:2258 msgid "Convert Image to Dots" msgstr "Converter imagem para pontos" #: f.effects.cc:2262 msgid "dot size" msgstr "tamanho do ponto" #: f.effects.cc:2484 msgid "Simulate Painting" msgstr "Simular Pintura" #: f.effects.cc:2502 msgid "color depth" msgstr "resolução de cor" #: f.effects.cc:2506 msgid "patch area goal" msgstr "remendar área objetivo" #: f.effects.cc:2510 msgid "req. color match" msgstr "correspondência de cores requeridas" #: f.effects.cc:2514 msgid "borders" msgstr "bordas" #: f.effects.cc:3081 f.widgets.cc:538 msgid "Vignette" msgstr "Vinheta" #: f.effects.cc:3430 msgid "Add Texture" msgstr "Adicionar Textura" #: f.effects.cc:3646 msgid "Background Pattern" msgstr "Padrão de Plano de Fundo" #: f.effects.cc:3650 msgid "Pattern File:" msgstr "Arquivo de Padrão:" #: f.effects.cc:3655 msgid "Zoom" msgstr "Zoom" #: f.effects.cc:3658 msgid "Geometry" msgstr "Geometria" #: f.effects.cc:3659 msgid "Calculate" msgstr "Calcular" #: f.effects.cc:3662 f.widgets.cc:540 msgid "Pattern" msgstr "Padrão" #: f.effects.cc:3670 msgid "Overlap" msgstr "Sobreposição" #: f.effects.cc:3678 msgid "Opacity" msgstr "Opacidade" #: f.effects.cc:4124 msgid "Create Mosaic" msgstr "Criar Mosaico" #: f.effects.cc:4170 msgid "Tile" msgstr "Ladrilho" #: f.effects.cc:4178 f.widgets.cc:535 msgid "Tiles" msgstr "Ladrilhos" #: f.effects.cc:4184 msgid "Tile blending" msgstr "Mistura de Ladrilhos" #: f.effects.cc:4265 #, c-format msgid "exceeded max. tiles: %d" msgstr "excedeu máx. de ladrilhos: %d" #: f.effects.cc:4272 #, c-format msgid "only %d tile images found" msgstr "apenas %d imagens de ladrilho encontradas" #: f.effects.cc:4756 f.widgets.cc:542 msgid "Custom Kernel" msgstr "Customizar Núcleo" #: f.effects.cc:4760 msgid "Kernel size" msgstr "Tamanho de Núcleo" #: f.effects.cc:4777 msgid "multiply" msgstr "multiplicação" #: f.effects.cc:4780 msgid "add" msgstr "adicionar" #: f.effects.cc:4784 msgid "Data file" msgstr "Arquivo de Dados" #: f.effects.cc:4804 fotoxx-18.01.cc:3690 msgid "Load settings from file" msgstr "Carregar configurações de um arquivo" #: f.effects.cc:5046 msgid "Pull image using the mouse." msgstr "Puxe a imagem utilizando o mouse." #: f.effects.cc:5067 f.widgets.cc:543 msgid "Directed Blur" msgstr "Desfoque Direcionado" #: f.effects.cc:5071 msgid "blur span" msgstr "extensão de desfoque" #: f.effects.cc:5074 msgid "intensity" msgstr "intensidade" #: f.effects.cc:5276 f.widgets.cc:544 msgid "Blur Background" msgstr "Desfocar Plano de Fundo" #: f.effects.cc:5280 msgid "constant blur" msgstr "desfoque constante" #: f.effects.cc:5283 msgid "increase blur with distance" msgstr "intensificar desfoque com a distância" #: f.effects.cc:5285 msgid "min. blur radius" msgstr "radio mínimo de desfoque" #: f.effects.cc:5288 msgid "max. blur radius" msgstr "raio máximo de desfoque" #: f.effects.cc:5322 f.warp.cc:819 f.warp.cc:1252 msgid "no active Select Area" msgstr "nenhuma Área Selecionada ativa" #: f.effects.cc:5515 f.widgets.cc:545 msgid "Alien Colors" msgstr "Cores Alien" #: f.effects.cc:5518 msgid "blocksize" msgstr "tamanho do bloco" #: f.effects.cc:5521 f.warp.cc:3298 msgid "amplitude" msgstr "amplitude" #: f.file.cc:190 msgid "" "Use EXIF photo date or \n" " file modification date?" msgstr "" "Usar dados EXIF da foto ou \n" " data de modificação do arquivo?" #: f.file.cc:204 f.widgets.cc:626 msgid "File" msgstr "Arquivo" #: f.file.cc:378 f.process.cc:1345 msgid "Raw Therapee not installed" msgstr "Raw Therapee não instalado" #: f.file.cc:396 f.widgets.cc:432 f.widgets.cc:777 msgid "Open RAW (Raw Therapee)" msgstr "Abrir RAW (Raw Therapee)" #: f.file.cc:401 msgid "RAW type not registered in User Settings" msgstr "Tipo RAW não reconhecido nas Configurações de Usuário" #: f.file.cc:416 msgid "Raw Therapee produced no tif file" msgstr "Raw Therapee não produziu nenhum arquivo tif" #: f.file.cc:827 f.widgets.cc:429 msgid "Open Image File" msgstr "Abrir arquivo" #: f.file.cc:846 f.process.cc:597 msgid "unknown file type" msgstr "tipo de arquivo desconhecido" #: f.file.cc:1024 #, c-format msgid "Start of gallery, preceding gallery: %s" msgstr "Início de Galeria, precedente: %s" #: f.file.cc:1025 #, c-format msgid "End of gallery, following gallery: %s" msgstr "Fim de Galeria, adiante: %s" #: f.file.cc:1133 msgid "Create Blank Image" msgstr "Criar imagem em branco" #: f.file.cc:1135 msgid "file name" msgstr "nome do arquvo" #: f.file.cc:1169 msgid "supply a file name" msgstr "forneça um nome de arquivo" #: f.file.cc:1339 f.widgets.cc:436 msgid "Rename Image File" msgstr "Renomear arquivo" #: f.file.cc:1346 msgid "Old Name" msgstr "nome antigo" #: f.file.cc:1347 f.process.cc:130 msgid "New Name" msgstr "novo nome" #: f.file.cc:1356 msgid "previous name" msgstr "nome anterior" #: f.file.cc:1357 msgid "Add 1" msgstr "Adicionar 1" #: f.file.cc:1360 f.file.cc:1596 f.file.cc:1891 msgid "keep this dialog open" msgstr "Mantenha esta caixa de diálogo aberta" #: f.file.cc:1545 msgid "Copy or Move Image File" msgstr "Copiar ou Mover arquivo de imagem" #: f.file.cc:1587 msgid "New Location:" msgstr "Nova Localidade:" #: f.file.cc:1592 msgid "copy (duplicate file)" msgstr "copiar (duplicar arquivo)" #: f.file.cc:1593 msgid "move (remove original)" msgstr "mover (remove arquivo original)" #: f.file.cc:1634 f.process.cc:573 f.process.cc:1503 f.process.cc:2625 msgid "Select directory" msgstr "Selecionar pasta" #: f.file.cc:1672 msgid "new location is not a directory" msgstr "nova localidade não é um diretório" #: f.file.cc:1713 f.file.cc:1967 #, c-format msgid "" "delete failed: \n" " %s" msgstr "" "exclusão falhou: \n" " %s" #: f.file.cc:1852 f.widgets.cc:442 msgid "Delete/Trash Image File" msgstr "Excluir/Descartar Arquivo de Imagem" #: f.file.cc:1921 msgid "GTK g_file_trash() function failed" msgstr "Função GTK g_file_trash() falhou" #: f.file.cc:1940 msgid "not a known image file" msgstr "arquivo de imagem desconhecido" #: f.file.cc:1946 msgid "Delete read-only file?" msgstr "Excluir arquivo somente leitura?" #: f.file.cc:1948 msgid "Trash read-only file?" msgstr "Descartar arquivos somente leitura?" #: f.file.cc:1996 msgid "no more images" msgstr "imagens esgotadas" #: f.file.cc:2234 msgid "Save Image File" msgstr "Salvar Arquivo-imagem" #: f.file.cc:2244 msgid "new version" msgstr "nova versão" #: f.file.cc:2245 msgid "new file ..." msgstr "novo arquivo ..." #: f.file.cc:2246 msgid "replace file" msgstr "substituir arquivo" #: f.file.cc:2253 f.file.cc:2826 msgid "save as new file name or type" msgstr "salvar com novo nome ou tipo de arquivo" #: f.file.cc:2255 msgid "replace old file (OVERWRITE)" msgstr "substituir arquivo antigo (SOBRESCREVER)" #: f.file.cc:2292 f.file.cc:2585 msgid "cannot save as RAW type" msgstr "não foi possível salvar como RAW" #: f.file.cc:2400 msgid "too many file versions: 99" msgstr "versões de arquivo extrapolaram: 99" #: f.file.cc:2579 msgid "" "Transparency map will be lost.\n" "save to PNG file to retain." msgstr "" "Mapa de Transparência será perdido. \n" "salve em arquivo PNG para mantê-lo." #: f.file.cc:2663 msgid "save anyway" msgstr "salvar assim mesmo" #: f.file.cc:2732 msgid "Unable to copy EXIF/IPTC data" msgstr "Incapaz de copiar dados EXIF/IPTC" #: f.file.cc:2844 f.file.cc:2847 msgid "make current" msgstr "tornar atual" #: f.file.cc:2845 msgid "(new file becomes current file)" msgstr "(novo arquivo se torna arquivo atual)" #: f.file.cc:2958 #, c-format msgid "" "Overwrite file? \n" " %s" msgstr "" "Sobrescrever arquivo? \n" " %s" #: f.file.cc:3153 f.widgets.cc:599 msgid "Quick Start" msgstr "Guia Rápido" #: f.file.cc:3156 f.widgets.cc:600 msgid "User Guide" msgstr "Guia de usuário" #: f.file.cc:3159 f.widgets.cc:601 msgid "User Guide Changes" msgstr "Mudanças no Guia de Usuários" #: f.file.cc:3162 f.widgets.cc:602 msgid "README" msgstr "LEIAME" #: f.file.cc:3165 f.widgets.cc:603 msgid "Change Log" msgstr "Relatório de mudanças" #: f.file.cc:3168 f.widgets.cc:604 msgid "Log File" msgstr "Arquivo de Log" #: f.file.cc:3171 f.widgets.cc:605 msgid "Translations" msgstr "Traduções" #: f.file.cc:3174 f.widgets.cc:606 msgid "Home Page" msgstr "Página na web" #: f.file.cc:3177 f.widgets.cc:607 msgid "About" msgstr "Sobre" #: f.file.cc:3183 f.widgets.cc:639 f.widgets.cc:663 f.widgets.cc:676 #: f.widgets.cc:690 fotoxx.h:1223 msgid "Help" msgstr "Ajuda" #: f.file.cc:3388 f.file.cc:3393 f.file.cc:3463 #, c-format msgid "file type not supported: %s" msgstr "tipo de arquivo não suportado: %s" #: f.gallery.cc:1221 msgid "GoTo" msgstr "IrPara" #: f.gallery.cc:1227 f.widgets.cc:655 msgid "Sort" msgstr "Ordenar" #: f.gallery.cc:1233 f.widgets.cc:653 msgid "Zoom+" msgstr "Zoom+" #: f.gallery.cc:1243 f.widgets.cc:654 msgid "Zoom-" msgstr "Zoom-" #: f.gallery.cc:1255 msgid "Row Up" msgstr "Subir Linha" #: f.gallery.cc:1263 msgid "Row Down" msgstr "Descer Linha" #: f.gallery.cc:1271 f.widgets.cc:658 msgid "Page Up" msgstr "Subir Página" #: f.gallery.cc:1279 f.widgets.cc:659 msgid "Page Down" msgstr "Descer Página" #: f.gallery.cc:1287 f.widgets.cc:656 msgid "First" msgstr "Primeiro" #: f.gallery.cc:1294 f.widgets.cc:657 msgid "Last" msgstr "Último" #: f.gallery.cc:1425 f.gallery.cc:1445 msgid "recent images" msgstr "imagens recentes" #: f.gallery.cc:1426 f.gallery.cc:1450 msgid "newest images" msgstr "imagens mais novas" #: f.gallery.cc:1504 msgid "no albums found" msgstr "nenhum álbum encontrado" #: f.gallery.cc:1541 msgid "Albums cannot be sorted" msgstr "Álbuns não podem ser ordenados" #: f.gallery.cc:1545 msgid "" " Reset all galleries\n" " to file name ascending" msgstr "" " Redefinir todas as galerias\n" " colocando nomes em ordem ascendente" #: f.gallery.cc:1564 msgid "Gallery Sort" msgstr "Ordenar Galeria" #: f.gallery.cc:1568 msgid "File Name" msgstr "Nome de Arquivo" #: f.gallery.cc:1569 msgid "File Mod Date/Time" msgstr "Data/Hora da Modificação do Arquivo" #: f.gallery.cc:1570 msgid "Photo Date/Time (EXIF)" msgstr "Foto Data/Hora (EXIF)" #: f.gallery.cc:1572 msgid "ascending" msgstr "ascendente" #: f.gallery.cc:1573 msgid "descending" msgstr "descendente" #: f.gallery.cc:2925 f.gallery.cc:2929 msgid "Image File" msgstr "Arquivo de Imagem" #: f.gallery.cc:2927 msgid "select thumbnail" msgstr "selecionar miniatura" #: f.gallery.cc:3037 fotoxx.h:1276 msgid "Select Files" msgstr "Selecionar arquivos" #: f.gallery.cc:3097 #, c-format msgid "exceed %d selected files" msgstr "excedido %d arquivos selecionados" #: f.gallery.cc:3457 msgid "" "Click a list position. Click a gallery thumbnail for the new bookmark.\n" "Bookmark for thumbnail will be added. Change the name and press [Rename]." msgstr "" "Clique numa posição da lista. Clique em uma miniatura da galeria para o novo " "favorito. \n" "O favorito da miniatura será adicionado. Mude o nome e pressione [Renomear]." #: f.gallery.cc:3484 f.gallery.cc:3734 msgid "Edit Bookmarks" msgstr "Editar Favoritos" #: f.gallery.cc:3662 msgid "unable to save bookmarks file" msgstr "incapaz de salvar arquivo de favoritos" #: f.gallery.cc:3734 msgid "Go To Bookmark" msgstr "Ir para Favorito" #: f.mashup.cc:170 msgid "Mashup layout and background image" msgstr "Leiaute de Mistura e imagem de fundo" #: f.mashup.cc:213 msgid "choose an image file" msgstr "escolher um arquivo de imagem" #: f.mashup.cc:214 msgid "use current image file" msgstr "usar arquivo de imagem atual" #: f.mashup.cc:215 msgid "specify layout size and color" msgstr "especificar tamanho de leiaute e cor" #: f.mashup.cc:216 msgid "open a Mashup project file" msgstr "abrir um arquivo de projeto de Mistura" #: f.mashup.cc:259 msgid "no current file" msgstr "nenhum arquivo sendo usado" #: f.mashup.cc:291 msgid "Make Layout Image" msgstr "Criar Imagem Leiaute" #: f.mashup.cc:293 msgid "project name" msgstr "nome do projeto" #: f.mashup.cc:319 msgid "supply a project name" msgstr "forneça um nome para o projeto" #: f.mashup.cc:411 msgid "Edit Images" msgstr "Editar imagens" #: f.mashup.cc:412 f.mashup.cc:2450 msgid "Edit Text" msgstr "Editar texto" #: f.mashup.cc:413 msgid "Edit Line" msgstr "Editar Linha" #: f.mashup.cc:414 msgid "Rescale" msgstr "Reescalar" #: f.mashup.cc:419 msgid "add or edit images" msgstr "adicionar ou editar imagens" #: f.mashup.cc:421 msgid "add or edit text" msgstr "adicionar ou editar texto" #: f.mashup.cc:423 msgid "add or edit lines/arrows" msgstr "adicionar ou editar linhas/setas" #: f.mashup.cc:425 msgid "change project scale" msgstr "mudar escala de projeto" #: f.mashup.cc:427 msgid "project complete" msgstr "projeto finalizado" #: f.mashup.cc:429 msgid "cancel project" msgstr "cancelar projeto" #: f.mashup.cc:447 msgid "rescale project" msgstr "reescalar projeto" #: f.mashup.cc:506 msgid "save Mashup output file" msgstr "salvar arquivo de saída de Mistura" #: f.mashup.cc:526 msgid "save Mashup project file?" msgstr "salvar arquivo de projeto da Mistura?" #: f.mashup.cc:538 msgid "delete Mashup project file?" msgstr "excluir arquivo de projeto da Mistura?" #: f.mashup.cc:597 msgid "Open Project" msgstr "Abrir projeto" #: f.mashup.cc:626 #, c-format msgid "" "layout image file missing: \n" " %s" msgstr "" "arquivo-imagem de leiaute faltando: \n" " %s" #: f.mashup.cc:683 #, c-format msgid "" "overlay image file missing: \n" " %s" msgstr "" "arquivo-imagem sobreposta faltando: \n" " %s" #: f.mashup.cc:989 msgid "project file is defective" msgstr "arquivo de projeto está defeituoso" #: f.mashup.cc:1019 msgid "Save Project" msgstr "Salvar projeto" #: f.mashup.cc:1160 msgid "layout exceeds 2 gigabytes" msgstr "layout excede 2 gigabytes" #: f.mashup.cc:1234 msgid "Click image to select, drag image to move." msgstr "Clique na imagem para selecionar, arrastar imagem para mover." #: f.mashup.cc:1235 msgid "Make black margins transparent" msgstr "Tornar margens pretas em transparentes" #: f.mashup.cc:1274 msgid "Current image:" msgstr "Imagem sendo usada:" #: f.mashup.cc:1278 msgid "Cycle through images:" msgstr "Período entre imagens:" #: f.mashup.cc:1285 msgid "Scale" msgstr "Escala" #: f.mashup.cc:1294 msgid "Stacking Order" msgstr "Ordem de Empilhamento" #: f.mashup.cc:1295 msgid "Raise" msgstr "Aumentar" #: f.mashup.cc:1296 msgid "Lower" msgstr "Diminuir" #: f.mashup.cc:1299 msgid "Base Transparency" msgstr "Transparência Base" #: f.mashup.cc:1303 msgid "Var. Transparency" msgstr "Transparência Variável" #: f.mashup.cc:1304 msgid "Paint" msgstr "Pintura" #: f.mashup.cc:1307 msgid "Bend and fine-align" msgstr "Curvatura e ajuste fino" #: f.mashup.cc:1308 f.widgets.cc:634 msgid "Warp" msgstr "Distorção" #: f.mashup.cc:1317 zfuncs.cc:11351 zfuncs.cc:11360 msgid "Margins" msgstr "Margens" #: f.mashup.cc:1318 msgid "Hard" msgstr "Rígido" #: f.mashup.cc:1319 f.repair.cc:4410 msgid "Blend" msgstr "Mistura" #: f.mashup.cc:1333 msgid "add images to layout" msgstr "adicionar imagens ao leiaute" #: f.mashup.cc:1570 msgid "Paint Image Transparencies" msgstr "Aplicar Transparências na Imagem" #: f.mashup.cc:1588 msgid "Gradual" msgstr "Gradual" #: f.mashup.cc:1590 fotoxx.h:1255 msgid "Power" msgstr "Força" #: f.mashup.cc:1823 msgid "Pull on the image with the mouse." msgstr "Puxe a imagem com o mouse." #: f.mashup.cc:1839 msgid "Warp Image" msgstr "Distorcer Imagem" #: f.mashup.cc:1845 f.warp.cc:1383 msgid "warp span" msgstr "distorção em alcance" #: f.mashup.cc:2281 #, c-format msgid "exceeded %d images" msgstr "excederam%d imagens" #: f.mashup.cc:2423 msgid "Enter text, [Add] to layout, edit properties." msgstr "Digite o texto, [Adicionar] para o leiaute, editar propriedades." #: f.mashup.cc:2503 msgid "Text File:" msgstr "Arquivo Texto:" #: f.mashup.cc:2507 msgid "add entered text to layout" msgstr "adicionar texto inserido ao leiaute" #: f.mashup.cc:2641 msgid "click position to add text" msgstr "clique na posição para adicionar texto" #: f.mashup.cc:2647 #, c-format msgid "exceeded %d text entries" msgstr "excederam %d entradas de texto" #: f.mashup.cc:2652 f.widgets.cc:485 msgid "Add Text" msgstr "Escrever Texto" #: f.mashup.cc:2761 msgid "Set line properties, [Add] to layout, edit." msgstr "Definir propriedades de linha, [Adicionar] ao leiaute, editar." #: f.mashup.cc:2785 msgid "Edit Line/Arrow" msgstr "Editar Linha/Seta" #: f.mashup.cc:2840 msgid "add line/arrow to layout" msgstr "adicionar linha/seta ao leiaute" #: f.mashup.cc:2931 msgid "click position to add line" msgstr "clique na posição para adicionar linha" #: f.mashup.cc:2937 #, c-format msgid "exceeded %d line entries" msgstr "excederam%d linhas de entrada" #: f.mashup.cc:2942 f.widgets.cc:486 msgid "Add Line" msgstr "Adicionar Linha" #: f.mashup.cc:4189 msgid "Image Montage" msgstr "Colagem de Imagens" #: f.mashup.cc:4198 msgid "Frame Width" msgstr "Largura do Quadro" #: f.mashup.cc:4201 f.mashup.cc:4209 msgid "Margin" msgstr "Margem" #: f.mashup.cc:4206 msgid "Image Columns" msgstr "Colunas de Imagens" #: f.mashup.cc:4223 #, c-format msgid "exceed %d rows" msgstr "excedido %d linhas" #: f.mashup.cc:4319 msgid "Optimize" msgstr "Otimizar" #: f.mashup.cc:4327 #, c-format msgid "column difference: %d pixels" msgstr "colunas diferem: %d pixels" #: f.mashup.cc:4385 #, c-format msgid "" "column difference: %d pixels \n" "Make columns even?" msgstr "" "colunas diferem: %d pixels \n" "Alinhar colunas?" #: f.mashup.cc:4407 msgid "Save with unique montage name" msgstr "Salvar colagem com um nome único" #: f.mashup.cc:4409 msgid "unique name:" msgstr "nome único:" #: f.mashup.cc:4411 msgid "create image map" msgstr "criar mapa de imagem" #: f.mashup.cc:4421 f.meta.cc:8637 msgid "supply a reasonable name" msgstr "fornecer um nome razoável" #: f.mashup.cc:4432 msgid "save montage" msgstr "salvar colagem" #: f.mashup.cc:4452 #, c-format msgid "map file saved: %s" msgstr "arquivo de mapa salvo como: %s" #: f.mashup.cc:4492 #, c-format msgid "%d max images exceeded" msgstr "máximo de %d imagens excedido" #: f.mashup.cc:4566 f.mashup.cc:4572 #, c-format msgid "image frame is too large: %d x %d" msgstr "quadro de imagem muito grande: %d x %d" #: f.mashup.cc:4873 #, c-format msgid "montage map file not found: %s" msgstr "arquivo mapa de colagem não encontrado: %s" #: f.mashup.cc:4877 #, c-format msgid "montage map file invalid: %s" msgstr "arquivo mapa de colagem inválido: %s" #: f.meta.cc:242 msgid "Add Metadata Items" msgstr "Adicionar Itens de Metadados" #: f.meta.cc:246 f.meta.cc:1583 f.meta.cc:3104 msgid "click to select" msgstr "clique para selecionar" #: f.meta.cc:251 msgid "click to unselect" msgstr "clique para deselecionar" #: f.meta.cc:475 msgid "Extras" msgstr "Extras" #: f.meta.cc:475 f.meta.cc:655 f.widgets.cc:754 msgid "View Metadata" msgstr "Ver metadados" #: f.meta.cc:813 f.widgets.cc:450 f.widgets.cc:755 msgid "Edit Metadata" msgstr "Editar Metadados" #: f.meta.cc:816 msgid "save metadata to file" msgstr "salvar metadados em arquivo" #: f.meta.cc:825 msgid "Image Date" msgstr "Data da Imagem" #: f.meta.cc:828 msgid "Time" msgstr "Hora" #: f.meta.cc:836 msgid "Rating (stars):" msgstr "Avaliação (estrelas):" #: f.meta.cc:848 msgid "Caption" msgstr "Legenda" #: f.meta.cc:853 msgid "Comments" msgstr "Comentários" #: f.meta.cc:860 msgid "Location" msgstr "Localização" #: f.meta.cc:863 msgid "Country" msgstr "País" #: f.meta.cc:886 msgid "Image Tags" msgstr "Etiquetas da imagem" #: f.meta.cc:891 f.meta.cc:1992 msgid "Recent Tags" msgstr "etiquetas recentes" #: f.meta.cc:896 f.meta.cc:1998 msgid "Enter New Tag" msgstr "Entre com Nova Etiqueta" #: f.meta.cc:902 f.meta.cc:2004 f.meta.cc:4565 msgid "Matching Tags" msgstr "Etiquetas Correspondentes" #: f.meta.cc:909 f.meta.cc:2013 f.meta.cc:2511 f.meta.cc:4571 msgid "Defined Tags Category" msgstr "Categorias de Etiquetas Definidas" #: f.meta.cc:916 msgid "search known locations" msgstr "procurar localidades conhecidas" #: f.meta.cc:917 msgid "search using web service" msgstr "procurar utilizando web service" #: f.meta.cc:918 f.meta.cc:2020 msgid "create tag categories and tags" msgstr "criar etiquetas e categorias de etiqueta" #: f.meta.cc:1368 f.meta.cc:3673 f.meta.cc:7231 #, c-format msgid "bad latitude/longitude: %s %s" msgstr "latitude/longitude inválido: %s %s" #: f.meta.cc:1423 fotoxx.h:1234 msgid "Manage Tags" msgstr "Gerenciar Etiquetas" #: f.meta.cc:1423 msgid "orphan tags" msgstr "etiquetas órfãs" #: f.meta.cc:1427 msgid "category" msgstr "categoria" #: f.meta.cc:1430 msgid "tag" msgstr "etiqueta" #: f.meta.cc:1437 msgid "Defined Tags:" msgstr "etiquetas definidas:" #: f.meta.cc:1579 f.widgets.cc:451 f.widgets.cc:756 msgid "Edit Any Metadata" msgstr "Editar qualquer Metadado" #: f.meta.cc:1579 f.meta.cc:3099 msgid "Full List" msgstr "Lista Completa" #: f.meta.cc:1592 f.meta.cc:3115 msgid "key name" msgstr "nome chave" #: f.meta.cc:1595 f.meta.cc:3116 msgid "key value" msgstr "valor chave" #: f.meta.cc:1677 f.meta.cc:3259 msgid "" "The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names" msgstr "" "O comando: $ man Image::ExifTool::TagNames \n" "mostrará mais de 15000 \"padrões\" para nomes de etiqueta" #: f.meta.cc:1777 f.widgets.cc:452 msgid "Delete Metadata" msgstr "Excluir Metadados" #: f.meta.cc:1783 fotoxx.h:1179 msgid "All" msgstr "Todos" #: f.meta.cc:1784 msgid "One Key:" msgstr "Uma chave:" #: f.meta.cc:1968 f.widgets.cc:568 msgid "Batch Add/Remove Tags" msgstr "Adicionar/Remover Etiqueras em Lote" #: f.meta.cc:1981 msgid "tags to add" msgstr "etiquetas a adicionar" #: f.meta.cc:1982 msgid "tags to remove" msgstr "etiquetas a remover" #: f.meta.cc:2095 #, c-format msgid "" "%s \n" " too many tags" msgstr "" "%s \n" " etiquetas demasiadas" #: f.meta.cc:2108 msgid "repeat with same files?" msgstr "repetir com os mesmos arquivos?" #: f.meta.cc:2290 msgid "specify files and tags" msgstr "especificar arquivos e etiquetas" #: f.meta.cc:2490 f.widgets.cc:569 msgid "Batch Rename Tags" msgstr "Renomear Etiquetas em Lote" #: f.meta.cc:2500 msgid "(click defined tag)" msgstr "(clique na etiqueta definida)" #: f.meta.cc:2502 f.process.cc:749 msgid "Rename to" msgstr "Renomear para" #: f.meta.cc:2519 msgid "old tag name >> new tag name" msgstr "nome de etiqueta antigo >> nome de etiqueta novo" #: f.meta.cc:2576 #, c-format msgid "" "%d tags to rename \n" "in %d image files. \n" "Proceed?" msgstr "" "%d etiquetas para renomear \n" "em %d arquivos de imagem. \n" "Proceder?" #: f.meta.cc:2720 msgid "max tags exceeded" msgstr "número máximo de etiquetas excedido" #: f.meta.cc:2782 f.widgets.cc:570 msgid "Batch Photo Date/Time" msgstr "Fotos em Lote Data/Hora" #: f.meta.cc:2790 msgid "set a new date/time:" msgstr "definir uma nova data/hora:" #: f.meta.cc:2798 msgid "shift existing date/time:" msgstr "deslocar data/hora existente:" #: f.meta.cc:2824 msgid "test: show changes, do not update files" msgstr "testar: mostrar alterações, sem atualizar arquivos" #: f.meta.cc:2850 f.meta.cc:3190 f.meta.cc:3368 msgid "no files selected" msgstr "nenhum arquivo selecionado" #: f.meta.cc:2880 f.meta.cc:2888 f.meta.cc:2894 msgid "invalid date/time format" msgstr "formato data/hora inválido" #: f.meta.cc:3099 f.widgets.cc:571 msgid "Batch Add/Change Metadata" msgstr "Adicionar/Modificar Metadados em Lote" #: f.meta.cc:3184 msgid "enter key names" msgstr "inserir nome de chave" #: f.meta.cc:3354 f.widgets.cc:572 msgid "Batch Report Metadata" msgstr "Relatório de Metadados em Lote" #: f.meta.cc:3498 f.widgets.cc:573 msgid "Batch Geotags" msgstr "Geotags em Lote" #: f.meta.cc:3528 msgid "location" msgstr "localidade" #: f.meta.cc:3531 msgid "country" msgstr "país" #: f.meta.cc:3659 msgid "" "data is incomplete \n" " proceed?" msgstr "" "Dados incompletos \n" " continuar?" #: f.meta.cc:3745 msgid "Report Image Locations" msgstr "Relatar Localização de Imagens" #: f.meta.cc:3746 msgid "Group by country" msgstr "Agrupar por país" #: f.meta.cc:3747 msgid "Group by country/location" msgstr "Agrupar por país/localização" #: f.meta.cc:3748 msgid "Group by country/location/date" msgstr "Agrupar por país/localização/data" #: f.meta.cc:3749 msgid "Group by date/country/location" msgstr "Agrupar por data/país/localização" #: f.meta.cc:3752 msgid "Combine within" msgstr "Combinar interiormente" #: f.meta.cc:3754 msgid "days" msgstr "dias" #: f.meta.cc:3860 f.widgets.cc:1026 f.widgets.cc:1048 msgid "Image Locations" msgstr "Localidade de Imagem" #: f.meta.cc:4137 msgid "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" msgstr "Jan Fev Mar Abr Mai Jun Jul Ago Set Out Nov Dez" #: f.meta.cc:4489 msgid "Search Image Metadata" msgstr "Procurar por Metadados de Imagem" #: f.meta.cc:4493 msgid "images to search:" msgstr "imagens para busca:" #: f.meta.cc:4494 msgid "all" msgstr "todos" #: f.meta.cc:4495 msgid "current set only" msgstr "configuração atual apenas" #: f.meta.cc:4498 msgid "matching images:" msgstr "imagens correspondentes:" #: f.meta.cc:4499 msgid "new set" msgstr "nova configuração" #: f.meta.cc:4500 msgid "add to set" msgstr "adicionar à configuração" #: f.meta.cc:4501 msgid "remove" msgstr "remover" #: f.meta.cc:4504 msgid "report type:" msgstr "tipo relatório:" #: f.meta.cc:4505 msgid "gallery" msgstr "galeria" #: f.meta.cc:4509 msgid "date range" msgstr "variação de data" #: f.meta.cc:4514 msgid "photo date" msgstr "data da foto" #: f.meta.cc:4515 msgid "file date" msgstr "data do arquivo" #: f.meta.cc:4516 msgid "(yyyy-mm-dd)" msgstr "(aaaa-mm-dd)" #: f.meta.cc:4519 msgid "stars range" msgstr "variação de estrelas" #: f.meta.cc:4522 msgid "last version only" msgstr "última versão apenas" #: f.meta.cc:4524 msgid "all/any" msgstr "todos/qualquer" #: f.meta.cc:4527 msgid "search tags" msgstr "procurar etiquetas" #: f.meta.cc:4533 msgid "search text" msgstr "procurar texto" #: f.meta.cc:4539 msgid "search files" msgstr "procurar arquivos" #: f.meta.cc:4545 msgid "search locations" msgstr "procurar localidades" #: f.meta.cc:4549 msgid "enter cities, countries" msgstr "entre com cidades, países" #: f.meta.cc:4554 msgid "search other metadata" msgstr "procurar outros metadados" #: f.meta.cc:4561 msgid "Enter Search Tag" msgstr "Entre com a Etiqueta de Busca" #: f.meta.cc:4837 msgid "" "to remove images from current set, \n" "search current set" msgstr "" "para remover imagens da configuração atual, \n" "busque na configuração atual" #: f.meta.cc:4844 msgid "" "to add images to current set, \n" "search all images" msgstr "" "para adicionar imagens à configuração atual, \n" "busque todas as imagens" #: f.meta.cc:4890 #, c-format msgid "" "search dates not reasonable \n" " %s %s" msgstr "" "datas de procura não fazem sentido \n" " %s %s" #: f.meta.cc:4915 msgid "stars range not reasonable" msgstr "varição de avaliação não é razoável" #: f.meta.cc:5129 #, c-format msgid "images added: %d removed: %d new count: %d" msgstr "imagens adicionadas: %d removidas: %d nova contagem: %d" #: f.meta.cc:5132 msgid "no changes made" msgstr "nenhuma mudança feita" #: f.meta.cc:5303 msgid "" "These items are always reported: \n" "date, stars, tags, caption, comment" msgstr "" "Esses itens são sempre relatados: \n" "data, estrelas, etiquetas, legenda, comentário" #: f.meta.cc:5328 msgid "Additional Items for Report" msgstr "Itens Adicionais para Relatar" #: f.meta.cc:5335 msgid "Keyword" msgstr "Palavra chave" #: f.meta.cc:5349 msgid "Match Criteria" msgstr "Equiparar Critério" #: f.meta.cc:5455 #, c-format msgid "bad number: %s" msgstr "número ruim: %s" #: f.meta.cc:5712 msgid "date format is YYYY-MM-DD" msgstr "formato da data é AAAA-MM-DD" #: f.meta.cc:5716 msgid "date is invalid" msgstr "data inválida" #: f.meta.cc:5750 msgid "time format is HH:MM [:SS]" msgstr "formato da hora é HH:MM [: SS]" #: f.meta.cc:5754 msgid "time is invalid" msgstr "hora inválida" #: f.meta.cc:6851 msgid "not found" msgstr "não encontrado" #: f.meta.cc:6852 msgid "location and country required" msgstr "localização e país requerido" #: f.meta.cc:7111 msgid "choose location" msgstr "escolher localização" #: f.meta.cc:7408 msgid "Set Map Markers" msgstr "Definir Marcadores no Mapa" #: f.meta.cc:7409 msgid "mark all image files" msgstr "marcar todas as imagens" #: f.meta.cc:7410 msgid "mark current gallery" msgstr "marcar galeria atual" #: f.meta.cc:7510 msgid "choose map file" msgstr "escolher arquivo de mapa" #: f.meta.cc:7652 msgid "" "fotoxx-maps package not installed \n" "(see https://kornelix.net)" msgstr "" "pacote fotoxx-maps não instalado \n" "(leia https://kornelix.net" #: f.meta.cc:7657 #, c-format msgid "map file %s is missing" msgstr "arquivo de mapa file %s faltando" #: f.meta.cc:7661 #, c-format msgid "" "map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f" msgstr "" "dados latitude/longitude do mapa não fazem sentido \n" " %.3f %.3f %.3f %.3f" #: f.meta.cc:7981 f.meta.cc:8508 msgid "No matching images found" msgstr "Nenhuma imagem encontrada" #: f.meta.cc:8073 msgid "Set Map Source" msgstr "Definir Fonte de Mapa" #: f.meta.cc:8584 msgid "Net Map Locations" msgstr "Localidades do Mapa de Rede" #: f.meta.cc:8589 msgid "map location:" msgstr "localidade do mapa:" #: f.process.cc:123 f.widgets.cc:559 msgid "Batch Convert" msgstr "Conversão em Lote" #: f.process.cc:135 msgid "Sequence Numbers" msgstr "Números Sequenciais" #: f.process.cc:137 msgid "base" msgstr "base" #: f.process.cc:140 msgid "adder" msgstr "somador" #: f.process.cc:145 msgid "New Location" msgstr "Nova Localidade" #: f.process.cc:150 msgid "New File Type" msgstr "Novo Tipo de Arquivo" #: f.process.cc:154 f.process.cc:162 f.process.cc:2538 msgid "no change" msgstr "sem mudanças" #: f.process.cc:157 f.process.cc:2533 msgid "max. Width" msgstr "Largura max." #: f.process.cc:166 msgid "Delete Originals" msgstr "Excluir Originais" #: f.process.cc:167 f.process.cc:754 msgid "Copy Metadata" msgstr "Copiar Metadados" #: f.process.cc:171 f.process.cc:756 f.process.cc:1320 f.repair.cc:121 #: f.widgets.cc:492 msgid "Sharpen" msgstr "Aguçar" #: f.process.cc:181 f.process.cc:757 msgid "Overlay Image" msgstr "Imagem Sobreposta" #: f.process.cc:184 msgid "Width %" msgstr "Largura %" #: f.process.cc:189 msgid "Position" msgstr "Posição" #: f.process.cc:201 msgid "Make constant size for screen:" msgstr "Fixar o tamanho para a tela:" #: f.process.cc:209 msgid "" "plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s" msgstr "" "extensões: (ano mês dia nome-antigo sequência) $yyyy $mm $dd $oldname $s" #: f.process.cc:331 #, c-format msgid "file type not supported: %s \n" msgstr "tipo de arquivo não suportado: %s \n" #: f.process.cc:471 f.process.cc:2588 msgid "cannot create new file" msgstr "impossível criar novo arquivo" #: f.process.cc:517 msgid "updating albums ..." msgstr "atualizando álbuns ..." #: f.process.cc:658 #, c-format msgid "invalid plugin: %s" msgstr "extensão inválida: %s" #: f.process.cc:665 msgid "you must use either $s or $oldname" msgstr "você deve usar $s ou $oldname" #: f.process.cc:670 msgid "$s plugin needs base and adder" msgstr "extensão $s precisa de base e somador" #: f.process.cc:675 msgid "base and adder need $s plugin" msgstr "base e somador requerem extensão $s" #: f.process.cc:697 #, c-format msgid "max. size %d x %d is not reasonable" msgstr "tamanho max. %d x %d não é razoável" #: f.process.cc:704 msgid "specify overlay image file" msgstr "especifique arquivo para imagem sobreposta" #: f.process.cc:720 msgid "specify overlay position" msgstr "especifique posição de sobreposição" #: f.process.cc:748 #, c-format msgid "Convert %d image files" msgstr "Converter %d arquivos de imagem" #: f.process.cc:750 msgid "Convert to" msgstr "Converter para" #: f.process.cc:751 msgid "Resize within" msgstr "Redimensionar dentro de" #: f.process.cc:752 msgid "Output to" msgstr "Saída para" #: f.process.cc:758 msgid "*** Delete Originals ***" msgstr "*** Excluir Originais ***" #: f.process.cc:759 msgid "*** Replace Originals ***" msgstr "*** Substituir Originais ***" #: f.process.cc:760 msgid "PROCEED?" msgstr "PROCEDER?" #: f.process.cc:913 f.widgets.cc:560 msgid "Batch Upright" msgstr "Orientação em Lote" #: f.process.cc:919 msgid "Survey all files" msgstr "Examinar todos arquivos" #: f.process.cc:956 msgid "file cannot be read" msgstr "arquivo não pode ser lido" #: f.process.cc:1072 msgid "cannot select both options" msgstr "não é possível selecionar ambas as opções" #: f.process.cc:1114 f.widgets.cc:561 msgid "Batch Delete/Trash" msgstr "Excluir/Descartar em Lote" #: f.process.cc:1119 msgid "delete" msgstr "excluir" #: f.process.cc:1122 msgid "trash" msgstr "lixo" #: f.process.cc:1260 msgid "Batch Convert RAW Files (Raw Therapee)" msgstr "Conversão em Lote de Arquivos RAW (RAW Therapee)" #: f.process.cc:1290 msgid "use libraw" msgstr "usar libraw" #: f.process.cc:1291 msgid "use Raw Therapee" msgstr "usar Raw Therapee" #: f.process.cc:1298 msgid "output location" msgstr "localização de saída" #: f.process.cc:1303 msgid "output file type" msgstr "tipo de arquivo de saída" #: f.process.cc:1322 msgid "amount" msgstr "quantia" #: f.process.cc:1325 msgid "threshold" msgstr "limiar" #: f.process.cc:1614 msgid "script file" msgstr "arquivo de rotina" #: f.process.cc:1627 msgid "begin making a script file" msgstr "iniciar a criação de um arquivo de rotina" #: f.process.cc:1630 msgid "finish making a script file" msgstr "terminar a criação do arquivo de rotina" #: f.process.cc:1636 msgid "execute a script file" msgstr "executar o arquivo de rotina" #: f.process.cc:1696 msgid "script already started" msgstr "rotina já foi iniciada" #: f.process.cc:1700 msgid "start a new script file" msgstr "iniciar um novo arquivo de rotina" #: f.process.cc:1723 msgid "perform edits to be included in the script file" msgstr "executar edições a serem incluídas no arquivo de rotina" #: f.process.cc:1741 msgid "script file error" msgstr "erro no arquivo de rotina" #: f.process.cc:1746 #, c-format msgid "%s added to script" msgstr "%s adicionado a rotina" #: f.process.cc:1759 msgid "no script file was started" msgstr "nenhum arquivo de rotina foi iniciado" #: f.process.cc:1767 msgid "script file closed" msgstr "arquivo de rotina finalizado" #: f.process.cc:1825 msgid "open script file" msgstr "abrir arquivo de rotina" #: f.process.cc:1833 #, c-format msgid "" "script file: %s \n" " %s" msgstr "" "arquivo de rotina: %s \n" " %s" #: f.process.cc:1855 f.process.cc:1904 #, c-format msgid "" "open failure: %s \n" " %s" msgstr "" "erro ao abrir: %s \n" " %s" #: f.process.cc:1876 #, c-format msgid "unknown edit function: %s" msgstr "função de edição desconhecida: %s" #: f.process.cc:1885 #, c-format msgid "load widgets failed: %s" msgstr "carregamento de dispositivo falhou: %s" #: f.process.cc:1913 #, c-format msgid "script file format error: %s" msgstr "erro no formato do arquivo de rotina: %s" #: f.process.cc:1949 msgid "growisofs not installed" msgstr "growisofs não instalado" #: f.process.cc:2001 msgid "no DVD/BlueRay device found" msgstr "nenhum dispositivo DVD/BluRay encontrado" #: f.process.cc:2024 f.widgets.cc:564 msgid "Burn Images to DVD/BlueRay" msgstr "Gravar Imagens em DVD/BluRay" #: f.process.cc:2029 msgid "Select device" msgstr "Selecionar dispositivo" #: f.process.cc:2136 f.widgets.cc:565 msgid "Find Duplicate Images" msgstr "Encontrar imagens Duplicadas" #: f.process.cc:2138 msgid "Thumbnail size" msgstr "Tamanho de miniatura" #: f.process.cc:2142 msgid "pixel difference" msgstr "diferença de pixel" #: f.process.cc:2145 msgid "pixel count" msgstr "contagem de pixel" #: f.process.cc:2152 msgid "Duplicates:" msgstr "Duplicidades:" #: f.process.cc:2170 #, c-format msgid "only %d image thumbnails found" msgstr "apenas %d miniaturas de imagem encontrados" #: f.process.cc:2374 f.widgets.cc:355 msgid "Create a file of selected image files" msgstr "Criar um arquivo com imagens selecionadas" #: f.process.cc:2398 f.process.cc:2467 msgid "Output File" msgstr "Arquivo de saída" #: f.process.cc:2418 msgid "no input files selected" msgstr "nenhum arquivo de entrada selecionado" #: f.process.cc:2424 msgid "no output file selected" msgstr "nenhum arquivo de saída selecionado" #: f.process.cc:2523 f.widgets.cc:567 msgid "Export Image Files" msgstr "Exportar Arquivos de Imagem" #: f.process.cc:2528 msgid "To Location" msgstr "Para Localização" #: f.process.cc:2563 msgid "file type not supported" msgstr "tipo de arquivo não suportado" #: f.process.cc:2643 msgid "location is not a directory" msgstr "localização não é um diretório" #: f.repair.cc:164 msgid "dark" msgstr "escuro" #: f.repair.cc:165 msgid "light" msgstr "claro" #: f.repair.cc:1193 msgid "Apply repeatedly while watching the image." msgstr "Aplicar repetidamente enquanto visualiza a imagem." #: f.repair.cc:1194 msgid "Measure" msgstr "Mensurar" #: f.repair.cc:1228 msgid "Noise Reduction" msgstr "Redução de ruído" #: f.repair.cc:1247 f.widgets.cc:479 f.widgets.cc:774 fotoxx.h:1218 msgid "Flatten" msgstr "Abrandar" #: f.repair.cc:1248 msgid "Median" msgstr "Mediano" #: f.repair.cc:1263 msgid "dark areas" msgstr "áreas escuras" #: f.repair.cc:1265 msgid "all areas" msgstr "todas as áreas" #: f.repair.cc:1856 msgid "Measure Noise" msgstr "Mensurar Ruído" #: f.repair.cc:1857 msgid "Move mouse in a monotone image area." msgstr "Mova o mouse em uma área monotom na imagem ." #: f.repair.cc:2165 msgid "" "Method 1:\n" " Left-click on red-eye to darken.\n" "Method 2:\n" " Drag down and right to enclose red-eye.\n" " Left-click on red-eye to darken.\n" "Undo red-eye:\n" " Right-click on red-eye." msgstr "" "Método 1:\n" " Clique com o botão esquerdo no olho vermelho a escurecer.\n" "Método 2:\n" " Arraste e solte com o botão direito enquadrando o olho vermelho.\n" " Clique com o botão esquerdo no olho vermelho a escurecer.\n" "Retornar ao olho vermelho:\n" " Clique com o botão direito no olho vermelho." #: f.repair.cc:2181 msgid "Red Eye Reduction" msgstr "Redução de olhos vermelhos" #: f.repair.cc:2642 f.widgets.cc:496 msgid "Color Mode" msgstr "Modos de Cores" #: f.repair.cc:2645 msgid "reset" msgstr "redefinir" #: f.repair.cc:2646 msgid "black/white positive" msgstr "preto/branco positivo" #: f.repair.cc:2647 msgid "black/white negative" msgstr "preto/branco negativo" #: f.repair.cc:2648 msgid "color negative" msgstr "cor negativa" #: f.repair.cc:2649 msgid "RGB -> GBR" msgstr "RGB -> GBR" #: f.repair.cc:2650 msgid "sepia" msgstr "sepia" #: f.repair.cc:2861 msgid "Set color depth to 1-16 bits" msgstr "Configurar resolução de cor para 1-16 bits" #: f.repair.cc:2875 msgid "Set Color Depth" msgstr "Configurar resolução de cor" #: f.repair.cc:3025 f.widgets.cc:498 msgid "Shift Colors" msgstr "Deslocar Cores" #: f.repair.cc:3259 f.widgets.cc:499 msgid "Color Saturation" msgstr "Saturação de Cor" #: f.repair.cc:3448 msgid "+Brightness" msgstr "+Brilho" #: f.repair.cc:3449 msgid "+Red -Cyan" msgstr "+Vermelho -Ciano" #: f.repair.cc:3450 msgid "+Green -Magenta" msgstr "+Verde -Magenta" #: f.repair.cc:3451 msgid "+Blue -Yellow" msgstr "+Azul -Amarelo" #: f.repair.cc:3457 fotoxx.h:1261 msgid "Red" msgstr "Vermelho" #: f.repair.cc:3458 fotoxx.h:1220 msgid "Green" msgstr "Verde" #: f.repair.cc:3459 fotoxx.h:1186 msgid "Blue" msgstr "Azul" #: f.repair.cc:3761 msgid "Input color to match and adjust:" msgstr "Cor de Entrada a buscar e ajustar" #: f.repair.cc:3763 msgid "shift+click on image to select color" msgstr "shift+clique na imagem para selecionar cor" #: f.repair.cc:3766 f.repair.cc:7937 msgid "Match using:" msgstr "Comparar usando:" #: f.repair.cc:3767 msgid "Hue" msgstr "Matiz" #: f.repair.cc:3779 msgid "Output Color" msgstr "Cor de Saída" #: f.repair.cc:3802 msgid "Adjustment" msgstr "Ajuste" #: f.repair.cc:4320 msgid "Click image to select areas." msgstr "Clique na imagem para selecionar áreas." #: f.repair.cc:4352 msgid "Local Color" msgstr "Cor Local" #: f.repair.cc:4363 msgid "Metric:" msgstr "Métrica:" #: f.repair.cc:4786 msgid "Color Match Images" msgstr "Imagens para mesclagem de cor" #: f.repair.cc:4812 msgid "mouse radius for color sample" msgstr "raio do mouse para a amostra de cor" #: f.repair.cc:4814 f.repair.cc:4819 fotoxx.h:1250 msgid "Open" msgstr "Abrir" #: f.repair.cc:4815 msgid "image for source color" msgstr "imagem para fonte de cor" #: f.repair.cc:4817 msgid "click on image to get source color" msgstr "clique na imagem para obter a cor fonte" #: f.repair.cc:4820 msgid "image to set matching color" msgstr "imagem para mesclagem de cor" #: f.repair.cc:4822 msgid "click on image to set matching color" msgstr "clique na imagem para aplicar mesclagem de cor" #: f.repair.cc:4881 msgid "select source image color first" msgstr "selecione imagem para fonte de cor primeiro" #: f.repair.cc:5059 msgid "" " Adjust each RGB color to minimize \n" " color fringes at the image extremes. " msgstr "" " Ajustar cada cor RGB para minimizar \n" " franjas púrpuras nas extremidades da imagem. " #: f.repair.cc:5082 f.widgets.cc:504 msgid "Color Fringes" msgstr "Franjas Púrpuras" #: f.repair.cc:5240 msgid "" "1. Drag mouse to select. \n" "2. Erase. 3. Repeat. " msgstr "" "1. Arraste o mouse para selecionar. \n" "2. Apague. 3. Repita. " #: f.repair.cc:5262 f.widgets.cc:505 msgid "Smart Erase" msgstr "Apagador inteligente" #: f.repair.cc:5267 fotoxx.h:1259 msgid "Radius" msgstr "Raio" #: f.repair.cc:5269 f.widgets.cc:493 msgid "Blur" msgstr "Embaçar" #: f.repair.cc:5272 msgid "New Area" msgstr "Nova Área" #: f.repair.cc:5617 msgid "" "Draw a line across the image in \n" "direction of brightness change." msgstr "" "Desenhe uma linha na imagem em \n" "direção à mudança de brilho." #: f.repair.cc:5656 f.widgets.cc:506 msgid "Brightness Ramp" msgstr "Rampa de Brilho" #: f.repair.cc:6182 f.widgets.cc:507 msgid "Remove Dust" msgstr "Remover Sujeira" #: f.repair.cc:6186 msgid "spot size limit" msgstr "limite de tamanho de mancha" #: f.repair.cc:6189 msgid "max. brightness" msgstr "brilho máximo" #: f.repair.cc:6192 msgid "min. contrast" msgstr "contraste mínimo" #: f.repair.cc:6999 f.widgets.cc:510 msgid "Stuck Pixels" msgstr "Pixels Presos" #: f.repair.cc:7005 msgid "pixel group" msgstr "grupo de pixels" #: f.repair.cc:7013 f.repair.cc:7085 msgid "stuck pixels:" msgstr "pixels presos:" #: f.repair.cc:7113 msgid "Load Stuck Pixels" msgstr "Carregar Pixels Presos" #: f.repair.cc:7115 msgid "File:" msgstr "Arquivo:" #: f.repair.cc:7139 f.repair.cc:7196 msgid "Stuck Pixels file" msgstr "Arquvio de Pixels Presos" #: f.repair.cc:7176 f.tools.cc:4220 msgid "file format error" msgstr "erro de formato de arquivo" #: f.repair.cc:7622 msgid "" "left drag: add transparency \n" "right drag: add opacity" msgstr "" "arrasto com botão esquerdo: adiciona transparência \n" "arrasto com botão direito: adiciona opacidade" #: f.repair.cc:7654 f.widgets.cc:511 msgid "Paint Transparency" msgstr "Aplicar Transparência" #: f.repair.cc:7662 msgid "paintbrush radius" msgstr "raio do pincel" #: f.repair.cc:7669 msgid "gradual paint" msgstr "pintura gradual" #: f.repair.cc:7923 f.widgets.cc:512 msgid "Add Transparency" msgstr "Adicionar Transparência" #: f.repair.cc:7927 msgid "Match Brightness" msgstr "Combinar Brilho" #: f.repair.cc:7932 msgid "Match Color" msgstr "Combinar Cor" #: f.repair.cc:7934 msgid "click on image to select color" msgstr "clique na imagem para selecionar cor" #: f.repair.cc:7950 msgid "Invert Match" msgstr "Inverter combinação" #: f.repair.cc:7953 fotoxx.h:1285 msgid "Transparency" msgstr "Transparência" #: f.tools.cc:86 msgid "" "Select directories containing image files \n" "(subdirectories are included automatically)." msgstr "" "Selecione o diretório contendo arquivos de imagem \n" "(subdiretórios são incluídos automaticamente)." #: f.tools.cc:88 msgid "Select to add, click on X to delete." msgstr "Selecione para adicionar, clique em X para apagar." #: f.tools.cc:89 msgid "directory for thumbnails" msgstr "diretório para miniaturas" #: f.tools.cc:90 msgid "extra metadata items to include in index" msgstr "metadados extras para incluir no índice" #: f.tools.cc:91 msgid "" "Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast." msgstr "" "Função de Indexação encerrada. \n" "Indexação é necessário para busca e mapeamento \n" "e também tornar páginas de galeria mais rápidas." #: f.tools.cc:126 f.widgets.cc:579 msgid "Index Image Files" msgstr "Arquivos de Índice de imagens" #: f.tools.cc:298 msgid "Choose top image directories" msgstr "Selecionar pasta raiz de imagens" #: f.tools.cc:299 msgid "Choose thumbnail directory" msgstr "Escolher diretório de miniaturas" #: f.tools.cc:300 msgid "" "All image files will be re-indexed. \n" " Continue?" msgstr "" "Todos os arquivos de imagem serão re-indexados. \n" " Continuar?" #: f.tools.cc:372 msgid "" "No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files." msgstr "" "Nenhum arquivo índice de imagem encontrado.\n" "Um índice de imagem será criado.\n" "Seus arquivos de imagem não serão alterados.\n" "Isto pode levar um tempo considerável, se você \n" "tiver alguns milhares de arquivos de imagem." #: f.tools.cc:378 #, c-format msgid "" "Invalid directory: \n" " %s \n" "Please remove." msgstr "" "Diretório inválido: \n" " %s \n" "Por favor, remova." #: f.tools.cc:381 #, c-format msgid "" "Thumbnails directory: \n" " %s \n" "must be named .../thumbnails" msgstr "" "Diretório de Miniaturas: \n" " %s \n" "tem de ser nomeado .../thumbnails" #: f.tools.cc:384 #, c-format msgid "" "Duplicate directory: \n" " %s \n" "Please remove." msgstr "" "Diretório duplicado: \n" " %s \n" "Por favor, remova." #: f.tools.cc:443 msgid "specify 1 thumbnail directory" msgstr "especifique 1 diretório de miniaturas" #: f.tools.cc:628 msgid "Top directories have no images" msgstr "Diretórios topo não possuem imagens" #: f.tools.cc:1075 msgid "Cancel image index function?" msgstr "Cancelar função de indexação de imagem?" #: f.tools.cc:1195 msgid "Recent Files Gallery" msgstr "Arquivos Recentes da Galeria" #: f.tools.cc:1196 msgid "Newest Files Gallery" msgstr "Galeria de Arquivos Recentes" #: f.tools.cc:1197 msgid "Specific Gallery" msgstr "Galeria Específica" #: f.tools.cc:1198 msgid "Album Gallery" msgstr "Galeria de Álbuns" #: f.tools.cc:1199 msgid "Previous Gallery" msgstr "Galeria Anterior" #: f.tools.cc:1200 msgid "Previous Image File" msgstr "Arquivo de Imagem Anterior" #: f.tools.cc:1201 msgid "Specific Image File" msgstr "Arquivo de Imagem Específico" #: f.tools.cc:1202 f.widgets.cc:435 msgid "Blank Window" msgstr "Janela em Branco" #: f.tools.cc:1250 f.widgets.cc:580 msgid "User Settings" msgstr "Configurações de Usuário" #: f.tools.cc:1253 msgid "Startup Display" msgstr "Exibição Inicial" #: f.tools.cc:1264 msgid "Background color:" msgstr "Cor de plano de fundo:" #: f.tools.cc:1265 msgid "F-View" msgstr "Visão-F" #: f.tools.cc:1268 msgid "G-View" msgstr "Visão-G" #: f.tools.cc:1272 msgid "Menu Text" msgstr "Texto do menu" #: f.tools.cc:1279 msgid "Menu Style" msgstr "Estilo de Menu" #: f.tools.cc:1280 msgid "Icons" msgstr "Ícones" #: f.tools.cc:1281 msgid "Icons + Text" msgstr "Ícones + Texto" #: f.tools.cc:1283 msgid "Icon size" msgstr "Tamanho de ícone" #: f.tools.cc:1287 msgid "Dialog font" msgstr "Fonte de diálogo" #: f.tools.cc:1292 msgid "Zoomed Image:" msgstr "Imagem Aproximada" #: f.tools.cc:1301 msgid "JPEG save quality" msgstr "Qualidade para JPEG" #: f.tools.cc:1305 msgid "Curve node capture distance" msgstr "Distância de captura de nó da curva" #: f.tools.cc:1309 msgid "Map marker size" msgstr "Tamanho do marcador de mapa" #: f.tools.cc:1313 msgid "show last file version only" msgstr "mostrar apenas a última versão do arquivo" #: f.tools.cc:1316 msgid "shift image to right margin" msgstr "deslocar imagem para margem direita" #: f.tools.cc:1319 f.tools.cc:1324 msgid "image index level" msgstr "nível de índice de imagens" #: f.tools.cc:1321 msgid "Fotoxx started directly" msgstr "Fotoxx iniciado diretamente" #: f.tools.cc:1326 msgid "Fotoxx started by file manager" msgstr "Fotoxx iniciado por gerenciador de arquivos" #: f.tools.cc:1329 msgid "RAW file types" msgstr "Tipos de arquivos RAW" #: f.tools.cc:1333 msgid "video file types" msgstr "tipos de arquivo de vídeo" #: f.tools.cc:1440 msgid "Select startup directory" msgstr "Selecionar diretório de inicialização" #: f.tools.cc:1447 msgid "Select startup image file" msgstr "Selecionar arquivo de imagem inicial" #: f.tools.cc:1454 msgid "Select startup album" msgstr "Selecionar álbum de inicialização" #: f.tools.cc:1497 msgid "startup directory is invalid" msgstr "diretório de iniciação é inválido" #: f.tools.cc:1508 msgid "startup file is invalid" msgstr "arquivo de iniação é inválido" #: f.tools.cc:1683 f.widgets.cc:581 msgid "Keyboard Shortcuts" msgstr "Teclas de Atalho" #: f.tools.cc:1690 msgid "Reserved Shortcuts \n" msgstr "Atalhos Reservados \n" #: f.tools.cc:1691 msgid " F1 User Guide for current function \n" msgstr " F1 Guia do usuário para a função atual \n" #: f.tools.cc:1692 msgid " F10 Full Screen with menus \n" msgstr " F10 Tela cheio com menus \n" #: f.tools.cc:1693 msgid " F11 Full Screen without menus \n" msgstr " F11 Tela cheia sem menus \n" #: f.tools.cc:1694 msgid " Escape Quit dialog; Quit Fotoxx \n" msgstr " Esc Fechar diálogo; Fechar Fotoxx \n" #: f.tools.cc:1695 msgid " Ctrl+H Show hidden files in Gallery mode \n" msgstr " Ctrl+H Mostrar arquivos ocultos no modo Galeria \n" #: f.tools.cc:1696 msgid " Delete Delete/Trash dialog \n" msgstr " Delete Diálogo de Exclusão/Lixeira \n" #: f.tools.cc:1697 msgid " Arrows Previous/Next Image or Gallery page \n" msgstr " Setas Prévia/Próxima Imagem ou Página da Galeria \n" #: f.tools.cc:1757 msgid "Edit KB Shortcuts" msgstr "Editar Atalhos" #: f.tools.cc:1766 msgid "shortcut key:" msgstr "tecla de atalho:" #: f.tools.cc:1767 msgid "(enter key)" msgstr "(tecla enter)" #: f.tools.cc:1768 f.tools.cc:1911 f.tools.cc:2015 msgid "(no selection)" msgstr "(nenhuma seleção)" #: f.tools.cc:1905 #, c-format msgid "\"%s\" Reserved, cannot be used" msgstr "\"%s\" Reservado, não pode ser usado" #: f.tools.cc:2036 msgid "unable to save KB-shortcuts file" msgstr "incapaz de salvar arquivo de atalhos do teclado" #: f.tools.cc:2356 f.widgets.cc:583 msgid "Grid Lines" msgstr "Linhas de grade" #: f.tools.cc:2365 msgid "x-spacing" msgstr "espaçamento-x" #: f.tools.cc:2366 msgid "x-count" msgstr "contagem-x" #: f.tools.cc:2367 msgid "x-enable" msgstr "x-habilitado" #: f.tools.cc:2373 msgid "y-spacing" msgstr "espaçamento-y" #: f.tools.cc:2374 msgid "y-count" msgstr "contagem-y" #: f.tools.cc:2375 msgid "y-enable" msgstr "y-habilitado" #: f.tools.cc:2495 f.widgets.cc:584 msgid "Line Color" msgstr "Cor da linha" #: f.tools.cc:2560 msgid "Click image to select pixels." msgstr "Clique na imagem para selecionar pixels." #: f.tools.cc:2595 f.widgets.cc:585 msgid "Show RGB" msgstr "Mostrar RGB" #: f.tools.cc:2890 msgid "" "Drag mouse on image. \n" "Left click to cancel. \n" "Key X to toggle dialog." msgstr "" "Arraste o mouse na imagem. \n" "Clique com botão esquerdo para cancelar. \n" "Tecla X alterna a caixa de diálogo." #: f.tools.cc:2918 f.widgets.cc:586 msgid "Magnify Image" msgstr "Ampliar Imagens" #: f.tools.cc:2927 msgid "X-size" msgstr "Tamanho-X" #: f.tools.cc:3172 msgid "Darkest and Brightest Pixels" msgstr "Pixels mais Escuros e Brilhantes" #: f.tools.cc:3195 msgid "Dark Limit" msgstr "Limite Escuro" #: f.tools.cc:3196 msgid "Bright Limit" msgstr "Limite Claro" #: f.tools.cc:3285 msgid "" "Brightness should show a gradual ramp \n" "extending all the way to the edges." msgstr "" "Brilho deveria mostrar uma inclinação gradual \n" "estendendo-se até as bordas." #: f.tools.cc:3376 f.widgets.cc:589 msgid "Monitor Gamma" msgstr "Monitor Gamma" #: f.tools.cc:3443 msgid "Available Translations" msgstr "Traduções disponíveis" #: f.tools.cc:3447 msgid "Set Language" msgstr "Configurar língua" #: f.tools.cc:3573 msgid "Change Color Profile" msgstr "Mudar Perfil de Cores" #: f.tools.cc:3577 msgid "input profile" msgstr "perfil de entrada" #: f.tools.cc:3581 msgid "output profile" msgstr "perfil de saída" #: f.tools.cc:3602 msgid "Unable to change EXIF color profile" msgstr "Impossibilitado de mudar Perfil de Cores EXIF" #: f.tools.cc:3604 msgid "automatic new version created" msgstr "nova versão criada automaticamente" #: f.tools.cc:3613 msgid "color profile" msgstr "perfil de cor" #: f.tools.cc:3661 f.tools.cc:3667 #, c-format msgid "unknown cms profile %s" msgstr "perfil cms desconhecido %s" #: f.tools.cc:3770 f.widgets.cc:593 msgid "Calibrate Printer" msgstr "Calibrar Impressora" #: f.tools.cc:3795 msgid "print color chart" msgstr "imprimir gráfico de cores" #: f.tools.cc:3796 msgid "scan and save color chart" msgstr "digitalizar e salvar gráfico de cores" #: f.tools.cc:3797 msgid "align and trim color chart" msgstr "alinhar e aparar gráfico de cores" #: f.tools.cc:3798 msgid "open and process color chart" msgstr "abrir e processar gráfico de cores" #: f.tools.cc:3799 msgid "print image with revised colors" msgstr "imprimir imagem com cores revisadas" #: f.tools.cc:3940 #, c-format msgid "" "Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/" msgstr "" "Digitalizar gráfico de cores impresso. \n" "A fileira mais escura está no topo. \n" "Salvar em %s/" #: f.tools.cc:3955 msgid "" "Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY." msgstr "" "Abra e edite o arquivo digitalizado do gráfico de cores. \n" "Remova qualquer inclinação ou rotação da digitalização. \n" "(Utilize a função Corrigir Perspectiva para isso). \n" "Corte retirando a margem verde e fina PRECISAMENTE." #: f.tools.cc:3987 msgid "Open the trimmed color chart file" msgstr "Abrir arquivo de gráfico de cores aparado" #: f.tools.cc:4120 msgid "" "Set the name for the output calibration file \n" "[your calibration name].dat" msgstr "" "Insira o nome para o arquivo de saída de calibração \n" "[seu nome para calibração].dat" #: f.tools.cc:4160 msgid "Color map file to use" msgstr "Arquivo de mapa de cores a usar" #: f.tools.cc:4179 msgid "Select the image file to print." msgstr "Selecione o arquivo de imagem para imprimir." #: f.tools.cc:4244 msgid "converting colors..." msgstr "convertendo cores..." #: f.tools.cc:4344 msgid "Image colors are converted for printing." msgstr "Cores da imagem foram convertidas para impressão." #: f.tools.cc:4467 msgid "This function is for AppImage package only" msgstr "Esta função é apenas para o pacote AppImage" #: f.tools.cc:4471 msgid "" "Completely remove the AppImage package \n" "Press F1 for more information. \n" "Continue?" msgstr "" "Remover completamente o pacote AppImage \n" "Pressione F1 para obter mais informações. \n" "Continuar?" #: f.warp.cc:94 f.widgets.cc:515 msgid "Unbend" msgstr "Desempenar" #: f.warp.cc:371 msgid "" " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle." msgstr "" " Clique nos quatro cantos de um quadrilátero. Pressione [aplicar]. \n" " A imagem será distorcida para fazer do quadrilátero um retângulo." #: f.warp.cc:388 msgid "Perspective Correction" msgstr "Correção de Perspectiva" #: f.warp.cc:621 msgid "must have 4 corners" msgstr "precisar ter 4 cantos" #: f.warp.cc:745 msgid "" " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]." msgstr "" " Selecione a área a distorcer usando a função de seleção. \n" " Precione [iniciar distorção] e puxe a área com o mouse. \n" " Faça vários ajustes até que esteja satisfeito. \n" " Quando terminar, selecione outra área e precione [pronto]." #: f.warp.cc:758 f.widgets.cc:517 msgid "Warp area" msgstr "Distorcer área" #: f.warp.cc:763 msgid "start warp" msgstr "iniciar distorção" #: f.warp.cc:1149 msgid "" " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n" msgstr "" " Use a Seleção de Área para selecionar a face. \n" " Clique no centro da distorção. \n" " Mova o controle deslizante. \n" #: f.warp.cc:1176 f.widgets.cc:518 msgid "Unwarp Closeup" msgstr "Conformação de aproximação" #: f.warp.cc:1356 f.warp.cc:1671 msgid "" " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Puxe uma posição da usando o mouse. \n" " Faça vários ajustes até que esteja satisfeito. \n" " Quando terminar, precione [pronto]." #: f.warp.cc:1374 f.widgets.cc:519 msgid "Warp curved" msgstr "Distorção curva" #: f.warp.cc:1689 f.widgets.cc:520 msgid "Warp linear" msgstr "Distorção linear" #: f.warp.cc:2005 msgid "" " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]." msgstr "" " Puxe um canto da imgem usando o mouse. \n" " Faça vários ajustes até que esteja satisfeito. \n" " Quando terminar, precione [pronto]." #: f.warp.cc:2021 f.widgets.cc:521 msgid "Warp affine" msgstr "Distorção afim" #: f.warp.cc:2368 f.widgets.cc:522 msgid "Flatten Book Page" msgstr "Achatar Página de Book" #: f.warp.cc:2369 msgid "" "Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: " msgstr "" "Recorte a imagem para isolar uma página. \n" "Mapeie as bordas superior e inferior com \n" "4 ou + cliques no mouse, daí achate: " #: f.warp.cc:2372 msgid "Stretch curved-down surfaces:" msgstr "Estique superfícies curvadas para baixo:" #: f.warp.cc:2427 msgid "Top:" msgstr "Topo:" #: f.warp.cc:2430 msgid "Bottom:" msgstr "Rodapé:" #: f.warp.cc:2811 f.widgets.cc:523 msgid "Spherical Projection" msgstr "Projeção Esférica" #: f.warp.cc:2855 msgid "Magnify" msgstr "Ampliar" #: f.warp.cc:3029 msgid "" " Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]." msgstr "" "Selecione Áreas que permanecerão inalteradas. \n" "Arraste a imagem através canto superior esquerdo. \n" "Quando terminar, pressione [pronto]." #: f.warp.cc:3045 f.widgets.cc:524 msgid "Selective Rescale" msgstr "Redimensionamento Seletivo" #: f.warp.cc:3068 msgid "select areas first" msgstr "selecione áreas primeiro" #: f.warp.cc:3290 f.widgets.cc:525 msgid "Make Waves" msgstr "Criar Ondas" #: f.warp.cc:3297 msgid "wavelength" msgstr "comprimento de onda" #: f.warp.cc:3299 msgid "variance" msgstr "variância" #: f.warp.cc:3310 msgid "perspective" msgstr "perspectiva" #: f.warp.cc:3484 f.warp.cc:3515 msgid "Twist" msgstr "Torção" #: f.warp.cc:3512 msgid "drag mouse to set center" msgstr "arraste o mouse para definir o centro" #: f.widgets.cc:103 msgid "Album" msgstr "Álbum" #: f.widgets.cc:105 msgid "TOP" msgstr "TOPO" #: f.widgets.cc:171 msgid "Current Image File (key F)" msgstr "Arquivo de Imagem atual (tecla F)" #: f.widgets.cc:172 msgid "Thumbnail Gallery (key G)" msgstr "Miniatura de Galeria (tecla G)" #: f.widgets.cc:173 msgid "World Maps (key W)" msgstr "Mapa Mundial (tecla W)" #: f.widgets.cc:174 msgid "Net Maps (key M)" msgstr "Mapas na Rede (tecla M)" #: f.widgets.cc:177 msgid "Favorite Functions" msgstr "Funções Favoritas" #: f.widgets.cc:178 msgid "File: Open, RAW, Rename, Delete, Print" msgstr "Arquivo: Arbri, RAW, Renomear, Excluir, Imprimir" #: f.widgets.cc:179 msgid "Save modified image file to disk" msgstr "Salvar arquivo-imagem modificado no disco " #: f.widgets.cc:180 msgid "Open previous or next file (left/right mouse click)" msgstr "Abrir arquivo prévio ou próximo (clique direito/esquerdo do mouse)" #: f.widgets.cc:181 msgid "Metadata: Captions, Tags, Ratings, Geotags, Search ... " msgstr "Metadados: Legendas, Etiquetas, Avaliações, Georreferências, Busca..." #: f.widgets.cc:182 msgid "Areas: Select areas to edit, copy and paste" msgstr "Áreas: Selecionar área para editar, copiar e colar" #: f.widgets.cc:183 msgid "Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..." msgstr "" "Editar: Recortar, Rotacionar, Redimensionar, Brilho, Contraste, Texto..." #: f.widgets.cc:184 msgid "Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..." msgstr "Reparo: Aguçar, Ruído, Olhos Vermelhos, Colorir, Pintar, Clonar..." #: f.widgets.cc:185 msgid "Warp: Fix Perspective, Warp or unwarp image ..." msgstr "Distorção: Corrigir perspectiva, Distorcer ou Conformar imagem ..." #: f.widgets.cc:186 msgid "Effects: Special Effects, Arty Transforms" msgstr "Efeitos: Efeitos especiais, Transformações Artísticas" #: f.widgets.cc:187 msgid "" "left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit" msgstr "" "clique esquerdo/direito: desfazer/refazer 1 edição \n" " com tecla A: desfazer/refazer todas edições \n" " clique no meio: ir a qualquer edição anterior" #: f.widgets.cc:190 msgid "Tools: Index, Settings, Shortcuts, Magnify ..." msgstr "Ferramentas: Índice, Configurações, Atalhos, Ampliar ..." #: f.widgets.cc:191 msgid "Help: Quick Start, User Guide, Recent Changes ..." msgstr "Ajuda: Guia Rápido, Guia de Usuário, Mudanças Recentes..." #: f.widgets.cc:194 msgid "Sync Gallery, Albums, Slide Show" msgstr "Sincronizar Galeria, Álbuns, Apresentações" #: f.widgets.cc:195 msgid "set and recall bookmarked image locations" msgstr "definir e recuperar localidade de imagem marcada como favorita" #: f.widgets.cc:196 msgid "increase thumbnail size" msgstr "aumentar tamanho de miniaturas" #: f.widgets.cc:197 msgid "reduce thumbnail size" msgstr "reduzir tamanho de miniaturas" #: f.widgets.cc:198 msgid "change sort order" msgstr "alterar a ordem de classificação" #: f.widgets.cc:199 msgid "jump to beginning (top)" msgstr "saltar para o início (topo)" #: f.widgets.cc:200 msgid "jump to end (bottom)" msgstr "saltar para final (rodapé)" #: f.widgets.cc:201 msgid "previous page" msgstr "página anterior" #: f.widgets.cc:202 msgid "next page" msgstr "próxima página" #: f.widgets.cc:203 msgid "Combine: HDR, HDF, Panorama, Stack, Mashup" msgstr "Combinação: HDR, HDF, Panorama, Empilhamento, Mistura" #: f.widgets.cc:204 msgid "Process: convert, export, metadata, search ..." msgstr "Processanento: conversão em lote, exportação, pesquisa de imagens ..." #: f.widgets.cc:207 msgid "Choose a map file" msgstr "Escolher arquivo de mapa" #: f.widgets.cc:208 f.widgets.cc:212 msgid "Mark all images or current gallery" msgstr "Marcar todas imagens ou galeria atual" #: f.widgets.cc:211 msgid "Choose Internet map source" msgstr "Escolher fonte de mapa da Internet" #: f.widgets.cc:213 msgid "Save and recall named map locations" msgstr "Salvar e recuperar localidades de mapa nomeados" #: f.widgets.cc:216 msgid "Open another window" msgstr "Abrir outra Janela" #: f.widgets.cc:217 msgid "Open a new image file" msgstr "Abrir novo arquvio de imagem" #: f.widgets.cc:218 f.widgets.cc:430 msgid "Cycle 2 Previous Files" msgstr "Ciclo 2 Arquivos Prévios" #: f.widgets.cc:219 f.widgets.cc:431 msgid "Cycle 3 Previous Files" msgstr "Ciclo 3 Arquivos Prévios" #: f.widgets.cc:220 msgid "Open a recently seen file" msgstr "Abrir um arquivo visto recentemente" #: f.widgets.cc:221 msgid "Open a newly added file" msgstr "Abrir arquivo adicionado recentemente" #: f.widgets.cc:222 msgid "Open and edit a camera RAW file" msgstr "Abrir e editar um arquivo RAW da câmera" #: f.widgets.cc:223 msgid "View a 360 degree panorama image file" msgstr "Visualizar um arquivo de imagem panorama 360 ​​graus" #: f.widgets.cc:224 msgid "Change the image file name" msgstr "Mudar nome do arquivo" #: f.widgets.cc:225 msgid "Copy or Move an image file to a new location" msgstr "Copiar ou Mover um arquivo de imagem para uma nova localidade" #: f.widgets.cc:226 msgid "Copy an image file to the desktop" msgstr "Copiar um arquivo de imagem para a área de trabalho" #: f.widgets.cc:227 msgid "Copy image file to the clipboard" msgstr "Copiar arquivo de imagem para a área de transferência" #: f.widgets.cc:228 msgid "Copy image file to the image cache" msgstr "Copiar imagem para o cache de imagens" #: f.widgets.cc:229 msgid "Show location on Interet map" msgstr "Mostrar localização no mapa da Interet" #: f.widgets.cc:230 msgid "Create a blank image" msgstr "Criar uma imagem em branco" #: f.widgets.cc:231 msgid "toggle - blank or restore window" msgstr "alterna - restaurar ou branquear a janela" #: f.widgets.cc:232 msgid "Delete or trash image file" msgstr "Excluir ou descartar arquivo de imagem" #: f.widgets.cc:233 msgid "Print the current image" msgstr "Imprimir imagem atual" #: f.widgets.cc:234 msgid "Print current image with adjusted colors" msgstr "Imprimir imagem atual com cores ajustadas" #: f.widgets.cc:235 f.widgets.cc:445 msgid "Quit Fotoxx" msgstr "Fechar Fotoxx" #: f.widgets.cc:238 msgid "List a few key metadata items" msgstr "Listar poucos ítens de metadados chave" #: f.widgets.cc:239 msgid "List all metadata items" msgstr "Listar todos ítens de metadados" #: f.widgets.cc:240 msgid "Edit image tags/geotags/caption/rating ..." msgstr "Editar etiquetas de imagem/georreferências/legendas/avaliações ..." #: f.widgets.cc:241 msgid "Edit any image metadata" msgstr "Edita qualquer metadados de uma imagem" #: f.widgets.cc:242 msgid "Remove selected image metadata" msgstr "Remove selected image metadata" #: f.widgets.cc:243 msgid "(Toggle) show captions and comments" msgstr "(Comutativo) mostrar legendas e comentários" #: f.widgets.cc:246 msgid "Select object or area for editing" msgstr "Selecionar objeto ou área para edição" #: f.widgets.cc:247 msgid "Find a gap in an area outline" msgstr "Procurar lacuna em área de contorno" #: f.widgets.cc:248 msgid "Select hairy or irregular edge" msgstr "Selecione contorno irregular ou felpudo" #: f.widgets.cc:249 msgid "Show (outline) existing area" msgstr "Mostrar (traçado) de área existente" #: f.widgets.cc:250 msgid "Hide existing area" msgstr "Esconder área existente" #: f.widgets.cc:251 msgid "Enable area for editing" msgstr "Habilitar área para edição" #: f.widgets.cc:252 msgid "Disable area for editing" msgstr "Desabilitar área para edição" #: f.widgets.cc:253 msgid "Reverse existing area" msgstr "Inverter área existente" #: f.widgets.cc:254 msgid "Erase existing area" msgstr "Apagar área existente" #: f.widgets.cc:255 msgid "Copy area for later pasting into image" msgstr "Copiar área para posterior colagem em imagem" #: f.widgets.cc:256 msgid "Save area to a file with transparency" msgstr "Salvar área em arquivo com transparência" #: f.widgets.cc:257 msgid "Open a file and paste as area into image" msgstr "Abrir um arquivo e colar como área na imagem" #: f.widgets.cc:258 msgid "Paste previously copied area into image" msgstr "Colar área previamente copiada em imagem" #: f.widgets.cc:261 msgid "Trim/Crop margins and/or Rotate" msgstr "Recortar/Aparar margens e/ou Rodar" #: f.widgets.cc:262 msgid "Upright a rotated image" msgstr "Orientar imagem rotacionada" #: f.widgets.cc:263 msgid "Change pixel dimensions" msgstr "Mudar dimensões de pixel" #: f.widgets.cc:264 f.widgets.cc:265 msgid "Fast auto enhance that may work OK" msgstr "Realce automático rápido que deve funcionar bem" #: f.widgets.cc:266 msgid "Adjust brightness, contrast, color" msgstr "Ajusta brilho, contraste, cor" #: f.widgets.cc:267 msgid "Edit brightness distribution" msgstr "Editar distribuição de brilho" #: f.widgets.cc:268 msgid "Magnify brightness gradients to enhance details" msgstr "Ampliar gradientes de brilho realçar detalhes" #: f.widgets.cc:269 msgid "Flatten brightness distribution" msgstr "Nivelar a distribuição de brilho" #: f.widgets.cc:270 msgid "Rescale brightness - reduce color caste and fog/haze" msgstr "Reescalar brilho - reduz separação de cores e névoa/neblina" #: f.widgets.cc:271 msgid "Mirror image horizontally or vertically" msgstr "Espelhar imagem horizontalmente ou verticalmente" #: f.widgets.cc:272 msgid "Paint image pixels using the mouse" msgstr "Pintar os pixels da imagem usando o mouse" #: f.widgets.cc:273 msgid "Clone image pixels using the mouse" msgstr "Clonar os pixels da imagem usando o mouse" #: f.widgets.cc:274 msgid "Blend image pixels using the mouse" msgstr "Misturar pixels da imagem usando o mouse" #: f.widgets.cc:277 msgid "Paint edit function gradually with mouse" msgstr "Função de pintura gradual com o mouse" #: f.widgets.cc:278 msgid "Leverage edits by brightness or color" msgstr "Influenciar edições por brilho ou cor" #: f.widgets.cc:279 msgid "Edit plugins menu or run a plugin function" msgstr "Editar menu de plugins ou executar uma função de plugin" #: f.widgets.cc:282 msgid "Make the image look sharper" msgstr "Tornar a imagem mais nítida" #: f.widgets.cc:283 msgid "Make the image look fuzzy" msgstr "Deixam a imagem difusa" #: f.widgets.cc:284 msgid "Filter noise from low-light photos" msgstr "Filtrar ruído de fotos com pouca luz" #: f.widgets.cc:285 msgid "Fix red-eyes from electronic flash" msgstr "Corrigir olhos vermelhos por flash eletrônico" #: f.widgets.cc:286 msgid "Make BW/color, negative/positive, sepia" msgstr "Fazer PB/colorido, negativo/positivo, sepia" #: f.widgets.cc:287 msgid "Reduce color depth (posterize)" msgstr "Reduzir profundidade de cor (posterizar)" #: f.widgets.cc:288 msgid "Shift/convert colors into other colors" msgstr "Deslocar/converter cores em outras" #: f.widgets.cc:289 msgid "Adjust color intensity (saturation)" msgstr "Ajustar intensidade de cor (saturação)" #: f.widgets.cc:290 msgid "Adjust color using RGB or CMY colors" msgstr "Ajustar cor usando cores RGB ou CMY" #: f.widgets.cc:291 msgid "Adjust color using HSL colors" msgstr "Ajustar cor usando cores HSL" #: f.widgets.cc:292 msgid "Adjust color in selected image areas" msgstr "Ajustar cor em áreas selecionadas da imagem" #: f.widgets.cc:293 msgid "Match colors on one image with another" msgstr "Equiparar cores de uma imagem com outra" #: f.widgets.cc:294 msgid "Reduce Chromatic Abberation" msgstr "Reduzir Aberrações Cromáticas" #: f.widgets.cc:295 msgid "Remove unwanted objects" msgstr "Remover objetos indesejados" #: f.widgets.cc:296 msgid "Add a brightness/color ramp across the image" msgstr "Adicionar rampa de brilho/cor através da imagem" #: f.widgets.cc:297 msgid "Remove dust spots from scanned slides" msgstr "Remover pontos de sujeira de digitalizações" #: f.widgets.cc:298 msgid "Smoothen edges with jaggies" msgstr "Suavisar contornos irregulares" #: f.widgets.cc:299 msgid "Erase known hot and dark pixels" msgstr "Apagar pixels quentes ou escuros conhecidos" #: f.widgets.cc:300 msgid "Paint image transparency using the mouse" msgstr "Aplicar transparência de imagem usando o mouse" #: f.widgets.cc:301 msgid "Add image transparency based on image attributes" msgstr "Adicionar transparência baseando-se em atributos da imagem" #: f.widgets.cc:304 msgid "Remove curvature, esp. panoramas" msgstr "Remover curvatura, esp. panoramas" #: f.widgets.cc:305 msgid "Straighten objects seen from an angle" msgstr "Endireitar objetos vistos a um ângulo" #: f.widgets.cc:306 msgid "Distort image areas using the mouse" msgstr "Distorcer as áreas da imagem usando o mouse" #: f.widgets.cc:307 msgid "Unwarp closeup face photo to remove distortion" msgstr "Remover distorção por aproximação em foto de face" #: f.widgets.cc:308 f.widgets.cc:309 f.widgets.cc:310 msgid "Distort the whole image using the mouse" msgstr "Distorcer toda a imagem utilizando o mouse" #: f.widgets.cc:311 msgid "Flatten a photographed book page" msgstr "Achatar uma página fotografada de um book" #: f.widgets.cc:312 msgid "Make a spherical projection of an image" msgstr "Criar uma projeção esférica de uma imagem" #: f.widgets.cc:313 msgid "Rescale image outside selected areas" msgstr "Redimensionar imagem por fora de áreas selecionadas" #: f.widgets.cc:314 msgid "Warp an image with a wave pattern" msgstr "Distorce uma imagem com um padrão de onda" #: f.widgets.cc:315 msgid "Twist image centered at mouse position" msgstr "Torcer a imagem centralizando na posição do mouse" #: f.widgets.cc:318 msgid "Convert to simulated sketch" msgstr "Converter para esboço simulado" #: f.widgets.cc:319 msgid "Convert image into a cartoon drawing" msgstr "Converter imagem em um desenho cartoon" #: f.widgets.cc:320 msgid "Convert to line drawing (edge detection)" msgstr "Converter para desenho de contorno (detecção de borda)" #: f.widgets.cc:321 msgid "Convert to solid color drawing" msgstr "Converter em desenho de cor sólida" #: f.widgets.cc:322 msgid "Graduated Blur depending on contrast" msgstr "Desfoque Graduado dependente do contraste" #: f.widgets.cc:323 msgid "Create an embossed or 3D appearance" msgstr "Criar relevo ou aparência de 3D" #: f.widgets.cc:324 msgid "Convert to square tiles" msgstr "Converter para ladrilhos quadrados" #: f.widgets.cc:325 msgid "Convert to dots (Roy Lichtenstein effect)" msgstr "Converter para pontos (efeito Roy Lichtenstein)" #: f.widgets.cc:326 msgid "Convert into a simulated painting" msgstr "Converter em uma pintura simulada" #: f.widgets.cc:327 msgid "Change brightness or color radially" msgstr "Alterar o brilho ou cor radialmente" #: f.widgets.cc:328 msgid "Add texture to an image" msgstr "Adicionar textura a uma imagem" #: f.widgets.cc:329 msgid "Tile image with a repeating pattern" msgstr "Ladrilhar imagem com um padrão repetitivo" #: f.widgets.cc:330 msgid "Create a mosaic with tiles made from all images" msgstr "Criar um mosaico com ladrilhos feitos de todas as imagens" #: f.widgets.cc:331 msgid "Process an image using a custom kernel" msgstr "Processar uma imagem usando um núcleo customizado" #: f.widgets.cc:332 msgid "Blur an image in the direction of mouse drags" msgstr "Desfocar uma imagem na direção dos arrastes com o mouse" #: f.widgets.cc:333 msgid "Increasing blur with distance from selected areas" msgstr "Intensificar desfoque com a distância de áreas selecionadas" #: f.widgets.cc:334 msgid "Change color hue using an algorithm" msgstr "Altera matiz de cor usando um algoritmo" #: f.widgets.cc:337 msgid "Combine bright/dark images for better detail" msgstr "Combine imagens claras/escuras para melhores detalhes" #: f.widgets.cc:338 msgid "Combine near/far focus images for deeper focus" msgstr "Combinar imagens de foco perto/longe para foco profundo" #: f.widgets.cc:339 msgid "Combine images to erase passing people, etc." msgstr "Combinar imagens para apagar pessoas que passam, etc" #: f.widgets.cc:340 msgid "Combine noisy images into a low-noise image" msgstr "Combinar imagens cheias de ruído em uma imagem com baixo ruído" #: f.widgets.cc:341 msgid "Combine images into a panorama" msgstr "Combinar imagens em um panorama" #: f.widgets.cc:342 msgid "Combine images into a vertical panorama" msgstr "Combinar imagens em um panorama vertical" #: f.widgets.cc:343 msgid "Combine images into a panorama (panorama tools)" msgstr "Combinar imagens em um panorama (panorama tools)" #: f.widgets.cc:344 msgid "Arrange images and text in a layout (montage)" msgstr "Arranjar imagens e texto em um leiaute (colagem)" #: f.widgets.cc:345 msgid "Combine images into a montage of images" msgstr "Combina várias imagens em uma colagem" #: f.widgets.cc:348 msgid "Rename/convert/resize/move multiple files" msgstr "Renomear/converter/redimensionar/mover vários arquivos" #: f.widgets.cc:349 msgid "Upright multiple rotated image files" msgstr "Orientar múltiplos arquivos de imagem rotacionados" #: f.widgets.cc:350 msgid "Delete or Trash multiple files" msgstr "Excluir ou Remover vários arquivos" #: f.widgets.cc:351 msgid "Convert camera RAW files using libraw or Raw Therapee" msgstr "Converter arquivos RAW da câmera usando libraw ou Raw Therapee" #: f.widgets.cc:352 msgid "Build and run edit script files" msgstr "Criar, editar e executar arquivo de rotina" #: f.widgets.cc:353 msgid "Burn selected image files to DVD/BlueRay disc" msgstr "Gravar arquivos de imagens selecionadas em disco DVD/BluRay" #: f.widgets.cc:354 msgid "Search all image files and report duplicates" msgstr "Procurar em todas os arquivos de imagem e relatar duplicidades" #: f.widgets.cc:356 msgid "Export selected image files to a directory" msgstr "Exportar arquivos de imagem selecionada para um diretório" #: f.widgets.cc:357 msgid "Add/remove tags for multiple images" msgstr "Adicionar/remover etiquetas para múltiplas imagens" #: f.widgets.cc:358 msgid "Convert tag names for all images" msgstr "Converter nome de etiqueta para todas as imagens" #: f.widgets.cc:359 msgid "change or shift photo dates/times" msgstr "alterar ou deslocar data/hora de várias fotos" #: f.widgets.cc:360 msgid "Add/change/delete metadata for multiple images" msgstr "Adicionar/Mudar/Excluir metadados em várias imagens" #: f.widgets.cc:361 msgid "Report metadata for multiple images" msgstr "Relatório de metadados para várias imagens" #: f.widgets.cc:362 msgid "Add/revise geotags for multiple images" msgstr "Adicionar/Revisar georeferências para várias imagens" #: f.widgets.cc:363 msgid "Find all images for a location [date]" msgstr "Encontrar todas imagens por uma localidade [data]" #: f.widgets.cc:364 msgid "Show image counts by month, select and report" msgstr "Mostra contagem de imagens por mês, seleção e relatório" #: f.widgets.cc:365 msgid "Find images meeting select criteria" msgstr "Encontrar imagens satisfazendo critério selecionado" #: f.widgets.cc:368 msgid "Index new files and make thumbnails" msgstr "Indexar novos arquivos gerar miniaturas" #: f.widgets.cc:369 msgid "Change user preferences" msgstr "Mudar preferências de usuário" #: f.widgets.cc:370 msgid "Change Keyboard Shortcut Keys" msgstr "Mudar Atalhos de Teclado" #: f.widgets.cc:371 msgid "Show a brightness distribution graph" msgstr "Mostrar gráfico de distribuição de brilho" #: f.widgets.cc:372 msgid "Show or revise grid lines" msgstr "Mostrar ou corrigir linhas de grade" #: f.widgets.cc:373 msgid "Change color of foreground lines" msgstr "Mudar cor de linhas de primeiro plano" #: f.widgets.cc:374 msgid "Show RGB colors at mouse click" msgstr "Mostrar cores RGB ao clique do mouse" #: f.widgets.cc:375 msgid "Magnify image around the mouse position" msgstr "Ampliar imagem em torno da posição do mouse" #: f.widgets.cc:376 msgid "Highlight darkest and brightest pixels" msgstr "Destacar pixels mais escuros e mais brilhantes" #: f.widgets.cc:377 msgid "Chart to adjust monitor color" msgstr "Gráfico para ajustar cores do monitor" #: f.widgets.cc:378 msgid "Chart to adjust monitor gamma" msgstr "Gráfico para ajustar gama do monitor" #: f.widgets.cc:379 msgid "Change the GUI language" msgstr "Mudar a língua da GUI" #: f.widgets.cc:380 msgid "Report missing translations" msgstr "Relatar tradução em falta" #: f.widgets.cc:381 msgid "Convert to another color profile" msgstr "Converter para outro perfil de cores" #: f.widgets.cc:382 msgid "Calibrate printer colors" msgstr "Calibrar cores de impressora" #: f.widgets.cc:383 msgid "Completely uninstall AppImage package" msgstr "Remover completamente o pacote AppImage" #: f.widgets.cc:384 msgid "Memory and CPU (to terminal/logfile)" msgstr "Memória e CPU (para terminal/arquivo log)" #: f.widgets.cc:387 msgid "Quick Start mini-guide" msgstr "Mini-guia resumido" #: f.widgets.cc:388 msgid "Read the user guide" msgstr "Leia o Guia de Usuários" #: f.widgets.cc:389 msgid "Recent user guide changes" msgstr "Mudanças recentes no Guia de Usuários" #: f.widgets.cc:390 msgid "Technical installation notes" msgstr "Notas técnicas da instalação do Fotoxx" #: f.widgets.cc:391 msgid "List updates by Fotoxx version" msgstr "Listar atualizações por versão do Fotoxx" #: f.widgets.cc:392 msgid "View the log file and error messages" msgstr "Ver arquivo de log e mensagens de erro" #: f.widgets.cc:393 msgid "How to do Fotoxx translations" msgstr "Como fazer traduções para o Fotoxx" #: f.widgets.cc:394 msgid "Show the Fotoxx web page" msgstr "Mostrar a página do Fotoxx na web" #: f.widgets.cc:395 msgid "Version, license, contact, credits" msgstr "Versão, licença, contato, créditos" #: f.widgets.cc:398 msgid "list all directories, click any for gallery view" msgstr "" "Lista todos os diretórios, clique em qualquer um para exibição em galeria" #: f.widgets.cc:399 msgid "Set gallery from current image file" msgstr "Definir galeria a partir de imagem atual" #: f.widgets.cc:400 msgid "Organize images into albums" msgstr "Organizar imagens em Álbuns" #: f.widgets.cc:401 msgid "Update album files to latest version" msgstr "Atualizar arquivos de álbum para última versão" #: f.widgets.cc:402 msgid "Replace album file with another file" msgstr "Substituir arquivo de álbum por outro arquivo" #: f.widgets.cc:403 msgid "Start a slide show" msgstr "Iniciar Apresentação de Slides" #: f.widgets.cc:425 msgid "New Window" msgstr "Nova Janela" #: f.widgets.cc:426 f.widgets.cc:610 fotoxx-18.01.cc:1821 msgid "Sync Gallery" msgstr "Sinc. Galeria" #: f.widgets.cc:427 msgid "Recently Seen Images" msgstr "Imagens vistas recentemente" #: f.widgets.cc:428 msgid "Newest Images" msgstr "Imagens mais Novas" #: f.widgets.cc:433 msgid "View 360° Panorama" msgstr "Ver Panorama 360 °" #: f.widgets.cc:434 msgid "New Blank Image" msgstr "Nova Imagem em Branco" #: f.widgets.cc:437 f.widgets.cc:758 msgid "Copy/Move to Location" msgstr "Copiar/Mover para Localidade" #: f.widgets.cc:438 f.widgets.cc:759 msgid "Copy to Desktop" msgstr "Copiar para Área de Trabalho" #: f.widgets.cc:439 f.widgets.cc:760 msgid "Copy to Clipboard" msgstr "Copiar para Área de Transferência" #: f.widgets.cc:440 f.widgets.cc:764 msgid "Copy to Image Cache" msgstr "Copiar para Reserva de Imagens" #: f.widgets.cc:441 f.widgets.cc:778 msgid "Show on Net Map" msgstr "Mostrar em Maps na Rede" #: f.widgets.cc:443 msgid "Print Image" msgstr "Imprimir Imagem" #: f.widgets.cc:444 msgid "Print Calibrated Image" msgstr "Imprimir Imagem Calibrada" #: f.widgets.cc:448 msgid "View Metadata (short)" msgstr "Visualizar Metadados (curto)" #: f.widgets.cc:449 msgid "View Metadata (long)" msgstr "Visualizar Metadados (longo)" #: f.widgets.cc:453 msgid "Show Captions on Image" msgstr "Mostrar Legendas na Imagem" #: f.widgets.cc:456 f.widgets.cc:776 msgid "Select Area" msgstr "Selecionar Área" #: f.widgets.cc:457 msgid "Find Area Gap" msgstr "Encontrar espaço livre" #: f.widgets.cc:459 msgid "Show Area" msgstr "Mostrar Área" #: f.widgets.cc:460 msgid "Hide Area" msgstr "Ocultar Área" #: f.widgets.cc:461 msgid "Enable Area" msgstr "Ativar Área" #: f.widgets.cc:462 msgid "Disable Area" msgstr "Desativar Área" #: f.widgets.cc:463 msgid "Invert Area" msgstr "Inverter Área" #: f.widgets.cc:464 msgid "Unselect Area" msgstr "Deselecionar Área" #: f.widgets.cc:466 msgid "Paste Area" msgstr "Colar Área" #: f.widgets.cc:467 msgid "Open Area File" msgstr "Abrir Arquivo de Área" #: f.widgets.cc:474 msgid "Voodoo 1" msgstr "Voodoo 1" #: f.widgets.cc:475 msgid "Voodoo 2" msgstr "Voodoo 2" #: f.widgets.cc:477 f.widgets.cc:773 msgid "Edit Brightness" msgstr "Editar Brilho" #: f.widgets.cc:478 f.widgets.cc:775 msgid "Gradients" msgstr "Gradientes" #: f.widgets.cc:482 msgid "Paint Image" msgstr "Pintar Imagem" #: f.widgets.cc:489 msgid "Plugins" msgstr "Extensões" #: f.widgets.cc:494 msgid "Denoise" msgstr "Reduzir ruído" #: f.widgets.cc:495 msgid "Red Eyes" msgstr "Olhos vermelhos" #: f.widgets.cc:497 msgid "Color Depth" msgstr "Resolução de cor" #: f.widgets.cc:500 msgid "Adjust RGB/CMY" msgstr "Ajustar RGB/CMY" #: f.widgets.cc:501 msgid "Adjust HSL" msgstr "Ajustar HSL" #: f.widgets.cc:502 msgid "Zonal Colors" msgstr "Cores Zonais" #: f.widgets.cc:503 msgid "Match Colors" msgstr "Mesclar Cores" #: f.widgets.cc:509 msgid "Anti-Alias" msgstr "Antisserrilhado" #: f.widgets.cc:516 msgid "Fix Perspective" msgstr "Corrigdir perspectiva" #: f.widgets.cc:526 msgid "Twist Image" msgstr "Torcer Imagem" #: f.widgets.cc:529 msgid "Sketch" msgstr "Esboço" #: f.widgets.cc:530 msgid "Cartoon" msgstr "Cartoon" #: f.widgets.cc:534 msgid "Embossing" msgstr "Alto-relevo" #: f.widgets.cc:536 msgid "Dots" msgstr "Pontos" #: f.widgets.cc:537 msgid "Painting" msgstr "Pintura" #: f.widgets.cc:539 msgid "Texture" msgstr "Textura" #: f.widgets.cc:541 msgid "Mosaic" msgstr "Mosaico" #: f.widgets.cc:548 msgid "High Dynamic Range" msgstr "Grande Alcance Dinâmico (HDR)" #: f.widgets.cc:549 msgid "High Depth of Field" msgstr "Alta Profundidade de Campo (HDF)" #: f.widgets.cc:550 msgid "Stack/Paint" msgstr "Pilha/Pintura" #: f.widgets.cc:551 msgid "Stack/Noise" msgstr "Pilha/Ruído" #: f.widgets.cc:552 msgid "Panorama" msgstr "Panorama" #: f.widgets.cc:553 msgid "Vertical Panorama" msgstr "Panorama Vertical" #: f.widgets.cc:554 msgid "PT Panorama" msgstr "Panorama PT" #: f.widgets.cc:555 msgid "Montage" msgstr "colagem" #: f.widgets.cc:556 msgid "Mashup" msgstr "Mistura" #: f.widgets.cc:562 msgid "Batch RAW" msgstr "RAW em Lote" #: f.widgets.cc:563 msgid "Script Files" msgstr "Arquivos de Rotina" #: f.widgets.cc:566 msgid "Export File List" msgstr "Exportar Lista de Arquivos" #: f.widgets.cc:574 msgid "Image Locations/Dates" msgstr "Localidade/Datas de Imagens" #: f.widgets.cc:575 msgid "Image Timeline" msgstr "Linha do Tempo de Imagens" #: f.widgets.cc:576 msgid "Search Images" msgstr "Procurar Imagens" #: f.widgets.cc:582 msgid "Brightness Graph" msgstr "Gráfico de Brilho" #: f.widgets.cc:587 msgid "Dark/Bright Pixels" msgstr "Pixels Claros/Escuros" #: f.widgets.cc:588 msgid "Monitor Color" msgstr "Cores do Monitor" #: f.widgets.cc:590 msgid "Change Language" msgstr "Trocar língua" #: f.widgets.cc:591 msgid "Missing Translations" msgstr "Traduções em falta" #: f.widgets.cc:592 msgid "Color Profile" msgstr "Perfil de Cores" #: f.widgets.cc:594 msgid "Uninstall AppImage" msgstr "Desinstalar AppImage" #: f.widgets.cc:611 msgid "All Directories" msgstr "Todos Diretórios" #: f.widgets.cc:612 msgid "Bookmarks" msgstr "Favoritos" #: f.widgets.cc:621 f.widgets.cc:646 f.widgets.cc:670 f.widgets.cc:683 msgid "Gallery" msgstr "Galeria" #: f.widgets.cc:622 f.widgets.cc:647 f.widgets.cc:671 f.widgets.cc:684 msgid "World Maps" msgstr "Mapas Mundiais" #: f.widgets.cc:623 f.widgets.cc:648 f.widgets.cc:672 f.widgets.cc:685 msgid "Net Maps" msgstr "Mapas na Rede" #: f.widgets.cc:625 f.widgets.cc:650 f.widgets.cc:1066 msgid "Favorites" msgstr "Favoritos" #: f.widgets.cc:627 msgid "Save File" msgstr "Salvar Arquivo" #: f.widgets.cc:628 msgid "Prev/Next" msgstr "Ant./Prox." #: f.widgets.cc:630 msgid "Areas" msgstr "Áreas" #: f.widgets.cc:631 msgid "Undo/Redo" msgstr "Desfazer/Refazer" #: f.widgets.cc:632 fotoxx.h:1208 msgid "Edit" msgstr "Editar" #: f.widgets.cc:633 msgid "Repair" msgstr "Reparo" #: f.widgets.cc:635 msgid "Effects" msgstr "Efeitos" #: f.widgets.cc:636 f.widgets.cc:660 msgid "Combine" msgstr "Combinar" #: f.widgets.cc:637 f.widgets.cc:661 msgid "Process" msgstr "Processamento" #: f.widgets.cc:638 f.widgets.cc:662 msgid "Tools" msgstr "Ferramentas" #: f.widgets.cc:674 msgid "Choose Map" msgstr "Escolher Mapa" #: f.widgets.cc:675 f.widgets.cc:688 msgid "Map Markers" msgstr "Marcadores de Mapa" #: f.widgets.cc:687 msgid "Map Source" msgstr "Fonte de Mapa" #: f.widgets.cc:689 msgid "Map Locations" msgstr "Localidade de Mapa" #: f.widgets.cc:753 msgid "Popup Image" msgstr "Imagem Popup" #: f.widgets.cc:757 fotoxx.h:1265 msgid "Rename" msgstr "Renomear" #: f.widgets.cc:761 msgid "Remove from Album" msgstr "Remover do Álbum" #: f.widgets.cc:763 msgid "Cut to Image Cache" msgstr "Recortar para Reserva de Imagens" #: f.widgets.cc:765 msgid "Paste Image Cache Here (clear)" msgstr "Colar Reserva de Imagens aqui (limpar)" #: f.widgets.cc:766 msgid "Paste Image Cache Here (keep)" msgstr "Colar Reserva de Imagens aqui (manter)" #: f.widgets.cc:779 msgid "Delete/Trash ..." msgstr "Excluir/Descartar ..." #: f.widgets.cc:1092 msgid "menu name is invalid" msgstr "nome de menu é inválido" #: fotoxx-18.01.cc:434 msgid "Please install missing programs:" msgstr "Por favor, instale os programas que faltam:" #: fotoxx-18.01.cc:706 #, c-format msgid "Kill active dialog? %s" msgstr "Encerrar a caixa de diálogo ativa? %s" #: fotoxx-18.01.cc:801 msgid "(reduced)" msgstr "(reduzido)" #: fotoxx-18.01.cc:802 msgid "area active" msgstr "área ativada" #: fotoxx-18.01.cc:803 msgid "dialog open" msgstr "caixa de diálogo aberta" #: fotoxx-18.01.cc:804 msgid "blocked" msgstr "bloqueado" #: fotoxx-18.01.cc:866 msgid "edits" msgstr "edições" #: fotoxx-18.01.cc:1801 msgid "File View" msgstr "Visão de Arquivo" #: fotoxx-18.01.cc:1806 msgid "Gallery View" msgstr "Visão de Galeria" #: fotoxx-18.01.cc:1811 msgid "World Map View" msgstr "Visão Mapa-mundi" #: fotoxx-18.01.cc:1816 msgid "Net Map View" msgstr "Visão do Mapa de Rede" #: fotoxx-18.01.cc:1831 msgid "Show Hidden" msgstr "Mostrar Ocultos" #: fotoxx-18.01.cc:3023 msgid "Exceed 50 anchor points" msgstr "Excedeu 50 pontos de ancoragem" #: fotoxx-18.01.cc:3250 msgid "load curve from a file" msgstr "carregar curva de arquivo" #: fotoxx-18.01.cc:3316 msgid "curve file is invalid" msgstr "arquivo de curva inválido" #: fotoxx-18.01.cc:3330 msgid "save curve to a file" msgstr "salvar curva para arquivo" #: fotoxx-18.01.cc:3399 #, c-format msgid "" "File cannot be edited \n" " %s" msgstr "" "Arquivo não pode ser editado \n" " %s" #: fotoxx-18.01.cc:3412 msgid "Too many edits, please save image" msgstr "Muitas edições, favor salvar imagem" #: fotoxx-18.01.cc:3417 msgid "this function cannot be scripted" msgstr "esta função não pode ser rotinada" #: fotoxx-18.01.cc:3434 msgid "" "Select area not active.\n" "Continue?" msgstr "" "Área selecionada não ativa.\n" "Continuar?" #: fotoxx-18.01.cc:3749 msgid "file data does not fit dialog" msgstr "dados do arquivo não cabem em caixa de diálogo" #: fotoxx-18.01.cc:3759 msgid "Save settings to a file" msgstr "Salvar configurações em arquivo" #: fotoxx-18.01.cc:4140 msgid "This action will discard changes to current image" msgstr "Esta ação irá descartar as alterações para a imagem atual" #: fotoxx-18.01.cc:4141 msgid "prior function still active" msgstr "função prévia ainda ativa" #: fotoxx-18.01.cc:4142 fotoxx.h:1229 msgid "Keep" msgstr "Manter" #: fotoxx-18.01.cc:4143 msgid "Discard" msgstr "Descartar" #: fotoxx.h:1177 msgid "Add" msgstr "Adicionar" #: fotoxx.h:1178 msgid "Add All" msgstr "Adicionar todos" #: fotoxx.h:1180 msgid "Amount" msgstr "Quantidade" #: fotoxx.h:1181 msgid "Angle" msgstr "Ângulo" #: fotoxx.h:1182 zfuncs.cc:6782 msgid "Apply" msgstr "Aplicar" #: fotoxx.h:1183 msgid "Auto" msgstr "Auto" #: fotoxx.h:1184 msgid "Black" msgstr "Preto" #: fotoxx.h:1185 msgid "Blend Width" msgstr "Quantidade de mistura" #: fotoxx.h:1187 zfuncs.cc:11368 msgid "Bottom" msgstr "Rodapé" #: fotoxx.h:1189 zfuncs.cc:6796 msgid "Browse" msgstr "Procurar" #: fotoxx.h:1190 zfuncs.cc:6782 msgid "Cancel" msgstr "Cancelar" #: fotoxx.h:1191 msgid "Center" msgstr "centralizar" #: fotoxx.h:1192 msgid "Choose" msgstr "Escolher" #: fotoxx.h:1193 msgid "Clear" msgstr "Limpar" #: fotoxx.h:1194 msgid "Close" msgstr "Fechar" #: fotoxx.h:1195 msgid "Color" msgstr "Cor" #: fotoxx.h:1196 msgid "COMPLETED" msgstr "COMPLETO" #: fotoxx.h:1197 msgid "continue" msgstr "continuar" #: fotoxx.h:1199 msgid "Copy" msgstr "Copiar" #: fotoxx.h:1200 msgid "Create" msgstr "Criar" #: fotoxx.h:1201 msgid "Curve File:" msgstr "Arquivo de curva:" #: fotoxx.h:1202 msgid "Cut" msgstr "Recortar" #: fotoxx.h:1203 msgid "Deband" msgstr "Debandar" #: fotoxx.h:1204 zfuncs.cc:6782 msgid "Delete" msgstr "Exlcuir" #: fotoxx.h:1205 msgid "Disable" msgstr "Desabilitar" #: fotoxx.h:1206 msgid "Done" msgstr "Pronto" #: fotoxx.h:1207 msgid "edge" msgstr "borda" #: fotoxx.h:1209 msgid "Enable" msgstr "Habilitar" #: fotoxx.h:1210 msgid "Erase" msgstr "Apagar" #: fotoxx.h:1211 msgid "Fetch" msgstr "Buscar" #: fotoxx.h:1212 msgid "output file already exists" msgstr "arquivo de saída já existe" #: fotoxx.h:1213 msgid "file not found" msgstr "arquivo não encontrado" #: fotoxx.h:1214 #, c-format msgid "file not found: %s" msgstr "arquivo não encontrado: %s" #: fotoxx.h:1215 #, c-format msgid "%d image files selected" msgstr "%d arquivos de imagem selecionados" #: fotoxx.h:1216 msgid "Find" msgstr "Encontrar" #: fotoxx.h:1217 msgid "Finish" msgstr "Finalizar" #: fotoxx.h:1219 msgid "Font" msgstr "Fonte" #: fotoxx.h:1221 msgid "Grid" msgstr "Grade" #: fotoxx.h:1222 zfuncs.cc:11398 msgid "Height" msgstr "Altura" #: fotoxx.h:1224 msgid "Hide" msgstr "Ocultar" #: fotoxx.h:1225 zfuncs.cc:11390 msgid "Image" msgstr "Imagem" #: fotoxx.h:1226 msgid "Images" msgstr "Imagens" #: fotoxx.h:1227 msgid "Insert" msgstr "Inserir" #: fotoxx.h:1228 msgid "Invert" msgstr "Inverter" #: fotoxx.h:1230 zfuncs.cc:11372 msgid "Left" msgstr "Esquerda" #: fotoxx.h:1231 msgid "limit" msgstr "limite" #: fotoxx.h:1232 msgid "Load" msgstr "Carga" #: fotoxx.h:1233 msgid "Make" msgstr "Gerar" #: fotoxx.h:1235 msgid "Map" msgstr "Mapa" #: fotoxx.h:1236 msgid "Match Level:" msgstr "Nível de Correspondência:" #: fotoxx.h:1237 msgid "Max" msgstr "Máximo" #: fotoxx.h:1238 msgid "mouse radius" msgstr "raio do mouse" #: fotoxx.h:1239 msgid "Negative" msgstr "Negativo" #: fotoxx.h:1240 msgid "New" msgstr "Novo" #: fotoxx.h:1241 msgid "Next" msgstr "Próximo" #: fotoxx.h:1242 zfuncs.cc:10365 msgid "No" msgstr "Não" #: fotoxx.h:1243 msgid "no images" msgstr "não há imagens" #: fotoxx.h:1244 msgid "" "image index disabled \n" " Enable?" msgstr "" "índice de imagens desativado \n" " Ativar?" #: fotoxx.h:1245 msgid "no image files selected" msgstr "nenhum arquivo de imagem selecionado" #: fotoxx.h:1246 msgid "None" msgstr "Nenhum" #: fotoxx.h:1247 msgid "no selection" msgstr "não há seleção" #: fotoxx.h:1248 msgid "OK" msgstr "OK" #: fotoxx.h:1249 msgid "image index not updated" msgstr "índice de imagens não atualizado" #: fotoxx.h:1251 msgid "Paint Radius" msgstr "Raio da Pintura" #: fotoxx.h:1252 msgid "Paste" msgstr "Colar" #: fotoxx.h:1253 msgid "Pause" msgstr "Pausar" #: fotoxx.h:1254 msgid "Percent" msgstr "Percentagem" #: fotoxx.h:1256 msgid "Presets" msgstr "Predefinições" #: fotoxx.h:1257 msgid "Prev" msgstr "Anterior" #: fotoxx.h:1258 msgid "Proceed" msgstr "Continuar" #: fotoxx.h:1260 msgid "range" msgstr "variação" #: fotoxx.h:1262 msgid "Redo" msgstr "Refazer" #: fotoxx.h:1263 msgid "Reduce" msgstr "Reduzir" #: fotoxx.h:1264 msgid "Remove" msgstr "Remover" #: fotoxx.h:1266 msgid "Replace" msgstr "Substituir" #: fotoxx.h:1267 msgid "Reserved" msgstr "Reservados" #: fotoxx.h:1268 msgid "Reset" msgstr "Redefinir" #: fotoxx.h:1269 zfuncs.cc:11376 msgid "Right" msgstr "Direita" #: fotoxx.h:1270 msgid "Rotate" msgstr "Rotacionar" #: fotoxx.h:1271 msgid "Run" msgstr "Executar" #: fotoxx.h:1272 msgid "Save" msgstr "Salvar" #: fotoxx.h:1273 msgid "Search" msgstr "Buscar" #: fotoxx.h:1274 msgid "Seconds" msgstr "Segundos" #: fotoxx.h:1275 msgid "Select" msgstr "Selecionar" #: fotoxx.h:1277 msgid "Show" msgstr "Mostrar" #: fotoxx.h:1278 msgid "Size" msgstr "Tamanho" #: fotoxx.h:1279 msgid "Start" msgstr "Iniciar" #: fotoxx.h:1280 msgid "Stop" msgstr "Parar" #: fotoxx.h:1281 msgid "Strength" msgstr "Força" #: fotoxx.h:1282 msgid "Threshold" msgstr "Limiar" #: fotoxx.h:1283 #, c-format msgid "exceed %d files" msgstr "excedido %d arquivos" #: fotoxx.h:1284 zfuncs.cc:11364 msgid "Top" msgstr "Topo" #: fotoxx.h:1286 msgid "Trash" msgstr "Lixeira" #: fotoxx.h:1287 msgid "Trim" msgstr "Recortar" #: fotoxx.h:1288 msgid "Undo All" msgstr "Desfazer tudo" #: fotoxx.h:1289 msgid "Undo Last" msgstr "Desfazer último" #: fotoxx.h:1290 msgid "Undo" msgstr "Desfazer" #: fotoxx.h:1291 msgid "Unfinish" msgstr "Inacabado" #: fotoxx.h:1292 msgid "Unselect" msgstr "Deselecionar" #: fotoxx.h:1293 msgid "View" msgstr "Ver" #: fotoxx.h:1294 msgid "Web" msgstr "Web" #: fotoxx.h:1295 msgid "White" msgstr "Branco" #: fotoxx.h:1296 zfuncs.cc:11394 msgid "Width" msgstr "Largura" #: fotoxx.h:1297 msgid "x-offset" msgstr "deslocamento-x" #: fotoxx.h:1298 msgid "y-offset" msgstr "deslocamento-y" #: fotoxx.h:1299 zfuncs.cc:10365 msgid "Yes" msgstr "Sim" #: zfuncs.cc:305 msgid "LOW MEMORY - performance may be poor" msgstr "MEMÓRIA CHEIA - performance deve baixar" #: zfuncs.cc:1722 #, c-format msgid "" "create directory? \n" " %s" msgstr "" "criar diretório? \n" " %s" #: zfuncs.cc:4878 msgid "user guide not found" msgstr "guia de usuário não encontrado" #: zfuncs.cc:5742 #, c-format msgid "cannot open file %s" msgstr "não foi possível abrir arquivo %s" #: zfuncs.cc:5765 msgid "save text to file" msgstr "salvar texto para arquivo" #: zfuncs.cc:6786 msgid "menu text" msgstr "texto do menu" #: zfuncs.cc:6787 msgid "menu func" msgstr "função do menu" #: zfuncs.cc:6788 msgid "menu icon" msgstr "ícone do menu" #: zfuncs.cc:6789 msgid "icon size" msgstr "tamanho do ícone" #: zfuncs.cc:6792 msgid "Bold" msgstr "Negrito" #: zfuncs.cc:6799 msgid "close window" msgstr "fechar janela" #: zfuncs.cc:10488 zfuncs.cc:11021 zfuncs.cc:11351 msgid "cancel" msgstr "cancelar" #: zfuncs.cc:10982 msgid "choose file" msgstr "escolher arquivo" #: zfuncs.cc:10987 msgid "choose files" msgstr "escolher arquivos" #: zfuncs.cc:10992 msgid "save" msgstr "salvar" #: zfuncs.cc:10998 msgid "choose folder" msgstr "escolher diretório" #: zfuncs.cc:11003 msgid "choose folders" msgstr "escolher diretórios" #: zfuncs.cc:11008 msgid "create folder" msgstr "criar pasta" #: zfuncs.cc:11015 msgid "hidden" msgstr "oculto" #: zfuncs.cc:11351 msgid "done" msgstr "pronto" #: zfuncs.cc:11381 msgid "image scale" msgstr "escala de imagem" #: zfuncs.cc:11383 msgid "percent" msgstr "porcentagem" #~ msgid "Retinex" #~ msgstr "Retinex" fotoxx-18.01.1/appdata/0000755000175000017500000000000013222767271013274 5ustar micomicofotoxx-18.01.1/appdata/fotoxx.appdata.xml0000744000175000017500000000237213222767271016763 0ustar micomico kornelix.fotoxx CC0 GPL-3.0+ fotoxx Photo Editor and Collection Manager

Edit photos and manage a large collection. Includes thumbnail browser/navigator, RAW file conversion, a comprehensive set of edit functions working in 24-bit color, edit selected image objects or areas independently from background, area copy/paste within and across images, file versioning, batch operations, scriptable batch editing, HDR, HDF, panorama, image stacking, photomontage, albums (alternate views), slide show with animated transitions and zoom, metadata edit and report, image search using any metadata and/or directory/file names, geographic maps with clickable markers to show images at location.

fotoxx views https://kornelix.net/fotoxx/appdata.jpg https://kornelix.net
fotoxx-18.01.1/f.area.cc0000644000175000017500000044757613222767271013354 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image editor - select area functions Select an area within the current image. Subsequent edit functions are carried out within the area. Otherwise, edit functions apply to the entire image. sa_stat 0/1/2/3/4 = none/edit/unused/complete/disabled sa_mode is current area selection method: mode_rect select rectangle by drag/click mode_ellipse select ellipse by drag mode_draw freehand draw by drag/click mode_follow follow edge indicated by clicks mode_replace freehand draw and replace nearby pixels mode_mouse select area within mouse (radius) mode_onecolor select one matching color within mouse mode_allcolors select all matching colors within mouse mode_image select whole image m_select select area dialog sa_finish finish selected areas sa_finish_auto auto finish areas where possible sa_unfinish unfinish selected areas m_select_find_gap find a gap in an area outline m_select_hairy hairy edge select function m_select_show show area outlines m_select_hide hide area outlines m_select_enable enable area m_select_disable disable area (reversible) m_select_invert invert selected area m_select_unselect delete selected area sa_show show area callable function sa_show_rect show area within an image rectangle sa_validate validate area for current image sa_enable enable area callable function sa_disable disable area callable function sa_invert invert area callable function sa_unselect delete area callable function sa_edgecalc calculate area edge distances sa_edgecreep adjust area edges +-1 pixel sa_blendfunc compute edge blend coefficient m_select_save save area to PNG file with alpha channel m_select_open open PNG file and make a select area m_select_paste paste area into image select_paste paste area in memory onto image *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // user select area dialog // line drawing and selection by color range are combined void m_select(GtkWidget *, cchar *) // menu function { int select_dialog_event(zdialog *, cchar *event); // dialog event and completion funcs cchar *title = ZTX("Select Area for Edits"); cchar *helptext = ZTX("Press F1 for help"); zdialog *zd; F1_help_topic = "select_area"; if (! curr_file) return; // no image if (zdsela) return; // already active if (CEF && CEF->Farea != 2) { // active edit function zmessageACK(Mwin,ZTX("Select Area not supported \n" "by this edit function")); return; } /*** _______________________________________________________________________ | Select Area for Edits | | Press F1 for help | | [x] select rectangle [x] select ellipse | | [x] freehand draw [x] follow edge [x] draw/replace | | [x] select area within mouse circle | | [x] select matching color within mouse circle: [####] | | [x] select all matching colors within mouse circle: | | mouse radius [___] match level % [___] search range [___] | | Area Edge Blend Width [___] Edge Creep [___] | | Line Color: [red] [green] [blue] [black] [white] | | | | [Show] [Hide] [Finish] [Unselect] [Enable] [Disable] [Invert] [Done] | |_______________________________________________________________________| ***/ zdsela = zdialog_new(title,Mwin,null); zd = zdsela; zdialog_add_widget(zd,"label","labhelp","dialog",helptext,"space=3"); zdialog_add_widget(zd,"hbox","hbshape","dialog"); zdialog_add_widget(zd,"check","ckrect","hbshape",ZTX("select rectangle"),"space=5"); zdialog_add_widget(zd,"check","ckelips","hbshape",ZTX("select ellipse"),"space=10"); zdialog_add_widget(zd,"hbox","hbdraw","dialog"); zdialog_add_widget(zd,"check","ckdraw","hbdraw",ZTX("freehand draw"),"space=5"); zdialog_add_widget(zd,"check","ckfollow","hbdraw",ZTX("follow edge"),"space=10"); zdialog_add_widget(zd,"check","ckrepl","hbdraw",ZTX("draw/replace"),"space=10"); zdialog_add_widget(zd,"hbox","hbm1","dialog"); zdialog_add_widget(zd,"check","ckmouse","hbm1",ZTX("select area within mouse circle"),"space=5"); zdialog_add_widget(zd,"hbox","hbm2","dialog"); zdialog_add_widget(zd,"check","ckonecolor","hbm2",ZTX("select one matching color within mouse circle:"),"space=5"); zdialog_add_widget(zd,"colorbutt","onecolor","hbm2","0|0|255","space=5"); zdialog_add_ttip(zd,"onecolor",ZTX("first select the checkbox, then \n" "shift+click on image to set the color")); zdialog_add_widget(zd,"hbox","hbm3","dialog"); zdialog_add_widget(zd,"check","ckallcolors","hbm3",ZTX("select all matching colors within mouse circle"),"space=5"); zdialog_add_widget(zd,"hbox","hbmm","dialog"); zdialog_add_widget(zd,"label","labmr","hbmm",Bmouseradius,"space=5"); zdialog_add_widget(zd,"zspin","mouserad","hbmm","1|300|1|20","space=5|size=3"); zdialog_add_widget(zd,"label","space","hbmm",0,"space=8"); zdialog_add_widget(zd,"label","labmatch","hbmm",ZTX("match level %")); zdialog_add_widget(zd,"zspin","colormatch","hbmm","0|100|1|90","space=5|size=3"); zdialog_add_widget(zd,"label","space","hbmm",0,"space=8"); zdialog_add_widget(zd,"label","labrange","hbmm",ZTX("search range")); zdialog_add_widget(zd,"zspin","searchrange","hbmm","1|20|1|5","space=5|size=3"); zdialog_add_widget(zd,"hbox","hbm5","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labblend","hbm5",ZTX("Area Edge Blend Width"),"space=5"); zdialog_add_widget(zd,"zspin","blendwidth","hbm5","0|500|1|0","size=2"); zdialog_add_widget(zd,"label","space","hbm5",0,"space=10"); zdialog_add_widget(zd,"label","labcreep","hbm5",ZTX("Edge Creep"),"space=3"); zdialog_add_widget(zd,"button","creep+","hbm5"," + ","space=3"); zdialog_add_widget(zd,"button","creep-","hbm5"," ‒ ","space=3"); zdialog_add_widget(zd,"hbox","hbm5","dialog"); zdialog_add_widget(zd,"label","labcolor","hbm5",ZTX("Line Color:"),"space=5"); zdialog_add_widget(zd,"imagebutt","red","hbm5","redball.png","size=15|space=5"); zdialog_add_widget(zd,"imagebutt","green","hbm5","greenball.png","size=15|space=5"); zdialog_add_widget(zd,"imagebutt","blue","hbm5","blueball.png","size=15|space=5"); zdialog_add_widget(zd,"imagebutt","black","hbm5","blackball.png","size=15|space=5"); zdialog_add_widget(zd,"imagebutt","white","hbm5","whiteball.png","size=15|space=5"); zdialog_add_widget(zd,"hsep","hsep3","dialog",0,"space=4"); zdialog_add_widget(zd,"hbox","hbb2","dialog",0,"space=2"); zdialog_add_widget(zd,"label","space","hbb2",0,"expand"); zdialog_add_widget(zd,"button","show","hbb2",Bshow,"space=2"); zdialog_add_widget(zd,"button","hide","hbb2",Bhide,"space=2"); zdialog_add_widget(zd,"button","finish","hbb2",Bfinish,"space=2"); zdialog_add_widget(zd,"button","unselect","hbb2",Bunselect,"space=2"); zdialog_add_widget(zd,"button","enable","hbb2",Benable,"space=2"); zdialog_add_widget(zd,"button","disable","hbb2",Bdisable,"space=2"); zdialog_add_widget(zd,"button","invert","hbb2",Binvert,"space=2"); zdialog_add_widget(zd,"button","done","hbb2",Bdone,"space=2"); zdialog_add_widget(zd,"label","space","hbb2",0,"space=2"); zdialog_add_ttip(zd,"labblend",ZTX("area edits fade away within edge distance")); zdialog_add_ttip(zd,"blendwidth",ZTX("area edits fade away within edge distance")); sa_mouseradius = 30; // initial values matching dialog sa_colormatch = 90; sa_searchrange = 2; sa_blend = 0; sa_mode = 0; zdialog_stuff(zd,"ckrect",0); zdialog_stuff(zd,"ckelips",0); zdialog_stuff(zd,"ckdraw",0); zdialog_stuff(zd,"ckfollow",0); zdialog_stuff(zd,"ckrepl",0); zdialog_stuff(zd,"ckmouse",0); zdialog_stuff(zd,"ckonecolor",0); zdialog_stuff(zd,"ckallcolors",0); zdialog_run(zd,select_dialog_event,"save"); // run dialog - parallel if (sa_stat) sa_show(1,0); // show existing area return; } // dialog event and completion callback function int select_dialog_event(zdialog *zd, cchar *event) { int Nckevents = 8, ii, kk, cc; cchar *ckevents[8] = { "ckrect", "ckelips", "ckdraw", "ckfollow", "ckrepl", "ckmouse", "ckonecolor", "ckallcolors" }; if (! curr_file) event = "done"; // image went away if (FGWM != 'F') event = "done"; if (strmatch(event,"done") || zd->zstat) { // done or cancel freeMouse(); // disconnect mouse function zdialog_free(zdsela); // kill dialog if (sa_stat) { cc = Fpxb->ww * Fpxb->hh; // check if any pixels mapped for (ii = 0; ii < cc; ii++) if (sa_pixmap[ii]) break; if (ii == cc) sa_unselect(); // no, delete area } return 0; } if (CEF && CEF->Fpreview && CEF->zd) // use full-size image zdialog_send_event(CEF->zd,"fullsize"); sa_validate(); // validate area, remove if no good if (CEF && CEF->Farea != 2) { // select area not supported printz("*** select area ignored for this edit function \n"); return 0; } if (! sa_stat) { // no area, create one cc = Fpxb->ww * Fpxb->hh * sizeof(uint16); // allocate sa_pixmap[] for new area sa_pixmap = (uint16 *) zmalloc(cc); // maps pixels in area memset(sa_pixmap,0,cc); sa_currseq = sa_Ncurrseq = 0; // reset selection sequence sa_Npixel = sa_blend = sa_calced = 0; sa_fww = Fpxb->ww; // valid image size for area sa_fhh = Fpxb->hh; sa_stat = 1; // status = edit sa_mode = 0; zdialog_stuff(zd,"blendwidth",0); // init. blend width = 0 } for (ii = 0; ii < Nckevents; ii++) // look for checkbox event { if (strmatch(event,ckevents[ii])) { // checkbox was changed zdialog_fetch(zd,event,kk); // checkbox status if (kk) { // ON sa_mode = ii+1; // edit mode 1-8 if (sa_mode == mode_replace) sa_unfinish(); // unmap internal pixels sa_stat = 1; // active edit status sa_Npixel = sa_blend = sa_calced = 0; // make area unfinished zdialog_stuff(zd,"blendwidth",0); sa_show(1,0); // show area } else sa_mode = 0; // OFF, no edit mode, edit paused for (kk = 0; kk < Nckevents; kk++) // other checkboxes >> off if (kk != ii) zdialog_stuff(zd,ckevents[kk],0); } } if (strmatch(event,"mouserad")) // mouse selection radius zdialog_fetch(zd,"mouserad",sa_mouseradius); if (strmatch(event,"colormatch")) // mouse color match limit, 0 - 99.9 zdialog_fetch(zdsela,"colormatch",sa_colormatch); if (strmatch(event,"searchrange")) // mouse color match search range, x radius zdialog_fetch(zdsela,"searchrange",sa_searchrange); if (strmatch(event,"show")) sa_show(1,0); // show area if (strmatch(event,"hide")) sa_show(0,0); // hide area if (strmatch(event,"finish")) sa_finish(); // finish (finalize) area if (strmatch(event,"unselect")) sa_unselect(); // unselect area if (strmatch(event,"enable")) sa_enable(); // enable area if (strmatch(event,"disable")) sa_disable(); // disable area if (strmatch(event,"invert")) sa_invert(); // invert area if (strmatch(event,"blendwidth") && sa_stat == 3) { // blend width changed and area finished zdialog_fetch(zd,"blendwidth",sa_blend); // update sa_blend if (sa_blend > 0) sa_edgecalc(); // do edge calc. if not already if (sa_calced && CEF && CEF->zd) // if edit is active zdialog_send_event(CEF->zd,event); // notify edit dialog } if (strmatchN(event,"creep",5) && sa_stat == 3) { // edge creep changed and area finished if (event[5] == '+') sa_edgecreep(+1); else sa_edgecreep(-1); sa_blend = 0; zdialog_stuff(zd,"blendwidth",0); } if (strmatch(event,"red")) LINE_COLOR = RED; if (strmatch(event,"green")) LINE_COLOR = GREEN; if (strmatch(event,"blue")) LINE_COLOR = BLUE; if (strmatch(event,"black")) LINE_COLOR = BLACK; if (strmatch(event,"white")) LINE_COLOR = WHITE; if (strstr("red green blue black white",event)) Fpaint2(); if (sa_stat == 1 && sa_mode && Fshowarea) // active edit mode { if (sa_mode == mode_rect) takeMouse(sa_geom_mousefunc,dragcursor); // rectangle if (sa_mode == mode_ellipse) takeMouse(sa_geom_mousefunc,dragcursor); // ellipse if (sa_mode == mode_draw) takeMouse(sa_draw_mousefunc,drawcursor); // freehand draw if (sa_mode == mode_follow) takeMouse(sa_draw_mousefunc,drawcursor); // follow edge if (sa_mode == mode_replace) takeMouse(sa_draw_mousefunc,drawcursor); // replace nearby if (sa_mode == mode_mouse) takeMouse(sa_mouse_select,0); // mouse radius select if (sa_mode == mode_onecolor) takeMouse(sa_mouse_select,0); // mouse radius, one color select if (sa_mode == mode_allcolors) takeMouse(sa_mouse_select,0); // mouse radius, all colors select } else { // edit paused or done freeMouse(); // disconnect mouse gdk_window_set_cursor(gdkwin,null); // normal cursor for (kk = 0; kk < Nckevents; kk++) // all checkboxes off zdialog_stuff(zd,ckevents[kk],0); sa_mode = 0; } return 0; } // select area mouse function - select a rectangle or ellipse void sa_geom_mousefunc() { static int mx1, my1, mx2, my2; static int mdx0, mdy0, drag; float a, b, a2, b2; float x, y, x2, y2, cx, cy; int px, py; cairo_t *cr; if (sa_stat != 1) return; // area gone? if (sa_currseq > sa_maxseq-2) { zmessageACK(Mwin,ZTX("exceed %d edits"),sa_maxseq); // cannot continue return; } cr = draw_context_create(gdkwin,draw_context); // 17.04 if (RMclick) // right mouse click { RMclick = 0; sa_unselect_pixels(cr); // remove latest selection drag = 0; Fpaint2(); draw_context_destroy(draw_context); // 17.04 return; } if (! Mxdrag && ! Mydrag) { // no drag underway draw_context_destroy(draw_context); // 17.04 return; } if (Mxdown != mdx0 || Mydown != mdy0) { // new drag initiated mdx0 = Mxdown; mdy0 = Mydown; mx1 = mdx0; // drag start, one corner my1 = mdy0; drag = 0; Mxdrag = Mydrag = 0; draw_context_destroy(draw_context); // 17.04 return; } mx2 = Mxdrag; // drag continues, 2nd corner my2 = Mydrag; Mxdrag = Mydrag = 0; if (drag) sa_unselect_pixels(cr); // remove prior drag result sa_nextseq(); // next sequence number drag = 1; if (sa_mode == mode_rect) // draw rectangle { sa_draw_line(mx1,my1,mx2,my1,cr); // draw 4 lines sa_draw_line(mx2,my1,mx2,my2,cr); sa_draw_line(mx2,my2,mx1,my2,cr); sa_draw_line(mx1,my2,mx1,my1,cr); } if (sa_mode == mode_ellipse) // draw ellipse { a = abs(mx2 - mx1); // ellipse constants from b = abs(my2 - my1); // enclosing rectangle a2 = a * a; b2 = b * b; cx = mx1; // center at drag origin cy = my1; for (y = -b; y < b; y++) // step through y values { y2 = y * y; x2 = a2 * (1 - y2 / b2); x = sqrtf(x2); // corresp. x values, + and - py = y + cy; px = cx - x + 0.5; sa_draw1pix(px,py,cr); // draw 2 points on ellipse px = cx + x + 0.5; sa_draw1pix(px,py,cr); } for (x = -a; x < a; x++) // step through x values { x2 = x * x; y2 = b2 * (1 - x2 / a2); y = sqrtf(y2); // corresp. y values, + and - px = cx + x; py = cy - y + 0.5; sa_draw1pix(px,py,cr); // draw 2 points on ellipse py = cy + y + 0.5; sa_draw1pix(px,py,cr); } } draw_context_destroy(draw_context); // 17.04 return; } // select area mouse function - freehand draw, follow edge, replace nearby void sa_draw_mousefunc() { void sa_follow_edge(int mx1, int my1, int &mx2, int &my2, cairo_t *cr); void sa_redraw(int mx1, int my1, int mx2, int my2, cairo_t *cr); int mx1, my1, mx2, my2; int ii, npdist, npx, npy; int click, newseq, thresh; static int drag = 0, openend = 0; static int mdx0, mdy0, mdx1, mdy1; cairo_t *cr; if (sa_stat != 1) return; // area gone? cr = draw_context_create(gdkwin,draw_context); sa_thresh = 4.0 / Mscale + 1; // mouse pixel distance threshold if (! (LMclick || RMclick || Mxdrag || Mydrag)) { // no mouse action if (openend) { openend = 0; // close pending gap after mx1 = sa_endpx[sa_currseq]; // prior draw/replace my1 = sa_endpy[sa_currseq]; thresh = 3 * sa_thresh; npdist = sa_nearpix(mx1,my1,thresh,mx2,my2,1); if (npdist) sa_draw_line(mx1,my1,mx2,my2,cr); } goto draw_exit; } click = newseq = 0; if (LMclick || Mxdrag || Mydrag) // left mouse click or mouse drag { if (LMclick) // left mouse click { mx1 = mx2 = Mxclick; // click position my1 = my2 = Myclick; newseq++; click++; drag = 0; } else // drag motion { if (Mxdown != mdx0 || Mydown != mdy0) { // new drag initiated mdx0 = mdx1 = Mxdown; mdy0 = mdy1 = Mydown; newseq++; } mx1 = mdx1; // drag start my1 = mdy1; mx2 = Mxdrag; // drag position my2 = Mydrag; mdx1 = mx2; // next drag start mdy1 = my2; drag++; click = 0; } if (Mbutton == 3) // right mouse >> erase { while (true) { thresh = sa_thresh; npdist = sa_nearpix(mx2,my2,thresh,npx,npy,0); // find nearest pixel if (! npdist) break; ii = npy * Fpxb->ww + npx; if (sa_pixmap[ii]) { sa_pixmap[ii] = 0; // unmap pixel erase_pixel(npx,npy,cr); // erase pixel } } goto draw_exit; } if (sa_currseq > sa_maxseq-2) { zmessageACK(Mwin,ZTX("exceed %d edits"),sa_maxseq); // cannot continue goto draw_exit; } if (sa_currseq == 0 && newseq) // 1st pixel(s) of 1st sequence { sa_nextseq(); // set next (1st) sequence no. sa_draw_line(mx1,my1,mx2,my2,cr); // draw initial pixel or line sa_endpx[sa_currseq] = mx2; sa_endpy[sa_currseq] = my2; goto draw_exit; } if (click) { mx1 = sa_endpx[sa_currseq]; // prior sequence end pixel my1 = sa_endpy[sa_currseq]; // (before this click) } if (drag) { if (newseq) thresh = 2 * sa_thresh; // new drag threshold else thresh = 5 * sa_thresh; // continuation drag threshold npx = sa_endpx[sa_currseq]; // distance from prior end pixel npy = sa_endpy[sa_currseq]; // (before this drag) if (abs(mx1-npx) < thresh && abs(my1-npy) < thresh) { mx1 = sa_endpx[sa_currseq]; // if < threshold, connect this my1 = sa_endpy[sa_currseq]; // drag to prior drag or click } } if (drag > 50 && sa_mode != mode_replace) newseq = 1; // incr. sequence each 50 pixels if (newseq) { sa_nextseq(); // set next sequence no. drag = 1; // drag length within sequence } if (sa_mode == mode_draw) sa_draw_line(mx1,my1,mx2,my2,cr); // draw line from end pixel to mouse if (sa_mode == mode_follow) sa_follow_edge(mx1,my1,mx2,my2,cr); // follow edge from end pixel to mouse if (sa_mode == mode_replace) sa_redraw(mx1,my1,mx2,my2,cr); // tweak end pixel to mouse sa_endpx[sa_currseq] = mx2; // set end pixel for this sequence sa_endpy[sa_currseq] = my2; if (sa_mode == mode_replace) openend = 1; else openend = 0; } else if (RMclick) // right mouse click sa_unselect_pixels(cr); // remove latest selection draw_exit: draw_context_destroy(draw_context); LMclick = RMclick = 0; // stop further mouse action Mxdrag = Mydrag = 0; return; } // Find the nearest drawn pixel within a radius of a given pixel. // Returns distance to pixel, or zero if nothing found. // Returns 1 for adjacent or diagonally adjacent pixel. // fx flag: exclude current selection (sequence no.) from search. int sa_nearpix(int mx, int my, int rad2, int &npx, int &npy, int fx) { int ii, rad, qx, qy, dx, dy; int mindist, dist; npx = npy = 0; mindist = (rad2+1) * (rad2+1); for (rad = 1; rad <= rad2; rad++) // seek neighbors within range { if (rad * rad > mindist) break; // can stop searching now for (qx = mx-rad; qx <= mx+rad; qx++) // search within rad for (qy = my-rad; qy <= my+rad; qy++) { if (qx != mx-rad && qx != mx+rad && // exclude within rad-1 qy != my-rad && qy != my+rad) continue; // (already searched) if (qx < 0 || qx > Fpxb->ww-1) continue; if (qy < 0 || qy > Fpxb->hh-1) continue; ii = qy * Fpxb->ww + qx; if (! sa_pixmap[ii]) continue; if (fx && sa_pixmap[ii] == sa_currseq) continue; // exclude curr. selection dx = (mx - qx) * (mx - qx); // found pixel dy = (my - qy) * (my - qy); dist = dx + dy; // distance**2 if (dist < mindist) { mindist = dist; npx = qx; // save nearest pixel found npy = qy; } } } if (npx + npy) return sqrt(mindist) + 0.5; return 0; } // draw a line between two given pixels // add all in-line pixels to sa_pixmap[] void sa_draw_line(int px1, int py1, int px2, int py2, cairo_t *cr) { int pxm, pym; float slope; int crflag = 0; if (sa_stat != 1) return; // area gone? if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } if (px1 == px2 && py1 == py2) { // only one pixel sa_draw1pix(px1,py1,cr); if (crflag) draw_context_destroy(draw_context); return; } if (abs(py2 - py1) > abs(px2 - px1)) { slope = 1.0 * (px2 - px1) / (py2 - py1); if (py2 > py1) { for (pym = py1; pym <= py2; pym++) { pxm = round(px1 + slope * (pym - py1)); sa_draw1pix(pxm,pym,cr); } } else { for (pym = py1; pym >= py2; pym--) { pxm = round(px1 + slope * (pym - py1)); sa_draw1pix(pxm,pym,cr); } } } else { slope = 1.0 * (py2 - py1) / (px2 - px1); if (px2 > px1) { for (pxm = px1; pxm <= px2; pxm++) { pym = round(py1 + slope * (pxm - px1)); sa_draw1pix(pxm,pym,cr); } } else { for (pxm = px1; pxm >= px2; pxm--) { pym = round(py1 + slope * (pxm - px1)); sa_draw1pix(pxm,pym,cr); } } } if (crflag) draw_context_destroy(draw_context); return; } // add to select area and draw one pixel if not already // mwcr must be set by caller void sa_draw1pix(int px, int py, cairo_t *cr) { int ii; if (px < 0 || px > Fpxb->ww-1) return; if (py < 0 || py > Fpxb->hh-1) return; ii = Fpxb->ww * py + px; if (! sa_pixmap[ii]) { // if not already selected, sa_pixmap[ii] = sa_currseq; // map pixel to curr. selection sa_Ncurrseq++; } draw_pixel(px,py,cr); // draw pixel return; } // Find series of edge pixels from px1/py1 to px2/py2 and connect them together. void sa_follow_edge(int px1, int py1, int &px2, int &py2, cairo_t *cr) { float sa_get_contrast(int px, int py); float px3, py3, px4, py4, px5, py5, px6, py6; float dx, dy, dist, contrast, maxcontrast; int crflag = 0; if (sa_stat != 1) return; // area gone? if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } px3 = px1; // p3 progresses from p1 to p2 py3 = py1; while (true) { dx = px2 - px3; dy = py2 - py3; dist = sqrt(dx * dx + dy * dy); // last segment if (dist < 5) break; // line laggs mouse 5 pixels px4 = px3 + dx / dist; // p4 = p3 moved toward p2 py4 = py3 + dy / dist; maxcontrast = 0; px6 = px4; py6 = py4; for (int ii = -2; ii <= +2; ii++) // p5 points are in a line through p4 { // and perpendicular to p4 - p2 px5 = px4 + ii * dy / dist; py5 = py4 - ii * dx / dist; contrast = sa_get_contrast(px5,py5); contrast *= (7 - abs(ii)); // favor points closer together if (contrast > maxcontrast) { px6 = px5; // p6 = highest contrast point in p5 py6 = py5; maxcontrast = contrast; } } sa_draw_line(px3,py3,px6,py6,cr); // draw p3 to p6 px3 = px6; // next p3 py3 = py6; } px2 = px3; // return lagging end point py2 = py3; if (crflag) draw_context_destroy(draw_context); return; } // freehand draw while erasing nearby pixels, effectively replacing them void sa_redraw(int mx1, int my1, int mx2, int my2, cairo_t *cr) { int ii, npx, npy; int thresh, npdist, d1, d2; int crflag = 0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } thresh = 2 * sa_thresh; npdist = sa_nearpix(mx1,my1,thresh,npx,npy,0); // nearest pixel to (mx1,my1) if (npdist) { ii = npy * Fpxb->ww + npx; if (sa_pixmap[ii] != sa_currseq) { // if not in current line, sa_pixmap[ii] = sa_currseq; // add to current line sa_draw_line(npx,npy,mx1,my1,cr); } } sa_draw_line(mx1,my1,mx2,my2,cr); // draw line from (mx1,my1) to (mx2,my2) while (true) { npdist = sa_nearpix(mx2,my2,thresh,npx,npy,1); // nearest pixel to (mx2,my2) not in line if (! npdist) break; d1 = (npx-mx1)*(npx-mx1) + (npy-my1)*(npy-my1); d2 = (npx-mx2)*(npx-mx2) + (npy-my2)*(npy-my2); if (d2 >= d1) break; ii = npy * Fpxb->ww + npx; // motion toward pixel sa_pixmap[ii] = 0; // unmap pixel erase_pixel(npx,npy,cr); // erase pixel } if (crflag) draw_context_destroy(draw_context); return; } // Find max. contrast between neighbors on opposite sides of given pixel float sa_get_contrast(int px, int py) { int map[4][2] = { {1, 0}, {1, 1}, {0, 1}, {-1, 1} }; int ii, qx, qy; uint8 *pix1, *pix2; float match, contrast, maxcontrast = 0; if (px < 1 || px > Fpxb->ww-2) return 0; // avoid edge pixels if (py < 1 || py > Fpxb->hh-2) return 0; for (ii = 0; ii < 4; ii++) // compare pixels around target { // e.g. (px-1,py) to (px+1,py) qx = map[ii][0]; qy = map[ii][1]; pix1 = PXBpix(Fpxb,px+qx,py+qy); pix2 = PXBpix(Fpxb,px-qx,py-qy); match = PIXMATCH(pix1,pix2); // 0..1 = zero..perfect match contrast = 1.0 - match; // max. contrast = 1.0 if (contrast > maxcontrast) maxcontrast = contrast; } return maxcontrast; } // mouse function for edit modes with mouse / color selection // sa_mode = mouse = select area within mouse radius // sa_mode = onecolor = select one matching color within mouse radius // sa_mode = allcolors = select all matching colors within mouse radius // and extend to contiguous matching colors // if left click or drag, find and select matching pixels // if right click, unselect last selection // if right drag, find and unselect matching pixels void sa_mouse_select() { void sa_mouse_select1(int mode, int select); void sa_mouse_select2(int select); int newdrag; static int pxcc, mxdown, mydown, dragseq; if (sa_stat != 1) return; // area gone? if (sa_mode == mode_allcolors && ! sa_pixselc) { // allocate memory for this mode pxcc = Fpxb->ww * Fpxb->hh; sa_stackdirec = (char *) zmalloc(pxcc); sa_stackii = (int *) zmalloc(4*pxcc); sa_maxstack = pxcc; sa_Nstack = 0; sa_pixselc = (char *) zmalloc(pxcc); } if (sa_mode != mode_allcolors && sa_pixselc) { // free memory otherwise zfree(sa_stackdirec); zfree(sa_stackii); zfree(sa_pixselc); sa_stackdirec = 0; sa_stackii = 0; sa_pixselc = 0; } if (sa_pixselc) memset(sa_pixselc,0,pxcc); // do always if (LMclick) { // left mouse click sa_mousex = Mxclick; sa_mousey = Myclick; LMclick = 0; sa_nextseq(); // set next sequence no. dragseq = 0; // reset drag counter if (sa_mode == mode_onecolor) sa_mouse_select1(1,1); // set color for matching within mouse else sa_mouse_select2(1); // select in mouse + matching colors beyond } if (RMclick) { // right mouse click RMclick = 0; sa_unselect_pixels(0); // remove latest selection sa_show(1,0); // show area } if (Mxdrag || Mydrag) // drag is underway { sa_mousex = Mxdrag; // new mouse position sa_mousey = Mydrag; Mxdrag = Mydrag = 0; newdrag = 0; if (Mxdown != mxdown || Mydown != mydown) { // detect if new drag started newdrag = 1; // yes mxdown = Mxdown; // save drag start position mydown = Mydown; } if (Mbutton == 1) { // left drag, select matching colors if (sa_mode == mode_onecolor) sa_mouse_select1(2,1); // select matching colors within mouse else sa_mouse_select2(1); // select in mouse + matching colors beyond if (newdrag || ++dragseq > 30) { // limit work per sequence no. sa_nextseq(); // set next sequence no. dragseq = 0; // reset drag counter } } if (Mbutton == 3) { // right drag, find and unselect pixels if (sa_mode == mode_onecolor) sa_mouse_select1(2,0); else sa_mouse_select2(0); /// sa_map_pixels(); removed 17.08 // re-establish area edge } } draw_mousecircle(Mxposn,Myposn,sa_mouseradius,0,0); // move mouse circle with mouse return; } // Left click - set one color to match from pixel at mouse position. // Left/right drag - select/unselect matching pixels within mouse radius. void sa_mouse_select1(int mode, int select) { int mrad, mrad2; int rad2, ii, jj; int px, py, rx, ry; int xlo, xhi, ylo, yhi, newpix; uint8 *pix1; float match1, match2; static float red, green, blue; static char colorbutt[16]; px = sa_mousex; py = sa_mousey; if (px < 0 || px > Fpxb->ww-2) return; // mouse outside image if (py < 0 || py > Fpxb->hh-2) return; if (mode == 1) // left click { red = green = blue = 0; for (ii = -1; ii <= +1; ii++) // get mean color for 3x3 pixels for (jj = -1; jj <= +1; jj++) // centered at px, py { pix1 = PXBpix(Fpxb,px+ii,py+jj); red += pix1[0]; green += pix1[1]; blue += pix1[2]; } red = red / 9.0; // color to match green = green / 9.0; blue = blue / 9.0; snprintf(colorbutt,16,"%.0f|%.0f|%.0f",red,green,blue); // set button to new match color zdialog_stuff(zdsela,"onecolor",colorbutt); return; } // select = 1/0 for left/right drag // left or right drag // test all pixels within mouse, select/unselect matching colors zdialog_fetch(zdsela,"onecolor",colorbutt,16); // get color button color ii = sscanf(colorbutt,"%f|%f|%f",&red,&green,&blue); if (ii != 3) { printz("color button error: %d %.0f|%.0f|%.0f \n",ii,red,green,blue); return; } match1 = 0.01 * sa_colormatch; // color match level, 0.01 to 1.0 sa_Ncurrseq = 0; // count newly selected pixels mrad = sa_mouseradius; mrad2 = mrad * mrad; xlo = px - mrad; // track changed area xhi = px + mrad; ylo = py - mrad; yhi = py + mrad; for (rx = -mrad; rx <= mrad; rx++) // loop every pixel in radius of mouse for (ry = -mrad; ry <= mrad; ry++) { rad2 = rx * rx + ry * ry; if (rad2 > mrad2) continue; // outside radius px = sa_mousex + rx; py = sa_mousey + ry; if (px < 0 || px >= Fpxb->ww) continue; // off the image edge if (py < 0 || py >= Fpxb->hh) continue; pix1 = PXBpix(Fpxb,px,py); // pixel to test match2 = RGBMATCH(red,green,blue,pix1[0],pix1[1],pix1[2]); // 0..1 = zero..perfect match if (match2 < match1) continue; // not a match ii = Fpxb->ww * py + px; newpix = 0; if (select) { // select mode if (! sa_pixmap[ii]) { // if selected for the first time, sa_pixmap[ii] = sa_currseq; // map pixel to current sequence sa_Ncurrseq++; // current sequence pixel count newpix = 1; } } else if (sa_pixmap[ii]) { // unselect mode sa_pixmap[ii] = 0; newpix = 1; } if (newpix) { if (px < xlo) xlo = px; // range of changed pixels if (px > xhi) xhi = px; if (py < ylo) ylo = py; if (py > yhi) yhi = py; } } return; } // Find all pixels within mouse radius and optionally extend selection // to all contiguous pixels matching colors within mouse and within range. // Select or unselect the matching pixels. void sa_mouse_select2(int select) { int mrad, mrad2, srange2; int rad1, rad2, ii; int kk, px, py, rx, ry; int ppx, ppy, npx, npy; int xlo, xhi, ylo, yhi, newpix; uint8 *pix1; float red, green, blue, ff1, ff2; float match1, match2, match3; int thresh, Npixels; char direc; struct Ctab_t { // table of pixel colors in mouse circle int count; // count of pixels with this color float rgb[3]; // RGB color }; Ctab_t Ctab[1000]; // table int Ntab; // table count px = sa_mousex; py = sa_mousey; if (px < 0 || px > Fpxb->ww-1) return; // mouse outside image if (py < 0 || py > Fpxb->hh-1) return; sa_Ncurrseq = 0; // count newly selected pixels mrad = sa_mouseradius; mrad2 = mrad * mrad; for (rx = -mrad; rx <= mrad; rx++) // loop every pixel in radius of mouse for (ry = -mrad; ry <= mrad; ry++) { rad2 = rx * rx + ry * ry; if (rad2 > mrad2) continue; // outside radius px = sa_mousex + rx; py = sa_mousey + ry; if (px < 0 || px >= Fpxb->ww) continue; // off the image edge if (py < 0 || py >= Fpxb->hh) continue; ii = Fpxb->ww * py + px; if (select) { // select pixel if (sa_pixmap[ii]) continue; // already selected sa_pixmap[ii] = sa_currseq; // map pixel to current sequence sa_Ncurrseq++; // current sequence pixel count } else sa_pixmap[ii] = 0; // unselect } if (sa_mode == mode_mouse) { // no color matching, done /// if (! select) sa_map_pixels(); removed 17.08 // re-establish edge Fpaint4(px-mrad,py-mrad,2*mrad,2*mrad,0); // repaint changed area return; } // find all colors within mouse radius and build table of mouse colors // and counts of pixels (nearly) matching these colors match1 = 0.01 * sa_colormatch; // user color match level, 0.01 to 1.0 match3 = match1 + 0.6 * (1.0 - match1); // level for combining colors Npixels = Ntab = 0; // match color counts rad1 = mrad - 1; // mouse radius - 1 if (rad1 < 1) rad1 = 1; rad2 = rad1 * rad1; for (rx = -rad1; rx <= rad1; rx++) // find every pixel within mouse for (ry = -rad1; ry <= rad1; ry++) { if (rx * rx + ry * ry > rad2) continue; px = sa_mousex + rx; py = sa_mousey + ry; if (px < 1 || px > Fpxb->ww-2) continue; // off the image edge if (py < 1 || py > Fpxb->hh-2) continue; Npixels++; // count pixels inside mouse circle pix1 = PXBpix(Fpxb,px,py); red = pix1[0]; // average of 3x3 block removed green = pix1[1]; blue = pix1[2]; for (ii = 0; ii < Ntab; ii++) { // see if color is already included match2 = RGBMATCH(red,green,blue, // 0..1 = zero..perfect match Ctab[ii].rgb[0],Ctab[ii].rgb[1],Ctab[ii].rgb[2]); if (match2 >= match3) break; // matches table color within margin } if (ii < Ntab) { // average aggregated pixel color ff1 = Ctab[ii].count; ff2 = 1.0 / (ff1 + 1.0); Ctab[ii].rgb[0] = (Ctab[ii].rgb[0] * ff1 + red) * ff2; Ctab[ii].rgb[1] = (Ctab[ii].rgb[1] * ff1 + green) * ff2; Ctab[ii].rgb[2] = (Ctab[ii].rgb[2] * ff1 + blue) * ff2; Ctab[ii].count += 1; // count of pixels matching color } else { Ctab[ii].rgb[0] = red; // add unmatched pixel color to table Ctab[ii].rgb[1] = green; Ctab[ii].rgb[2] = blue; Ctab[ii].count = 1; Ntab++; if (Ntab == 1000) goto endmatch; // exit two nested loops } } endmatch: int keys[1][3] = { { 0, sizeof(int), 4 } }; // sort position, length, descending MemSort((char *) Ctab, sizeof(Ctab_t), Ntab, keys, 1); // sort descending count of matching pixels thresh = 0.03 * Ntab; // exclude minority pixels if (Ntab < 100) thresh = 3; if (Ntab < 40) thresh = 2; for (ii = 0; ii < Ntab; ii++) if (Ctab[ii].count < thresh) break; Ntab = ii; // search pixels outside mouse radius but within range for matching colors srange2 = mrad * sa_searchrange; // search range (* mouse radius) srange2 = srange2 * srange2; // squared px = sa_mousex; // pixel at mouse py = sa_mousey; ii = Fpxb->ww * py + px; sa_pixselc[ii] = 1; // pixel is in current selection xlo = px - mrad; // track limits of changed area xhi = px + mrad; ylo = py - mrad; yhi = py + mrad; sa_stackii[0] = ii; // put 1st pixel into stack sa_stackdirec[0] = 'a'; // direction = ahead sa_Nstack = 1; // stack count while (sa_Nstack) { kk = sa_Nstack - 1; // get last pixel in stack ii = sa_stackii[kk]; direc = sa_stackdirec[kk]; py = ii / Fpxb->ww; // reconstruct px, py px = ii - Fpxb->ww * py; if (direc == 'x') { // no neighbors left to check sa_Nstack--; continue; } if (sa_Nstack > 1) { ii = sa_Nstack - 2; // get prior pixel in stack ii = sa_stackii[ii]; ppy = ii / Fpxb->ww; ppx = ii - ppy * Fpxb->ww; } else { ppx = px - 1; // if only one, assume prior = left ppy = py; } if (direc == 'a') { // next ahead pixel npx = px + px - ppx; npy = py + py - ppy; sa_stackdirec[kk] = 'r'; // next search direction } else if (direc == 'r') { // next right pixel npx = px + py - ppy; npy = py + px - ppx; sa_stackdirec[kk] = 'l'; } else { /* direc = 'l' */ // next left pixel npx = px + ppy - py; npy = py + ppx - px; sa_stackdirec[kk] = 'x'; } if (npx < 0 || npx > Fpxb->ww-1) continue; // pixel off the edge if (npy < 0 || npy > Fpxb->hh-1) continue; ii = npy * Fpxb->ww + npx; if (sa_pixselc[ii]) continue; // already in current selection rx = npx - Mxposn; // limit search to ry = npy - Myposn; // mouse radius * search range rad2 = rx * rx + ry * ry; if (rad2 > srange2) continue; pix1 = PXBpix(Fpxb,npx,npy); // pixel at mouse for (kk = 0; kk < Ntab; kk++) { // compare pixel RGB to mouse colors match2 = PIXMATCH(pix1,Ctab[kk].rgb); // 0..1 = zero..perfect match if (match2 >= match1) break; // good match, accept pixel } if (kk == Ntab) continue; // not within range of any color sa_pixselc[ii] = 1; // map pixel to current selection newpix = 0; if (select) { // select mode if (! sa_pixmap[ii]) { // if selected for the first time, sa_pixmap[ii] = sa_currseq; // map pixel to current sequence sa_Ncurrseq++; // current sequence pixel count newpix = 1; } } else if (sa_pixmap[ii]) { // unselect mode sa_pixmap[ii] = 0; newpix = 1; } if (newpix) { if (npx < xlo) xlo = npx; // range of changed pixels if (npx > xhi) xhi = npx; if (npy < ylo) ylo = npy; if (npy > yhi) yhi = npy; } if (sa_Nstack == sa_maxstack) continue; // stack is full kk = sa_Nstack++; // push pixel into stack sa_stackii[kk] = ii; sa_stackdirec[kk] = 'a'; // direction = ahead } Fpaint4(xlo,ylo,xhi-xlo+1,yhi-ylo+1,0); // repaint changed area return; } // set next sequence number for pixels about to be selected void sa_nextseq() { if (sa_Ncurrseq > 0) sa_currseq++; // increase only if some pixels mapped if (sa_currseq < sa_initseq) sa_currseq = sa_initseq; // start at initial value sa_Ncurrseq = 0; return; } // un-select all pixels mapped to current sequence number // reduce sequence number and set pixel count = 1 void sa_unselect_pixels(cairo_t *cr) { int px, py, xlo, xhi, ylo, yhi; int crflag = 0; xlo = Fpxb->ww; xhi = 0; ylo = Fpxb->hh; yhi = 0; if (sa_stat != 1) return; // area gone? if (! sa_currseq) return; // no pixels mapped if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } for (int ii = 0; ii < Fpxb->ww * Fpxb->hh; ii++) { if (sa_pixmap[ii] == sa_currseq) { sa_pixmap[ii] = 0; // unmap current selection py = ii / Fpxb->ww; px = ii - Fpxb->ww * py; if (px < xlo) xlo = px; // range of changed pixels if (px > xhi) xhi = px; if (py < ylo) ylo = py; if (py > yhi) yhi = py; } } if (xhi >= xlo) Fpaint4(xlo,ylo,xhi-xlo+1,yhi-ylo+1,cr); // repaint changed area if (sa_currseq > sa_initseq) { // reduce sequence no. sa_currseq--; sa_Ncurrseq = 1; // unknown but > 0 } else sa_Ncurrseq = 0; // initial sequence no. reached if (crflag) draw_context_destroy(draw_context); return; } // Finish select area - map pixels enclosed by edge pixels // into sa_pixmap[ii]: 0/1/2 = outside/edge/inside (ii=py*Fww+px) // total count = sa_Npixel zdialog *sa_finzd = 0; // finish area zdialog VOL int sa_fincancel; // finish area - user cancel void sa_finish() // overhauled { void sa_finish_mousefunc(); cchar *fmess = ZTX("Click one time inside each enclosed area. \n" "Possible gaps in the outline will be found. \n" "Press F1 for help."); GtkWidget *pwin = zdialog_widget(zdsela,"dialog"); int zstat, cc; if (! sa_stat) return; // no area? if (! sa_validate()) return; // invalid for current image if (sa_mode == mode_image) return; // a whole image area sa_map_pixels(); // find edge pixels if (! sa_Npixel) return; sa_finish_auto(); // auto-finish mouse-selected areas sa_show(1,0); // show area cc = Fpxb->ww * Fpxb->hh; if (sa_stackdirec) zfree(sa_stackdirec); sa_stackdirec = (char *) zmalloc(cc); if (sa_stackii) zfree(sa_stackii); sa_stackii = (int *) zmalloc(cc * sizeof(int)); sa_maxstack = cc; sa_fincancel = 0; sa_Nstack = 0; sa_finzd = zdialog_new(ZTX("finish area"),pwin,Bkeep,Bundo,null); // dialog for user to click inside zdialog_add_widget(sa_finzd,"hbox","hbmess","dialog",0,"space=3"); // each enclosed area zdialog_add_widget(sa_finzd,"label","fmess","hbmess",fmess,"space=5"); takeMouse(sa_finish_mousefunc,dragcursor); // connect mouse function zdialog_run(sa_finzd,0,"save"); // run dialog, wait for completion zstat = zdialog_wait(sa_finzd); zdialog_free(sa_finzd); sa_finzd = 0; freeMouse(); // disconnect mouse if (! sa_stat) return; // area gone? if (zstat != 1) sa_fincancel = 1; // user cancel while (sa_Nstack) { // wait for pixel search to complete zmainloop(); zsleep(0.01); } if (sa_fincancel) { // user cancel sa_unfinish(); // unmap interior pixels, set edit mode return; } sa_map_pixels(); // count pixels, map interior pixels if (sa_Npixel < 10) { // ignore tiny area zmessageACK(Mwin,ZTX("found %d pixels"),sa_Npixel); return; } sa_stat = 3; // area is finished areanumber++; // next sequential number sa_calced = sa_blend = 0; // edge calculation is missing if (zdsela) zdialog_stuff(zdsela,"blendwidth",0); Fpaint2(); return; } // mouse function - get user clicks and perform pixel searches void sa_finish_mousefunc() // overhauled { void sa_finish_mappix(); int ii, px, py; int npaint; cairo_t *cr; if (! LMclick) return; LMclick = 0; if (! sa_stat) return; // area gone? if (sa_Nstack) return; // prior search still busy ii = Fpxb->ww * Myclick + Mxclick; // seed pixel from mouse click if (sa_pixmap[ii] == 1) return; // ignore if edge pixel sa_pixmap[ii] = 2; // map the pixel, inside area sa_stackii[0] = ii; // put seed pixel into stack sa_stackdirec[0] = 'a'; // direction = ahead sa_Nstack = 1; // stack count sa_finish_mappix(); // do pixel search cr = draw_context_create(gdkwin,draw_context); // 17.04 npaint = 2.0 / Mscale + 1; // area fill, sparse for (py = 0; py < Fpxb->hh; py += npaint) // mark pixels inside area for (px = 0; px < Fpxb->ww; px += npaint) { ii = py * Fpxb->ww + px; if (sa_pixmap[ii]) draw_pixel(px,py,cr); } draw_context_destroy(draw_context); // 17.04 return; } // function to map all pixels found within defined edge pixels. void sa_finish_mappix() { int px, py, ii, jj, kk; int ppx, ppy, npx, npy; char direc; while (sa_Nstack) // find all pixels outside enclosed area(s) { kk = sa_Nstack - 1; // get last pixel in stack ii = sa_stackii[kk]; direc = sa_stackdirec[kk]; py = ii / Fpxb->ww; // reconstruct px, py px = ii - Fpxb->ww * py; if (direc == 'x') { // no neighbors left to check sa_Nstack--; continue; } if (sa_Nstack > 1) { jj = sa_Nstack - 2; // get prior pixel in stack jj = sa_stackii[jj]; ppy = jj / Fpxb->ww; ppx = jj - ppy * Fpxb->ww; } else { ppx = px - 1; // if only one, assume prior = left ppy = py; } if (direc == 'a') { // next ahead pixel npx = px + px - ppx; npy = py + py - ppy; sa_stackdirec[kk] = 'r'; // next search direction } else if (direc == 'r') { // next right pixel npx = px + py - ppy; npy = py + px - ppx; sa_stackdirec[kk] = 'l'; } else { /* direc = 'l' */ // next left pixel npx = px + ppy - py; npy = py + ppx - px; sa_stackdirec[kk] = 'x'; } if (npx < 0 || npx >= Fpxb->ww || npy < 0 || npy >= Fpxb->hh) { // next pixel off image edge sa_pixmap[ii] = 1; // this pixel is edge pixel continue; } jj = npy * Fpxb->ww + npx; if (Fpxb->nc > 3 && PXBpix(Fpxb,npx,npy)[3] < 254) { // next pixel in transparent area sa_pixmap[ii] = 1; // this pixel is edge pixel sa_pixmap[jj] = 1; // next pixel is edge pixel continue; } if (sa_pixmap[jj]) continue; // next pixel already mapped sa_pixmap[jj] = 2; // map next pixel, inside area kk = sa_Nstack++; // put pixel into stack sa_stackii[kk] = jj; sa_stackdirec[kk] = 'a'; // direction = ahead } sa_Nstack = 0; // thread is done return; } // Finish select area automatically when the // interior selected pixels are already known. void sa_finish_auto() { if (! sa_stat) return; // no area? if (! sa_validate()) return; // invalid for current image sa_stat = 1; // area is unfinished sa_Npixel = 0; sa_map_pixels(); // count pixels, map interior pixels if (sa_Npixel < 10) { zmessageACK(Mwin,ZTX("found %d pixels"),sa_Npixel); sa_unselect(); return; } sa_stat = 3; // area is finished areanumber++; // next sequential number sa_calced = sa_blend = 0; // edge calculation is missing Fpaint2(); return; } // unfinish an area - unmap interior pixels and put back in edit mode void sa_unfinish() { int px, py, ii; for (py = 0; py < Fpxb->hh; py++) // loop all pixels for (px = 0; px < Fpxb->ww; px++) { ii = py * Fpxb->ww + px; // clear interior pixels found if (sa_pixmap[ii] == 2) sa_pixmap[ii] = 0; // by finish search function } sa_stat = 1; // resume edit mode sa_calced = sa_blend = 0; if (zdsela) zdialog_stuff(zdsela,"blendwidth",0); Fpaint2(); return; } // private function // map edge and interior pixels (sa_pixmap[*] = 1 or 2) // set sa_Npixel = total pixel count void sa_map_pixels() { int npix, px, py, ii, kk; if (! sa_stat) return; // no area? sa_minx = Fpxb->ww; sa_maxx = 0; sa_miny = Fpxb->hh; sa_maxy = 0; npix = 0; for (ii = 0; ii < Fpxb->ww * Fpxb->hh; ii++) { if (! sa_pixmap[ii]) continue; // outside area npix++; // count pixels inside area py = ii / Fpxb->ww; // simplify px = ii - Fpxb->ww * py; if (px >= sa_maxx) sa_maxx = px+1; // get enclosing rectangle if (px < sa_minx) sa_minx = px; // (sa_maxx = last pixel + 1) if (py >= sa_maxy) sa_maxy = py+1; if (py < sa_miny) sa_miny = py; if (px == 0 || px == Fpxb->ww-1 || py == 0 || py == Fpxb->hh-1) // image edge goto edgepix; if (Fpxb->nc > 3 && PXBpix(Fpxb,px,py)[3] < 254) // transparency edge goto edgepix; if (! sa_pixmap[ii-1] || ! sa_pixmap[ii+1]) goto edgepix; // check 8 neighbor pixels kk = ii - Fpxb->ww; if (! sa_pixmap[kk-1] || ! sa_pixmap[kk] || ! sa_pixmap[kk+1]) goto edgepix; kk = ii + Fpxb->ww; if (! sa_pixmap[kk-1] || ! sa_pixmap[kk] || ! sa_pixmap[kk+1]) goto edgepix; sa_pixmap[ii] = 2; // interior pixel continue; edgepix: sa_pixmap[ii] = 1; // edge pixel } sa_Npixel = npix; // total pixel count sa_minx -= 10; // add margins where possible if (sa_minx < 0) sa_minx = 0; sa_maxx += 10; if (sa_maxx > Fpxb->ww) sa_maxx = Fpxb->ww; sa_miny -= 10; if (sa_miny < 0) sa_miny = 0; sa_maxy += 10; if (sa_maxy > Fpxb->hh) sa_maxy = Fpxb->hh; return; } // Find the edge pixels surrounding the clicked pixel location. // Trace around the edge outline to see if there is a gap. void m_select_find_gap(GtkWidget *, cchar *menu) { int sa_find_gap_dialog_event(zdialog *zd, cchar *event); void sa_find_gap_mousefunc(); cchar *fmess = ZTX("Click near any position on the area outline. \n" // 17.01 "Possible gaps in the outline will be found. \n" "Press F1 for help."); zdialog *zd; F1_help_topic = "find_area_gap"; if (! sa_stat) return; // no area? if (! sa_validate()) return; // invalid for current image if (sa_mode == mode_image) return; // a whole image area sa_unfinish(); // 17.01 if (sa_finzd) zdialog_destroy(sa_finzd); // terminate finish dialog sa_map_pixels(); // find edge pixels if (! sa_Npixel) return; sa_show(1,0); // show area zd = zdialog_new(ZTX("find outline gap"),Mwin,BOK,null); // dialog for user to click inside zdialog_add_widget(zd,"hbox","hbmess","dialog",0,"space=3"); // each enclosed area zdialog_add_widget(zd,"label","fmess","hbmess",fmess,"space=5"); zdialog_run(zd,sa_find_gap_dialog_event,"save"); // run dialog takeMouse(sa_find_gap_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion function int sa_find_gap_dialog_event(zdialog *zd, cchar *event) { void sa_find_gap_mousefunc(); if (strmatch(event,"focus")) takeMouse(sa_find_gap_mousefunc,dragcursor); // connect mouse function if (! zd->zstat) return 1; // wait for completion zdialog_free(zd); freeMouse(); // disconnect mouse if (sa_stackii) zfree(sa_stackii); // free memory bugfix 17.01 sa_stackii = 0; return 1; } // mouse function - search area outline surrounding mouse click position void sa_find_gap_mousefunc() { int rad, ii, jj, kk, nn, ff, np1, np2, npx; float angle; int ww, hh, cc; int mx, my, px, py, rx, ry; char *pixmark = 0; cairo_t *cr; if (! sa_stat) return; // 17.01 if (sa_stackii) zfree(sa_stackii); cc = Fpxb->ww * Fpxb->hh; sa_stackii = (int *) zmalloc(cc * sizeof(int)); sa_Nstack = 0; sa_maxstack = cc; if (! LMclick) return; // no left mouse click data LMclick = 0; sa_map_pixels(); // find edge pixels if (! sa_Npixel) return; nn = np1 = np2 = -1; ww = Fpxb->ww; // image size hh = Fpxb->hh; cc = ww * hh; mx = Mxclick; // mouse click position my = Myclick; if (mx == 0 || mx >= ww) return; // reject if image edge if (my == 0 || my >= hh) return; for (rad = 1; rad < 100; rad++) // loop radius = 1 - 100 pixels for (angle = 0; angle < 2*PI; angle += 0.7/rad) // loop angle = 0 - 360 degrees { px = mx + rad * cosf(angle); // search for nearest edge pixel py = my + rad * sinf(angle); ii = py * ww + px; if (sa_pixmap[ii] != 1) continue; nn = np1 = np2 = 0; // test if edge pixel has exactly for (ry = -1; ry <= +1; ry++) // two neighbor edge pixels for (rx = -1; rx <= +1; rx++) { if (ry == 0 && rx == 0) continue; // skip self jj = ii + ww * ry + rx; if (sa_pixmap[jj] == 1) { // neighbor is an edge pixel if (++nn > 2) goto break2; // > 2 edge neighbors, reject if (! np1) np1 = jj; // edge neighbor 1 else if (! np2) np2 = jj; // edge neighbor 2 } } break2: // 17.08 if (nn == 2) break; // found suitable edge pixel } if (nn != 2 || np1 == 0 || np2 == 0) { // no edge pixel with 2 neighbor 17.08 zmessage_post(Mwin,3,ZTX("cannot find area outline")); // edge pixels was found return; } sa_show(0,0); // hide area Fpaintnow(); if (draw_context.dcr) { // 17.08 printz("*** draw context active \n"); return; } cr = draw_context_create(gdkwin,draw_context); // 17.04 pixmark = (char *) zmalloc(cc); // create pixel mark map Ffuncbusy++; for (ff = 0; ff < 2; ff++) // 17.01 { memset(pixmark,0,cc); // clear all pixel marks pixmark[ii] = 1; // mark edge pixel sa_stackii[0] = np1; // put neighbor 1 pixel into stack sa_Nstack = 1; // stack count while (sa_Nstack) { kk = --sa_Nstack; // pull pixel from stack ii = sa_stackii[kk]; if (ii == np2) break; // = neighbor 2 pixel, no outline gap pixmark[ii] = 1; // mark pixel py = ii / ww; px = ii - ww * py; draw_pixel(px,py,cr,1); // draw fat pixel 17.08 zsleep(0.001); zmainloop(); if (! sa_stackii) goto ukill; // killed by user bugfix 17.04.3 for (ry = -1; ry <= +1; ry++) // find unmarked edge neighbor pixels for (rx = -1; rx <= +1; rx++) { if (py+ry < 0 || py+ry > hh-1) continue; // off the image edge if (px+rx < 0 || px+rx > ww-1) continue; jj = ii + ww * ry + rx; if (pixmark[jj]) continue; // pixel already marked if (sa_pixmap[jj] == 1) { // neighbor is an unmarked edge pixel kk = sa_Nstack++; // add to stack sa_stackii[kk] = jj; } } } if (ii == np2) break; // no gap npx = np1; // np1 <--> np2 np1 = np2; np2 = npx; zsleep(1); // pause and loop other direction } ukill: Ffuncbusy = 0;; if (pixmark) zfree(pixmark); // free memory sa_show(1,cr); // show area draw_context_destroy(draw_context); return; } /********************************************************************************/ // Hairy Edge selection function. // Set transparencies for hairy edge pixels based on color matching // with a set of selected pixels well within the hairy edge. namespace select_hairy { int ww, hh, pcc; int mradius = 30; // mouse radius int Fmode; // 1/2/3 = select/deselect/both int selrad = 6; // radius for match pixel selection (select) int dselrad = 4; // radius for match pixel selection (deselect) float smatch = 80; // pixel select match level 0-100 float dmatch = 80; // pixel deselect match level 0-100 float selRGB[200][3]; // pixels to match & select float dselRGB[200][3]; // pixels to match & deselect int Nsel, Ndsel; // pixel counts zdialog *zdselhairy; GdkPixbuf *pxbsel, *pxbdsel; // pixbufs to show match colors int pxbsize, pxbmid, rs; // size, middle pixel, row stride uint8 *pixels; } // menu function void m_select_hairy(GtkWidget *, cchar *) // overhauled 17.08 { using namespace select_hairy; int select_hairy_dialog_event(zdialog *zd, cchar *event); void select_hairy_mousefunc(); cchar *title = ZTX("Select Hairy"); cchar *helptext = ZTX("Press F1 for help"); F1_help_topic = "select_hairy"; if (CEF) edit_done(0); // if edit active, complete it if (! sa_stat) { // no selected area zmessageACK(Mwin,ZTX("select the area first")); return; } if (sa_stat < 3) { zmessageACK(Mwin,ZTX("the area is not finished")); return; } if (! E0pxm) { // get poss. 16-bit image paintlock(1); // block window updates E0pxm = PXM_load(curr_file,1); paintlock(0); // unblock window updates if (! E0pxm) return; } PXM_addalpha(E0pxm); // add alpha channel if not ww = E0pxm->ww; hh = E0pxm->hh; pcc = 4 * sizeof(float); if (E1pxm) PXM_free(E1pxm); // make reference copy of original E1pxm = PXM_copy(E0pxm); Fpaint2(); for (int ii = 0; ii < 200; ii++) { // initial match pixels selRGB[ii][0] = 0; // default = black selRGB[ii][1] = 0; selRGB[ii][2] = 0; dselRGB[ii][0] = 0; dselRGB[ii][1] = 0; dselRGB[ii][2] = 0; } Nsel = Ndsel = 100; // arbitrary at this point pxbsel = gdk_pixbuf_new(GDKRGB,0,8,40,40); // pixbuf showing select match colors rs = gdk_pixbuf_get_rowstride(pxbsel); pixels = gdk_pixbuf_get_pixels(pxbsel); memset(pixels,0,40*rs); // initially all black pxbdsel = gdk_pixbuf_new(GDKRGB,0,8,40,40); // pixbuf showing deselect match colors rs = gdk_pixbuf_get_rowstride(pxbdsel); pixels = gdk_pixbuf_get_pixels(pxbdsel); memset(pixels,0,40*rs); // initially all black /*** ____________________________________________ | Select Hairy | | | | Press F1 for help | | | | mouse radius ========[]============= [18] | | | | ##### | | [X] Select ===========[]===== [92] ##### | | ##### | | | | ##### | | [X] Deselect =======[]======= [83] ##### | | ##### | | | | [Copy] [Save] [Cancel] | |____________________________________________| ***/ zdselhairy = zdialog_new(title,Mwin,Bcopy,Bsave,Bcancel,null); zdialog *zd = zdselhairy; zdialog_add_widget(zd,"label","labhelp","dialog",helptext,"space=3"); zdialog_add_widget(zd,"hbox","hbmrad","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labmrad","hbmrad",Bmouseradius,"space=3"); zdialog_add_widget(zd,"hscale","mradius","hbmrad","3|300|1|40","expand"); zdialog_add_widget(zd,"label","mradius2","hbmrad","20","space=3"); zdialog_add_widget(zd,"hbox","hbsel","dialog",0,"space=3"); zdialog_add_widget(zd,"check","select","hbsel",ZTX("select")); zdialog_add_widget(zd,"hscale","smatch","hbsel","10|99|0.1|80","expand"); zdialog_add_widget(zd,"label","smatch2","hbsel","80","space=3"); zdialog_add_widget(zd,"image","scolors","hbsel",(cchar *) pxbsel); zdialog_add_widget(zd,"hbox","hbdes","dialog",0,"space=3"); zdialog_add_widget(zd,"check","deselect","hbdes",ZTX("deselect")); zdialog_add_widget(zd,"hscale","dmatch","hbdes","10|99|0.1|80","expand"); zdialog_add_widget(zd,"label","dmatch2","hbdes","80","space=3"); zdialog_add_widget(zd,"image","dcolors","hbdes",(cchar *) pxbdsel); zdialog_rescale(zd,"mradius",3,3,300); zdialog_rescale(zd,"smatch",10,99,99); zdialog_rescale(zd,"dmatch",10,99,99); smatch = dmatch = 80; // defaults mradius = 20; zdialog_stuff(zd,"smatch",smatch); zdialog_stuff(zd,"dmatch",dmatch); zdialog_stuff(zd,"mradius",mradius); zdialog_stuff(zd,"select",0); zdialog_stuff(zd,"deselect",0); Fmode = 0; zdialog_resize(zd,400,0); // run dialog zdialog_run(zd,select_hairy_dialog_event,"save"); takeMouse(select_hairy_mousefunc,dragcursor); // capture mouse return; } // dialog event and completion function int select_hairy_dialog_event(zdialog *zd, cchar *event) { using namespace select_hairy; int ii; char text[8]; void select_hairy_mousefunc(); if (! curr_file) zd->zstat = 3; // image went away if (sa_stat < 3) zd->zstat = 3; // area gone if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"focus")) takeMouse(select_hairy_mousefunc,dragcursor); if (zd->zstat) // done or cancel { if (zd->zstat == 1) // copy area m_select_save(null,"copy"); if (zd->zstat == 2) // save area to file m_select_save(null,"save"); paintlock(1); PXM_free(E0pxm); E0pxm = E1pxm; // refresh image, remove transparency E1pxm = 0; paintlock(0); Fpaint2(); zdialog_free(zd); // kill dialog freeMouse(); // disconnect mouse g_object_unref(pxbsel); // free pixbuf memory g_object_unref(pxbdsel); return 1; } Fmode = 0; // mode: 1/2/3 = select/deselect/both zdialog_fetch(zd,"select",ii); if (ii) Fmode = 1; zdialog_fetch(zd,"deselect",ii); if (ii) Fmode += 2; zdialog_fetch(zd,"mradius",mradius); // mouse radius zdialog_fetch(zd,"smatch",smatch); // select match level 0 - 100 zdialog_fetch(zd,"dmatch",dmatch); // deselect match level 0 - 100 snprintf(text,8,"%d",mradius); // stuff number values for sliders zdialog_stuff(zd,"mradius2",text); snprintf(text,8,"%.1f",smatch); zdialog_stuff(zd,"smatch2",text); snprintf(text,8,"%.1f",dmatch); zdialog_stuff(zd,"dmatch2",text); return 1; } // mouse function void select_hairy_mousefunc() { using namespace select_hairy; int select_hairy_ucomp(cchar *, cchar*); int ii, rx, ry, px, py; int mrad, rrad; int pcc3 = 3 * sizeof(float); uint8 *pix; float *pix0, *pix1, *pixM; float pixmatch, bestmatch; float smlev, dmlev, psml, pdml; float f1, f2, alpha; GdkPixbuf *pxbtemp = 0; if (LMclick) // left mouse click, { // get colors to select mrad = selrad; // area for pixel color select ii = 0; for (ry = -mrad; ry <= mrad; ry++) // loop pixels within selection radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxclick + rx; py = Myclick + ry; if (px < 0 || px > ww-1) continue; // off the image edge if (py < 0 || py > hh-1) continue; pix1 = PXMpix(E1pxm,px,py); selRGB[ii][0] = pix1[0]; // pixels to match and select selRGB[ii][1] = pix1[1]; selRGB[ii][2] = pix1[2]; ii++; } Nsel = ii; // pixel count pxbsize = 2 * mrad + 3; // pixbuf size (odd number) pxbmid = pxbsize / 2; // middle pixel pxbtemp = gdk_pixbuf_new(GDKRGB,0,8,pxbsize,pxbsize); rs = gdk_pixbuf_get_rowstride(pxbtemp); pixels = gdk_pixbuf_get_pixels(pxbtemp); // pixbuf to show match colors memset(pixels,0,pxbsize*rs); ii = 0; for (ry = -mrad; ry <= mrad; ry++) // loop pixels within selection radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxclick + rx; py = Myclick + ry; if (px < 0 || px > ww-1) continue; // off the image edge if (py < 0 || py > hh-1) continue; px = pxbmid + rx; // corresp. pixbuf pixel py = pxbmid + ry; pix = pixels + py * rs + px * 3; pix[0] = selRGB[ii][0]; // fill pixbuf with colors pix[1] = selRGB[ii][1]; pix[2] = selRGB[ii][2]; ii++; } g_object_unref(pxbsel); // show enlarged pxbsel = gdk_pixbuf_scale_simple(pxbtemp,40,40,BILINEAR); zdialog_set_image(zdselhairy,"scolors",pxbsel); } else if (RMclick) // right mouse click, { // get colors to deselect mrad = dselrad; // area for pixel color deselect ii = 0; for (ry = -mrad; ry <= mrad; ry++) // loop pixels within selection radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxclick + rx; py = Myclick + ry; if (px < 0 || px > ww-1) continue; // off the image edge if (py < 0 || py > hh-1) continue; pix1 = PXMpix(E1pxm,px,py); dselRGB[ii][0] = pix1[0]; // pixels to match and deselect dselRGB[ii][1] = pix1[1]; dselRGB[ii][2] = pix1[2]; ii++; } Ndsel = ii; // pixel count pxbsize = 2 * mrad + 3; // pixbuf size (odd number) pxbmid = pxbsize / 2; // middle pixel pxbtemp = gdk_pixbuf_new(GDKRGB,0,8,pxbsize,pxbsize); rs = gdk_pixbuf_get_rowstride(pxbtemp); pixels = gdk_pixbuf_get_pixels(pxbtemp); // pixbuf to show match colors memset(pixels,0,pxbsize*rs); ii = 0; for (ry = -mrad; ry <= mrad; ry++) // loop pixels within selection radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxclick + rx; py = Myclick + ry; if (px < 0 || px > ww-1) continue; // off the image edge if (py < 0 || py > hh-1) continue; px = pxbmid + rx; // corresp. pixbuf pixel py = pxbmid + ry; pix = pixels + py * rs + px * 3; pix[0] = dselRGB[ii][0]; // fill pixbuf with colors pix[1] = dselRGB[ii][1]; pix[2] = dselRGB[ii][2]; ii++; } g_object_unref(pxbdsel); // show enlarged pxbdsel = gdk_pixbuf_scale_simple(pxbtemp,40,40,BILINEAR); zdialog_set_image(zdselhairy,"dcolors",pxbdsel); } cairo_t *cr = draw_context_create(gdkwin,draw_context); if ((Mxdrag || Mydrag) && Mbutton == 1) // left drag underway, { // select or deselect pixels mrad = mradius; smlev = 0.01 * smatch; // select match level scaled 0-1 dmlev = 0.01 * dmatch; // deselect match level scaled 0-1 psml = pdml = 0; // stop GCC warnings for (ry = -mrad; ry <= mrad; ry++) // loop pixels within mouse radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxdrag + rx; py = Mydrag + ry; if (px < 0 || px > ww-1) continue; // outside image edge if (py < 0 || py > hh-1) continue; ii = py * ww + px; if (! sa_pixmap[ii]) continue; // outside area pix1 = PXMpix(E1pxm,px,py); // input pixel pix0 = PXMpix(E0pxm,px,py); // output pixel bestmatch = -1; pixM = selRGB[0]; for (ii = 0; ii < Nsel; ii++) { // compare to selected colors pixmatch = RGBMATCH(pix1[0], pix1[1], pix1[2], selRGB[ii][0], selRGB[ii][1], selRGB[ii][2]); if (pixmatch > bestmatch) { bestmatch = pixmatch; // save best match level pixM = selRGB[ii]; // save best match color } } psml = bestmatch; // select colors best match level bestmatch = -1; for (ii = 0; ii < Ndsel; ii++) { // compare to deselect colors pixmatch = RGBMATCH(pix1[0], pix1[1], pix1[2], dselRGB[ii][0], dselRGB[ii][1], dselRGB[ii][2]); if (pixmatch > bestmatch) bestmatch = pixmatch; // save best match level } pdml = bestmatch; // deselect colors best match level if (Fmode == 1) // select only - increase opacity { if (psml > smlev) { alpha = (psml - smlev) / (1.0 - smlev); alpha = 255.0 * pow(alpha,0.3); if (pix0[3] < alpha) { pix0[3] = alpha; memcpy(pix0,pixM,pcc3); // color = match color } } } else if (Fmode == 2) // deselect only - increase transparency { if (pdml >= dmlev) alpha = 0; else { alpha = 1.0 - pdml / dmlev; alpha = 255.0 * pow(alpha,0.3); } alpha = 255.0 * pow(alpha,0.3); if (alpha < 20) alpha = 0; if (pix0[3] > alpha) pix0[3] = alpha; } else /* Fmode == 3 */ // parallel select and deselect { if (psml > pdml) // pixel better matches select colors alpha = 1 - 0.5 * (1 - psml) / (1 - pdml); // scale 0.5 to 1.0 else if (pdml > psml) alpha = 0.5 * (1 - pdml) / (1 - psml); // pixel better matches deselect colors else alpha = 0.5; // scale 0 to 0.5 alpha = 255 * alpha; if (alpha < 20) alpha = 0; pix0[3] = alpha; alpha = 255.0 * pow(alpha,0.3); memcpy(pix0,pixM,pcc3); // color = match color } } Fpaint0(Mxdrag-mrad,Mydrag-mrad,2*mrad+1,2*mrad+1,cr); // update drawing window } if ((Mxdrag || Mydrag) && Mbutton == 3) // right drag underway, { // restore original pixels mrad = mradius; for (ry = -mrad; ry <= mrad; ry++) // loop pixels within mouse radius for (rx = -mrad; rx <= mrad; rx++) { rrad = sqrtf(rx * rx + ry * ry); if (rrad > mrad) continue; // outside radius px = Mxdrag + rx; py = Mydrag + ry; if (px < 0 || px > ww-1) continue; // outside image edge if (py < 0 || py > hh-1) continue; ii = py * ww + px; if (! sa_pixmap[ii]) continue; // outside area f1 = 1.0 - 1.0 * rrad / mrad; // center ... edge >> 1 ... 0 f1 = 0.3 * f1 * f1 + 0.03; // 0.33 ... 0.03 f2 = 1.0 - f1; // restore slowly at edge pix0 = PXMpix(E0pxm,px,py); pix1 = PXMpix(E1pxm,px,py); pix0[0] = f2 * pix0[0] + f1 * pix1[0]; pix0[1] = f2 * pix0[1] + f1 * pix1[1]; pix0[2] = f2 * pix0[2] + f1 * pix1[2]; pix0[3] = f2 * pix0[3] + f1 * pix1[3]; } Fpaint0(Mxdrag-mrad,Mydrag-mrad,2*mrad+1,2*mrad+1,cr); // update drawing window } draw_mousecircle(Mxposn,Myposn,mradius,0,cr); // refresh mouse circle draw_context_destroy(draw_context); LMclick = RMclick = Mxdrag = Mydrag = 0; return; } /********************************************************************************/ // menu function for show, hide, enable, disable, invert, unselect // (also implemented as buttons in select area dialog) void m_select_show(GtkWidget *, cchar *menu) { F1_help_topic = "area_show_hide"; sa_show(1,0); // show area return; } void m_select_hide(GtkWidget *, cchar *menu) { F1_help_topic = "area_show_hide"; sa_show(0,0); return; } void m_select_enable(GtkWidget *, cchar *menu) { F1_help_topic = "area_enable_disable"; sa_enable(); return; } void m_select_disable(GtkWidget *, cchar *menu) { F1_help_topic = "area_enable_disable"; sa_disable(); return; } void m_select_invert(GtkWidget *, cchar *menu) { F1_help_topic = "area_invert"; sa_invert(); return; } void m_select_unselect(GtkWidget *, cchar *menu) // delete the area { F1_help_topic = "area_unselect"; sa_unselect(); return; } // show or hide outline of select area // also called from Fpaint() if Fshowarea = 1 // edges are detected independently of sa_pixmap[] void sa_show(int flag, cairo_t *cr) { int px, py, ii, kk; int crflag = 0; if (! sa_stat) return; // no area if (! sa_validate()) return; // invalid for current image if (sa_mode == mode_image) return; // a whole image area Fshowarea = flag; // flag for Fpaint*() if (! flag) { Fpaint2(); // erase area outline return; } if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } for (ii = 0; ii < Fpxb->ww * Fpxb->hh; ii++) { if (! sa_pixmap[ii]) continue; py = ii / Fpxb->ww; px = ii - Fpxb->ww * py; if (px == 0 || px == Fpxb->ww-1 || py == 0 || py == Fpxb->hh-1) // edge of image continue; if (Fpxb->nc > 3 && PXBpix(Fpxb,px,py)[3] < 254) // transparency edge continue; if (! sa_pixmap[ii-1] || ! sa_pixmap[ii+1]) goto edgepix; // check 8 neighbor pixels kk = ii - Fpxb->ww; if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix; kk = ii + Fpxb->ww; if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix; continue; edgepix: draw_pixel(px,py,cr); } if (crflag) draw_context_destroy(draw_context); return; } // Show the area outline only within a rectangular section. // Improve responsiveness during user mouse-driven updates. // Also called by Fpaint4() after a sectional edit is applied. void sa_show_rect(int px1, int py1, int ww, int hh, cairo_t *cr) { int px, py, px2, py2, ii, kk; int crflag = 0; if (! Fshowarea) return; if (! sa_stat) return; // no area if (! sa_validate()) return; // invalid for current image if (sa_mode == mode_image) return; // a whole image area px2 = px1 + ww; py2 = py1 + hh; if (px1 < 0) px1 = 0; if (py1 < 0) py1 = 0; if (px2 > Fpxb->ww) px2 = Fpxb->ww; if (py2 > Fpxb->hh) py2 = Fpxb->hh; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } for (py = py1; py < py2; py++) // loop pixels in rectangle for (px = px1; px < px2; px++) { ii = Fpxb->ww * py + px; if (! sa_pixmap[ii]) continue; if (px == 0 || px == Fpxb->ww-1 || py == 0 || py == Fpxb->hh-1) // edge of image continue; if (Fpxb->nc > 3 && PXBpix(Fpxb,px,py)[3] < 254) // transparency edge continue; if (! sa_pixmap[ii-1] || ! sa_pixmap[ii+1]) goto edgepix; // check 8 neighbor pixels kk = ii - Fpxb->ww; if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix; kk = ii + Fpxb->ww; if (! sa_pixmap[kk] || ! sa_pixmap[kk-1] || ! sa_pixmap[kk+1]) goto edgepix; continue; edgepix: draw_pixel(px,py,cr); } if (crflag) draw_context_destroy(draw_context); return; } // validate an area for use in the current image. // returns 1 if OK, 0 if not (area will have been deleted). int sa_validate() { int ww = 0, hh = 0; if (! sa_stat) return 0; // no area if (! curr_file || (CEF && CEF->Fpreview)) { // no image file or edit preview image sa_unselect(); return 0; } if (E1pxm) { ww = E1pxm->ww; hh = E1pxm->hh; } else if (E0pxm) { ww = E0pxm->ww; hh = E0pxm->hh; } else { ww = Fpxb->ww; hh = Fpxb->hh; } if (sa_fww == ww && sa_fhh == hh) return 1; sa_unselect(); return 0; } // enable select area that was disabled void sa_enable() { if (! sa_stat) return; // no area if (! sa_validate()) return; // invalid for current image if (sa_stat < 3) { // finished or finished/disabled zmessageACK(Mwin,ZTX("the area is not finished")); return; } sa_stat = 3; // finished/enabled areanumber++; // next sequential number sa_show(1,0); // show area return; } // disable select area void sa_disable() { if (! sa_stat) return; // no area if (sa_stat < 3) { // finished or */disabled zmessageACK(Mwin,ZTX("the area is not finished")); return; } sa_stat = 4; // finished/disabled sa_show(0,0); // hide area return; } // invert a selected area void sa_invert() { int ii, jj, px, py, npix; if (! sa_stat) return; // no area if (! sa_validate()) return; // invalid for current image if (sa_mode == mode_image) return; // a whole image area if (sa_stat < 3) { zmessageACK(Mwin,ZTX("the area is not finished")); return; } sa_minx = Fpxb->ww; // get new enclosing rectangle sa_maxx = 0; sa_miny = Fpxb->hh; sa_maxy = 0; npix = 0; for (py = 0; py < Fpxb->hh; py++) // loop all pixels for (px = 0; px < Fpxb->ww; px++) { ii = py * Fpxb->ww + px; jj = sa_pixmap[ii]; // 0/1/2+ = outside/edge/inside if (jj > 1) { // inside pixel (2+) sa_pixmap[ii] = 0; // is now outside (0) continue; } sa_pixmap[ii] = 2 - jj; // edge/outside (1/0) >> edge/inside (1/2) npix++; // count if (px >= sa_maxx) sa_maxx = px + 1; if (px < sa_minx) sa_minx = px; if (py >= sa_maxy) sa_maxy = py + 1; if (py < sa_miny) sa_miny = py; } sa_minx -= 10; // add margins where possible if (sa_minx < 0) sa_minx = 0; sa_maxx += 10; if (sa_maxx > Fpxb->ww) sa_maxx = Fpxb->ww; sa_miny -= 10; if (sa_miny < 0) sa_miny = 0; sa_maxy += 10; if (sa_maxy > Fpxb->hh) sa_maxy = Fpxb->hh; sa_stat = 3; // if disabled, now finished sa_Npixel = npix; // new select area pixel count sa_calced = sa_blend = 0; // edge calculation missing if (zdsela) zdialog_stuff(zdsela,"blendwidth",0); // reset blend width sa_show(1,0); // show area return; } // unselect current area (delete the area) void sa_unselect() { sa_stat = sa_Npixel = sa_blend = sa_calced = 0; sa_currseq = sa_Ncurrseq = 0; sa_fww = sa_fhh = 0; if (sa_pixmap) zfree(sa_pixmap); if (sa_stackii) zfree(sa_stackii); if (sa_stackdirec) zfree(sa_stackdirec); if (sa_pixselc) zfree(sa_pixselc); sa_pixmap = 0; sa_stackii = 0; sa_stackdirec = 0; sa_Nstack = 0; sa_pixselc = 0; Fpaint2(); return; } // compute distance from all pixels in area to nearest edge // output: sa_pixmap[*] = 0/1/2+ = outside, edge, inside distance from edge namespace sa_edgecalc_names { uint16 *sa_edgepx, *sa_edgepy, *sa_edgedist; int sa_Nedge; } void sa_edgecalc() { using namespace sa_edgecalc_names; int edgecalc_dialog_event(zdialog*, cchar *event); void * edgecalc_thread(void *); int ii, nn, cc, px, py; zdialog *zd = 0; cchar *zectext = ZTX("Edge calculation in progress"); if (! sa_stat) return; // area gone? if (sa_mode == mode_image) return; // a whole image area if (sa_calced) return; // done already if (sa_stat < 3) sa_finish(); // finish if needed if (sa_stat != 3) return; // failed or canceled zd = zdialog_new(ZTX("Area Edge Calc"),Mwin,Bcancel,null); // start dialog for user cancel zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"label","lab1","hb1",zectext,"space=10"); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,edgecalc_dialog_event,"save"); Fkillfunc = 0; cc = Fpxb->ww * Fpxb->hh * sizeof(uint16); // allocate memory for calculations sa_edgedist = (uint16 *) zmalloc(cc); memset(sa_edgedist,0,cc); for (ii = nn = 0; ii < Fpxb->ww * Fpxb->hh; ii++) // count edge pixels in select area if (sa_pixmap[ii] == 1) nn++; cc = nn * sizeof(uint16); sa_edgepx = (uint16 *) zmalloc(cc); // allocate memory sa_edgepy = (uint16 *) zmalloc(cc); for (ii = nn = 0; ii < Fpxb->ww * Fpxb->hh; ii++) // build list of edge pixels { if (sa_pixmap[ii] != 1) continue; py = ii / Fpxb->ww; px = ii - py * Fpxb->ww; if (px == 0 || px == Fpxb->ww-1) continue; // omit edge pixels if (py == 0 || py == Fpxb->hh-1) continue; if (Fpxb->nc > 3) { // omit pixels at transparency edge if (PXBpix(Fpxb,px-1,py)[3] < 254) continue; if (PXBpix(Fpxb,px, py)[3] < 254) continue; if (PXBpix(Fpxb,px+1,py)[3] < 254) continue; if (PXBpix(Fpxb,px-1,py-1)[3] < 254) continue; if (PXBpix(Fpxb,px, py-1)[3] < 254) continue; if (PXBpix(Fpxb,px+1,py-1)[3] < 254) continue; if (PXBpix(Fpxb,px-1,py+1)[3] < 254) continue; if (PXBpix(Fpxb,px, py+1)[3] < 254) continue; if (PXBpix(Fpxb,px+1,py+1)[3] < 254) continue; } sa_edgepx[nn] = px; sa_edgepy[nn] = py; nn++; } sa_Nedge = nn; Fbusy_goal = sa_Npixel; Fbusy_done = 0; for (ii = 0; ii < NWT; ii++) // start worker threads to calculate start_wthread(edgecalc_thread,&Nval[ii]); // sa_pixmap[] edge distances wait_wthreads(0); // wait for completion Fbusy_goal = 0; zdialog_free(zd); // kill dialog if (Fkillfunc) { // user killed edge calc Fkillfunc = 0; sa_calced = 0; if (zdsela) zdialog_stuff(zdsela,"blendwidth",0); // reset blend width } else { for (ii = 0; ii < Fpxb->ww * Fpxb->hh; ii++) { // copy sa_edgedist[] to sa_pixmap[] if (sa_pixmap[ii] <= 1) continue; // skip outside and edge pixels sa_pixmap[ii] = sa_edgedist[ii]; // interior pixel edge distance } sa_calced = 1; // edge calculation available } zfree(sa_edgedist); // free memory zfree(sa_edgepx); zfree(sa_edgepy); Fpaint2(); return; } // dialog event and completion callback function int edgecalc_dialog_event(zdialog *zd, cchar *event) // respond to user cancel { if (! zd->zstat) return 0; Fkillfunc = 1; return 0; } void * edgecalc_thread(void *arg) // worker thread function { // new algorithm using namespace sa_edgecalc_names; void edgecalc_func(int px, int py); int index = *((int *) (arg)); int midx, midy, radx, rady, rad; int ii, px, py; midx = (sa_maxx + sa_minx) / 2; midy = (sa_maxy + sa_miny) / 2; radx = (sa_maxx - sa_minx) / 2 + 1; rady = (sa_maxy - sa_miny) / 2 + 1; px = midx; // center of enclosing rectangle py = midy; ii = py * Fpxb->ww + px; if (sa_pixmap[ii]) edgecalc_func(px,py); // do center pixel first for (rad = 1; rad < radx || rad < rady; rad++) // expanding square from the center { for (px = midx-rad; px <= midx+rad; px += 2 * rad) // edges only, interior already done for (py = midy-rad+index; py <= midy+rad; py += NWT) { if (px < 0 || px > Fpxb->ww-1) continue; if (py < 0 || py > Fpxb->hh-1) continue; ii = py * Fpxb->ww + px; if (! sa_pixmap[ii]) continue; if (sa_edgedist[ii]) continue; edgecalc_func(px,py); if (Fkillfunc) exit_wthread(); } for (py = midy-rad; py <= midy+rad; py += 2 * rad) for (px = midx-rad+index; px <= midx+rad; px += NWT) { if (px < 0 || px > Fpxb->ww-1) continue; if (py < 0 || py > Fpxb->hh-1) continue; ii = py * Fpxb->ww + px; if (! sa_pixmap[ii]) continue; if (sa_edgedist[ii]) continue; edgecalc_func(px,py); if (Fkillfunc) exit_wthread(); } } exit_wthread(); return 0; // not executed, stop gcc warning } // Find the nearest edge pixel for a given pixel. // For all pixels in a line from the given pixel to the edge pixel, // the same edge pixel is used to compute edge distance. void edgecalc_func(int px1, int py1) { using namespace sa_edgecalc_names; int ii, px2, py2, mindist; uint dist2, mindist2; int epx, epy, pxm, pym, dx, dy, inc; int ww = Fpxb->ww, hh = Fpxb->hh; int cc = ww * hh; float slope; mindist = 9999; mindist2 = mindist * mindist; epx = epy = 0; for (ii = 0; ii < sa_Nedge; ii++) // loop all edge pixels { // (ii += 2 tried, buggy) px2 = sa_edgepx[ii]; py2 = sa_edgepy[ii]; dx = px2 - px1; dy = py2 - py1; dist2 = dx*dx + dy*dy; // avoid sqrt() if (dist2 < mindist2) { mindist2 = dist2; // remember minimum epx = px2; // remember nearest edge pixel epy = py2; } } if (abs(epy - py1) > abs(epx - px1)) { // find all pixels along a line slope = 1.0 * (epx - px1) / (epy - py1); // to the edge pixel if (epy > py1) inc = 1; else inc = -1; for (pym = py1; pym != epy; pym += inc) { pxm = px1 + slope * (pym - py1); ii = pym * ww + pxm; if (ii < 0 || ii >= cc) { printz("edgecalc() bug %d \n",ii); return; } if (sa_edgedist[ii]) return; dx = epx - pxm; // calculate distance to edge dy = epy - pym; dist2 = sqrt(dx*dx + dy*dy) + 1; // minor bug fix sa_edgedist[ii] = dist2; // save Fbusy_done++; // track progress } } else { slope = 1.0 * (epy - py1) / (epx - px1); if (epx > px1) inc = 1; else inc = -1; for (pxm = px1; pxm != epx; pxm += inc) { pym = py1 + slope * (pxm - px1); ii = pym * ww + pxm; if (ii < 0 || ii >= cc) { printz("edgecalc() bug %d \n",ii); return; } if (sa_edgedist[ii]) return; dx = epx - pxm; dy = epy - pym; dist2 = sqrt(dx*dx + dy*dy) + 1; sa_edgedist[ii] = dist2; Fbusy_done++; } } return; } // adjust area edge 1 pixel out (+) or in (-) void sa_edgecreep(int kk) { int px, py, ii, jj; if (sa_stat != 3) { zmessageACK(Mwin,ZTX("the area is not finished")); return; } for (py = sa_miny; py < sa_maxy; py++) // find all area edge pixels for (px = sa_minx; px < sa_maxx; px++) { ii = Fpxb->ww * py + px; if (sa_pixmap[ii] != 1) continue; if (px == 0 || px == Fpxb->ww-1 || py == 0 || py == Fpxb->hh-1) // edge of image, no change continue; if (kk < 0) { // shrink area sa_pixmap[ii] = 0; continue; } sa_pixmap[ii-1] = sa_pixmap[ii+1] = 2; // expand area jj = ii - Fpxb->ww; sa_pixmap[jj] = sa_pixmap[jj-1] = sa_pixmap[jj+1] = 2; // mark 8 neighbor pixels in area jj = ii + Fpxb->ww; sa_pixmap[jj] = sa_pixmap[jj-1] = sa_pixmap[jj+1] = 2; } sa_map_pixels(); // remap edge and interior pixels sa_finish_auto(); // finish area again sa_show(1,0); // show area sa_calced = 0; // invalidate prior edge calc. Fpaint2(); return; } // Compute edge blend coefficient for given edge distance. // Returned coefficient: 0.0 to 1.0 for edge distance from 0 to sa_blend. float sa_blendfunc(int edgedist) { static float coeff[5000]; // edge distance limit static int Pblend = -1; float ff; int ii; if (sa_mode == mode_image) { // whole image area (see m_paint_edits()) ff = 1.0 * edgedist / sa_blend; return ff; } if (edgedist >= sa_blend) return 1.0; if (sa_blend == Pblend) return coeff[edgedist]; if (sa_blend < 1 || sa_blend > 4999) return 1.0; Pblend = sa_blend; for (ii = 0; ii <= sa_blend; ii++) { ff = ii; ff = PI * (ff / sa_blend - 0.5); // -PI/2 ... +PI/2 coeff[ii] = 0.5 * (sinf(ff) + 1.0); // 0.0 ... 1.0 } return coeff[edgedist]; } /******************************************************************************** select area copy/paste and open/save - select area <--> disk file *********************************************************************************/ namespace sa_diskfile { PXM *sacp_pxm = 0; // select area pixmap image int sacp_ww, sacp_hh; // original dimensions PXM *sacpR_pxm = 0; // resized/rotated image int sacpR_ww, sacpR_hh; // resized/rotated dimensions float sacp_scale; // scale, 1.0 = original size float sacp_angle; // angle of rotation, -180 to +180 int sacp_orgx, sacp_orgy; // origin in target image float sacp_blend; // pasted area edge blend with image float sacp_brite; // pasted area brightness adjustment int sacp_porg = 0; // pasted area is present int sacp_porgx, sacp_porgy; // pasted area origin in image int sacp_pww, sacp_phh; // pasted area dimensions editfunc EFpaste; } // Save a select area in memory to a disk PNG file. // For menu "Copy Area" use default file: ~/.fotoxx/saved_areas/copied_area.png // For menu "Save Area File" use file name from user input. void m_select_save(GtkWidget *, cchar *menu) // consolidate 2 menus { using namespace sa_diskfile; int ii, px1, py1, px2, py2, dist; int ww, nc, pcc, alpha; float *pix1, *pix2; char *pp, *file; char filename[100]; if (strmatch(menu,ZTX("Copy Area")) || strmatch(menu,"copy")) F1_help_topic = "area_copy_paste"; else if (strmatch(menu,ZTX("Save Area File")) || strmatch(menu,"save")) F1_help_topic = "area_open_save"; else { zmessageACK(Mwin,"menu name bug: %s",menu); return; } if (! sa_stat) return; // no selected area if (sa_mode == mode_image) return; // a whole image area if (sa_stat < 3) { zmessageACK(Mwin,ZTX("the area is not finished")); return; } if (! E0pxm) { // get poss. 16-bit image paintlock(1); // block window updates E0pxm = PXM_load(curr_file,1); paintlock(0); // unblock window updates if (! E0pxm) return; } ww = E0pxm->ww; nc = E0pxm->nc; pcc = nc * sizeof(float); PXM_free(sacp_pxm); // free prior if any PXM_free(sacpR_pxm); sacp_ww = sa_maxx - sa_minx; // new area image PXM sacp_hh = sa_maxy - sa_miny; sacp_pxm = PXM_make(sacp_ww,sacp_hh,4); // alpha channel for (py2 = 0; py2 < sacp_hh; py2++) // loop area pixels for (px2 = 0; px2 < sacp_ww; px2++) { px1 = px2 + sa_minx; py1 = py2 + sa_miny; pix1 = PXMpix(E0pxm,px1,py1); // copy to area PXM pix2 = PXMpix(sacp_pxm,px2,py2); memcpy(pix2,pix1,pcc); // copy RGB(A) data ii = py1 * ww + px1; dist = sa_pixmap[ii]; // 0/1/2+ = outside/edge/inside if (dist == 0) pix2[3] = 0; // outside pixel, transparent else if (nc < 4) pix2[3] = 255; // inside, opaque if alpha added } for (py2 = 1; py2 < sacp_hh-1; py2++) // loop area edge pixels for (px2 = 1; px2 < sacp_ww-1; px2++) { px1 = px2 + sa_minx; py1 = py2 + sa_miny; ii = py1 * ww + px1; if (sa_pixmap[ii] != 1) continue; // not an edge pixel alpha = 0; if (sa_pixmap[ii-1] == 1 && sa_pixmap[ii+1] == 1) alpha += 128; // add transparency to edge pixels else if (sa_pixmap[ii-1] == 1 || sa_pixmap[ii+1] == 1) alpha += 32; // depending on neighboring if (sa_pixmap[ii-ww] == 1 && sa_pixmap[ii+ww] == 1) alpha += 128; // edge pixels else if (sa_pixmap[ii-ww] == 1 || sa_pixmap[ii+ww] == 1) alpha += 32; if (sa_pixmap[ii-ww-1] == 1 && sa_pixmap[ii+ww+1] == 1) alpha += 128; else if (sa_pixmap[ii-ww-1] == 1 || sa_pixmap[ii+ww+1] == 1) alpha += 32; if (sa_pixmap[ii-ww-1] == 1 && sa_pixmap[ii+ww+1] == 1) alpha += 128; else if (sa_pixmap[ii-ww-1] == 1 || sa_pixmap[ii+ww+1] == 1) alpha += 32; if (alpha > 255) alpha = 255; pix2 = PXMpix(sacp_pxm,px2,py2); if (pix2[3] > alpha) pix2[3] = alpha; // only reduce opacity } if (strmatch(menu,ZTX("Copy Area")) || strmatch(menu,"copy")) { snprintf(filename,100,"%s/copied_area.png",saved_areas_dirk); // save to default PNG file PXM_PNG_save(sacp_pxm,filename,16); return; } pp = zgetfile(ZTX("save area as a PNG file"),MWIN,"save",saved_areas_dirk); // get file name from user if (! pp) return; file = zstrdup(pp,8); zfree(pp); pp = strrchr(file,'/'); pp = strcasestr(pp,".png"); if (! pp) strcat(file,".png"); PXM_PNG_save(sacp_pxm,file,16); // use PNG-16 file zfree(file); return; } // Read a select area from a disk PNG file. void m_select_open(GtkWidget *, cchar *) { using namespace sa_diskfile; void select_paste(GtkWidget *, cchar *); PXM *pxmtemp; char *file; float *pix1, *pix2; int px, py, nc, pcc; F1_help_topic = "area_open_save"; PXM_free(sacp_pxm); // free prior if any PXM_free(sacpR_pxm); file = gallery_select1(saved_areas_dirk); // use thumbnail selection 17.08 if (! file) return; pxmtemp = ANY_PXM_load(file); // load image file zfree(file); if (! pxmtemp) { if (*file_errmess) zmessageACK(Mwin,file_errmess); return; } nc = pxmtemp->nc; pcc = nc * sizeof(float); // 3 or 4 channels, RGB or RGBA sacp_ww = pxmtemp->ww; // image dimensiona sacp_hh = pxmtemp->hh; sacp_pxm = PXM_make(sacp_ww,sacp_hh,4); // alpha channel for (py = 0; py < sacp_hh; py++) for (px = 0; px < sacp_ww; px++) { pix1 = PXMpix(pxmtemp,px,py); pix2 = PXMpix(sacp_pxm,px,py); memcpy(pix2,pix1,pcc); // copy all channels if (nc < 4) pix2[3] = 255; // no alpha, all pixels opaque } PXM_free(pxmtemp); select_paste(0,0); // interactive move/resize area image return; } // paste the area last copied on to the current image // uses the default file from "copy area" menu (above) void m_select_paste(GtkWidget *, cchar *) { using namespace sa_diskfile; void select_paste(GtkWidget *, cchar *); PXM *pxmtemp; char filename[100]; float *pix1, *pix2; int px, py, nc, pcc; F1_help_topic = "area_copy_paste"; PXM_free(sacp_pxm); // free prior if any PXM_free(sacpR_pxm); snprintf(filename,100,"%s/copied_area.png",saved_areas_dirk); pxmtemp = ANY_PXM_load(filename); // load image file if (! pxmtemp) { if (*file_errmess) zmessageACK(Mwin,file_errmess); return; } nc = pxmtemp->nc; pcc = nc * sizeof(float); sacp_ww = pxmtemp->ww; // image dimensiona sacp_hh = pxmtemp->hh; sacp_pxm = PXM_make(sacp_ww,sacp_hh,4); // alpha channel for (py = 0; py < sacp_hh; py++) for (px = 0; px < sacp_ww; px++) { pix1 = PXMpix(pxmtemp,px,py); pix2 = PXMpix(sacp_pxm,px,py); memcpy(pix2,pix1,pcc); // copy all channels if (nc < 4) pix2[3] = 255; // no alpha, all pixels opaque } PXM_free(pxmtemp); select_paste(0,0); // interactive move/resize area image return; } // paste select area in memory into current image // this is an edit function - select area image is copied into main image void select_paste(GtkWidget *, cchar *) { using namespace sa_diskfile; void select_paste_image(); int select_paste_dialog_event(zdialog *, cchar *event); void select_paste_mousefunc(); cchar *dragmess = ZTX("position with mouse click/drag"); if (! sacp_pxm) return; // nothing to paste sa_unselect(); // unselect area if present EFpaste.menufunc = select_paste; EFpaste.funcname = "paste area"; EFpaste.Frestart = 1; // make restartable if (! edit_setup(EFpaste)) return; // setup edit for paste sacp_scale = 1.0; // size = 1x sacp_blend = 0; // edge blend = 0 sacp_angle = 0; // angle = 0 sacp_brite = 1.0; // no brightness adjustment sacp_porg = 0; // no image paste location yet sa_calced = 0; // no edge distance calculation PXM_free(sacpR_pxm); // free prior if any if (sacp_ww > E0pxm->ww) // if paste image > current image sacp_scale = 1.0 * E0pxm->ww / sacp_ww; // scale down to fit if (sacp_hh * sacp_scale > E0pxm->hh) sacp_scale = 1.0 * E0pxm->hh / sacp_hh; if (sacp_scale < 1.0) { sacp_ww *= sacp_scale; sacp_hh *= sacp_scale; sacpR_pxm = PXM_rescale(sacp_pxm,sacp_ww,sacp_hh); PXM_free(sacp_pxm); sacp_pxm = sacpR_pxm; sacp_scale = 1.0; } sacpR_pxm = PXM_copy(sacp_pxm); // initial paste image sacpR_ww = sacp_ww; // size = 1.0, no rotation sacpR_hh = sacp_hh; sacp_orgx = sacp_orgy = 0; // initial position select_paste_image(); // paste area image if (! sa_Npixel) { // failed edit_cancel(0); // cancel edit, restore image PXM_free(sacpR_pxm); // free memory return; } /*** __________________________________________________ | position with mouse click/drag | | | | resize [-10%] [-1%] [-.1%] [+.1%] [+1%] [+10%] | | angle [-10°] [-1°] [-.1°] [+.1°] [+1°] [+10°] | | brightness ==============[]==================== | | edge blend ===[]=============================== | | | | [Done] [Cancel] | |__________________________________________________| ***/ CEF->zd = zdialog_new(ZTX("Paste Image"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(CEF->zd,"hbox","hb0","dialog",0,"space=3"); zdialog_add_widget(CEF->zd,"label","lab1","hb0",dragmess,"space=5"); zdialog_add_widget(CEF->zd,"hbox","hbres","dialog",0,"space=3"); zdialog_add_widget(CEF->zd,"label","labres","hbres",ZTX("resize"),"space=3"); zdialog_add_widget(CEF->zd,"button","-10%","hbres","-10%"); zdialog_add_widget(CEF->zd,"button","-1%","hbres","-1%"); zdialog_add_widget(CEF->zd,"button","-.1%","hbres","-.1%"); zdialog_add_widget(CEF->zd,"button","+.1%","hbres","+.1%"); zdialog_add_widget(CEF->zd,"button","+1%","hbres","+1%"); zdialog_add_widget(CEF->zd,"button","+10%","hbres","+10%"); zdialog_add_widget(CEF->zd,"hbox","hbang","dialog",0,"space=3"); zdialog_add_widget(CEF->zd,"label","labang","hbang",Bangle,"space=3"); zdialog_add_widget(CEF->zd,"button","-10°","hbang","-10°"); zdialog_add_widget(CEF->zd,"button","-1°","hbang","-1°"); zdialog_add_widget(CEF->zd,"button","-.1°","hbang","-.1°"); zdialog_add_widget(CEF->zd,"button","+.1°","hbang","+.1°"); zdialog_add_widget(CEF->zd,"button","+1°","hbang","+1°"); zdialog_add_widget(CEF->zd,"button","+10°","hbang","+10°"); zdialog_add_widget(CEF->zd,"hbox","hbbr","dialog",0,"space=3"); zdialog_add_widget(CEF->zd,"label","labbr","hbbr","brightness","space=3"); zdialog_add_widget(CEF->zd,"hscale","brite","hbbr","0.3|3.0|0.001|1.0","expand|space=3"); zdialog_add_widget(CEF->zd,"hbox","hbbl","dialog",0,"space=3"); zdialog_add_widget(CEF->zd,"label","labbl","hbbl","edge blend","space=3"); zdialog_add_widget(CEF->zd,"hscale","blend","hbbl","0|50|0.1|0","expand|space=3"); zdialog_run(CEF->zd,select_paste_dialog_event,"save"); // start dialog takeMouse(select_paste_mousefunc,0); // connect mouse function return; } // Dialog event and completion callback function. // Get dialog values and convert image. When done, commit edited image // (with pasted area) and set up a new select area for the pasted area, // allowing further editing of the area. int select_paste_dialog_event(zdialog *zd, cchar *event) { using namespace sa_diskfile; void select_paste_mousefunc(); void select_paste_image(); void select_paste_adjust(); int ww, hh; PXM *pxm_temp; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (zd->zstat) // dialog completed { freeMouse(); // disconnect mouse if (zd->zstat != 1 || ! sacp_porg) { // cancel paste edit_cancel(0); // cancel edit, restore image sa_unselect(); PXM_free(sacpR_pxm); // free memory return 1; } if (! sa_calced) sa_edgecalc(); // update edge distance data sa_show(1,0); // show area edit_done(0); // commit the edit (pasted image) PXM_free(sacpR_pxm); // free memory return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(select_paste_mousefunc,0); if (strstr(event,"%") || strstr(event,"°")) // new size or angle { if (strmatch(event,"+.1%")) sacp_scale *= 1.001; if (strmatch(event,"+1%")) sacp_scale *= 1.01; if (strmatch(event,"+10%")) sacp_scale *= 1.10; if (strmatch(event,"-.1%")) sacp_scale *= 0.999001; if (strmatch(event,"-1%")) sacp_scale *= 0.990099; if (strmatch(event,"-10%")) sacp_scale *= 0.909091; // -10% is really 1.0/1.10 if (strmatch(event,"+.1°")) sacp_angle += 0.1; // rotation if (strmatch(event,"+1°")) sacp_angle += 1.0; if (strmatch(event,"+10°")) sacp_angle += 10.0; if (strmatch(event,"-.1°")) sacp_angle -= 0.1; if (strmatch(event,"-1°")) sacp_angle -= 1.0; if (strmatch(event,"-10°")) sacp_angle -= 10.0; PXM_free(sacpR_pxm); // free prior if any ww = sacp_scale * sacp_ww; // new size hh = sacp_scale * sacp_hh; pxm_temp = PXM_rescale(sacp_pxm,ww,hh); // resized area image sacpR_pxm = PXM_rotate(pxm_temp,sacp_angle); // rotated area image PXM_free(pxm_temp); sacpR_ww = sacpR_pxm->ww; // size after resize/rotate sacpR_hh = sacpR_pxm->hh; select_paste_image(); // copy onto target image sa_calced = 0; // edge distance calculation needed } if (strmatch(event,"blend") && sacp_porg) { // edge blend width adjustment zdialog_fetch(zd,"blend",sacp_blend); select_paste_adjust(); } if (strmatch(event,"brite") && sacp_porg) { // area brightness adjustment zdialog_fetch(zd,"brite",sacp_brite); select_paste_adjust(); } Fpaint2(); return 1; } // mouse function - follow mouse drags and move pasted area accordingly void select_paste_mousefunc() { using namespace sa_diskfile; void select_paste_image(); int mx1, my1, mx2, my2; static int mdx0, mdy0, mdx1, mdy1; if (LMclick) { // left mouse click LMclick = 0; sacp_orgx = Mxclick - sacpR_ww / 2; // position image at mouse sacp_orgy = Myclick - sacpR_hh / 2; select_paste_image(); } if (! sacp_porg) return; // no select area paste yet if (Mxposn < sacp_orgx || Mxposn > sacp_orgx + sacpR_ww || // mouse outside select area Myposn < sacp_orgy || Myposn > sacp_orgy + sacpR_hh) gdk_window_set_cursor(gdkwin,0); // set normal cursor else gdk_window_set_cursor(gdkwin,dragcursor); // set drag cursor if (! Mxdrag && ! Mydrag) return; // no drag underway if (Mxdown != mdx0 || Mydown != mdy0) { // new drag initiated mdx0 = mdx1 = Mxdown; mdy0 = mdy1 = Mydown; } mx1 = mdx1; // drag start my1 = mdy1; mx2 = Mxdrag; // drag position my2 = Mydrag; mdx1 = mx2; // next drag start mdy1 = my2; Mxdrag = Mydrag = 0; sacp_orgx += (mx2 - mx1); // move position of select area sacp_orgy += (my2 - my1); // by mouse drag amount select_paste_image(); // re-copy area to new position sa_calced = 0; // edge distance calculation needed return; } // copy select area into edit image, starting at sacp_orgx/y // called when area moved, resized, rotated void select_paste_image() { using namespace sa_diskfile; void select_paste_makearea(); int px1, py1, px2, py2, nc, pcc; float *pix1, *pix2, *pix3; float f1, f2; float red, green, blue; if (sacp_porg) // prior area overlap rectangle { nc = E1pxm->nc; pcc = nc * sizeof(float); for (py2 = 0; py2 < sacp_phh; py2++) // loop area pixels for (px2 = 0; px2 < sacp_pww; px2++) { px1 = px2 + sacp_porgx; // coresp. E1/E3 image pixel py1 = py2 + sacp_porgy; if (px1 < 0 || px1 >= Fpxb->ww) continue; // parts may be beyond edges if (py1 < 0 || py1 >= Fpxb->hh) continue; pix1 = PXMpix(E1pxm,px1,py1); // restore E1 pixels to E3 pix3 = PXMpix(E3pxm,px1,py1); memcpy(pix3,pix1,pcc); } Fpaint3(sacp_porgx,sacp_porgy,sacp_pww,sacp_phh,0); // update window } for (py2 = 0; py2 < sacpR_hh; py2++) // copy paste area pixels to new for (px2 = 0; px2 < sacpR_ww; px2++) // image overlap rectangle { px1 = px2 + sacp_orgx; py1 = py2 + sacp_orgy; if (px1 < 0 || px1 >= E3pxm->ww) continue; // parts may be beyond edges if (py1 < 0 || py1 >= E3pxm->hh) continue; pix2 = PXMpix(sacpR_pxm,px2,py2); // area pixel red = pix2[0]; green = pix2[1]; blue = pix2[2]; pix3 = PXMpix(E3pxm,px1,py1); // corresp. image pixel if (pix2[3] < 255) { // opacity of area pixel f1 = pix2[3] / 255.0; f2 = 1.0 - f1; red = f1 * red + f2 * pix3[0]; // blend area and image pixels green = f1 * green + f2 * pix3[1]; blue = f1 * blue + f2 * pix3[2]; } pix3[0] = red; pix3[1] = green; pix3[2] = blue; } Fpaint3(sacp_orgx,sacp_orgy,sacpR_ww,sacpR_hh,0); // update window for new overlap area sacp_porgx = sacp_orgx; // remember location for next call sacp_porgy = sacp_orgy; sacp_pww = sacpR_ww; sacp_phh = sacpR_hh; sacp_porg = 1; select_paste_makearea(); if (! sa_Npixel) return; CEF->Fmods++; // image is modified CEF->Fsaved = 0; return; } // convert the pasted image area into an equivalent select area by mouse void select_paste_makearea() { using namespace sa_diskfile; int cc, ii; int px1, py1, px2, py2; float *pix2, opac; sa_unselect(); // unselect old area cc = Fpxb->ww * Fpxb->hh * sizeof(uint16); sa_pixmap = (uint16 *) zmalloc(cc); // pixel map for new area memset(sa_pixmap,0,cc); for (py2 = 0; py2 < sacpR_hh; py2++) // loop area pixels for (px2 = 0; px2 < sacpR_ww; px2++) { px1 = px2 + sacp_orgx; // corresp. E0 image pixel py1 = py2 + sacp_orgy; if (px1 < 0 || px1 >= Fpxb->ww) continue; // parts may be beyond edges if (py1 < 0 || py1 >= Fpxb->hh) continue; pix2 = PXMpix(sacpR_pxm,px2,py2); // get opacity opac = pix2[3]; ii = py1 * Fpxb->ww + px1; // set sa_pixmap[] from opacity if (opac== 0) sa_pixmap[ii] = 0; // pixel outside area else sa_pixmap[ii] = 2; // interior pixel } sa_stat = 1; sa_mode = mode_mouse; // equivalent select by mouse area sa_fww = Fpxb->ww; sa_fhh = Fpxb->hh; sa_finish_auto(); // will find new edge pixels sa_show(0,0); // hide area return; } // make area brightness and edge blend adjustments. void select_paste_adjust() { using namespace sa_diskfile; int px1, py1, px2, py2; float *pix1, *pix2, *pix3; float red, green, blue; float maxrgb, f1, f2; int ii, dist; if (! sa_calced) sa_edgecalc(); // update edge distance data for (py2 = 0; py2 < sacpR_hh; py2++) // copy paste area pixels to new for (px2 = 0; px2 < sacpR_ww; px2++) // image overlap rectangle { px1 = px2 + sacp_orgx; py1 = py2 + sacp_orgy; if (px1 < 0 || px1 >= E3pxm->ww) continue; // parts may be beyond edges if (py1 < 0 || py1 >= E3pxm->hh) continue; pix2 = PXMpix(sacpR_pxm,px2,py2); // area pixel pix1 = PXMpix(E1pxm,px1,py1); // corresp. E1 image pixel pix3 = PXMpix(E3pxm,px1,py1); // corresp. E3 image pixel red = pix2[0]; // area pixel green = pix2[1]; blue = pix2[2]; red *= sacp_brite; // adjust brightness green *= sacp_brite; blue *= sacp_brite; maxrgb = red; // prevent RGB limit overflow if (green > maxrgb) maxrgb = green; if (blue > maxrgb) maxrgb = blue; if (maxrgb > 255.9) { f1 = 255.9 / maxrgb; red *= f1; green *= f1; blue *= f1; } f1 = pix2[3] / 255.0; // area pixel opacity if (f1 < 1.0) { f2 = 1.0 - f1; red = f1 * red + f2 * pix1[0]; // blend area and image pixels green = f1 * green + f2 * pix1[1]; blue = f1 * blue + f2 * pix1[2]; } ii = py1 * Fpxb->ww + px1; dist = sa_pixmap[ii]; // edge distance if (dist == 0) f1 = 0; else if (dist < sacp_blend) f1 = f1 * dist / sacp_blend; // ramp opacity within blend distance f2 = 1.0 - f1; red = f1 * red + f2 * pix1[0]; green = f1 * green + f2 * pix1[1]; blue = f1 * blue + f2 * pix1[2]; pix3[0] = red; pix3[1] = green; pix3[2] = blue; } return; } fotoxx-18.01.1/f.tools.cc0000644000175000017500000053302113222767271013561 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit - Tools menu functions m_index dialog to create/update image index file index_rebuild create/update image index file index_rebuild_old use old image index file without updates m_settings user settings dialog m_KBshortcuts edit keyboard shortcuts KBshorts_load load KB shortcuts file into memory m_show_brdist show brightness distribution graph m_gridlines setup for grid lines m_line_color choose color for foreground lines m_show_RGB show RGB values for clicked image positions m_magnify magnify the image within a radius of the mouse m_extreme_pixels highlight darkest and brightest pixels m_monitor_color monitor color and contrast check m_monitor_gamma monitor gamma check and adjust m_change_lang choose GUI language m_untranslated report misting translations m_color_profile convert from one color profile to another m_calibrate_printer printer color calibration make_appimage_desktop install desktop and icon files for appimage package m_uninstall_appimage completely uninstall appimage package m_resources print memory allocated and CPU time used m_zappcrash zappcrash test *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // Index Image Files menu function // Dialog to get top image directories, thumbnails directory, indexed metadata items. // Update the index_config file and generate new image index. namespace index_names { zdialog *zd_indexlog = 0; xxrec_t *xxrec = 0, **xxrec_old = 0, **xxrec_new = 0; int *Fupdate = 0, *Tupdate = 0; int Nold, Nnew; int indexupdates, thumbupdates, thumbdeletes; int indexthread1, indexthread2, thumbthread1, thumbthread2; int Fuserkill; } // menu function void m_index(GtkWidget *, cchar *) { using namespace index_names; void index_callbackfunc(GtkWidget *widget, int line, int pos, int kbkey); int index_dialog_event(zdialog *zd, cchar *event); zdialog *zd; FILE *fid; char filespec[200], buff[200], sthumbdirk[200]; char *pp; GtkWidget *widget; int line, ii, cc, zstat; cchar *greet1 = ZTX("Select directories containing image files \n" "(subdirectories are included automatically)."); cchar *greet2 = ZTX("Select to add, click on X to delete."); cchar *greet3 = ZTX("directory for thumbnails"); cchar *greet4 = ZTX("extra metadata items to include in index"); cchar *termmess = ZTX("Index function terminated. \n" "Indexing is required for search and map functions \n" "and to make the gallery pages acceptably fast."); F1_help_topic = "index_files"; if (checkpend("all")) return; // check nothing pending Fblock = 1; Findexvalid = 0; /*** _____________________________________________________ | Index Image Files | | | | Select directories containing image files. | | (subdirectories are included automatically). | | [Select] Select to add, click on X to delete. | | _________________________________________________ | | | X /home//Pictures | | | | X /home//... | | | | | | | | | | | | | | | |_________________________________________________| | | | | [Select] directory for thumbnails | | [_________________________________________________] | | | | [Select] extra metadata items to include in index | 18.01 | | | [Help] [Proceed] [Cancel] | |_____________________________________________________| ***/ zd = zdialog_new(ZTX("Index Image Files"),Mwin,Bhelp,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbgreet1","dialog"); zdialog_add_widget(zd,"label","labgreet1","hbgreet1",greet1,"space=5"); zdialog_add_widget(zd,"hbox","hbtop","dialog"); zdialog_add_widget(zd,"button","browsetop","hbtop",Bselect,"space=5"); zdialog_add_widget(zd,"label","labgreet2","hbtop",greet2,"space=5"); zdialog_add_widget(zd,"hbox","hbtop2","dialog",0,"expand"); zdialog_add_widget(zd,"label","space","hbtop2",0,"space=3"); zdialog_add_widget(zd,"vbox","vbtop2","hbtop2",0,"expand"); zdialog_add_widget(zd,"label","space","hbtop2",0,"space=3"); zdialog_add_widget(zd,"frame","frtop","vbtop2",0,"expand"); zdialog_add_widget(zd,"scrwin","scrtop","frtop",0,"expand"); zdialog_add_widget(zd,"text","topdirks","scrtop"); // topdirks text zdialog_add_widget(zd,"vbox","vbspace","dialog",0,"space=4"); zdialog_add_widget(zd,"hbox","hbthumb1","dialog"); zdialog_add_widget(zd,"button","browsethumb","hbthumb1",Bselect,"space=3"); // browsethumb button zdialog_add_widget(zd,"label","labgreet3","hbthumb1",greet3,"space=5"); zdialog_add_widget(zd,"hbox","hbthumb2","dialog"); zdialog_add_widget(zd,"frame","frthumb","hbthumb2",0,"space=5|expand"); zdialog_add_widget(zd,"zentry","sthumbdirk","frthumb"); // thumbnail directory 17.08 zdialog_add_widget(zd,"vbox","vbspace","dialog",0,"space=4"); zdialog_add_widget(zd,"hbox","hbxmeta","dialog"); zdialog_add_widget(zd,"button","browsxmeta","hbxmeta",Bselect,"space=3"); // browse xmeta metadata 18.01 zdialog_add_widget(zd,"label","labgreet4","hbxmeta",greet4,"space=5"); widget = zdialog_widget(zd,"topdirks"); // set click function for textwidget_set_callbackfunc(widget,index_callbackfunc); // top directories text window textwidget_clear(widget); // default top directory textwidget_append(widget,0," X %s\n",getenv("HOME")); // /home/ snprintf(sthumbdirk,200,"%s/thumbnails",get_zhomedir()); // default thumbnails directory zdialog_stuff(zd,"sthumbdirk",sthumbdirk); // /home//.fotoxx/thumbnails xmeta_keys[0] = 0; // default no indexed metadata snprintf(filespec,200,"%s/index_config",index_dirk); // read index_config file, // stuff data into dialog widgets fid = fopen(filespec,"r"); if (fid) { textwidget_clear(widget); while (true) { pp = fgets_trim(buff,200,fid,1); if (! pp) break; if (strmatchN(buff,"thumbnails:",11)) { // if "thumbnails: /..." if (buff[12] == '/') zdialog_stuff(zd,"sthumbdirk",buff+12); // stuff thumbnails directory } else if (strmatchN(buff,"metadata:",9)) { // if "metadata:" 18.01 for (ii = 0; ii < Mxmeta; ii++) { // build indexed metadata list pp = (char *) strField(buff+9,"^",ii+1); if (! pp) break; xmeta_keys[ii] = zstrdup(pp); } xmeta_keys[ii] = 0; // mark EOL } else textwidget_append(widget,0," X %s\n",buff); // stuff " X /dir1/dir2..." } fclose(fid); } zdialog_resize(zd,500,500); // run dialog zdialog_run(zd,index_dialog_event,"parent"); zstat = zdialog_wait(zd); // wait for completion if (zstat != 2) { // canceled zdialog_free(zd); if (! Findexvalid) zmessageACK(Mwin,termmess); // index started and not finished Fblock = 0; return; } snprintf(filespec,200,"%s/index_config",index_dirk); // open/write index config file fid = fopen(filespec,"w"); if (! fid) { // fatal bug zmessageACK(Mwin,"index_config file: \n %s",strerror(errno)); Fblock = 0; m_quit(0,0); } widget = zdialog_widget(zd,"topdirks"); // get top directories from dialog widget for (line = 0; ; line++) { pp = textwidget_line(widget,line,1); // loop widget text lines if (! pp || ! *pp) break; pp += 4; // skip " X " if (*pp != '/') continue; strncpy0(buff,pp,200); // /dir1/dir2/... cc = strlen(buff); if (cc < 5) continue; // ignore blanks or rubbish if (buff[cc-1] == '/') buff[cc-1] = 0; // remove trailing '/' fprintf(fid,"%s\n",buff); // write top directory to config file } zdialog_fetch(zd,"sthumbdirk",buff,200); // get thumbnails directory from dialog strTrim2(buff); // remove surrounding blanks cc = strlen(buff); if (cc && buff[cc-1] == '/') buff[cc-1] = 0; // remove trailing '/' fprintf(fid,"thumbnails: %s\n",buff); // thumbnails directory >> config file *buff = 0; for (ii = 0; ii < Mxmeta; ii++) { // indexed metadata >> config file 18.01 if (! xmeta_keys[ii]) break; strncatv(buff,200,xmeta_keys[ii],"^ ",0); } fprintf(fid,"metadata: %s\n",buff); fclose(fid); zdialog_free(zd); // close dialog index_rebuild(2,1); // build image index and thumbnail files if (! Findexvalid) m_index(0,0); // failed, try again Fblock = 0; // OK return; } // ------------------------------------------------------------------------------ // mouse click function for top directories text window // remove directory from list where "X" is clicked void index_callbackfunc(GtkWidget *widget, int line, int pos, int kbkey) { GdkWindow *gdkwin; char *pp; char *dirlist[maxtopdirks]; int ii, jj; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } gdkwin = gtk_widget_get_window(widget); // stop updates between clear and refresh gdk_window_freeze_updates(gdkwin); for (ii = jj = 0; ii < maxtopdirks; ii++) // loop text lines in widget { // " X /dir1/dir2/... " pp = textwidget_line(widget,ii,1); if (! pp || strlen(pp) < 4) break; if (ii == line && pos < 3) continue; // if "X" clicked, skip deleted line dirlist[jj] = zstrdup(pp); jj++; } textwidget_clear(widget); for (ii = 0; ii < jj; ii++) // stuff remaining lines back into widget { textwidget_append(widget,0,"%s\n",dirlist[ii]); zfree(dirlist[ii]); } gdk_window_thaw_updates(gdkwin); return; } // ------------------------------------------------------------------------------ // index dialog event and completion function int index_dialog_event(zdialog *zd, cchar *event) { int ii; GtkWidget *widget; char **flist, *pp, *sthumbdirk; cchar *topmess = ZTX("Choose top image directories"); cchar *thumbmess = ZTX("Choose thumbnail directory"); cchar *xmetamess = ZTX("All image files will be re-indexed. \n" " Continue?"); if (strmatch(event,"browsetop")) { // [browse] top directories flist = zgetfiles(topmess,MWIN,"folders",getenv("HOME")); // get top directories from user if (! flist) return 1; widget = zdialog_widget(zd,"topdirks"); // add to dialog list for (ii = 0; flist[ii]; ii++) { textwidget_append(widget,0," X %s\n",flist[ii]); // " X /dir1/dir2/..." zfree(flist[ii]); } zfree(flist); } if (strmatch(event,"browsethumb")) { // [browse] thumbnail directory pp = zgetfile(thumbmess,MWIN,"folder",getenv("HOME")); if (! pp) return 1; sthumbdirk = zstrdup(pp,12); if (! strstr(sthumbdirk,"/thumbnails")) // if not containing /thumbnails, strcat(sthumbdirk,"/thumbnails"); // append /thumbnails zdialog_stuff(zd,"sthumbdirk",sthumbdirk); zfree(sthumbdirk); zfree(pp); } if (strmatch(event,"browsxmeta")) { // [select] 18.01 ii = zmessageYN(Mwin,xmetamess); // add optional indexed metadata if (! ii) return 1; ii = select_meta_keys(xmeta_keys,1); if (ii) xmeta_changed = 1; // changes made, force full index save_params(); } if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) { // [help] zd->zstat = 0; showz_userguide("index_files"); return 1; } return 1; // [proceed] or cancel status } // ------------------------------------------------------------------------------ // Rebuild the image index from the index config data. // index level = 0/1/2 = no index / old files only / old + new files // Called from main() when Fotoxx is started (index level from user setting) // Called from menu function m_index() (index level = 2) void index_rebuild(int indexlev, int menu) { using namespace index_names; void index_rebuild_old(); int indexlog_dialog_event(zdialog *zd, cchar *event); int index_compare(cchar *rec1, cchar *rec2); void *index_thread(void *); void *thumb_thread(void *); GtkWidget *wlog; FILE *fid; int ii, jj, Ntop, Nthumb, err, updatesreq; int ftf, cc, NF, orec, orec2, nrec, comp; char *pp, filespec[200]; char buff[XFCC+500]; char **flist, *file, *thumbfile; STATB statdat; double startime; cchar *indexmess = ZTX("No image file index was found.\n" "An image file index will be created.\n" "Your image files will not be changed.\n" "This may need considerable time if you \n" "have many thousands of image files."); cchar *indexerr = ZTX("Invalid directory: \n %s \n" "Please remove."); cchar *thumberr = ZTX("Thumbnails directory: \n %s \n" "must be named .../thumbnails"); cchar *duperr = ZTX("Duplicate directory: \n %s \n" "Please remove."); startime = get_seconds(); // index function start time Findexvalid = 0; Fblock = 1; Fuserkill = 0; // get current top image directories and thumbnails directory // from /home//.fotoxx/image_index/index_config Ntop = Nthumb = 0; snprintf(filespec,200,"%s/index_config",index_dirk); // read index config file fid = fopen(filespec,"r"); if (fid) { while (true) { // get top image directories pp = fgets_trim(buff,200,fid,1); if (! pp) break; if (strmatchN(buff,"thumbnails: /",13)) { // get thumbnails directory if (thumbdirk) zfree(thumbdirk); thumbdirk = zstrdup(buff+12); Nthumb++; } else if (strmatchN(buff,"metadata:",9)) { // get indexed metadata keys 18.01 for (ii = 0; ii < Mxmeta; ii++) { pp = (char *) strField(buff+9,"^",ii+1); if (! pp) { xmeta_keys[ii] = 0; break; } xmeta_keys[ii] = zstrdup(pp); } } else { if (topdirks[Ntop]) zfree(topdirks[Ntop]); // save top directories list topdirks[Ntop] = zstrdup(buff); if (++Ntop == maxtopdirks) break; } } fclose(fid); } Ntopdirks = Ntop; if (! Ntopdirks) { // if nothing found, must ask user zmessageACK(Mwin,indexmess); goto cleanup; } for (ii = 0; ii < Ntopdirks; ii++) { // validate top directories err = stat(topdirks[ii],&statdat); if (err || ! S_ISDIR(statdat.st_mode)) { zmessageACK(Mwin,indexerr,topdirks[ii]); goto cleanup; } } if (Nthumb != 1) { // 0 or >1 thumbnail directories zmessageACK(Mwin,ZTX("specify 1 thumbnail directory")); goto cleanup; } cc = strlen(thumbdirk) - 11 ; // check /thumbnails name if (! strmatch(thumbdirk+cc,"/thumbnails")) { zmessageACK(Mwin,thumberr,thumbdirk); goto cleanup; } err = stat(thumbdirk,&statdat); // create thumbnails directory if needed if (err || ! S_ISDIR(statdat.st_mode)) err = shell_ack("mkdir -p -m 0750 \'%s\'",thumbdirk); // use shell mkdir 17.08 if (err) { zmessageACK(Mwin,"%s \n %s",thumbdirk,strerror(errno)); goto cleanup; } for (ii = 0; ii < Ntopdirks; ii++) { // disallow top dir = thumbnail dir if (strmatch(topdirks[ii],thumbdirk)) { zmessageACK(Mwin,indexerr,topdirks[ii]); goto cleanup; } } for (ii = 0; ii < Ntopdirks; ii++) // check for duplicate directories for (jj = ii+1; jj < Ntopdirks; jj++) { if (strmatch(topdirks[ii],topdirks[jj])) { zmessageACK(Mwin,duperr,topdirks[jj]); goto cleanup; } } // process image index according to indexlev: // 0 / 1 / 2 = no index / old files only / old + new files if (indexlev == 0) { printz("no image index: reports disabled \n"); // no image index Findexvalid = 0; Fblock = 0; return; } if (indexlev == 1) { printz("old image index: reports will omit new files \n"); // image index has old files only index_rebuild_old(); Fblock = 0; return; } if (indexlev == 2) // update image index for all image files printz("full image index: reports will be complete \n"); // create log window for reporting status and statistics if (zd_indexlog) zdialog_free(zd_indexlog); // make dialog for output log zd_indexlog = zdialog_new("build index",Mwin,BOK,Bcancel,null); zdialog_add_widget(zd_indexlog,"frame","frame","dialog",0,"expand"); zdialog_add_widget(zd_indexlog,"scrwin","scrwin","frame"); zdialog_add_widget(zd_indexlog,"text","text","scrwin"); wlog = zdialog_widget(zd_indexlog,"text"); ///zdialog_set_modal(zd_indexlog); // removed 18.01 zdialog_resize(zd_indexlog,700,500); zdialog_run(zd_indexlog,indexlog_dialog_event,"parent"); textwidget_append(wlog,0,"top image directories:\n"); // log top image directories printz("top image directories:\n"); for (ii = 0; ii < Ntopdirks; ii++) { textwidget_append(wlog,0," %s\n",topdirks[ii]); printz(" %s\n",topdirks[ii]); } textwidget_append(wlog,0,"thumbnails directory: \n"); // and thumbnails directory textwidget_append(wlog,0," %s \n",thumbdirk); textwidget_scroll(wlog,0); printz("thumbnails directory: \n"); printz(" %s \n",thumbdirk); if (xmeta_changed) { // indexed metadata list changed 18.01 Nold = 0; // force fully new image index goto get_new; } // read image index file and build "old list" of index recs textwidget_append(wlog,0,"reading image index file ...\n"); cc = maximages * sizeof(xxrec_t *); xxrec_old = (xxrec_t **) zmalloc(cc); // "old" image index recs Nold = 0; ftf = 1; while (true) { xxrec = read_xxrec_seq(ftf); // read curr. index recs if (! xxrec) break; xxrec_old[Nold] = xxrec; Nold++; if (Nold == maximages) { zmessageACK(Mwin,"exceeded max. images: %d \n",maximages); Fblock = 0; m_quit(0,0); } zmainloop(100); } // sort old index recs in order of file name and file mod date if (Nold) HeapSort((char **) xxrec_old,Nold,index_compare); // replace older recs with newer recs that were appended at the end before sorting if (Nold) { for (orec = 0, orec2 = 1; orec2 < Nold; orec2++) { if (strmatch(xxrec_old[orec]->file,xxrec_old[orec2]->file)) xxrec_old[orec] = xxrec_old[orec2]; else { orec++; xxrec_old[orec] = xxrec_old[orec2]; } } Nold = orec + 1; // new count } get_new: // find all image files and create "new list" of index recs textwidget_append(wlog,0,"find all image files ...\n"); cc = maximages * sizeof(xxrec_t *); xxrec_new = (xxrec_t **) zmalloc(cc); // "new" image index recs Fupdate = (int *) zmalloc(maximages * sizeof(int)); // flags, index update needed 17.04.3 Tupdate = (int *) zmalloc(maximages * sizeof(int)); // flags, thumb update needed 18.01 Nnew = 0; for (ii = 0; ii < Ntopdirks; ii++) { err = find_imagefiles(topdirks[ii],1+16,flist,NF); // image files, recurse dirs 18.01 if (err) { zmessageACK(Mwin,"find_imagefiles() failure \n"); Fblock = 0; m_quit(0,0); } if (Nnew + NF > maximages) { zmessageACK(Mwin,"exceeded max. images: %d \n",maximages); Fblock = 0; m_quit(0,0); } for (jj = 0; jj < NF; jj++) { file = flist[jj]; nrec = Nnew++; xxrec_new[nrec] = (xxrec_t *) zmalloc(sizeof(xxrec_t)); // allocate xxrec xxrec_new[nrec]->file = file; // filespec stat(file,&statdat); compact_time(statdat.st_mtime,xxrec_new[nrec]->fdate); // file mod date xxrec_new[nrec]->pdate[0] = 0; // image date = empty strcpy(xxrec_new[nrec]->rating,"0"); // stars = "0" strcpy(xxrec_new[nrec]->size,"0x0"); // size = "0x0" xxrec_new[nrec]->tags = 0; // tags = empty xxrec_new[nrec]->capt = 0; // caption = empty xxrec_new[nrec]->comms = 0; // comments = empty xxrec_new[nrec]->location = 0; // location = empty 17.01 xxrec_new[nrec]->country = 0; xxrec_new[nrec]->flati = 0; // earth coordinates = 0 17.01 xxrec_new[nrec]->flongi = 0; xxrec_new[nrec]->xmeta = 0; // indexed metadata = none 18.01 } if (flist) zfree(flist); } textwidget_append(wlog,0,"image files found: %d \n",Nnew); printz("image files found: %d \n",Nnew); if (Nnew == 0) { // no images found zmessageACK(Mwin,ZTX("Top directories have no images")); goto cleanup; } // sort new index recs in order of file name and file mod date if (Nnew) HeapSort((char **) xxrec_new,Nnew,index_compare); // merge and compare lists // if filespecs match and have the same date, then "old" record is OK // otherwise flag the file for index record update updatesreq = Nnew; for (orec = nrec = 0; nrec < Nnew; nrec++) // loop all image files { Fupdate[nrec] = 1; // assume index update is needed Tupdate[nrec] = 1; // and thumb update 18.01 if (thumbnail_OK(xxrec_new[nrec]->file)) Tupdate[nrec] = 0; // no thumbnail update needed 18.01 if (orec == Nold) continue; // no more old recs while (true) { comp = strcmp(xxrec_old[orec]->file,xxrec_new[nrec]->file); // compare orec file to nrec file if (comp >= 0) break; // orec >= nrec orec++; // orec < nrec, next orec if (orec == Nold) break; } if (comp == 0) // orec = nrec (same image file) { if (strmatch(xxrec_new[nrec]->fdate,xxrec_old[orec]->fdate)) // file dates match { Fupdate[nrec] = 0; // index update not needed updatesreq--; strncpy0(xxrec_new[nrec]->pdate,xxrec_old[orec]->pdate,15); // copy data from old to new strncpy0(xxrec_new[nrec]->rating,xxrec_old[orec]->rating,2); strncpy0(xxrec_new[nrec]->size,xxrec_old[orec]->size,15); xxrec_new[nrec]->tags = xxrec_old[orec]->tags; xxrec_old[orec]->tags = 0; xxrec_new[nrec]->capt = xxrec_old[orec]->capt; xxrec_old[orec]->capt = 0; xxrec_new[nrec]->comms = xxrec_old[orec]->comms; xxrec_old[orec]->comms = 0; xxrec_new[nrec]->location = xxrec_old[orec]->location; // 17.01 xxrec_old[orec]->location = 0; xxrec_new[nrec]->country = xxrec_old[orec]->country; xxrec_old[orec]->country = 0; xxrec_new[nrec]->flati = xxrec_old[orec]->flati; // 17.01 xxrec_new[nrec]->flongi = xxrec_old[orec]->flongi; xxrec_new[nrec]->xmeta = xxrec_old[orec]->xmeta; // 18.01 xxrec_old[orec]->xmeta = 0; } orec++; // next old rec } } textwidget_append(wlog,0,"index updates needed: %d \n",updatesreq); printz("index updates needed: %d \n",updatesreq); // Process entries needing update in the new index list // (new files or files dated later than image index date). // Get updated metadata from image file EXIF/IPTC data. // Check if thumbnail is missing or stale and update if needed. textwidget_append(wlog,0,"updating image index and thumbnails ... \n"); textwidget_append(wlog,0,"\n"); indexupdates = thumbupdates = 0; indexthread1 = indexthread2 = 0; thumbthread1 = thumbthread2 = 0; for (nrec = 0; nrec < Nnew; nrec++) // loop all index recs 17.01 { if (Fuserkill) goto cleanup; // killed by user if (Fupdate[nrec]) { // update metadata indexupdates++; while (indexthread1 - indexthread2 == 4) zsleep(0.001); // = exif_server count (4) indexthread1++; Fupdate[nrec] = nrec; // bugfix: separate arg per thread 17.04.3 start_detached_thread(index_thread,&Fupdate[nrec]); } if (Tupdate[nrec]) { // update thumbnail 18.01 while (thumbthread1 - thumbthread2 == 8) zsleep(0.001); // 8 parallel threads thumbthread1++; Tupdate[nrec] = nrec; start_detached_thread(thumb_thread,&Tupdate[nrec]); } if (Fupdate[nrec] || Tupdate[nrec]) { // update counters 18.01 file = xxrec_new[nrec]->file; textwidget_replace(wlog,0,-1,"%d %d %s \n", indexupdates,thumbupdates,file); } } while (indexthread2 < indexthread1) zsleep(0.001); // done, wait for last threads 17.01.1 while (thumbthread2 < thumbthread1) zsleep(0.001); // 18.01 textwidget_replace(wlog,0,-1,"index updates: %d thumbnails: %d \n", // final statistics indexupdates, thumbupdates); printz("index updates: %d thumbnail updates: %d, deletes: %d \n", indexupdates, thumbupdates, thumbdeletes); // write updated index records to image index file printz("writing updated image index file \n"); if (Nnew) { err = 0; ftf = 1; for (nrec = 0; nrec < Nnew; nrec++) { err = write_xxrec_seq(xxrec_new[nrec],ftf); if (err) break; } if (err) { printz("image index file: %s",strerror(errno)); m_quit(0,0); } else write_xxrec_seq(null,ftf); // close output } // create image index table in memory if (xxrec_tab) { for (ii = 0; ii < Nxxrec; ii++) // free memory for old xxrec_tab { if (xxrec_tab[ii]->file) zfree(xxrec_tab[ii]->file); if (xxrec_tab[ii]->tags) zfree(xxrec_tab[ii]->tags); if (xxrec_tab[ii]->capt) zfree(xxrec_tab[ii]->capt); if (xxrec_tab[ii]->comms) zfree(xxrec_tab[ii]->comms); if (xxrec_tab[ii]->location) zfree(xxrec_tab[ii]->location); // 17.01 if (xxrec_tab[ii]->country) zfree(xxrec_tab[ii]->country); if (xxrec_tab[ii]->xmeta) zfree(xxrec_tab[ii]->xmeta); // 18.01 zfree(xxrec_tab[ii]); } zfree(xxrec_tab); xxrec_tab = 0; Nxxrec = 0; } if (Nnew) { cc = maximages * sizeof(xxrec_t *); // make new table with max. capacity xxrec_tab = (xxrec_t **) zmalloc(cc); for (nrec = 0; nrec < Nnew; nrec++) { if (! xxrec_new[nrec]->tags) xxrec_new[nrec]->tags = zstrdup("null"); if (! xxrec_new[nrec]->capt) xxrec_new[nrec]->capt = zstrdup("null"); if (! xxrec_new[nrec]->comms) xxrec_new[nrec]->comms = zstrdup("null"); if (! xxrec_new[nrec]->location) xxrec_new[nrec]->location = zstrdup("null"); // 17.01 if (! xxrec_new[nrec]->country) xxrec_new[nrec]->country = zstrdup("null"); if (! xxrec_new[nrec]->xmeta) xxrec_new[nrec]->xmeta = zstrdup("null"); // 18.01 xxrec_tab[nrec] = xxrec_new[nrec]; } Nxxrec = Nnew; } // find orphan thumbnails and delete them if (Fuserkill) goto cleanup; textwidget_append(wlog,0,"deleting orphan thumbnails ... \n"); thumbdeletes = 0; err = find_imagefiles(thumbdirk,2+16,flist,NF); // thumbnails, recurse dirs 18.01 if (err) { zmessageACK(Mwin,strerror(errno)); NF = 0; } for (ii = 0; ii < NF; ii++) { if (Fuserkill) break; // killed by user zmainloop(100); thumbfile = flist[ii]; file = thumb2imagefile(thumbfile); if (file) { zfree(file); zfree(thumbfile); continue; } remove(thumbfile); zfree(thumbfile); thumbdeletes++; } if (flist) zfree(flist); textwidget_append(wlog,0,"thumbnails deleted: %d \n",thumbdeletes); if (Fuserkill) goto cleanup; textwidget_append(wlog,0,"%s\n",Bcompleted); // index complete and OK printz("index time: %.1f seconds \n",get_seconds() - startime); // log elapsed time Findexvalid = 2; // image index is complete cleanup: // free allocated memory if (xxrec_old) { for (orec = 0; orec < Nold; orec++) { zfree(xxrec_old[orec]->file); // free xxrec-> char. strings if (xxrec_old[orec]->tags) zfree(xxrec_old[orec]->tags); if (xxrec_old[orec]->capt) zfree(xxrec_old[orec]->capt); if (xxrec_old[orec]->comms) zfree(xxrec_old[orec]->comms); if (xxrec_old[orec]->location) zfree(xxrec_old[orec]->location); // 17.01 if (xxrec_old[orec]->country) zfree(xxrec_old[orec]->country); if (xxrec_old[orec]->xmeta) zfree(xxrec_old[orec]->xmeta); // 18.01 zfree(xxrec_old[orec]); // free xxrec record } zfree(xxrec_old); xxrec_old = 0; } if (xxrec_new) // xxrec_new[*] xxrec records { // now belong to xxrec_tab[*] zfree(xxrec_new); // free pointers only xxrec_new = 0; } if (Fupdate) { zfree(Fupdate); Fupdate = 0; } if (zd_indexlog && ! menu) // if not manual run, kill log window zdialog_send_event(zd_indexlog,"done"); Fblock = 0; // unblock if (xmeta_changed) { // indexed metadata was changed 18.01 xmeta_changed = 0; save_params(); } if (! Findexvalid) { // 18.01 if (menu) return; // called manually else m_index(0,0); // called from fotoxx startup } return; } // ------------------------------------------------------------------------------ // thread process - create thumbnail file for given image file void * thumb_thread(void *arg) // 18.01 { using namespace index_names; int nrec, Fthumb; char *file; nrec = *((int *) arg); file = xxrec_new[nrec]->file; // image file file = xxrec_new[nrec]->file; // image file to check Fthumb = update_thumbnail_file(file); // do thumbnail update if needed if (Fthumb) zadd_locked(thumbupdates,+1); zadd_locked(thumbthread2,+1); pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // thread process - get image file metadata and build image index rec. // thread has no GTK calls void * index_thread(void *arg) { using namespace index_names; int nrec, err, ii, nkey, xcc; char *file, *pp; float flati, flongi; char xmetarec[1000]; cchar *exifkeys[100] = { "FileName", exif_date_key, iptc_keywords_key, // first 11 keys are fixed iptc_rating_key, exif_size_key, exif_comment_key, iptc_caption_key, exif_city_key, exif_country_key, exif_lati_key, exif_longi_key }; char *ppv[100], *exiffile = 0; char *exifdate = 0, *iptctags = 0, *iptcrating = 0; char *exifsize = 0, *iptccapt = 0, *exifcomms = 0; char *exifcity = 0, *exifcountry = 0, *exiflat = 0, *exiflong = 0; char city2[100], country2[100], lat2[20], long2[20]; nrec = *((int *) arg); file = xxrec_new[nrec]->file; // image file nkey = 11; // add keys for indexed metadata 18.01 for (ii = 0; ii < Mxmeta; ii++) { // from exifkeys[11] if (! xmeta_keys[ii]) break; exifkeys[nkey] = xmeta_keys[ii]; nkey++; } err = exif_get(file,exifkeys,ppv,nkey); // get exif/iptc metadata if (err) { printz("exif_get() failure: %s \n",file); // metadata unreadable goto exit_thread; } exiffile = ppv[0]; pp = strrchr(file,'/'); if (! exiffile || ! strmatch(exiffile,pp+1)) { // image file has no metadata printz("exif_get() no data: %s \n",file); goto exit_thread; } exifdate = ppv[1]; // exif/iptc metadata returned iptctags = ppv[2]; // 11 fixed keys iptcrating = ppv[3]; exifsize = ppv[4]; exifcomms = ppv[5]; iptccapt = ppv[6]; exifcity = ppv[7]; exifcountry = ppv[8]; exiflat = ppv[9]; exiflong = ppv[10]; if (exifdate && strlen(exifdate) > 3) // exif date (photo date) exif_tagdate(exifdate,xxrec_new[nrec]->pdate); else strcpy(xxrec_new[nrec]->pdate,"null"); // not present if (iptcrating && strlen(iptcrating)) { // iptc rating xxrec_new[nrec]->rating[0] = *iptcrating; xxrec_new[nrec]->rating[1] = 0; } else strcpy(xxrec_new[nrec]->rating,"0"); // not present if (exifsize && strlen(exifsize)) // exif size strncpy0(xxrec_new[nrec]->size,exifsize,15); if (iptctags && strlen(iptctags)) { // iptc tags xxrec_new[nrec]->tags = iptctags; iptctags = 0; } if (iptccapt && strlen(iptccapt)) { // iptc caption xxrec_new[nrec]->capt = iptccapt; iptccapt = 0; } if (exifcomms && strlen(exifcomms)) { // exif comments xxrec_new[nrec]->comms = exifcomms; exifcomms = 0; } strcpy(city2,"null"); // geotags = empty strcpy(country2,"null"); strcpy(lat2,"null"); strcpy(long2,"null"); if (exifcity) strncpy0(city2,exifcity,99); // get from exif if any if (exifcountry) strncpy0(country2,exifcountry,99); if (exiflat) strncpy0(lat2,exiflat,10); if (exiflong) strncpy0(long2,exiflong,10); xxrec_new[nrec]->location = zstrdup(city2); // 17.01 xxrec_new[nrec]->country = zstrdup(country2); if (strmatch(lat2,"null") || strmatch(long2,"null")) // 17.01 xxrec_new[nrec]->flati = xxrec_new[nrec]->flongi = 0; else { flati = atof(lat2); flongi = atof(long2); if (flati < -90.0 || flati > 90.0) flati = flongi = 0; if (flongi < -180.0 || flongi > 180.0) flati = flongi = 0; xxrec_new[nrec]->flati = flati; xxrec_new[nrec]->flongi = flongi; } if (exiffile) zfree(exiffile); if (exifdate) zfree(exifdate); // free EXIF data if (iptcrating) zfree(iptcrating); if (exifsize) zfree(exifsize); if (iptctags) zfree(iptctags); if (iptccapt) zfree(iptccapt); if (exifcomms) zfree(exifcomms); if (exifcity) zfree(exifcity); if (exifcountry) zfree(exifcountry); if (exiflat) zfree(exiflat); if (exiflong) zfree(exiflong); xcc = 0; for (ii = 11; ii < nkey; ii++) { // add indexed metadata if any 18.01 if (! ppv[ii]) continue; if (strlen(exifkeys[ii]) + strlen(ppv[ii]) > 100) continue; // impractical for image search strcpy(xmetarec+xcc,exifkeys[ii]); // construct series xcc += strlen(exifkeys[ii]); // "keyname=keydata^ " xmetarec[xcc++] = '='; strcpy(xmetarec+xcc,ppv[ii]); xcc += strlen(ppv[ii]); strcpy(xmetarec+xcc,"^ "); xcc += 2; zfree(ppv[ii]); if (xcc > 895) { printz("file metadata exceeds record size: %s \n",file); break; } } if (xcc > 0) xxrec_new[nrec]->xmeta = zstrdup(xmetarec); else xxrec_new[nrec]->xmeta = zstrdup("null"); exit_thread: zadd_locked(indexthread2,+1); // bugfix 18.01 pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // index log window dialog response function int indexlog_dialog_event(zdialog *zd, cchar *event) { using namespace index_names; int nn; cchar *canmess = ZTX("Cancel image index function?"); if (strmatch(event,"done")) { // auto-kill from index_rebuild() zmainloop(); zsleep(0.5); zdialog_free(zd); zd_indexlog = 0; return 1; } if (! zd->zstat) return 1; // continue if (zd->zstat == 1 || zd->zstat == 2) { // [OK] or [Cancel] button if (Findexvalid) { zdialog_free(zd); // index completed, kill dialog zd_indexlog = 0; return 1; } nn = zdialog_choose(Mwin,canmess,Bcontinue,Bcancel,null); // ask for confirmation if (nn == 1) { zd->zstat = 0; // continue return 1; } } zdialog_free(zd); // cancel, kill index job 18.01 zd_indexlog = 0; Fuserkill = 1; return 1; } // ------------------------------------------------------------------------------ // sort compare function - compare index records and return // <0 0 >0 for rec1 < rec2 rec1 == rec2 rec1 > rec2 int index_compare(cchar *rec1, cchar *rec2) { xxrec_t *xxrec1 = (xxrec_t *) rec1; xxrec_t *xxrec2 = (xxrec_t *) rec2; int nn = strcmp(xxrec1->file,xxrec2->file); if (nn) return nn; nn = strcmp(xxrec1->fdate,xxrec2->fdate); return nn; } /********************************************************************************/ // Rebuild image index table from existing image index file // without searching for new and modified files. void index_rebuild_old() { using namespace index_names; int ftf, cc, rec, rec2, Ntab; Fblock = 1; Findexvalid = 0; // read image index file and build table of index records cc = maximages * sizeof(xxrec_t *); xxrec_tab = (xxrec_t **) zmalloc(cc); // image index recs Ntab = 0; ftf = 1; while (true) { xxrec = read_xxrec_seq(ftf); // read curr. index recs if (! xxrec) break; xxrec_tab[Ntab] = xxrec; Ntab++; if (Ntab == maximages) { zmessageACK(Mwin,"exceeded max. images: %d \n",maximages); Fblock = 0; m_quit(0,0); } zmainloop(100); } // sort index recs in order of file name and file mod date if (Ntab) HeapSort((char **) xxrec_tab,Ntab,index_compare); // replace older recs with newer (appended) recs now sorted together if (Ntab) { for (rec = 0, rec2 = 1; rec2 < Ntab; rec2++) { if (strmatch(xxrec_tab[rec]->file,xxrec_tab[rec2]->file)) xxrec_tab[rec] = xxrec_tab[rec2]; else { rec++; xxrec_tab[rec] = xxrec_tab[rec2]; } } Ntab = rec + 1; // new count } Nxxrec = Ntab; Findexvalid = 1; // index OK but missing new files Fblock = 0; // unblock return; } /********************************************************************************/ // user settings dialog namespace usersettings { cchar *startopt[8][2] = { "recent", ZTX("Recent Files Gallery"), // fotoxx startup display options "newest", ZTX("Newest Files Gallery"), "specG", ZTX("Specific Gallery"), "album", ZTX("Album Gallery"), "prevG", ZTX("Previous Gallery"), "prevF", ZTX("Previous Image File"), // better name 17.08 "specF", ZTX("Specific Image File"), // better name 17.08 "blank", ZTX("Blank Window") }; } // menu function void m_settings(GtkWidget *, cchar *) { using namespace usersettings; int settings_dialog_event(zdialog *zd, cchar *event); int ii, zstat; zdialog *zd; char txrgb[20]; /*** ____________________________________________________________________ | User Settings | | | | Startup Display [ previous image file |v] | | [______________________________________________________] [Browse] | | | | Background color: F-View [###] G-View [###] | | Menu Text [###] Background [###] | 18.01 | Menu Style (o) Icons (o) Icons + Text Icon Size [___] | | Dialog Font [ sans 10 _______] [choose] | | Zoomed Image: (o) drag (o) scroll [x] fast zooms/2x [___] | 17.08 | JPEG save quality [____] | | Curve node capture distance [____] | | Map marker size [___] | | [x] prev/next shows last file version only | | [x] shift image right when editing | | image index level [___] Fotoxx started directly | 0/1/2 = none/old/old+search new | image index level [___] Fotoxx started by file manager | | RAW file types [_______________________________________________] | | VIDEO file types [_____________________________________________] | 17.08 | | | [done] | |____________________________________________________________________| ***/ F1_help_topic = "user_settings"; if (checkpend("all")) return; // check nothing pending Fblock = 1; zd = zdialog_new(ZTX("User Settings"),Mwin,Bdone,null); zdialog_add_widget(zd,"hbox","hbstart","dialog"); zdialog_add_widget(zd,"label","labstart","hbstart",ZTX("Startup Display"),"space=5"); zdialog_add_widget(zd,"combo","startopt","hbstart",0); for (ii = 0; ii < 8; ii++) zdialog_cb_app(zd,"startopt",startopt[ii][1]); // startup display option zdialog_add_widget(zd,"hbox","hbbrowse","dialog","space=3"); zdialog_add_widget(zd,"zentry","browsefile","hbbrowse",0,"expand"); zdialog_add_widget(zd,"button","browse","hbbrowse",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbbg","dialog",0,"space=3"); // F-view and G-view background color zdialog_add_widget(zd,"label","labbg","hbbg",ZTX("Background color:"),"space=5"); zdialog_add_widget(zd,"label","labfbg","hbbg",ZTX("F-View"),"space=5"); zdialog_add_widget(zd,"colorbutt","FBrgb","hbbg"); zdialog_add_widget(zd,"label","space","hbbg",0,"space=5"); zdialog_add_widget(zd,"label","labgbg","hbbg",ZTX("G-View"),"space=5"); zdialog_add_widget(zd,"colorbutt","GBrgb","hbbg"); zdialog_add_widget(zd,"hbox","hbmenu","dialog"); // menu text and background color 18.01 zdialog_add_widget(zd,"label","labmt","hbmenu",ZTX("Menu Text"),"space=5"); zdialog_add_widget(zd,"colorbutt","MFrgb","hbmenu"); zdialog_add_widget(zd,"label","space","hbmenu",0,"space=5"); zdialog_add_widget(zd,"label","labmb","hbmenu",ZTX("Background"),"space=5"); zdialog_add_widget(zd,"colorbutt","MBrgb","hbmenu"); zdialog_add_widget(zd,"hbox","hbmenu","dialog"); // menu style zdialog_add_widget(zd,"label","labms","hbmenu",ZTX("Menu Style"),"space=5"); zdialog_add_widget(zd,"radio","icons","hbmenu",ZTX("Icons"),"space=3"); zdialog_add_widget(zd,"radio","both","hbmenu",ZTX("Icons + Text"),"space=5"); zdialog_add_widget(zd,"label","space","hbmenu",0,"space=10"); zdialog_add_widget(zd,"label","labis","hbmenu",ZTX("Icon size")); zdialog_add_widget(zd,"zspin","iconsize","hbmenu","30|48|1|40","space=3"); // 17.01 zdialog_add_widget(zd,"hbox","hbfont","dialog"); // menu and dialog font zdialog_add_widget(zd,"label","labfont","hbfont",ZTX("Dialog font"),"space=5"); zdialog_add_widget(zd,"zentry","font","hbfont","Sans 10","size=20"); zdialog_add_widget(zd,"button","choosefont","hbfont",Bchoose,"space=5"); zdialog_add_widget(zd,"hbox","hbz","dialog"); // pan/zoom options 17.08 zdialog_add_widget(zd,"label","labips","hbz",ZTX("Zoomed Image:"),"space=5"); zdialog_add_widget(zd,"radio","drag","hbz","drag","space=3"); zdialog_add_widget(zd,"radio","scroll","hbz","scroll","space=3"); zdialog_add_widget(zd,"check","fast","hbz","fast","space=3"); zdialog_add_widget(zd,"label","space","hbz",0,"space=8"); zdialog_add_widget(zd,"label","labz","hbz","zooms/2x"); zdialog_add_widget(zd,"zspin","zoomcount","hbz","1|8|1|2","space=5|size=3"); zdialog_add_widget(zd,"hbox","hbjpeg","dialog"); zdialog_add_widget(zd,"label","labqual","hbjpeg",ZTX("JPEG save quality"),"space=5"); zdialog_add_widget(zd,"zspin","quality","hbjpeg","1|100|1|90"); zdialog_add_widget(zd,"hbox","hbcap","dialog"); zdialog_add_widget(zd,"label","labcap","hbcap",ZTX("Curve node capture distance"),"space=5"); zdialog_add_widget(zd,"zspin","nodecap","hbcap","3|20|1|5"); zdialog_add_widget(zd,"hbox","hbnet","dialog"); zdialog_add_widget(zd,"label","labnet","hbnet",ZTX("Map marker size"),"space=5"); zdialog_add_widget(zd,"zspin","map_dotsize","hbnet","5|20|1|8"); zdialog_add_widget(zd,"hbox","hblastver","dialog"); zdialog_add_widget(zd,"check","lastver","hblastver",ZTX("show last file version only"),"space=5"); zdialog_add_widget(zd,"hbox","hbshiftright","dialog"); zdialog_add_widget(zd,"check","shiftright","hbshiftright",ZTX("shift image to right margin"),"space=5"); zdialog_add_widget(zd,"hbox","hbxlev","dialog"); zdialog_add_widget(zd,"label","labxlev","hbxlev",ZTX("image index level"),"space=5"); zdialog_add_widget(zd,"zspin","indexlev","hbxlev","0|2|1|2"); zdialog_add_widget(zd,"label","labxlev2","hbxlev",ZTX("Fotoxx started directly"),"space=5"); zdialog_add_widget(zd,"hbox","hbfmxlev","dialog"); zdialog_add_widget(zd,"label","labfmxlev","hbfmxlev",ZTX("image index level"),"space=5"); zdialog_add_widget(zd,"zspin","fmindexlev","hbfmxlev","0|2|1|2"); zdialog_add_widget(zd,"label","labfmxlev2","hbfmxlev",ZTX("Fotoxx started by file manager"),"space=5"); zdialog_add_widget(zd,"hbox","hbrawfile","dialog"); zdialog_add_widget(zd,"label","rawlab","hbrawfile",ZTX("RAW file types"),"space=5"); zdialog_add_widget(zd,"zentry","rawtypes","hbrawfile",".raw .rw2","expand"); zdialog_add_widget(zd,"hbox","hbvideos","dialog"); zdialog_add_widget(zd,"label","videolab","hbvideos",ZTX("video file types"),"space=5"); zdialog_add_widget(zd,"zentry","videotypes","hbvideos",".mp4 .mov","expand"); for (ii = 0; ii < 8; ii++) { if (strmatch(startdisplay,startopt[ii][0])) // set startup display option list zdialog_stuff(zd,"startopt",startopt[ii][1]); } if (strmatch(startdisplay,"specG")) // restore current startup option 17.08 zdialog_stuff(zd,"browsefile",startdirk); else if (strmatch(startdisplay,"specF")) zdialog_stuff(zd,"browsefile",startfile); else if (strmatch(startdisplay,"album")) zdialog_stuff(zd,"browsefile",startalbum); // bugfix (omission) else zdialog_stuff(zd,"browsefile",""); zdialog_stuff(zd,"icons",0); // menu style zdialog_stuff(zd,"both",0); if (strmatch(menu_style,"icons")) zdialog_stuff(zd,"icons",1); else zdialog_stuff(zd,"both",1); snprintf(txrgb,20,"%d|%d|%d",FBrgb[0],FBrgb[1],FBrgb[2]); // F-view background color zdialog_stuff(zd,"FBrgb",txrgb); snprintf(txrgb,20,"%d|%d|%d",GBrgb[0],GBrgb[1],GBrgb[2]); // G-view background color zdialog_stuff(zd,"GBrgb",txrgb); snprintf(txrgb,20,"%d|%d|%d",MFrgb[0],MFrgb[1],MFrgb[2]); // menus font color 18.01 zdialog_stuff(zd,"MFrgb",txrgb); snprintf(txrgb,20,"%d|%d|%d",MBrgb[0],MBrgb[1],MBrgb[2]); // menus background color 18.01 zdialog_stuff(zd,"MBrgb",txrgb); zdialog_stuff(zd,"iconsize",iconsize); // icon size zdialog_stuff(zd,"font",dialog_font); // curr. dialog font zdialog_stuff(zd,"drag",1); // drag/scroll defaults 17.08 zdialog_stuff(zd,"scroll",1); zdialog_stuff(zd,"fast",0); if (Fdragopt == 1) zdialog_stuff(zd,"drag",1); // set drag/scroll option 17.08 if (Fdragopt == 2) zdialog_stuff(zd,"scroll",1); if (Fdragopt == 3) zdialog_stuff(zd,"drag",1); if (Fdragopt == 4) zdialog_stuff(zd,"scroll",1); if (Fdragopt >= 3) zdialog_stuff(zd,"fast",1); if (zoomcount >= 1 && zoomcount <= 8) // zooms for 2x increase zdialog_stuff(zd,"zoomcount",zoomcount); zdialog_stuff(zd,"quality",jpeg_def_quality); // default jpeg file save quality zdialog_stuff(zd,"nodecap",splcurve_minx); // edit curve min. node distance zdialog_stuff(zd,"map_dotsize",map_dotsize); // map dot size zdialog_stuff(zd,"lastver",Flastversion); // prev/next shows last version only zdialog_stuff(zd,"shiftright",Fshiftright); // shift image to right margin zdialog_stuff(zd,"indexlev",Findexlev); // index level, always zdialog_stuff(zd,"fmindexlev",FMindexlev); // index level, file manager call zdialog_stuff(zd,"rawtypes",RAWfiletypes); // RAW file types zdialog_stuff(zd,"videotypes",VIDEOfiletypes); // VIDEO file types zdialog_run(zd,settings_dialog_event,"parent"); // run dialog and wait for completion zstat = zdialog_wait(zd); zdialog_free(zd); Fblock = 0; if (zstat == 1) { m_clone(0,0); // start new session if changes made 18.01 quitxx(); } return; } // settings dialog event function int settings_dialog_event(zdialog *zd, cchar *event) { using namespace usersettings; int ii, jj, nn; char *pp, temp[200]; cchar *ppc; static char browsefile[500] = ""; char txrgb[20]; GtkWidget *font_dialog; if (zd->zstat && zd->zstat != 1) return 1; // cancel if (strmatch(event,"browse")) // browse for startup directory or file { zdialog_fetch(zd,"startopt",temp,200); // get startup option for (ii = 0; ii < 8; ii++) { if (strmatch(temp,startopt[ii][1])) { zfree(startdisplay); startdisplay = zstrdup(startopt[ii][0]); } } zdialog_fetch(zd,"browsefile",browsefile,500); // initial browse location if (! *browsefile && topdirks[0]) strncpy0(browsefile,topdirks[0],500); if (strmatch(startdisplay,"specG")) { pp = zgetfile(ZTX("Select startup directory"),MWIN,"folder",browsefile); if (! pp) return 1; zdialog_stuff(zd,"browsefile",pp); zfree(pp); } if (strmatch(startdisplay,"specF")) { pp = zgetfile(ZTX("Select startup image file"),MWIN,"file",browsefile); if (! pp) return 1; zdialog_stuff(zd,"browsefile",pp); zfree(pp); } if (strmatch(startdisplay,"album")) { // choose album 17.04 pp = zgetfile(ZTX("Select startup album"),MWIN,"file",albums_dirk); if (! pp) return 1; zdialog_stuff(zd,"browsefile",pp); zfree(pp); } } if (strmatch(event,"choosefont")) // choose menu/dialog font { zdialog_fetch(zd,"font",temp,200); font_dialog = gtk_font_chooser_dialog_new(ZTX("select font"),MWIN); gtk_font_chooser_set_font(GTK_FONT_CHOOSER(font_dialog),temp); gtk_dialog_run(GTK_DIALOG(font_dialog)); pp = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(font_dialog)); gtk_widget_destroy(font_dialog); if (! pp) return 1; zdialog_stuff(zd,"font",pp); zsetfont(pp); dialog_font = zstrdup(pp); g_free(pp); } if (zd->zstat != 1) return 1; // wait for dialog completion zdialog_fetch(zd,"startopt",temp,200); // get startup option for (ii = 0; ii < 8; ii++) { if (strmatch(temp,startopt[ii][1])) { zfree(startdisplay); startdisplay = zstrdup(startopt[ii][0]); } } if (strmatch(startdisplay,"album")) { // startup option = album 17.04 zdialog_fetch(zd,"browsefile",browsefile,500); // get startup album if (startalbum) zfree(startalbum); startalbum = zstrdup(browsefile); } if (strmatch(startdisplay,"specG")) { // startup option = directory gallery zdialog_fetch(zd,"browsefile",browsefile,500); // get startup directory if (startdirk) zfree(startdirk); startdirk = zstrdup(browsefile); if (image_file_type(startdirk) != FDIR) { zmessageACK(Mwin,ZTX("startup directory is invalid")); zd->zstat = 0; return 1; } } if (strmatch(startdisplay,"specF")) { // startup option = image file zdialog_fetch(zd,"browsefile",browsefile,500); // get startup file if (startfile) zfree(startfile); startfile = zstrdup(browsefile); if (image_file_type(startfile) != IMAGE) { zmessageACK(Mwin,ZTX("startup file is invalid")); zd->zstat = 0; return 1; } } zdialog_fetch(zd,"icons",nn); // menu type = icons if (nn) { if (! strmatch(menu_style,"icons")) { zfree(menu_style); menu_style = zstrdup("icons"); } } zdialog_fetch(zd,"both",nn); // menu type = icons + text if (nn) { if (! strmatch(menu_style,"both")) { zfree(menu_style); menu_style = zstrdup("both"); } } zdialog_fetch(zd,"iconsize",nn); // icon size if (nn != iconsize) { iconsize = nn; } zdialog_fetch(zd,"FBrgb",txrgb,20); // F-view background color ppc = strField(txrgb,"|",1); if (ppc) FBrgb[0] = atoi(ppc); ppc = strField(txrgb,"|",2); if (ppc) FBrgb[1] = atoi(ppc); ppc = strField(txrgb,"|",3); if (ppc) FBrgb[2] = atoi(ppc); zdialog_fetch(zd,"GBrgb",txrgb,20); // G-view background color ppc = strField(txrgb,"|",1); if (ppc) GBrgb[0] = atoi(ppc); ppc = strField(txrgb,"|",2); if (ppc) GBrgb[1] = atoi(ppc); ppc = strField(txrgb,"|",3); if (ppc) GBrgb[2] = atoi(ppc); zdialog_fetch(zd,"MFrgb",txrgb,20); // menu font color 18.01 ppc = strField(txrgb,"|",1); if (ppc) MFrgb[0] = atoi(ppc); ppc = strField(txrgb,"|",2); if (ppc) MFrgb[1] = atoi(ppc); ppc = strField(txrgb,"|",3); if (ppc) MFrgb[2] = atoi(ppc); zdialog_fetch(zd,"MBrgb",txrgb,20); // menu background color 18.01 ppc = strField(txrgb,"|",1); if (ppc) MBrgb[0] = atoi(ppc); ppc = strField(txrgb,"|",2); if (ppc) MBrgb[1] = atoi(ppc); ppc = strField(txrgb,"|",3); if (ppc) MBrgb[2] = atoi(ppc); zdialog_fetch(zd,"drag",nn); // drag/scroll/zoom options 17.08 if (nn) Fdragopt = 1; // 1/2 = drag/scroll else Fdragopt = 2; zdialog_fetch(zd,"fast",nn); // 3/4 = drag/scroll fast if (nn) Fdragopt += 2; zdialog_fetch(zd,"zoomcount",zoomcount); // zooms for 2x image size zoomratio = pow( 2.0, 1.0 / zoomcount); // 2.0, 1.4142, 1.2599, 1.1892 ... zdialog_fetch(zd,"quality",jpeg_def_quality); // default jpeg file save quality zdialog_fetch(zd,"nodecap",splcurve_minx); // edit curve min. node distance zdialog_fetch(zd,"map_dotsize",map_dotsize); // map dot size zdialog_fetch(zd,"lastver",Flastversion); // prev/next shows last version only zdialog_fetch(zd,"shiftright",Fshiftright); // shift image to right margin zdialog_fetch(zd,"indexlev",Findexlev); // index level, always zdialog_fetch(zd,"fmindexlev",FMindexlev); // index level, started from FM zdialog_fetch(zd,"rawtypes",temp,200); // RAW file types, .raw .rw2 ... pp = zstrdup(temp,100); for (ii = jj = 0; temp[ii]; ii++) { // insure blanks between types if (temp[ii] == '.' && temp[ii-1] != ' ') pp[jj++] = ' '; pp[jj++] = temp[ii]; } if (pp[jj-1] != ' ') pp[jj++] = ' '; // insure 1 final blank pp[jj] = 0; if (RAWfiletypes) zfree(RAWfiletypes); RAWfiletypes = pp; zdialog_fetch(zd,"videotypes",temp,200); // VIDEO file types, .mp4 .mov ... 17.08 pp = zstrdup(temp,100); for (ii = jj = 0; temp[ii]; ii++) { // insure blanks between types if (temp[ii] == '.' && temp[ii-1] != ' ') pp[jj++] = ' '; pp[jj++] = temp[ii]; } if (pp[jj-1] != ' ') pp[jj++] = ' '; // insure 1 final blank pp[jj] = 0; if (VIDEOfiletypes) zfree(VIDEOfiletypes); VIDEOfiletypes = pp; save_params(); // done, save modified parameters Fpaint2(); // 17.08 return 1; } /********************************************************************************/ // keyboard shortcuts namespace KBshortcutnames { zdialog *zd; char *shortcutkey2[maxshortcuts]; // KB shortcut keys (or combinations) char *shortcutmenu2[maxshortcuts]; // corresponding menu names int Nshortcuts2; // count of entries int Nreserved = 14; // reserved shortcuts (hard coded) 18.01 cchar *reserved[14] = { "F1", "F10", "F11", "Escape", "Ctrl+H", "Delete", "Left", "Right", "Up", "Down", "First", "Last", "Page_Up", "Page_Down" }; int Nmenulist = 96; cchar *menulist_xx[96]; // translated menus cchar *menulist_en[96] = // english menus - functions that can be assigned shortcut keys { "Add Line", "Add Text", "All Directories", "Batch Add/Change Metadata", "Batch Add/Remove Tags", "Batch Convert", "Batch Delete/Trash", "Batch Geotags", "Batch Photo Date/Time", "Batch RAW", "Batch Rename Tags", "Batch Report Metadata", "Batch Upright", "Blend Image", "Blur", "Blur Background", "Bookmarks", "Brightness Graph", "Change Language", "Clone Image", "Color Depth", "Color Profile", "Color Saturation", "Copy Area", "Copy/Move to Location", "Copy to Clipboard", "Copy to Desktop", "Copy to Image Cache", "Custom Kernel", "Cycle 2 Previous Files", "Cycle 3 Previous Files", "Delete Metadata", "Delete/Trash Image File", "Denoise", "Disable Area", "Edit Any Metadata", "Edit Brightness", "Edit Metadata", "Enable Area", "File View", "Find Area Gap", "Gallery View", "Grid Lines", "Hide Area", "Image Locations/Dates", "Image Timeline", "Invert Area", "Keyboard Shortcuts", "Line Color", "Magnify Gradients", "Magnify Image", "Manage Albums", "Mirror Image", "Net Map View", "Newest Images", "New Window", "Open Area File", "Open Image File", "Open with EOG", "Paint Image", "Paste Area", "Print Image", "Recently Seen Images", "Red Eyes", "Rename Image File", "Replace Album File", "Resize", "Retouch Combo", "Save Area File", "Save File", "Search Images", "Select Area", "Sharpen", "Shift Colors", "Show Area", "Show Captions on Image", "Show Hidden Files", "Show on Net Map", "Show RGB", "Smart Erase", "Sync Gallery", "Trim/Rotate", "Unbend", "Unselect Area", "Unwarp Closeup", "Update Album Files", "Upright", "World Map View", "View Metadata (long)", "View 360° Panorama", "View Metadata (short)", "Voodoo 1", "Voodoo 2", "Zoom-in", "Zoom-out", "Zoom-1x toggle" }; } // KB shortcuts menu function - list current shortcuts void m_KBshortcuts(GtkWidget *, cchar *) { using namespace KBshortcutnames; int KBshorts_dialog_event(zdialog *zd, cchar *event); void KBshortcuts_edit(); int zstat; GtkWidget *widget; F1_help_topic = "KB_shortcuts"; zd = zdialog_new(ZTX("Keyboard Shortcuts"),Mwin,Bedit,Bcancel,null); zdialog_add_widget(zd,"hbox","hblist","dialog",0,"expand"); zdialog_add_widget(zd,"frame","frlist","hblist",0,"space=5|expand"); zdialog_add_widget(zd,"text","shortlist","frlist",0,"expand"); widget = zdialog_widget(zd,"shortlist"); textwidget_append(widget,1,ZTX("Reserved Shortcuts \n")); // reduced list 18.01 textwidget_append(widget,0,ZTX(" F1 User Guide for current function \n")); textwidget_append(widget,0,ZTX(" F10 Full Screen with menus \n")); textwidget_append(widget,0,ZTX(" F11 Full Screen without menus \n")); textwidget_append(widget,0,ZTX(" Escape Quit dialog; Quit Fotoxx \n")); textwidget_append(widget,0,ZTX(" Ctrl+H Show hidden files in Gallery mode \n")); textwidget_append(widget,0,ZTX(" Delete Delete/Trash dialog \n")); textwidget_append(widget,0,ZTX(" Arrows Previous/Next Image or Gallery page \n")); textwidget_append(widget,0,"\n"); textwidget_append(widget,1,"Custom Shortcuts \n"); for (int ii = 0; ii < Nshortcuts; ii++) textwidget_append(widget,0," %-14s %s \n",shortcutkey[ii],shortcutmenu[ii]); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,KBshorts_dialog_event,"parent"); zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat == 1) KBshortcuts_edit(); return; } // dialog event and completion function int KBshorts_dialog_event(zdialog *zd, cchar *event) { if (zd->zstat) zdialog_destroy(zd); return 1; } // KB shortcuts edit function void KBshortcuts_edit() { using namespace KBshortcutnames; void KBshorts_callbackfunc1(GtkWidget *widget, int line, int pos, int kbkey); void KBshorts_callbackfunc2(GtkWidget *widget, int line, int pos, int kbkey); int KBshorts_keyfunc(GtkWidget *dialog, GdkEventKey *event); int KBshorts_edit_dialog_event(zdialog *zd, cchar *event); int ii; GtkWidget *widget; /*** _________________________________________________________________ | Edit KB Shortcuts | |_________________________________________________________________| | Alt+G Grid Lines | Add Line | | Alt+U Undo | Add Text | | Alt+R Redo | Add Transparency | | T Trim/Rotate | Adjust HSL | | V View Metadata (short) | Adjust RGB/CMY | | Ctrl+Shift+V View Metadata (long) | Alien Colors | | ... ... | ... | |_________________________________________|_______________________| | | | shortcut key: (enter key) (no selection) | | | | [add] [remove] [done] [cancel] | |_________________________________________________________________| ***/ zd = zdialog_new(ZTX("Edit KB Shortcuts"),Mwin,Badd,Bdelete,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hblists","dialog",0,"expand"); zdialog_add_widget(zd,"frame","frshorts","hblists",0,"space=5"); zdialog_add_widget(zd,"text","shortlist","frshorts"); zdialog_add_widget(zd,"frame","frmenus","hblists",0,"space=5|expand"); zdialog_add_widget(zd,"scrwin","scrmenus","frmenus",0,"expand"); zdialog_add_widget(zd,"text","menufuncs","scrmenus"); zdialog_add_widget(zd,"hbox","hbshort","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labshort","hbshort",ZTX("shortcut key:"),"space=5"); zdialog_add_widget(zd,"label","shortkey","hbshort",ZTX("(enter key)"),"size=10"); zdialog_add_widget(zd,"label","shortfunc","hbshort",ZTX("(no selection)"),"space=5"); for (ii = 0; ii < Nmenulist; ii++) // translate menulist_en[] menulist_xx[ii] = ZTX(menulist_en[ii]); // >> menulist_xx[] for (ii = 0; ii < Nshortcuts; ii++) { // copy current shortcuts list shortcutkey2[ii] = zstrdup(shortcutkey[ii]); // for subsequent editing shortcutmenu2[ii] = zstrdup(shortcutmenu[ii]); } Nshortcuts2 = Nshortcuts; widget = zdialog_widget(zd,"shortlist"); // show shortcuts list in dialog textwidget_clear(widget); for (int ii = 0; ii < Nshortcuts2; ii++) textwidget_append(widget,0,"%-14s %s \n",shortcutkey2[ii],shortcutmenu2[ii]); textwidget_set_callbackfunc(widget,KBshorts_callbackfunc1); // callback func for mouse clicks widget = zdialog_widget(zd,"menufuncs"); // show menu list in dialog textwidget_clear(widget); for (ii = 0; ii < Nmenulist; ii++) textwidget_append(widget,0,"%s\n",menulist_xx[ii]); textwidget_set_callbackfunc(widget,KBshorts_callbackfunc2); // callback func for mouse clicks widget = zdialog_widget(zd,"dialog"); // capture KB keys pressed G_SIGNAL(widget,"key-press-event",KBshorts_keyfunc,0); zdialog_resize(zd,650,400); zdialog_run(zd,KBshorts_edit_dialog_event,"save"); return; } // mouse callback function to select existing shortcut from list // and stuff into dialog "shortfunc" void KBshorts_callbackfunc1(GtkWidget *widget, int line, int pos, int kbkey) { using namespace KBshortcutnames; char *txline; char shortkey[20]; char shortfunc[60]; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_userguide(F1_help_topic); return; } txline = textwidget_line(widget,line,1); // get clicked line if (! txline || ! *txline) return; textwidget_highlight_line(widget,line); strncpy0(shortkey,txline,14); // get shortcut key and menu strncpy0(shortfunc,txline+15,60); zfree(txline); strTrim(shortkey); strTrim(shortfunc); zdialog_stuff(zd,"shortkey",shortkey); // stuff into dialog zdialog_stuff(zd,"shortfunc",shortfunc); return; } // mouse callback function to select new shortcut function from menu list // and stuff into dialog "shortfunc" void KBshorts_callbackfunc2(GtkWidget *widget, int line, int pos, int kbkey) { using namespace KBshortcutnames; char *txline; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_userguide(F1_help_topic); return; } txline = textwidget_line(widget,line,1); // get clicked line if (! txline || ! *txline) return; textwidget_highlight_line(widget,line); zdialog_stuff(zd,"shortfunc",txline); // stuff into dialog zfree(txline); return; } // intercept KB key events, stuff into dialog "shortkey" int KBshorts_keyfunc(GtkWidget *dialog, GdkEventKey *event) { using namespace KBshortcutnames; int Ctrl = 0, Alt = 0, Shift = 0; int key, ii, cc; char keyname[20]; key = event->keyval; if (event->state & GDK_CONTROL_MASK) Ctrl = 1; if (event->state & GDK_SHIFT_MASK) Shift = 1; if (event->state & GDK_MOD1_MASK) Alt = 1; if (key == GDK_KEY_Escape) return 0; // pass escape (cancel) to zdialog if (key == GDK_KEY_F1) { // key is F1 (context help) KBevent(event); // send to main app return 1; } if (key >= GDK_KEY_F2 && key <= GDK_KEY_F9) { // key is F2 to F9 ii = key - GDK_KEY_F1; strcpy(keyname,"F1"); keyname[1] += ii; } else if (key > 255) return 1; // not a simple Ascii key else { *keyname = 0; // build input key combination if (Ctrl) strcat(keyname,"Ctrl+"); // [Ctrl+] [Alt+] [Shift+] key if (Alt) strcat(keyname,"Alt+"); if (Shift) strcat(keyname,"Shift+"); cc = strlen(keyname); keyname[cc] = toupper(key); // x --> X, Ctrl+x --> Ctrl+X keyname[cc+1] = 0; } for (ii = 0; ii < Nreserved; ii++) if (strmatch(keyname,reserved[ii])) break; if (ii < Nreserved) { zmessageACK(Mwin,ZTX("\"%s\" Reserved, cannot be used"),keyname); Ctrl = Alt = 0; return 1; } zdialog_stuff(zd,"shortkey",keyname); // stuff key name into dialog zdialog_stuff(zd,"shortfunc",ZTX("(no selection)")); // clear menu choice return 1; } // dialog event and completion function int KBshorts_edit_dialog_event(zdialog *zd, cchar *event) { using namespace KBshortcutnames; int KBshorts_edit_menufuncs_event(zdialog *zd, cchar *event); void KBshorts_callbackfunc2(GtkWidget *widget, int line, int pos, int kbkey); int ii, jj, err; GtkWidget *widget; char shortkey[20]; char shortfunc[60]; char kbfile[200]; FILE *fid = 0; if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) // add shortcut { zd->zstat = 0; // keep dialog active if (Nshortcuts2 == maxshortcuts) { zmessageACK(Mwin,"exceed %d shortcuts",maxshortcuts); return 1; } zdialog_fetch(zd,"shortkey",shortkey,20); // get shortcut key and menu zdialog_fetch(zd,"shortfunc",shortfunc,60); // from dialog widgets if (*shortkey <= ' ' || *shortfunc <= ' ') return 0; for (ii = 0; ii < Nshortcuts2; ii++) // find matching shortcut key in list if (strmatch(shortcutkey2[ii],shortkey)) break; if (ii < Nshortcuts2) { // if found, remove from list zfree(shortcutkey2[ii]); zfree(shortcutmenu2[ii]); for (jj = ii; jj < Nshortcuts2; jj++) { shortcutkey2[jj] = shortcutkey2[jj+1]; shortcutmenu2[jj] = shortcutmenu2[jj+1]; } --Nshortcuts2; } for (ii = 0; ii < Nshortcuts2; ii++) // find matching shortcut func in list if (strmatch(shortcutmenu2[ii],shortfunc)) break; if (ii < Nshortcuts2) { // if found, remove from list zfree(shortcutkey2[ii]); zfree(shortcutmenu2[ii]); for (jj = ii; jj < Nshortcuts2; jj++) { shortcutkey2[jj] = shortcutkey2[jj+1]; shortcutmenu2[jj] = shortcutmenu2[jj+1]; } --Nshortcuts2; } ii = Nshortcuts2++; // add new shortcut to end of list shortcutkey2[ii] = zstrdup(shortkey); shortcutmenu2[ii] = zstrdup(shortfunc); widget = zdialog_widget(zd,"shortlist"); textwidget_clear(widget); for (ii = 0; ii < Nshortcuts2; ii++) // show existing shortcuts in dialog textwidget_append(widget,0,"%-14s %s \n", shortcutkey2[ii],shortcutmenu2[ii]); return 1; } if (zd->zstat == 2) // remove shortcut { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"shortkey",shortkey,20); // get shortcut key if (*shortkey <= ' ') return 0; for (ii = 0; ii < Nshortcuts2; ii++) // find matching shortcut key in list if (strmatch(shortcutkey2[ii],shortkey)) break; if (ii < Nshortcuts2) { // if found, remove from list zfree(shortcutkey2[ii]); zfree(shortcutmenu2[ii]); for (jj = ii; jj < Nshortcuts2; jj++) { shortcutkey2[jj] = shortcutkey2[jj+1]; shortcutmenu2[jj] = shortcutmenu2[jj+1]; } --Nshortcuts2; } widget = zdialog_widget(zd,"shortlist"); // clear shortcuts list textwidget_clear(widget); for (ii = 0; ii < Nshortcuts2; ii++) // show shortcuts in dialog textwidget_append(widget,0,"%-14s %s \n", shortcutkey2[ii],shortcutmenu2[ii]); zdialog_stuff(zd,"shortkey",""); // clear entered key and menu zdialog_stuff(zd,"shortfunc",ZTX("(no selection)")); return 1; } if (zd->zstat == 3) // done - save new shortcut list { zdialog_free(zd); // kill menu funcs list for (ii = 0; ii < Nshortcuts2; ii++) { // convert selected shortcut menus for (jj = 0; jj < Nmenulist; jj++) { // to english menus if (strmatch(shortcutmenu2[ii],menulist_xx[jj])) { zfree(shortcutmenu2[ii]); shortcutmenu2[ii] = zstrdup(menulist_en[jj]); break; } } } err = locale_filespec("user","KB-shortcuts2",kbfile); // write shortcuts file if (! err) fid = fopen(kbfile,"w"); if (err || ! fid) { zmessageACK(Mwin,ZTX("unable to save KB-shortcuts file")); return 1; } for (ii = 0; ii < Nshortcuts2; ii++) fprintf(fid,"%-14s %s \n",shortcutkey2[ii],shortcutmenu2[ii]); fclose(fid); KBshortcuts_load(); // reload and activate return 1; } zdialog_free(zd); // cancel or [x] return 1; } // read KB-shortcuts file and load shortcuts table in memory // also called from initzfunc() at fotoxx startup int KBshortcuts_load() { int ii, err; char kbfile[200], buff[200]; char *pp1, *pp2; FILE *fid; for (ii = 0; ii < Nshortcuts; ii++) { // clear shortcuts data 18.01 zfree(shortcutkey[ii]); zfree(shortcutmenu[ii]); } Nshortcuts = 0; err = locale_filespec("user","KB-shortcuts2",kbfile); if (err) return 0; ii = 0; fid = fopen(kbfile,"r"); if (! fid) return 0; while (true) { pp1 = fgets_trim(buff,200,fid,1); // next record if (! pp1) break; if (*pp1 == '#') continue; // comment if (*pp1 <= ' ') continue; // blank pp2 = strchr(pp1,' '); if (! pp2) continue; if (pp2 - pp1 > 20) continue; *pp2 = 0; shortcutkey[ii] = zstrdup(pp1); // shortcut key pp1 = pp2 + 1; while (*pp1 && *pp1 == ' ') pp1++; if (! *pp1) continue; shortcutmenu[ii] = zstrdup(ZTX(pp1)); // menus can be English or translation if (++ii == maxshortcuts) break; } fclose(fid); Nshortcuts = ii; return 0; } /********************************************************************************/ // show a brightness distribution graph - live update as image is edited namespace brdist_names { GtkWidget *drawwin_dist, *drawwin_scale; // brightness distribution graph widgets int RGBW[4] = { 0, 0, 0, 1 }; // " colors: red/green/blue/white (all) } // menu function void m_show_brdist(GtkWidget *, cchar *menu) // menu function { using namespace brdist_names; int show_brdist_dialog_event(zdialog *zd, cchar *event); GtkWidget *frdist, *frscale, *widget; zdialog *zd; if (! Fpxb) return; if (menu && strmatch(menu,"kill")) { if (zdbrdist) zdialog_free(zdbrdist); // bugfix 18.01 zdbrdist = 0; return; } if (zdbrdist) { // dialog already present gtk_widget_queue_draw(drawwin_dist); // refresh drawing windows return; } if (menu) F1_help_topic = "brightness_graph"; zd = zdialog_new(ZTX("Brightness Distribution"),Mwin,null); zdialog_add_widget(zd,"frame","frdist","dialog",0,"expand"); // frames for 2 drawing areas zdialog_add_widget(zd,"frame","frscale","dialog"); frdist = zdialog_widget(zd,"frdist"); frscale = zdialog_widget(zd,"frscale"); drawwin_dist = gtk_drawing_area_new(); // histogram drawing area gtk_container_add(GTK_CONTAINER(frdist),drawwin_dist); G_SIGNAL(drawwin_dist,"draw",brdist_drawgraph,RGBW); drawwin_scale = gtk_drawing_area_new(); // brightness scale under histogram gtk_container_add(GTK_CONTAINER(frscale),drawwin_scale); gtk_widget_set_size_request(drawwin_scale,300,12); G_SIGNAL(drawwin_scale,"draw",brdist_drawscale,0); zdialog_add_widget(zd,"hbox","hbcolors","dialog"); zdialog_add_widget(zd,"check","all","hbcolors",Ball,"space=5"); zdialog_add_widget(zd,"check","red","hbcolors",Bred,"space=5"); zdialog_add_widget(zd,"check","green","hbcolors",Bgreen,"space=5"); zdialog_add_widget(zd,"check","blue","hbcolors",Bblue,"space=5"); zdialog_stuff(zd,"red",RGBW[0]); zdialog_stuff(zd,"green",RGBW[1]); zdialog_stuff(zd,"blue",RGBW[2]); zdialog_stuff(zd,"all",RGBW[3]); zdialog_resize(zd,300,250); zdialog_run(zd,show_brdist_dialog_event,"save"); widget = zdialog_widget(zd,"dialog"); // stop focus on this window gtk_window_set_accept_focus(GTK_WINDOW(widget),0); zdbrdist = zd; return; } // dialog event and completion function int show_brdist_dialog_event(zdialog *zd, cchar *event) { using namespace brdist_names; if (zd->zstat) { zdialog_free(zd); zdbrdist = 0; return 0; } if (strstr("all red green blue",event)) { // update chosen colors zdialog_fetch(zd,"red",RGBW[0]); zdialog_fetch(zd,"green",RGBW[1]); zdialog_fetch(zd,"blue",RGBW[2]); zdialog_fetch(zd,"all",RGBW[3]); gtk_widget_queue_draw(drawwin_dist); // refresh drawing window } return 0; } // draw brightness distribution graph in drawing window void brdist_drawgraph(GtkWidget *drawin, cairo_t *cr, int *rgbw) { int bin, Nbins = 85, brdist[85][4]; // bin count, R/G/B/all int px, py, dx, dy; int ww, hh, winww, winhh; int ii, rgb, maxdist, bright; float *pixf, pixg[3]; uint8 *pixi; if (! Fpxb) return; if (rgbw[0]+rgbw[1]+rgbw[2]+rgbw[3] == 0) return; paintlock(1); winww = gtk_widget_get_allocated_width(drawin); // drawing window size winhh = gtk_widget_get_allocated_height(drawin); for (bin = 0; bin < Nbins; bin++) // clear brightness distribution for (rgb = 0; rgb < 4; rgb++) brdist[bin][rgb] = 0; ww = Fpxb->ww; hh = Fpxb->hh; for (ii = 0; ii < ww * hh; ii++) // overhaul { if (sa_stat == 3 && ! sa_pixmap[ii]) continue; // stay within active select area py = ii / ww; // image pixel px = ii - ww * py; dx = Mscale * px - Morgx + Dorgx; // stay within visible window if (dx < 0 || dx > Dww-1) continue; // for zoomed image dy = Mscale * py - Morgy + Dorgy; if (dy < 0 || dy > Dhh-1) continue; pixi = PXBpix(Fpxb,px,py); // use displayed image pixg[0] = pixi[0]; // convert uint8 RGB to float pixg[1] = pixi[1]; pixg[2] = pixi[2]; pixf = pixg; for (rgb = 0; rgb < 3; rgb++) { // get R/G/B brightness levels bright = pixf[rgb] * Nbins / 256.0; // scale 0 to Nbins-1 if (bright < 0 || bright > 255) { printz("pixel %d/%d: %.2f %.2f %.2f \n",px,py,pixf[0],pixf[1],pixf[2]); paintlock(0); return; } ++brdist[bright][rgb]; } bright = (pixf[0] + pixf[1] + pixf[2]) * 0.333 * Nbins / 256.0; // R+G+B, 0 to Nbins-1 ++brdist[bright][3]; } paintlock(0); maxdist = 0; for (bin = 1; bin < Nbins-1; bin++) // find max. bin over all RGB for (rgb = 0; rgb < 3; rgb++) // omit bins 0 and last if (brdist[bin][rgb] > maxdist) // which can be huge maxdist = brdist[bin][rgb]; for (rgb = 0; rgb < 4; rgb++) // R/G/B/white (all) { if (! rgbw[rgb]) continue; // color not selected for graph if (rgb == 0) cairo_set_source_rgb(cr,1,0,0); if (rgb == 1) cairo_set_source_rgb(cr,0,1,0); if (rgb == 2) cairo_set_source_rgb(cr,0,0,1); if (rgb == 3) cairo_set_source_rgb(cr,0,0,0); // color "white" = R+G+B uses black line cairo_move_to(cr,0,winhh-1); // start at (0,0) for (px = 0; px < winww; px++) // x from 0 to window width { bin = Nbins * px / winww; // bin = 0-Nbins for x = 0-width py = 0.9 * winhh * brdist[bin][rgb] / maxdist; // height of bin in window py = winhh * sqrt(1.0 * py / winhh); py = winhh - py - 1; cairo_line_to(cr,px,py); // draw line from bin to bin } cairo_stroke(cr); } return; } /********************************************************************************/ // Paint a horizontal stripe drawing area with a color progressing from // black to white. This represents a brightness scale from 0 to 255. void brdist_drawscale(GtkWidget *drawarea, cairo_t *cr, int *) { int px, ww, hh; float fbright; ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); for (px = 0; px < ww; px++) // draw brightness scale { fbright = 1.0 * px / ww; cairo_set_source_rgb(cr,fbright,fbright,fbright); cairo_move_to(cr,px,0); cairo_line_to(cr,px,hh-1); cairo_stroke(cr); } return; } /********************************************************************************/ // setup x and y grid lines - count/spacing, enable/disable, offsets void m_gridlines(GtkWidget *widget, cchar *menu) { int gridlines_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int G; if (FGWM != 'F') m_viewmode(0,"F"); // insure file view mode 18.01 if (menu && strmatchN(menu,"grid ",5)) // grid N from some edit functions currgrid = menu[5] - '0'; else if (! widget) { // from KB shortcut toggle_grid(2); // toggle grid lines on/off return; } else currgrid = 0; // must be menu call if (currgrid == 0) F1_help_topic = "grid_lines"; /*** ____________________________________________ | Grid Lines | | | | x-spacing [____] y-spacing [____] | | x-count [____] y-count [____] | | x-enable [_] y-enable [_] | | | | x-offset =================[]============= | | y-offset ==============[]================ | | | | [Done] | |____________________________________________| ***/ zd = zdialog_new(ZTX("Grid Lines"),Mwin,Bdone,null); zdialog_add_widget(zd,"hbox","hb0","dialog",0,"space=10"); zdialog_add_widget(zd,"vbox","vb1","hb0",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vb2","hb0",0,"homog"); zdialog_add_widget(zd,"vbox","vbspace","hb0",0,"space=5"); zdialog_add_widget(zd,"vbox","vb3","hb0",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vb4","hb0",0,"homog"); zdialog_add_widget(zd,"label","lab1x","vb1",ZTX("x-spacing")); zdialog_add_widget(zd,"label","lab2x","vb1",ZTX("x-count")); zdialog_add_widget(zd,"label","lab4x","vb1",ZTX("x-enable")); zdialog_add_widget(zd,"zspin","spacex","vb2","10|200|1|50","space=2"); zdialog_add_widget(zd,"zspin","countx","vb2","0|100|1|2","space=2"); zdialog_add_widget(zd,"check","enablex","vb2",0); zdialog_add_widget(zd,"label","lab1y","vb3",ZTX("y-spacing")); zdialog_add_widget(zd,"label","lab2y","vb3",ZTX("y-count")); zdialog_add_widget(zd,"label","lab4y","vb3",ZTX("y-enable")); zdialog_add_widget(zd,"zspin","spacey","vb4","10|200|1|50"); zdialog_add_widget(zd,"zspin","county","vb4","0|100|1|2"); zdialog_add_widget(zd,"check","enabley","vb4",0); zdialog_add_widget(zd,"hbox","hboffx","dialog"); zdialog_add_widget(zd,"label","lab3x","hboffx",Bxoffset,"space=7"); zdialog_add_widget(zd,"hscale","offsetx","hboffx","0|100|1|0","expand"); zdialog_add_widget(zd,"label","space","hboffx",0,"space=20"); zdialog_add_widget(zd,"hbox","hboffy","dialog"); zdialog_add_widget(zd,"label","lab3y","hboffy",Byoffset,"space=7"); zdialog_add_widget(zd,"hscale","offsety","hboffy","0|100|1|0","expand"); zdialog_add_widget(zd,"label","space","hboffy",0,"space=20"); G = currgrid; // grid logic overhaul if (! gridsettings[G][GXS]) // if [G] never set, get defaults for (int ii = 0; ii < 9; ii++) gridsettings[G][ii] = gridsettings[0][ii]; zdialog_stuff(zd,"enablex",gridsettings[G][GX]); // current settings >> dialog widgets zdialog_stuff(zd,"enabley",gridsettings[G][GY]); zdialog_stuff(zd,"spacex",gridsettings[G][GXS]); zdialog_stuff(zd,"spacey",gridsettings[G][GYS]); zdialog_stuff(zd,"countx",gridsettings[G][GXC]); zdialog_stuff(zd,"county",gridsettings[G][GYC]); zdialog_stuff(zd,"offsetx",gridsettings[G][GXF]); zdialog_stuff(zd,"offsety",gridsettings[G][GYF]); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,gridlines_dialog_event,"parent"); zdialog_wait(zd); zdialog_free(zd); return; } // dialog event function int gridlines_dialog_event(zdialog *zd, cchar *event) { int G = currgrid; if (zd->zstat) // done or cancel { if (zd->zstat != 1) return 1; if (gridsettings[G][GX] || gridsettings[G][GY]) gridsettings[G][GON] = 1; else gridsettings[G][GON] = 0; Fpaint2(); return 1; } if (strmatch(event,"enablex")) // x/y grid enable or disable zdialog_fetch(zd,"enablex",gridsettings[G][GX]); if (strmatch(event,"enabley")) zdialog_fetch(zd,"enabley",gridsettings[G][GY]); if (strmatch(event,"spacex")) // x/y grid spacing (if counts == 0) zdialog_fetch(zd,"spacex",gridsettings[G][GXS]); if (strmatch(event,"spacey")) zdialog_fetch(zd,"spacey",gridsettings[G][GYS]); if (strmatch(event,"countx")) // x/y grid line counts zdialog_fetch(zd,"countx",gridsettings[G][GXC]); if (strmatch(event,"county")) zdialog_fetch(zd,"county",gridsettings[G][GYC]); if (strmatch(event,"offsetx")) // x/y grid starting offsets zdialog_fetch(zd,"offsetx",gridsettings[G][GXF]); if (strmatch(event,"offsety")) zdialog_fetch(zd,"offsety",gridsettings[G][GYF]); if (gridsettings[G][GX] || gridsettings[G][GY]) // if either grid enabled, show grid gridsettings[G][GON] = 1; Fpaint2(); return 1; } // toggle grid lines on or off // action: 0 = off, 1 = on, 2 = toggle: on > off, off > on void toggle_grid(int action) { int G = currgrid; if (action == 0) gridsettings[G][GON] = 0; // grid off if (action == 1) gridsettings[G][GON] = 1; // grid on if (action == 2) gridsettings[G][GON] = 1 - gridsettings[G][GON]; // toggle grid if (gridsettings[G][GON]) if (! gridsettings[G][GX] && ! gridsettings[G][GY]) // if grid on and x/y both off, gridsettings[G][GX] = gridsettings[G][GY] = 1; // set both grids on Fpaint2(); return; } /********************************************************************************/ // choose color for foreground lines // (area outline, mouse circle, trim rectangle ...) void m_line_color(GtkWidget *, cchar *menu) { int line_color_dialog_event(zdialog *zd, cchar *event); zdialog *zd; F1_help_topic = "line_color"; zd = zdialog_new(ZTX("Line Color"),Mwin,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"radio",Bblack,"hb1",Bblack,"space=3"); // add radio button per color zdialog_add_widget(zd,"radio",Bwhite,"hb1",Bwhite,"space=3"); zdialog_add_widget(zd,"radio",Bred,"hb1",Bred,"space=3"); zdialog_add_widget(zd,"radio",Bgreen,"hb1",Bgreen,"space=3"); zdialog_stuff(zd,Bblack,0); // all are initially off zdialog_stuff(zd,Bwhite,0); zdialog_stuff(zd,Bred,0); zdialog_stuff(zd,Bgreen,0); if (LINE_COLOR == BLACK) // set current color on zdialog_stuff(zd,Bblack,1); if (LINE_COLOR == WHITE) zdialog_stuff(zd,Bwhite,1); if (LINE_COLOR == RED) zdialog_stuff(zd,Bred,1); if (LINE_COLOR == GREEN) zdialog_stuff(zd,Bgreen,1); zdialog_run(zd,line_color_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion function int line_color_dialog_event(zdialog *zd, cchar *event) { if (strstr("Black White Red Green",event)) { if (strmatch(event,"Black")) LINE_COLOR = BLACK; // set selected color if (strmatch(event,"White")) LINE_COLOR = WHITE; if (strmatch(event,"Red")) LINE_COLOR = RED; if (strmatch(event,"Green")) LINE_COLOR = GREEN; if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"line_color"); Fpaint2(); } if (zd->zstat) zdialog_free(zd); // [x] button return 1; } /********************************************************************************/ // Show RGB values for 1-10 pixels selected with mouse-clicks. void show_RGB_mousefunc(); int show_RGB_timefunc(void *); zdialog *RGBSzd; int RGBSpixel[10][2]; // last 10 pixels clicked int RGBSnpix; // count of pixels int RGBSmetric = 1; // 1/2/3 = /RGB/EV/OD int RGBSdelta = 0; // abs/delta mode int RGBSlabels = 0; // pixel labels on/off int RGBSupdate = 0; // window update needed void m_show_RGB(GtkWidget *, cchar *menu) { int show_RGB_event(zdialog *zd, cchar *event); cchar *mess = ZTX("Click image to select pixels."); cchar *header = " Pixel Red Green Blue"; char hbx[8] = "hbx", pixx[8] = "pixx"; // last char. is '0' to '9' int ii; F1_help_topic = "show_RGB"; if (! curr_file) return; // no image file RGBSnpix = 0; // no pixels yet /*** _________________________________________ | | | Click image to select pixels. | | [x] delta [x] labels | | Metric: (o) RGB (o) EV | | Pixel Red Green Blue | | A xxxxx xxxxx xxxxx xxxxx xxxxx | | B xxxxx xxxxx xxxxx xxxxx xxxxx | | C xxxxx xxxxx xxxxx xxxxx xxxxx | | D xxxxx xxxxx xxxxx xxxxx xxxxx | | E xxxxx xxxxx xxxxx xxxxx xxxxx | | F xxxxx xxxxx xxxxx xxxxx xxxxx | | G xxxxx xxxxx xxxxx xxxxx xxxxx | | H xxxxx xxxxx xxxxx xxxxx xxxxx | | I xxxxx xxxxx xxxxx xxxxx xxxxx | | J xxxxx xxxxx xxxxx xxxxx xxxxx | | xxxxx xxxxx xxxxx xxxxx xxxxx | | | | [clear] [done] | |_________________________________________| ***/ if (RGBSzd) zdialog_free(RGBSzd); // delete previous if any zdialog *zd = zdialog_new(ZTX("Show RGB"),Mwin,Bclear,Bdone,null); RGBSzd = zd; zdialog_add_widget(zd,"hbox","hbmess","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labmess","hbmess",mess,"space=5"); zdialog_add_widget(zd,"hbox","hbmym","dialog"); zdialog_add_widget(zd,"check","delta","hbmym","delta","space=8"); zdialog_add_widget(zd,"check","labels","hbmym","labels","space=8"); if (RGBSdelta && E3pxm) zdialog_stuff(zd,"delta",1); zdialog_add_widget(zd,"hbox","hbmetr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labmetr","hbmetr","Metric:","space=5"); zdialog_add_widget(zd,"radio","radRGB","hbmetr","RGB","space=3"); zdialog_add_widget(zd,"radio","radEV","hbmetr","EV","space=3"); if (RGBSmetric == 1) zdialog_stuff(zd,"radRGB",1); // get which metric to use if (RGBSmetric == 2) zdialog_stuff(zd,"radEV",1); zdialog_add_widget(zd,"vbox","vbdat","dialog"); // vbox for current pixel values zdialog_add_widget(zd,"hbox","hbpix","vbdat"); zdialog_add_widget(zd,"label","labheader","hbpix",header); // Pixel Red Green Blue zdialog_labelfont(zd,"labheader","monospace 9",header); for (ii = 0; ii < 10; ii++) { // 10 labels for pixel data output hbx[2] = '0' + ii; pixx[3] = '0' + ii; zdialog_add_widget(zd,"hbox",hbx,"vbdat"); zdialog_add_widget(zd,"label",pixx,hbx); } zdialog_run(zd,show_RGB_event,"save"); // run dialog takeMouse(show_RGB_mousefunc,dragcursor); // connect mouse function g_timeout_add(200,show_RGB_timefunc,0); // start timer function, 200 ms return; } // dialog event function int show_RGB_event(zdialog *zd, cchar *event) { int button; if (zd->zstat) { if (zd->zstat == 1) { // clear zd->zstat = 0; // keep dialog active RGBSnpix = 0; // point count = 0 erase_toptext(102); // erase labels on image RGBSupdate++; // update display window } else { // done or kill freeMouse(); // disconnect mouse function zdialog_free(RGBSzd); // kill dialog RGBSzd = 0; erase_toptext(102); } Fpaint2(); } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(show_RGB_mousefunc,dragcursor); // connect mouse function if (strmatch(event,"delta")) { // set absolute/delta mode zdialog_fetch(zd,"delta",RGBSdelta); if (RGBSdelta && ! E3pxm) { RGBSdelta = 0; // block delta mode if no edit underway zdialog_stuff(zd,"delta",0); zmessageACK(Mwin,ZTX("Edit function must be active")); // 13.02.1 } } if (strmatch(event,"labels")) { // get labels on/off zdialog_fetch(zd,"labels",RGBSlabels); RGBSupdate++; // update window } if (strmatch(event,"radRGB")) { // metric = RGB zdialog_fetch(zd,event,button); if (button) RGBSmetric = 1; } if (strmatch(event,"radEV")) { // metric = EV zdialog_fetch(zd,event,button); if (button) RGBSmetric = 2; } return 0; } // mouse function // table positions [0] to [RGBSnpix-1] for labeled pixel positions, fixed // table position [RGBSnpix] = values from current mouse position void show_RGB_mousefunc() // mouse function { int ii; PXM *pxm; if (E3pxm) pxm = E3pxm; // report image being edited else if (E1pxm) pxm = E1pxm; else if (E0pxm) pxm = E0pxm; else { E0pxm = PXM_load(curr_file,1); // never edited, if (! E0pxm) return; // create E0 image for my use pxm = E0pxm; curr_file_bpc = f_load_bpc; } if (Mxposn < 0 || Mxposn > pxm->ww-1) return; // mouse outside image, ignore if (Myposn < 0 || Myposn > pxm->hh-1) return; if (LMclick) // left click, add labeled position { LMclick = 0; if (RGBSnpix == 9) { // if all 9 labeled positions filled, for (ii = 1; ii < 9; ii++) { // remove first (oldest) and RGBSpixel[ii-1][0] = RGBSpixel[ii][0]; // push the rest back RGBSpixel[ii-1][1] = RGBSpixel[ii][1]; } RGBSnpix = 8; // position for newest } ii = RGBSnpix; // next position to fill RGBSpixel[ii][0] = Mxclick; // save newest pixel RGBSpixel[ii][1] = Myclick; RGBSnpix++; } ii = RGBSnpix; // fill next position from active mouse RGBSpixel[ii][0] = Mxposn; RGBSpixel[ii][1] = Myposn; /****** if (sa_stat == 3) { // output area edge distance ii = Myposn * pxm->ww + Mxposn; printz("pixel: %d %d edge dist: %d \n",Mxposn,Myposn,sa_pixmap[ii]); } ******/ RGBSupdate++; // update window return; } // timer function - continuously display RGB values for selected pixels int show_RGB_timefunc(void *arg) // up to 10 pixels, live update { static char label[10][4] = { " A ", " B ", " C ", " D ", " E ", " F ", " G ", " H ", " I ", " J " }; PXM *pxm = 0; int ii, jj, px, py, delta; int ww, hh; float red1, green1, blue1; float red3, green3, blue3; float *ppixa, *ppixb; char text[100], pixx[8] = "pixx"; #define EVfunc(rgb) (log2(rgb) - 7) // RGB units to EV (128 == 0 EV) if (! RGBSzd) return 0; // user quit, cancel timer if (RGBSdelta && E3pxm) delta = 1; // delta mode only if edit underway else delta = 0; if (E3pxm) pxm = E3pxm; // report image being edited else if (E1pxm) pxm = E1pxm; else if (E0pxm) pxm = E0pxm; else return 1; ww = pxm->ww; hh = pxm->hh; if (RGBSnpix) // some labeled positions to report { for (ii = 0; ii < 10; ii++) { px = RGBSpixel[ii][0]; // next pixel to report py = RGBSpixel[ii][1]; if (px >= 0 && px < ww && py >= 0 && py < hh) continue; // within image limits for (jj = ii+1; jj < 10; jj++) { RGBSpixel[jj-1][0] = RGBSpixel[jj][0]; // remove pixel outside limits RGBSpixel[jj-1][1] = RGBSpixel[jj][1]; // and pack the remaining down } RGBSpixel[9][0] = RGBSpixel[9][1] = 0; // last position is empty RGBSnpix--; ii--; RGBSupdate++; // update window } } if (RGBSupdate) // refresh window labels { RGBSupdate = 0; erase_toptext(102); if (RGBSlabels) { for (ii = 0; ii < RGBSnpix; ii++) { // show pixel labels on image px = RGBSpixel[ii][0]; py = RGBSpixel[ii][1]; add_toptext(102,px,py,label[ii],"Sans 8"); } } Fpaint2(); } red1 = green1 = blue1 = 0; for (ii = 0; ii < 10; ii++) // loop positons 0 to 9 { pixx[3] = '0' + ii; // widget names "pix0" ... "pix9" px = RGBSpixel[ii][0]; // next pixel to report py = RGBSpixel[ii][1]; if (ii > RGBSnpix) { // no pixel there yet zdialog_stuff(RGBSzd,pixx,""); // blank report line continue; } ppixa = PXMpix(pxm,px,py); // get pixel RGB values red3 = ppixa[0]; green3 = ppixa[1]; blue3 = ppixa[2]; if (delta && E1pxm && px < E1pxm->ww && py < E1pxm->hh) { // delta RGB for ongoing edited image ppixb = PXMpix(E1pxm,px,py); // "before" image E1 red1 = ppixb[0]; green1 = ppixb[1]; blue1 = ppixb[2]; } if (ii == RGBSnpix) snprintf(text,100," %5d %5d ",px,py); // mouse pixel, format " xxxx yyyy" else snprintf(text,100," %c %5d %5d ",'A'+ii,px,py); // fixed pixel, format " A xxxx yyyy" if (RGBSmetric == 1) { // output RGB values if (delta) { red3 -= red1; // delta RGB green3 -= green1; blue3 -= blue1; } snprintf(text+14,86," %6.2f %6.2f %6.2f ",red3,green3,blue3); } if (RGBSmetric == 2) { // output EV values red3 = EVfunc(red3); green3 = EVfunc(green3); blue3 = EVfunc(blue3); if (delta) { red3 -= EVfunc(red1); // delta EV green3 -= EVfunc(green1); blue3 -= EVfunc(blue1); } snprintf(text+14,86," %6.3f %6.3f %6.3f ",red3,green3,blue3); } zdialog_labelfont(RGBSzd,pixx,"monospace 9",text); } return 1; } /********************************************************************************/ // magnify image within a given radius of dragged mouse namespace magnify_names { int magnify_dialog_event(zdialog* zd, cchar *event); void magnify_mousefunc(); void magnify_dopixels(int ftf); float Xsize; // magnification, 1 - 10x int Mxpos, Mypos; // mouse location, image space int Drad; // mouse radius, window space uint8 kernel[601][601]; // up to radius 300 } // menu function void m_magnify(GtkWidget *, cchar *) { using namespace magnify_names; F1_help_topic = "magnify"; cchar * mess = ZTX("Drag mouse on image. \n" "Left click to cancel. \n" "Key X to toggle dialog."); /*** __________________________ | Magnify Image | | | | Drag mouse on image. | | Left click to cancel. | | Key X to toggle dialog. | | | | radius [_____] | | X-size [_____] | | | | [cancel] | |__________________________| ***/ if (FGWM != 'F') m_viewmode(0,"F"); // insure file view mode 18.01 if (zdmagnify) { // toggle magnify mode zdialog_send_event(zdmagnify,"kill"); return; } else { zdialog *zd = zdialog_new(ZTX("Magnify Image"),Mwin,Bcancel,null); zdmagnify = zd; zdialog_add_widget(zd,"label","labdrag","dialog",mess,"space=5"); zdialog_add_widget(zd,"hbox","hbr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labr","hbr",Bradius,"space=5"); zdialog_add_widget(zd,"zspin","Drad","hbr","100|300|10|100"); zdialog_add_widget(zd,"hbox","hbx","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labx","hbx",ZTX("X-size"),"space=5"); zdialog_add_widget(zd,"zspin","Xsize","hbx","1.5|5|0.1|2"); zdialog_fetch(zd,"Drad",Drad); // initial mouse radius zdialog_fetch(zd,"Xsize",Xsize); // initial magnification zdialog_resize(zd,200,0); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,magnify_dialog_event,"save"); // run dialog, parallel zdialog_send_event(zd,"Drad"); // initializations zdialog_send_event(zd,"Xsize"); } takeMouse(magnify_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int magnify_names::magnify_dialog_event(zdialog *zd, cchar *event) { using namespace magnify_names; int dx, dy, rad, kern; if (strmatch(event,"kill")) zd->zstat = 1; // from slide show if (zd->zstat) { // terminate zdmagnify = 0; zdialog_free(zd); freeMouse(); return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(magnify_mousefunc,dragcursor); if (strmatch(event,"Drad")) { zdialog_fetch(zd,"Drad",Drad); // new mouse radius for (dy = -Drad; dy <= Drad; dy++) // build kernel for (dx = -Drad; dx <= Drad; dx++) { rad = sqrt(dx*dx + dy*dy); if (rad < Drad-3) kern = 1; // margin for circle else kern = 0; kernel[dx+Drad][dy+Drad] = kern; } return 1; } if (strmatch(event,"Xsize")) { zdialog_fetch(zd,"Xsize",Xsize); // new magnification return 1; } return 1; } // pixel paint mouse function void magnify_names::magnify_mousefunc() { using namespace magnify_names; static int ftf; if (! curr_file) return; if (FGWM != 'F') return; if (! zdmagnify) return; if (Mxdown) Fpaint2(); // drag start, erase prior if any Mxdown = 0; if (Mxdrag || Mydrag) // drag in progress { Mxpos = Mxdrag; // save mouse position Mypos = Mydrag; Mxdrag = Mydrag = 0; magnify_dopixels(ftf); // magnify pixels inside mouse gdk_window_set_cursor(gdkwin,blankcursor); ftf = 0; } else { ftf = 1; gdk_window_set_cursor(gdkwin,dragcursor); zdialog_send_event(zdmagnify,"focus"); } return; } // get pixels from mouse circle within full size image // scale and move into pixbuf and write to window void magnify_names::magnify_dopixels(int ftf) { using namespace magnify_names; static PIXBUF *pxb1 = 0, *pxb2 = 0; int Srad, drad, Dxpos, Dypos, rx, ry; static int org1x, org1y, org2x, org2y; static int fww, fhh, ww1, hh1, ww2, hh2; uint8 *pixels1, *pixels2, *pix1, *pix2; int rs1, rs2, nch1, nch2, px1, py1, px2, py2; float scale; cairo_t *cr; if (! ftf && pxb2) // continuation of mouse drag { org1x = org2x - Dorgx + Morgx; // replace prior display area if (org1x < 0) org1x = 0; // with original image pixels if (org1x + ww2 > Mpxb->ww) ww2 = Mpxb->ww - org1x; org1y = org2y - Dorgy + Morgy; if (org1y < 0) org1y = 0; if (org1y + hh2 > Mpxb->hh) hh2 = Mpxb->hh - org1y; g_object_unref(pxb2); pxb2 = gdk_pixbuf_new_subpixbuf(Mpxb->pixbuf,org1x,org1y,ww2,hh2); if (! pxb2) return; cr = draw_context_create(gdkwin,draw_context); // 17.04 if (! cr) return; gdk_cairo_set_source_pixbuf(cr,pxb2,org2x,org2y); cairo_paint(cr); draw_context_destroy(draw_context); // 17.04 } fww = Fpxb->ww; // curr. image size fhh = Fpxb->hh; scale = Mscale * Xsize; // image to window pixels scale Srad = Drad / scale; // mouse radius scaled to 1x image if (Srad > fww/3) Srad = fww/3; // restrict to 1/3 image size if (Srad > fhh/3) Srad = fhh/3; drad = Srad * scale; // restricted mouse radius drad = drad / 2 * 2 + 1; // make odd if (Mxpos < Srad) Mxpos = Srad; // restrict within image limits if (Mxpos > fww-1-Srad) Mxpos = fww-1-Srad; if (Mypos < Srad) Mypos = Srad; if (Mypos > fhh-1-Srad) Mypos = fhh-1-Srad; Dxpos = Mxpos * Mscale - Morgx + Dorgx; // corresp. mouse position in window Dypos = Mypos * Mscale - Morgy + Dorgy; org1x = Mxpos - Srad; // source pixels in 1x image org1y = Mypos - Srad; ww1 = hh1 = Srad * 2; // enclosing square pxb1 = gdk_pixbuf_new_subpixbuf(Fpxb->pixbuf,org1x,org1y,ww1,hh1); // pixbuf with source pixels if (! pxb1) return; ww2 = hh2 = drad * 2 + 1; // enclosing square for scaled pixels if (pxb2) g_object_unref(pxb2); pxb2 = gdk_pixbuf_scale_simple(pxb1,ww2,hh2,BILINEAR); g_object_unref(pxb1); if (! pxb2) return; org2x = Dxpos - (Mxpos - org1x) * scale; // pixbuf position in window org2y = Dypos - (Mypos - org1y) * scale; if (org2x < Dorgx) org2x = Dorgx; // keep within window if (org2x + ww2 > Dww - Dorgx) org2x = Dww - Dorgx - ww2; if (org2y < Dorgy) org2y = Dorgy; if (org2y + hh2 > Dhh - Dorgy) org2y = Dhh - Dorgy - hh2; pixels1 = gdk_pixbuf_get_pixels(Mpxb->pixbuf); // image pixels at window scale rs1 = gdk_pixbuf_get_rowstride(Mpxb->pixbuf); // corresponding to square area nch1 = gdk_pixbuf_get_n_channels(Mpxb->pixbuf); pixels2 = gdk_pixbuf_get_pixels(pxb2); // magnified pixels to display rs2 = gdk_pixbuf_get_rowstride(pxb2); nch2 = gdk_pixbuf_get_n_channels(Mpxb->pixbuf); for (ry = -drad; ry <= drad; ry++) // loop all pixels for (rx = -drad; rx <= drad; rx++) { if (kernel[rx+drad][ry+drad]) continue; // within mouse circle, no changes px1 = rx + drad + org2x - Dorgx + Morgx; // outside mouse circle py1 = ry + drad + org2y - Dorgy + Morgy; pix1 = pixels1 + py1 * rs1 + px1 * nch1; // normal image pixel px2 = rx + drad; py2 = ry + drad; pix2 = pixels2 + py2 * rs2 + px2 * nch2; // magnified pixel memcpy(pix2,pix1,3); // normal pixel replaces magnified pixel } paintlock(1); cr = draw_context_create(gdkwin,draw_context); // 17.04 if (! cr) return; gdk_cairo_set_source_pixbuf(cr,pxb2,org2x,org2y); cairo_paint(cr); Dxpos = org2x + drad + 2; // draw circle around magnified area Dypos = org2y + drad + 2; cairo_set_source_rgb(cr,0,0,0); cairo_set_line_width(cr,2); cairo_arc(cr,Dxpos,Dypos,drad-2,0,2*PI); cairo_stroke(cr); draw_context_destroy(draw_context); // 17.04 paintlock(0); return; } /********************************************************************************/ // dark_brite menu function // highlight darkest and brightest pixels namespace darkbrite { float darklim = 0; float brightlim = 255; } void m_extreme_pixels(GtkWidget *, const char *) { using namespace darkbrite; int darkbrite_dialog_event(zdialog* zd, const char *event); cchar *title = ZTX("Darkest and Brightest Pixels"); F1_help_topic = "darkbrite_pixels"; if (! curr_file) return; // no image file /** ______________________________________ | Darkest and Brightest Pixels | | | | Dark Limit ===[]============ NNN | | Bright Limit ============[]=== NNN | | | | [Done] | |______________________________________| **/ zdialog *zd = zdialog_new(title,Mwin,Bdone,null); // darkbrite dialog zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=3|homog|expand"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"label","labD","vb1",ZTX("Dark Limit")); zdialog_add_widget(zd,"label","labB","vb1",ZTX("Bright Limit")); zdialog_add_widget(zd,"hscale","limD","vb2","0|255|1|0","expand"); zdialog_add_widget(zd,"hscale","limB","vb2","0|255|1|255","expand"); zdialog_add_widget(zd,"label","valD","vb3"); zdialog_add_widget(zd,"label","valB","vb3"); zdialog_rescale(zd,"limD",0,0,255); zdialog_rescale(zd,"limB",0,255,255); zdialog_stuff(zd,"limD",darklim); // start with prior values zdialog_stuff(zd,"limB",brightlim); zdialog_resize(zd,300,0); zdialog_run(zd,darkbrite_dialog_event,"save"); // run dialog - parallel zddarkbrite = zd; // global pointer for Fpaint*() zdialog_send_event(zd,"limD"); // initz. NNN labels zdialog_send_event(zd,"limB"); return; } // darkbrite dialog event and completion function int darkbrite_dialog_event(zdialog *zd, const char *event) // darkbrite dialog event function { using namespace darkbrite; char text[8]; if (zd->zstat) { zdialog_free(zd); zddarkbrite = 0; Fpaint2(); return 0; } if (strmatch(event,"limD")) { zdialog_fetch(zd,"limD",darklim); snprintf(text,8,"%.0f",darklim); zdialog_stuff(zd,"valD",text); } if (strmatch(event,"limB")) { zdialog_fetch(zd,"limB",brightlim); snprintf(text,8,"%.0f",brightlim); zdialog_stuff(zd,"valB",text); } Fpaint2(); return 0; } // this function called by Fpaint() if zddarkbrite dialog active void darkbrite_paint() { using namespace darkbrite; int px, py; uint8 *pix; float P, D = darklim, B = brightlim; for (py = 0; py < Mpxb->hh; py++) // loop all image pixels for (px = 0; px < Mpxb->ww; px++) { pix = PXBpix(Mpxb,px,py); P = pixbright(pix); if (P < D) pix[0] = 100; // dark pixel = mostly red else if (P > B) pix[0] = 0; // bright pixel = mostly green/blue } return; // not executed, avoid gcc warning } /********************************************************************************/ // monitor color and contrast test function void m_monitor_color(GtkWidget *, cchar *) { char file[200]; int err; char *savecurrfile = 0; zdialog *zd; cchar *message = ZTX("Brightness should show a gradual ramp \n" "extending all the way to the edges."); F1_help_topic = "monitor_color"; if (checkpend("all")) return; // check nothing pending Fblock = 1; if (curr_file) savecurrfile = zstrdup(curr_file); snprintf(file,200,"%s/colorchart.png",get_zimagedir()); // color chart .png file err = f_open(file,0,0,1); if (err) goto restore; m_viewmode(0,"F"); // 18.01 Fzoom = 1; gtk_window_set_title(MWIN,"check monitor"); zd = zdialog_new("check monitor",Mwin,Bdone,null); // start user dialog if (message) { zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"label","lab1","hb1",message,"space=5"); } zdialog_resize(zd,300,0); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,0,"0/0"); zdialog_wait(zd); // wait for dialog complete zdialog_free(zd); restore: Fzoom = 0; if (savecurrfile) { f_open(savecurrfile); zfree(savecurrfile); } else { curr_file = 0; if (curr_dirk) { gallery(curr_dirk,"init",0); gallery(0,"sort",-2); // recall sort and position 18.01 } } Fblock = 0; return; } /********************************************************************************/ // check and adjust monitor gamma void m_monitor_gamma(GtkWidget *, cchar *) { int mongamma_dialog_event(zdialog *zd, cchar *event); int err; char gammachart[200]; zdialog *zd; char *savecurrfile = 0; cchar *permit = "Chart courtesy of Norman Koren"; cchar *website = "http://www.normankoren.com/makingfineprints1A.html#gammachart"; F1_help_topic = "monitor_gamma"; if (checkpend("all")) return; // check nothing pending Fblock = 1; err = shell_quiet("which xgamma"); // check for xgamma if (err) { zmessageACK(Mwin,"xgamma program is not installed"); return; } if (curr_file) savecurrfile = zstrdup(curr_file); snprintf(gammachart,200,"%s/gammachart2.png",get_zimagedir()); // gamma chart .png file err = f_open(gammachart); if (err) goto restore; m_viewmode(0,"F"); // 18.01 Fzoom = 1; // scale 100% (required) gtk_window_set_title(MWIN,"adjust monitor gamma"); zd = zdialog_new(ZTX("Monitor Gamma"),Mwin,Bdone,null); // start user dialog zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=8"); zdialog_add_widget(zd,"label","labgamma","hb1","gamma","space=5"); zdialog_add_widget(zd,"hscale","gamma","hb1","0.6|1.4|0.02|1.0","expand"); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"link",permit,"hb2",website); zdialog_resize(zd,200,0); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_set_modal(zd); // 17.08 zdialog_run(zd,mongamma_dialog_event,"0/0"); zdialog_wait(zd); // wait for dialog complete zdialog_free(zd); restore: Fzoom = 0; if (savecurrfile) { f_open(savecurrfile); // back to current file zfree(savecurrfile); } else { curr_file = 0; if (curr_dirk) { gallery(curr_dirk,"init",0); gallery(0,"sort",-2); // recall sort and position 18.01 } } Fblock = 0; return; } // dialog event function int mongamma_dialog_event(zdialog *zd, cchar *event) { double gamma; if (strmatch(event,"gamma")) { zdialog_fetch(zd,"gamma",gamma); shell_ack("xgamma -quiet -gamma %.2f",gamma); } return 0; } /********************************************************************************/ // set GUI language void m_change_lang(GtkWidget *, cchar *) { #define NL 8 zdialog *zd; int ii, cc, val, zstat; char progexe[300], lang1[NL], *pp; cchar *langs[NL] = { "en English", "de German", // english first "ca Catalan", "es Spanish", "fr French", "it Italian", "pt Portuguese", null }; cchar *title = ZTX("Available Translations"); F1_help_topic = "change_language"; zd = zdialog_new(ZTX("Set Language"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"label","title","dialog",title,"space=5"); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1"); for (ii = 0; langs[ii]; ii++) // make radio button per language zdialog_add_widget(zd,"radio",langs[ii],"vb1",langs[ii]); cc = strlen(zfuncs::zlang); // current language for (ii = 0; langs[ii]; ii++) // match on lc_RC if (strmatchN(zfuncs::zlang,langs[ii],cc)) break; if (! langs[ii]) for (ii = 0; langs[ii]; ii++) // failed, match on lc alone if (strmatchN(zfuncs::zlang,langs[ii],2)) break; if (! langs[ii]) ii = 0; // failed, default english zdialog_stuff(zd,langs[ii],1); zdialog_resize(zd,200,0); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,0,"mouse"); // run dialog zstat = zdialog_wait(zd); for (ii = 0; langs[ii]; ii++) { // get active radio button zdialog_fetch(zd,langs[ii],val); if (val) break; } zdialog_free(zd); // kill dialog if (zstat != 1) return; // user cancel if (! val) return; // no selection strncpy0(lang1,langs[ii],NL); pp = strchr(lang1,' '); // isolate lc_RC part *pp = 0; cc = readlink("/proc/self/exe",progexe,300); // get own program path if (cc <= 0) { zmessageACK(Mwin,"cannot get /proc/self/exe"); return; } progexe[cc] = 0; shell_ack("%s -l %s -p &",progexe,lang1); // start new fotoxx with language m_quit(0,0); // exit return; } /********************************************************************************/ // report missing translations in a popup window void m_untranslated(GtkWidget *, cchar *) { int ftf = 1, Nmiss = 0; cchar *missing; F1_help_topic = "missing_translations"; if (strmatchN(zfuncs::zlang,"en",2)) { zmessageACK(Mwin,"use a non-English locale"); return; } popup_report_open(Mwin,"missing translations",400,200); while (true) { missing = ZTX_missing(ftf); if (! missing) break; popup_report_write(0,"%s \n",missing); Nmiss++; } popup_report_write(0,"%d missing translations \n",Nmiss); popup_report_top(); return; } /********************************************************************************/ // convert color profile of current image editfunc EFcolorprof; char ICCprofilename[100]; // new color profile name 17.08 char colorprof1[200] = "/usr/share/color/icc/colord/AdobeRGB1998.icc"; char colorprof2[200] = "/usr/share/color/icc/colord/sRGB.icc"; // menu function void m_color_profile(GtkWidget *, cchar *menu) { int colorprof_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int err; cchar *exifkey[2], *exifdata[2]; F1_help_topic = "color_profile"; EFcolorprof.menuname = menu; EFcolorprof.menufunc = m_color_profile; EFcolorprof.funcname = "color_profile"; EFcolorprof.Frestart = 1; // allow restart EFcolorprof.Fscript = 1; // scripting supported 17.04 if (! edit_setup(EFcolorprof)) return; // setup edit *ICCprofilename = 0; // no color profile change 17.08 /*** ________________________________________________________ | Change Color Profile | | | | input profile [___________________________] [Browse] | | output profile [___________________________] [Browse] | | | | [Apply] [Done] [Cancel] | |________________________________________________________| ***/ zd = zdialog_new(ZTX("Change Color Profile"),Mwin,Bapply,Bdone,Bcancel,null); EFcolorprof.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",ZTX("input profile"),"space=5"); zdialog_add_widget(zd,"zentry","prof1","hb1",0,"expand|size=30"); zdialog_add_widget(zd,"button","butt1","hb1",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"label","lab2","hb2",ZTX("output profile"),"space=5"); zdialog_add_widget(zd,"zentry","prof2","hb2",0,"expand|size=30"); zdialog_add_widget(zd,"button","butt2","hb2",Bbrowse,"space=5"); zdialog_stuff(zd,"prof1",colorprof1); zdialog_stuff(zd,"prof2",colorprof2); zdialog_run(zd,colorprof_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); // wait for completion 17.08 if (! *ICCprofilename) return; // no color profile change m_file_save_version(0,0); // save as new version and re-open 17.08 shell_quiet("rm -f %s/undo_*",tempdir); // remove undo/redo files URS_pos = URS_max = 0; // reset undo/redo stack exifkey[0] = exif_colorprof2_key; // remove embedded color profile exifdata[0] = ""; exifkey[1] = exif_colorprof1_key; // set new color profile name exifdata[1] = ICCprofilename; err = exif_put(curr_file,exifkey,exifdata,2); if (err) zmessageACK(Mwin,ZTX("Unable to change EXIF color profile")); zmessageACK(Mwin,ZTX("automatic new version created")); return; } // dialog event and completion callback function int colorprof_dialog_event(zdialog *zd, cchar *event) { cchar *title = ZTX("color profile"); char *file; float *fpix1, *fpix2; float f256 = 1.0 / 256.0; uint Npix, nn; cmsHTRANSFORM cmsxform; cmsHPROFILE cmsprof1, cmsprof2; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"butt1")) { zdialog_fetch(zd,"prof1",colorprof1,200); // select input profile file = zgetfile(title,MWIN,"file",colorprof1); if (! file) return 1; zdialog_stuff(zd,"prof1",file); zfree(file); } if (strmatch(event,"butt2")) { zdialog_fetch(zd,"prof2",colorprof2,200); // select output profile file = zgetfile(title,MWIN,"file",colorprof2); if (! file) return 1; zdialog_stuff(zd,"prof2",file); zfree(file); } if (! zd->zstat) return 1; // wait for user completion if (zd->zstat == 1) zd->zstat = 0; // [apply] - keep dialog open if (zd->zstat) { if (zd->zstat == 2 && CEF->Fmods) edit_done(0); // commit edit else { edit_cancel(0); // discard edit *ICCprofilename = 0; // no ICC profile change 17.08 } return 1; } zdialog_fetch(zd,"prof1",colorprof1,200); // [apply] - get final profiles zdialog_fetch(zd,"prof2",colorprof2,200); cmsprof1 = cmsOpenProfileFromFile(colorprof1,"r"); if (! cmsprof1) { zmessageACK(Mwin,ZTX("unknown cms profile %s"),colorprof1); return 1; } cmsprof2 = cmsOpenProfileFromFile(colorprof2,"r"); if (! cmsprof2) { zmessageACK(Mwin,ZTX("unknown cms profile %s"),colorprof2); return 1; } // calculate the color space transformation table Ffuncbusy = 1; zmainsleep(0.2); // 17.01 cmsxform = cmsCreateTransform(cmsprof1,TYPE_RGB_FLT,cmsprof2,TYPE_RGB_FLT,INTENT_PERCEPTUAL,0); if (! cmsxform) { zmessageACK(Mwin,"cmsCreateTransform() failed"); Ffuncbusy = 0; return 1; } fpix1 = E0pxm->pixels; // input and output pixels fpix2 = E3pxm->pixels; Npix = E0pxm->ww * E0pxm->hh; for (uint ii = 0; ii < 3 * Npix; ii++) // rescale to range 0 - 0.9999 fpix2[ii] = f256 * fpix1[ii]; while (Npix) // convert image pixels { nn = Npix; if (nn > 100000) nn = 100000; // do 100K per call cmsDoTransform(cmsxform,fpix2,fpix2,nn); // speed: 3 megapixels/sec for 3 GHz CPU fpix2 += nn * 3; Npix -= nn; zmainloop(20); // keep GTK alive } fpix2 = E3pxm->pixels; Npix = E0pxm->ww * E0pxm->hh; for (uint ii = 0; ii < 3 * Npix; ii++) { // rescale back to 0 - 255.99 fpix2[ii] = fpix2[ii] * 256.0; if (fpix2[ii] > 255.9) fpix2[ii] = 255.9; if (fpix2[ii] < 0) fpix2[ii] = 0; // compensate cms bug 17.04.3 } cmsInfoType it = (cmsInfoType) 0; cmsGetProfileInfoASCII(cmsprof2,it,"en","US",ICCprofilename,100); // new color profile name 17.08 cmsDeleteTransform(cmsxform); // free resources cmsCloseProfile(cmsprof1); cmsCloseProfile(cmsprof2); Ffuncbusy = 0; CEF->Fmods++; // image is modified CEF->Fsaved = 0; Fpaint2(); // update window image return 1; } /********************************************************************************/ // printer color calibration tool namespace calibprint { int dialog_event(zdialog *zd, cchar *event); void printchart(); void scanchart(); void fixchart(); void processchart(); // parameters for RGB step size of 23: 0 23 46 69 ... 253 (253 --> 255) // NC colors per RGB dimension (12) (counting both 0 and 255) // step size from 16 to 23 // CS color step size (23) // TS tile size in pixels (70) // ROWS chart rows (50) ROWS x COLS must be >= NC*NC*NC // COLS chart columns (35) #define NC 12 #define CS 23 #define TS 70 #define ROWS 50 #define COLS 35 #define NC2 (NC*NC) #define NC3 (NC*NC*NC) int RGBvals[NC]; int Ntiles = NC3; int chartww = COLS * TS; // chart image size int charthh = ROWS * TS; int margin = 80; // chart margins char printchartfile[200]; char scanchartfile[200]; } // menu function void m_calibrate_printer(GtkWidget *, cchar *menu) { using namespace calibprint; zdialog *zd; cchar *title = ZTX("Calibrate Printer"); F1_help_topic = "calibrate_printer"; for (int ii = 0; ii < NC; ii++) // construct RGBvals table RGBvals[ii] = CS * ii; RGBvals[NC-1] = 255; // set last value = 255 /*** ______________________________________ | Calibrate Printer | | | | (o) print color chart | | (o) scan and save color chart | | (o) align and trim color chart | | (o) open and process color chart | | (o) print image with revised colors | | | | [Proceed] [Cancel] | |______________________________________| ***/ zd = zdialog_new(title,Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"radio","printchart","dialog",ZTX("print color chart")); zdialog_add_widget(zd,"radio","scanchart","dialog",ZTX("scan and save color chart")); zdialog_add_widget(zd,"radio","fixchart","dialog",ZTX("align and trim color chart")); zdialog_add_widget(zd,"radio","processchart","dialog",ZTX("open and process color chart")); zdialog_add_widget(zd,"radio","printimage","dialog",ZTX("print image with revised colors")); zdialog_stuff(zd,"printchart",1); zdialog_stuff(zd,"scanchart",0); zdialog_stuff(zd,"fixchart",0); zdialog_stuff(zd,"processchart",0); zdialog_stuff(zd,"printimage",0); zdialog_resize(zd,250,0); zdialog_run(zd,dialog_event,"parent"); return; } // dialog event and completion function int calibprint::dialog_event(zdialog *zd, cchar *event) { using namespace calibprint; int nn; F1_help_topic = "calibrate_printer"; if (! zd->zstat) return 1; // wait for [proceed] or [cancel] if (zd->zstat != 1) { // cancel zdialog_free(zd); return 1; } zdialog_fetch(zd,"printchart",nn); if (nn) { printchart(); return 1; } zdialog_fetch(zd,"scanchart",nn); if (nn) { scanchart(); return 1; } zdialog_fetch(zd,"fixchart",nn); if (nn) { fixchart(); return 1; } zdialog_fetch(zd,"processchart",nn); if (nn) { processchart(); return 1; } zdialog_fetch(zd,"printimage",nn); if (nn) { print_calibrated(); return 1; } zd->zstat = 0; // keep dialog active return 1; } // generate and print the color chart void calibprint::printchart() { using namespace calibprint; int ii, cc, fww, fhh, rx, gx, bx; int row, col, px, py; int chartrs; uint8 *chartpixels, *pix1; PIXBUF *chartpxb; GError *gerror = 0; fww = chartww + 2 * margin; fhh = charthh + 2 * margin; chartpxb = gdk_pixbuf_new(GDKRGB,0,8,fww,fhh); // make chart image if (! chartpxb) { zmessageACK(Mwin,"cannot create pixbuf"); return; } chartpixels = gdk_pixbuf_get_pixels(chartpxb); // clear to white chartrs = gdk_pixbuf_get_rowstride(chartpxb); cc = fhh * chartrs; memset(chartpixels,255,cc); for (py = 0; py < charthh; py++) // fill chart tiles with colors for (px = 0; px < chartww; px++) { row = py / TS; col = px / TS; ii = row * COLS + col; if (ii >= Ntiles) break; // last chart positions may be unused rx = ii / NC2; gx = (ii - NC2 * rx) / NC; // RGB index values for tile ii bx = ii - NC2 * rx - NC * gx; pix1 = chartpixels + (py + margin) * chartrs + (px + margin) * 3; pix1[0] = RGBvals[rx]; pix1[1] = RGBvals[gx]; pix1[2] = RGBvals[bx]; } for (py = margin-10; py < fhh-margin+10; py++) // add green margin around tiles for (px = margin-10; px < fww-margin+10; px++) // for easier de-skew and trim { if (py > margin-1 && py < fhh-margin && px > margin-1 && px < fww-margin) continue; pix1 = chartpixels + py * chartrs + px * 3; pix1[0] = pix1[2] = 0; pix1[1] = 255; } snprintf(printchartfile,200,"%s/printchart.png",printer_color_dirk); gdk_pixbuf_save(chartpxb,printchartfile,"png",&gerror,null); if (gerror) { zmessageACK(Mwin,gerror->message); return; } g_object_unref(chartpxb); zmessageACK(Mwin,"Print chart in vertical orientation without margins."); print_image_file(Mwin,printchartfile); // print the chart return; } // scan the color chart void calibprint::scanchart() { using namespace calibprint; zmessageACK(Mwin,ZTX("Scan the printed color chart. \n" "The darkest row is at the top. \n" "Save in %s/"),printer_color_dirk); return; } // edit and fix the color chart void calibprint::fixchart() { using namespace calibprint; char *pp; zmessageACK(Mwin,ZTX("Open and edit the scanned color chart file. \n" "Remove any skew or rotation from scanning. \n" "(Use the Fix Perspective function for this). \n" "Cut off the thin green margin ACCURATELY.")); pp = zgetfile("scanned color chart file",MWIN,"file",printer_color_dirk,1); if (! pp) return; strncpy0(scanchartfile,pp,200); f_open(scanchartfile,0,0,1,0); return; } // process the scanned and fixed color chart void calibprint::processchart() { using namespace calibprint; PIXBUF *chartpxb; GError *gerror = 0; uint8 *chartpixels, *pix1; FILE *fid; char mapfile[200], *pp, *pp2; int chartrs, chartnc, px, py; int ii, nn, row, col, rx, gx, bx; int xlo, xhi, ylo, yhi; float fww, fhh; int Rsum, Gsum, Bsum, Rout, Gout, Bout; int r1, r2, ry, g1, g2, gy, b1, b2, by; int ERR1[NC][NC][NC][3], ERR2[NC][NC][NC][3]; zmessageACK(Mwin,ZTX("Open the trimmed color chart file")); pp = zgetfile("trimmed color chart file",MWIN,"file",printer_color_dirk,1); if (! pp) return; strncpy0(scanchartfile,pp,200); chartpxb = gdk_pixbuf_new_from_file(scanchartfile,&gerror); // scanned chart without margins if (! chartpxb) { if (gerror) zmessageACK(Mwin,gerror->message); return; } chartww = gdk_pixbuf_get_width(chartpxb); charthh = gdk_pixbuf_get_height(chartpxb); chartpixels = gdk_pixbuf_get_pixels(chartpxb); chartrs = gdk_pixbuf_get_rowstride(chartpxb); chartnc = gdk_pixbuf_get_n_channels(chartpxb); fww = 1.0 * chartww / COLS; fhh = 1.0 * charthh / ROWS; for (row = 0; row < ROWS; row++) // loop each tile for (col = 0; col < COLS; col++) { ii = row * COLS + col; if (ii >= Ntiles) break; ylo = row * fhh; // tile position within chart image yhi = ylo + fhh; xlo = col * fww; xhi = xlo + fww; Rsum = Gsum = Bsum = nn = 0; for (py = ylo+fhh/5; py < yhi-fhh/5; py++) // get tile pixels less 20% margins for (px = xlo+fww/5; px < xhi-fww/5; px++) { pix1 = chartpixels + py * chartrs + px * chartnc; Rsum += pix1[0]; Gsum += pix1[1]; Bsum += pix1[2]; nn++; } Rout = Rsum / nn; // average tile RGB values Gout = Gsum / nn; Bout = Bsum / nn; rx = ii / NC2; gx = (ii - NC2 * rx) / NC; bx = ii - NC2 * rx - NC * gx; ERR1[rx][gx][bx][0] = Rout - RGBvals[rx]; // error = (scammed RGB) - (printed RGB) ERR1[rx][gx][bx][1] = Gout - RGBvals[gx]; ERR1[rx][gx][bx][2] = Bout - RGBvals[bx]; } g_object_unref(chartpxb); // anneal the error values to reduce randomness for (int pass = 1; pass <= 4; pass++) // 4 passes { for (rx = 0; rx < NC; rx++) // use neighbors in 3 channels for (gx = 0; gx < NC; gx++) for (bx = 0; bx < NC; bx++) { r1 = rx-1; r2 = rx+1; g1 = gx-1; g2 = gx+1; b1 = bx-1; b2 = bx+1; if (r1 < 0) r1 = 0; if (r2 > NC-1) r2 = NC-1; if (g1 < 0) g1 = 0; if (g2 > NC-1) g2 = NC-1; if (b1 < 0) b1 = 0; if (b2 > NC-1) b2 = NC-1; Rsum = Gsum = Bsum = nn = 0; for (ry = r1; ry <= r2; ry++) for (gy = g1; gy <= g2; gy++) for (by = b1; by <= b2; by++) { Rsum += ERR1[ry][gy][by][0]; Gsum += ERR1[ry][gy][by][1]; Bsum += ERR1[ry][gy][by][2]; nn++; } ERR2[rx][gx][bx][0] = Rsum / nn; ERR2[rx][gx][bx][1] = Gsum / nn; ERR2[rx][gx][bx][2] = Bsum / nn; } for (rx = 0; rx < NC; rx++) for (gx = 0; gx < NC; gx++) for (bx = 0; bx < NC; bx++) { ERR1[rx][gx][bx][0] = ERR2[rx][gx][bx][0]; ERR1[rx][gx][bx][1] = ERR2[rx][gx][bx][1]; ERR1[rx][gx][bx][2] = ERR2[rx][gx][bx][2]; } for (rx = 1; rx < NC-1; rx++) // use neighbors in same channel for (gx = 0; gx < NC; gx++) for (bx = 0; bx < NC; bx++) ERR2[rx][gx][bx][0] = 0.5 * (ERR1[rx-1][gx][bx][0] + ERR1[rx+1][gx][bx][0]); for (rx = 0; rx < NC; rx++) for (gx = 1; gx < NC-1; gx++) for (bx = 0; bx < NC; bx++) ERR2[rx][gx][bx][1] = 0.5 * (ERR1[rx][gx-1][bx][1] + ERR1[rx][gx+1][bx][1]); for (rx = 0; rx < NC; rx++) for (gx = 0; gx < NC; gx++) for (bx = 1; bx < NC-1; bx++) ERR2[rx][gx][bx][2] = 0.5 * (ERR1[rx][gx][bx-1][2] + ERR1[rx][gx][bx+1][2]); for (rx = 0; rx < NC; rx++) for (gx = 0; gx < NC; gx++) for (bx = 0; bx < NC; bx++) { ERR1[rx][gx][bx][0] = ERR2[rx][gx][bx][0]; ERR1[rx][gx][bx][1] = ERR2[rx][gx][bx][1]; ERR1[rx][gx][bx][2] = ERR2[rx][gx][bx][2]; } } // pass loop // save finished color map to user-selected file zmessageACK(Mwin,ZTX("Set the name for the output calibration file \n" "[your calibration name].dat")); snprintf(mapfile,200,"%s/%s",printer_color_dirk,colormapfile); pp = zgetfile("Color Map File",MWIN,"save",mapfile,1); if (! pp) return; pp2 = strrchr(pp,'/'); zfree(colormapfile); colormapfile = zstrdup(pp2+1); save_params(); zfree(pp); snprintf(mapfile,200,"%s/%s",printer_color_dirk,colormapfile); fid = fopen(mapfile,"w"); if (! fid) return; for (rx = 0; rx < NC; rx++) for (gx = 0; gx < NC; gx++) for (bx = 0; bx < NC; bx++) { fprintf(fid,"RGB: %3d %3d %3d ERR: %4d %4d %4d \n", RGBvals[rx], RGBvals[gx], RGBvals[bx], ERR2[rx][gx][bx][0], ERR2[rx][gx][bx][1], ERR2[rx][gx][bx][2]); } fclose(fid); return; } // Print the current image file with adjusted colors // Also called from the file menu function m_print_calibrated() void print_calibrated() { using namespace calibprint; zdialog *zd; int zstat; cchar *title = ZTX("Color map file to use"); char mapfile[200]; FILE *fid; PIXBUF *pixbuf; GError *gerror = 0; uint8 *pixels, *pix1; char *pp, *pp2, printfile[100]; int ww, hh, rs, nc, px, py, nn, err; int R1, G1, B1, R2, G2, B2; int RGB[NC][NC][NC][3], ERR[NC][NC][NC][3]; int rr1, rr2, gg1, gg2, bb1, bb2; int rx, gx, bx; int ii, Dr, Dg, Db; float W[8], w, Wsum, D, Dmax, F; float Er, Eg, Eb; F1_help_topic = "print_calibrated"; if (! curr_file) { zmessageACK(Mwin,ZTX("Select the image file to print.")); return; } for (int ii = 0; ii < NC; ii++) // construct RGBvals table RGBvals[ii] = CS * ii; RGBvals[NC-1] = 255; // set last value = 255 zd = zdialog_new(title,Mwin,Bbrowse,Bproceed,Bcancel,null); // show current color map file zdialog_add_widget(zd,"hbox","hbmap","dialog"); // and allow user to choose another zdialog_add_widget(zd,"label","labmap","hbmap",0,"space=3"); zdialog_stuff(zd,"labmap",colormapfile); zdialog_resize(zd,250,0); zdialog_run(zd,0,"parent"); zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat == 1) { // [browse] snprintf(mapfile,200,"%s/%s",printer_color_dirk,colormapfile); pp = zgetfile("Color Map File",MWIN,"file",mapfile,1); if (! pp) return; pp2 = strrchr(pp,'/'); if (colormapfile) zfree(colormapfile); colormapfile = zstrdup(pp2+1); zfree(pp); } else if (zstat != 2) return; // not proceed: cancel snprintf(mapfile,200,"%s/%s",printer_color_dirk,colormapfile); fid = fopen(mapfile,"r"); // read color map file if (! fid) return; for (R1 = 0; R1 < NC; R1++) for (G1 = 0; G1 < NC; G1++) for (B1 = 0; B1 < NC; B1++) { nn = fscanf(fid,"RGB: %d %d %d ERR: %d %d %d ", &RGB[R1][G1][B1][0], &RGB[R1][G1][B1][1], &RGB[R1][G1][B1][2], &ERR[R1][G1][B1][0], &ERR[R1][G1][B1][1], &ERR[R1][G1][B1][2]); if (nn != 6) { zmessageACK(Mwin,ZTX("file format error")); fclose(fid); return; } } fclose(fid); pixbuf = gdk_pixbuf_copy(Fpxb->pixbuf); // get image pixbuf to convert if (! pixbuf) { if (gerror) zmessageACK(Mwin,gerror->message); return; } if (checkpend("all")) return; Fblock = 1; Ffuncbusy = 1; ww = gdk_pixbuf_get_width(pixbuf); hh = gdk_pixbuf_get_height(pixbuf); pixels = gdk_pixbuf_get_pixels(pixbuf); rs = gdk_pixbuf_get_rowstride(pixbuf); nc = gdk_pixbuf_get_n_channels(pixbuf); poptext_window(ZTX("converting colors..."),MWIN,300,200,0,-1); for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { pix1 = pixels + py * rs + px * nc; R1 = pix1[0]; // image RGB values G1 = pix1[1]; B1 = pix1[2]; rr1 = R1/CS; // get color map values surrounding RGB rr2 = rr1 + 1; if (rr2 > NC-1) { // if > last entry, use last entry rr1--; rr2--; } gg1 = G1/CS; gg2 = gg1 + 1; if (gg2 > NC-1) { gg1--; gg2--; } bb1 = B1/CS; bb2 = bb1 + 1; if (bb2 > NC-1) { bb1--; bb2--; } ii = 0; Wsum = 0; Dmax = CS; for (rx = rr1; rx <= rr2; rx++) // loop 8 enclosing error map nodes for (gx = gg1; gx <= gg2; gx++) for (bx = bb1; bx <= bb2; bx++) { Dr = R1 - RGBvals[rx]; // RGB distance from enclosing node Dg = G1 - RGBvals[gx]; Db = B1 - RGBvals[bx]; D = sqrtf(Dr*Dr + Dg*Dg + Db*Db); if (D > Dmax) W[ii] = 0; else W[ii] = (Dmax - D) / Dmax; // weight of node Wsum += W[ii]; // sum of weights ii++; } ii = 0; Er = Eg = Eb = 0; for (rx = rr1; rx <= rr2; rx++) // loop 8 enclosing error map nodes for (gx = gg1; gx <= gg2; gx++) for (bx = bb1; bx <= bb2; bx++) { w = W[ii] / Wsum; Er += w * ERR[rx][gx][bx][0]; // weighted sum of map node errors Eg += w * ERR[rx][gx][bx][1]; Eb += w * ERR[rx][gx][bx][2]; ii++; } F = 1.0; // use 100% of calculated error R2 = R1 - F * Er; // adjusted RGB = image RGB - error G2 = G1 - F * Eg; B2 = B1 - F * Eb; if (R2 < 0) R2 = 0; if (G2 < 0) G2 = 0; if (B2 < 0) B2 = 0; if (R2 > 255) R2 = 255; // preserving RGB ratio does not help if (G2 > 255) G2 = 255; if (B2 > 255) B2 = 255; pix1[0] = R2; pix1[1] = G2; pix1[2] = B2; zmainloop(100); // keep GTK alive } poptext_killnow(); Ffuncbusy = 0; Fblock = 0; snprintf(printfile,100,"%s/printfile.png",tempdir); // save revised pixbuf to print file gdk_pixbuf_save(pixbuf,printfile,"png",&gerror,"compression","1",null); if (gerror) { zmessageACK(Mwin,gerror->message); return; } g_object_unref(pixbuf); err = f_open(printfile,0,0,1,0); // open print file if (err) return; zmessageACK(Mwin,ZTX("Image colors are converted for printing.")); print_image_file(Mwin,printfile); return; } /******************************************************************************** Create a desktop menu from fotoxx desktop file and icon file. Not a menu function. For AppImage package only. Source files in AppImage file system: /usr/share/fotoxx/fotoxx.desktop /usr/share/fotoxx/fotoxx.png Destination files in /home// /home//.local/bin/fotoxx-NN.N-appimage // appimage executable file /home//.local/share/applications/fotoxx.desktop // XDG desktop file /home//.local/share/icons/fotoxx.png // XDG icon file returns: 1 already done 2 new desktop file created 0 failure *****/ int make_appimage_desktop() // 18.01 { FILE *fid; int err, cc, TF; char *pp, *homedir = getenv("HOME"); // /home/ char desktop_file[100], icon_file[100], buff[300]; char desktop_file_org[100], desktop_file_loc[100]; char icon_file_org[100], icon_file_loc[100]; cchar *exectext1, *icontext1; // desktop file original text char exectext2[100], icontext2[100]; // desktop file new text if (! appimage) return 0; // not an AppImage package pp = strrchr(appimage,'/'); if (! pp) return 0; pp++; // pp -> "fotoxx-NN.N-appimage" if (! strmatchN(pp,"fotoxx-",7)) return 0; cc = readlink("/proc/self/exe",buff,300); // get own program path if (cc <= 0) { zmessageACK(Mwin,"cannot get /proc/self/exe"); return 0; } buff[cc] = 0; // /tmp/mountpoint/usr/bin/fotoxx pp = strstr(buff,"/usr/"); if (! pp) { printz("cannot find appimage /usr/ %s \n",buff); return 0; } pp[4] = 0; // /appimage-filesys .../usr snprintf(desktop_file_org,100,"%s/share/fotoxx/fotoxx.desktop",buff); // /.../usr/share/fotoxx/fotoxx.desktop snprintf(desktop_file_loc,100,"%s/.local/share/applications/",homedir); snprintf(icon_file_org,100,"%s/share/fotoxx/icons/fotoxx.png",buff); snprintf(icon_file_loc,100,"%s/.local/share/icons/",homedir); strcpy(desktop_file,desktop_file_loc); strcat(desktop_file,"fotoxx.desktop"); strcpy(icon_file,icon_file_loc); strcat(icon_file,"fotoxx.png"); fid = fopen(desktop_file,"r"); // open desktop file if (fid) { while (true) { // read desktop file pp = fgets(buff,300,fid); if (! pp) break; if (strmatchN(buff,"Exec=",5)) break; // look for "Exec=[my appimage]" } fclose(fid); if (pp) { cc = strlen(appimage); // source appimage file TF = strmatchN(appimage,buff+5,cc); // compare to Exec= appimage file if (TF) return 1; // same, do nothing } } shell_quiet("mkdir -p %s",desktop_file_loc); // copy fotoxx.desktop to err = copyFile(desktop_file_org,desktop_file_loc); // ~/.local/share/applications/ 17.08 if (err) return 0; shell_quiet("mkdir -p %s",icon_file_loc); // copy fotoxx.png to err = copyFile(icon_file_org,icon_file_loc); // ~/.local/share/icons/ 17.08 if (err) return 0; exectext1 = "Exec=fotoxx"; // desktop file original text icontext1 = "Icon=/usr/share/fotoxx/icons/fotoxx.png"; snprintf(exectext2,100,"Exec=%s",appimage); // desktop file new text snprintf(icontext2,100,"Icon=%s",icon_file); err = zsed(desktop_file,exectext1,exectext2,icontext1,icontext2,null); // make text substitutions if (err) return 0; chmod(desktop_file_loc,0751); // make fotoxx.desktop executable printz("desktop file created at %s \n\n", desktop_file); return 2; } /********************************************************************************/ // Uninstall AppImage package void m_uninstall_appimage(GtkWidget *, cchar *) // 17.04 { int yn; char *homedir; char desktop_file_loc[100]; char icon_file_loc[100]; F1_help_topic = "uninstall_appimage"; if (! appimage) { zmessageACK(Mwin,ZTX("This function is for AppImage package only")); return; } yn = zmessageYN(Mwin,ZTX("Completely remove the AppImage package \n" "Press F1 for more information. \n" "Continue?")); if (! yn) return; homedir = getenv("HOME"); snprintf(desktop_file_loc,100,"%s/.local/share/applications/",homedir); snprintf(icon_file_loc,100,"%s/.local/share/icons/",homedir); shell_quiet("rm -f %s/fotoxx.desktop",desktop_file_loc); shell_quiet("rm -f %s/fotoxx.png",icon_file_loc); shell_quiet("rm -f %s",appimage); quitxx(); return; } /********************************************************************************/ // Dump CPU usage, zdialog statistics, memory statistics // counters are reset void m_resources(GtkWidget *, cchar *) { F1_help_topic = "resources"; zmalloc_report(); printz(" CPU time: %.3f seconds \n",CPUtime()); return; } /********************************************************************************/ // zappcrash test - make a segment fault char *zapptext = null; void m_zappcrash(GtkWidget *, cchar *) { int ii, jj; for (ii = 0; ii < 100; ii++) { // fake-out compiler diagnostics jj = 1000.0 * drandz(); zapptext[jj] = 'a' + ii / 10; // write to address 0 + jj } printz("zapptext: %s \n",zapptext); return; } fotoxx-18.01.1/zfuncs.h0000644000175000017500000015216413222767271013354 0ustar micomico/******************************************************************************** zfuncs.h include file for zfuncs functions Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. *********************************************************************************/ // zfuncs.h version v.6.8 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VERTICAL GTK_ORIENTATION_VERTICAL // GTK shortcuts #define HORIZONTAL GTK_ORIENTATION_HORIZONTAL #define PIXBUF GdkPixbuf #define GDKCOLOR GdkColorspace #define int8 char // number types #define int16 short #define int32 int #define int64 long long // long long is always 64 bits #define uint8 unsigned char #define uint16 unsigned short #define uint32 unsigned int #define uint64 unsigned long long #define uchar unsigned char #define cchar const char #define VOL volatile #define wstrerror(err) strerror(WEXITSTATUS(err)) // get text status for child process #define STATB struct stat // stat() file status buffer #define mutex_t pthread_mutex_t // abbreviations #define mutex_init pthread_mutex_init #define mutex_lock pthread_mutex_lock #define mutex_trylock pthread_mutex_trylock #define mutex_unlock pthread_mutex_unlock #define mutex_destroy pthread_mutex_destroy #define XFCC 1000 // max. file pathname cc tolerated #define null NULL #define true 1 #define false 0 // trace execution: source file, function, line no, caller address #define TRACE trace(__FILE__,__FUNCTION__,__LINE__,__builtin_return_address(0)); // system functions ============================================================ void *zmalloc(uint cc); // malloc() with counter void zfree(void *pp); // free() with counter char *zstrdup(cchar *string, int addcc = 0); // strdup() with counter void zmalloc_report(); // print statistics report void printz(cchar *format, ...); // printf() with immediate fflush() void zpopup_message(int secs, cchar *format, ...); // popup message, thread safe void zbacktrace(); // produce a backtrace to stdout void zappcrash(cchar *format, ...); // crash with popup message in text window void catch_signals(); // catch signals and do backtrace dump void trace(cchar *file, cchar *func, int line, void *addr); // implements TRACE macro void tracedump(); // dump program trace data void beroot(int argc = 0, char *argv[] = 0); // restart image as root if password OK double get_seconds(); // seconds since 2000.01.01 void start_timer(double &time0); // start a timer double get_timer(double &time0); // get elapsed time in seconds void start_CPUtimer(double &time0); // start a process CPU timer double get_CPUtimer(double &time0); // get elapsed CPU time, seconds double CPUtime(); // get elapsed process CPU time for main() double CPUtime2(); // " include all threads double jobtime(); // " include all threads + subprocesses void compact_time(const time_t DT, char *compactDT); // time_t DT to yyyymmddhhmmss void pretty_datetime(const time_t DT, char *prettyDT); // time_t DT to yyyy-mm-dd hh:mm:ss int parseprocfile(cchar *pfile, cchar *pname, double *value, ...); // get data from /proc file int parseprocrec(char *prec, int field, double *value, ...); // get data from /proc file record int coretemp(); // get CPU core temperature, deg. C int disktemp(char *disk); // get disk temp, e.g. "/dev/sda" void zsleep(double dsecs); // sleep specified seconds int global_lock(cchar *lockfile); // obtain exclusive lock, multi-process int global_unlock(int fd, cchar *lockfile); // release the lock int resource_lock(int &resource); // simple lock/unlock usable with GTK void resource_unlock(int &resource); // (never waits, returns lock status) int zget_locked(int ¶m); // lock and get multi-thread parameter void zput_locked(int ¶m, int value); // put and unlock multi-thread parameter int zadd_locked(int ¶m, int incr); // increment multi-thread parameter void start_detached_thread(void * tfunc(void *), void * arg); // start detached thread function void synch_threads(int NT = 0); // synchronize NT threads int zsystem(cchar *command); // system() + return child process status int shell_quiet(cchar *command, ...); // format/run shell command, return status int shell_ack(cchar *command, ...); // "" + popup an error message if error int shell_asynch(cchar *command, ...); // start shell command, return immediately int shell_asynch_status(int handle); // get status of asynch shell command char * command_output(int &contx, cchar *command, ...); // get shell command output int command_status(int contx); // get exit status of command int command_kill(int contx); // kill command before completion int signalProc(cchar * pname, cchar * signal); // pause/resume/kill subprocess int runroot(cchar *sucomm, cchar *command); // run command as root via su or sudo char * fgets_trim(char * buff, int maxcc, FILE *, int bf = 0); // fgets + trim trailing \n \r (blanks) int samedirk(cchar *file1, cchar *file2); // returns 1 if files in same directory int parsefile(cchar *path, char **dir, char **file, char **ext); // parse a filespec int renamez(cchar *file1, cchar *file2); // rename, also across file systems int check_create_dir(char *path); // check if directory exists, ask to create int copyFile(cchar *sfile, cchar *dfile); // copy file to file or file to directory char * combine_argvs(int argc, char *argv[], int Nth); // combine argv[ii] elements from Nth to last // measure CPU time spent in a function or code block within a function extern VOL double cpu_profile_timer; // internal data tables extern VOL double cpu_profile_table[100]; extern VOL double cpu_profile_elapsed; void cpu_profile_init(); // initialize at start of test void cpu_profile_report(); // report CPU time per function inline void cpu_profile_enter(int fnum) // at entry to measured code block { cpu_profile_timer = cpu_profile_elapsed; } inline void cpu_profile_exit(int fnum) // at exit from measured code block { cpu_profile_table[fnum] += cpu_profile_elapsed - cpu_profile_timer; } int pagefaultrate(); // monitor own process hard fault rate // string macros and functions ================================================= #define strmatch(str1,str2) (! strcmp((str1),(str2))) // TRUE if strings equal #define strmatchN(str1,str2,cc) (! strncmp((str1),(str2),(cc))) // TRUE if strings[cc] equal #define strmatchcase(str1,str2) (! strcasecmp((str1),(str2))) // TRUE if strings equal, ignoring case #define strmatchcaseN(str1,str2,cc) (! strncasecmp((str1),(str2),(cc))) // TRUE if strings[cc] equal, ignoring case cchar * strField(cchar *string, cchar *delims, int Nth); // get Nth delimited field in string cchar * strField(cchar *string, cchar delim, int Nth); // get Nth delimited field in string int strParms(int &bf, cchar *inp, char *pname, int maxcc, double &pval); // parse string: name1=val1 | name2 ... int strHash(cchar *string, int max); // string --> random int 0 to max-1 int strncpy0(char *dest, cchar *source, uint cc); // strncpy, insure null, return 0 if fit void strnPad(char *dest, cchar *source, int cc); // strncpy with blank padding to cc int strTrim(char *dest, cchar *source); // remove trailing blanks int strTrim(char *string); // remove trailing blanks int strTrim2(char *dest, cchar *source); // remove leading and trailing blanks int strTrim2(char *string); // remove leading and trailing blanks int strCompress(char *dest, cchar *source); // remove all blanks incl. imbedded int strCompress(char *string); // remove all blanks int strncatv(char *dest, int maxcc, cchar *source, ...); // catenate strings (last = null) int strmatchV(cchar *string, ...); // compare to N strings, return 1-N or 0 void strToUpper(char *dest, cchar *source); // move and conv. string to upper case void strToUpper(char *string); // conv. string to upper case void strToLower(char *dest, cchar *source); // move and conv. string to lower case void strToLower(char *string); // conv. string to lower case int repl_1str(cchar *strin, char *strout, cchar *ssin, cchar *ssout); // copy string and replace 1 substring int repl_Nstrs(cchar *strin, char *strout, ...); // copy string and replace N substrings int breakup_text(cchar *in, char **&out, cchar *dlm, int cc1, int cc2); // break long string into substrings void strncpyx(char *out, cchar *in, int ccin); // conv. string to hex format void StripZeros(char *pNum); // 1.230000E+12 --> 1.23E+12 int blank_null(cchar *string); // test for blank/null string int clean_escapes(char *string); // replace \x escapes with real characters int utf8len(cchar *utf8string); // get graphic cc for UTF8 string int utf8substring(char *utf8out, cchar *utf8in, int pos, int cc); // get graphic substring from UTF8 string int utf8_check(cchar *string); // check utf8 string for encoding errors int utf8_position(cchar *utf8in, int Nth); // get byte position of Nth graphic char. int zsed(cchar *file, ...); // replace string1/3... with string2/4... // number conversion =========================================================== int convSI (cchar *s, int &i, cchar **delm = 0); // string to int int convSI (cchar *s, int &i, int low, int hi, cchar **delm = 0); // (with low/high limit checking) int convSD (cchar *s, double &d, cchar **delm = 0); // string to double int convSD (cchar *s, double &d, double low, double hi, cchar **delm = 0); // (with low/high limit checking) int convSF (cchar *s, float &f, cchar **delm = 0); // string to double int convSF (cchar *s, float &f, float low, float hi, cchar **delm = 0); // (with low/high limit checking) int convIS (int iin, char *outp, int *cc = 0); // int to string, returned cc int convDS (double din, int prec, char *outp, int *cc = 0); // double to string, precision, ret. cc char * formatKBMB(double fnum, int prec); // format nnn B, nn.n KB, n.nn MB, etc. // wildcard functions ========================================================== int MatchWild(cchar * wildstr, cchar * str); // wildcard string match (match = 0) int MatchWildIgnoreCase(cchar * wildstr, cchar * str); // wildcard string match, ignoring case cchar * SearchWild(cchar *wpath, int &flag); // wildcard file search cchar * SearchWildCase(cchar *wpath, int &flag); // wildcard file search, ignoring case // search and sort functions =================================================== int bsearch(int seekint, int nn, int list[]); // binary search sorted list[nn] int bsearch(cchar *seekrec, cchar *allrecs, int recl, int nrecs); // binary search sorted records int bsearch(cchar *seekrec, cchar **allrecs, int N, int nrecs); // binary search of sorted pointers to recs typedef int HeapSortUcomp(cchar *rec1, cchar *rec2); // return -1/0/+1 if rec1 rec2 void HeapSort(int vv[], int nn); // Heap Sort - integer void HeapSort(float vv[], int nn); // Heap Sort - float void HeapSort(double vv[], int nn); // Heap Sort - double void HeapSort(char *vv[], int nn); // Heap Sort - char, ascending order void HeapSort(char *vv1[], char *vv2[], int nn); // Heap Sort - parallel char *, ascending void HeapSort(char *vv[], int nn, HeapSortUcomp); // Heap Sort - char, user-defined order void HeapSort(char *recs, int RL, int NR, HeapSortUcomp); // Heap Sort - records, user-defined order int MemSort(char * RECS, int RL, int NR, int KEYS[][3], int NK); // memory sort, records with multiple keys int zmember(int testval, int matchval1, ...); // test if value matches any in a list // variable string list functions ============================================== struct pvlist { int max; // max. entries int act; // actual entries char **list; // entries }; pvlist * pvlist_create(int max); // create pvlist void pvlist_free(pvlist *pv); // free pvlist int pvlist_append(pvlist *pv, cchar *entry, int unique = 0); // append new entry (opt. if unique) int pvlist_prepend(pvlist *pv, cchar *entry, int unique = 0); // prepend new entry (opt. if unique) int pvlist_find(pvlist *pv, cchar *entry); // find entry by name int pvlist_remove(pvlist *pv, cchar *entry); // remove entry by name int pvlist_remove(pvlist *pv, int Nth); // remove entry by number (0...) int pvlist_count(pvlist *pv); // return entry count int pvlist_replace(pvlist *pv, int Nth, cchar *entry); // replace Nth entry (0...) char * pvlist_get(pvlist *pv, int Nth); // return Nth entry (0...) int pvlist_sort(pvlist *pv); // sort list, ascending // random number functions ===================================================== int lrandz(int64 * seed); // returns 0 to 0x7fffffff int lrandz(); // built-in seed double drandz(int64 * seed); // returns 0.0 to 0.99999... double drandz(); // built-in seed // spline curve-fitting functions ============================================== void spline1(int nn, float *dx, float *dy); // define a curve using nn data points float spline2(float x); // return y-value for given x-value // FIFO queue for text strings, single or dual-thread access =================== typedef struct { int qcap; // queue capacity int qnewest; // newest entry position, circular int qoldest; // oldest entry position, circular int qdone; // flag, last entry added to queue char **qtext; // up to qcap text strings } Qtext; void Qtext_open(Qtext *qtext, int cap); // initialize Qtext queue, empty void Qtext_put(Qtext *qtext, cchar *format, ...); // add text string to Qtext queue char * Qtext_get(Qtext *qtext); // remove text string from Qtext queue void Qtext_close(Qtext *qtext); // close Qtext, zfree() leftover strings // application initialization and administration =============================== int zinitapp(cchar *appname, cchar *homedir = 0); // initz. app directories and files cchar * get_zprefix(); // get /usr or /usr/local ... cchar * get_zhomedir(); // get /home/user/.appname/ cchar * get_zdatadir(); // get data directory cchar * get_zimagedir(); // get image directory cchar * get_zdocdir(); // get document directory cchar * get_zicondir(); // get icon directory void zsetfont(cchar *newfont); // set new app font and size int widget_font_metrics(GtkWidget *widget, int &fww, int &fhh); // get widget font char width/height int locale_filespec(cchar *ftype, cchar *fname, char *filespec); // get a locale dependent file void showz_userguide(cchar *context = 0); // show user guide in new process void showz_logfile(); // show log file in popup window void showz_textfile(cchar *type, cchar *file); // show text file [.gz] in popup window void showz_html(cchar *url); // show html via preferred browser // translation functions ======================================================= #define ZTXmaxent 2000 // max. translation strings #define ZTXmaxcc 4000 // max. cc per string void ZTXinit(cchar *lang); // setup for message translation cchar * ZTX(cchar *english); // get translation for English message cchar * ZTX_missing(int &ftf); // get missing translations, one per call /******************************************************************************** GTK utility functions *********************************************************************************/ void zmainloop(int skip = 0); // do main loop, process menu events void zmainsleep(float secs); // do main loop and sleep designated time // cairo drawing region for GDK window GTK 3.21 version #if GTK_CHECK_VERSION(3,22,0) typedef struct { GdkWindow *win; cairo_rectangle_int_t rect; cairo_region_t *reg; GdkDrawingContext *ctx; cairo_t *dcr = 0; } draw_context_t; #else typedef struct { cairo_t *dcr; } draw_context_t; #endif cairo_t * draw_context_create(GdkWindow *gdkwin, draw_context_t &context); void draw_context_destroy(draw_context_t &context); /********************************************************************************/ // textwidget functions - scrollable text widget for text reports and line editing // widget = zdialog_widget(zd,textwidget) where textwidget is a zdialog "text" widget type void textwidget_clear(GtkWidget *widget); // clear all text void textwidget_clear(GtkWidget *widget, int line); // clear text from line to end int textwidget_linecount(GtkWidget *widget); // get current line count void textwidget_append(GtkWidget *widget, int bold, cchar *format, ...); // append line void textwidget_insert(GtkWidget *widget, int bold, int line, cchar *format, ...); // insert line void textwidget_replace(GtkWidget *widget, int bold, int line, cchar *format, ...); // replace line void textwidget_delete(GtkWidget *widget, int line); // delete line int textwidget_find(GtkWidget *widget, char *matchtext, int line1); // find matching line void textwidget_scroll(GtkWidget *widget, int line); // scroll to make line visible void textwidget_dump(GtkWidget *widget, cchar *filename); // dump all text into a file void textwidget_save(GtkWidget *widget, GtkWindow *parent); // same, with save-as dialog char * textwidget_line(GtkWidget *widget, int line, int strip); // retrieve line (strip \n) void textwidget_highlight_line(GtkWidget *widget, int line); // highlight line char * textwidget_word(GtkWidget *, int line, int posn, cchar *dlims, char &end); // retrieve word void textwidget_highlight_word(GtkWidget *widget, int line, int posn, int cc); // highlight word typedef void textwidget_callbackfunc_t(GtkWidget *, int line, int posn, int KBkey); // callback function to receive void textwidget_set_callbackfunc(GtkWidget *, textwidget_callbackfunc_t func); // clicked line/posn or KB press /********************************************************************************/ // functions to simplify building menus, tool bars, status bars #define G_SIGNAL(window,event,func,arg) \ g_signal_connect(G_OBJECT(window),event,G_CALLBACK(func),(void *) arg) #define zdcbmax 100 // max. combo box drop-down list typedef void cbFunc(GtkWidget *, cchar *mname); // menu or button response function GtkWidget * create_menubar(GtkWidget *vbox); // create menubar in packing box GtkWidget * add_menubar_item(GtkWidget *mbar, cchar *mname, cbFunc func = 0); // add menu item to menubar GtkWidget * add_submenu_item(GtkWidget *mitem, cchar *subname, // add submenu item to menu item cbFunc func = 0, cchar *mtip = 0); // with opt. function and popup tip GtkWidget * create_toolbar(GtkWidget *vbox, int iconsize = 24); // toolbar in packing box (no vert gtk3) GtkWidget * add_toolbar_button(GtkWidget *tbar, cchar *lab, cchar *tip, // add button with label, tool-tip, icon cchar *icon, cbFunc func); GtkWidget * create_stbar(GtkWidget *vbox); // create status bar in packing box int stbar_message(GtkWidget *stbar, cchar *message); // display message in status bar /********************************************************************************/ GtkWidget * create_popmenu(); // create an empty popup menu GtkWidget * add_popmenu_item(GtkWidget *popmenu, cchar *mname, // add menu item to popup menu cbFunc func, cchar *arg, cchar *mtip = 0); void popup_menu(GtkWidget *, GtkWidget *popmenu); // pop-up menu at current mouse posn. /********************************************************************************/ // user editable graphic menu in popup window // menus can be added and arranged using the mouse typedef void Gmenuz_cbfunc(cchar *menu); // caller-supplied callback function void Gmenuz(GtkWidget *parent, cchar *title, cchar *ufile, Gmenuz_cbfunc); // show window, handle mouse drag/click /********************************************************************************/ // create vertical menu/toolbar in vertical packing box struct vmenuent { // menu data from caller cchar *name; // menu name, text cchar *icon; // opt. icon file name cchar *desc; // description (mouse hover popup) cbFunc *func; // callback func (GtkWidget *, cchar *arg) cchar *arg; // callback arg PIXBUF *pixbuf; // icon pixbuf or null PangoLayout *playout1, *playout2; // normal and bold menu text int namex, namey; // menu name position in layout int iconx, icony; // menu icon position int ylo, yhi; // menu height limits int iconww, iconhh; // icon width and height }; struct Vmenu { GtkWidget *vbox; // parent window (container) GtkWidget *topwin; // top-level window of parent GtkWidget *layout; // drawing window float fgRGB[3]; // font color, RGB scaled 0-1 float bgRGB[3]; // background color, RGB scaled 0-1 int xmax, ymax; // layout size int mcount; // menu entry count vmenuent menu[100]; }; Vmenu *Vmenu_new(GtkWidget *vbox, float fgRGB[3], float bgRGB[3]); // create new menu in parent vbox void Vmenu_add(Vmenu *vbm, cchar *name, cchar *icon, // add menu item with response function int iconww, int iconhh, cchar *desc, cbFunc func, cchar *arg); // function may be popup_menu() void Vmenu_block(int flag); // block or unblock menu /********************************************************************************/ // functions to implement GTK dialogs with less complexity // widget types: dialog, hbox, vbox, hsep, vsep, frame, scrwin, label, link, // entry, edit, text, radio, check, button, togbutt, spin, // combo, comboE, hscale, vscale, imagebutt, colorbutt, icon, image #define zdmaxwidgets 300 #define zdmaxbutts 10 #define zdsentinel 0x97530000 #define zdialog_max 20 struct zwidget { cchar *type; // dialog, hbox, vbox, label, entry ... cchar *name; // widget name cchar *pname; // parent (container) name char *data; // widget data, initial / returned pvlist *cblist; // combo box drop-down list int size; // text entry cc or image pixel size int homog; // hbox/vbox: equal spacing flag int expand; // widget is expandable flag int space; // extra padding space (pixels) int wrap; // wrap mode for edit widget int rescale; // widget is rescaled for more resolution float lval, nval, hval; // scale range and neutral value float lolim, hilim, step; // range and step value for "float" widget GtkWidget *widget; // GTK widget pointer }; struct zdialog { int sentinel1; // validity sentinel1 int uniqueID; // unique ID, monotone increasing char *title; // dialog title void *eventCB; // dialog event user callback function int zrunning; // dialog is running (0,1) int zstat; // dialog status (from completion button) int disabled; // widget signals/events are disabled int saveposn; // save and recall window position each use int saveinputs; // save and recall user inputs each use GtkWidget *dialog; // dialog window GtkWidget *parent; // parent window or null cchar *compbutton[zdmaxbutts]; // dialog completion button labels GtkWidget *compwidget[zdmaxbutts]; // dialog completion button widgets zwidget widget[zdmaxwidgets]; // dialog widgets (EOF = type = 0) char event[40]; // active event or widget GtkWidget *lastwidget; // last widget active int sentinel2; // validity sentinel2 }; zdialog *zdialog_new(cchar *title, GtkWidget *parent, ...); // create a zdialog with opt. buttons void zdialog_set_title(zdialog *zd, cchar *title); // change zdialog title void zdialog_set_modal(zdialog *zd); // set zdialog modal void zdialog_set_decorated(zdialog *zd, int decorated); // set zdialog decorated or not void zdialog_present(zdialog *zd); // zdialog visible and on top int zdialog_add_widget(zdialog *zd, // add widget to zdialog cchar *type, cchar *name, cchar *pname, // required args cchar *data = 0, int size = 0, int homog = 0, // optional args int expand = 0, int space = 0, int wrap = 0); int zdialog_add_widget(zdialog *zd, // add widget to zdialog cchar *type, cchar *name, cchar *pname, // (alternative form) cchar *data, cchar *options); // "size=nn|homog|expand|space=nn|wrap" int zdialog_valid(zdialog *zd, int errmess = 1); // return 1/0 if zdialog valid/invalid GtkWidget * zdialog_widget(zdialog *zd, cchar *name); // GTK widget from zdialog widget name int zdialog_set_image(zdialog *zd, cchar *name, GdkPixbuf *); // set "image" widget from a GDK pixbuf int zdialog_add_ttip(zdialog *zd, cchar *wname, cchar *ttip); // add popup tool tip to a widget int zdialog_resize(zdialog *zd, int width, int height); // set size > widget sizes int zdialog_put_data(zdialog *zd, cchar *name, cchar *data); // put data in widget (entry, spin ...) cchar * zdialog_get_data(zdialog *zd, cchar *name); // get widget data int zdialog_set_limits(zdialog *, cchar *name, double min, double max); // set new widget limits (spin, scale) int zdialog_rescale(zdialog *zd, cchar *wname, float, float, float); // rescale widget, lo/neut/hi vals typedef int zdialog_event(zdialog *zd, cchar *name); // widget event callback function int zdialog_run(zdialog *zd, zdialog_event = 0, cchar *posn = 0); // run dialog, handle events void KBevent(GdkEventKey *event); // extern: pass KB events to main app int zdialog_send_event(zdialog *zd, cchar *event); // send an event to an active dialog int zdialog_send_response(zdialog *zd, int zstat); // complete a dialog, set status int zdialog_show(zdialog *zd, int flag); // show or hide a dialog int zdialog_destroy(zdialog *zd); // destroy dialog (caller resp.) int zdialog_free(zdialog *&zd); // free zdialog memory int zdialog_wait(zdialog *zd); // wait for dialog completion int zdialog_goto(zdialog *zd, cchar *name); // put cursor at named widget void zdialog_set_cursor(zdialog *zd, GdkCursor *cursor); // set cursor for dialog window int zdialog_stuff(zdialog *zd, cchar *name, cchar *data); // stuff string data into widget int zdialog_stuff(zdialog *zd, cchar *name, int data); // stuff int data int zdialog_stuff(zdialog *zd, cchar *name, double data); // stuff double data int zdialog_stuff(zdialog *zd, cchar *name, double data, cchar *format); // stuff double data, formatted int zdialog_labelfont(zdialog *zd, cchar *lab, cchar *font, cchar *txt); // stuff label text with font int zdialog_fetch(zdialog *zd, cchar *name, char *data, int maxcc); // get string data from widget int zdialog_fetch(zdialog *zd, cchar *name, int &data); // get int data int zdialog_fetch(zdialog *zd, cchar *name, double &data); // get double data int zdialog_fetch(zdialog *zd, cchar *name, float &data); // get float data int zdialog_cb_app(zdialog *zd, cchar *name, cchar *data); // append entry to combo drop down list int zdialog_cb_prep(zdialog *zd, cchar *name, cchar *data); // prepend entry to combo drop down list char * zdialog_cb_get(zdialog *zd, cchar *name, int Nth); // get combo drop down list Nth entry int zdialog_cb_delete(zdialog *zd, cchar *name, cchar *data); // delete combo drop down list entry int zdialog_cb_clear(zdialog *zd, cchar *name); // clear all combo box entries int zdialog_cb_popup(zdialog *zd, cchar *name); // show all combo box list entries int zdialog_cb_save(zdialog *zd, cchar *name, cchar *file); // save combo box list to a file int zdialog_cb_load(zdialog *zd, cchar *name, cchar *file); // load combo box list from a file int zdialog_positions(cchar *action); // load or save zdialog window positions void zdialog_set_position(zdialog *zd, cchar *posn); // set initial/new zdialog window position void zdialog_save_position(zdialog *zd); // save zdialog window position int zdialog_inputs(cchar *action); // load or save zdialog input fields int zdialog_save_inputs(zdialog *zd); // save zdialog input fields when finished int zdialog_restore_inputs(zdialog *zd); // restore zdialog inputs from prior use void window_to_mouse(GtkWidget *window); // move GtkWidget/window to mouse position // write text report in popup window GtkWidget * popup_report_open(GtkWidget *paren, cchar *titl, int ww, int hh); // open popup text report window void popup_report_header(int bold, cchar *format, ...); // write non-scrolling header line void popup_report_write(int bold, cchar *format, ...); // write text line void popup_report_top(); // go to top of window void popup_report_move(int ww, int hh); // move window ww/hh pixels void popup_report_close(int secs); // close window in given seconds // shell command to popup window int popup_command(cchar *command, int ww = 400, int hh = 300, GtkWidget *parent = 0, int top = 0); // popup message dialogs void zmessageACK(GtkWidget *parent, cchar *format, ... ); // display message, wait for OK int zmessageYN(GtkWidget *parent, cchar *format, ... ); // display message, wait for YES/NO zdialog * zmessage_post(GtkWidget *parent, int secs, cchar *format, ...); // show message, timeout or cancel zdialog * zmessage_post_bold(GtkWidget *parent, int secs, cchar *format, ...); // " " with big red bold font char * zdialog_text(GtkWidget *parent, cchar *title, cchar *inittext); // get short text input from user int zdialog_choose(GtkWidget *parent, cchar *message, ...); // show message, return choice void poptext_mouse(GtkWidget *parent, cchar *text, int dx, int dy, float s1, float s2); // show popup text at mouse void poptext_window(cchar *text, GtkWindow *, int mx, int my, float s1, float s2); // show popup text at window posn int poptext_killnow(); // kill current popup window int popup_image(cchar *imagefile, GtkWindow *parent, int Fnewin, int size); // popup window with image // file chooser dialogs for one file or multiple files char * zgetfile(cchar *title, GtkWindow *parent, cchar *action, cchar *file, int hidden = 0); char ** zgetfiles(cchar *title, GtkWindow *parent, cchar *action, cchar *file, int hidden = 0); // print an image file, choosing printer, paper, orientation, margins, and scale void print_image_file(GtkWidget *parent, cchar *imagefile); // drag and drop functions typedef char * drag_drop_source_func(); // user function, set drag-drop text typedef void drag_drop_dest_func(int x, int y, char *text); // user function, get drag-drop text void drag_drop_source(GtkWidget *window, drag_drop_source_func); // connect source window to user function void drag_drop_dest(GtkWidget *window, drag_drop_dest_func); // connect dest. window to user function // miscellaneous GDK/GTK functions PIXBUF * get_thumbnail(cchar *fpath, int size); // get sized thumbnail for image file GdkCursor * zmakecursor(cchar *iconfile); // make a cursor from an image file PIXBUF * gdk_pixbuf_rotate(PIXBUF *, float deg, int alfa = 0); // rotate pixbuf through any angle PIXBUF * gdk_pixbuf_stripalpha(PIXBUF *pixbuf); // strip alpha channel from pixbuf PIXBUF * text_pixbuf(cchar *text, cchar *font, int fsize, GtkWidget *); // create pixbuf with text using font int move_pointer(GtkWidget *, int px, int py); // move the mouse pointer to px, py /******************************************************************************** C++ classes *********************************************************************************/ // dynamic string class ======================================================== class xstring { static int tcount; // total xstring count static int tmem; // total memory used int wmi; // internal ID int xcc; // actual cc (excl. NULL) int xmem; // memory allocated cc char * xpp; // memory pointer public: xstring(int cc = 0); // default constructor xstring(cchar * ); // string constructor xstring(const xstring &); // copy constructor ~xstring(); // destructor operator cchar * () const { return xpp; } // conversion operator (cchar *) xstring operator= (const xstring &); // operator = xstring operator= (cchar *); // operator = friend xstring operator+ (const xstring &, const xstring &); // operator + friend xstring operator+ (const xstring &, cchar *); // operator + friend xstring operator+ (cchar *, const xstring &); // operator + void insert(int pos, cchar * string, int cc = 0); // insert substring at position (expand) void overlay(int pos, cchar * string, int cc = 0); // overlay substring (possibly expand) static void getStats(int & tcount2, int & tmem2); // get statistics void validate() const; // verify integrity int getcc() const { return xcc; } // return string length }; // vector (array) of xstring =================================================== class Vxstring { int nd; // count xstring * pdata; // xstring[nd] public: Vxstring(int = 0); // constructor ~Vxstring(); // destructor Vxstring(const Vxstring &); // copy constructor Vxstring operator= (const Vxstring &); // operator = xstring & operator[] (int); // operator [] const xstring & operator[] (int) const; // operator [] (const) int search(cchar * string); // find element in unsorted Vxstring int bsearch(cchar * string); // find element in sorted Vxstring int sort(int nkeys, int keys[][3]); // sort by designated subfields int sort(int pos = 0, int cc = 0); // sort by 1 subfield (cc 0 = all) int getCount() const { return nd; } // get current count }; // hash table class ============================================================ class HashTab { static int trys1; // insert trys static int trys2; // find/delete trys int cap; // table capacity int count; // strings contained int cc; // string length char * table; // table[cc][cap] public: HashTab(int cc, int cap); // constructor ~HashTab(); // destructor int Add(cchar * string); // add a new string int Del(cchar * string); // delete a string int Find(cchar * string); // find a string int GetCount() { return count; }; // get string count int GetNext(int & first, char * string); // get first/next string int Dump(); // dump hash table }; // Queue class, FIFO, LIFO or mixed ============================================ class Queue { char wmi[8]; Vxstring * vd; // vector of xstrings mutex_t qmutex; // for multi-thread access int qcap; // queue capacity int qcount; // curr. queue count int ent1; // first entry pointer int entN; // last entry pointer char * lastent; // last entry retrieved int lcc; // last entry cc private: void lock(); // auto locking and unlocking void unlock(); // (for multi-thread access) public: Queue(int cap); // create queue with capacity ~Queue(); // destroy queue int getCount(); // get current entry count int push(const xstring * entry, double secs); // add new entry with max. wait time xstring * pop1(); // get 1st entry (oldest) xstring * popN(); // get Nth entry (newest) }; /* ============================================================================= Tree class - sparse array indexed by names or numbers - every element of a Tree is a Tree put(): cc is data length to store get(): cc is max. data length to retrieve actual length is returned, = 0 if not found nn is array count for nodes[] arguments */ class Tree { int wmi; // for ID checking char *tname; // tree name int tmem; // tree data memory void *tdata; // tree data[tmem] int nsub; // no. sub-nodes (Trees) Tree **psub; // pointer to sub-nodes public: Tree(cchar * name); // create Tree ~Tree(); // destroy Tree int put(void * data, int cc, char * nodes[], int nn); // put data by node names[] int put(void * data, int cc, int nodes[], int nn); // put data by node numbers[] int get(void * data, int cc, char * nodes[], int nn); // get data by node names[] int get(void * data, int cc, int nodes[], int nn); // get data by node numbers[] void stats(int nnodes[], int ndata[]); // get nodes and data per level void dump(int level = 0); // diagnostic private: Tree * find(char * nodes[], int nn); // find a sub-node by names[] Tree * find(int nodes[], int nn); // find a sub-node by numbers[] Tree * make(char * nodes[], int nn); // find/create a sub-node by names[] Tree * make(int nodes[], int nn); // find/create a sub-node by numbers[] }; fotoxx-18.01.1/fotoxx-18.01.1.cc0000644000175000017500000057070013222767271014335 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* main main program, set up defaults initzfunc initializations, carry out command line options delete_event response function for main window delete event destroy_event response function for main window destroy event state_event response function for main window fullscreen state change drop_event response function for main window file drag-drop event gtimefunc periodic function update_Fpanel update status parameters on F window top panel paintlock block window updates from main thread or created thread Fpaint main / drawing window refresh (draw signal response function) Fpaintnow immediate Fpaint, not callable from threads Fpaint2 queue Fpaint, callable from threads Fpaint3 update drawing window section from updated E3 section Fpaint4 update drawing window section (direct write) Fpaint3_thread Fpaint3, callable from threads mouse_event mouse event response function mouse_convert convert mouse/window space to image space m_zoom main window zoom in/out function KBevent send KB key from dialog to main window KBpress KB key press event function win_fullscreen set main window full screen status win_unfullscreen restore main window to former size set_mwin_title update the main window title bar draw_pixel draw one overlay pixel using image space erase_pixel erase one pixel draw_line draw overlay line in image space erase_line erase line draw_toplines draw or redraw a set of overlay lines draw_gridlines draw grid lines over image add_toptext add to set of overlay text strings draw_toptext draw set of overlay text strings erase_toptext remove text from set of overlay text strings draw_text draw text on window in image space add_topcircle add a circle to set of overlay circles draw_topcircles draw the set of overlay circles in window space erase_topcircles erase the set of overlay circles draw_mousecircle draw a circle around pointer in image space draw_mousecircle2 2nd instance for paint/clone tracking circle draw_mousearc draw an ellipse around pointer in image space splcurve_init set up a spline curve drawing area splcurve_adjust mouse event function to manipulate curve nodes splcurve_addnode add an anchor point to a curve splcurve_resize resize drawing area if too small splcurve_draw draw curve through nodes splcurve_generate generate x/y table of values from curve splcurve_yval get curve y-value for given x-value splcurve_load load curve data from a saved file splcurve_save save curve data to a file edit_setup start an image edit function CEF_invalid diagnostic edit_cancel cancle image edit edit_done finish image edit edit_fullsize convert preview to full size edit edit_undo undo current edit (reset) edit_redo redo current edit edit_reset reset all edit changes edit_load_widgets load zdialog widgets and curves from a file edit_save_widgets save last-used widgets (for [done] buttons) edit_load_prev_widgets load last-used widgets (for [prev] buttons) edit_save_last_widgets save last-used widgets (for [prev] buttons) m_undo_redo undo/redo depending on mouse button undo_redo_choice popup menu response function m_undo restore previous edit in undo/redo stack m_redo restore next edit in undo/redo stack undo_all undo all edits for image redo_all redo all edits for image save_undo save image in the undo stack load_undo load image from the undo stack checkpend check status: edit mods busy block any quiet takeMouse set mouse event function and special cursor freeMouse remove mouse event function, set normal cursor start_thread start thread running signal_thread signal thread that work is pending wait_thread_idle wait for pending work complete wrapup_thread wait for thread exit or command thread exit thread_idle_loop wait for pending work, exit if commanded thread_exit exit thread unconditionally start_wthread start a working thread per SMP processor exit_wthread exit a working thread wait_wthreads wait for all working threads to exit save_params save parameters when fotoxx exits load_params load parameters at fotoxx startup free_resources free resources for the current image file sigdiff compare 2 floats with margin of significance *********************************************************************************/ #define EX // disable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are defined) /********************************************************************************/ // fotoxx main program using namespace zfuncs; int main(int argc, char *argv[]) { char lang[8] = "", *homedir, *temp; int Fclone=0, cloxx=0, cloyy=0, cloww=0, clohh=0; int ii, jj, cc, err; char cssfile[200]; STATB statb; appimage = getenv("APPIMAGE"); // detect appimage package 17.04 Arelease = Frelease; // "fotoxx=NN.N" if (appimage) Arelease = Frelease "-appimage"; // "fotoxx-NN.N-appimage" printz("%s \n",Arelease); if (appimage) make_appimage_desktop(); // add desktop menu/launcher 17.08 if (argc > 1 && strmatchV(argv[1],"-ver","-v",0)) exit(0); // if fotoxx -ver, exit now setenv("GTK_THEME","default",0); // set theme if missing (KDE etc.) 17.08 setenv("GDK_BACKEND","x11",1); // wayland 18.01 printz("initz. clutter and GTK \n"); if (gtk_clutter_init(&argc,&argv) != CLUTTER_INIT_SUCCESS) { printz("failure \n"); exit(1); } homedir = 0; if (argc > 2 && strmatch(argv[1],"-home")) homedir = argv[2]; // relocate user directory zinitapp("fotoxx",homedir); // initz. app directories // modify GTK widgets to take less screen space snprintf(cssfile,200,"%s/widgets.css",get_zhomedir()); // 18.01 GtkStyleProvider *provider = (GtkStyleProvider *) gtk_css_provider_new(); gtk_style_context_add_provider_for_screen(zfuncs::screen,provider,999); gtk_css_provider_load_from_path(GTK_CSS_PROVIDER(provider),cssfile,0); // initialize externals to default values (saved parameters will override) strcpy(zfuncs::zappname,Frelease); // app name and version Ffirsttime = 1; // first startup (params override) Findexlev = 2; // full image index processing FMindexlev = 2; // " also if start via file manager Pindexlev = -1; // no -index command parameter xxrec_tab = 0; // no image index yet Nxxrec = Findexvalid = 0; xmeta_changed = 0; // flag, indexed metadata changed 18.01 Prelease = zstrdup("unknown"); // prev. fotoxx release (params override) mwgeom[0] = mwgeom[1] = 100; // default main window geometry mwgeom[2] = 1200; mwgeom[3] = 800; *paneltext = 0; // no status bar text trimww = 1600; // default initial image trim size 17.04 trimhh = 1000; trimbuttons[0] = zstrdup("5:4"); trimratios[0] = zstrdup("5:4"); // default trim ratio buttons trimbuttons[1] = zstrdup("4:3"); trimratios[1] = zstrdup("4:3"); trimbuttons[2] = zstrdup("8:5"); trimratios[2] = zstrdup("8:5"); trimbuttons[3] = zstrdup("16:9"); trimratios[3] = zstrdup("16:9"); trimbuttons[4] = zstrdup("2:1"); trimratios[4] = zstrdup("2:1"); trimbuttons[5] = zstrdup("gold"); trimratios[5] = zstrdup("1.62:1"); editresize[0] = 1600; // default initial resize size editresize[1] = 1200; currgrid = 0; // default initial grid settings gridsettings[0][GON] = 0; // grid off gridsettings[0][GX] = gridsettings[0][GY] = 1; // x/y grid lines enabled gridsettings[0][GXS] = gridsettings[0][GYS] = 200; // x/y spacing gridsettings[0][GXC] = gridsettings[0][GYC] = 5; // x/y count menu_style = zstrdup("icons"); // default menu style (icons only) FBrgb[0] = FBrgb[1] = FBrgb[2] = 50; // F view background color GBrgb[0] = GBrgb[1] = GBrgb[2] = 200; // G view background color MFrgb[0] = MFrgb[1] = MFrgb[2] = 250; // menu font color 18.01 MBrgb[0] = MBrgb[1] = MBrgb[2] = 80; // menu background color 18.01 dialog_font = zstrdup("Sans 11"); // default dialog font iconsize = 32; // default icon size splcurve_minx = 5; // default curve node separation % startdisplay = zstrdup("prevF"); // start with previous image 17.08 Fdragopt = 1; // image drag with mouse zoomcount = 2; // zooms to reach 2x image size zoomratio = sqrtf(2); // corresp. zoom ratio Nshortcuts = 0; // KB shortcut list is empty 18.01 map_dotsize = 8; // map dot size, mouse capture dist curr_file = curr_dirk = 0; // no curr. file or directory copymove_loc = 0; // copy/move target directory color_palette_file = 0; // user's color palette file 17.04 thumbdirk = 0; // no thumbnail directory navi::thumbsize = 256; // gallery default thumbnail size commandmenu = 0; // command line menu function commandalbum = 0; // command line album gallery initial_file = 0; // start with image file or directory jpeg_def_quality = 90; // default .jpeg save quality jpeg_1x_quality = 90; // default for 1-time jpeg quality lens_mm = 35; // pano lens parameter netmap_source = zstrdup("mapnik"); // default net map source mapbox_access_key = zstrdup("undefined"); // mapbox map source access key colormapfile = zstrdup("undefined"); // printer calibration color map ss_KBkeys = zstrdup("BNPX"); // default slide show control keys 18.01 RAWfiletypes = zstrdup(".arw .crw .cr2 .dng .erf .iiq .mef .mos " // some of the known RAW file types ".mpo .nef .nrw .orf .pef .ptx .raw " // (case is not significant here) ".rw2 .rwl .srf .srw .sr2 "); imagefiletypes = zstrdup(".jpg .jpeg .png .tif .tiff .bmp .ico " // supported image file types ".gif .svg .xpm .tga "); VIDEOfiletypes = zstrdup(".avi .wmv .mov .flv .mpeg .mp4 " // some known video file types 17.08.3 ".3gp .3g2 .vob .h264 .webm .ogv "); BLACK[0] = BLACK[1] = BLACK[2] = 0; // define RGB colors WHITE[0] = WHITE[1] = WHITE[2] = 255; RED[0] = 255; RED[1] = RED[2] = 0; GREEN[1] = 255; GREEN[0] = GREEN[2] = 0; BLUE[2] = 255; BLUE[0] = BLUE[1] = 0; LINE_COLOR = RED; // initial foreground drawing color for (int ii = 0; ii < 100; ii++) // static integer values 0-99 Nval[ii] = ii; // file and directory names in user directory /home//.fotoxx/* snprintf(index_dirk,199,"%s/image_index",get_zhomedir()); // image index directory snprintf(tags_defined_file,199,"%s/tags_defined",get_zhomedir()); // defined tags file snprintf(recentfiles_file,199,"%s/recent_files",get_zhomedir()); // recent files file (index func) snprintf(saved_areas_dirk,199,"%s/saved_areas",get_zhomedir()); // saved areas directory snprintf(albums_dirk,199,"%s/albums",get_zhomedir()); // albums directory snprintf(gallerymem_file,199,"%s/gallery_memory",get_zhomedir()); // recent gallery memory 18.01 snprintf(saved_curves_dirk,199,"%s/saved_curves",get_zhomedir()); // saved curves directory snprintf(addtext_dirk,199,"%s/add_text",get_zhomedir()); // add_text directory snprintf(addline_dirk,199,"%s/add_line",get_zhomedir()); // add_line directory snprintf(favorites_dirk,199,"%s/favorites",get_zhomedir()); // favorites directory snprintf(mashup_dirk,199,"%s/mashup",get_zhomedir()); // mashup projects directory locale_filespec("data","quickstart.html",quickstart_file); // quickstart html file snprintf(slideshow_dirk,199,"%s/slideshows",get_zhomedir()); // slide show directory snprintf(slideshow_trans_dirk,199,"%s/slideshow_trans",get_zhomedir()); // slide show transitions snprintf(pattern_dirk,199,"%s/patterns",get_zhomedir()); // pattern files directory snprintf(retouch_combo_dirk,199,"%s/retouch_combo",get_zhomedir()); // retouch combo settings directory snprintf(custom_kernel_dirk,199,"%s/custom_kernel",get_zhomedir()); // custom kernel files directory snprintf(printer_color_dirk,199,"%s/printer_color",get_zhomedir()); // printer calibration directory snprintf(edit_scripts_dirk,199,"%s/edit_scripts",get_zhomedir()); // edit script files directory snprintf(searchresults_file,199,"%s/search_results",get_zhomedir()); // output of image search function snprintf(maps_dirk,199,"/usr/share/fotoxx-maps/data"); // map files in fotoxx-maps package snprintf(user_maps_dirk,199,"%s/user_maps",get_zhomedir()); // map files made by user snprintf(montage_maps_dirk,199,"%s/montage_maps",get_zhomedir()); // montage map files made by user 17.04 err = stat(index_dirk,&statb); // create missing directories if (err) mkdir(index_dirk,0750); err = stat(saved_areas_dirk,&statb); if (err) mkdir(saved_areas_dirk,0750); err = stat(albums_dirk,&statb); if (err) mkdir(albums_dirk,0750); err = stat(saved_curves_dirk,&statb); if (err) mkdir(saved_curves_dirk,0750); err = stat(addtext_dirk,&statb); if (err) mkdir(addtext_dirk,0750); err = stat(addline_dirk,&statb); if (err) mkdir(addline_dirk,0750); err = stat(mashup_dirk,&statb); if (err) mkdir(mashup_dirk,0750); err = stat(slideshow_dirk,&statb); if (err) mkdir(slideshow_dirk,0750); err = stat(slideshow_trans_dirk,&statb); if (err) mkdir(slideshow_trans_dirk,0750); err = stat(printer_color_dirk,&statb); if (err) mkdir(printer_color_dirk,0750); err = stat(edit_scripts_dirk,&statb); if (err) mkdir(edit_scripts_dirk,0750); err = stat(maps_dirk,&statb); if (err) mkdir(maps_dirk,0750); err = stat(user_maps_dirk,&statb); if (err) mkdir(user_maps_dirk,0750); err = stat(montage_maps_dirk,&statb); if (err) mkdir(montage_maps_dirk,0750); load_params(); // restore parameters from last session for (ii = 1; ii < argc; ii++) // command line parameters { char *pp = argv[ii]; if (strmatch(pp,"-home")) ii++; // -home homedir skip, see above else if (strmatchV(pp,"-debug","-d",0)) // -d -debug Fdebug = 1; else if (strmatchV(pp,"-lang","-l",0) && argc > ii+1) // -l -lang lc_RC language/region code strncpy0(lang,argv[++ii],7); else if (strmatchV(pp,"-clone","-c",0) && argc > ii+4) { // -c -clone clone new instance Fclone = 1; cloxx = atoi(argv[ii+1]); // window position and size cloyy = atoi(argv[ii+2]); // passed from parent instance cloww = atoi(argv[ii+3]); clohh = atoi(argv[ii+4]); ii += 4; } else if (strmatchV(pp,"-recent","-r",0)) // -r -recent recent files gallery Frecent = 1; else if (strmatchV(pp,"-new","-n",0)) // -n -new newest files gallery Fnew = 1; else if (strmatchV(pp,"-prev","-p",0)) // -p -prev open previous file Fprev = 1; else if (strmatchV(pp,"-blank","-b",0)) // -b -blank start with blank window Fblank = 1; else if (strmatch(pp,"-index") && argc > ii+1) { // -index N index level jj = atoi(argv[++ii]); if (jj >= 0 && jj <= 2) Pindexlev = jj; // 0/1/2 = none/old/old+new image files } else if (strmatchV(pp,"-menu","-m",0) && argc > ii+1) // -m -menu func command line menu func commandmenu = zstrdup(argv[++ii]); else if (strmatchV(pp,"-album","-a",0) && argc > ii+1) // -a -album command line album commandalbum = zstrdup(argv[++ii]); else { // must be initial file or directory initial_file = combine_argvs(argc,argv,ii); // combine remaining argv[] elements 17.08 initial_file = zstrdup(initial_file); // (fix file paths with blanks) if (*initial_file != '/') { // relative to initial directory cc = strlen(initial_file); temp = zstrdup(getcwd(0,0),cc+4); strncatv(temp,200,"/",initial_file,0); // /initial-directory/initial-file 17.08 initial_file = temp; } break; } } ZTXinit(lang); // setup locale, translations setlocale(LC_NUMERIC,"en_US.UTF-8"); // stop comma decimal points zsetfont(dialog_font); // set default font for widgets build_widgets(); // build window widgets and menus if (Fclone) { // clone: open new window gtk_window_move(MWIN,cloxx+10,cloyy+10); // slightly offset from old window gtk_window_resize(MWIN,cloww,clohh); } else { gtk_window_move(MWIN,mwgeom[0],mwgeom[1]); // main window geometry gtk_window_resize(MWIN,mwgeom[2],mwgeom[3]); // defaults or last session params } gtk_widget_show_all(Mwin); arrowcursor = gdk_cursor_new_for_display(display,GDK_TOP_LEFT_ARROW); // cursor for selection dragcursor = gdk_cursor_new_for_display(display,GDK_CROSSHAIR); // cursor for dragging drawcursor = gdk_cursor_new_for_display(display,GDK_PENCIL); // cursor for drawing lines blankcursor = gdk_cursor_new_for_display(display,GDK_BLANK_CURSOR); // invisible cursor m_viewmode(0,"F"); // set F mode initially g_timeout_add(100,initzfunc,0); // initz. call from gtk_main() gtk_main(); // start processing window events return 0; } /********************************************************************************/ // Initial function called from gtk_main() at startup. // This function MUST return 0. int initzfunc(void *) { int Fexiftool = 0, Fxdgopen = 0; int ii, err, npid, contx; FTYPE ftype; char *pp, *pp2; char procfile[20], buff[200]; char badnews[200], albumfile[200]; char colorwheelfile[200]; double freememory, cachememory; float exifver = 0; FILE *fid; STATB statb; double startsecs; struct timeb startime2; printz("%s \n",Arelease); // print Fotoxx release version int v1 = gtk_get_major_version(); // get GTK release version int v2 = gtk_get_minor_version(); int v3 = gtk_get_micro_version(); printz("GTK version: %d.%02d.%02d \n",v1,v2,v3); // check that necessary programs are installed fid = popen("exiftool -ver","r"); // check exiftool version if (fid) { ii = fscanf(fid,"%s",buff); pclose(fid); // accept period or comma convSF(buff,exifver); printz("exiftool version: %.2f \n",exifver); if (exifver >= 8.60) Fexiftool = 1; } err = shell_quiet("which xdg-open >/dev/null 2>&1"); // check for xdg-open if (! err) Fxdgopen = 1; err = shell_quiet("which rawtherapee >/dev/null 2>&1"); // check for Raw Therapee if (! err) Frawtherapee = 1; err = shell_quiet("which growisofs >/dev/null 2>&1"); // check for growisofs if (! err) Fgrowisofs = 1; Fvideo = 1; err = shell_quiet("which ffmpeg >/dev/null 2>&1"); // check for ffmpeg 17.08 if (err) Fvideo = 0; err = shell_quiet("which totem >/dev/null 2>&1"); // check for totem 17.08 if (err) Fvideo = 0; err = shell_quiet("which hugin >/dev/null 2>&1"); // need all of it 17.08.3 if (! err) PTtools = 1; if (Fexiftool + Fxdgopen < 2) { // check mandatory dependencies strcpy(badnews,ZTX("Please install missing programs:")); if (! Fexiftool) strcat(badnews,"\n exiftool (or libimage-exiftool-perl)"); if (! Fxdgopen) strcat(badnews,"\n xdg-utils"); zmessageACK(Mwin,badnews); m_quit(0,0); } if (! Frawtherapee) printz("Raw Therapee not installed \n"); // optional dependencies if (! Fgrowisofs) printz("growisofs not installed \n"); if (! Fvideo) printz("ffmpeg and Totem not installed \n"); if (! PTtools) printz("Panorama Tools (Hugin) not installed \n"); // check for first time Fotoxx install or new release install if (Ffirsttime) { // new fotoxx install showz_html(quickstart_file); Prelease = zstrdup(Frelease); } if (! Ffirsttime && ! strmatch(Prelease,Frelease)) { // Fotoxx release change zmessageACK(Mwin,"fotoxx new release %s",Frelease); Prelease = zstrdup(Frelease); showz_textfile("doc","changelog"); } Ffirsttime = 0; // reset first time flag // copy add text/line files from old directories to new // FIXME remove after 18.01 snprintf(buff,200,"%s/write_text",get_zhomedir()); err = stat(buff,&statb); if (! err) { snprintf(buff,200,"%s/write_text",get_zhomedir()); shell_quiet("mv -n %s/* %s ",buff,addtext_dirk); rmdir(buff); snprintf(buff,200,"%s/write_line",get_zhomedir()); shell_quiet("mv -n %s/* %s ",buff,addline_dirk); rmdir(buff); } // delete fotoxx tempdir files if owner process is no longer running contx = 0; while ((pp = command_output(contx,"find /tmp/fotoxx-* 2>/dev/null",0))) { pp2 = strchr(pp,'-'); if (! pp2) continue; npid = atoi(pp2+1); // pid of fotoxx owner process snprintf(procfile,20,"/proc/%d",npid); err = stat(procfile,&statb); if (! err) continue; // pid is active, keep shell_quiet("rm -R -f -v %s",pp); // delete } // set up temp directory /tmp/fotoxx-nnnnnn-XXXXXX snprintf(tempdir,100,"/tmp/fotoxx-%06d-XXXXXX",getpid()); // use PID + random chars. pp = mkdtemp(tempdir); if (! pp) { zmessageACK(Mwin,"%s \n %s",tempdir,strerror(errno)); exit(1); } strncpy0(tempdir,pp,100); printz("tempdir: %s \n",tempdir); // file name template for undo/redo files snprintf(URS_filename,100,"%s/undo_nn",tempdir); // /tmp/fotoxx-nnnnnn/undo_nn // check free memory and suggest image size limits parseprocfile("/proc/meminfo","MemFree:",&freememory,0); // get amount of free memory parseprocfile("/proc/meminfo","Cached:",&cachememory,0); freememory = (freememory + cachememory) / 1024; // megabytes printz("free memory: %.0f MB \n",freememory); printz("image size limits for good performance: \n"); printz(" view: %.0f megapixels \n",(freememory-100)/6); // F + preview, 3 bytes/pixel each printz(" edit: %.0f megapixels \n",(freememory-100)/54); // + E0/E1/E3/ER, 12 bytes/pixel each // miscellaneous printz("screen width: %d height: %d \n", // monitor pixel size zfuncs::monitor_ww,zfuncs::monitor_hh); NWT = get_nprocs(); // get SMP CPU count if (NWT <= 0) NWT = 2; if (NWT > max_threads) NWT = max_threads; // compile time limit printz("using %d threads \n",NWT); exif_server(0,0,0); // kill orphan exiftool process zdialog_inputs("load"); // load saved dialog inputs zdialog_positions("load"); // load saved dialog positions gallery_memory("load"); // load recent gallery positions 17.08 KBshortcuts_load(); // load KB shortcuts from file if (! color_palette_file) err = 1; // initialize color palette file 17.08 else err = stat(color_palette_file,&statb); // if this is missing if (err) { snprintf(colorwheelfile,199,"%s/colorwheel.jpg",get_zhomedir()); err = stat(colorwheelfile,&statb); if (! err) color_palette_file = zstrdup(colorwheelfile); } // create or update image index file and memory table if (xmeta_changed) { // indexed metadata changed 18.01 printz("indexed metadata list changed, full index required \n"); index_rebuild(2,0); } else if (Pindexlev >= 0) index_rebuild(Pindexlev,0); // -index command parameter given else if (initial_file) index_rebuild(FMindexlev,0); // likely a file manager call else index_rebuild(Findexlev,0); // index level from user setting // set current file and gallery from command line if present if (topdirks[0]) curr_dirk = zstrdup(topdirks[0]); // default 1st top image directory else curr_dirk = zstrdup(getenv("HOME")); if (initial_file) { // file parameter printz("initial file: %s \n",initial_file); ftype = image_file_type(initial_file); if (ftype == FNF) { // non-existent file printz(" -invalid file \n"); zfree(initial_file); initial_file = 0; } else if (ftype == FDIR) { // directory if (curr_dirk) zfree(curr_dirk); curr_dirk = initial_file; initial_file = 0; gallery(curr_dirk,"init",0); gallery(0,"sort",-2); // recall sort and position 18.01 m_viewmode(0,"G"); } else if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) { // image file 17.08 if (curr_dirk) zfree(curr_dirk); curr_dirk = zstrdup(initial_file); // set current directory from file pp = strrchr(curr_dirk,'/'); if (pp) *pp = 0; f_open(initial_file); } else { printz(" -invalid file \n"); zfree(initial_file); initial_file = 0; } } else if (commandalbum) { // -album parameter printz("initial album: %s \n",commandalbum); // 17.04 snprintf(albumfile,200,"%s/albums/%s",get_zhomedir(),commandalbum); err = stat(albumfile,&statb); if (err) { printz("invalid album file: %s \n",commandalbum); commandalbum = 0; } else { navi::gallerytype = ALBUM; gallery(albumfile,"initF",0); gallery(0,"sort",-2); // recall sort and position 18.01 m_viewmode(0,"G"); } } else if (Fprev) { // start with previous file if (last_curr_file && *last_curr_file == '/') f_open(last_curr_file); } else if (Frecent) // start with recent files gallery m_recentfiles(0,0); else if (Fnew) // start with newest files gallery m_newfiles(0,"file"); // by file mod date else if (Fblank) { // blank window, no gallery curr_file = curr_dirk = 0; navi::galleryname = 0; navi::gallerytype = TNONE; set_mwin_title(); } // if no command line option, get startup display from user settings else if (strmatch(startdisplay,"album")) { printz("initial album: %s \n",startalbum); // 17.04 err = stat(startalbum,&statb); if (err) { printz("invalid album file: %s \n",startalbum); commandalbum = 0; } else { navi::gallerytype = ALBUM; gallery(startalbum,"initF",0); gallery(0,"sort",-2); // recall sort and position 18.01 m_viewmode(0,"G"); } } else if (strmatch(startdisplay,"recent")) // start with recent files gallery m_recentfiles(0,0); else if (strmatch(startdisplay,"newest")) // start with newest files gallery m_newfiles(0,"file"); // by file mode date else if (strmatch(startdisplay,"prevG")) { // start with previous gallery if (last_gallerytype != TNONE) { navi::gallerytype = last_gallerytype; if (last_gallerytype == GDIR) gallery(last_galleryname,"init",0); else gallery(last_galleryname,"initF",0); gallery(0,"sort",-2); // recall sort and position 18.01 m_viewmode(0,"G"); } } else if (strmatch(startdisplay,"prevF")) { // start with previous image file if (last_curr_file && *last_curr_file == '/') f_open(last_curr_file); } else if (strmatch(startdisplay,"specG")) { // start with specified gallery (dirk) if (startdirk && *startdirk == '/') { if (curr_dirk) zfree(curr_dirk); curr_dirk = zstrdup(startdirk); gallery(curr_dirk,"init",0); gallery(0,"sort",-2); // recall sort and position 18.01 m_viewmode(0,"G"); } } else if (strmatch(startdisplay,"specF")) // start with given image file f_open(startfile); if (commandmenu) { // startup menu on command line printz("start menu: %s \n",commandmenu); for (ii = 0; ii < Nmenus; ii++) { // convert menu name to menu function if (! menutab[ii].menu) continue; // separator, null menu if (strmatchcase(commandmenu,ZTX(menutab[ii].menu))) break; } if (ii < Nmenus) menutab[ii].func(0,menutab[ii].arg); // call the menu function } save_params(); // save parameters now g_timeout_add(20,gtimefunc,0); // start periodic function (20 ms) ftime(&startime2); // time to startup and initialize 17.08 startsecs = startime2.time - startime.time + 0.001 * (startime2.millitm - startime.millitm); printz("startup time: %.1f secs.\n",startsecs); return 0; // don't come back } /********************************************************************************/ // functions for main window event signals int delete_event() // main window closed { int yn; zdialog *zd = 0; cchar *title = "unknown"; printz("main window delete event \n"); if (checkpend("mods")) return 1; // allow user bailout if (zfuncs::zdialog_busy) { zd = zdialog_list[0]; if (zd) title = zd->title; yn = zmessageYN(Mwin,ZTX("Kill active dialog? %s"),title); // allow user bailout 18.01 if (! yn) return 1; } quitxx(); return 0; } int destroy_event() // main window destroyed { printz("main window destroy event \n"); quitxx(); return 0; } int state_event(GtkWidget *, GdkEvent *event) // main window state changed { int state = ((GdkEventWindowState *) event)->new_window_state; // track window fullscreen status if (state & GDK_WINDOW_STATE_FULLSCREEN) Ffullscreen = 1; else if (state & GDK_WINDOW_STATE_MAXIMIZED) Ffullscreen = 1; // 17.08 else Ffullscreen = 0; return 0; } void drop_event(int mousex, int mousey, char *file) // file drag-drop event { if (! file) return; printf("drag-drop file: %s \n",file); f_open(file,0,0,1); return; } /********************************************************************************/ // Periodic function (20 milliseconds) int gtimefunc(void *) { static int domore = 0; if (Fshutdown) return 0; // shutdown underway if (Fpaintlock && gdkwin) { // thread request to freeze gdk_window_freeze_updates(gdkwin); // window updates zadd_locked(Fpaintlock,-1); } if (Fpaintunlock && gdkwin) { // thread request to thaw gdk_window_thaw_updates(gdkwin); // window updates zadd_locked(Fpaintunlock,-1); } if (Fpaintrequest && Cdrawin) gtk_widget_queue_draw(Cdrawin); if (zd_thread && zd_thread_event) { // send dialog event from thread if (! CEF || CEF->thread_status < 2) { zdialog_send_event(zd_thread,zd_thread_event); // only if thread done or not busy zd_thread_event = 0; } } void update_window_area(); // update window area 17.01 update_window_area(); // pending from a thread if (--domore > 0) return 1; // do rest every 200 milliseconds domore = 10; update_Fpanel(); // update top panel information return 1; } /********************************************************************************/ // update F window top panel with current status information // called from timer function void update_Fpanel() { static double time1 = 0, time2, cpu1 = 0, cpu2, cpuload; static double pagesize, mem, MB; char *pp, text1[300], text2[200]; static char ptext1[300] = ""; int ww, hh, scale, bpc; static int ftf = 1; double file_MB = 1.0 / MEGA * curr_file_size; static cchar *reduced, *areaactive, *dialogopen; static cchar *blocked, *modified; FILE *fid; if (! Fpanelshow) return; // panel currently hidden if (ftf) { ftf = 0; reduced = ZTX("(reduced)"); areaactive = ZTX("area active"); dialogopen = ZTX("dialog open"); blocked = ZTX("blocked"); modified = "mod"; pagesize = getpagesize(); // 17.08 } if (Fslideshow) return; if (FGWM == 'G') goto update_busy; if (FGWM != 'F') return; *text1 = *text2 = 0; if (! time1) { time1 = get_seconds(); cpu1 = jobtime(); } time2 = get_seconds(); // compute process cpu load % if (time2 - time1 > 1.0) { // at 1 second intervals cpu2 = jobtime(); cpuload = 100.0 * (cpu2 - cpu1) / (time2 - time1); time1 = time2; cpu1 = cpu2; } mem = 0; // get memory MB in use 17.08 fid = fopen("/proc/self/stat","r"); if (fid) { pp = fgets(text2,200,fid); fclose(fid); if (pp) { pp = strchr(pp,')'); // closing ')' after (short) filename if (pp) parseprocrec(pp+1,22,&mem,null); // get memory usage } } MB = mem * pagesize / MEGA; snprintf(text1,300,"CPU %03.0f%c MB %.0f",cpuload,'%',MB); // CPU 23% MB 123 if (curr_file && Fpxb) { if (E3pxm) { ww = E3pxm->ww; hh = E3pxm->hh; } else { ww = Fpxb->ww; hh = Fpxb->hh; } bpc = curr_file_bpc; snprintf(text2,100," %dx%dx%d",ww,hh,bpc); // 2345x1234x16 (preview) 1.56MB 45% strncatv(text1,300,text2,0); if (CEF && CEF->Fpreview) strncatv(text1,300," ",reduced,0); snprintf(text2,100," %.2fM",file_MB); strncatv(text1,300,text2,0); scale = Mscale * 100 + 0.5; snprintf(text2,100," %d%c",scale,'%'); strncatv(text1,300,text2,0); if (URS_pos) { // edit undo/redo stack depth snprintf(text2,100," %s: %d",ZTX("edits"),URS_pos); strncatv(text1,300,text2,0); } if (Fmetamod) strncatv(text1,300," ","metadata",0); } else if (Fpxb) { snprintf(text2,100," %dx%d",Fpxb->ww,Fpxb->hh); strncatv(text1,300,text2,0); } if (sa_stat == 3) strncatv(text1,300," ",areaactive,0); if (zfuncs::zdialog_busy) strncatv(text1,300," ",dialogopen,0); if (Fblock) strncatv(text1,300," ",blocked,0); // "blocked" if (CEF && CEF->Fmods) strncatv(text1,300," ",modified,0); // "mod" if (*paneltext) strncatv(text1,300," ",paneltext,0); // application text if (curr_file) { pp = strrchr(curr_file,'/'); // "filename.jpg" 17.01 if (pp && Ffullscreen && ! (Ffuncbusy | Fthreadbusy)) { // 17.08 strncpy0(text2,pp+1,100); strncatv(text1,300," ",text2,0); } } if (! strmatch(text1,ptext1)) { // if text changed, update panel bar gtk_label_set_label(GTK_LABEL(Fpanlab),text1); gtk_widget_show_all(Fpanel); strcpy(ptext1,text1); } // Show BUSY label if Ffuncbusy or Fthreadbusy active. // Show progress counter if Fbusy_goal > 0 // added to top panel: BUSY NN% update_busy: static GtkWidget *busylabel = 0, *donelabel = 0; static cchar *busytext = " BUSY "; static char donetext[] = "xx% "; GtkWidget *FGpanel; int pct; char nn[4]; if (FGWM == 'F') FGpanel = Fpanel; else if (FGWM == 'G') FGpanel = Gpanel; else return; if (Ffuncbusy | Fthreadbusy) { if (! busylabel) { busylabel = gtk_label_new(null); gtk_label_set_markup(GTK_LABEL(busylabel),busytext); gtk_box_pack_start(GTK_BOX(FGpanel),busylabel,0,0,5); } } else { if (busylabel) gtk_widget_destroy(busylabel); busylabel = 0; } if (Fbusy_done > 0 && Fbusy_done < Fbusy_goal) { // 17.04 pct = 100 * Fbusy_done / Fbusy_goal; if (pct > 99) pct = 99; snprintf(nn,4,"%02d",pct); strncpy(donetext+33,nn,2); // watch out: hidden dependency if (! donelabel) { donelabel = gtk_label_new(""); gtk_box_pack_start(GTK_BOX(FGpanel),donelabel,0,0,0); } gtk_label_set_markup(GTK_LABEL(donelabel),donetext); } else { if (donelabel) gtk_widget_destroy(donelabel); donelabel = 0; } gtk_widget_show_all(FGpanel); return; } // block image updated by created threads during painting by main thread // block main thread window updates during changes to image data by created threads // lock = 1: block updates lock = 0: unblock void paintlock(int lock) { static int freezecount = 0; int nn; if (! gdkwin) return; if (pthread_equal(pthread_self(),zfuncs::tid_main)) { // main thread caller if (lock) { gdk_window_freeze_updates(gdkwin); // freeze window updates nn = zadd_locked(freezecount,+1); // increment freeze counter if (nn <= 0) zappcrash("paintlock() freezecount: %d",freezecount); } else { gdk_window_thaw_updates(gdkwin); // thaw window updates nn = zadd_locked(freezecount,-1); // decrement freeze counter if (nn < 0) zappcrash("paintlock() freezecount: %d",freezecount); } } else { // created thread caller if (lock) { zadd_locked(Fpaintlock,+1); // set flag for gtimefunc() while (zadd_locked(Fpaintlock,0)) zsleep(0.01); // wait for freeze done nn = zadd_locked(freezecount,+1); // increment freeze counter if (nn <= 0) zappcrash("paintlock() freezecount: %d",freezecount); } else { zadd_locked(Fpaintunlock,+1); // set flag for gtimefunc() while (zadd_locked(Fpaintunlock,0)) zsleep(0.01); // wait for thaw done nn = zadd_locked(freezecount,-1); // decrement freeze counter if (nn < 0) zappcrash("paintlock() freezecount: %d",freezecount); } } return; } /********************************************************************************/ // GTK3 "draw" function for F and W mode drawing windows. // Paint window when created, exposed, resized, or image modified (edited). // Update window image if scale change or window size change. // Otherwise do only the draw function (triggered from GTK). // Draw the image section currently within the visible window. // May NOT be called from threads. See Fpaint2() for threads. int Fpaint(GtkWidget *Cdrawin, cairo_t *cr) { PIXBUF *pixbuf; PXB *pxb1; GdkRGBA rgba; static int pdww = 0, pdhh = 0; // prior window size float wscale, hscale; int fww, fhh; // current image size at 1x int mww, mhh; // scaled image size int morgx, morgy; int dorgx, dorgy; int centerx, centery; int refresh = 0; int mousex, mousey; // mouse position after zoom float magx, magy; // mouse drag, magnification ratios uint8 *pixels, *pix, bgpix[3]; int rs, px, py; if (Fshutdown) return 1; // shutdown underway if (! Cdrawin || ! gdkwin || ! Cstate || ! Cstate->fpxb) { // no image Fpaintrequest = 0; return 1; } if (Fview360) return 1; // 18.01 Dww = gdk_window_get_width(gdkwin); // (new) drawing window size Dhh = gdk_window_get_height(gdkwin); if (Dww < 20 || Dhh < 20) return 1; // too small if (Dww != pdww || Dhh != pdhh) { // window size changed refresh = 1; // image refresh needed pdww = Dww; pdhh = Dhh; } if (Fpaintrequest) { // window image changed Fpaintrequest = 0; // window updated as of NOW refresh = 1; // image refresh needed if (FGWM == 'F' && (E0pxm || E3pxm)) { // insure F-view and E0 or E3 if (E3pxm) pxb1 = PXM_PXB_copy(E3pxm); // update fpxb from E0/E3 image else pxb1 = PXM_PXB_copy(E0pxm); // or use already edited image PXB_free(Cstate->fpxb); Cstate->fpxb = pxb1; } } centerx = (Cstate->morgx + 0.5 * dww) / Cstate->mscale; // center of window, image space centery = (Cstate->morgy + 0.5 * dhh) / Cstate->mscale; // (before window or scale change) fww = Cstate->fpxb->ww; // 1x image size fhh = Cstate->fpxb->hh; if (Cstate->fzoom == 0) { // scale to fit window wscale = 1.0 * Dww / fww; hscale = 1.0 * Dhh / fhh; if (wscale < hscale) Cstate->mscale = wscale; // use greatest ww/hh ratio else Cstate->mscale = hscale; if (fww <= Dww && fhh <= Dhh && ! Fblowup) Cstate->mscale = 1.0; // small image 1x unless Fblowup zoomx = zoomy = 0; // no zoom target } else Cstate->mscale = Cstate->fzoom; // scale to fzoom level mww = fww * Cstate->mscale; // scaled image size for window mhh = fhh * Cstate->mscale; dww = Dww; // image fitting inside drawing window if (dww > mww) dww = mww; // image < window size dhh = Dhh; if (dhh > mhh) dhh = mhh; if (Cstate->mscale != Cstate->pscale) { // scale changed Cstate->morgx = Cstate->mscale * centerx - 0.5 * dww; // change origin to keep same center Cstate->morgy = Cstate->mscale * centery - 0.5 * dhh; // (subject to later rules) Cstate->pscale = Cstate->mscale; // remember scale refresh = 1; // image refresh needed } if (! Cstate->mpxb) refresh++; // need to make mpxb if (refresh) { // image refresh needed if (Cstate->mpxb) PXB_free(Cstate->mpxb); if (Cstate->mscale == 1) Cstate->mpxb = PXB_copy(Cstate->fpxb); // fast 1x image else Cstate->mpxb = PXB_rescale(Cstate->fpxb,mww,mhh); // rescaled image } if ((Mxdrag || Mydrag)) { // pan/scroll via mouse drag zoomx = zoomy = 0; // no zoom target magx = 1.0 * (mww - dww) / dww; magy = 1.0 * (mhh - dhh) / dhh; // needed magnification of mouse movement if (magx < 1) magx = 1; // retain a minimum speed if (magy < 1) magy = 1; if (Fdragopt == 1) { Cstate->morgx -= round(Mwdragx); // same direction Cstate->morgy -= round(Mwdragy); } if (Fdragopt == 2) { Cstate->morgx += round(Mwdragx); // opposite direction Cstate->morgy += round(Mwdragy); } if (Fdragopt == 3) { Cstate->morgx -= round(Mwdragx * magx); // same direction, magnified Cstate->morgy -= round(Mwdragy * magy); } if (Fdragopt == 4) { Cstate->morgx += round(Mwdragx * magx); // opposite direction, magnified Cstate->morgy += round(Mwdragy * magy); } } if (dww < Dww) Cstate->dorgx = 0.5 * (Dww - dww); // if scaled image < window, else Cstate->dorgx = 0; // center image in window if (dhh < Dhh) Cstate->dorgy = 0.5 * (Dhh - dhh); else Cstate->dorgy = 0; if (Fshiftright && dww < Dww-1) Cstate->dorgx = Dww-1 - dww; // shift image to right margin if (zoomx || zoomy) { // requested zoom center Cstate->morgx = Cstate->mscale * zoomx - 0.5 * dww; // corresp. window position within image Cstate->morgy = Cstate->mscale * zoomy - 0.5 * dhh; } if (Cstate->morgx < 0) Cstate->morgx = 0; // maximize image within window if (Cstate->morgy < 0) Cstate->morgy = 0; // (no unused margins) if (Cstate->morgx + dww > mww) Cstate->morgx = mww - dww; if (Cstate->morgy + dhh > mhh) Cstate->morgy = mhh - dhh; if (zoomx || zoomy) { // zoom target mousex = zoomx * Cstate->mscale - Cstate->morgx + Cstate->dorgx; mousey = zoomy * Cstate->mscale - Cstate->morgy + Cstate->dorgy; // mouse pointer follows target move_pointer(Cdrawin,mousex,mousey); zoomx = zoomy = 0; // reset zoom target } if (zddarkbrite) darkbrite_paint(); // update dark/bright pixels rgba.red = 0.00392 * FBrgb[0]; // window background color rgba.green = 0.00392 * FBrgb[1]; // 0 - 255 --> 0.0 - 1.0 rgba.blue = 0.00392 * FBrgb[2]; rgba.alpha = 1.0; gdk_cairo_set_source_rgba(cr,&rgba); cairo_paint(cr); morgx = Cstate->morgx; // window position in (larger) image morgy = Cstate->morgy; dorgx = Cstate->dorgx; dorgy = Cstate->dorgy; if (refresh) { // renew background image if (BGpixbuf) g_object_unref(BGpixbuf); BGpixbuf = gdk_pixbuf_new(GDKRGB,0,8,dww,dhh); pixels = gdk_pixbuf_get_pixels(BGpixbuf); rs = gdk_pixbuf_get_rowstride(BGpixbuf); bgpix[0] = FBrgb[0]; // use background color bgpix[1] = FBrgb[1]; bgpix[2] = FBrgb[2]; for (py = 0; py < dhh; py++) for (px = 0; px < dww; px++) { pix = pixels + py * rs + px * 3; if (py % 10 < 2 && px % 10 < 2) // with periodic black dots memset(pix,0,3); else memcpy(pix,bgpix,3); } } gdk_cairo_set_source_pixbuf(cr,BGpixbuf,dorgx,dorgy); // paint background image cairo_paint(cr); pixbuf = Cstate->mpxb->pixbuf; // get image section within window pixbuf = gdk_pixbuf_new_subpixbuf(pixbuf,morgx,morgy,dww,dhh); gdk_cairo_set_source_pixbuf(cr,pixbuf,dorgx,dorgy); // paint section cairo_paint(cr); g_object_unref(pixbuf); if (Cstate == &Fstate) { // view mode is image if (Ntoplines) draw_toplines(1,cr); // draw line overlays if (gridsettings[currgrid][GON]) draw_gridlines(cr); // draw grid lines if (Ntoptext) draw_toptext(cr); // draw text strings if (Ntopcircles) draw_topcircles(cr); // draw circles if (Fshowarea) sa_show(1,cr); // draw select area outline if (refresh && zdbrdist) m_show_brdist(0,0); // update brightness dist. } if (Cstate == &Wstate) // view mode is world maps filemap_paint_dots(); return 1; } /********************************************************************************/ // Repaint modified image synchrounously. // May NOT be called from threads. void Fpaintnow() { if (! Cdrawin || ! gdkwin || ! Cstate || ! Cstate->fpxb) { // no image printf("Fpaintnow(), no image \n"); return; } Fpaintrequest = 1; // request repaint of changed image gtk_widget_queue_draw(Cdrawin); while (Fpaintrequest) { zsleep(0.001); zmainloop(); } return; } // Cause (modified) output image to get repainted soon. // Fpaint() will be called by gtimefunc() next timer cycle. // MAY be called from threads. void Fpaint2() { Fpaintrequest = 1; // request repaint of changed image return; } // Update a section of Fpxb and Mpxb from an updated section of E3pxm, // then update the corresponding section of the drawing window. // This avoids a full image refresh, E3pxm > fpxb > mpxb > drawing window. // px3, py3, ww3, hh3: modified section within E3pxm to be propagated. // May NOT be called from threads. void Fpaint3(int px3, int py3, int ww3, int hh3, cairo_t *cr) { int crflag = 0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } PXM_PXB_update(E3pxm,Fpxb,px3,py3,ww3,hh3); // E3pxm > Fpxb, both 1x scale PXB_PXB_update(Fpxb,Mpxb,px3,py3,ww3,hh3); // Fpxb > Mpxb, scaled up or down Fpaint4(px3,py3,ww3,hh3,cr); // update drawing window from Mpxb if (crflag) draw_context_destroy(draw_context); return; } // Do the same using E0pxm instead of E3pxm void Fpaint0(int px3, int py3, int ww3, int hh3, cairo_t *cr) { int crflag = 0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } PXM_PXB_update(E0pxm,Fpxb,px3,py3,ww3,hh3); PXB_PXB_update(Fpxb,Mpxb,px3,py3,ww3,hh3); Fpaint4(px3,py3,ww3,hh3,cr); if (crflag) draw_context_destroy(draw_context); return; } // Repaint a section of the Mpxb image in the visible window. // px3, py3, ww3, hh3: area to be repainted (in 1x image space). // May NOT be called from threads. // Writes directly on the window (cairo pixbuf paint). void Fpaint4(int px3, int py3, int ww3, int hh3, cairo_t *cr) { PIXBUF *pixbuf, *bgpixbuf; int px1, py1, ww1, hh1; int px2, py2, ww2, hh2; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image px2 = Mscale * px3 - 2; // 1x image space to Mpxb space py2 = Mscale * py3 - 2; // (expanded a few pixels) ww2 = Mscale * ww3 + 2 / Mscale + 4; hh2 = Mscale * hh3 + 2 / Mscale + 4; if (px2 < Morgx) { // reduce to currently visible window ww2 = ww2 - (Morgx - px2); px2 = Morgx; } if (py2 < Morgy) { hh2 = hh2 - (Morgy - py2); py2 = Morgy; } if (px2 + ww2 >= Mpxb->ww) ww2 = Mpxb->ww - px2 - 1; // stay within image bugfix 17.04.3 if (py2 + hh2 >= Mpxb->hh) hh2 = Mpxb->hh - py2 - 1; if (ww2 <= 0 || hh2 <= 0) return; px1 = px2 - Morgx + Dorgx; // corresp. position in drawing window py1 = py2 - Morgy + Dorgy; if (px1 + ww2 >= Dww) ww2 = Dww - px1 - 1; // stay within window bugfix 17.04.3 if (py1 + hh2 >= Dhh) hh2 = Dhh - py1 - 1; if (ww2 <= 0 || hh2 <= 0) return; pixbuf = gdk_pixbuf_new_subpixbuf(Mpxb->pixbuf,px2,py2,ww2,hh2); // Mpxb area to paint if (! pixbuf) { printz("Fpaint4() pixbuf failure \n"); return; } px2 = px1; // corresp. position in drawing window py2 = py1; px1 = px2 - Dorgx; // corresp. position in background image py1 = py2 - Dorgy; paintlock(1); if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } if (Mpxb->nc > 3) { // alpha channel present ww1 = ww2; // draw background image to area hh1 = hh2; if (px1 + ww1 > dww) ww1 = dww - px1; if (py1 + hh1 > dhh) hh1 = dhh - py1; if (ww1 > 0 && hh1 > 0) { bgpixbuf = gdk_pixbuf_new_subpixbuf(BGpixbuf,px1,py1,ww1,hh1); if (bgpixbuf) { gdk_cairo_set_source_pixbuf(cr,bgpixbuf,px2,py2); cairo_paint(cr); g_object_unref(bgpixbuf); } else printz("Fpaint4() bgpixbuf failure \n"); } } gdk_cairo_set_source_pixbuf(cr,pixbuf,px2,py2); // draw area to window cairo_paint(cr); g_object_unref(pixbuf); if (Fshowarea) { px3 = (px2 - Dorgx + Morgx) / Mscale; // back to image scale, expanded py3 = (py2 - Dorgy + Morgy) / Mscale; ww3 = ww2 / Mscale + 2; hh3 = hh2 / Mscale + 2; sa_show_rect(px3,py3,ww3,hh3,cr); // refresh select area outline } if (crflag) draw_context_destroy(draw_context); paintlock(0); return; } // update window area pending from a thread // update_window_area() will be called by gtimefunc() next timer cycle. namespace update_window_area_names { int lock = 0; int pending = 0; int px3a, py3a, ww3a, hh3a; } void update_window_area() // 17.01 { using namespace update_window_area_names; if (! pending) return; resource_lock(lock); cairo_t *cr = draw_context_create(gdkwin,draw_context); // 17.04 Fpaint3(px3a,py3a,ww3a,hh3a,cr); draw_context_destroy(draw_context); // 17.04 pending = 0; resource_unlock(lock); return; } // Fpaint3 callable from threads. // Prepare data about region to update. // Main thread (above) does the window update. void Fpaint3_thread(int px3, int py3, int ww3, int hh3) // 17.01 { using namespace update_window_area_names; resource_lock(lock); if (pending) { if (px3 < px3a) { ww3a += (px3a - px3); px3a = px3; } if (py3 < py3a) { hh3a += (py3a - py3); py3a = py3; } if (px3 + ww3 > px3a + ww3a) ww3a += px3 + ww3 - (px3a + ww3a); if (py3 + hh3 > py3a + hh3a) hh3a += py3 + hh3 - (py3a + hh3a); } pending = 1; resource_unlock(lock); return; } /********************************************************************************/ // F/W view window mouse event function - capture buttons and drag movements void mouse_event(GtkWidget *widget, GdkEventButton *event, void *) { void mouse_convert(int xpos1, int ypos1, int &xpos2, int &ypos2); int button, time, type, scroll; int mxdist, mydist, mdist; static int bdtime = 0, butime = 0; static int mdragx0, mdragy0; char *pp; #define GAPR GDK_AXIS_PRESSURE if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image type = event->type; button = event->button; // button, 1/2/3 = left/center/right time = event->time; Mwxposn = event->x; // mouse position in window Mwyposn = event->y; scroll = ((GdkEventScroll *) event)->direction; // scroll wheel event mouse_convert(Mwxposn,Mwyposn,Mxposn,Myposn); // convert to image space KBcontrolkey = KBshiftkey = KBaltkey = 0; if (event->state & GDK_CONTROL_MASK) KBcontrolkey = 1; if (event->state & GDK_SHIFT_MASK) KBshiftkey = 1; if (event->state & GDK_MOD1_MASK) KBaltkey = 1; if (type == GDK_SCROLL) { // scroll wheel = zoom zoomx = Mxposn; // zoom center = mouse position zoomy = Myposn; if (scroll == GDK_SCROLL_UP) m_zoom(0,"in"); if (scroll == GDK_SCROLL_DOWN) m_zoom(0,"out"); return; } if (type == GDK_BUTTON_PRESS) { // button down Mdrag++; // possible drag start bdtime = time; // time of button down Mbutton = button; mdragx0 = Mwxposn; // window position at button down mdragy0 = Mwyposn; Mxdown = Mxposn; // image position at button down Mydown = Myposn; Mxdrag = Mydrag = 0; } if (type == GDK_MOTION_NOTIFY) { if (Mdrag) { // drag underway Mwdragx = Mwxposn - mdragx0; // drag increment, window space Mwdragy = Mwyposn - mdragy0; mdragx0 = Mwxposn; // new drag origin = current position mdragy0 = Mwyposn; Mxdrag = Mxposn; // drag position, image space Mydrag = Myposn; mouse_dragtime = time - bdtime; // track drag duration gdk_event_get_axis((GdkEvent *) event, GAPR, &wacom_pressure); // wacom tablet stylus pressure 17.04 } else Mwdragx = Mwdragy = Mxdrag = Mydrag = 0; // 18.01 } if (type == GDK_BUTTON_RELEASE) { // button up Mxclick = Myclick = 0; // reset click status butime = time; // time of button up if (butime - bdtime < 400) { // less than 0.4 secs down mxdist = Mxposn - Mxdown; // get mouse movement between mydist = Myposn - Mydown; // button press and release mdist = sqrtf(mxdist * mxdist + mydist * mydist); // if < 10 pixels, 17.04 if (mdist < 10) { // call this a mouse click if (Mbutton == 1) LMclick++; // left mouse click if (Mbutton == 3) RMclick++; // right mouse click Mxclick = Mxdown; // click = button down position Myclick = Mydown; if (button == 2) { // center button click zoomx = Mxposn; // re-center at mouse (Doriano) zoomy = Myposn; gtk_widget_queue_draw(Cdrawin); } } } Mxdown = Mydown = Mxdrag = Mydrag = Mdrag = Mbutton = 0; // forget buttons and drag } Fmousemain = 1; // mouse acts on main window if (Mcapture) Fmousemain = 0; // curr. function handles mouse if (mouseCBfunc) Fmousemain = 0; // mouse owned by callback function if (KBcontrolkey) Fmousemain = 1; // mouse acts on main window if (mouseCBfunc && ! Fmousemain) { // pass to callback function (* mouseCBfunc)(); // remove busy test 17.04 Fmousemain = 1; // click/drag params are processed here } // unless reset by callback func. if (FGWM == 'W') filemap_mousefunc(); // geomap mouse function if (! Fmousemain) return; // curr. function handles mouse if (curr_file && LMclick && FGWM == 'F') { // F-view, left click on image 17.04 pp = strrchr(curr_file,'/'); if (! pp) pp = curr_file; if (strstr(pp,"(fotoxx montage)")) { // click on image montage file, montage_Lclick_func(Mxclick,Myclick); // popup corresp. image file LMclick = 0; } } if (LMclick) { // left click = zoom request LMclick = 0; zoomx = Mxclick; // zoom center = mouse zoomy = Myclick; m_zoom(0,"in"); } if (RMclick) { // right click RMclick = 0; if (Cstate->fzoom) { // if zoomed image, reset to fit window zoomx = zoomy = 0; m_zoom(0,"fit"); } else if (curr_file && FGWM == 'F') image_Rclick_popup(); // image right-click popup menu } if (Mxdrag || Mydrag) // drag = scroll by mouse gtk_widget_queue_draw(Cdrawin); return; } // convert mouse position from window space to image space void mouse_convert(int xpos1, int ypos1, int &xpos2, int &ypos2) { xpos2 = (xpos1 - Cstate->dorgx + Cstate->morgx) / Cstate->mscale + 0.5; ypos2 = (ypos1 - Cstate->dorgy + Cstate->morgy) / Cstate->mscale + 0.5; if (xpos2 < 0) xpos2 = 0; // if outside image put at edge if (ypos2 < 0) ypos2 = 0; if (E3pxm) { if (xpos2 >= E3pxm->ww) xpos2 = E3pxm->ww-1; if (ypos2 >= E3pxm->hh) ypos2 = E3pxm->hh-1; } else { if (xpos2 >= Cstate->fpxb->ww) xpos2 = Cstate->fpxb->ww-1; if (ypos2 >= Cstate->fpxb->hh) ypos2 = Cstate->fpxb->hh-1; } return; } /********************************************************************************/ // set new image zoom level or magnification // zoom: "in" zoom-in in steps // "out" zoom-out in steps // "fit" zoom to fit window // "100" toggle 100% and fit window void m_zoom(GtkWidget *, cchar *zoom) { int fww, fhh; float scalew, scaleh, fitscale, fzoom2, zratio = 1; float Rzoom, pixels; if (! Cstate || ! Cstate->fpxb) return; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image Rzoom = Cstate->fzoom; // current zoom ratio if (E3pxm) { fww = E3pxm->ww; // 1x image size fhh = E3pxm->hh; } else { fww = Cstate->fpxb->ww; fhh = Cstate->fpxb->hh; } if (fww > Dww || fhh > Dhh) { // get window fit scale scalew = 1.0 * Dww / fww; scaleh = 1.0 * Dhh / fhh; if (scalew < scaleh) fitscale = scalew; else fitscale = scaleh; // window/image, < 1.0 } else fitscale = 1.0; // if image < window use 100% if (strmatch(zoom,"Zoom+")) zoom = "in"; // menu button: + = zoom in if (strmatch(zoom,"Zoom-")) zoom = "fit"; // - = fit window if (strmatch(zoom,"fit")) Cstate->fzoom = 0; // zoom to fit window if (strmatch(zoom,"100")) { if (Cstate->fzoom != 0) Cstate->fzoom = 0; // toggle 100% and fit window else Cstate->fzoom = 1; } if (strstr("in out",zoom)) // caller: zoom in or out { if (! Cstate->fzoom) Cstate->fzoom = fitscale; // current zoom scale for (fzoom2 = 0.125; fzoom2 < 4.0; fzoom2 *= zoomratio) // find nearest natural ratio if (Cstate->fzoom < fzoom2 * sqrt(zoomratio)) break; if (strmatch(zoom,"in")) zratio = zoomratio; // zoom in, make image larger if (strmatch(zoom,"out")) zratio = 1.0 / zoomratio; // zoom out, make image smaller Cstate->fzoom = fzoom2 * zratio; if (Cstate->fzoom > 0.124 && Cstate->fzoom < 0.126) // hit these ratios exactly Cstate->fzoom = 0.125; if (Cstate->fzoom > 0.24 && Cstate->fzoom < 0.26) Cstate->fzoom = 0.25; if (Cstate->fzoom > 0.49 && Cstate->fzoom < 0.51) Cstate->fzoom = 0.50; if (Cstate->fzoom > 0.99 && Cstate->fzoom < 1.01) Cstate->fzoom = 1.00; if (Cstate->fzoom > 1.99 && Cstate->fzoom < 2.01) Cstate->fzoom = 2.00; if (Cstate->fzoom > 3.99) Cstate->fzoom = 4.0; // max. allowed zoom if (Cstate->fzoom < fitscale) Cstate->fzoom = 0; // image < window } if (FGWM == 'W') { // optimize for geomaps if (strmatch(zoom,"in") && Cstate->fzoom < 1.0) Cstate->fzoom = 1.0; // zoom to 100% directly if (strmatch(zoom,"out")) Cstate->fzoom = 0.0; // zoom out = fit window directly if (Cstate->fzoom == 1.0 && Rzoom == 1.0) { gtk_widget_queue_draw(Cdrawin); // if already, move mouse pointer only return; } } if (Cstate->fzoom > 1.0) { // limit image in window to 1 gigabyte pixels = Cstate->fzoom * fww * Cstate->fzoom * fhh; // (333 megapixels x 3 bytes/pixel) if (pixels > 333 * MEGA) { // if size is at maximum, Cstate->fzoom = Rzoom; // move mouse pointer only gtk_widget_queue_draw(Cdrawin); return; } } if (! Cstate->fzoom) zoomx = zoomy = 0; // no requested zoom center Fpaint2(); // refresh window return; } /********************************************************************************/ // function for dialogs to call to send KB keys for processing by main app void KBevent(GdkEventKey *event) { KBpress(0,event,0); return; } // keyboard event functions // GDK key symbols: /usr/include/gtk-3.0/gdk/gdkkeysyms.h namespace trimrotate { void KBfunc(int key); } // keyboard functions called from here namespace perspective { void KBfunc(int key); } namespace mashup { void KBfunc(int key); } namespace view360 { void KB_func(int key); } int KBpress(GtkWidget *win, GdkEventKey *event, void *) // keyboard key was pressed { int ii, jj, cc; char shortkey[20] = ""; cchar *action = 0; KBkey = event->keyval; // input key KBcontrolkey = KBshiftkey = KBaltkey = 0; // look for combination keys if (event->state & GDK_CONTROL_MASK) KBcontrolkey = 1; if (event->state & GDK_SHIFT_MASK) KBshiftkey = 1; if (event->state & GDK_MOD1_MASK) KBaltkey = 1; if (KBshiftkey && KBkey == GDK_KEY_plus) KBshiftkey = 0; // treat Shift [+] same as [+] 18.01 if (KBkey == GDK_KEY_equal) KBkey = GDK_KEY_plus; // treat [=] same as [+] if (KBkey == GDK_KEY_KP_Add) KBkey = GDK_KEY_plus; // treat keypad [+] same as [+] if (KBkey == GDK_KEY_KP_Subtract) KBkey = GDK_KEY_minus; // treat keypad [-] same as [-] if (KBkey == GDK_KEY_F1) { // F1 >> user guide showz_userguide(F1_help_topic); // show topic if there, or page 1 return 1; } if (Fslideshow) { // slide show active 18.01 if (KBkey == GDK_KEY_F10) ss_escape = 1; // tell slide show to quit if (KBkey == GDK_KEY_F11) ss_escape = 1; if (KBkey == GDK_KEY_Escape) ss_escape = 1; if (ss_escape) return 1; ss_KBfunc(KBkey); // pass other keys to slide show return 1; } if (Fview360) { // view360 active view360::KB_func(KBkey); // pass KB keys to view360 return 1; } if (KBkey == GDK_KEY_F10) { // F10: fullscreen toggle with menu if (! Ffullscreen) win_fullscreen(0); // toggle full-screen mode and back else win_unfullscreen(); return 1; } if (KBkey == GDK_KEY_F11) { // F11: fullscreen toggle no menu if (! Ffullscreen) win_fullscreen(1); // toggle full-screen mode and back else win_unfullscreen(); return 1; } if (KBkey == GDK_KEY_Escape) { // ESC key if (Ffullscreen) win_unfullscreen(); // exit full screen mode else delete_event(); // quit fotoxx 17.04 return 1; } if (KBkey == GDK_KEY_p && image_file_type(curr_file) == VIDEO) { // 'P' for totem, OK to use elsewhere /// shell_quiet("ffplay -loglevel 8 -autoexit \"%s\" ",curr_file); FIXME // best UI, fails appimage + Wayland /// shell_quiet("gst-launch-1.0 playbin uri=file:\"///%s\" ",curr_file); // works everywhere, bad UI shell_quiet("totem \"%s\" &",curr_file); // works everywhere, adequate UI return 1; } if (KBkey == GDK_KEY_Delete) action = (char *) "Delete"; // reserved shortcuts 18.01 if (KBkey == GDK_KEY_Left) action = (char *) "Left"; if (KBkey == GDK_KEY_Right) action = (char *) "Right"; if (KBkey == GDK_KEY_Up) action = (char *) "Up"; if (KBkey == GDK_KEY_Down) action = (char *) "Down"; if (KBkey == GDK_KEY_Home) action = (char *) "First"; if (KBkey == GDK_KEY_End) action = (char *) "Last"; if (KBkey == GDK_KEY_Page_Up) action = (char *) "Page_Up"; if (KBkey == GDK_KEY_Page_Down) action = (char *) "Page_Down"; if (KBkey == GDK_KEY_h && KBcontrolkey) action = (char *) "Show Hidden"; if (! action) { if (KBkey >= GDK_KEY_F2 && KBkey <= GDK_KEY_F9) { // input key is F2 to F9 ii = KBkey - GDK_KEY_F1; strcpy(shortkey,"F1"); shortkey[1] += ii; } if (! *shortkey && KBkey < 256) // single ascii character { if (KBcontrolkey) strcat(shortkey,"Ctrl+"); // build input key combination if (KBaltkey) strcat(shortkey,"Alt+"); // [Ctrl+] [Alt+] [Shift+] key if (KBshiftkey) strcat(shortkey,"Shift+"); cc = strlen(shortkey); shortkey[cc] = KBkey; shortkey[cc+1] = 0; } if (*shortkey) { // find shortcut key in shortcut list for (ii = 0; ii < Nshortcuts; ii++) if (strmatchcase(shortkey,shortcutkey[ii])) break; if (ii < Nshortcuts) action = shortcutmenu[ii]; // corresp. action or function } } if (! action) return 1; // no match if (strmatch(action,ZTX("File View"))) { // use translations 18.01 m_viewmode(0,"F"); return 1; } if (strmatch(action,ZTX("Gallery View"))) { m_viewmode(0,"G"); return 1; } if (strmatch(action,ZTX("World Map View"))) { m_viewmode(0,"W"); return 1; } if (strmatch(action,ZTX("Net Map View"))) { m_viewmode(0,"M"); return 1; } if (strmatch(action,ZTX("Sync Gallery"))) { m_sync_gallery(0,0); return 1; } if (FGWM == 'G') { // G view mode navi::KBaction(action); // pass KB action to gallery return 1; } if (strmatch(action,ZTX("Show Hidden"))) return 1; // only meaningful in G view if (FGWM == 'W' || FGWM == 'M') return 1; // map view mode, no other KB actions if (KBcapture) return 1; // let current function handle it if (Fmashup) { // mashup active, pass KB key mashup::KBfunc(KBkey); return 1; } if (CEF && CEF->menufunc == m_trim_rotate) { // trim_rotate active, pass KB key trimrotate::KBfunc(KBkey); return 1; } if (CEF && CEF->menufunc == m_perspective) { // perspective active, pass KB key perspective::KBfunc(KBkey); return 1; } if (strmatch(action,"Left")) { // left arrow - previous image m_prev(0,0); return 1; } if (strmatch(action,"Right")) { // right arrow - next image m_next(0,0); return 1; } if (KBkey == GDK_KEY_Delete) { // delete key - delete/trash dialog m_delete_trash(0,0); return 1; } if (strmatch(action,"Zoom-in")) { // zoom center = mouse position zoomx = Mxposn; zoomy = Myposn; m_zoom(0,"in"); return 1; } if (strmatch(action,"Zoom-out")) { // zoom to fit window m_zoom(0,"fit"); return 1; } if (strmatch(action,"Zoom-1x toggle")) { // zoom to 1x / fit window toggle m_zoom(0,"100"); return 1; } // check for menu function shortcut for (jj = 0; jj < Nmenus; jj++) { if (! menutab[jj].menu) continue; // ignore separator 'menu' if (strmatchcase(action,ZTX(menutab[jj].menu))) break; } if (jj == Nmenus) { printz("shortcut not found: %s %s \n",shortkey,action); return 1; } menutab[jj].func(0,menutab[jj].arg); // call the menu function return 1; } int KBrelease(GtkWidget *win, GdkEventKey *event, void *) // KB key released { KBkey = 0; // reset current active key return 1; } /********************************************************************************/ // set the main window to fullscreen status // (with no menu or panel) void win_fullscreen(int hidemenu) { if (FGWM == 'F' && hidemenu) { // if F window, hide menu and panel gtk_widget_hide(Fmenu); gtk_widget_hide(Fpanel); Fpanelshow = 0; } if (hidemenu) gtk_window_fullscreen(MWIN); else gtk_window_maximize(MWIN); // 17.08 while (! Ffullscreen) zmainloop(); return; } // restore window to former size and restore menu etc. void win_unfullscreen() { gtk_window_unfullscreen(MWIN); // restore old window size gtk_window_unmaximize(MWIN); gtk_widget_show(Fmenu); gtk_widget_show(Fpanel); Fpanelshow = 1; while (Ffullscreen) zmainloop(); return; } /********************************************************************************/ // update information in main window title bar namespace meta_names { extern char meta_pdate[16]; // image (photo) date, yyyymmddhhmmss } void set_mwin_title() { GTYPE gtype; int cc, fposn, Nfiles, Nimages; char *pp, titlebar[250]; char pdate[12], ptime[12], pdatetime[24]; char fname[100], gname[100], fdirk[100]; if (FGWM != 'F') return; // 18.01 if (! curr_file || *curr_file != '/') { gtk_window_set_title(MWIN,Arelease); return; } pp = (char *) strrchr(curr_file,'/'); strncpy0(fname,pp+1,99); // file name cc = pp - curr_file; if (cc < 99) strncpy0(fdirk,curr_file,cc+2); // get dirk/path/ if short enough else { strncpy(fdirk,curr_file,96); // or use /dirk/path... strcpy(fdirk+95,"..."); } Nfiles = navi::Nfiles; // total gallery files (incl. directories) Nimages = navi::Nimages; // total image files fposn = file_position(curr_file,curr_file_posn); // curr. file in curr. gallery? if (fposn >= 0) { curr_file_posn = fposn; fposn = fposn + 1 - Nfiles + Nimages; // position among images, 1-based } if (*meta_names::meta_pdate) { metadate_pdate(meta_names::meta_pdate,pdate,ptime); // get formatted date and time strncpy0(pdatetime,pdate,11); // yyyy-mm-dd hh:mm:ss 18.01 strncpy0(pdatetime+11,ptime,9); pdatetime[10] = ' '; } else strcpy(pdatetime,"(undated)"); gtype = navi::gallerytype; if (gtype == GDIR) // gallery name = directory snprintf(titlebar,250," %d/%d %s %s %s", fposn,Nimages,fdirk,fname,pdatetime); else { if (gtype == SEARCH || gtype == META) strcpy(gname,"SEARCH RESULTS"); else if (gtype == ALBUM) { pp = strrchr(navi::galleryname,'/'); if (! pp) pp = navi::galleryname; else pp++; strcpy(gname,"ALBUM: "); strncpy0(gname+7,pp,87); } else if (gtype == RECENT) strcpy(gname,"RECENT FILES"); else if (gtype == NEWEST) strcpy(gname,"NEWEST FILES"); else strcpy(gname,"NO GALLERY"); if (fposn > 0) snprintf(titlebar,250,"%s %d/%d %s %s %s", // window title bar gname,fposn,Nimages,fdirk,fname,pdatetime); else snprintf(titlebar,250,"%s (*)/%d %s %s %s", // image not in gallery gname,Nimages,fdirk,fname,pdatetime); } gtk_window_set_title(MWIN,titlebar); return; } /********************************************************************************/ // draw a pixel using foreground color. // px, py are image space. void draw_pixel(int px, int py, cairo_t *cr, int fat) { int qx, qy; static int pqx, pqy; static uint8 pixel[12]; // 2x2 block of pixels static PIXBUF *pixbuf1 = 0, *pixbuf4 = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! pixbuf1) { pixbuf1 = gdk_pixbuf_new_from_data(pixel,GDKRGB,0,8,1,1,3,0,0); // 1x1 pixels pixbuf4 = gdk_pixbuf_new_from_data(pixel,GDKRGB,0,8,2,2,6,0,0); // 2x2 pixels } if (Cstate->fpxb->nc > 3) // omit transparent pixels if (PXBpix(Cstate->fpxb,px,py)[3] < 128) return; qx = Mscale * px - Morgx; // image to window space qy = Mscale * py - Morgy; if (qx == pqx && qy == pqy) return; // avoid redundant points pqx = qx; pqy = qy; if (qx < 0 || qx > dww-2) return; // keep off image edges if (qy < 0 || qy > dhh-2) return; if (Mscale <= 1 && ! fat) { // write 1x1 pixels 17.08 pixel[0] = LINE_COLOR[0]; pixel[1] = LINE_COLOR[1]; pixel[2] = LINE_COLOR[2]; gdk_cairo_set_source_pixbuf(cr,pixbuf1,qx+Dorgx,qy+Dorgy); cairo_paint(cr); } else { // write 2x2 fat pixels pixel[0] = pixel[3] = pixel[6] = pixel[9] = LINE_COLOR[0]; pixel[1] = pixel[4] = pixel[7] = pixel[10] = LINE_COLOR[1]; pixel[2] = pixel[5] = pixel[8] = pixel[11] = LINE_COLOR[2]; gdk_cairo_set_source_pixbuf(cr,pixbuf4,qx+Dorgx,qy+Dorgy); cairo_paint(cr); } return; } // erase one drawn pixel - restore from window image Mpxb. // px, py are image space. void erase_pixel(int px, int py, cairo_t *cr) { GdkPixbuf *pixbuf; static int pqx, pqy; int qx, qy; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image qx = Mscale * px; // image to window space qy = Mscale * py; if (qx == pqx && qy == pqy) return; // avoid same target pixel pqx = qx; pqy = qy; if (qx < 0 || qx > Mpxb->ww-2) return; // pixel outside scaled image if (qy < 0 || qy > Mpxb->hh-2) return; if (qx < Morgx || qx > Morgx + dww-2) return; // pixel outside drawing window if (qy < Morgy || qy > Morgy + dhh-2) return; pixbuf = gdk_pixbuf_new_subpixbuf(Mpxb->pixbuf,qx,qy,2,2); // 2x2 Mpxb area to copy qx = qx - Morgx + Dorgx; // target pixel in window qy = qy - Morgy + Dorgy; gdk_cairo_set_source_pixbuf(cr,pixbuf,qx,qy); cairo_paint(cr); g_object_unref(pixbuf); return; } /********************************************************************************/ // draw line. // coordinates are image space. // type = 1/2 for solid/dotted line void draw_line(int x1, int y1, int x2, int y2, int type, cairo_t *cr) { float px1, py1, px2, py2; double dashes[2] = { 4, 4 }; double R, G, B; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image px1 = Mscale * x1 - Morgx + Dorgx; // image to window space py1 = Mscale * y1 - Morgy + Dorgy; px2 = Mscale * x2 - Morgx + Dorgx; py2 = Mscale * y2 - Morgy + Dorgy; if (px1 > Dww-2) px1 = Dww-2; // play nice if (py1 > Dhh-2) py1 = Dhh-2; if (px2 > Dww-2) px2 = Dww-2; if (py2 > Dhh-2) py2 = Dhh-2; R = LINE_COLOR[0] / 255.0; // use line color G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } cairo_set_source_rgb(cr,R,G,B); if (type == 2) cairo_set_dash(cr,dashes,2,0); // dotted line else cairo_set_dash(cr,dashes,0,0); cairo_move_to(cr,px1,py1); // draw line cairo_line_to(cr,px2,py2); cairo_stroke(cr); if (crflag) draw_context_destroy(draw_context); return; } // erase line. refresh line path from mpxb window image. // double line width is erased. // coordinates are image space. void erase_line(int x1, int y1, int x2, int y2, cairo_t *cr) { float pxm, pym, slope; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } if (abs(y2 - y1) > abs(x2 - x1)) { slope = 1.0 * (x2 - x1) / (y2 - y1); for (pym = y1; pym <= y2; pym++) { pxm = x1 + slope * (pym - y1); erase_pixel(pxm,pym,cr); } } else { slope = 1.0 * (y2 - y1) / (x2 - x1); for (pxm = x1; pxm <= x2; pxm++) { pym = y1 + slope * (pxm - x1); erase_pixel(pxm,pym,cr); } } if (crflag) draw_context_destroy(draw_context); return; } /********************************************************************************/ // draw pre-set overlay lines on top of image // arg = 1: paint lines only (because window repainted) // 2: erase lines and forget them // 3: erase old lines, paint new lines, save new in old void draw_toplines(int arg, cairo_t *cr) { int ii; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } if (arg == 2 || arg == 3) // erase old lines for (ii = 0; ii < Nptoplines; ii++) erase_line(ptoplines[ii].x1,ptoplines[ii].y1, ptoplines[ii].x2,ptoplines[ii].y2,cr); if (arg == 1 || arg == 3) // draw new lines for (ii = 0; ii < Ntoplines; ii++) draw_line(toplines[ii].x1,toplines[ii].y1, toplines[ii].x2,toplines[ii].y2,toplines[ii].type,cr); if (crflag) draw_context_destroy(draw_context); if (arg == 2) { Nptoplines = Ntoplines = 0; // forget lines return; } for (ii = 0; ii < Ntoplines; ii++) // save for future erase ptoplines[ii] = toplines[ii]; Nptoplines = Ntoplines; return; } /********************************************************************************/ // draw a grid of horizontal and vertical lines. // grid line spacings are in window space. void draw_gridlines(cairo_t *cr) { int G = currgrid; int px, py, gww, ghh; int startx, starty, endx, endy, stepx, stepy; int startx1, starty1; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! gridsettings[G][GON]) return; // grid lines off gww = dww; // grid box size ghh = dhh; startx = Dorgx; // starting corner (top left) starty = Dorgy; if (CEF && strmatch(CEF->funcname,"trim_rotate")) { // trim/rotate function is active gww = Mscale * (trimx2 - trimx1); // fit grid box to trim rectangle ghh = Mscale * (trimy2 - trimy1); startx = Mscale * trimx1 - Morgx + Dorgx; starty = Mscale * trimy1 - Morgy + Dorgy; } endx = startx + gww; endy = starty + ghh; stepx = gridsettings[G][GXS]; // space between grid lines stepy = gridsettings[G][GYS]; // (window space) if (gridsettings[G][GXC]) stepx = gww / (1 + gridsettings[G][GXC]); // if line counts specified, if (gridsettings[G][GYC]) // set spacing accordingly stepy = ghh / ( 1 + gridsettings[G][GYC]); startx1 = startx + stepx * gridsettings[G][GXF] / 100; // variable starting offsets starty1 = starty + stepy * gridsettings[G][GYF] / 100; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } cairo_set_source_rgb(cr,1,1,1); // white lines if (gridsettings[G][GX] && stepx) for (px = startx1; px < endx; px += stepx) { cairo_move_to(cr,px,starty); cairo_line_to(cr,px,endy); } if (gridsettings[G][GY] && stepy) for (py = starty1; py < endy; py += stepy) { cairo_move_to(cr,startx,py); cairo_line_to(cr,endx,py); } cairo_stroke(cr); cairo_set_source_rgb(cr,0,0,0); // adjacent black lines if (gridsettings[G][GX] && stepx) for (px = startx1+1; px < endx+1; px += stepx) { cairo_move_to(cr,px,starty); cairo_line_to(cr,px,endy); } if (gridsettings[G][GY] && stepy) for (py = starty1+1; py < endy+1; py += stepy) { cairo_move_to(cr,startx,py); cairo_line_to(cr,endx,py); } cairo_stroke(cr); if (crflag) draw_context_destroy(draw_context); return; } /********************************************************************************/ // maintain a set of text strings written over the image in the window. // add a new text string to the list. // multiple text strings can be added with the same ID. // px and py are image space. void add_toptext(int ID, int px, int py, cchar *text, cchar *font) { if (Ntoptext == maxtoptext) { printz("*** maxtoptext exceeded \n"); return; } int ii = Ntoptext++; toptext[ii].ID = ID; toptext[ii].px = px; toptext[ii].py = py; toptext[ii].text = text; toptext[ii].font = font; return; } // draw current text strings over the image in window. // called from Fpaint(). void draw_toptext(cairo_t *cr) { int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } for (int ii = 0; ii < Ntoptext; ii++) draw_text(toptext[ii].px,toptext[ii].py,toptext[ii].text,toptext[ii].font,cr); if (crflag) draw_context_destroy(draw_context); return; } // delete text strings having the given ID from the list void erase_toptext(int ID) { int ii, jj; for (ii = jj = 0; ii < Ntoptext; ii++) { if (toptext[ii].ID == ID) continue; else toptext[jj++] = toptext[ii]; } Ntoptext = jj; return; } // draw text on window, black on white background // coordinates are image space void draw_text(int px, int py, cchar *text, cchar *font, cairo_t *cr) { static PangoFontDescription *pangofont = 0; static PangoLayout *pangolayout = 0; static char priorfont[40] = ""; int ww, hh; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; px = Mscale * px - Morgx + Dorgx; // image to window space py = Mscale * py - Morgy + Dorgy; if (! strmatch(font,priorfont)) { // change font strncpy0(priorfont,font,40); if (pangofont) pango_font_description_free(pangofont); if (pangolayout) g_object_unref(pangolayout); pangofont = pango_font_description_from_string(font); // make pango layout for font pangolayout = gtk_widget_create_pango_layout(Cdrawin,0); pango_layout_set_font_description(pangolayout,pangofont); } pango_layout_set_text(pangolayout,text,-1); // add text to layout pango_layout_get_pixel_size(pangolayout,&ww,&hh); if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } cairo_set_source_rgb(cr,1,1,1); // draw white background cairo_rectangle(cr,px,py,ww,hh); cairo_fill(cr); cairo_move_to(cr,px,py); // draw layout with text cairo_set_source_rgb(cr,0,0,0); pango_cairo_show_layout(cr,pangolayout); if (crflag) draw_context_destroy(draw_context); return; } /********************************************************************************/ // maintain a set of circles drawn over the image in the window // px, py are image space, radius is window space void add_topcircle(int px, int py, int radius) { if (Ntopcircles == maxtopcircles) { printz("*** maxtopcircles exceeded \n"); return; } int ii = Ntopcircles++; topcircles[ii].px = px; topcircles[ii].py = py; topcircles[ii].radius = radius; return; } // draw current circles over the image in the window // called from window repaint function Fpaint() void draw_topcircles(cairo_t *cr) { int ii, px, py, rad; double R, G, B; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image R = LINE_COLOR[0] / 255.0; // use LINE_COLOR G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } for (ii = 0; ii < Ntopcircles; ii++) { px = topcircles[ii].px * Mscale - Morgx + Dorgx; // image to window space py = topcircles[ii].py * Mscale - Morgy + Dorgy; rad = topcircles[ii].radius; // radius is window space cairo_set_source_rgb(cr,R,G,B); cairo_arc(cr,px,py,rad,0,2*PI); // draw 360 deg. arc cairo_stroke(cr); } if (crflag) draw_context_destroy(draw_context); return; } // erase top circles (next window repaint) void erase_topcircles() { Ntopcircles = 0; return; } /********************************************************************************/ // Draw circle around the mouse pointer. // Prior circle will be erased first. // Used for mouse/brush radius in select and paint functions. // cx, cy, rad: center and radius of circle in image space. // if Ferase, then erase previous circle only. void draw_mousecircle(int cx, int cy, int rad, int Ferase, cairo_t *cr) { int px3, py3, ww3, hh3; static int ppx3, ppy3, pww3 = 0, phh3; int px, py, pok; double R, G, B; double t, dt, t1, t2; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->mpxb) return; // no image if (pww3 > 0) { // erase prior Fpaint4(ppx3,ppy3,pww3,phh3,cr); // refresh from Mpxb pww3 = 0; } if (Ferase) return; // erase only, done px3 = cx - rad - 2; // convert pointer center + radius py3 = cy - rad - 2; // to block position, width, length ww3 = 2 * rad + 4; hh3 = 2 * rad + 4; ppx3 = px3; // remember pixel block area ppy3 = py3; // to erase in next call pww3 = ww3; phh3 = hh3; cx = cx * Mscale - Morgx + Dorgx; // convert to window coordinates cy = cy * Mscale - Morgy + Dorgy; rad = rad * Mscale; R = LINE_COLOR[0] / 255.0; // use LINE_COLOR G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } cairo_set_source_rgba(cr,R,G,B,1.0); t1 = t2 = -1; // angle limits of arc to draw dt = 1.0 / rad; for (t = 0; t < 2*PI; t += dt) // loop 0-360 degrees { px = cx + rad * cos(t); // pixel on mouse circle py = cy + rad * sin(t); pok = 1; // assume pixel OK to draw if (px < Dorgx || py < Dorgy) pok = 0; // outside image limits if (px >= Dorgx+dww || py >= Dorgy+dhh) pok = 0; if (pok) { // pixel ok, add to arc if (t1 < 0) t1 = t; // start of arc to draw t2 = t; // end of arc, so far } else if (t1 >= 0) { // pixel not ok cairo_arc(cr,cx,cy,rad,t1,t2); // draw accumulated arc cairo_stroke(cr); t1 = t2 = -1; // start over } } if (t1 >= 0) { cairo_arc(cr,cx,cy,rad,t1,t2); // draw rest of arc cairo_stroke(cr); } if (crflag) draw_context_destroy(draw_context); return; } // duplicate for drawing and tracking a 2nd mouse circle // (used by paint/clone to track source pixels being cloned) void draw_mousecircle2(int cx, int cy, int rad, int Ferase, cairo_t *cr) { int px3, py3, ww3, hh3; static int ppx3, ppy3, pww3 = 0, phh3; int px, py, pok; double R, G, B; double t, dt, t1, t2; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->mpxb) return; // no image if (pww3 > 0) { // erase prior Fpaint4(ppx3,ppy3,pww3,phh3,cr); // refresh from Mpxb pww3 = 0; } if (Ferase) return; // erase only, done px3 = cx - rad - 2; // convert pointer center + radius py3 = cy - rad - 2; // to block position, width, length ww3 = 2 * rad + 4; hh3 = 2 * rad + 4; ppx3 = px3; // remember pixel block area ppy3 = py3; // to erase in next call pww3 = ww3; phh3 = hh3; cx = cx * Mscale - Morgx + Dorgx; // convert to window coordinates cy = cy * Mscale - Morgy + Dorgy; rad = rad * Mscale; R = LINE_COLOR[0] / 255.0; // use LINE_COLOR G = LINE_COLOR[1] / 255.0; B = LINE_COLOR[2] / 255.0; if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } cairo_set_source_rgba(cr,R,G,B,1.0); t1 = t2 = -1; // angle limits of arc to draw dt = 1.0 / rad; for (t = 0; t < 2*PI; t += dt) // loop 0-360 degrees { px = cx + rad * cos(t); // pixel on mouse circle py = cy + rad * sin(t); pok = 1; // assume pixel OK to draw if (px < Dorgx || py < Dorgy) pok = 0; // outside image limits if (px >= Dorgx+dww || py >= Dorgy+dhh) pok = 0; if (pok) { // pixel ok, add to arc if (t1 < 0) t1 = t; // start of arc to draw t2 = t; // end of arc, so far } else if (t1 >= 0) { // pixel not ok cairo_arc(cr,cx,cy,rad,t1,t2); // draw accumulated arc cairo_stroke(cr); t1 = t2 = -1; // start over } } if (t1 >= 0) { cairo_arc(cr,cx,cy,rad,t1,t2); // draw rest of arc cairo_stroke(cr); } if (crflag) draw_context_destroy(draw_context); return; } /********************************************************************************/ // Draw ellipse around the mouse pointer. // Prior ellipse will be erased first. // cx, cy, cww, chh: center and axes of ellipse in image space. // if Ferase, then erase previous ellipse only. void draw_mousearc(int cx, int cy, int cww, int chh, int Ferase, cairo_t *cr) { int px3, py3, ww3, hh3; static int ppx3, ppy3, pww3 = 0, phh3; int px, py; float a, b, a2, b2; float x, y, x2, y2; int crflag = 0; if (! Cdrawin) return; if (! gdkwin) return; if (! Cstate || ! Cstate->fpxb) return; // no image paintlock(1); if (! cr) { cr = draw_context_create(gdkwin,draw_context); crflag = 1; } if (pww3 > 0) { // erase prior Fpaint4(ppx3,ppy3,pww3,phh3,cr); // refresh from Mpxb pww3 = 0; } if (Ferase) { if (crflag) draw_context_destroy(draw_context); paintlock(0); return; } px3 = cx - (cww + 2) / 2; // convert pointer center + radius py3 = cy - (chh + 2) / 2; // to block position, width, length ww3 = cww + 2; hh3 = chh + 2; ppx3 = px3; // remember pixel block area ppy3 = py3; // to erase in next call pww3 = ww3; phh3 = hh3; a = cww / 2; // ellipse constants from b = chh / 2; // enclosing rectangle a2 = a * a; b2 = b * b; for (y = -b; y < b; y++) // step through y values, omitting { // curve points covered by x values y2 = y * y; x2 = a2 * (1 - y2 / b2); x = sqrtf(x2); // corresp. x values, + and - py = y + cy; px = cx - x + 0.5; draw_pixel(px,py,cr); // draw 2 points on ellipse px = cx + x + 0.5; draw_pixel(px,py,cr); } for (x = -a; x < a; x++) // step through x values, omitting { // curve points covered by y values x2 = x * x; y2 = b2 * (1 - x2 / a2); y = sqrtf(y2); // corresp. y values, + and - px = x + cx; py = cy - y + 0.5; draw_pixel(px,py,cr); // draw 2 points on ellipse py = cy + y + 0.5; draw_pixel(px,py,cr); } if (crflag) draw_context_destroy(draw_context); paintlock(0); return; } /******************************************************************************** spline curve setup and edit functions Support multiple frames with curves in parallel (edit curve(s) and image mask curve) Usage: Add frame widget to dialog or zdialog. Set up drawing in frame: sd = splcurve_init(frame, callback_func) Initialize no. of curves in frame (1-10): sd->Nspc = n Initialize active flag for curve spc: sd->fact[spc] = 1 Initialize vert/horz flag for curve spc: sd->vert[spc] = hv Initialize anchor points for curve spc: sd->nap[spc], sd->apx[spc][xx], sd->apy[spc][yy] Generate data for curve spc: splcurve_generate(sd,spc) Curves will now be shown and edited inside the frame when window is realized. The callback_func(spc) will be called when curve spc is edited. Change curve in program: set anchor points, call splcurve_generate(sd,spc) Get y-value (0-1) for curve spc and given x-value (0-1): yval = splcurve_yval(sd,spc,xval) If faster access to curve is needed (no interpolation): kk = 1000 * xval; if (kk > 999) kk = 999; yval = sd->yval[spc][kk]; ***/ // initialize for spline curve editing // initial anchor points are pre-loaded into spldat before window is realized spldat * splcurve_init(GtkWidget *frame, void func(int spc)) { int cc = sizeof(spldat); // allocate spc curve data area spldat * sd = (spldat *) zmalloc(cc); memset(sd,0,cc); sd->drawarea = gtk_drawing_area_new(); // drawing area for curves gtk_container_add(GTK_CONTAINER(frame),sd->drawarea); sd->spcfunc = func; // user callback function gtk_widget_add_events(sd->drawarea,GDK_BUTTON_PRESS_MASK); // connect mouse events to drawing area gtk_widget_add_events(sd->drawarea,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(sd->drawarea,GDK_BUTTON1_MOTION_MASK); G_SIGNAL(sd->drawarea,"motion-notify-event",splcurve_adjust,sd); G_SIGNAL(sd->drawarea,"button-press-event",splcurve_adjust,sd); G_SIGNAL(sd->drawarea,"realize",splcurve_resize,sd); // 17.04 G_SIGNAL(sd->drawarea,"draw",splcurve_draw,sd); return sd; } // modify anchor points in curve using mouse int splcurve_adjust(void *, GdkEventButton *event, spldat *sd) { int ww, hh, kk; int mx, my, button, evtype; static int spc, ap, mbusy = 0, Fdrag = 0; // drag continuation logic int minspc, minap, apset = 0; float mxval, myval, cxval, cyval; float dist2, mindist2 = 0; float dist, dx, dy; float minx = 0.01 * splcurve_minx; // % to absolute distance mx = event->x; // mouse position in drawing area my = event->y; evtype = event->type; button = event->button; if (evtype == GDK_MOTION_NOTIFY) { if (mbusy) return 0; // discard excess motion events mbusy++; zmainloop(); mbusy = 0; } if (evtype == GDK_BUTTON_RELEASE) { Fdrag = 0; return 0; } ww = gtk_widget_get_allocated_width(sd->drawarea); // drawing area size hh = gtk_widget_get_allocated_height(sd->drawarea); if (mx < 0) mx = 0; // limit edge excursions if (mx > ww) mx = ww; if (my < 0) my = 0; if (my > hh) my = hh; if (evtype == GDK_BUTTON_PRESS) Fdrag = 0; // left or right click if (Fdrag) // continuation of drag { if (sd->vert[spc]) { mxval = 1.0 * my / hh; // mouse position in curve space myval = 1.0 * mx / ww; } else { mxval = 1.0 * mx / ww; myval = 1.0 * (hh - my) / hh; } if (ap < sd->nap[spc] - 1) { // not the last anchor point dx = sd->apx[spc][ap+1] - mxval; // get distance to next anchor point if (dx < 0.01) return 0; // x-value not increasing, forbid dy = sd->apy[spc][ap+1] - myval; dist = sqrtf(dx * dx + dy * dy); if (dist < minx) return 0; // too close, forbid } if (ap > 0) { // not the first anchor point dx = mxval - sd->apx[spc][ap-1]; // get distance to prior anchor point if (dx < 0.01) return 0; // x-value not increasing, forbid dy = myval - sd->apy[spc][ap-1]; dist = sqrtf(dx * dx + dy * dy); if (dist < minx) return 0; // too close, forbid } apset = 1; // mxval/myval = new node position } else // mouse click or new drag begin { minspc = minap = -1; // find closest curve/anchor point mindist2 = 999999; for (spc = 0; spc < sd->Nspc; spc++) // loop curves { if (! sd->fact[spc]) continue; // not active if (sd->vert[spc]) { mxval = 1.0 * my / hh; // mouse position in curve space myval = 1.0 * mx / ww; } else { mxval = 1.0 * mx / ww; myval = 1.0 * (hh - my) / hh; } for (ap = 0; ap < sd->nap[spc]; ap++) // loop anchor points { cxval = sd->apx[spc][ap]; cyval = sd->apy[spc][ap]; dist2 = (mxval-cxval)*(mxval-cxval) + (myval-cyval)*(myval-cyval); if (dist2 < mindist2) { mindist2 = dist2; // remember closest anchor point minspc = spc; minap = ap; } } } if (minspc < 0) return 0; // impossible spc = minspc; // nearest curve ap = minap; // nearest anchor point } if (evtype == GDK_BUTTON_PRESS && button == 3) // right click, remove anchor point { if (sqrtf(mindist2) > minx) return 0; // not close enough if (sd->nap[spc] < 3) return 0; // < 2 anchor points would remain sd->nap[spc]--; // decr. before loop for (kk = ap; kk < sd->nap[spc]; kk++) { sd->apx[spc][kk] = sd->apx[spc][kk+1]; sd->apy[spc][kk] = sd->apy[spc][kk+1]; } splcurve_generate(sd,spc); // regenerate data for modified curve gtk_widget_queue_draw(sd->drawarea); sd->spcfunc(spc); // call user function return 0; } if (! Fdrag) // new drag or left click { if (sd->vert[spc]) { mxval = 1.0 * my / hh; // mouse position in curve space myval = 1.0 * mx / ww; } else { mxval = 1.0 * mx / ww; myval = 1.0 * (hh - my) / hh; } if (sqrtf(mindist2) < minx) // anchor point close enough, { // move this one to mouse position if (ap < sd->nap[spc]-1) { // not the last anchor point dx = sd->apx[spc][ap+1] - mxval; // get distance to next anchor point if (dx < 0.01) return 0; // x-value not increasing, forbid dy = sd->apy[spc][ap+1] - myval; dist = sqrtf(dx * dx + dy * dy); if (dist < minx) return 0; // too close, forbid } if (ap > 0) { // not the first anchor point dx = mxval - sd->apx[spc][ap-1]; // get distance to prior anchor point if (dx < 0.01) return 0; // x-value not increasing, forbid dy = myval - sd->apy[spc][ap-1]; dist = sqrtf(dx * dx + dy * dy); if (dist < minx) return 0; // too close, forbid } apset = 1; // mxval/myval = new node position } else // none close, add new anchor point { minspc = -1; // find closest curve to mouse mindist2 = 999999; for (spc = 0; spc < sd->Nspc; spc++) // loop curves { if (! sd->fact[spc]) continue; // not active if (sd->vert[spc]) { mxval = 1.0 * my / hh; // mouse position in curve space myval = 1.0 * mx / ww; } else { mxval = 1.0 * mx / ww; myval = 1.0 * (hh - my) / hh; } cyval = splcurve_yval(sd,spc,mxval); dist2 = fabsf(myval - cyval); if (dist2 < mindist2) { mindist2 = dist2; // remember closest curve minspc = spc; } } if (minspc < 0) return 0; // impossible if (mindist2 > minx) return 0; // not close enough to any curve spc = minspc; if (sd->nap[spc] > 49) { zmessageACK(Mwin,ZTX("Exceed 50 anchor points")); return 0; } if (sd->vert[spc]) { mxval = 1.0 * my / hh; // mouse position in curve space myval = 1.0 * mx / ww; } else { mxval = 1.0 * mx / ww; myval = 1.0 * (hh - my) / hh; } for (ap = 0; ap < sd->nap[spc]; ap++) // find anchor point with next higher x if (mxval <= sd->apx[spc][ap]) break; // (ap may come out 0 or nap) if (ap < sd->nap[spc] && sd->apx[spc][ap] - mxval < minx) // disallow < minx from next return 0; // or prior anchor point if (ap > 0 && mxval - sd->apx[spc][ap-1] < minx) return 0; for (kk = sd->nap[spc]; kk > ap; kk--) { // make hole for new point sd->apx[spc][kk] = sd->apx[spc][kk-1]; sd->apy[spc][kk] = sd->apy[spc][kk-1]; } sd->nap[spc]++; // up point count apset = 1; // mxval/myval = new node position } } if (evtype == GDK_MOTION_NOTIFY) Fdrag = 1; // remember drag is underway if (apset) { sd->apx[spc][ap] = mxval; // new or moved anchor point sd->apy[spc][ap] = myval; // at mouse position splcurve_generate(sd,spc); // regenerate data for modified curve if (sd->drawarea) gtk_widget_queue_draw(sd->drawarea); // redraw graph if (sd->spcfunc) sd->spcfunc(spc); // call user function } return 0; } // add a new anchor point to a curve // spc: curve number // px, py: node coordinates in the range 0-1 int splcurve_addnode(spldat *sd, int spc, float px, float py) { int ap, kk; float minx = 0.01 * splcurve_minx; // % to absolute distance for (ap = 0; ap < sd->nap[spc]; ap++) // find anchor point with next higher x if (px <= sd->apx[spc][ap]) break; // (ap may come out 0 or nap) if (ap < sd->nap[spc] && sd->apx[spc][ap] - px < minx) // disallow < minx from next return 0; // or prior anchor point if (ap > 0 && px - sd->apx[spc][ap-1] < minx) return 0; for (kk = sd->nap[spc]; kk > ap; kk--) { // make hole for new point sd->apx[spc][kk] = sd->apx[spc][kk-1]; sd->apy[spc][kk] = sd->apy[spc][kk-1]; } sd->apx[spc][ap] = px; // add node coordinates sd->apy[spc][ap] = py; sd->nap[spc]++; // up point count return 1; } // if height/width too small, make bigger int splcurve_resize(GtkWidget *drawarea) // 17.04 { int ww = gtk_widget_get_allocated_width(drawarea); int hh = gtk_widget_get_allocated_height(drawarea); if (hh < ww/2) gtk_widget_set_size_request(drawarea,ww,ww/2); return 1; } // for expose event or when a curve is changed // draw all curves based on current anchor points int splcurve_draw(GtkWidget *drawarea, cairo_t *cr, spldat *sd) { int ww, hh, spc, ap; float xval, yval, px, py, qx, qy; ww = gtk_widget_get_allocated_width(sd->drawarea); // drawing area size hh = gtk_widget_get_allocated_height(sd->drawarea); if (ww < 50 || hh < 50) return 0; cairo_set_line_width(cr,1); cairo_set_source_rgb(cr,0.7,0.7,0.7); for (int ii = 0; ii < sd->Nscale; ii++) // draw y-scale lines if any { px = ww * sd->xscale[0][ii]; py = hh - hh * sd->yscale[0][ii]; qx = ww * sd->xscale[1][ii]; qy = hh - hh * sd->yscale[1][ii]; cairo_move_to(cr,px,py); cairo_line_to(cr,qx,qy); } cairo_stroke(cr); cairo_set_source_rgb(cr,0,0,0); for (spc = 0; spc < sd->Nspc; spc++) // loop all curves { if (! sd->fact[spc]) continue; // not active if (sd->vert[spc]) // vert. curve { for (py = 0; py < hh; py++) // generate all points for curve { xval = 1.0 * py / hh; yval = splcurve_yval(sd,spc,xval); px = ww * yval; if (py == 0) cairo_move_to(cr,px,py); cairo_line_to(cr,px,py); } cairo_stroke(cr); for (ap = 0; ap < sd->nap[spc]; ap++) // draw boxes at anchor points { xval = sd->apx[spc][ap]; yval = sd->apy[spc][ap]; px = ww * yval; py = hh * xval; cairo_rectangle(cr,px-2,py-2,4,4); } cairo_fill(cr); } else // horz. curve { for (px = 0; px < ww; px++) // generate all points for curve { xval = 1.0 * px / ww; yval = splcurve_yval(sd,spc,xval); py = hh - hh * yval; if (px == 0) cairo_move_to(cr,px,py); cairo_line_to(cr,px,py); } cairo_stroke(cr); for (ap = 0; ap < sd->nap[spc]; ap++) // draw boxes at anchor points { xval = sd->apx[spc][ap]; yval = sd->apy[spc][ap]; px = ww * xval; py = hh - hh * yval; cairo_rectangle(cr,px-2,py-2,4,4); } cairo_fill(cr); } } return 0; } // generate all curve data points when anchor points are modified int splcurve_generate(spldat *sd, int spc) { int kk, kklo, kkhi; float xval, yvalx; spline1(sd->nap[spc],sd->apx[spc],sd->apy[spc]); // compute curve fitting anchor points kklo = 1000 * sd->apx[spc][0] - 30; // xval range = anchor point range if (kklo < 0) kklo = 0; // + 0.03 extra below/above kkhi = 1000 * sd->apx[spc][sd->nap[spc]-1] + 30; if (kkhi > 1000) kkhi = 1000; for (kk = 0; kk < 1000; kk++) // generate all points for curve { xval = 0.001 * kk; // remove anchor point limits yvalx = spline2(xval); if (yvalx < 0) yvalx = 0; // yval < 0 not allowed, > 1 OK sd->yval[spc][kk] = yvalx; } return 0; } // Retrieve curve data using interpolation of saved table of values float splcurve_yval(spldat *sd, int spc, float xval) { int ii; float x1, x2, y1, y2, y3; if (xval <= 0) return sd->yval[spc][0]; if (xval >= 0.999) return sd->yval[spc][999]; x2 = 1000.0 * xval; ii = x2; x1 = ii; y1 = sd->yval[spc][ii]; y2 = sd->yval[spc][ii+1]; y3 = y1 + (y2 - y1) * (x2 - x1); return y3; } // load curve data from a file // returns 0 if succcess, sd is initialized from file data // returns 1 if fail (invalid file data), sd not modified int splcurve_load(spldat *sd, FILE *fid) { char *pfile, *pp, buff[300]; int nn, ii, jj, err, myfid = 0; int Nspc, fact[10], vert[10], nap[10]; float apx[10][50], apy[10][50]; if (! fid) // request file from user { pfile = zgetfile(ZTX("load curve from a file"),MWIN,"file",saved_curves_dirk); if (! pfile) return 1; fid = fopen(pfile,"r"); zfree(pfile); if (! fid) goto fail; myfid = 1; } pp = fgets_trim(buff,300,fid,1); if (! pp) goto fail; nn = sscanf(pp,"%d",&Nspc); // no. of curves if (nn != 1) goto fail; if (Nspc < 1 || Nspc > 10) goto fail; if (Nspc != sd->Nspc) goto fail; for (ii = 0; ii < Nspc; ii++) // loop each curve { pp = fgets_trim(buff,300,fid,1); if (! pp) goto fail; nn = sscanf(pp,"%d %d %d",&fact[ii],&vert[ii],&nap[ii]); // active flag, vert flag, anchors if (nn != 3) goto fail; if (fact[ii] < 0 || fact[ii] > 1) goto fail; if (vert[ii] < 0 || vert[ii] > 1) goto fail; if (nap[ii] < 2 || nap[ii] > 50) goto fail; pp = fgets_trim(buff,300,fid,1); // anchor points: nnn/nnn nnn/nnn ... for (jj = 0; jj < nap[ii]; jj++) // anchor point values { pp = (char *) strField(buff,"/ ",2*jj+1); if (! pp) goto fail; err = convSF(pp,apx[ii][jj],0,1); if (err) goto fail; pp = (char *) strField(buff,"/ ",2*jj+2); err = convSF(pp,apy[ii][jj],0,1); if (err) goto fail; } } if (myfid) fclose(fid); sd->Nspc = Nspc; // copy curve data to caller's arg for (ii = 0; ii < Nspc; ii++) { sd->fact[ii] = fact[ii]; sd->vert[ii] = vert[ii]; sd->nap[ii] = nap[ii]; for (jj = 0; jj < nap[ii]; jj++) { sd->apx[ii][jj] = apx[ii][jj]; sd->apy[ii][jj] = apy[ii][jj]; } } for (ii = 0; ii < Nspc; ii++) // generate curve data from anchor points splcurve_generate(sd,ii); if (sd->drawarea) // redraw all curves gtk_widget_queue_draw(sd->drawarea); return 0; // success fail: if (fid && myfid) fclose(fid); zmessageACK(Mwin,ZTX("curve file is invalid")); return 1; } // save curve data to a file int splcurve_save(spldat *sd, FILE *fid) { char *pfile, *pp; int ii, jj, myfid = 0; if (! fid) { pp = zgetfile(ZTX("save curve to a file"),MWIN,"save",saved_curves_dirk); if (! pp) return 1; pfile = zstrdup(pp,8); zfree(pp); pp = strrchr(pfile,'/'); // force .curve extension if (pp) pp = strrchr(pp,'.'); if (pp) strcpy(pp,".curve"); else strcat(pfile,".curve"); fid = fopen(pfile,"w"); zfree(pfile); if (! fid) return 1; myfid = 1; } fprintf(fid,"%d \n",sd->Nspc); // no. of curves for (ii = 0; ii < sd->Nspc; ii++) // loop each curve { fprintf(fid,"%d %d %d \n",sd->fact[ii],sd->vert[ii],sd->nap[ii]); // activ flag, vert flag, anchors for (jj = 0; jj < sd->nap[ii]; jj++) // anchor point values fprintf(fid,"%.4f/%.4f ",sd->apx[ii][jj],sd->apy[ii][jj]); fprintf(fid,"\n"); } if (myfid) fclose(fid); return 0; } /******************************************************************************** edit transaction management edit_setup() get E0 if none, E0 > E1 > E3 edit_cancel() free (E1 E3 ER) edit_done() E3 > E0, free (E1 ER) add to undo stack edit_undo() E3 > ER, E1 > E3 edit_redo() ER > E3 edit_reset() free ER, E1 > E3 edit_fullsize() free (E1 E3) E0 > E1 > E3 ********************************************************************************* Setup for a new edit transaction Create E1 (edit input) and E3 (edit output) pixmaps from previous edit (E0) or image file (new E0). FprevReq 0 edit full-size image 1 edit preview image unless select area exists Farea 0 select_area is invalid and will be deleted (e.g. rotate) 1 select_area not used but remains valid (e.g. red-eye) 2 select_area can be used and remains valid (e.g. gamma) *********************************************************************************/ int edit_setup(editfunc &EF) { int yn, rww, rhh, ftype; int Fpreview; char *pp; if (! curr_file) return 0; // no image file ftype = image_file_type(curr_file); if (ftype != IMAGE && ftype != RAW) { // not editable 17.08 pp = strrchr(curr_file,'/'); if (pp) pp++; zmessageACK(Mwin,ZTX("File cannot be edited \n %s"),pp); return 0; } if (FGWM != 'F') m_viewmode(0,"F"); // insure file view mode if (checkpend("busy block")) return 0; // blocking function if (CEF && CEF->zd) // if pending edit, complete it zdialog_send_event(CEF->zd,"done"); if (checkpend("edit")) return 0; // failed (HDR etc.) if (URS_pos > maxedits-2) { // undo/redo stack capacity reached zmessageACK(Mwin,ZTX("Too many edits, please save image")); return 0; } if (Fscriptbuild && ! EF.Fscript) { // this function not scriptable zmessageACK(Mwin,ZTX("this function cannot be scripted")); return 0; } free_filemap(); // free map memory for edit usage sa_validate(); // delete area if not valid if (EF.Farea == 0 && sa_stat) { // select area will be lost, warn user yn = zmessageYN(Mwin,ZTX("Select area cannot be kept.\n" "Continue?")); if (! yn) return 0; sa_unselect(); // unselect area if (zdsela) zdialog_free(zdsela); } if (EF.Farea == 2 && sa_stat && sa_stat != 3) { // select area exists and can be used, yn = zmessageYN(Mwin,ZTX("Select area not active.\n" // but not active, ask user "Continue?")); if (! yn) return 0; } if (! E0pxm) { // first edit for this file E0pxm = PXM_load(curr_file,1); // get E0 image (poss. 16-bit color) if (! E0pxm) return 0; curr_file_bpc = f_load_bpc; } paintlock(1); // block window updates if (URS_pos == 0) save_undo(); // initial image >> undo/redo stack Fpreview = 0; // assume no preview if (EF.FprevReq && ! Fzoom) // preview requested by edit func. Fpreview = 1; if (EF.Farea == 2 && sa_stat == 3) // not if select area active Fpreview = 0; if (E0pxm->ww * E0pxm->hh < 2000000) // if image is small, don't use preview Fpreview = 0; if (E0pxm->ww < 1.4 * Dww && E0pxm->hh < 1.4 * Dhh) // if image slightly larger than window, Fpreview = 0; // don't use preview if (Fpreview) { if (Fpxb->ww * Dhh > Fpxb->hh * Dww) { // use preview image 1.4 * window size rww = 1.4 * Dww; if (rww < 1200) rww = 1200; // at least 1200 on one side rhh = 1.0 * rww * Fpxb->hh / Fpxb->ww + 0.5; } else { rhh = 1.4 * Dhh; if (rhh < 1200) rhh = 1200; rww = 1.0 * rhh * Fpxb->ww / Fpxb->hh + 0.5; } if (rww > Fpxb->ww) Fpreview = 0; } if (Fpreview) { E1pxm = PXM_rescale(E0pxm,rww,rhh); // scale image to preview size sa_show(0,0); // hide select area if present } else E1pxm = PXM_copy(E0pxm); // else use full size imagez E3pxm = PXM_copy(E1pxm); // E1 >> E3 CEF = &EF; // set current edit function CEF->Fmods = 0; // image not modified yet CEF->Fpreview = Fpreview; CEF->thread_command = CEF->thread_status = 0; // no thread running CEF->thread_pend = CEF->thread_done = CEF->thread_hiwater = 0; // no work pending or done if (CEF->threadfunc) start_thread(CEF->threadfunc,0); // start thread func if any paintlock(0); // unblock window updates Fpaintnow(); // update image synchronous return 1; } /********************************************************************************/ // print error message if CEF invalid int CEF_invalid() { if (CEF) return 0; printz("CEF invalid \n"); return 1; } /********************************************************************************/ // process edit cancel // keep: retain zdialog, mousefunc, curves void edit_cancel(int keep) { if (CEF_invalid()) return; wrapup_thread(9); // tell thread to quit, wait if (CEF_invalid()) return; paintlock(1); // block window updates PXM_free(E1pxm); // free E1, E3, ER PXM_free(E3pxm); PXM_free(ERpxm); PXM_free(E8pxm); PXM_free(E9pxm); if (! keep) { if (CEF->zd == zd_thread) zd_thread = 0; // thread -> zdialog event if (CEF->curves) zfree(CEF->curves); // free curves data if (CEF->mousefunc == mouseCBfunc) freeMouse(); // if my mouse, free mouse if (CEF->zd) zdialog_free(CEF->zd); // kill dialog } CEF = 0; // no current edit func paintlock(0); // unblock window updates Fpaintnow(); // update image synchronous return; } /********************************************************************************/ // process edit done // keep: retain zdialog, mousefunc, curves void edit_done(int keep) { if (CEF_invalid()) return; wrapup_thread(8); // tell thread to finish, wait if (CEF_invalid()) return; paintlock(1); // block window updates if (CEF->Fmods) { // image was modified PXM_free(E0pxm); E0pxm = E3pxm; // E3 updated image >> E0 E3pxm = 0; PXM_free(E1pxm); // free E1, ER PXM_free(ERpxm); PXM_free(E8pxm); PXM_free(E9pxm); URS_pos++; // next undo/redo stack position URS_max = URS_pos; // image modified - higher mods now obsolete save_undo(); // save undo state (for following undo) if (Fscriptbuild) addscript(CEF); // add to script file } else { // not modified PXM_free(E1pxm); // free E1, E3, ER PXM_free(E3pxm); PXM_free(ERpxm); PXM_free(E8pxm); PXM_free(E9pxm); } if (! keep) { if (CEF->zd == zd_thread) zd_thread = 0; // thread -> zdialog event if (CEF->curves) zfree(CEF->curves); // free curves data if (CEF->mousefunc == mouseCBfunc) freeMouse(); // if my mouse, free mouse if (CEF->zd) zdialog_free(CEF->zd); // kill dialog } CEF = 0; // no current edit func paintlock(0); // unblock window updates Fpaintnow(); // update image synchronous return; } /********************************************************************************/ // Convert from preview mode (window-size pixmaps) to full-size pixmaps. // Called by the edit function prior to edit_done(). void edit_fullsize() { if (CEF_invalid()) return; wait_thread_idle(); // wait for thread idle if (CEF_invalid()) return; if (! CEF->Fpreview) return; // FprevReq ignored if small image paintlock(1); // block window updates PXM_free(E1pxm); // free preview pixmaps PXM_free(E3pxm); E1pxm = PXM_copy(E0pxm); // E0 >> E1, full size image E3pxm = PXM_copy(E1pxm); // E1 >> E3 PXB_free(Cstate->fpxb); Cstate->fpxb = PXM_PXB_copy(E3pxm); // update Fpxb from image E3 Fzoom = 0; paintlock(0); // unblock window updates CEF->Fpreview = 0; return; } // edit undo, redo, reset functions // these apply within an active edit function void edit_undo() { F1_help_topic = "undo_redo"; if (CEF_invalid()) return; wait_thread_idle(); // wait for thread idle if (CEF_invalid()) return; if (! CEF->Fmods) return; // not modified paintlock(1); // block window updates PXM_free(ERpxm); // E3 >> redo copy ERpxm = E3pxm; E3pxm = PXM_copy(E1pxm); // E1 >> E3 CEF->Fmods = 0; // reset image modified status paintlock(0); // unblock window updates Fpaintnow(); // update image synchronous return; } void edit_redo() { F1_help_topic = "undo_redo"; if (CEF_invalid()) return; wait_thread_idle(); // wait for thread idle if (CEF_invalid()) return; if (! ERpxm) return; // no prior undo paintlock(1); // block window updates PXM_free(E3pxm); // redo copy >> E3 E3pxm = ERpxm; ERpxm = 0; CEF->Fmods = 1; // image modified paintlock(0); // unblock window updates Fpaintnow(); // update image synchronous return; } void edit_reset() // reset E3 to E1 status { if (CEF_invalid()) return; wait_thread_idle(); // wait for thread idle if (CEF_invalid()) return; if (! CEF->Fmods) return; // not modified paintlock(1); // block window updates PXM_free(ERpxm); // delete redo copy PXM_free(E3pxm); E3pxm = PXM_copy(E1pxm); // E1 >> E3 CEF->Fmods = 0; // reset image modified status paintlock(0); // unblock window updates Fpaintnow(); // update image synchronous return; } /********************************************************************************/ // Load/save all edit zdialog widgets data from/to a file. // dirname for edit data files: /home//.fotoxx/funcname // where zdialog data is saved for the respective function. // return 0 = OK, +N = error int edit_load_widgets(editfunc *EF, FILE *fid) { using namespace zfuncs; cchar *mess = ZTX("Load settings from file"); zdialog *zd; spldat *sd; int myfid = 0; char *filename, dirname[200], buff[1000]; char *wname, *wdata, wdata2[1000]; char *pp, *pp1, *pp2; int ii, kk, err, cc1, cc2; zd = EF->zd; sd = EF->curves; if (! fid) // fid from script { snprintf(dirname,200,"%s/%s",get_zhomedir(),EF->funcname); // directory for data files filename = zgetfile(mess,MWIN,"file",dirname,0); // open data file if (! filename) return 1; // user cancel fid = fopen(filename,"r"); zfree(filename); if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); return 1; } myfid = 1; } for (ii = 0; ii < zdmaxwidgets; ii++) // read widget data recs { pp = fgets_trim(buff,1000,fid,1); if (! pp) break; if (strmatch(pp,"curves")) { if (! sd) goto baddata; err = splcurve_load(sd,fid); // load curves data if (err) goto baddata; continue; } if (strmatch(pp,"end")) break; pp1 = pp; pp2 = strstr(pp1," =="); if (! pp2) continue; // widget has no data cc1 = pp2 - pp1; if (cc1 > 100) continue; pp1[cc1] = 0; wname = pp1; // widget name pp2 += 3; if (*pp2 == ' ') pp2++; wdata = pp2; // widget data cc2 = strlen(wdata); if (cc2 < 1) wdata = (char *) ""; if (cc2 > 300) continue; repl_1str(wdata,wdata2,"\\n","\n"); // replace "\n" with newline chars. kk = zdialog_put_data(zd,wname,wdata2); if (! kk) goto baddata; } if (myfid) fclose(fid); return 0; baddata: zmessageACK(Mwin,ZTX("file data does not fit dialog")); if (myfid) fclose(fid); return 1; } int edit_save_widgets(editfunc *EF, FILE *fid) { using namespace zfuncs; cchar *mess = ZTX("Save settings to a file"); zdialog *zd; spldat *sd; int myfid = 0; char *filename, dirname[200]; char *wtype, *wname, *wdata, wdata2[1000]; int ii, cc; cchar *editwidgets = "entry zentry edit text togbutt check combo comboE " // widget types to save 17.08 "radio spin zspin hscale vscale colorbutt"; zd = EF->zd; sd = EF->curves; if (! fid) // fid from script { snprintf(dirname,200,"%s/%s",get_zhomedir(),EF->funcname); // directory for data files filename = zgetfile(mess,MWIN,"save",dirname,0); // open data file if (! filename) return 1; // user cancel fid = fopen(filename,"w"); zfree(filename); if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); return 1; } myfid = 1; } for (ii = 0; ii < zdmaxwidgets; ii++) { wtype = (char *) zd->widget[ii].type; if (! wtype) break; if (! strstr(editwidgets,wtype)) continue; wname = (char *) zd->widget[ii].name; // write widget data recs: wdata = zd->widget[ii].data; // widgetname == widgetdata if (! wdata) continue; cc = strlen(wdata); if (cc > 900) continue; repl_1str(wdata,wdata2,"\n","\\n"); fprintf(fid,"%s == %s \n",wname,wdata); } if (sd) { fprintf(fid,"curves\n"); splcurve_save(sd,fid); } fprintf(fid,"end\n"); if (myfid) fclose(fid); return 0; } // functions to support [prev] buttons in edit dialogs // load or save last-used widgets int edit_load_prev_widgets(editfunc *EF) { using namespace zfuncs; char filename[200]; FILE *fid; int err; snprintf(filename,200,"%s/%s/%s",get_zhomedir(),EF->funcname,"last-used"); fid = fopen(filename,"r"); if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); return 1; } err = edit_load_widgets(EF,fid); fclose(fid); return err; } int edit_save_last_widgets(editfunc *EF) { using namespace zfuncs; char filename[200], dirname[200]; FILE *fid; int err; snprintf(filename,200,"%s/%s/%s",get_zhomedir(),EF->funcname,"last-used"); fid = fopen(filename,"w"); if (! fid) { snprintf(dirname,200,"%s/%s",get_zhomedir(),EF->funcname); // create missing directory 17.08 err = mkdir(dirname,0760); if (err) { printz("%s \n %s \n",dirname,strerror(errno)); return 1; } fid = fopen(filename,"w"); // open again } if (! fid) { printz("%s \n %s \n",filename,strerror(errno)); return 1; } err = edit_save_widgets(EF,fid); fclose(fid); return err; } /******************************************************************************** undo / redo menu buttons *********************************************************************************/ // [undo/redo] menu function // Call m_undo() / m_redo() if left / right mouse click on menu. // If A key is pressed, call undo_all() or redo_all(). // If middle mouse button is clicked, pop-up a list of all edit steps // and choose a step to go back to. void m_undo_redo(GtkWidget *, cchar *) { void undo_redo_choice(GtkWidget *, cchar *menu); GtkWidget *popmenu; int button = zfuncs::vmenuclickbutton; char menuitem[40], flag; F1_help_topic = "undo_redo"; if (button == 1) { if (KBkey == GDK_KEY_a) undo_all(); // undo all steps else m_undo(0,0); // undo one step } if (button == 2) // go back to selected edit step { if (URS_max == 0) return; popmenu = create_popmenu(); for (int ii = 0; ii < 30; ii++) { // insert all edit steps if (ii > URS_max) break; if (ii == URS_pos) flag = '*'; // flag step matching current status else flag = ' '; snprintf(menuitem,40,"%d %s %c",ii,URS_funcs[ii],flag); add_popmenu_item(popmenu,menuitem,undo_redo_choice,(char *) &Nval[ii],0); } popup_menu(Mwin,popmenu); } if (button == 3) { if (KBkey == GDK_KEY_a) redo_all(); // redo all steps else m_redo(0,0); // redo one step } return; } // popup menu response function void undo_redo_choice(GtkWidget *, cchar *menu) { int nn = *((int *) menu); if (nn < 0 || nn > URS_max) return; URS_pos = nn; load_undo(); return; } // [undo] menu function - reinstate previous edit in undo/redo stack void m_undo(GtkWidget *, cchar *) { if (CEF) { // undo active edit edit_undo(); return; } if (URS_pos == 0) return; // undo past edit URS_pos--; load_undo(); return; } // [redo] menu function - reinstate next edit in undo/redo stack void m_redo(GtkWidget *, cchar *) { if (CEF) { // redo active edit edit_redo(); return; } if (URS_pos == URS_max) return; // redo past edit URS_pos++; load_undo(); return; } // undo all edits of the current image // (discard modifications) void undo_all() { if (CEF) return; // not if edit active if (URS_pos == 0) return; URS_pos = 0; // original image load_undo(); return; } // redo all edits of the current image // (reinstate all modifications) void redo_all() { if (CEF) return; // not if edit active if (URS_pos == URS_max) return; URS_pos = URS_max;; // image with last edit applied load_undo(); return; } // Save E0 to undo/redo file stack // stack position = URS_pos void save_undo() // overhauled for 4 GB files { char *pp, buff[24]; FILE *fid; int ww, hh, nc, nn; uint cc1, cc2; uint ccmax = 256 * MEGA; ww = E0pxm->ww; hh = E0pxm->hh; nc = E0pxm->nc; pp = strstr(URS_filename,"undo_"); // get undo/redo stack filename to use if (! pp) zappcrash("undo/redo stack corrupted"); snprintf(pp+5,3,"%02d",URS_pos); fid = fopen(URS_filename,"w"); if (! fid) goto writefail; snprintf(buff,24,"fotoxx %05d %05d %d",ww,hh,nc); // write header nn = fwrite(buff,20,1,fid); if (nn != 1) goto writefail; cc1 = ww * hh * nc * sizeof(float); // write ww * hh RGB(A) pixels cc2 = 0; while (cc1) { if (cc1 <= ccmax) { pp = (char *) E0pxm->pixels; nn = fwrite(pp+cc2,cc1,1,fid); if (nn != 1) goto writefail; break; } else { pp = (char *) E0pxm->pixels; nn = fwrite(pp+cc2,ccmax,1,fid); if (nn != 1) goto writefail; cc1 -= ccmax; cc2 += ccmax; } } fclose(fid); if (URS_pos == 0) { // stack posn. 0 = original image strcpy(URS_funcs[0],"original"); // function name for original image URS_saved[0] = 1; // original image already on disk } else { // stack position if (CEF_invalid()) return; // must have an edit function strncpy0(URS_funcs[URS_pos],CEF->funcname,32); // edit function name URS_saved[URS_pos] = 0; // not yet saved to disk } return; writefail: zmessageACK(Mwin,"undo/redo stack write failure: %d \n" "(may be out of disk space)",errno); exit(1); } // Load E0 from undo/redo file stack // stack position = URS_pos void load_undo() // overhauled { char *pp, buff[24]; FILE *fid; int ww, hh, nc, nn; uint cc1, cc2; uint ccmax = 128 * MEGA; // reduced from 256 17.04 pp = strstr(URS_filename,"undo_"); if (! pp) goto err1; snprintf(pp+5,3,"%02d",URS_pos); fid = fopen(URS_filename,"r"); if (! fid) goto err2; nn = fread(buff,20,1,fid); if (nn != 1) goto err3; nn = sscanf(buff,"fotoxx %d %d %d",&ww,&hh,&nc); if (nn != 3) goto err4; PXM_free(E0pxm); E0pxm = PXM_make(ww,hh,nc); cc1 = ww * hh * nc * sizeof(float); cc2 = 0; while (cc1) { if (cc1 <= ccmax) { pp = (char *) E0pxm->pixels; nn = fread(pp+cc2,cc1,1,fid); if (nn != 1) goto err3; break; } else { pp = (char *) E0pxm->pixels; nn = fread(pp+cc2,ccmax,1,fid); if (nn == 0 && errno == 11) { zmessage_post(Mwin,3,"fread() fail, resource unavailable",0); // this shit does happen 17.04 exit(11); // there is no recovery } if (nn != 1) goto err3; cc1 -= ccmax; cc2 += ccmax; } } fclose(fid); sa_validate(); // delete area if invalid if (Cdrawin) Fpaintnow(); // update image synchronous return; err1: printz("err1: %s \n",URS_filename); // extended diagnostics 17.01 goto readfail; err2: printz("err2: open failure, errno: %d \n",errno); goto readfail; err3: printz("err3: fread() failure, errno: %d \n",errno); goto readfail; err4: printz("err4: %s \n",buff); goto readfail; readfail: zmessageACK(Mwin,"undo/redo stack read failure \n"); exit(1); } /********************************************************************************/ // check for busy or pending conditions and message the user // returns 1 if busy or pending condition // 0 if nothing is pending or user decides to discard mods // conditions: // edit edit function is active (CEF not null) // mods current file has unsaved mods (image or metadata edits) // busy function is still running // block edit function mutex flag // all check all the above // quiet suppress user message int checkpend(cchar *list) { int edit, mods, busy, block, all, quiet, pend, choice; cchar *modmess = ZTX("This action will discard changes to current image"); cchar *activemess = ZTX("prior function still active"); cchar *keep = ZTX("Keep"); cchar *discard = ZTX("Discard"); edit = mods = busy = block = all = quiet = pend = 0; if (strstr(list,"edit")) edit = 1; if (strstr(list,"mods")) mods = 1; if (strstr(list,"busy")) busy = 1; if (strstr(list,"block")) block = 1; if (strstr(list,"all")) all = 1; if (strstr(list,"quiet")) quiet = 1; if (all) edit = mods = busy = block = 1; if ((edit && CEF) || (busy && (Ffuncbusy | Fthreadbusy)) || (block && Fblock)) { pend = 1; if (! quiet) zmessage_post_bold(Mwin,2,activemess); } if (! pend && mods) { if (CEF && CEF->Fmods && ! CEF->Fsaved) pend = 1; // active edits unsaved if (URS_pos > 0 && URS_saved[URS_pos] == 0) pend = 1; // completed edits unsaved if (Fmetamod) pend = 1; // metadata edits unsaved if (pend && ! quiet) { choice = zdialog_choose(Mwin,modmess,keep,discard,null); // ask user if (choice == 2) { // choice is discard if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"cancel"); if (URS_pos > 0) undo_all(); // undo prior edits Fmetamod = 0; // discard metadata edits pend = 0; } else m_viewmode(0,"F"); // keep - back to current image } } return pend; // 1 if something pending } /********************************************************************************/ // zdialog mouse capture and release void takeMouse(mcbFunc func, GdkCursor *cursor) // capture mouse for dialog { if (! Cdrawin) return; if (! gdkwin) return; freeMouse(); mouseCBfunc = func; Mcapture = 1; gdk_window_set_cursor(gdkwin,cursor); return; } void freeMouse() // free mouse for main window { if (! Cdrawin) return; if (! gdkwin) return; if (! Mcapture) return; mouseCBfunc = 0; Mcapture = 0; gdk_window_set_cursor(gdkwin,0); // set normal cursor return; } /******************************************************************************** functions to manage working threads main level thread management start_thread(func,arg) start thread running signal_thread() signal thread that work is pending wait_thread_idle() wait for pending work complete wrapup_thread(command) wait for exit or command thread exit thread function thread_idle_loop() wait for pending work, exit if commanded thread_exit() exit thread unconditionally thread_status (thread ownership 0 no thread is running 1 thread is running and idle (no work) 2 thread is working 0 thread has exited thread_command (main program ownership) 0 idle, initial status 8 exit when pending work is done 9 exit now, unconditionally thread_pend work requested counter thread_done work done counter thread_hiwater high water mark *********************************************************************************/ // start thread that does the edit work void start_thread(threadfunc func, void *arg) { CEF->thread_status = 1; // thread is running CEF->thread_command = CEF->thread_pend = CEF->thread_done = CEF->thread_hiwater = 0; // nothing pending start_detached_thread(func,arg); return; } // signal thread that new work is pending void signal_thread() { if (CEF_invalid()) return; if (CEF->thread_status > 0) CEF->thread_pend++; return; } // wait for edit thread to complete pending work and become idle void wait_thread_idle() { if (CEF_invalid()) return; if (! CEF->thread_status) return; if (CEF->thread_pend == CEF->thread_done) return; while (CEF->thread_status && CEF->thread_pend > CEF->thread_done) { zmainloop(); zsleep(0.01); if (CEF_invalid()) return; } return; } // wait for thread exit or command thread exit // command = 0 wait for normal completion // 8 finish pending work and exit // 9 quit, exit now void wrapup_thread(int command) { if (CEF_invalid()) return; if (! CEF->thread_status) return; CEF->thread_command = command; // tell thread to quit or finish while (CEF->thread_status > 0) { // wait for thread to finish zmainloop(); zsleep(0.01); if (CEF_invalid()) return; } return; } // called only from edit threads // idle loop - wait for work request or exit command void thread_idle_loop() { if (CEF_invalid()) thread_exit(); if (CEF->thread_status == 2) zadd_locked(Fthreadbusy,-1); CEF->thread_status = 1; // status = idle CEF->thread_done = CEF->thread_hiwater; // work done = high-water mark while (true) { if (CEF->thread_command == 9) thread_exit(); // quit now command if (CEF->thread_command == 8) // finish work and exit if (CEF->thread_pend <= CEF->thread_done) thread_exit(); if (CEF->thread_pend > CEF->thread_done) break; // wait for work request zsleep(0.01); } CEF->thread_hiwater = CEF->thread_pend; // set high-water mark CEF->thread_status = 2; // thread is working zadd_locked(Fthreadbusy,+1); return; // perform edit } // exit thread unconditionally, called only from edit threads void thread_exit() { wait_wthreads(); // wait for worker threads if any if (CEF_invalid()) pthread_exit(0); if (CEF->thread_status == 2) zadd_locked(Fthreadbusy,-1); CEF->thread_pend = CEF->thread_done = CEF->thread_hiwater = 0; CEF->thread_status = 0; pthread_exit(0); // "return" cannot be used here } /********************************************************************************/ // edit support functions for worker threads (per processor core) void start_wthread(threadfunc func, void *arg) // start thread and increment busy count { // may be called from a thread zadd_locked(Fthreadbusy,+1); zadd_locked(wthreads_busy,+1); start_detached_thread(func,arg); return; } void exit_wthread() // decrement busy count and exit thread { zadd_locked(Fthreadbusy,-1); zadd_locked(wthreads_busy,-1); pthread_exit(0); // "return" cannot be used here } void wait_wthreads(int block) // wait for all worker threads done { // may be called from thread or non-thread if (CEF) { while (wthreads_busy) { zsleep(0.001); if (block) continue; zmainloop(); } if (CEF_invalid()) return; } else { while (wthreads_busy) { zsleep(0.0003); if (block) continue; zmainloop(); } } return; } /********************************************************************************/ // table for loading and saving adjustable parameters between sessions typedef struct { char name[20]; char type[12]; int count; void *location; } param; #define Nparms 49 param paramTab[Nparms] = { // name type count location { "fotoxx release", "char", 1, &Prelease }, { "first time", "int", 1, &Ffirsttime }, { "session count", "int", 1, &sessioncount }, { "window geometry", "int", 4, &mwgeom }, { "thumbnail size", "int", 1, &navi::thumbsize }, { "menu style", "char", 1, &menu_style }, { "F-base-color", "int", 3, &FBrgb }, { "G-base-color", "int", 3, &GBrgb }, { "menu font color", "int", 3, &MFrgb }, { "menu background", "int", 3, &MBrgb }, { "index level", "int", 1, &Findexlev }, { "FM index level", "int", 1, &FMindexlev }, { "icon size", "int", 1, &iconsize }, { "dialog font", "char", 1, &dialog_font }, { "drag option", "int", 1, &Fdragopt }, { "zoom count", "int", 1, &zoomcount }, { "map_dotsize", "int", 1, &map_dotsize }, { "last version", "int", 1, &Flastversion }, { "shift right", "int", 1, &Fshiftright }, { "xmeta changed", "int", 1, &xmeta_changed }, { "curve node dist %", "float", 1, &splcurve_minx }, { "start display", "char", 1, &startdisplay }, { "start album", "char", 1, &startalbum }, { "start image file", "char", 1, &startfile }, { "start directory", "char", 1, &startdirk }, { "last curr file", "char", 1, &last_curr_file }, { "last galleryname", "char", 1, &last_galleryname }, { "last gallerytype", "int", 1, &last_gallerytype }, { "copy/move location", "char", 1, ©move_loc }, { "color palette file", "char", 1, &color_palette_file }, { "netmap source", "char", 1, &netmap_source }, { "mapbox access key", "char", 1, &mapbox_access_key }, { "grid base", "int", 10, &gridsettings[0] }, { "grid trim/rotate", "int", 10, &gridsettings[1] }, { "grid unbend", "int", 10, &gridsettings[2] }, { "grid warp curved", "int", 10, &gridsettings[3] }, { "grid warp linear", "int", 10, &gridsettings[4] }, { "grid warp affine", "int", 10, &gridsettings[5] }, { "RAW file types", "char", 1, &RAWfiletypes }, { "video file types", "char", 1, &VIDEOfiletypes }, // bugfix 17.08.3 { "trim width", "int", 1, &trimww }, { "trim height", "int", 1, &trimhh }, { "trim buttons", "char", 6, &trimbuttons }, { "trim ratios", "char", 6, &trimratios }, { "edit resize", "int", 2, &editresize }, { "jpeg def quality", "int", 1, &jpeg_def_quality }, { "lens mm", "float", 1, &lens_mm }, { "SS KB keys", "char", 1, &ss_KBkeys }, // 18.01 { "printer color map", "char", 1, &colormapfile } }; // save parameters to file /.../.fotoxx/parameters void save_params() { FILE *fid; char buff[1050], text[1000]; // limit for character data cc char *name, *type; int count; void *location; char **charloc; int *intloc; float *floatloc; last_curr_file = last_galleryname = 0; // save curr_file and gallery poop last_gallerytype = TNONE; // for next session startup if (curr_file && *curr_file == '/') last_curr_file = zstrdup(curr_file); if (navi::galleryname) { last_galleryname = zstrdup(navi::galleryname); last_gallerytype = navi::gallerytype; } snprintf(buff,199,"%s/parameters",get_zhomedir()); // open output file fid = fopen(buff,"w"); if (! fid) return; for (int ii = 0; ii < Nparms; ii++) // write table of state data { name = paramTab[ii].name; type = paramTab[ii].type; count = paramTab[ii].count; location = paramTab[ii].location; charloc = (char **) location; intloc = (int *) location; floatloc = (float *) location; fprintf(fid,"%-20s %-8s %02d ",name,type,count); // write parm name, type, count for (int kk = 0; kk < count; kk++) // write "value" "value" ... { if (strmatch(type,"char")) { if (! *charloc) break; // missing, discontinue this parameter repl_1str(*charloc++,text,"\n","\\n"); // replace newlines with "\n" fprintf(fid," \"%s\"",text); } if (strmatch(type,"int")) fprintf(fid," \"%d\"",*intloc++); if (strmatch(type,"float")) fprintf(fid," \"%.2f\"",*floatloc++); } fprintf(fid,"\n"); // write EOR } fprintf(fid,"\n"); fclose(fid); // close file return; } // load parameters from file /.../.fotoxx/parameters void load_params() { FILE *fid; int ii, err, pcount; int Idata; float Fdata; char buff[1000], text[1000], *pp; char name[20], type[12], count[8], data[1000]; void *location; char **charloc; int *intloc; float *floatloc; snprintf(buff,199,"%s/parameters",get_zhomedir()); // open parameters file fid = fopen(buff,"r"); if (! fid) return; // none, defaults are used while (true) // read parameters { pp = fgets_trim(buff,999,fid,1); if (! pp) break; if (*pp == '#') continue; // comment if (strlen(pp) < 40) continue; // rubbish err = 0; strncpy0(name,pp,20); // parm name strTrim2(name); strncpy0(type,pp+22,8); // parm type strTrim2(type); strncpy0(count,pp+32,4); // parm count strTrim2(count); err = convSI(count,pcount); strncpy0(data,pp+38,1000); // parm value(s) strTrim2(data); for (ii = 0; ii < Nparms; ii++) // match file record to param table { if (! strmatch(name,paramTab[ii].name)) continue; // parm name if (! strmatch(type,paramTab[ii].type)) continue; // parm type if (paramTab[ii].count != pcount) continue; // parm count break; } if (ii == Nparms) continue; // not found, ignore file param location = paramTab[ii].location; // get parameter memory location charloc = (char **) location; intloc = (int *) location; floatloc = (float *) location; for (ii = 1; ii <= pcount; ii++) // get parameter value(s) { pp = (char *) strField(data,' ',ii); if (! pp) break; if (strmatch(type,"char")) { repl_1str(pp,text,"\\n","\n"); // replace "\n" with real newlines *charloc++ = zstrdup(text); } if (strmatch(type,"int")) { err = convSI(pp,Idata); if (err) continue; *intloc++ = Idata; } if (strmatch(type,"float")) { err = convSF(pp,Fdata); if (err) continue; *floatloc++ = Fdata; } } } fclose(fid); for (ii = 0; ii < Nparms; ii++) // set any null strings to "" { if (! strmatch(paramTab[ii].type,"char")) continue; charloc = (char **) paramTab[ii].location; pcount = paramTab[ii].count; for (int jj = 0; jj < pcount; jj++) if (! charloc[jj]) charloc[jj] = zstrdup("",20); } zoomratio = pow( 2.0, 1.0 / zoomcount); // set zoom ratio from zoom count return; } /********************************************************************************/ // free all resources associated with the current image file void free_resources(int fkeepundo) { paintlock(1); // block window updates if (! fkeepundo) shell_quiet("rm -f %s/undo_*",tempdir); // remove undo/redo files if (Fshutdown) return; // stop here if shutdown mode URS_pos = URS_max = 0; // reset undo/redo stack Fmetamod = 0; // no unsaved metadata changes sa_unselect(); // unselect select area Nptoplines = Ntoplines = 0; // no toplines Ntopcircles = 0; // no topcircles Ntoptext = 0; // no toptext Fbusy_goal = 0; // not busy if (curr_file) { if (zdsela) zdialog_free(zdsela); // kill select area dialog if active freeMouse(); // free mouse zfree(curr_file); // free image file curr_file = 0; *paneltext = 0; } gtk_window_set_title(MWIN,Arelease); // sparse title PXB_free(Fpxb); PXM_free(E0pxm); PXM_free(E1pxm); PXM_free(E3pxm); PXM_free(ERpxm); PXM_free(E8pxm); PXM_free(E9pxm); paintlock(0); // unblock window updates return; } /********************************************************************************/ // compare two floats for significant difference // signf: e.g. 0.01 for 1% threshold of significance // return: 0 difference not significant // +1 d1 > d2 // -1 d1 < d2 int sigdiff(float d1, float d2, float signf) { float diff = fabsf(d1-d2); if (diff == 0.0) return 0; diff = diff / (fabsf(d1) + fabsf(d2)); if (diff < signf) return 0; if (d1 > d2) return 1; else return -1; } fotoxx-18.01.1/f.meta.cc0000644000175000017500000142455313222767271013361 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image editor - image metadata functions. View and edit metadata ---------------------- select_meta_keys dialog to select metadata items m_meta_view_short metadata short report m_meta_view_long report all metadata m_meta_edit primary edit metadata dialog manage_tags maintain list of defined tags m_meta_edit_any dialog to fetch and save any image metadata by name m_meta_delete dialog to delete any image file metadata by name m_captions write captions and comments at top of current image m_batch_tags batch add and delete tags for selected image files m_batch_rename_tags convert tag names for all image files using a from-to list m_batch_photo_date_time change or shift photo date/time m_batch_change_metadata add/change or delete metadata for selected image files m_batch_report_metadata batch metadata report to text file m_batch_geotags add given geotag to selected set of images Image search utilities ---------------------- m_locations find images by location and date range m_timeline find images by year and month m_search_images find images using any metadata and/or file names pdate_metadate convert yyyy-mm-dd to yyyymmdd ptime_metatime convert hh:mm[:ss] to hhmmss pdatetime_metadatetime convert yyyy-mm-dd hh:mm[:ss] to yyyymmddhhmmss metadate_pdate convert yyyymmddhhmmss to yyyy-mm-dd and hh:mm:ss datetimeOK validate yyyy-mm-dd hh:mm[:ss] date/time add_tag add tag to a tag list add_tag_fotoxx add tag "fotoxx" for edited image (for f_save()) set_meta_size set image size metadata del_tag remove tag from a tag list add_recentag add tag to recent tags list, remove oldest if needed load_deftags load defined tags list from tags file and image index save_deftags save defined tags list to tags file find_deftag check if given tag is in defined tags list add_deftag add new tag to defined tags list or change category del_deftag remove tag from defined tags list deftags_stuff stuff defined tags into dialog text widget defcats_stuff stuff defined categories into dialog combobox widget tag_orphans report tags defined and not used in any image file load_filemeta load image file metadata into memory (indexed data only) save_filemeta save metadata to image file EXIF and to image index update_image_index update index data for current image file delete_image_index delete index record for deleted image file web_geocode web service to map location to earth coordinates init_geolocs load geolocations table from image index file find_location find locations matching partial location/country input put_geolocs put new location data in geolocations table validate_latlong validate earth coordinates data earth_distance compute km distance between two earth coordinates get_gallerymap get map coordinates for current gallery files m_set_map_markers set map markers: all images or current gallery Geotag mapping (W view) ----------------------- m_load_filemap load a geographic map file chosen by user filemap_position convert earth coordinates to map pixel position filemap_coordinates convert map pixel position to earth coordinates filemap_paint_dots paint red dots on map where images are located filemap_mousefunc respond to mouse movement and left clicks on map find_filemap_images find images within range of geolocation free_filemap free huge memory for filemap image Geotag mapping (M view) ----------------------- m_netmap_source choose net map source m_load_netmap initialize net map netmap_paint_dots paint red dots on map where images are located m_netmap_zoomin zoom net map in on image location netmap_zoomto callable with input zoom level netmapscale get net map scale at zoom level netmap_mousefunc respond to clicks on net map find_netmap_images find images m_netmap_locs save and recall net map locs (center, zoom level) EXIF store and retrieve ----------------------- exif_get get image metadata from list of keys exif_put update image metadata from list of keys and data exif_copy copy metadata from file to file, with revisions exif_server start exiftool server process, send data requests exif_tagdate yyyy:mm:dd hh:mm:ss to yyyymmddhhmmss tag_exifdate yyyymmddhhmmss to yyyy:mm:dd hh:mm:ss Image index functions --------------------- get_xxrec get image index record for image file put_xxrec add or update index record for an image file read_xxrec_seq read all index records sequentially, one per call write_xxrec_seq write all index records sequentially *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ char *pdate_metadate(cchar *pdate); // "yyyy-mm-dd" to "yyyymmdd" char *ptime_metatime(cchar *ptime); // "hh:mm[:ss]" to "hhmmss" char *pdatetime_metadatetime(cchar *pdatetime); // yyyy-mm-dd hh:mm[:ss] to yyyymmddhhmmss void manage_tags(); // manage tags dialog int add_tag(char *tag, char *taglist, int maxcc); // add tag if unique and enough space int del_tag(char *tag, char *taglist); // remove tag from tag list int add_recentag(char *tag); // add tag to recent tags, keep recent void load_deftags(); // tags_defined file >> tags_deftags[] void save_deftags(); // tags_deftags[] >> defined_tags file int find_deftag(char *tag); // find tag in tags_deftags[] int add_deftag(char *catg, char *tag); // add tag to tags_deftags[] int del_deftag(char *tag); // remove tag from tags_deftags[] void deftags_stuff(zdialog *zd, cchar *catg); // tags_deftags[] >> zd widget deftags void defcats_stuff(zdialog *zd); // defined categories >> " widget defcats cchar *web_geocode(zdialog *zd); // find earth coordinates via web service int init_geolocs(); // load geolocs table from index file int geolocs_compare(cchar *rec1, cchar *rec2); // compare geolocs recs location int find_location(zdialog *zd); // find locations matching partial inputs int put_geolocs(zdialog *zd); // Update geolocations table in memory int validate_latlong(char *lati, char *longi, float &flat, float &flong); // convert and validate earth coordinates float earth_distance(float lat1, float long1, float lat2, float long2); // compute distance from earth coordinates int get_gallerymap(); // get map coordinates for gallery files void netmap_zoomto(float flati, float flongi, int zoomlev); // zoom net map to location and zoom level float netmapscale(int zoomlev, float flat, float flong); // net map scale at given zoom and location namespace meta_names { char meta_pdate[16]; // image (photo) date, yyyymmddhhmmss char meta_rating[4]; // image rating in stars, "0" to "5" char meta_size[16]; // image size, "NNNNxNNNN" char meta_tags[tagFcc]; // tags for current image file char meta_comments[exif_maxcc]; // image comments expanded char meta_caption[exif_maxcc]; // image caption char meta_location[100], meta_country[100]; // geolocs: location, country char meta_lati[20], meta_longi[20]; // geolocs: earth coordinates (-123.4567) char p_meta_pdate[16]; // previous file metadata char p_meta_rating[4]; char p_meta_tags[tagFcc]; char p_meta_comments[exif_maxcc]; char p_meta_caption[exif_maxcc]; char p_meta_location[100], p_meta_country[100]; char p_meta_lati[20], p_meta_longi[20]; char *xmeta_data[Mxmeta]; // indexed metadata (xmeta_key[]) 18.01 char *tags_deftags[maxtagcats]; // defined tags: catg: tag1, ... tagN, char tags_recentags[tagRcc] = ""; // recently added tags list char keyname[40], keydata[exif_maxcc]; zdialog *zd_mapgeotags = 0; // zdialog wanting geotags via map click struct geolocs_t { // geolocations table, memory DB char *location, *country; // maps locations <-> earth coordinates float flati, flongi; // " float, 7 digit precision }; geolocs_t **geolocs = 0; // geolocs table int Ngeolocs = 0; // size of geolocations table struct gallerymap_t { // geocoordinates for gallery files char *file; float flati, flongi; }; gallerymap_t *gallerymap = 0; int Ngallerymap = 0; } using namespace meta_names; /********************************************************************************/ // Dialog to select metadata items (for index, view, report). // The item list is an input and an output. EOL marker is null. // Fexclude: exclude items indexed by default. // returns 0/1 = no changes / changes made namespace select_meta_keys_names { GtkWidget *mtext1, *mtext2; } int select_meta_keys(char *itemlist[], int Fexclude) { using namespace select_meta_keys_names; void select_meta_keys_clickfunc1(GtkWidget *, int line, int pos, int kbkey); void select_meta_keys_clickfunc2(GtkWidget *, int line, int pos, int kbkey); zdialog *zd; char itemfile[200], buff[100], buff2[100], *pp; FILE *fid; int zstat, ii; /*** __________________________________________________________ | Add Metadata Items | | | | click to select click to unselect | | _________________________ __________________________ | | | Orientation | | | | | | Rotation | | | | | | Exposure Time | | | | | | Aperture | | | | | | ... | | | | | | Other Item ... | | | | | |_________________________| |__________________________| | | | | [done] [cancel] | |__________________________________________________________| ***/ zd = zdialog_new(ZTX("Add Metadata Items"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"expand|space=3"); zdialog_add_widget(zd,"label","lab1","vb1",ZTX("click to select")); zdialog_add_widget(zd,"scrwin","scroll1","vb1",0,"expand"); zdialog_add_widget(zd,"text","mtext1","scroll1",0,"expand"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"expand|space=3"); zdialog_add_widget(zd,"label","lab2","vb2",ZTX("click to unselect")); zdialog_add_widget(zd,"scrwin","scroll2","vb2",0,"expand"); zdialog_add_widget(zd,"text","mtext2","scroll2",0,"expand"); mtext1 = zdialog_widget(zd,"mtext1"); textwidget_clear(mtext1); mtext2 = zdialog_widget(zd,"mtext2"); textwidget_clear(mtext2); snprintf(itemfile,200,"%s/metadata_short_list",get_zhomedir()); // metadata item list >> left widget fid = fopen(itemfile,"r"); if (! fid) { zmessageACK(Mwin,"%s \n %s",itemfile,strerror(errno)); return 0; } while (true) { pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (*pp <= ' ') continue; if (Fexclude) { strCompress(buff2,buff); if (strmatch(buff2,iptc_keywords_key)) continue; // exclude those items which are 18.01 if (strmatch(buff2,iptc_rating_key)) continue; // indexed by default if (strmatch(buff2,exif_date_key)) continue; if (strmatch(buff2,exif_comment_key)) continue; if (strmatch(buff2,exif_usercomment_key)) continue; if (strmatch(buff2,iptc_caption_key)) continue; if (strmatch(buff2,exif_city_key)) continue; if (strmatch(buff2,exif_country_key)) continue; if (strmatch(buff2,exif_lati_key)) continue; if (strmatch(buff2,exif_longi_key)) continue; if (strmatch(buff2,"Location")) continue; // synonym for "City" } textwidget_append(mtext1,0,"%s\n",buff); } textwidget_append(mtext1,0,"%s\n","Other Item ..."); // append "Other Item ..." fclose(fid); textwidget_scroll(mtext1,0); for (ii = 0; ii < Mxmeta; ii++) // input item list >> right widget 18.01 { if (! itemlist[ii]) break; textwidget_append(mtext2,0,"%s\n",itemlist[ii]); } textwidget_set_callbackfunc(mtext1,select_meta_keys_clickfunc1); // connect mouse clicks textwidget_set_callbackfunc(mtext2,select_meta_keys_clickfunc2); zdialog_resize(zd,500,300); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,0,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for dialog completion if (zstat != 1) { // canceled zdialog_free(zd); return 0; // return "no change" } for (ii = 0; ii < Mxmeta; ii++) { // free input list if (! itemlist[ii]) break; zfree(itemlist[ii]); } for (ii = 0; ii < Mxmeta; ii++) { // replace input list with output 18.01 pp = textwidget_line(mtext2,ii,1); // remove trailing \n if (! pp || ! *pp) break; itemlist[ii] = zstrdup(pp); } itemlist[ii] = 0; // mark EOL zdialog_free(zd); return 1; // return "changes made" 18.01 } // get clicked tag name from input list and insert into output list void select_meta_keys_clickfunc1(GtkWidget *widget, int line, int pos, int kbkey) { using namespace select_meta_keys_names; char *pp; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } pp = textwidget_line(widget,line,1); // get clicked line, highlight if (! pp || ! *pp) return; textwidget_highlight_line(widget,line); if (strmatch(pp,"Other Item ...")) { // get manually input metadata name pp = zdialog_text(Mwin,"metadata item name",0); if (! pp) return; strCompress(pp); // remove blanks textwidget_append(mtext2,0,"%s\n",pp); // append to output list zfree(pp); return; } strCompress(pp); // remove blanks textwidget_append(mtext2,0,"%s\n",pp); // append to output list return; } // get clicked tag name from output list and remove it void select_meta_keys_clickfunc2(GtkWidget *widget, int line, int pos, int kbkey) { using namespace select_meta_keys_names; char *pp1, *pp2; int ii, Nitems = 0; char *itemlist[Mxmeta]; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } pp1 = textwidget_line(widget,line,1); // get clicked line if (! pp1 || ! *pp1) return; for (line = 0; ; line++) { // read and save all lines pp2 = textwidget_line(mtext2,line,1); if (! pp2 || ! *pp2) break; if (strmatch(pp2,pp1)) continue; // skip clicked line itemlist[Nitems] = zstrdup(pp2); if (++Nitems == Mxmeta) break; } textwidget_clear(mtext2); // clear output list for (ii = 0; ii < Nitems; ii++) // write new output list { textwidget_append(mtext2,0,"%s\n",itemlist[ii]); zfree(itemlist[ii]); } return; } /********************************************************************************/ // menu function and popup dialog to show EXIF/IPTC data // window is updated when navigating to another image int metadata_report_type = 1; // called by f_open() if zdexifview is defined void meta_view(int type) { if (type && metadata_report_type != type) { if (zdexifview) zdialog_free(zdexifview); zdexifview = 0; } if (type) metadata_report_type = type; if (metadata_report_type == 2) m_meta_view_long(0,0); else m_meta_view_short(0,0); return; } // menu function - short report void m_meta_view_short(GtkWidget *, cchar *menu) { int meta_view_dialog_event(zdialog *zd, cchar *event); #define NK 20 cchar *keyname[NK] = { "ImageSize", "FileSize", exif_date_key, "FileModifyDate", "Make", "Model", exif_focal_length_key, "ExposureTime", "FNumber", "ISO", exif_city_key, exif_country_key, exif_lati_key, exif_longi_key, iptc_keywords_key, iptc_rating_key, iptc_caption_key, exif_comment_key, exif_usercomment_key, exif_editlog_key }; char *keyval[NK]; char chsec[12], **text; static char *file, *filen, *chsize; float fsize, fsec; int err, ii, nn; cchar *textdelims = "!-,.:;?/)}]"; cchar *editdelims = ":|,"; GtkWidget *widget; FILE *fid; int nkx; char *keynamex[NK], *keyvalx[NK]; char extras_file[200], buff[100], *pp; F1_help_topic = "view_metadata"; if (metadata_report_type != 1) { if (zdexifview) zdialog_free(zdexifview); zdexifview = 0; metadata_report_type = 1; } if (clicked_file) { // use clicked file if present 18.01 file = clicked_file; clicked_file = 0; } else if (curr_file) file = zstrdup(curr_file); else return; if (FGWM == 'G') gallery(0,"paint",-1); // if gallery view, repaint if (! zdexifview) // popup dialog if not already { zdexifview = zdialog_new(ZTX("View Metadata"),Mwin,ZTX("Extras"),Bcancel,null); zdialog_add_widget(zdexifview,"scrwin","scroll","dialog",0,"expand"); zdialog_add_widget(zdexifview,"text","exifdata","scroll",0,"expand"); zdialog_resize(zdexifview,550,350); zdialog_run(zdexifview,meta_view_dialog_event,"save"); } widget = zdialog_widget(zdexifview,"exifdata"); textwidget_clear(widget); err = exif_get(file,keyname,keyval,NK); if (err) { zmessageACK(Mwin,"exif failure"); return; } filen = strrchr(file,'/'); // get file name without directory if (filen) filen++; else filen = file; fsize = 0; if (keyval[1]) fsize = atof(keyval[1]); // get file size in B/KB/MB units chsize = formatKBMB(fsize,3); if (keyval[2] && strlen(keyval[2]) > 19) keyval[2][19] = 0; // truncate dates to yyyy-mm-dd hh:mm:ss if (keyval[3] && strlen(keyval[3]) > 19) keyval[3][19] = 0; pp = keyval[2]; if (pp && strlen(pp) > 4 && pp[4] == ':') pp[4] = '-'; // change yyyy:mm:dd to yyyy-mm-dd if (pp && strlen(pp) > 7 && pp[7] == ':') pp[7] = '-'; pp = keyval[3]; if (pp && strlen(pp) > 4 && pp[4] == ':') pp[4] = '-'; if (pp && strlen(pp) > 7 && pp[7] == ':') pp[7] = '-'; textwidget_append(widget,0,"File %s \n",filen); textwidget_append(widget,0,"Size %s %s \n",keyval[0],chsize); textwidget_append(widget,0,"Dates photo: %s file: %s \n",keyval[2],keyval[3]); if (keyval[4] || keyval[5]) textwidget_append(widget,0,"Camera %s %s \n",keyval[4],keyval[5]); if (keyval[6] || keyval[7] || keyval[8] || keyval[9]) // photo exposure data { strcpy(chsec,"null"); if (keyval[7]) { fsec = atof(keyval[7]); // convert 0.008 seconds to 1/125 etc. if (fsec > 0 && fsec <= 0.5) { fsec = 1/fsec; snprintf(chsec,12,"1/%.0f",fsec); } else if (fsec > 0) snprintf(chsec,12,"%.0f",fsec); } textwidget_append(widget,0,"Exposure %s mm %s sec F%s ISO %s \n", keyval[6],chsec,keyval[8],keyval[9]); } if (keyval[10] || keyval[11] || keyval[12] || keyval[13]) // geotag data textwidget_append(widget,0,"Location %s %s %s %s \n", keyval[10],keyval[11],keyval[12],keyval[13]); if (keyval[14]) textwidget_append(widget,0,"Keywords %s \n",keyval[14]); // tags if (keyval[15]) // rating textwidget_append(widget,0,"Rating %s \n",keyval[15]); if (keyval[16]) { // caption-abstract nn = breakup_text(keyval[16],text,textdelims,40,60); textwidget_append(widget,0,"Caption %s \n",text[0]); for (ii = 1; ii < nn; ii++) textwidget_append(widget,0," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } if (keyval[17]) { // comment nn = breakup_text(keyval[17],text,textdelims,40,60); textwidget_append(widget,0,"Comment %s \n",text[0]); for (ii = 1; ii < nn; ii++) textwidget_append(widget,0," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } if (keyval[18]) { // usercomment nn = breakup_text(keyval[18],text,textdelims,40,60); textwidget_append(widget,0,"UserComment %s \n",text[0]); for (ii = 1; ii < nn; ii++) textwidget_append(widget,0," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } if (keyval[19]) { // edit history log nn = breakup_text(keyval[19],text,editdelims,40,60); textwidget_append(widget,0,"Edits %s \n",text[0]); for (ii = 1; ii < nn; ii++) textwidget_append(widget,0," %s \n",text[ii]); for (ii = 0; ii < nn; ii++) zfree(text[ii]); zfree(text); } for (ii = 0; ii < NK; ii++) // free memory if (keyval[ii]) zfree(keyval[ii]); // append extra report items if any snprintf(extras_file,200,"%s/metadata_view_extra",get_zhomedir()); fid = fopen(extras_file,"r"); if (! fid) goto finished; // no extras file for (nkx = 0; nkx < NK; nkx++) { // get list of user extras pp = fgets_trim(buff,100,fid,1); if (! pp) break; strCompress(pp); if (*pp <= ' ') { nkx--; continue; } keynamex[nkx] = zstrdup(pp); } fclose(fid); if (nkx == 0) goto finished; // empty file err = exif_get(file,(cchar **) keynamex,keyvalx,nkx); // get all items at once if (err) { zmessageACK(Mwin,"exif failure"); goto finished; } textwidget_append(widget,0,"\n"); // blank line for (ii = 0; ii < nkx; ii++) // report user extra items if (keyvalx[ii]) textwidget_append(widget,0,"%-24s : %s \n",keynamex[ii],keyvalx[ii]); for (ii = 0; ii < nkx; ii++) { // free memory if (keynamex[ii]) zfree(keynamex[ii]); if (keyvalx[ii]) zfree(keyvalx[ii]); } finished: textwidget_scroll(widget,0); // back to top of report zfree(file); // 18.01 return; } // menu function - long report void m_meta_view_long(GtkWidget *, cchar *menu) { int meta_view_dialog_event(zdialog *zd, cchar *event); char *file, *buff; int contx = 0; GtkWidget *widget; F1_help_topic = "view_metadata"; if (metadata_report_type != 2) { if (zdexifview) zdialog_free(zdexifview); zdexifview = 0; metadata_report_type = 2; } if (clicked_file) { // use clicked file if present 18.01 file = clicked_file; clicked_file = 0; } else if (curr_file) file = zstrdup(curr_file); else return; if (FGWM == 'G') gallery(0,"paint",-1); // if gallery view, repaint if (! zdexifview) // popup dialog if not already { zdexifview = zdialog_new(ZTX("View Metadata"),Mwin,Bcancel,null); zdialog_add_widget(zdexifview,"scrwin","scroll","dialog",0,"expand"); zdialog_add_widget(zdexifview,"text","exifdata","scroll",0,"expand|wrap"); zdialog_resize(zdexifview,700,700); zdialog_run(zdexifview,meta_view_dialog_event,"save"); } widget = zdialog_widget(zdexifview,"exifdata"); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),0); // disable widget editing gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_NONE); // disable text wrap textwidget_clear(widget); snprintf(command,CCC,"exiftool -s -e \"%s\" ",file); while ((buff = command_output(contx,command))) { // run command, output into window textwidget_append(widget,0,"%s\n",buff); zfree(buff); } command_status(contx); textwidget_scroll(widget,0); // back to top of report zfree(file); // 18.01 return; } // dialog event and completion callback function int meta_view_dialog_event(zdialog *zd, cchar *event) { char filex[200]; int zstat, ii; char *itemlist[Mxmeta], *pp, buff[100]; FILE *fid; zstat = zd->zstat; if (! zstat) return 1; // wait for completion zdialog_free(zd); // kill dialog zdexifview = 0; if (metadata_report_type != 1) return 1; // not short report if (zstat != 1) return 1; // not [extras] button snprintf(filex,200,"%s/metadata_view_extra",get_zhomedir()); // [extras] button itemlist[0] = 0; // extra metadata items = empty fid = fopen(filex,"r"); // read file of extra metadata items 18.01 if (fid) { for (ii = 0; ii < Mxmeta; ii++) { pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (*pp <= ' ') continue; itemlist[ii] = zstrdup(pp); } itemlist[ii] = 0; // mark EOL fclose(fid); } select_meta_keys(itemlist,1); // user edit of extras list 18.01 fid = fopen(filex,"w"); // update extras list file 18.01 if (! fid) { zmessageACK(Mwin,"%s \n %s",filex,strerror(errno)); return 1; } for (ii = 0; ii < Mxmeta; ii++) { if (! itemlist[ii]) break; fprintf(fid,"%s\n",itemlist[ii]); } fclose(fid); return 1; } /********************************************************************************/ // edit metadata menu function void m_meta_edit(GtkWidget *, cchar *menu) { void edit_imagetags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void edit_recentags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void edit_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void edit_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int editmeta_dialog_event(zdialog *zd, cchar *event); cchar *mapquest = "Geocoding by MapQuest"; GtkWidget *widget; zdialog *zd; char *ppv, pdate[12], ptime[12]; char cctext[exif_maxcc+50]; char RN[4] = "R0"; int ii; F1_help_topic = "edit_metadata"; if (clicked_file) { // use clicked file if present if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry 18.01 f_open(clicked_file,0,0,1,0); clicked_file = 0; } if (! curr_file) { if (zdeditmeta) zdialog_free(zdeditmeta); zdeditmeta = 0; zd_mapgeotags = 0; return; } if (! init_geolocs()) return; // initialize geotags if (FGWM == 'G') gallery(0,"paint",-1); // if gallery view, repaint /*** ___________________________________________________________ | Edit Metadata | | | | Image File: filename.jpg | | Image Date: [__________] Time: [________] [prev] | | Rating (stars): (o) 1 (o) 2 (o) 3 (o) 4 (o) 5 (o) 6 | | Caption [______________________________________________] | | Comments [_____________________________________________] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | location [_______________] country [______________] | | latitude [__________] longitude [__________] | | [Find] [Web] [Prev] [Clear] Geocoding by MapQuest | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Current Tags [_________________________________________] | | Recent Tags [__________________________________________] | | Enter New Tag [__________] [Add] | | Matching Tags [________________________________________] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Defined Tags Category [______________________________|v] | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |______________________________________________________| | | | | [Manage Tags] [Prev] [Apply] [Cancel] | |___________________________________________________________| ***/ if (! zdeditmeta) // (re) start edit dialog { zd = zdialog_new(ZTX("Edit Metadata"),Mwin,Bmanagetags,Bprev,Bapply,Bcancel,null); zdeditmeta = zd; zdialog_add_ttip(zd,Bapply,ZTX("save metadata to file")); // Image File: xxxxxxxxx.jpg zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labf","hbf",ZTX("Image File:"),"space=3"); zdialog_add_widget(zd,"label","file","hbf","filename.jpg","space=5"); // Image Date [__________] Time [_____] [prev] zdialog_add_widget(zd,"hbox","hbdt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labdate","hbdt",ZTX("Image Date"),"space=3"); zdialog_add_widget(zd,"zentry","date","hbdt",0,"size=12"); zdialog_add_widget(zd,"label","space","hbdt",0,"space=5"); zdialog_add_widget(zd,"label","labtime","hbdt",ZTX("Time"),"space=3"); zdialog_add_widget(zd,"zentry","time","hbdt",0,"size=8"); zdialog_add_widget(zd,"button","ppdate","hbdt",Bprev,"space=8"); zdialog_add_ttip(zd,"date","yyyy-mm-dd"); zdialog_add_ttip(zd,"time","hh:mm[:ss]"); // Rating (stars): ʘ 1 ʘ 2 ʘ 3 ʘ 4 ʘ 5 ʘ 6 zdialog_add_widget(zd,"hbox","hbrate","dialog"); zdialog_add_widget(zd,"label","labrate","hbrate",ZTX("Rating (stars):"),"space=3"); zdialog_add_widget(zd,"radio","R0","hbrate","0","space=6"); zdialog_add_widget(zd,"radio","R1","hbrate","1","space=6"); zdialog_add_widget(zd,"radio","R2","hbrate","2","space=6"); zdialog_add_widget(zd,"radio","R3","hbrate","3","space=6"); zdialog_add_widget(zd,"radio","R4","hbrate","4","space=6"); zdialog_add_widget(zd,"radio","R5","hbrate","5","space=6"); zdialog_add_widget(zd,"hbox","space","dialog",0,"space=3"); // Caption [___________________________________________] zdialog_add_widget(zd,"hbox","hbcap","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labcap","hbcap",ZTX("Caption"),"space=3"); zdialog_add_widget(zd,"edit","caption","hbcap",0,"wrap|expand"); // Comments [__________________________________________] zdialog_add_widget(zd,"hbox","hbcom","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labcom","hbcom",ZTX("Comments"),"space=3"); zdialog_add_widget(zd,"edit","comments","hbcom",0,"wrap|expand"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // location [_____________] country [____________] zdialog_add_widget(zd,"hbox","hbcc","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labloc","hbcc",ZTX("Location"),"space=5"); zdialog_add_widget(zd,"zentry","location","hbcc",0,"expand"); zdialog_add_widget(zd,"label","space","hbcc",0,"space=5"); zdialog_add_widget(zd,"label","labcountry","hbcc",ZTX("Country"),"space=5"); zdialog_add_widget(zd,"zentry","country","hbcc",0,"expand"); // latitude [__________] longitude [__________] zdialog_add_widget(zd,"hbox","hbll","dialog"); zdialog_add_widget(zd,"label","lablat","hbll","Latitude","space=3"); zdialog_add_widget(zd,"zentry","lati","hbll",0,"size=10"); zdialog_add_widget(zd,"label","space","hbll",0,"space=5"); zdialog_add_widget(zd,"label","lablong","hbll","Longitude","space=3"); zdialog_add_widget(zd,"zentry","longi","hbll",0,"size=10"); // [Find] [Web] [Prev] [Clear] Geocoding by MapQuest zdialog_add_widget(zd,"hbox","hbgeo","dialog",0,"space=3"); zdialog_add_widget(zd,"button","geofind","hbgeo",Bfind,"space=5"); zdialog_add_widget(zd,"button","geoweb","hbgeo",Bweb,"space=5"); zdialog_add_widget(zd,"button","geoprev","hbgeo",Bprev,"space=5"); // 17.01 zdialog_add_widget(zd,"button","geoclear","hbgeo",Bclear,"space=5"); zdialog_add_widget(zd,"label","labmq","hbgeo",mapquest,"space=3"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // Image Tags [________________________________________] zdialog_add_widget(zd,"hbox","hbit","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labit","hbit",ZTX("Image Tags"),"space=3"); zdialog_add_widget(zd,"text","imagetags","hbit",0,"wrap|expand"); // Recent Tags [_______________________________________] zdialog_add_widget(zd,"hbox","hbrt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labrt","hbrt",ZTX("Recent Tags"),"space=3"); zdialog_add_widget(zd,"text","recentags","hbrt",0,"wrap|expand"); // Enter New Tag [________________] [Add] zdialog_add_widget(zd,"hbox","hbnt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labnt","hbnt",ZTX("Enter New Tag"),"space=3"); zdialog_add_widget(zd,"zentry","newtag","hbnt",0,"size=20"); zdialog_add_widget(zd,"zbutton","add","hbnt",Badd,"space=5"); // Matching Tags [_____________________________________] zdialog_add_widget(zd,"hbox","hbmt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labmt","hbmt",ZTX("Matching Tags"),"space=3"); zdialog_add_widget(zd,"text","matchtags","hbmt",0,"wrap|expand"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // Defined Tags Category zdialog_add_widget(zd,"hbox","hbdt1","dialog"); zdialog_add_widget(zd,"label","labdt","hbdt1",ZTX("Defined Tags Category"),"space=3"); zdialog_add_widget(zd,"label","space","hbdt1",0,"expand"); zdialog_add_widget(zd,"combo","defcats","hbdt1",0,"expand|space=5"); zdialog_add_widget(zd,"hbox","hbdt2","dialog",0,"expand"); zdialog_add_widget(zd,"scrwin","swdt2","hbdt2",0,"expand"); zdialog_add_widget(zd,"text","deftags","swdt2",0,"wrap"); zdialog_add_ttip(zd,"geofind",ZTX("search known locations")); zdialog_add_ttip(zd,"geoweb",ZTX("search using web service")); zdialog_add_ttip(zd,Bmanagetags,ZTX("create tag categories and tags")); load_deftags(); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories widget = zdialog_widget(zd,"imagetags"); // tag widget mouse functions textwidget_set_callbackfunc(widget,edit_imagetags_clickfunc); widget = zdialog_widget(zd,"recentags"); textwidget_set_callbackfunc(widget,edit_recentags_clickfunc); widget = zdialog_widget(zd,"matchtags"); textwidget_set_callbackfunc(widget,edit_matchtags_clickfunc); widget = zdialog_widget(zd,"deftags"); textwidget_set_callbackfunc(widget,edit_deftags_clickfunc); zdialog_resize(zd,400,700); // run dialog zdialog_run(zd,editmeta_dialog_event,"parent"); } zd = zdeditmeta; // edit metadata active zd_mapgeotags = zd; // map clicks active ppv = (char *) strrchr(curr_file,'/'); zdialog_stuff(zd,"file",ppv+1); // stuff dialog fields from curr. file metadate_pdate(meta_pdate,pdate,ptime); // "yyyymmddhhmmss" to zdialog_stuff(zd,"date",pdate); // "yyyy-mm-dd" and "hh:mm:ss" zdialog_stuff(zd,"time",ptime); for (ii = 0; ii <= 5; ii++) { // set all rating radio buttons OFF RN[1] = '0' + ii; zdialog_stuff(zd,RN,0); } RN[1] = meta_rating[0]; // set radio button ON for current rating zdialog_stuff(zd,RN,1); repl_1str(meta_caption,cctext,"\\n","\n"); zdialog_stuff(zd,"caption",cctext); repl_1str(meta_comments,cctext,"\\n","\n"); zdialog_stuff(zd,"comments",cctext); if (strmatch(meta_location,"null")) zdialog_stuff(zd,"location",""); // geotags data else zdialog_stuff(zd,"location",meta_location); // if null, stuff blank if (strmatch(meta_country,"null")) zdialog_stuff(zd,"country",""); else zdialog_stuff(zd,"country",meta_country); if (strmatch(meta_lati,"null")) zdialog_stuff(zd,"lati",""); else zdialog_stuff(zd,"lati",meta_lati); if (strmatch(meta_longi,"null")) zdialog_stuff(zd,"longi",""); else zdialog_stuff(zd,"longi",meta_longi); zdialog_stuff(zd,"imagetags",meta_tags); zdialog_stuff(zd,"recentags",tags_recentags); return; } // mouse click functions for various text widgets for tags void edit_imagetags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // existing image tag was clicked { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; del_tag(txtag,meta_tags); // remove tag from image zdialog_stuff(zdeditmeta,"imagetags",meta_tags); Fmetamod++; // note change zfree(txtag); return; } void edit_recentags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // recent tag was clicked { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; add_tag(txtag,meta_tags,tagFcc); // add recent tag to image zdialog_stuff(zdeditmeta,"imagetags",meta_tags); Fmetamod++; // note change zfree(txtag); return; } void edit_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // matching tag was clicked { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; add_tag(txtag,meta_tags,tagFcc); // add matching tag to image Fmetamod++; // note change add_recentag(txtag); // and add to recent tags zdialog_stuff(zdeditmeta,"imagetags",meta_tags); // update dialog widgets zdialog_stuff(zdeditmeta,"recentags",tags_recentags); zdialog_stuff(zdeditmeta,"newtag",""); zdialog_stuff(zdeditmeta,"matchtags",""); zdialog_goto(zdeditmeta,"newtag"); // put focus back on newtag widget zfree(txtag); return; } void edit_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // defined tag was clicked { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;:",end); if (! txtag || end == ':') return; // tag category clicked, ignore add_tag(txtag,meta_tags,tagFcc); // add new tag to image zdialog_stuff(zdeditmeta,"imagetags",meta_tags); // from defined tags list Fmetamod++; // note change add_recentag(txtag); // and add to recent tags zdialog_stuff(zdeditmeta,"recentags",tags_recentags); zfree(txtag); return; } // dialog event and completion callback function int editmeta_dialog_event(zdialog *zd, cchar *event) // overhauled { char pdate[12], ptime[12]; // yyyy-mm-dd and hh:mm:ss char *metadate, *metatime; // yyyymmdd and hhmmss cchar *errmess; int ii, jj, nt, cc1, cc2, ff, err; char *pp1, *pp2; char catgname[tagcc]; char newtag[tagcc], matchtags[20][tagcc]; char matchtagstext[(tagcc+2)*20]; char cctext[exif_maxcc+50]; char RN[4] = "R0"; char location[100], country[100], lati[20], longi[20]; float flati, flongi; if (! curr_file) zd->zstat = 4; // current file gone if (strmatch(event,"cancel")) zd->zstat = 4; if (strstr("date time caption comments",event)) // note change but process later Fmetamod++; if (strmatch(event,"ppdate")) { // repeat last date used if (*p_meta_pdate) { metadate_pdate(p_meta_pdate,pdate,ptime); zdialog_stuff(zd,"date",pdate); zdialog_stuff(zd,"time",ptime); Fmetamod++; return 1; } } if (strstr("R0 R1 R2 R3 R4 R5",event)) { // note if rating changed Fmetamod++; return 1; } if (strstr("location country lati longi",event)) { // dialog inputs changed Fmetamod++; return 1; } if (strmatch(event,"geomap")) { // have geotags data from map click Fmetamod++; return 1; } if (strmatch(event,"geofind")) // [find] search location data { find_location(zd); // find location data via user Fmetamod++; return 1; } if (strmatch(event,"geoweb")) // [web] search location data { errmess = web_geocode(zd); // look-up in web service if (errmess) zmessageACK(Mwin,errmess); // fail else Fmetamod++; return 1; } if (strmatch(event,"geoprev")) // [prev] stuff previous geotags 17.01 { zdialog_stuff(zd,"location",p_meta_location); // get last-used geotags zdialog_stuff(zd,"country",p_meta_country); zdialog_stuff(zd,"lati",p_meta_lati); zdialog_stuff(zd,"longi",p_meta_longi); Fmetamod++; } if (strmatch(event,"geoclear")) // [clear] location data { zdialog_stuff(zd,"location",""); // erase dialog fields zdialog_stuff(zd,"country",""); zdialog_stuff(zd,"lati",""); zdialog_stuff(zd,"longi",""); Fmetamod++; return 1; } if (strmatch(event,"defcats")) { // new tag category selection zdialog_fetch(zd,"defcats",catgname,tagcc); deftags_stuff(zd,catgname); } if (strmatch(event,"newtag")) // new tag is being typed in { zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog zdialog_fetch(zd,"newtag",newtag,tagcc); // get chars. typed so far cc1 = strlen(newtag); for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters if (strchr(",:;",newtag[ii])) continue; newtag[jj++] = newtag[ii]; } if (jj < cc1) { // something was removed newtag[jj] = 0; cc1 = jj; zdialog_stuff(zd,"newtag",newtag); } if (cc1 < 2) return 1; // wait for at least 2 chars. for (ii = nt = 0; ii < maxtagcats; ii++) // loop all categories { pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, if (! pp2) continue; // | | pp2 = strchr(pp2,':'); // pp1 pp2 while (true) // loop all deftags in category { pp1 = pp2 + 2; if (! *pp1) break; pp2 = strchr(pp1,','); if (! pp2) break; if (strmatchcaseN(newtag,pp1,cc1)) { // deftag matches chars. typed so far cc2 = pp2 - pp1; strncpy(matchtags[nt],pp1,cc2); // save deftags that match matchtags[nt][cc2] = 0; if (++nt == 20) return 1; // quit if 20 matches or more } } } if (nt == 0) return 1; // no matches pp1 = matchtagstext; for (ii = 0; ii < nt; ii++) // matchtag list: aaaaa, bbb, cccc ... { strcpy(pp1,matchtags[ii]); pp1 += strlen(pp1); strcpy(pp1,", "); pp1 += 2; } zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog return 1; } if (strmatch(event,"add")) // enter new tag finished { zdialog_fetch(zd,"newtag",newtag,tagcc); // get finished tag cc1 = strlen(newtag); if (! cc1) return 1; if (newtag[cc1-1] == '\n') { // remove newline character cc1--; newtag[cc1] = 0; } for (ii = ff = 0; ii < maxtagcats; ii++) // loop all categories { pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, if (! pp2) continue; // | | pp2 = strchr(pp2,':'); // pp1 pp2 while (true) // loop all deftags in category { pp1 = pp2 + 2; if (! *pp1) break; pp2 = strchr(pp1,','); if (! pp2) break; cc2 = pp2 - pp1; if (cc2 != cc1) continue; if (strmatchcaseN(newtag,pp1,cc1)) { // entered tag matches deftag strncpy(newtag,pp1,cc1); // use deftag upper/lower case ff = 1; break; } } if (ff) break; } add_tag(newtag,meta_tags,tagFcc); // add to image tag list Fmetamod++; // note change add_recentag(newtag); // and add to recent tags if (! ff) { // if new tag, add to defined tags add_deftag((char *) "nocatg",newtag); deftags_stuff(zd,"ALL"); } zdialog_stuff(zd,"newtag",""); // update dialog widgets zdialog_stuff(zd,"imagetags",meta_tags); zdialog_stuff(zd,"recentags",tags_recentags); zdialog_stuff(zd,"matchtags",""); zdialog_goto(zd,"newtag"); // put focus back on newtag widget return 1; } if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) { // [manage tags] zd->zstat = 0; // keep dialog active zdialog_show(zd,0); // hide parent dialog manage_tags(); return 1; } if (zd->zstat == 2) // [prev] stuff previous file data { zd->zstat = 0; // keep dialog active if (! *meta_pdate && *p_meta_pdate) { // stuff photo date only if none metadate_pdate(p_meta_pdate,pdate,ptime); zdialog_stuff(zd,"date",pdate); zdialog_stuff(zd,"time",ptime); } for (ii = 0; ii <= 5; ii++) { // stuff rating RN[1] = '0' + ii; // radio buttons, "R0" to "R5" if (RN[1] == *p_meta_rating) zdialog_stuff(zd,RN,1); // for ratings "0" to "5" else zdialog_stuff(zd,RN,0); } zdialog_stuff(zd,"location",p_meta_location); // get last-used geotags zdialog_stuff(zd,"country",p_meta_country); zdialog_stuff(zd,"lati",p_meta_lati); zdialog_stuff(zd,"longi",p_meta_longi); zdialog_stuff(zd,"imagetags",p_meta_tags); // stuff tags strncpy0(meta_tags,p_meta_tags,tagFcc); repl_1str(p_meta_caption,cctext,"\\n","\n"); // stuff caption zdialog_stuff(zd,"caption",cctext); repl_1str(p_meta_comments,cctext,"\\n","\n"); // stuff comments zdialog_stuff(zd,"comments",cctext); Fmetamod++; return 1; } if (zd->zstat != 3) { // [cancel] or [x] zdialog_free(zd); // kill dialog zdeditmeta = 0; zd_mapgeotags = 0; // deactivate map clicks Fmetamod = 0; return 1; } zd->zstat = 0; // [apply] - keep dialog active gtk_window_present(MWIN); // return focus to main window if (! Fmetamod) return 1; // no metadata changes zdialog_fetch(zd,"date",pdate,12); // get photo date and time zdialog_fetch(zd,"time",ptime,12); if (*pdate) { // date available metadate = pdate_metadate(pdate); // validate if (! metadate) return 1; // bad, re-input strcpy(meta_pdate,metadate); // convert to yyyymmdd if (*ptime) { // time available metatime = ptime_metatime(ptime); // validate if (! metatime) return 1; // bad, re-input strcat(meta_pdate,metatime); // append hhmmss } } else *meta_pdate = 0; // leave empty strcpy(meta_rating,"0"); for (ii = 0; ii <= 5; ii++) { // get which rating radio button ON RN[1] = '0' + ii; zdialog_fetch(zd,RN,jj); if (jj) meta_rating[0] = '0' + ii; // set corresponding rating } zdialog_fetch(zd,"caption",cctext,exif_maxcc); // get new caption repl_1str(cctext,meta_caption,"\n","\\n"); // replace newlines with "\n" zdialog_fetch(zd,"comments",cctext,exif_maxcc); // get new comments repl_1str(cctext,meta_comments,"\n","\\n"); // replace newlines with "\n" zdialog_fetch(zd,"location",location,100); // get location from dialog zdialog_fetch(zd,"country",country,100); *location = toupper(*location); // capitalize *country = toupper(*country); zdialog_stuff(zd,"location",location); zdialog_stuff(zd,"country",country); zdialog_fetch(zd,"lati",lati,20); // get latitude, longitude zdialog_fetch(zd,"longi",longi,20); if (*lati > ' ' && ! strmatch(lati,"null") && // if coordinates present, validate *longi > ' ' && ! strmatch(longi,"null")) { err = validate_latlong(lati,longi,flati,flongi); if (err) { zmessageACK(Mwin,ZTX("bad latitude/longitude: %s %s"),lati,longi); return 1; } } strncpy0(meta_location,location,100); // save geotags in image file EXIF strncpy0(meta_country,country,100); // and in search-index file strncpy0(meta_lati,lati,20); strncpy0(meta_longi,longi,20); put_geolocs(zd); // update geolocs table in memory save_filemeta(curr_file); // save metadata changes to image file return 1; } /********************************************************************************/ // manage tags function - auxilliary dialog zdialog *zdmanagetags = 0; void manage_tags() { void manage_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int managetags_dialog_event(zdialog *zd, cchar *event); GtkWidget *widget; zdialog *zd; /*** ______________________________________________________________ | Manage Tags | | | | category [____________] tag [___________] [Create] [Delete] | | | | Defined Tags: _____________________________________________ | | | | | | | category1: tag11, tag12, tag13 ... | | | | category2: tag21, tag22, tag23 ... | | | | ... | | | | | | | | | | | | | | | | | | | | | | | |__________________________________________________________| | | | | [orphan tags] [Done] | |______________________________________________________________| ***/ if (zdmanagetags) return; zd = zdialog_new(ZTX("Manage Tags"),Mwin,ZTX("orphan tags"),Bdone,null); zdmanagetags = zd; zdialog_add_widget(zd,"hbox","hb7","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labcatg","hb7",ZTX("category"),"space=5"); zdialog_add_widget(zd,"zentry","catg","hb7",0,"size=12"); zdialog_add_widget(zd,"label","space","hb7",0,"space=5"); zdialog_add_widget(zd,"label","labtag","hb7",ZTX("tag"),"space=5"); zdialog_add_widget(zd,"zentry","tag","hb7",0,"size=20|expand"); zdialog_add_widget(zd,"label","space","hb7",0,"space=5"); zdialog_add_widget(zd,"button","create","hb7",Bcreate); zdialog_add_widget(zd,"button","delete","hb7",Bdelete); zdialog_add_widget(zd,"hbox","hb8","dialog"); zdialog_add_widget(zd,"label","labdeftags","hb8",ZTX("Defined Tags:"),"space=5"); zdialog_add_widget(zd,"hbox","hb9","dialog",0,"expand"); zdialog_add_widget(zd,"frame","frame8","hb9",0,"space=5|expand"); zdialog_add_widget(zd,"scrwin","scrwin8","frame8",0,"expand"); zdialog_add_widget(zd,"text","deftags","scrwin8",0,"expand|wrap"); widget = zdialog_widget(zd,"deftags"); // tag widget mouse function textwidget_set_callbackfunc(widget,manage_deftags_clickfunc); load_deftags(); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); zdialog_resize(zd,0,400); ///zdialog_set_modal(zd); // blocks cancel orphan tags report 17.08.3 zdialog_run(zd,managetags_dialog_event,"parent"); // run dialog zdialog_wait(zd); zdialog_free(zd); return; } // mouse click functions for widget having tags void manage_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // tag or tag category was clicked { char *txword, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txword = textwidget_word(widget,line,pos,",;:",end); if (! txword) return; if (end == ':') zdialog_stuff(zdmanagetags,"catg",txword); // selected category >> dialog widget else zdialog_stuff(zdmanagetags,"tag",txword); // selected tag >> dialog widget zfree(txword); return; } // dialog event and completion callback function int managetags_dialog_event(zdialog *zd, cchar *event) { void tag_orphans(GtkWidget *); char tag[tagcc], catg[tagcc]; int err, changed = 0; if (zd->zstat) { if (zd->zstat == 1) { // report orphan tags zd->zstat = 0; // keep dialog active tag_orphans(zdialog_widget(zd,"dialog")); } else { // done or [x] zdialog_free(zd); if (zdeditmeta) zdialog_show(zdeditmeta,1); // reinstate parent dialog zdmanagetags = 0; return 0; } } if (strmatch(event,"create")) { // add new tag to defined tags zdialog_fetch(zd,"catg",catg,tagcc); zdialog_fetch(zd,"tag",tag,tagcc); err = add_deftag(catg,tag); if (! err) changed++; } if (strmatch(event,"delete")) { // remove tag from defined tags zdialog_fetch(zd,"tag",tag,tagcc); del_deftag(tag); changed++; } if (changed) { save_deftags(); // save tag updates to file deftags_stuff(zd,"ALL"); // update dialog "deftags" window if (zdeditmeta) // and edit metadata dialog if active deftags_stuff(zdeditmeta,"ALL"); if (zdbatchtags) // and batch tags dialog if active deftags_stuff(zdbatchtags,"ALL"); } return 0; } /********************************************************************************/ // edit EXIF/IPTC data - add or change specified EXIF/IPTC/etc. key void m_meta_edit_any(GtkWidget *, cchar *menu) { int meta_edit_any_dialog_event(zdialog *zd, cchar *event); void meta_edit_any_clickfunc(GtkWidget *, int line, int pos, int kbkey); zdialog *zd; GtkWidget *mtext; char filename[200], buff[100]; cchar *pp1[1]; char *pp2[1], *pp; FILE *fid; F1_help_topic = "edit_any_metadata"; if (clicked_file) { // use clicked file if present if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry 18.01 f_open(clicked_file,0,0,1,0); clicked_file = 0; } if (! curr_file) { if (zdeditanymeta) zdialog_free(zdeditanymeta); zdeditanymeta = 0; return; } if (FGWM == 'G') gallery(0,"paint",-1); // if gallery view, repaint /*** _____________________________________________________ | Click to Select | Image File: filename.jpg | | | | | (metadata list) | key name [___________] [fetch] | | | key value [__________________] | | | | | | | | | | | | [Full List] [Save] [Done] | |____________________|________________________________| ***/ if (! zdeditanymeta) // popup dialog if not already { zd = zdialog_new(ZTX("Edit Any Metadata"),Mwin,ZTX("Full List"),Bsave,Bdone,null); zdeditanymeta = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"expand|space=3"); zdialog_add_widget(zd,"label","lab1","vb1",ZTX("click to select"),"size=25"); zdialog_add_widget(zd,"scrwin","scroll","vb1",0,"expand"); zdialog_add_widget(zd,"text","mtext","scroll",0,"expand"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"expand|space=3"); zdialog_add_widget(zd,"hbox","hbf","vb2",0,"space=6"); zdialog_add_widget(zd,"label","labf","hbf",ZTX("Image File:"),"space=3"); zdialog_add_widget(zd,"label","file","hbf","filename.jpg","space=5"); zdialog_add_widget(zd,"hbox","hbkey","vb2",0,"space=2"); zdialog_add_widget(zd,"hbox","hbdata","vb2",0,"space=2"); zdialog_add_widget(zd,"label","labkey","hbkey",ZTX("key name")); zdialog_add_widget(zd,"zentry","keyname","hbkey",0,"expand"); zdialog_add_widget(zd,"button","fetch","hbkey",Bfetch,"space=5"); zdialog_add_widget(zd,"label","labdata","hbdata",ZTX("key value")); zdialog_add_widget(zd,"zentry","keydata","hbdata",0,"expand"); zdialog_resize(zd,600,300); zdialog_run(zd,meta_edit_any_dialog_event,"parent"); mtext = zdialog_widget(zd,"mtext"); // make clickable metadata list textwidget_clear(mtext); snprintf(filename,200,"%s/metadata_short_list",get_zhomedir()); fid = fopen(filename,"r"); // read metadata list if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); return; } while (true) { // populate dialog, left side pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (*pp <= ' ') continue; if (strstr(buff,"White Balance")) continue; // remove immutable items ********* 18.01 textwidget_append(mtext,0,"%s \n",buff); } fclose(fid); textwidget_scroll(mtext,0); textwidget_set_callbackfunc(mtext,meta_edit_any_clickfunc); // set click callback func *keyname = 0; } zd = zdeditanymeta; pp = strrchr(curr_file,'/'); // stuff file name in dialog if (pp) zdialog_stuff(zd,"file",pp+1); if (*keyname) // update live dialog { pp1[0] = keyname; // look for key data exif_get(curr_file,pp1,pp2,1); if (pp2[0]) { strncpy0(keydata,pp2[0],exif_maxcc); zfree(pp2[0]); } else *keydata = 0; zdialog_stuff(zd,"keydata",keydata); // stuff into dialog } return; } // dialog event and completion callback function int meta_edit_any_dialog_event(zdialog *zd, cchar *event) { cchar *pp1[1]; char *pp2[1]; int err; if (! curr_file) return 1; if (strmatch(event,"fetch")) // accept entered key name 17.08 { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"keyname",keyname,40); // get key name from dialog strCompress(keyname); pp1[0] = keyname; // look for key data exif_get(curr_file,pp1,pp2,1); if (pp2[0]) { strncpy0(keydata,pp2[0],exif_maxcc); zfree(pp2[0]); } else *keydata = 0; zdialog_stuff(zd,"keydata",keydata); // stuff into dialog } if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) { // show full list zd->zstat = 0; // keep dialog active zmessageACK(Mwin,ZTX("The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names")); } else if (zd->zstat == 2) // save { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"keyname",keyname,40); // get key name from dialog zdialog_fetch(zd,"keydata",keydata,exif_maxcc); strCompress(keyname); pp1[0] = keyname; pp2[0] = keydata; err = exif_put(curr_file,pp1,(cchar **) pp2,1); // change metadata in image file if (err) zmessageACK(Mwin,"error: %s",strerror(err)); load_filemeta(curr_file); // update image index in case update_image_index(curr_file); // searchable metadata item updated if (zdexifview) meta_view(0); // update exif view if active } else { zdialog_free(zd); // done or cancel zdeditanymeta = 0; } return 1; } // get clicked tag name from short list and insert into dialog void meta_edit_any_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { char *pp, *pp2[1]; cchar *pp1[1]; if (! zdeditanymeta) return; if (! curr_file) return; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } pp = textwidget_line(widget,line,1); // get clicked line, highlight if (! pp || ! *pp) return; textwidget_highlight_line(widget,line); zdialog_stuff(zdeditanymeta,"keyname",pp); zdialog_fetch(zdeditanymeta,"keyname",keyname,40); // get key name from dialog strCompress(keyname); pp1[0] = keyname; // look for key data exif_get(curr_file,pp1,pp2,1); if (pp2[0]) { strncpy0(keydata,pp2[0],exif_maxcc); zfree(pp2[0]); } else *keydata = 0; zdialog_stuff(zdeditanymeta,"keydata",keydata); // stuff into dialog return; } /********************************************************************************/ // delete EXIF/IPTC data, specific key or all data void m_meta_delete(GtkWidget *, cchar *menu) { int meta_delete_dialog_event(zdialog *zd, cchar *event); zdialog *zd; char *pp; F1_help_topic = "delete_metadata"; if (clicked_file) { // use clicked file if present if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry 18.01 f_open(clicked_file,0,0,1,0); clicked_file = 0; } if (FGWM == 'G') gallery(0,"paint",-1); // if gallery view, repaint /*** _________________________________________ | Delete Metadata | | | | Image File: [________________________] | | (o) ALL (o) One Key: [______________] | | | | [apply] [cancel] | |_________________________________________| ***/ if (! zddeletemeta) { zd = zdialog_new(ZTX("Delete Metadata"),Mwin,Bapply,Bcancel,null); zddeletemeta = zd; zdialog_add_widget(zd,"hbox","hbf","dialog"); zdialog_add_widget(zd,"label","labf","hbf",ZTX("Image File:"),"space=3"); zdialog_add_widget(zd,"label","file","hbf",0,"space=5"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","kall","hb1",ZTX("All"),"space=5"); zdialog_add_widget(zd,"radio","key1","hb1",ZTX("One Key:")); zdialog_add_widget(zd,"zentry","keyname","hb1",0,"size=20"); zdialog_stuff(zd,"key1",1); zdialog_run(zd,meta_delete_dialog_event,"parent"); } zd = zddeletemeta; pp = (char *) ""; if (curr_file) { pp = strrchr(curr_file,'/'); if (pp) pp++; else pp = curr_file; } zdialog_stuff(zd,"file",pp); // 18.01 return; } // dialog event and completion callback function int meta_delete_dialog_event(zdialog *zd, cchar *event) { int kall, key1; char keyname[40]; if (! zd->zstat) return 1; // wait for completion if (zd->zstat != 1) { // canceled zdialog_free(zd); zddeletemeta = 0; return 1; } zd->zstat = 0; // dialog remains active if (! curr_file) return 1; zdialog_fetch(zd,"kall",kall); zdialog_fetch(zd,"key1",key1); zdialog_fetch(zd,"keyname",keyname,40); strCompress(keyname); if (kall) // update file metadata shell_ack("exiftool -m -q -overwrite_original -all= \"%s\"",curr_file); else if (key1) shell_ack("exiftool -m -q -overwrite_original -%s= \"%s\"",keyname,curr_file); else return 1; load_filemeta(curr_file); // update image index in case a update_image_index(curr_file); // searchable metadata deleted if (zdexifview) meta_view(0); // update exif view if active return 1; } /********************************************************************************/ // Show captions and comments on top of current image in main window. // Menu call (menu arg not null): toggle switch on/off. // Non-menu call: write caption/comment on image if switch is ON. void m_captions(GtkWidget *, cchar *menu) { cchar *keynames[2] = { iptc_caption_key, exif_comment_key }; char *pp, *keyvals[2]; char fname[200], caption[200], comment[200]; static char text[402]; F1_help_topic = "show_captions"; if (menu) { // if menu call, flip toggle Fcaptions = 1 - Fcaptions; if (curr_file) f_open(curr_file); return; } if (! Fcaptions) return; if (! curr_file) return; pp = strrchr(curr_file,'/'); if (pp) pp++; else pp = curr_file; strncpy0(fname,pp,200); *caption = *comment = 0; exif_get(curr_file,keynames,keyvals,2); // get captions and comments metadata if (keyvals[0]) { strncpy0(caption,keyvals[0],200); zfree(keyvals[0]); } if (keyvals[1]) { strncpy0(comment,keyvals[1],200); zfree(keyvals[1]); } strcpy(text,fname); // 17.01 if (*caption) { strcat(text,"\n"); strcat(text,caption); } if (*comment) { strcat(text,"\n"); strcat(text,comment); } for (int ii = 0; text[ii]; ii++) // replace "\n" with newline chars. if (text[ii] == '\\' && text[ii+1] == 'n') memmove(text+ii,"\n ",2); if (*text) add_toptext(1,0,0,text,"Sans 10"); return; } /********************************************************************************/ // menu function - add and remove tags for many files at once namespace batchtags { char addtags[tagMcc]; // tags to add, list char deltags[tagMcc]; // tags to remove, list int radadd, raddel; // dialog radio buttons char countmess[80]; } void m_batch_tags(GtkWidget *, cchar *menu) // combine batch add/del tags { using namespace batchtags; void batch_addtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void batch_deltags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void batch_recentags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void batch_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void batch_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int batch_tags_dialog_event(zdialog *zd, cchar *event); char *ptag, *file; int zstat, ii, jj, err, yn; zdialog *zd; GtkWidget *widget; F1_help_topic = "batch_tags"; if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** ________________________________________________________ | Batch Add/Remove Tags | | | | [Select Files] NN files selected | | | | (o) tags to add [________________________________] | | (o) tags to remove [________________________________] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Recent Tags [_______________________________________] | | Enter New Tag [___________] [Add] | | Matching Tags [_____________________________________] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Defined Tags Category [___________________________|v] | | | | | | | | | | | | | | | | | | | | | | | | | | |___________________________________________________| | | | | [Manage Tags] [Proceed] [Cancel] | |________________________________________________________| ***/ zd = zdialog_new(ZTX("Batch Add/Remove Tags"),Mwin,Bmanagetags,Bproceed,Bcancel,null); zdbatchtags = zd; // [Select Files] NN files selected zdialog_add_widget(zd,"hbox","hbfiles","dialog",0,"space=3"); zdialog_add_widget(zd,"button","files","hbfiles",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","labcount","hbfiles",Bnofileselected,"space=10"); // (o) tags to add [_______________________________________] // (o) tags to remove [_______________________________________] zdialog_add_widget(zd,"hbox","hbtags","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hbtags",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vb2","hbtags",0,"space=3|homog|expand"); zdialog_add_widget(zd,"radio","radadd","vb1",ZTX("tags to add")); zdialog_add_widget(zd,"radio","raddel","vb1",ZTX("tags to remove")); zdialog_add_widget(zd,"frame","fradd","vb2",0,"expand"); zdialog_add_widget(zd,"text","addtags","fradd",0,"expand|wrap"); zdialog_add_widget(zd,"frame","frdel","vb2",0,"expand"); zdialog_add_widget(zd,"text","deltags","frdel",0,"expand|wrap"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // Recent Tags [______________________________________________] zdialog_add_widget(zd,"hbox","hbrt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labrt","hbrt",ZTX("Recent Tags"),"space=3"); zdialog_add_widget(zd,"frame","frrt","hbrt",0,"space=3|expand"); zdialog_add_widget(zd,"text","recentags","frrt",0,"wrap"); // Enter New Tag [________________] [Add] zdialog_add_widget(zd,"hbox","hbnt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labnt","hbnt",ZTX("Enter New Tag"),"space=3"); zdialog_add_widget(zd,"zentry","newtag","hbnt"); zdialog_add_widget(zd,"button","add","hbnt",Badd,"space=5"); // Matching Tags [____________________________________________] zdialog_add_widget(zd,"hbox","hbmt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labmt","hbmt",ZTX("Matching Tags"),"space=3"); zdialog_add_widget(zd,"frame","frmt","hbmt",0,"space=3|expand"); zdialog_add_widget(zd,"text","matchtags","frmt",0,"wrap"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); // Defined Tags Category [__________________________________|v] zdialog_add_widget(zd,"hbox","space","dialog"); zdialog_add_widget(zd,"hbox","hbdt1","dialog"); zdialog_add_widget(zd,"label","labdt","hbdt1",ZTX("Defined Tags Category"),"space=3"); zdialog_add_widget(zd,"combo","defcats","hbdt1",0,"expand|space=5"); zdialog_add_widget(zd,"hbox","hbdt2","dialog",0,"expand"); zdialog_add_widget(zd,"frame","frdt2","hbdt2",0,"expand|space=3"); zdialog_add_widget(zd,"scrwin","swdt2","frdt2",0,"expand"); zdialog_add_widget(zd,"text","deftags","swdt2",0,"wrap"); zdialog_add_ttip(zd,Bmanagetags,ZTX("create tag categories and tags")); zdialog_stuff(zd,"radadd",1); // initz. radio buttons zdialog_stuff(zd,"raddel",0); zdialog_stuff(zd,"recentags",tags_recentags); // stuff recent tags into dialog load_deftags(); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories *addtags = *deltags = 0; if (menu) gallery_select_clear(); // clear gallery_select() file list else { snprintf(countmess,80,Bfileselected,GScount); // re-run with same files zdialog_stuff(zd,"labcount",countmess); } widget = zdialog_widget(zd,"addtags"); // tag widget mouse functions textwidget_set_callbackfunc(widget,batch_addtags_clickfunc); widget = zdialog_widget(zd,"deltags"); textwidget_set_callbackfunc(widget,batch_deltags_clickfunc); widget = zdialog_widget(zd,"recentags"); textwidget_set_callbackfunc(widget,batch_recentags_clickfunc); widget = zdialog_widget(zd,"matchtags"); textwidget_set_callbackfunc(widget,batch_matchtags_clickfunc); widget = zdialog_widget(zd,"deftags"); textwidget_set_callbackfunc(widget,batch_deftags_clickfunc); zdialog_resize(zd,500,500); // run dialog zdialog_run(zd,batch_tags_dialog_event,"parent"); zstat = zdialog_wait(zd); // wait for dialog completion zdialog_free(zd); zdbatchtags = 0; if (zstat != 2) { // cancel Fblock = 0; return; } popup_report_open(Mwin,"Batch Tags",500,200); // status report popup window for (ii = 0; ii < GScount; ii++) // loop all selected files { zmainsleep(0.1); // keep GTK alive file = GSfiles[ii]; // display image err = f_open(file,0,0,0); if (err) continue; popup_report_write(0,"%s \n",file); // report progress for (jj = 1; ; jj++) // remove tags if present { ptag = (char *) strField(deltags,",;",jj); if (! ptag) break; if (*ptag == ' ') continue; err = del_tag(ptag,meta_tags); if (err) continue; } for (jj = 1; ; jj++) // add new tags unless already { ptag = (char *) strField(addtags,",;",jj); if (! ptag) break; if (*ptag == ' ') continue; err = add_tag(ptag,meta_tags,tagFcc); if (err == 2) { zmessageACK(Mwin,ZTX("%s \n too many tags"),file); break; } } save_filemeta(file); // save tag changes } popup_report_write(0,"%s \n",Bcompleted); popup_report_move(-200,0); Fblock = 0; yn = zmessageYN(Mwin,ZTX("repeat with same files?")); // allow repeat with same files 18.01 if (yn) { popup_report_close(0); m_batch_tags(0,0); } return; } // mouse click functions for widgets holding tags void batch_addtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a tag in the add list was clicked { using namespace batchtags; char *txtag, end; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; del_tag(txtag,addtags); // remove tag from list zdialog_stuff(zdbatchtags,"addtags",addtags); zfree(txtag); return; } void batch_deltags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a tag in the remove list was clicked { using namespace batchtags; char *txtag, end; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; del_tag(txtag,deltags); // remove tag from list zdialog_stuff(zdbatchtags,"deltags",deltags); zfree(txtag); return; } void batch_recentags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // recent tag was clicked { using namespace batchtags; char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; zdialog_fetch(zdbatchtags,"radadd",radadd); // which radio button? if (radadd) { add_tag(txtag,addtags,tagMcc); // add recent tag to tag add list zdialog_stuff(zdbatchtags,"addtags",addtags); } else { add_tag(txtag,deltags,tagMcc); // add recent tag to tag remove list zdialog_stuff(zdbatchtags,"deltags",deltags); } zfree(txtag); return; } void batch_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // matching tag was clicked { using namespace batchtags; char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; zdialog_fetch(zdbatchtags,"radadd",radadd); // which radio button? if (radadd) { add_tag(txtag,addtags,tagMcc); // add recent tag to tag add list zdialog_stuff(zdbatchtags,"addtags",addtags); } else { add_tag(txtag,deltags,tagMcc); // add recent tag to tag remove list zdialog_stuff(zdbatchtags,"deltags",deltags); } add_recentag(txtag); // add to recent tags zdialog_stuff(zdbatchtags,"recentags",tags_recentags); zdialog_stuff(zdbatchtags,"newtag",""); // clear newtag and matchtags zdialog_stuff(zdbatchtags,"matchtags",""); zdialog_goto(zdbatchtags,"newtag"); // put focus back on newtag widget zfree(txtag); return; } void batch_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a defined tag was clicked { using namespace batchtags; char *txtag, end; int radadd; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;:",end); if (! txtag || end == ':') return; // tag category clicked, ignore zdialog_fetch(zdbatchtags,"radadd",radadd); // which radio button? if (radadd) { add_tag(txtag,addtags,tagMcc); // add defined tag to tag add list zdialog_stuff(zdbatchtags,"addtags",addtags); } else { add_tag(txtag,deltags,tagMcc); // add defined tag to tag remove list zdialog_stuff(zdbatchtags,"deltags",deltags); } add_recentag(txtag); // and add to recent tags zdialog_stuff(zdbatchtags,"recentags",tags_recentags); zfree(txtag); return; } // batchTags dialog event function int batch_tags_dialog_event(zdialog *zd, cchar *event) { using namespace batchtags; char catgname[tagcc]; int ii, jj, nt, cc1, cc2, ff; char *pp1, *pp2; char newtag[tagcc], matchtags[20][tagcc]; char matchtagstext[(tagcc+2)*20]; if (strmatch(event,"cancel")) zd->zstat = 3; if (zd->zstat) // dialog completed { if (zd->zstat == 1) { // manage tags zd->zstat = 0; // keep dialog active zdialog_show(zd,0); // hide parent dialog manage_tags(); zdialog_show(zd,1); } if (zd->zstat == 2) { // proceed if (! GScount || (*addtags <= ' ' && *deltags <= ' ')) { zmessageACK(Mwin,ZTX("specify files and tags")); zd->zstat = 0; // keep dialog active } } return 1; // cancel } if (strmatch(event,"files")) // select images to process { zdialog_show(zd,0); // hide parent dialog gallery_select(); // get new list zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labcount",countmess); } if (strstr("radadd raddel",event)) { // get state of radio buttons zdialog_fetch(zd,"radadd",radadd); zdialog_fetch(zd,"raddel",raddel); } if (strmatch(event,"defcats")) { // new tag category selection zdialog_fetch(zd,"defcats",catgname,tagcc); deftags_stuff(zd,catgname); } if (strmatch(event,"newtag")) // new tag is being typed in { zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog zdialog_fetch(zd,"newtag",newtag,tagcc); // get chars. typed so far cc1 = strlen(newtag); for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters if (strchr(",:;",newtag[ii])) continue; newtag[jj++] = newtag[ii]; } if (jj < cc1) { // something was removed newtag[jj] = 0; cc1 = jj; zdialog_stuff(zd,"newtag",newtag); } if (cc1 < 2) return 1; // wait for at least 2 chars. for (ii = nt = 0; ii < maxtagcats; ii++) // loop all categories { pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, if (! pp2) continue; // | | pp2 = strchr(pp2,':'); // pp1 pp2 while (true) // loop all deftags in category { pp1 = pp2 + 2; if (! *pp1) break; pp2 = strchr(pp1,','); if (! pp2) break; if (strmatchcaseN(newtag,pp1,cc1)) { // deftag matches chars. typed so far cc2 = pp2 - pp1; strncpy(matchtags[nt],pp1,cc2); // save deftags that match matchtags[nt][cc2] = 0; if (++nt == 20) return 1; // quit if 20 matches or more } } } if (nt == 0) return 1; // no matches pp1 = matchtagstext; for (ii = 0; ii < nt; ii++) // make deftag list: aaaaa, bbb, cccc ... { strcpy(pp1,matchtags[ii]); pp1 += strlen(pp1); strcpy(pp1,", "); pp1 += 2; } zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog return 1; } if (strmatch(event,"add")) // enter new tag finished { zdialog_fetch(zd,"newtag",newtag,tagcc); // get finished tag cc1 = strlen(newtag); if (! cc1) return 1; if (newtag[cc1-1] == '\n') { // remove newline character cc1--; newtag[cc1] = 0; } for (ii = ff = 0; ii < maxtagcats; ii++) // loop all categories { pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, if (! pp2) continue; // | | pp2 = strchr(pp2,':'); // pp1 pp2 while (true) // loop all deftags in category { pp1 = pp2 + 2; if (! *pp1) break; pp2 = strchr(pp1,','); if (! pp2) break; cc2 = pp2 - pp1; if (cc2 != cc1) continue; if (strmatchcaseN(newtag,pp1,cc1)) { // entered tag matches deftag strncpy(newtag,pp1,cc1); // use deftag upper/lower case ff = 1; break; } } if (ff) break; } if (! ff) { // if new tag, add to defined tags add_deftag((char *) "nocatg",newtag); deftags_stuff(zd,"ALL"); } add_tag(newtag,addtags,tagMcc); // add to tag add list zdialog_stuff(zdbatchtags,"addtags",addtags); add_recentag(newtag); // add to recent tags zdialog_stuff(zd,"recentags",tags_recentags); zdialog_stuff(zd,"newtag",""); // update dialog widgets zdialog_stuff(zd,"matchtags",""); zdialog_goto(zd,"newtag"); // put focus back on newtag widget return 1; } return 1; } /********************************************************************************/ // menu function - rename multiple tags for selected image files namespace batchrenametags { int Ntags; // count, 1-100 char *oldtags[100]; // tags to rename char *newtags[100]; // corresponding new name #define tpcc (tagcc+tagcc+10) zdialog *zd; } // menu function void m_batch_rename_tags(GtkWidget *, cchar *menu) { using namespace batchrenametags; void batchrenametags_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void batchrenametags_taglist_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int batchrenametags_dialog_event(zdialog *zd, cchar *event); char *file; int ii, jj, kk, ff, yn, err; int zstat, Nfiles, Nlist; GtkWidget *widget; char **filelist; char *pp, *filetag; char *oldtaglist[100], *newtaglist[100]; xxrec_t *xxrec; F1_help_topic = "batch_rename_tags"; if (checkpend("all")) return; // check nothing pending Fblock = 1; Ntags = Nfiles = 0; filelist = 0; /*** ____________________________________________________________________________ | Batch Rename Tags | | | | old tag name >> new tag name | | Tag [_______] Rename to [_________] [->] | aaaaaaaa >> bbbbbbbbbbb | | | ccccccccccc >> ddddddddd | | Defined Tags Category [________________|v| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |________________________________________| |_______________________________| | | | [Proceed] [Cancel] | |____________________________________________________________________________| ***/ zd = zdialog_new(ZTX("Batch Rename Tags"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"expand"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=8|expand"); // tag [_________________] rename to [___________________] [-->] zdialog_add_widget(zd,"hbox","hbtags","vb1",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hbtags","Tag","space=3"); zdialog_add_widget(zd,"frame","frot","hbtags"); zdialog_add_widget(zd,"label","oldtag","frot",ZTX("(click defined tag)")); zdialog_add_widget(zd,"label","space","hbtags",0,"space=5"); zdialog_add_widget(zd,"label","lab2","hbtags",ZTX("Rename to"),"space=3"); zdialog_add_widget(zd,"zentry","newtag","hbtags",0,"expand"); zdialog_add_widget(zd,"label","space","hbtags",0,"space=3"); zdialog_add_widget(zd,"button","addtags","hbtags",">>"); zdialog_add_widget(zd,"hsep","hsep1","vb1",0,"space=5"); // Defined Tags Category [_____________________|v] zdialog_add_widget(zd,"hbox","hbdt","vb1",0); zdialog_add_widget(zd,"label","labdt","hbdt",ZTX("Defined Tags Category"),"space=3"); zdialog_add_widget(zd,"combo","defcats","hbdt",0,"space=5"); zdialog_add_widget(zd,"frame","frdt","vb1",0,"expand|space=3"); zdialog_add_widget(zd,"scrwin","swdt","frdt",0,"expand"); zdialog_add_widget(zd,"text","deftags","swdt",0,"wrap"); // old tag name >> new tag name zdialog_add_widget(zd,"hbox","hblist","vb2"); zdialog_add_widget(zd,"label","lablist","hblist",ZTX("old tag name >> new tag name"),"space=10"); zdialog_add_widget(zd,"frame","frlist","vb2",0,"expand|space=3"); zdialog_add_widget(zd,"scrwin","swlist","frlist"); zdialog_add_widget(zd,"text","taglist","swlist"); load_deftags(); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories widget = zdialog_widget(zd,"deftags"); // connect mouse to defined tags widget textwidget_set_callbackfunc(widget,batchrenametags_deftags_clickfunc); widget = zdialog_widget(zd,"taglist"); // connect mouse to taglist widget textwidget_set_callbackfunc(widget,batchrenametags_taglist_clickfunc); zdialog_resize(zd,700,400); // run dialog zdialog_run(zd,batchrenametags_dialog_event,"parent"); zstat = zdialog_wait(zd); // wait for dialog completion zdialog_free(zd); zd = 0; if (zstat != 1) goto cleanup; // [cancel] filelist = (char **) zmalloc(Nxxrec * sizeof(char *)); // find all affected image files Nfiles = 0; popup_report_open(Mwin,"rename tags",500,300); // 18.01 for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; if (! xxrec->tags) continue; // search for tags to rename ff = 0; for (jj = 1; ; jj++) { pp = (char *) strField(xxrec->tags,',',jj); if (! pp) break; if (strmatch(pp,"null")) continue; for (kk = 0; kk < Ntags; kk++) { if (strmatchcase(pp,oldtags[kk])) { // this file has one or more tags ff = 1; // that will be renamed break; } } if (ff) break; } if (ff) { filelist[Nfiles] = zstrdup(xxrec->file); // add to list of files to process Nfiles++; popup_report_write(0,"file included: %s \n",xxrec->file); } } yn = zmessageYN(Mwin,ZTX("%d tags to rename \n" "in %d image files. \n" "Proceed?"),Ntags,Nfiles); if (! yn) goto cleanup; for (ii = 0; ii < Nfiles; ii++) // loop all selected files { zmainloop(); // keep GTK alive file = filelist[ii]; // open image file err = f_open(file,0,0,0); if (err) continue; popup_report_write(0,"%s \n",file); // report progress Nlist = 0; for (jj = 1; ; jj++) { // loop file tags filetag = (char *) strField(meta_tags,',',jj); if (! filetag) break; for (kk = 0; kk < Ntags; kk++) { // loop tag replacement list if (strmatchcase(filetag,oldtags[kk])) { // file tag matches tag to replace oldtaglist[Nlist] = oldtags[kk]; // save old and new tags newtaglist[Nlist] = newtags[kk]; Nlist++; break; // next file tag } } } for (jj = 0; jj < Nlist; jj++) // remove old tags err = del_tag(oldtaglist[jj],meta_tags); for (jj = 0; jj < Nlist; jj++) { // add new tags if (! newtaglist[jj]) continue; // must be after removals popup_report_write(0,"%s \n",newtaglist[jj]); err = add_tag(newtaglist[jj],meta_tags,tagFcc); if (err && err != 1) popup_report_write(1,"ERROR \n"); // ignore already there, else report } save_filemeta(file); // save tag changes } popup_report_write(0,"%s \n",Bcompleted); cleanup: // free resources Fblock = 0; for (ii = 0; ii < Ntags; ii++) { zfree(oldtags[ii]); zfree(newtags[ii]); } for (ii = 0; ii < Nfiles; ii++) zfree(filelist[ii]); if (filelist) zfree(filelist); return; } // a defined tag was clicked void batchrenametags_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace batchrenametags; char *txtag, end; char tagname[tagcc]; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;:",end); // clicked word if (! txtag || end == ':') return; // tag category clicked, ignore snprintf(tagname,tagcc," %s ",txtag); // add spaces for appearance zdialog_stuff(zd,"oldtag",tagname); zdialog_stuff(zd,"newtag",""); zfree(txtag); return; } // a tag list line was clicked void batchrenametags_taglist_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace batchrenametags; int ii; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } if (line >= Ntags) return; for (ii = line; ii < Ntags-1; ii++) { // remove tags pair corresponding oldtags[ii] = oldtags[ii+1]; // to the line clicked newtags[ii] = newtags[ii+1]; } Ntags--; widget = zdialog_widget(zd,"taglist"); // rewrite dialog tag list textwidget_clear(widget); for (int ii = 0; ii < Ntags; ii++) textwidget_append(widget,0,"%s >> %s\n",oldtags[ii],newtags[ii]); return; } // batch rename tags dialog event function int batchrenametags_dialog_event(zdialog *zd, cchar *event) { using namespace batchrenametags; char catgname[tagcc]; char oldtag[tagcc], newtag[tagcc]; GtkWidget *widget; if (strmatch(event,"cancel")) zd->zstat = 2; if (zd->zstat) return 1; // dialog completed if (strmatch(event,"defcats")) { // new tag category selection zdialog_fetch(zd,"defcats",catgname,tagcc); deftags_stuff(zd,catgname); } if (strmatch(event,"addtags")) { // [ --> ] button pressed zdialog_fetch(zd,"oldtag",oldtag,tagcc); // save new pair of tag names zdialog_fetch(zd,"newtag",newtag,tagcc); strTrim2(oldtag); strTrim2(newtag); if (*oldtag <= ' ' || *newtag <= ' ') return 1; if (Ntags == 100) { zmessageACK(Mwin,ZTX("max tags exceeded")); return 1; } oldtags[Ntags] = zstrdup(oldtag); newtags[Ntags] = zstrdup(newtag); Ntags++; } widget = zdialog_widget(zd,"taglist"); // rewrite dialog tag list textwidget_clear(widget); for (int ii = 0; ii < Ntags; ii++) textwidget_append(widget,0,"%s >> %s\n",oldtags[ii],newtags[ii]); return 1; } /********************************************************************************/ // batch change or shift photo date/time void m_batch_photo_date_time(GtkWidget *, cchar *menu) // 18.01 { int batch_photo_time_dialog_event(zdialog *zd, cchar *event); cchar *keyname[1] = { "DateTimeOriginal" }; char *keyvalue[1]; char *file, olddatetime[24], newdatetime[24]; // exif format "yyyy:mm:dd hh:mm:ss" int ii, cc, err, zstat; int Fyearonly, Fdateonly; int Fsetnew, Fshift, Ftest; // check boxes time_t timep; struct tm DTold, DTnew; // old and new date/time int s_years, s_mons, s_mdays, s_hours, s_mins, s_secs; // shift amounts zdialog *zd; F1_help_topic = "batch_photo_time"; if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** __________________________________________________ | Batch Photo Date/Time | | | | [Select Files] NN files selected | | | | [x] set a new date/time: [_____________________] | | (yyyy-mm-dd hh:mm:ss) | | | | [x] shift existing date/time: | | years [__] months [__] days [__] | | hours [__] minutes [__] seconds [__] | | | | [x] test: show changes, do not update files | | | | [proceed] [cancel] | |__________________________________________________| ***/ zd = zdialog_new(ZTX("Batch Photo Date/Time"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbfiles","dialog",0,"space=3"); zdialog_add_widget(zd,"button","files","hbfiles",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","labcount","hbfiles",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbsetnew","dialog",0,"space=3"); zdialog_add_widget(zd,"check","Fsetnew","hbsetnew",ZTX("set a new date/time:"),"space=3"); zdialog_add_widget(zd,"zentry","newdatetime","hbsetnew",0,"expand|size=15"); zdialog_add_widget(zd,"hbox","hbsetnew2","dialog"); zdialog_add_widget(zd,"label","labspace","hbsetnew2","","expand"); zdialog_add_widget(zd,"label","labtemplate","hbsetnew2","yyyy-mm-dd hh:mm[:ss]","space=5"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbshift1","dialog",0,"space=3"); zdialog_add_widget(zd,"check","Fshift","hbshift1",ZTX("shift existing date/time:"),"space=3"); zdialog_add_widget(zd,"hbox","hbshift2","dialog"); zdialog_add_widget(zd,"label","space","hbshift2",0,"space=10"); zdialog_add_widget(zd,"label","labyears","hbshift2","years","space=5"); zdialog_add_widget(zd,"zspin","s_years","hbshift2","-99|+99|1|0"); zdialog_add_widget(zd,"label","space","hbshift2",0,"space=5"); zdialog_add_widget(zd,"label","labmons","hbshift2","months","space=5"); zdialog_add_widget(zd,"zspin","s_mons","hbshift2","-11|+11|1|0"); zdialog_add_widget(zd,"label","space","hbshift2",0,"space=5"); zdialog_add_widget(zd,"label","labmdays","hbshift2","days","space=5"); zdialog_add_widget(zd,"zspin","s_mdays","hbshift2","-30|+30|1|0"); zdialog_add_widget(zd,"hbox","hbshift3","dialog"); zdialog_add_widget(zd,"label","space","hbshift3",0,"space=10"); zdialog_add_widget(zd,"label","labhours","hbshift3","hours","space=5"); zdialog_add_widget(zd,"zspin","s_hours","hbshift3","-23|+23|1|0"); zdialog_add_widget(zd,"label","space","hbshift3",0,"space=5"); zdialog_add_widget(zd,"label","labmins","hbshift3","minutes","space=5"); zdialog_add_widget(zd,"zspin","s_mins","hbshift3","-59|+59|1|0"); zdialog_add_widget(zd,"label","space","hbshift3",0,"space=5"); zdialog_add_widget(zd,"label","labsecs","hbshift3","seconds","space=5"); zdialog_add_widget(zd,"zspin","s_secs","hbshift3","-59|+59|1|0"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbtest","dialog",0,"space=5"); zdialog_add_widget(zd,"check","Ftest","hbtest",ZTX("test: show changes, do not update files"),"space=3"); zdialog_restore_inputs(zd); zstat = zdialog_run(zd,batch_photo_time_dialog_event,"parent"); retry: zstat = zdialog_wait(zd); // wait for dialog, get status if (zstat != 1) { // not [proceed] zdialog_free(zd); // cancel Fblock = 0; return; } zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"Fsetnew",Fsetnew); // checkboxes zdialog_fetch(zd,"Fshift",Fshift); zdialog_fetch(zd,"Ftest",Ftest); if (Fsetnew + Fshift != 1) { zmessageACK(Mwin,"please make a choice"); goto retry; } if (GScount == 0) { zmessageACK(Mwin,ZTX("no files selected")); goto retry; } Fyearonly = Fdateonly = 0; if (Fsetnew) // input is new date/time { zdialog_fetch(zd,"newdatetime",newdatetime,24); strTrim2(newdatetime); // strip leading and trailing blanks cc = strlen(newdatetime); if (cc == 4) { // have only "yyyy" strcat(newdatetime,"-01-01 00:00:00"); // append "-01-01 00:00:00" Fyearonly = 1; cc = 19; } if (cc == 10) { // have only "yyyy-mm-dd" strcat(newdatetime," 00:00:00"); // append " 00:00:00" Fdateonly = 1; // flag, change date only cc = 19; } if (cc == 16) { // have only "yyyy-mm-dd hh:mm" strcat(newdatetime,":00"); // append ":00" cc = 19; } if (cc != 19) { // must have yyyy-mm-dd hh:mm:ss zmessageACK(Mwin,ZTX("invalid date/time format")); goto retry; } ii = sscanf(newdatetime,"%d-%d-%d %d:%d:%d", // yyyy-mm-dd hh:mm:ss >> DTnew &DTnew.tm_year, &DTnew.tm_mon, &DTnew.tm_mday, &DTnew.tm_hour, &DTnew.tm_min, &DTnew.tm_sec); if (ii != 6) { // check input format zmessageACK(Mwin,ZTX("invalid date/time format")); goto retry; } timep = mktime(&DTnew); // DTnew >> timep if (timep < 0) { // validate DTnew by validating timep zmessageACK(Mwin,ZTX("invalid date/time format")); goto retry; } } // DTnew is final value to use if (Fshift) { zdialog_fetch(zd,"s_years",s_years); // inputs are shifted date/time values zdialog_fetch(zd,"s_mons",s_mons); zdialog_fetch(zd,"s_mdays",s_mdays); zdialog_fetch(zd,"s_hours",s_hours); zdialog_fetch(zd,"s_mins",s_mins); zdialog_fetch(zd,"s_secs",s_secs); } zdialog_free(zd); popup_report_open(Mwin,"Batch Photo date/time Update",500,200); // start popup report if (Fshift) { popup_report_write(0,"changes: year mon day hours mins secs \n"); popup_report_write(0," %4d %3d %3d %5d %4d %4d \n", s_years,s_mons,s_mdays,s_hours,s_mins,s_secs); } for (ii = 0; ii < GScount; ii++) // loop all selected files { file = GSfiles[ii]; err = f_open(file,0,0,0); // open image file if (err) continue; popup_report_write(0,"\n"); // report progress popup_report_write(0,"%s \n",file); exif_get(curr_file,keyname,(char **) keyvalue,1); // metadata >> yyyy:mm:dd hh:mm:ss if (! keyvalue[0]) { popup_report_write(0," *** no date/time available \n"); continue; } strncpy0(olddatetime,keyvalue[0],20); // yyyy:mm:dd hh:mm:ss zfree(keyvalue[0]); DTold.tm_year = atoi(olddatetime); // >> DTold DTold.tm_mon = atoi(olddatetime + 5) - 1; // month is 0-11 (note inconsistency) DTold.tm_mday = atoi(olddatetime + 8); // day is 1-31 DTold.tm_hour = atoi(olddatetime + 11); DTold.tm_min = atoi(olddatetime + 14); DTold.tm_sec = atoi(olddatetime + 17); if (Fsetnew) // set new date/time { if (Fyearonly) // change year only, leave rest { DTnew.tm_mon = DTold.tm_mon; // >> revised DTnew DTnew.tm_mday = DTold.tm_mday; // set month/day/hour/min/sec only DTnew.tm_hour = DTold.tm_hour; // year remains fixed DTnew.tm_min = DTold.tm_min; DTnew.tm_sec = DTold.tm_sec; } if (Fdateonly) // change year/mon/day only, leave time { DTnew.tm_hour = DTold.tm_hour; // >> revised DTnew DTnew.tm_min = DTold.tm_min; // set hour/min/sec only DTnew.tm_sec = DTold.tm_sec; // year/mon/day remains fixed } } if (Fshift) // shift existing date/time values { DTnew.tm_year = DTold.tm_year + s_years; DTnew.tm_mon = DTold.tm_mon + s_mons; DTnew.tm_mday = DTold.tm_mday + s_mdays; DTnew.tm_hour = DTold.tm_hour + s_hours; DTnew.tm_min = DTold.tm_min + s_mins; DTnew.tm_sec = DTold.tm_sec + s_secs; } timep = mktime(&DTnew); if (timep < 0) { popup_report_write(0," %s *** date/time conversion failed \n",olddatetime); continue; } DTnew = *localtime(&timep); snprintf(newdatetime,20,"%04d:%02d:%02d %02d:%02d:%02d", // DTnew >> yyyy:mm:dd hh:mm:ss DTnew.tm_year, DTnew.tm_mon+1, DTnew.tm_mday, // (tm_mon 0-11 >> 1-12) DTnew.tm_hour, DTnew.tm_min, DTnew.tm_sec); olddatetime[4] = olddatetime[7] = newdatetime[4] = newdatetime[7] = '-'; // format: yyyy-mm-dd popup_report_write(0," %s %s \n",olddatetime,newdatetime); if (Ftest) continue; // test only, no file updates olddatetime[4] = olddatetime[7] = newdatetime[4] = newdatetime[7] = ':'; // format: yyyy:mm:dd keyvalue[0] = (char *) &newdatetime; err = exif_put(curr_file,keyname,(cchar **) keyvalue,1); // yyyy:mm:dd hh:mm:ss >> metadata load_filemeta(curr_file); // get all indexed data for file snprintf(meta_pdate,16,"%04d%02d%02d%02d%02d%02d", // update photo date, yyyymmddhhmmss DTnew.tm_year, DTnew.tm_mon+1, DTnew.tm_mday, // (tm_mon 0-11 >> 1-12) DTnew.tm_hour, DTnew.tm_min, DTnew.tm_sec); update_image_index(curr_file); // update image index rec. zmainloop(); // keep GTK alive } popup_report_write(1,"\n *** %s \n",Bcompleted); Fblock = 0; m_batch_photo_date_time(0,0); // repeat return; } // dialog event and completion callback function int batch_photo_time_dialog_event(zdialog *zd, cchar *event) { char countmess[80]; int nn; if (strmatch(event,"files")) // select images to process { zdialog_show(zd,0); // hide parent dialog gallery_select(); // get new file list zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labcount",countmess); } if (strmatch(event,"setnew")) { // toggle option, set new date/time zdialog_fetch(zd,"setnew",nn); if (nn) zdialog_stuff(zd,"shift",0); } if (strmatch(event,"shift")) { // toggle option, shift hours zdialog_fetch(zd,"shift",nn); if (nn) zdialog_stuff(zd,"setnew",0); } return 1; } /********************************************************************************/ // batch add or change any EXIF/IPTC metadata namespace batchchangemeta { zdialog *zd; } // menu function void m_batch_change_metadata(GtkWidget *, cchar *menu) // overhauled { using namespace batchchangemeta; int batch_change_metadata_dialog_event(zdialog *zd, cchar *event); void batch_change_metadata_clickfunc(GtkWidget *, int line, int pos, int kbkey); int ii, jj, err, zstat, nkeys; char keynameN[12] = "keynameN", keyvalueN[12] = "keyvalueN"; char keyname[40], keyvalue[exif_maxcc]; cchar *pp1[10], *pp2[10]; char *file, *pp; GtkWidget *mtext; char filename[200], buff[100]; FILE *fid; F1_help_topic = "batch_change_metadata"; if (checkpend("all")) return; // check nothing pending Fblock = 1; /** _________________________________________________________________ | Click to Select | Batch Add/Change Metadata | | | | | (metadata list) | [Select Files] NN files selected | | | | | | key name key value | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | [______________] [_____________________] | | | | | | [Full List] [apply] [cancel] | |____________________|____________________________________________| **/ zd = zdialog_new(ZTX("Batch Add/Change Metadata"),Mwin,ZTX("Full List"),Bapply,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand"); zdialog_add_widget(zd,"vbox","vb1","hb1"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"expand|space=5"); zdialog_add_widget(zd,"label","lab1","vb1",ZTX("click to select"),"size=30|space=3"); zdialog_add_widget(zd,"scrwin","scroll","vb1",0,"expand"); zdialog_add_widget(zd,"text","mtext","scroll",0,"expand"); zdialog_add_widget(zd,"hbox","hbfiles","vb2",0,"space=3"); zdialog_add_widget(zd,"button","files","hbfiles",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","labcount","hbfiles",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbkeys","vb2",0,"space=5"); zdialog_add_widget(zd,"vbox","vbname","hbkeys"); zdialog_add_widget(zd,"vbox","vbvalue","hbkeys",0,"expand"); zdialog_add_widget(zd,"label","labkey","vbname",ZTX("key name")); zdialog_add_widget(zd,"label","labdata","vbvalue",ZTX("key value")); zdialog_add_widget(zd,"zentry","keyname0","vbname",0,"size=25"); zdialog_add_widget(zd,"zentry","keyname1","vbname",0,"size=25"); zdialog_add_widget(zd,"zentry","keyname2","vbname",0,"size=25"); zdialog_add_widget(zd,"zentry","keyname3","vbname",0,"size=25"); zdialog_add_widget(zd,"zentry","keyname4","vbname",0,"size=25"); zdialog_add_widget(zd,"zentry","keyname5","vbname",0,"size=25"); zdialog_add_widget(zd,"zentry","keyname6","vbname",0,"size=25"); zdialog_add_widget(zd,"zentry","keyname7","vbname",0,"size=25"); zdialog_add_widget(zd,"zentry","keyname8","vbname",0,"size=25"); zdialog_add_widget(zd,"zentry","keyname9","vbname",0,"size=25"); zdialog_add_widget(zd,"zentry","keyvalue0","vbvalue",0,"expand"); zdialog_add_widget(zd,"zentry","keyvalue1","vbvalue",0,"expand"); zdialog_add_widget(zd,"zentry","keyvalue2","vbvalue",0,"expand"); zdialog_add_widget(zd,"zentry","keyvalue3","vbvalue",0,"expand"); zdialog_add_widget(zd,"zentry","keyvalue4","vbvalue",0,"expand"); zdialog_add_widget(zd,"zentry","keyvalue5","vbvalue",0,"expand"); zdialog_add_widget(zd,"zentry","keyvalue6","vbvalue",0,"expand"); zdialog_add_widget(zd,"zentry","keyvalue7","vbvalue",0,"expand"); zdialog_add_widget(zd,"zentry","keyvalue8","vbvalue",0,"expand"); zdialog_add_widget(zd,"zentry","keyvalue9","vbvalue",0,"expand"); zdialog_resize(zd,600,300); mtext = zdialog_widget(zd,"mtext"); // make clickable metadata list textwidget_clear(mtext); snprintf(filename,200,"%s/metadata_short_list",get_zhomedir()); fid = fopen(filename,"r"); if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); return; } while (true) { pp = fgets_trim(buff,100,fid,1); if (! pp) break; textwidget_append(mtext,0,"%s \n",buff); } fclose(fid); textwidget_scroll(mtext,0); textwidget_set_callbackfunc(mtext,batch_change_metadata_clickfunc); gallery_select_clear(); // clear gallery_select() file list nkeys = 0; zstat = zdialog_run(zd,batch_change_metadata_dialog_event,"parent"); // run dialog retry: zstat = zdialog_wait(zd); // wait for completion if (zstat != 2) goto cleanup; // not [apply] for (ii = jj = 0; ii < 10; ii++) { keynameN[7] = '0' + ii; keyvalueN[8] = '0' + ii; zdialog_fetch(zd,keynameN,keyname,40); zdialog_fetch(zd,keyvalueN,keyvalue,exif_maxcc); strCompress(keyname); if (*keyname <= ' ') continue; pp1[jj] = zstrdup(keyname); pp2[jj] = zstrdup(keyvalue); jj++; } nkeys = jj; if (nkeys == 0) { zmessageACK(Mwin,ZTX("enter key names")); zd->zstat = 0; goto retry; } if (GScount == 0) { zmessageACK(Mwin,ZTX("no files selected")); zd->zstat = 0; goto retry; } popup_report_open(Mwin,"Batch Metadata",500,200); // popup report for (ii = 0; ii < nkeys; ii++) { if (*pp2[ii]) popup_report_write(0,"%s = %s \n",pp1[ii],pp2[ii]); else popup_report_write(0,"%s = DELETED \n",pp1[ii]); } ii = zdialog_choose(Mwin,Bproceed,Bproceed,Bcancel,null); if (ii != 1) { zd->zstat = 0; goto retry; } zdialog_free(zd); zd = 0; for (ii = 0; ii < GScount; ii++) // loop all selected files { zmainloop(); // keep GTK alive file = GSfiles[ii]; // display image err = f_open(file,0,0,0); if (err) continue; popup_report_write(0,"%s \n",file); // report progress err = exif_put(curr_file,pp1,pp2,nkeys); // change metadata in image file load_filemeta(curr_file); // update image index in case update_image_index(curr_file); // indexed metadata updated if (zdexifview) meta_view(0); // update exif view if active } popup_report_write(0,"%s \n",Bcompleted); cleanup: if (zd) zdialog_free(zd); // kill dialog zd = 0; for (ii = 0; ii < nkeys; ii++) { // free memory zfree((char *) pp1[ii]); zfree((char *) pp2[ii]); } nkeys = 0; Fblock = 0; return; } // dialog event and completion callback function int batch_change_metadata_dialog_event(zdialog *zd, cchar *event) { using namespace batchchangemeta; char countmess[80]; if (zd->zstat == 1) // full list { zd->zstat = 0; // keep dialog active zmessageACK(Mwin,ZTX("The command: $ man Image::ExifTool::TagNames \n" "will show over 15000 \"standard\" tag names")); } if (strmatch(event,"files")) // select images to process { gallery_select_clear(); // clear gallery_select() file list zdialog_show(zd,0); // hide parent dialog gallery_select(); // get new list zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labcount",countmess); } return 1; } // get clicked tag name from short list and insert into dialog void batch_change_metadata_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace batchchangemeta; int ii; char *pp; char keynameX[12] = "keynameX"; char keyname[60]; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } pp = textwidget_line(widget,line,1); // get clicked line, highlight if (! pp || ! *pp) return; textwidget_highlight_line(widget,line); for (ii = 0; ii < 10; ii++) { // find 1st empty dialog key name keynameX[7] = '0' + ii; zdialog_fetch(zd,keynameX,keyname,60); if (*keyname <= ' ') break; } if (ii < 10) zdialog_stuff(zd,keynameX,pp); return; } /********************************************************************************/ // batch report metadata for selected image files namespace batchreportmeta { char filex[200]; // file for metadata item list } // menu function void m_batch_report_metadata(GtkWidget *, cchar *menu) { using namespace batchreportmeta; int batch_report_metadata_dialog_event(zdialog *zd, cchar *event); zdialog *zd; char buff[200]; char *file, *pp; FILE *fid = 0; int zstat, ff, ii, err; int nkx = 0; char *keynamex[NK], *keyvalx[NK]; F1_help_topic = "batch_report_metadata"; snprintf(filex,200,"%s/metadata_report_items",get_zhomedir()); // file for items to report gallery_select_clear(); // clear gallery_select() file list /*** ____________________________________________ | Batch Report Metadata | | | | [Select Files] NN files selected | | [Edit] list of reported metadata items | | | | [proceed] [cancel] | |____________________________________________| ***/ zd = zdialog_new(ZTX("Batch Report Metadata"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbfiles","dialog",0,"space=3"); zdialog_add_widget(zd,"button","files","hbfiles",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","labcount","hbfiles",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbedit","dialog",0,"space=3"); zdialog_add_widget(zd,"button","edit","hbedit",Bedit,"space=5"); zdialog_add_widget(zd,"label","labedit","hbedit","list of reported metadata items","space=10"); zstat = zdialog_run(zd,batch_report_metadata_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) goto cleanup; // cancel zdialog_free(zd); if (GScount == 0) { zmessageACK(Mwin,ZTX("no files selected")); return; } fid = fopen(filex,"r"); if (! fid) { zmessageACK(Mwin,"no metadata items to report"); return; } for (nkx = ii = 0; ii < 20; ii++) // read items to report { pp = fgets_trim(buff,100,fid,1); if (! pp) break; strCompress(pp); if (*pp <= ' ') continue; if (strmatchN(pp,"enteritems",5)) continue; keynamex[nkx] = zstrdup(pp); nkx++; } fclose(fid); if (! nkx) { zmessageACK(Mwin,"no metadata items to report"); return; } popup_report_open(Mwin,"metadata report",600,400); // 17.08 for (ff = 0; ff < GScount; ff++) // loop selected files { zmainloop(20); // keep GTK alive file = GSfiles[ff]; popup_report_write(0,"%s \n",file); // 17.08 err = exif_get(file,(cchar **) keynamex,keyvalx,nkx); // get all report items if (err) { zmessageACK(Mwin,"exif failure"); goto cleanup; } for (ii = 0; ii < nkx; ii++) // output keyword names and values if (keyvalx[ii]) popup_report_write(0,"%-24s : %s \n",keynamex[ii],keyvalx[ii]); // 17.08 for (ii = 0; ii < nkx; ii++) // free memory if (keyvalx[ii]) zfree(keyvalx[ii]); popup_report_write(0,"\n"); // blank line separator } cleanup: for (ii = 0; ii < nkx; ii++) // free memory zfree(keynamex[ii]); return; } // dialog event and completion function int batch_report_metadata_dialog_event(zdialog *zd, cchar *event) { using namespace batchreportmeta; char countmess[80]; char *itemlist[Mxmeta], *pp, buff[100]; FILE *fid; int ii; if (zd->zstat) zdialog_destroy(zd); if (strmatch(event,"files")) // select images to process { gallery_select_clear(); // clear gallery_select() file list zdialog_show(zd,0); // hide parent dialog gallery_select(); // get new list zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labcount",countmess); } if (strmatch(event,"edit")) // select metadata items to report { itemlist[0] = 0; // empty list fid = fopen(filex,"r"); // read file of extra metadata items 18.01 if (fid) { for (ii = 0; ii < Mxmeta; ii++) { pp = fgets_trim(buff,100,fid,1); if (! pp) break; if (*pp <= ' ') continue; itemlist[ii] = zstrdup(pp); } itemlist[ii] = 0; // mark EOL fclose(fid); } select_meta_keys(itemlist,0); // user edit of extras list 18.01 fid = fopen(filex,"w"); // update extras list file 18.01 if (! fid) { zmessageACK(Mwin,"%s \n %s",filex,strerror(errno)); return 1; } for (ii = 0; ii < Mxmeta; ii++) { if (! itemlist[ii]) break; fprintf(fid,"%s\n",itemlist[ii]); } fclose(fid); } return 1; } /********************************************************************************/ // batch geotags - set geotags for multiple image files void m_batch_geotags(GtkWidget *, cchar *menu) { int batch_geotags_dialog_event(zdialog *zd, cchar *event); cchar *title = ZTX("Batch Geotags"); int ii, err; char *file; char location[100], country[100]; char lati[20], longi[20]; cchar *mapquest1 = "Geocoding web service courtesy of"; cchar *mapquest2 = "http://www.mapquest.com"; zdialog *zd; F1_help_topic = "batch_geotags"; if (! init_geolocs()) return; // initialize geotags if (checkpend("all")) return; // check nothing pending (block below) /** Batch Geotags [select files] NN files selected location [______________] country [______________] latitude [_______] longitude [_______] [find] [web] [proceed] [cancel] **/ zd = zdialog_new(title,Mwin,Bfind,Bweb,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"button","files","hb1",Bselectfiles,"space=10"); zdialog_add_widget(zd,"label","labcount","hb1",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labloc","hb2",ZTX("location"),"space=5"); zdialog_add_widget(zd,"zentry","location","hb2",0,"expand"); zdialog_add_widget(zd,"label","space","hb2",0,"space=5"); zdialog_add_widget(zd,"label","labcountry","hb2",ZTX("country"),"space=5"); zdialog_add_widget(zd,"zentry","country","hb2",0,"expand"); zdialog_add_widget(zd,"hbox","hb3","dialog"); zdialog_add_widget(zd,"label","lablat","hb3","Latitude","space=3"); zdialog_add_widget(zd,"zentry","lati","hb3",0,"size=10"); zdialog_add_widget(zd,"label","space","hb3",0,"space=5"); zdialog_add_widget(zd,"label","lablong","hb3","Longitude","space=3"); zdialog_add_widget(zd,"zentry","longi","hb3",0,"size=10"); zdialog_add_widget(zd,"hbox","hbmq","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labmq","hbmq",mapquest1,"space=3"); zdialog_add_widget(zd,"link","MapQuest","hbmq",mapquest2); gallery_select_clear(); // clear gallery_select() file list zd_mapgeotags = zd; // activate map clicks zdialog_run(zd,batch_geotags_dialog_event,"parent"); // run dialog zdialog_wait(zd); // wait for dialog completion if (zd->zstat != 3) goto cleanup; // status not [proceed] if (! GScount) goto cleanup; // no files selected zdialog_fetch(zd,"location",location,100); // get location from dialog zdialog_fetch(zd,"country",country,100); zdialog_fetch(zd,"lati",lati,20); // and latitude, longitude zdialog_fetch(zd,"longi",longi,20); zd_mapgeotags = 0; // deactivate map clicks zdialog_free(zd); // kill dialog if (GScount == 0) goto cleanup; if (checkpend("all")) goto cleanup; // check nothing pending Fblock = 1; popup_report_open(Mwin,"Adding Geotags",500,200); // status monitor popup window for (ii = 0; ii < GScount; ii++) // loop all selected files { zmainloop(); // keep GTK alive file = GSfiles[ii]; // display image err = f_open(file,0,0,0); if (err) continue; if (*location > ' ') strncpy0(meta_location,location,100); // save geotags in image file EXIF if (*country > ' ') strncpy0(meta_country,country,100); // and in search-index file if (*lati > ' ') strncpy0(meta_lati,lati,20); // do not stuff missing data 18.01 if (*longi > ' ') strncpy0(meta_longi,longi,20); Fmetamod++; save_filemeta(file); // update file metadata popup_report_write(0,"%s \n",file); // report progress } popup_report_write(0,"%s \n",Bcompleted); cleanup: Fblock = 0; zd_mapgeotags = 0; // deactivate map clicks if (zd) zdialog_free(zd); return; } // batch_geotags dialog event function int batch_geotags_dialog_event(zdialog *zd, cchar *event) { int yn, zstat, err; char countmess[80]; char location[100], country[100]; char lati[20], longi[20]; cchar *errmess; float flati, flongi; if (strmatch(event,"files")) // select images to add tags { gallery_select_clear(); // clear gallery_select() file list zdialog_show(zd,0); // hide parent dialog gallery_select(); // get file list from user zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labcount",countmess); } if (! zd->zstat) return 1; // wait for action button zstat = zd->zstat; zd->zstat = 0; // keep dialog active if (zstat == 1) // [find] { find_location(zd); // find location data via user return 1; } else if (zstat == 2) // [web] { errmess = web_geocode(zd); // look-up in web service if (errmess) zmessageACK(Mwin,errmess); // fail } else if (zstat == 3) // [proceed] { zdialog_fetch(zd,"location",location,100); // get location from dialog zdialog_fetch(zd,"country",country,100); zdialog_fetch(zd,"lati",lati,20); zdialog_fetch(zd,"longi",longi,20); *location = toupper(*location); // capitalize *country = toupper(*country); zdialog_stuff(zd,"location",location); zdialog_stuff(zd,"country",country); if (*lati > ' ' && ! strmatch(lati,"null") && // if coordinates present, validate *longi > ' ' && ! strmatch(longi,"null")) { err = validate_latlong(lati,longi,flati,flongi); if (err) goto badcoord; } if (! GScount) goto nofiles; if (*location <= ' ' || *country <= ' ' || *lati <= ' ' || *longi <= ' ') { yn = zmessageYN(Mwin,ZTX("data is incomplete \n proceed?")); if (! yn) return 1; } put_geolocs(zd); // update geolocs table zd->zstat = 3; // OK to proceed zdialog_destroy(zd); } else zdialog_destroy(zd); // [cancel] or [x] return 1; badcoord: zmessageACK(Mwin,ZTX("bad latitude/longitude: %s %s"),lati,longi); return 1; nofiles: zmessageACK(Mwin,Bnofileselected); return 1; } /********************************************************************************/ // Group images by location and date, with a count of images in each group. // Click on a group to get a thumbnail gallery of all images in the group. namespace locs_names { struct grec_t { // image geotags data char *location, *country; // group location char pdate[12]; // nominal group date, yyyymmdd int lodate, hidate; // range, days since 0 CE int count; // images in group }; grec_t *grec = 0; int Ngrec = 0; int locs_groupby, locs_daterange; int pline; int locs_comp(cchar *rec1, cchar *rec2); int locs_comp2(cchar *rec1, cchar *rec2); void locs_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int locs_getdays(cchar *date); } // menu function void m_locations(GtkWidget *, cchar *) { using namespace locs_names; zdialog *zd; int zstat, ii, cc, cc1, cc2; int yn, ww, iix, iig, newgroup; char country[100], location[100], pdate[12]; xxrec_t *xxrec; GtkWidget *textwin; F1_help_topic = "image_locations"; if (Findexvalid == 0) { yn = zmessageYN(Mwin,Bnoindex); // no image index, offer to enable if (yn) index_rebuild(2,0); else return; } if (Findexvalid == 1) zmessage_post_bold(Mwin,2,Boldindex); // warn, index missing new files if (checkpend("all")) return; // check nothing pending /*** Report Image Locations (o) Group by country (o) Group by country/location (o) Group by country/location/date (o) Group by date/country/location Combine within [ xx |-|+] days [proceed] [cancel] ***/ zd = zdialog_new(ZTX("Report Image Locations"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"radio","country","dialog",ZTX("Group by country")); zdialog_add_widget(zd,"radio","location","dialog",ZTX("Group by country/location")); zdialog_add_widget(zd,"radio","date","dialog",ZTX("Group by country/location/date")); zdialog_add_widget(zd,"radio","date2","dialog",ZTX("Group by date/country/location")); zdialog_add_widget(zd,"hbox","hbr","dialog"); zdialog_add_widget(zd,"label","space","hbr",0,"space=10"); zdialog_add_widget(zd,"label","labr1","hbr",ZTX("Combine within"),"space=10"); zdialog_add_widget(zd,"zspin","range","hbr","0|999|1|1"); zdialog_add_widget(zd,"label","labr2","hbr",ZTX("days"),"space=10"); zdialog_stuff(zd,"country",0); zdialog_stuff(zd,"location",1); // default by location zdialog_stuff(zd,"date",0); zdialog_stuff(zd,"date2",0); zdialog_restore_inputs(zd); // 18.01 zdialog_resize(zd,300,0); zdialog_run(zd,null,"parent"); zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"country",iix); if (iix) locs_groupby = 1; // group country zdialog_fetch(zd,"location",iix); if (iix) locs_groupby = 2; // group country/location zdialog_fetch(zd,"date",iix); if (iix) locs_groupby = 3; // group country/location/date-range zdialog_fetch(zd,"date2",iix); if (iix) locs_groupby = 4; // group date-range/country/location zdialog_fetch(zd,"range",locs_daterange); // combine recs within date range zdialog_free(zd); if (Ngrec) { // free prior memory for (iix = 0; iix < Ngrec; iix++) { if (grec[iix].location) zfree(grec[iix].location); if (grec[iix].country) zfree(grec[iix].country); } zfree(grec); } cc = Nxxrec * sizeof(grec_t); // allocate memory grec = (grec_t *) zmalloc(cc); memset(grec,0,cc); if (! Nxxrec) { zmessageACK(Mwin,"no geotags data found"); return; } for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { xxrec = xxrec_tab[ii]; grec[ii].location = zstrdup(xxrec->location); // get location and country 17.01 grec[ii].country = zstrdup(xxrec->country); strncpy0(grec[ii].pdate,xxrec->pdate,9); // photo date, truncate to yyyymmdd grec[ii].lodate = locs_getdays(xxrec->pdate); // days since 0 CE grec[ii].hidate = grec[ii].lodate; } Ngrec = Nxxrec; if (Ngrec > 1) // sort grecs by country/location/date HeapSort((char *) grec, sizeof(grec_t), Ngrec, locs_comp); iig = 0; // 1st group from grec[0] grec[iig].count = 1; // group count = 1 for (iix = 1; iix < Ngrec; iix++) // scan following grecs { newgroup = 0; if (! strmatch(grec[iix].country,grec[iig].country)) newgroup = 1; // new country >> new group if (locs_groupby >= 2) // new location >> new group if (! strmatch(grec[iix].location,grec[iig].location)) newgroup = 1; // if group by location if (locs_groupby >= 3) if (grec[iix].lodate - grec[iig].hidate > locs_daterange) // new date >> new group if group by date newgroup = 1; // and date out of range if (newgroup) { iig++; // new group if (iix > iig) { grec[iig] = grec[iix]; // copy and pack down grec[iix].location = grec[iix].country = 0; // no zfree() } grec[iig].count = 1; // group count = 1 } else { zfree(grec[iix].location); // same group zfree(grec[iix].country); // free memory grec[iix].location = grec[iix].country = 0; grec[iig].hidate = grec[iix].lodate; // expand group date-range grec[iig].count++; // increment group count } } Ngrec = iig + 1; // unique groups count if (locs_groupby == 1) ww = 350; // group country if (locs_groupby == 2) ww = 600; // group country/location if (locs_groupby == 3) ww = 650; // group country/location/date-range if (locs_groupby == 4) ww = 650; // group date-range/country/location textwin = popup_report_open(Mwin,ZTX("Image Locations"),ww,400); // write groups to popup window if (locs_groupby == 1) // group by country { popup_report_header(1,"%-30s %5s","Country","Count"); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,30); cc1 = 30 + strlen(country) - utf8len(country); popup_report_write(0,"%-*s %5d \n",cc1,country,grec[iig].count); } } if (locs_groupby == 2) // group by country/location { popup_report_header(1,"%-30s %-30s %5s","Country","Location","Count"); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,30); cc1 = 30 + strlen(country) - utf8len(country); utf8substring(location,grec[iig].location,0,30); cc2 = 30 + strlen(location) - utf8len(location); popup_report_write(0,"%-*s %-*s %5d \n",cc1,country,cc2,location,grec[iig].count); } } if (locs_groupby == 3) // group by country/location/date-range { popup_report_header(1,"%-26s %-26s %-10s %5s","Country","Location","Date","Count"); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,26); // get graphic cc for UTF-8 names cc1 = 26 + strlen(country) - utf8len(country); utf8substring(location,grec[iig].location,0,26); cc2 = 26 + strlen(location) - utf8len(location); strncpy(pdate,grec[iig].pdate,8); // date, yyyymmdd if (! strmatch(pdate,"null")) { memcpy(pdate+8,pdate+6,2); // convert to yyyy-mm-dd memcpy(pdate+5,pdate+4,2); pdate[4] = pdate[7] = '-'; pdate[10] = 0; } popup_report_write(0,"%-*s %-*s %-10s %6d \n",cc1,country,cc2,location,pdate,grec[iig].count); } } if (locs_groupby == 4) // group by date-range/country/location { if (Ngrec > 1) // re-sort by date/country/location HeapSort((char *) grec, sizeof(grec_t), Ngrec, locs_comp2); popup_report_header(1,"%-10s %-26s %-26s %5s","Date","Country","Location","Count"); for (iig = 0; iig < Ngrec; iig++) { utf8substring(country,grec[iig].country,0,26); // get graphic cc for UTF-8 names cc1 = 26 + strlen(country) - utf8len(country); utf8substring(location,grec[iig].location,0,26); cc2 = 26 + strlen(location) - utf8len(location); strncpy(pdate,grec[iig].pdate,8); // date, yyyymmdd if (! strmatch(pdate,"null")) { memcpy(pdate+8,pdate+6,2); // convert to yyyy-mm-dd memcpy(pdate+5,pdate+4,2); pdate[4] = pdate[7] = '-'; pdate[10] = 0; } popup_report_write(0,"%-10s %-*s %-*s %6d \n",pdate,cc1,country,cc2,location,grec[iig].count); } } textwidget_set_callbackfunc(textwin,locs_clickfunc); // response function for mouse click pline = 0; return; } // Compare 2 grec records by geotags and date, // return < 0 = 0 > 0 for rec1 < = > rec2. int locs_names::locs_comp(cchar *rec1, cchar *rec2) { using namespace locs_names; int ii; char * country1 = ((grec_t *) rec1)->country; // compare countries char * country2 = ((grec_t *) rec2)->country; ii = strcmp(country1,country2); if (ii) return ii; char * loc1 = ((grec_t *) rec1)->location; // compare cities char * loc2 = ((grec_t *) rec2)->location; ii = strcmp(loc1,loc2); if (ii) return ii; int date1 = ((grec_t *) rec1)->lodate; // compare dates int date2 = ((grec_t *) rec2)->lodate; ii = date1 - date2; return ii; } // Compare 2 grec records by date and geotags, // return < 0 = 0 > 0 for rec1 < = > rec2. int locs_names::locs_comp2(cchar *rec1, cchar *rec2) { using namespace locs_names; int ii; int date1 = ((grec_t *) rec1)->lodate; // compare dates int date2 = ((grec_t *) rec2)->lodate; ii = date1 - date2; if (ii) return ii; char * country1 = ((grec_t *) rec1)->country; // compare countries char * country2 = ((grec_t *) rec2)->country; ii = strcmp(country1,country2); if (ii) return ii; char * loc1 = ((grec_t *) rec1)->location; // compare cities char * loc2 = ((grec_t *) rec2)->location; ii = strcmp(loc1,loc2); return ii; } // convert yyyymmdd date into days from 0 C.E. // "null" date returns 999999 (year 2737) int locs_names::locs_getdays(cchar *date) { using namespace locs_names; int year, month, day; char temp[8]; int montab[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; int elaps; year = month = day = 0; strncpy0(temp,date,5); year = atoi(temp); strncpy0(temp,date+4,3); month = atoi(temp); strncpy0(temp,date+6,3); day = atoi(temp); elaps = 365 * (year-1) + (year-1) / 4; // elapsed days in prior years elaps += montab[month-1]; // + elapsed days in prior months if (year % 4 == 0 && month > 2) elaps += 1; // + 1 for Feb. 29 elaps += day-1; // + elapsed days in month return elaps; } // Receive clicks on report window and generate gallery of images // matching the selected country/location/date void locs_names::locs_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace locs_names; int ii, cc, nn; int lodate, hidate, datex; static char matchtext[20]; char location[100], country[100]; FILE *fid; xxrec_t *xxrec; if (checkpend("all")) return; // check nothing pending if (line >= 0) // line clicked { textwidget_scroll(widget,line); // keep line on screen 18.01 textwidget_highlight_line(widget,line); // highlight pline = line; // remember last line selected *matchtext = 0; } else // KBkey pressed { if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); *matchtext = 0; return; } if (kbkey >= 0xfd00) { if (kbkey == GDK_KEY_Up) pline--; // KB arrow key navigation if (kbkey == GDK_KEY_Down) pline++; if (kbkey == GDK_KEY_Page_Up) pline -= 10; if (kbkey == GDK_KEY_Page_Down) pline += 10; if (kbkey == GDK_KEY_Home) pline = 0; if (kbkey == GDK_KEY_End) pline = Ngrec - 1; if (pline < 0) pline = 0; if (pline > Ngrec-1) pline = Ngrec - 1; textwidget_highlight_line(widget,pline); // highlight line *matchtext = 0; } else { // other keys - look for matching text cc = strlen(matchtext); if (cc >= 19) return; matchtext[cc] = kbkey; matchtext[cc+1] = 0; nn = textwidget_find(widget,matchtext,pline); // highlight matching text if (nn >= 0) pline = nn; } } textwidget_scroll(widget,pline); // keep line on screen 18.01 strncpy0(country,grec[pline].country,99); // selected country/location/date-range strncpy0(location,grec[pline].location,99); lodate = grec[pline].lodate; hidate = grec[pline].hidate; fid = fopen(searchresults_file,"w"); // open output file if (! fid) goto filerror; for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; if (! strmatch(xxrec->country,country)) continue; // no country match if (locs_groupby >= 2) // bugfix 17.01 if (! strmatch(xxrec->location,location)) continue; // no location match 17.01 if (locs_groupby >= 3) { datex = locs_getdays(xxrec->pdate); if (datex < lodate || datex > hidate) continue; // no date match } fprintf(fid,"%s\n",xxrec->file); // output matching file } fclose(fid); free_resources(); navi::Fpreload_thumbs_block = 1; // stop rapid gallery switch crash 18.01 navi::gallerytype = SEARCH; // search results gallery(searchresults_file,"initF",0); // generate gallery of matching files gallery(0,"paint",0); m_viewmode(0,"G"); navi::Fpreload_thumbs_block = 0; return; filerror: zmessageACK(Mwin,"file error: %s",strerror(errno)); return; } /********************************************************************************/ // Produce a report of image counts by year and month. // Click on a report line to get a thumbnail gallery of images. namespace timeline_names { int Nyears = 2100; int Nperds = 12 * Nyears; int Nyears2 = 0; cchar *months = ZTX("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); int colpos[14] = { 0, 6, 13, 18, 23, 28, 33, 38, 43, 48, 53, 58, 63, 68 }; } // menu function void m_timeline(GtkWidget *, cchar *) { using namespace timeline_names; void timeline_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int Ycount[Nyears], Pcount[Nperds]; // image counts per year and period int Ncount, Ecount; // counts for "null" and invalid dates int yn, ii, jj, cc; int yy, mm, pp; char pdate[8], nnnnn[8], buff[100]; xxrec_t *xxrec; GtkWidget *textwin; F1_help_topic = "image_timeline"; if (Findexvalid == 0) { yn = zmessageYN(Mwin,Bnoindex); // no image index, offer to enable if (yn) index_rebuild(2,0); else return; } if (Findexvalid == 1) zmessage_post_bold(Mwin,2,Boldindex); // warn, index missing new files if (checkpend("all")) return; // check nothing pending Ncount = Ecount = 0; // clear null and error counts for (yy = 0; yy < Nyears; yy++) // clear totals per year Ycount[yy] = 0; for (pp = 0; pp < Nperds; pp++) // clear totals per period (month) Pcount[pp] = 0; for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; strncpy0(pdate,xxrec->pdate,7); // photo date, truncate to yyyymm if (strmatch(pdate,"null")) { // if null, add to null count ++Ncount; continue; } jj = atoi(pdate); // photo date, 0 to 209912 yy = jj / 100; // year, 0000 to 2099 mm = jj - yy * 100; // month, 1 to 12 if (yy < 0 || yy >= Nyears || mm < 1 || mm > 12) { ++Ecount; // invalid, add to error count continue; } ++Ycount[yy]; // add to year totals pp = yy * 12 + mm - 1; // add to period totals ++Pcount[pp]; } textwin = popup_report_open(Mwin,"Image Timeline",600,400); // write report to popup window popup_report_header(1,"year count %s",months); // "year count Jan Feb ... " if (Ncount) popup_report_write(0,"null %-6d \n",Ncount); // images with no date if (Ecount) popup_report_write(0,"invalid %-4d \n",Ecount); // images with invalid date Nyears2 = 0; for (yy = 0; yy < Nyears; yy++) // loop years { if (! Ycount[yy]) continue; // omit years without images snprintf(buff,100,"%04d %-6d ",yy,Ycount[yy]); // output "yyyy NNNNNN " cc = 13; for (mm = 0; mm < 12; mm++) { // loop months 0 - 11 pp = yy * 12 + mm; // period snprintf(nnnnn,6,"%-5d",Pcount[pp]); // output "NNNNN" strncpy(buff+cc,nnnnn,5); cc += 5; } buff[cc] = 0; popup_report_write(0,"%s \n",buff); Nyears2++; // count reported years } textwidget_scroll(textwin,-1); // scroll to bottom of report textwidget_set_callbackfunc(textwin,timeline_clickfunc); // response function for mouse click return; } // Receive clicks on report window and generate gallery of images // matching the selected period void timeline_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace timeline_names; int ii, jj, cc; int Fnull = 0, Finvalid = 0; int yy, mm; static int pline, ppos; char *txline, pdate[8], *pp, end; FILE *fid; xxrec_t *xxrec; if (checkpend("all")) return; // check nothing pending if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } if (line == -1) // arrow key navigation 18.01 { for (ii = 0; ii < 14; ii++) // current report column if (ppos == colpos[ii]) break; if (kbkey == GDK_KEY_Left) { // prior month if (ii > 2) ppos = colpos[ii-1]; else { pline -= 1; ppos = colpos[13]; } } else if (kbkey == GDK_KEY_Right) { // next month if (ii < 13) ppos = colpos[ii+1]; else { pline += 1; ppos = colpos[2]; } } else if (kbkey == GDK_KEY_Up) pline -= 1; // prior year else if (kbkey == GDK_KEY_Down) pline += 1; // next year line = pline; pos = ppos; } if (line < 0) line = 0; if (line > Nyears2) line = Nyears2; // 18.01 if (pos < 0) pos = 0; for (ii = 0; ii < 14; ii++) if (pos < colpos[ii]) break; pos = colpos[ii-1]; textwidget_scroll(widget,line); // keep line on screen pline = line; // remember chosen line, position 18.01 ppos = pos; pp = textwidget_word(widget,line,pos," ",end); // hilite clicked word if (pp) textwidget_highlight_word(widget,line,pos,strlen(pp)); txline = textwidget_line(widget,line,1); // get clicked line if (! txline || ! *txline) return; cc = 0; if (strmatchN(txline,"null",4)) Fnull = 1; // find images with null date else if (strmatchN(txline,"invalid",7)) Finvalid = 1; // find images with invalid date else if (pos < 13) { // clicked on year or year count strncpy0(pdate,txline,5); // have "yyyy" cc = 4; } else // month was clicked { mm = (pos - 13) / 5 + 1; // month, 1-12 if (mm < 1 || mm > 12) return; strncpy(pdate,txline,4); // "yyyy" pdate[4] = '0' + mm/10; pdate[5] = '0' + mm % 10; // have "yyyymm" pdate[6] = 0; cc = 6; } fid = fopen(searchresults_file,"w"); // open output file if (! fid) goto filerror; for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; if (Fnull) { // search for missing dates if (strmatch(xxrec->pdate,"null")) { fprintf(fid,"%s\n",xxrec->file); continue; } } else if (Finvalid) { // search for invalid dates if (strmatch(xxrec->pdate,"null")) continue; // reject "null" strncpy0(pdate,xxrec->pdate,7); // yyyymm jj = atoi(pdate); // 0 to 209912 yy = jj / 100; // 0 to 2099 mm = jj - yy * 100; // 1 to 12 if (yy < 0 || yy >= Nyears || mm < 1 || mm > 12) { fprintf(fid,"%s\n",xxrec->file); continue; } } else if (strmatchN(xxrec->pdate,pdate,cc)) { // screen for desired period if (strmatch(xxrec->pdate,"null")) continue; // reject "null" strncpy0(pdate,xxrec->pdate,7); // yyyymm jj = atoi(pdate); // 0 to 209912 yy = jj / 100; // 0 to 2099 mm = jj - yy * 100; // 1 to 12 if (yy < 0 || yy >= Nyears || mm < 1 || mm > 12) continue; // invalid, reject fprintf(fid,"%s\n",xxrec->file); // output matching file } } fclose(fid); free_resources(); navi::Fpreload_thumbs_block = 1; // stop rapid gallery switch crash 18.01 navi::gallerytype = SEARCH; // search results gallery(searchresults_file,"initF",0); // generate gallery of matching files gallery(0,"paint",0); m_viewmode(0,"G"); navi::Fpreload_thumbs_block = 0; return; filerror: zmessageACK(Mwin,"file error: %s",strerror(errno)); return; } /********************************************************************************/ // Search image tags, geotags, dates, stars, comments, captions // to find matching images. This is fast using the image index. // Search also any other metadata, but relatively slow. namespace search_images { zdialog *zdsearchimages = 0; // search images dialog char searchDateFrom[20] = ""; // search images char searchDateTo[20] = ""; // format is "yyyy-mm-dd hh:mm" 18.01 char searchdatelo[16], searchdatehi[16]; // format is yyyymmddhhmmss 18.01 char searchStarsFrom[4] = ""; char searchStarsTo[4] = ""; char searchtags[tagScc] = ""; // search tags list char searchtext[tagScc] = ""; // search comments & captions word list char searchfiles[tagScc] = ""; // search files list char searchLocations[200] = ""; // search locations int Fscanall, Fscancurr, Fnewset, Faddset, Fremset; int Fdates, Ftext, Ffiles, Ftags, Fstars, Flocs; int Fphotodate, Ffiledate; int Flastver, Fmeta; int Falltags, Falltext, Fallfiles, Falllocs; int Frepgallery, Frepmeta; int Nsearchkeys = 0; char *searchkeys[5]; // metadata keys to search char *searchvals[5]; // data values to search for char matchtype[5]; // match type: string or number < = > int keyindexed[5]; // key included in indexed metadata int allkeysindexed; // all search keys are indexed } using namespace search_images; // menu function void m_search_images(GtkWidget *, cchar *) { void search_searchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void search_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); void search_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); int searchimages_dialog_event(zdialog*, cchar *event); int yn; zdialog *zd; GtkWidget *widget; F1_help_topic = "search_images"; if (Findexvalid == 0) { yn = zmessageYN(Mwin,Bnoindex); // no image index, offer to enable if (yn) index_rebuild(2,0); else return; } if (Findexvalid == 1) zmessage_post_bold(Mwin,2,Boldindex); // warn, index missing new files if (checkpend("all")) return; // check nothing pending /*** ___________________________________________________________ | Search Image Metadata | | | | images to search: (o) all (o) current set only | | matching images: (o) new set (o) add to set (o) remove | | report type: (o) gallery (o) metadata | | date range [______________] [______________] | | (o) photo date (o) file date yyyy-mm-dd | | stars range [__] [__] [x] last version only all/any | | search tags [________________________________] (o) (o) | | search text [________________________________] (o) (o) | | search files [________________________________] (o) (o) | | search locations [____________________________] (o) (o) | // 17.08 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | search metadata [Add] (*) | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Enter Search Tag [________________________] | | Matching Tags [_______________________________________] | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | Defined Tags Category [______________________________|v] | | | | | | | | | | | | | | | | | | | | | | | | | | |______________________________________________________| | | | | [clear] [proceed] [cancel] | |___________________________________________________________| ***/ zd = zdialog_new(ZTX("Search Image Metadata"),Mwin,Bclear,Bproceed,Bcancel,null); zdsearchimages = zd; zdialog_add_widget(zd,"hbox","hbs1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labs1","hbs1",ZTX("images to search:"),"space=3"); zdialog_add_widget(zd,"radio","allimages","hbs1",ZTX("all"),"space=3"); zdialog_add_widget(zd,"radio","currset","hbs1",ZTX("current set only"),"space=3"); zdialog_add_widget(zd,"hbox","hbm1","dialog"); zdialog_add_widget(zd,"label","labs1","hbm1",ZTX("matching images:"),"space=3"); zdialog_add_widget(zd,"radio","newset","hbm1",ZTX("new set"),"space=5"); zdialog_add_widget(zd,"radio","addset","hbm1",ZTX("add to set"),"space=5"); zdialog_add_widget(zd,"radio","remset","hbm1",ZTX("remove"),"space=5"); zdialog_add_widget(zd,"hbox","hbrt","dialog"); zdialog_add_widget(zd,"label","labrt","hbrt",ZTX("report type:"),"space=3"); zdialog_add_widget(zd,"radio","repgallery","hbrt",ZTX("gallery"),"space=5"); zdialog_add_widget(zd,"radio","repmeta","hbrt","Metadata","space=5"); zdialog_add_widget(zd,"hbox","hbd1","dialog"); zdialog_add_widget(zd,"label","labD","hbd1",ZTX("date range"),"space=3"); zdialog_add_widget(zd,"zentry","datefrom","hbd1",0,"size=12|space=5"); zdialog_add_widget(zd,"zentry","dateto","hbd1",0,"size=12"); zdialog_add_widget(zd,"hbox","hbd2","dialog"); // 18.01 zdialog_add_widget(zd,"radio","photo date","hbd2",ZTX("photo date"),"space=8"); zdialog_add_widget(zd,"radio","file date","hbd2",ZTX("file date")); zdialog_add_widget(zd,"label","labD","hbd2",ZTX("(yyyy-mm-dd)"),"space=20"); zdialog_add_widget(zd,"hbox","hbstars","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labS","hbstars",ZTX("stars range"),"space=3"); zdialog_add_widget(zd,"zentry","starsfrom","hbstars",0,"size=2"); zdialog_add_widget(zd,"zentry","starsto","hbstars",0,"size=2"); zdialog_add_widget(zd,"check","lastver","hbstars",ZTX("last version only"),"space=15"); zdialog_add_widget(zd,"label","space","hbstars",0,"expand"); zdialog_add_widget(zd,"label","all-any","hbstars",ZTX("all/any"),"space=2"); zdialog_add_widget(zd,"hbox","hbtags","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labtags","hbtags",ZTX("search tags"),"space=3"); zdialog_add_widget(zd,"edit","searchtags","hbtags",0,"expand|wrap"); zdialog_add_widget(zd,"radio","alltags","hbtags",0,"space=3"); zdialog_add_widget(zd,"radio","anytags","hbtags",0,"space=3"); zdialog_add_widget(zd,"hbox","hbtext","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labtext","hbtext",ZTX("search text"),"space=3"); zdialog_add_widget(zd,"zentry","searchtext","hbtext",0,"expand"); zdialog_add_widget(zd,"radio","alltext","hbtext",0,"space=3"); zdialog_add_widget(zd,"radio","anytext","hbtext",0,"space=3"); zdialog_add_widget(zd,"hbox","hbfiles","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labfiles","hbfiles",ZTX("search files"),"space=3"); zdialog_add_widget(zd,"zentry","searchfiles","hbfiles",0,"expand"); zdialog_add_widget(zd,"radio","allfiles","hbfiles",0,"space=3"); zdialog_add_widget(zd,"radio","anyfiles","hbfiles",0,"space=3"); zdialog_add_widget(zd,"hbox","hblocs","dialog",0,"space=2"); zdialog_add_widget(zd,"label","lablocs","hblocs",ZTX("search locations"),"space=3"); zdialog_add_widget(zd,"zentry","searchlocs","hblocs",0,"expand"); zdialog_add_widget(zd,"radio","alllocs","hblocs",0,"space=3"); // 17.08 zdialog_add_widget(zd,"radio","anylocs","hblocs",0,"space=3"); zdialog_add_ttip(zd,"searchlocs",ZTX("enter cities, countries")); // 17.01 zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbmeta","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labmeta","hbmeta",ZTX("search other metadata"),"space=3"); zdialog_add_widget(zd,"button","addmeta","hbmeta",Badd,"space=8"); zdialog_add_widget(zd,"label","metadata#","hbmeta","( )"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbnt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labnt","hbnt",ZTX("Enter Search Tag"),"space=3"); zdialog_add_widget(zd,"zentry","entertag","hbnt",0,"size=20"); zdialog_add_widget(zd,"hbox","hbmt","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labmt","hbmt",ZTX("Matching Tags"),"space=3"); zdialog_add_widget(zd,"text","matchtags","hbmt",0,"wrap|expand"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbdt1","dialog"); zdialog_add_widget(zd,"label","labdt","hbdt1",ZTX("Defined Tags Category"),"space=3"); zdialog_add_widget(zd,"label","space","hbdt1",0,"expand"); zdialog_add_widget(zd,"combo","defcats","hbdt1",0,"expand|space=5"); zdialog_add_widget(zd,"hbox","hbdt2","dialog",0,"expand"); zdialog_add_widget(zd,"frame","frdt2","hbdt2",0,"expand|space=3"); zdialog_add_widget(zd,"scrwin","swdt2","frdt2",0,"expand"); zdialog_add_widget(zd,"text","deftags","swdt2",0,"wrap"); widget = zdialog_widget(zd,"searchtags"); // tag widget mouse functions textwidget_set_callbackfunc(widget,search_searchtags_clickfunc); widget = zdialog_widget(zd,"matchtags"); textwidget_set_callbackfunc(widget,search_matchtags_clickfunc); widget = zdialog_widget(zd,"deftags"); textwidget_set_callbackfunc(widget,search_deftags_clickfunc); zdialog_stuff(zd,"allimages",1); // defaults zdialog_stuff(zd,"currset",0); zdialog_stuff(zd,"newset",1); zdialog_stuff(zd,"addset",0); zdialog_stuff(zd,"remset",0); zdialog_stuff(zd,"repgallery",1); zdialog_stuff(zd,"repmeta",0); zdialog_stuff(zd,"lastver",0); zdialog_stuff(zd,"alltags",0); zdialog_stuff(zd,"anytags",1); zdialog_stuff(zd,"alltext",0); zdialog_stuff(zd,"anytext",1); zdialog_stuff(zd,"allfiles",0); zdialog_stuff(zd,"anyfiles",1); zdialog_stuff(zd,"alllocs",0); // 17.08 zdialog_stuff(zd,"anylocs",1); zdialog_restore_inputs(zd); // preload prior user inputs load_deftags(); // stuff defined tags into dialog deftags_stuff(zd,"ALL"); defcats_stuff(zd); // and defined categories zdialog_resize(zd,0,700); // start dialog zdialog_run(zd,searchimages_dialog_event,"parent"); zdialog_wait(zd); // wait for dialog completion zdialog_free(zd); return; } // search tag was clicked void search_searchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;:",end); if (! txtag) return; del_tag(txtag,searchtags); // remove from search list zdialog_stuff(zdsearchimages,"searchtags",searchtags); zfree(txtag); return; } // matching tag was clicked void search_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;",end); if (! txtag) return; add_tag(txtag,searchtags,tagScc); // add to search tag list zdialog_stuff(zdsearchimages,"entertag",""); // update dialog widgets zdialog_stuff(zdsearchimages,"matchtags",""); zdialog_stuff(zdsearchimages,"searchtags",searchtags); zdialog_goto(zdsearchimages,"entertag"); // focus back to entertag widget zfree(txtag); return; } // defined tag was clicked void search_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { char *txtag, end = 0; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } txtag = textwidget_word(widget,line,pos,",;:",end); if (! txtag || end == ':') return; // tag category clicked, ignore add_tag(txtag,searchtags,tagScc); // add to search tag list zdialog_stuff(zdsearchimages,"searchtags",searchtags); zfree(txtag); return; } // search images dialog event and completion callback function int searchimages_dialog_event(zdialog *zd, cchar *event) // overhauled { using namespace navi; int datetimeOK(char *datetime); int searchimages_select(xxrec_t *xxrec); int searchimages_metadata_dialog(zdialog *zd); int searchimages_metadata_report(); cchar dateLoDefault[20] = "0000-01-01 00:00:00"; // start of time 18.01 cchar dateHiDefault[20] = "2099-12-31 23:59:59"; // end of fime char *file; char **flist, *pp, buffer[XFCC]; int match, ii, jj, err, cc; int nt, cc1, cc2, ff; int Nadded, Nremoved, Nleft, Npver; xxrec_t *xxrec; FILE *fid; char *pp1, *pp2; char entertag[tagcc], matchtags[20][tagcc]; char matchtagstext[(tagcc+2)*20]; char catgname[tagcc]; char albumfile[200]; if (strmatch(event,"addmeta")) // get other metadata criteria Fmeta = searchimages_metadata_dialog(zd); if (Fmeta) zdialog_stuff(zd,"metadata#","(*)"); // show presense of metadata criteria else zdialog_stuff(zd,"metadata#","(_)"); if (zd->zstat == 1) // clear selection criteria { zdialog_stuff(zd,"allimages",1); zdialog_stuff(zd,"currset",0); zdialog_stuff(zd,"newset",1); zdialog_stuff(zd,"addset",0); zdialog_stuff(zd,"remset",0); zdialog_stuff(zd,"repgallery",1); zdialog_stuff(zd,"repmeta",0); zdialog_stuff(zd,"lastver",0); zdialog_stuff(zd,"alltags",0); zdialog_stuff(zd,"anytags",1); zdialog_stuff(zd,"alltext",0); zdialog_stuff(zd,"anytext",1); zdialog_stuff(zd,"allfiles",0); zdialog_stuff(zd,"anyfiles",1); zdialog_stuff(zd,"datefrom",""); zdialog_stuff(zd,"dateto",""); zdialog_stuff(zd,"photo date",1); // 18.01 zdialog_stuff(zd,"starsfrom",""); zdialog_stuff(zd,"starsto",""); zdialog_stuff(zd,"searchtags",""); zdialog_stuff(zd,"searchtext",""); zdialog_stuff(zd,"searchfiles",""); zdialog_stuff(zd,"searchlocs",""); *searchtags = 0; Flocs = 0; *searchLocations = 0; zdialog_stuff(zd,"metadata#","( )"); Fmeta = 0; Nsearchkeys = 0; zd->zstat = 0; // keep dialog active return 1; } if (strmatch(event,"entertag")) // new tag is being typed in { zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog zdialog_fetch(zd,"entertag",entertag,tagcc); // get chars. typed so far cc1 = strlen(entertag); for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters if (strchr(",:;",entertag[ii])) continue; entertag[jj++] = entertag[ii]; } if (jj < cc1) { // something was removed entertag[jj] = 0; cc1 = jj; zdialog_stuff(zd,"entertag",entertag); } if (cc1 < 2) return 1; // wait for at least 2 chars. for (ii = nt = 0; ii < maxtagcats; ii++) // loop all categories { pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, if (! pp2) continue; // | | pp2 = strchr(pp2,':'); // pp1 pp2 while (true) // loop all deftags in category { pp1 = pp2 + 2; if (! *pp1) break; pp2 = strchr(pp1,','); if (! pp2) break; if (strmatchcaseN(entertag,pp1,cc1)) { // deftag matches chars. typed so far cc2 = pp2 - pp1; strncpy(matchtags[nt],pp1,cc2); // save deftags that match matchtags[nt][cc2] = 0; if (++nt == 20) return 1; // quit if 20 matches or more } } } if (nt == 0) return 1; // no matches pp1 = matchtagstext; for (ii = 0; ii < nt; ii++) // make deftag list: aaaaa, bbb, cccc ... { strcpy(pp1,matchtags[ii]); pp1 += strlen(pp1); strcpy(pp1,", "); pp1 += 2; } zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog return 1; } if (strmatch(event,"defcats")) { // new tag category selection zdialog_fetch(zd,"defcats",catgname,tagcc); deftags_stuff(zd,catgname); } if (! zd->zstat) return 1; // wait for dialog completion if (zd->zstat != 2) return 1; // cancel if not [proceed] // inputs are complete. validate all inputs. zdialog_fetch(zd,"allimages",Fscanall); // search all images zdialog_fetch(zd,"currset",Fscancurr); // search current set (gallery) zdialog_fetch(zd,"newset",Fnewset); // matching images --> new set zdialog_fetch(zd,"addset",Faddset); // add matching image to set zdialog_fetch(zd,"remset",Fremset); // remove matching images from set if (Fremset && Fscanall) { // illogical search zmessageACK(Mwin,ZTX("to remove images from current set, \n" "search current set")); zd->zstat = 0; // keep dialog active return 1; } if (Faddset && Fscancurr) { zmessageACK(Mwin,ZTX("to add images to current set, \n" "search all images")); zd->zstat = 0; // keep dialog active return 1; } zdialog_fetch(zd,"repgallery",Frepgallery); // gallery report zdialog_fetch(zd,"repmeta",Frepmeta); // metadata report zdialog_fetch(zd,"lastver",Flastver); // get last versions only zdialog_fetch(zd,"datefrom",searchDateFrom,20); // get search date range zdialog_fetch(zd,"dateto",searchDateTo,20); zdialog_fetch(zd,"photo date",Fphotodate); // photo or file date 18.01 zdialog_fetch(zd,"file date",Ffiledate); zdialog_fetch(zd,"starsfrom",searchStarsFrom,2); // get search stars range zdialog_fetch(zd,"starsto",searchStarsTo,2); zdialog_fetch(zd,"searchtags",searchtags,tagScc); // get search tags zdialog_fetch(zd,"searchtext",searchtext,tagScc); // get search text* zdialog_fetch(zd,"searchfiles",searchfiles,tagScc); // get search /path*/file* zdialog_fetch(zd,"searchlocs",searchLocations,200); // get search locations zdialog_fetch(zd,"alltags",Falltags); // get match all/any options zdialog_fetch(zd,"alltext",Falltext); zdialog_fetch(zd,"allfiles",Fallfiles); zdialog_fetch(zd,"alllocs",Falllocs); // 17.08 Fdates = 0; if (*searchDateFrom) Fdates++; // search date from was given else strcpy(searchDateFrom,dateLoDefault); // else search from start of time if (*searchDateTo) Fdates++; // search date to was given else strcpy(searchDateTo,dateHiDefault); // else search to end of time if (Fdates) { // complete partial date/time data cc = strlen(searchDateFrom); for (ii = cc; ii < 20; ii++) // default date from: searchDateFrom[ii] = dateLoDefault[ii]; // 0000-01-01 00:00:00 cc = strlen(searchDateTo); for (ii = cc; ii < 20; ii++) // default date to: searchDateTo[ii] = dateHiDefault[ii]; // 2099-12-31 23:59:59 ff = 0; // check search dates reasonable if (! datetimeOK(searchDateFrom)) ff = 1; // invalid year/mon/day (e.g. mon 13) if (! datetimeOK(searchDateTo)) ff = 1; // or hour/min/sec (e.g. hour 33) if (strcmp(searchDateFrom,searchDateTo) >= 0) ff = 1; // search from date >= search to date if (ff) { zmessageACK(Mwin,ZTX("search dates not reasonable \n %s %s"), searchDateFrom,searchDateTo); zd->zstat = 0; return 1; } pp = pdatetime_metadatetime(searchDateFrom); // convert to yyyymmddhhmmss 18.01 if (! pp) return 1; // for metadata comparisons strncpy0(searchdatelo,pp,16); pp = pdatetime_metadatetime(searchDateTo); if (! pp) return 1; strncpy0(searchdatehi,pp,16); } Fstars = 0; if (*searchStarsFrom || *searchStarsTo) { Fstars = 1; // stars was given ii = *searchStarsFrom; if (! ii) ii = '0'; if (ii < '0' || ii > '5') Fstars = 0; // validate inputs jj = *searchStarsTo; if (! jj) jj = '5'; if (jj < '0' || jj > '5') Fstars = 0; if (jj < ii) Fstars = 0; if (! Fstars) { zmessageACK(Mwin,ZTX("stars range not reasonable")); zd->zstat = 0; return 1; } } Ffiles = 0; if (! blank_null(searchfiles)) Ffiles = 1; // search path / file (fragment) was given Ftext = 0; if (! blank_null(searchtext)) Ftext = 1; // search text was given Ftags = 0; if (! blank_null(searchtags)) Ftags = 1; // search tags was given Flocs = 0; if (*searchLocations) Flocs = 1; // search locations was given Fmeta = 0; if (Nsearchkeys) Fmeta = 1; // search other metadata was given if (checkpend("all")) return 1; // check nothing pending Ffuncbusy = 1; Fblock = 1; Nadded = Nremoved = Npver = Nleft = 0; // image counts // search all images and keep those meeting search criteria // result is gallery of images meeting criteria if (Fscanall && Fnewset) { fid = fopen(searchresults_file,"w"); // open output file if (! fid) goto filerror; for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; match = searchimages_select(xxrec); // test against select criteria if (match) { // all criteria passed Nadded++; // count matches fprintf(fid,"%s\n",xxrec->file); // save matching filename } } fclose(fid); Nleft = Nadded; } // search all images and add those meeting search criteria // to current image set (gallery) if (Fscanall && Faddset) { fid = fopen(searchresults_file,"w"); // open output file if (! fid) goto filerror; for (ii = 0; ii < navi::Nfiles; ii++) // scan current gallery { file = gallery(0,"get",ii); if (! file) break; if (*file != '!') { // skip directories fprintf(fid,"%s\n",file); // add image files to output Nleft++; } zfree(file); // free memory } for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; match = searchimages_select(xxrec); // test against select criteria if (match) { // all criteria passed Nadded++; // count matches fprintf(fid,"%s\n",xxrec->file); // save matching filename } } fclose(fid); Nleft += Nadded; } // search current image set and keep only those meeting search criteria if (Fscancurr && Fnewset) { fid = fopen(searchresults_file,"w"); // open output file if (! fid) goto filerror; for (ii = 0; ii < navi::Nfiles; ii++) // scan current gallery { zmainloop(100); // keep GTK alive file = gallery(0,"get",ii); if (! file) break; if (*file == '!') { // skip directories zfree(file); continue; } xxrec = get_xxrec(file); if (! xxrec) { // no image index rec? zfree(file); continue; } match = searchimages_select(xxrec); // test against select criteria if (match) { // passed Nleft++; // count retained images fprintf(fid,"%s\n",file); // save retained filename } else Nremoved++; zfree(file); // free memory } fclose(fid); } // search current image set and remove those meeting search criteria if (Fscancurr && Fremset) { fid = fopen(searchresults_file,"w"); // open output file if (! fid) goto filerror; for (ii = 0; ii < navi::Nfiles; ii++) // scan current gallery { zmainloop(100); // keep GTK alive file = gallery(0,"get",ii); if (! file) break; if (*file == '!') { // skip directories zfree(file); continue; } xxrec = get_xxrec(file); if (! xxrec) { // no image index rec? zfree(file); continue; } match = searchimages_select(xxrec); // test against select criteria if (! match) { // failed Nleft++; fprintf(fid,"%s\n",file); // save retained filename } else Nremoved++; zfree(file); // free memory } fclose(fid); } if (Flastver && Nleft) // remove all but latest versions { cc = Nleft * sizeof(char *); flist = (char **) zmalloc(cc); fid = fopen(searchresults_file,"r"); // read file of selected image files if (! fid) goto filerror; for (ii = 0; ii < Nleft; ii++) // build file list in memory { file = fgets_trim(buffer,XFCC,fid); if (! file) break; flist[ii] = zstrdup(file); } fclose(fid); for (ii = 1; ii < Nleft; ii++) // scan file list in memory { pp = strrchr(flist[ii],'/'); // /directory.../filename.v... if (! pp) continue; // | | pp = strstr(pp,".v"); // flist[ii] pp if (! pp) continue; cc = pp - flist[ii] + 1; if (strmatchN(flist[ii],flist[ii-1],cc)) { // compare each filespec with prior zfree(flist[ii-1]); // match: remove prior from list flist[ii-1] = 0; } } fid = fopen(searchresults_file,"w"); // write remaining file list if (! fid) goto filerror; // to results file Npver = 0; for (ii = 0; ii < Nleft; ii++) { file = flist[ii]; if (file) { fprintf(fid,"%s\n",file); zfree(file); } else Npver++; } fclose(fid); zfree(flist); if (Fscanall) Nadded -= Npver; // adjust counts if (Fscancurr) Nremoved += Npver; Nleft -= Npver; } zmessageACK(Mwin,ZTX("images added: %d removed: %d new count: %d"), Nadded, Nremoved, Nleft); if (Nleft == 0) { zmessageACK(Mwin,ZTX("no changes made")); zd->zstat = 0; // stay in search dialog Ffuncbusy = 0; Fblock = 0; return 1; } snprintf(albumfile,200,"%s/Search Results",albums_dirk); // save search results in the err = copyFile(searchresults_file,albumfile); // album "Search Results" 17.08 if (err) zmessageACK(Mwin,strerror(err)); free_resources(); navi::gallerytype = SEARCH; // search results if (Frepmeta) navi::gallerytype = META; // search results in metadata format 17.04 gallery(searchresults_file,"initF",0); // generate gallery of matching files if (Frepmeta) // metadata report format searchimages_metadata_report(); Ffuncbusy = 0; Fblock = 0; if (Fphotodate) { // seletc and sort by photo date 18.01 gallerysort = PDATE; galleryseq = ASCEND; gallery(0,"sort",-1); } if (Ffiledate) { // select and sort by file mod date 18.01 gallerysort = FDATE; galleryseq = ASCEND; gallery(0,"sort",-1); } gallery(0,"paint",0); // position at top m_viewmode(0,"G"); return 1; filerror: zmessageACK(Mwin,"file error: %s",strerror(errno)); Ffuncbusy = 0; Fblock = 0; return 1; } // test a given image against selection criteria, return match status int searchimages_select(xxrec_t *xxrec) { int searchimages_metadata_select(xxrec_t *xxrec); cchar *pps, *ppf; int iis, iif; int Nmatch, Nnomatch; if (Ffiles) // file name match is wanted { Nmatch = Nnomatch = 0; for (iis = 1; ; iis++) { pps = strField(searchfiles,' ',iis); // step thru search file names if (! pps) break; ppf = strcasestr(xxrec->file,pps); // compare image file name if (ppf) Nmatch++; else Nnomatch++; } if (Nmatch == 0) return 0; // no match any file if (Fallfiles && Nnomatch) return 0; // no match all files (dir & file names) } if (Fdates) // date match is wanted { if (Fphotodate) { if (strcmp(xxrec->pdate,searchdatelo) < 0) return 0; // test photo date 18.01 if (strcmp(xxrec->pdate,searchdatehi) > 0) return 0; } if (Ffiledate) { // test file mode date 18.01 if (strcmp(xxrec->fdate,searchdatelo) < 0) return 0; if (strcmp(xxrec->fdate,searchdatehi) > 0) return 0; } } if (Ftags) // tags match is wanted { Nmatch = Nnomatch = 0; for (iis = 1; ; iis++) // step thru search tags { pps = strField(searchtags,",;",iis); // delimited if (! pps) break; if (*pps == ' ') continue; for (iif = 1; ; iif++) // step thru file tags (delimited) { ppf = strField(xxrec->tags,",;",iif); if (! ppf) { Nnomatch++; break; } // count matches and fails if (*ppf == ' ') continue; if (strmatch(pps,ppf)) { Nmatch++; break; } // match } } if (Nmatch == 0) return 0; // no match to any tag if (Falltags && Nnomatch) return 0; // no match to all tags } if (Fstars) // rating (stars) match is wanted { if (*searchStarsFrom && xxrec->rating[0] < *searchStarsFrom) return 0; if (*searchStarsTo && xxrec->rating[0] > *searchStarsTo) return 0; } if (Ftext) // text match is wanted { Nmatch = Nnomatch = 0; for (iis = 1; ; iis++) // step through search words { pps = strField(searchtext,' ',iis); if (! pps) break; if (*pps == ' ') continue; ppf = strcasestr(xxrec->capt,pps); // match words in captions and comments if (! ppf) ppf = strcasestr(xxrec->comms,pps); if (ppf) Nmatch++; else Nnomatch++; } if (Nmatch == 0) return 0; // no match to any word if (Falltext && Nnomatch) return 0; // no match to all words } if (Flocs ) // location match is wanted { Nmatch = Nnomatch = 0; for (iis = 1; ; iis++) // step thru search locations { pps = strField(searchLocations,", ",iis); // comma or blank delimiter 17.01 if (! pps) break; if (*pps == ' ') continue; if (strcasestr(xxrec->location,pps)) Nmatch++; // all/any logic 17.08 else if (strcasestr(xxrec->country,pps)) Nmatch++; else Nnomatch++; } if (! Nmatch) return 0; // no match found if (Falllocs && Nnomatch) return 0; } if (Fmeta) // other metadata match if (! searchimages_metadata_select(xxrec)) return 0; return 1; } /********************************************************************************/ // dialog to get metadata search criteria int searchimages_metadata_dialog(zdialog *zdp) // overhauled { int searchimages_metadata_dialog_event(zdialog *zd, cchar *event); cchar *metamess = ZTX("These items are always reported: \n" "date, stars, tags, caption, comment"); zdialog *zd; int ii, jj, zstat; char matchx[8] = "matchx"; /*** Search and Report Metadata These items are always reported: date, stars, tags, caption, comment Additional Items for Report Keyword Match Criteria [__________] [ matches ] [__________________] [__________] [ contains ] [__________________] [__________] [ equal ] [__________________] [__________] [ less ] [__________________] [__________] [ greater ] [__________________] [clear] [apply] [cancel] ***/ zd = zdialog_new("Search and Report Metadata",Mwin,Bclear,Bapply,Bcancel,null); zdialog_add_widget(zd,"label","labmeta","dialog",metamess,"space=5"); zdialog_add_widget(zd,"label","labopts","dialog",ZTX("Additional Items for Report")); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"space=5|homog|expand"); zdialog_add_widget(zd,"label","lab1","vb1",ZTX("Keyword")); zdialog_add_widget(zd,"zentry","key0","vb1"); zdialog_add_widget(zd,"zentry","key1","vb1"); zdialog_add_widget(zd,"zentry","key2","vb1"); zdialog_add_widget(zd,"zentry","key3","vb1"); zdialog_add_widget(zd,"zentry","key4","vb1"); zdialog_add_widget(zd,"label","lab2","vb2"); zdialog_add_widget(zd,"combo","match0","vb2"); zdialog_add_widget(zd,"combo","match1","vb2"); zdialog_add_widget(zd,"combo","match2","vb2"); zdialog_add_widget(zd,"combo","match3","vb2"); zdialog_add_widget(zd,"combo","match4","vb2"); zdialog_add_widget(zd,"label","lab3","vb3",ZTX("Match Criteria")); zdialog_add_widget(zd,"zentry","value0","vb3",0,"expand"); zdialog_add_widget(zd,"zentry","value1","vb3",0,"expand"); zdialog_add_widget(zd,"zentry","value2","vb3",0,"expand"); zdialog_add_widget(zd,"zentry","value3","vb3",0,"expand"); zdialog_add_widget(zd,"zentry","value4","vb3",0,"expand"); if (! searchkeys[0]) // first call initialization { for (ii = 0; ii < 5; ii++) { searchkeys[ii] = (char *) zmalloc(40); searchvals[ii] = (char *) zmalloc(100); *searchkeys[ii] = *searchvals[ii] = 0; } } for (ii = 0; ii < 5; ii++) { matchx[5] = '0' + ii; zdialog_cb_app(zd,matchx,"matches"); zdialog_cb_app(zd,matchx,"contains"); zdialog_cb_app(zd,matchx,"number ="); zdialog_cb_app(zd,matchx,"number =>"); zdialog_cb_app(zd,matchx,"number <="); } zdialog_show(zdp,0); // hide parent dialog zdialog_resize(zd,600,0); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,searchimages_metadata_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); zdialog_show(zdp,1); // restore parent dialog if (zstat != 1) return 0; // no search criteria entered for (ii = 0; ii < Nsearchkeys; ii++) { // loop search keys keyindexed[ii] = 0; for (jj = 0; jj < Mxmeta; jj++) { // compare to xmeta keys if (! xmeta_keys[jj]) break; if (strmatchcase(searchkeys[ii],xmeta_keys[jj])) keyindexed[ii] = 1; // found, search key is indexed } } allkeysindexed = 1; // all search keys indexed for (ii = 0; ii < Nsearchkeys; ii++) if (! keyindexed[ii]) allkeysindexed = 0; // NOT return 1; } // dialog event and completion callback function int searchimages_metadata_dialog_event(zdialog *zd, cchar *event) // overhauled { int ii, jj, err; float fnum; char keyx[8] = "keyx", valuex[8] = "valuex", matchx[8] = "matchx"; char temp[100]; if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) { for (ii = 0; ii < 5; ii++) { // clear keyx[3] = '0' + ii; valuex[5] = '0' + ii; matchx[5] = '0' + ii; zdialog_stuff(zd,keyx,""); zdialog_stuff(zd,valuex,""); zdialog_stuff(zd,matchx,"matches"); zd->zstat = 0; // keep dialog active } return 1; } if (zd->zstat != 2) { Nsearchkeys = 0; // no search keys return 1; } Nsearchkeys = 0; // apply for (ii = jj = 0; ii < 5; ii++) { keyx[3] = '0' + ii; // get search key zdialog_fetch(zd,keyx,searchkeys[ii],40); strCompress(searchkeys[ii]); // remove all blanks from key names if (*searchkeys[ii] <= ' ') continue; memmove(searchkeys[jj],searchkeys[ii],40); // repack blank keys valuex[5] = '0' + ii; zdialog_fetch(zd,valuex,searchvals[ii],100); // get corresp. match value strTrim2(searchvals[jj],searchvals[ii]); // trim leading and trailing blanks matchx[5] = '0' + ii; zdialog_fetch(zd,matchx,temp,20); // get corresp. match type if (strmatch(temp,"matches")) matchtype[jj] = 'm'; if (strmatch(temp,"contains")) matchtype[jj] = 'c'; if (strmatch(temp,"number =")) matchtype[jj] = '='; if (strmatch(temp,"number =>")) matchtype[jj] = '>'; if (strmatch(temp,"number <=")) matchtype[jj] = '<'; if (strstr(temp,"number")) { // check numeric values err = convSF(searchvals[jj],fnum); if (err) { snprintf(temp,100,ZTX("bad number: %s"),searchvals[jj]); zmessageACK(Mwin,temp); zd->zstat = 0; return 1; } } if (ii > jj) *searchkeys[ii] = *searchvals[ii] = 0; jj++; } Nsearchkeys = jj; // keys found, no blanks if (Nsearchkeys) zd->zstat = 1; // keys were entered else zd->zstat = 2; // no keys were entered return 1; } // Test image file metadata against metadata select criteria. // indexed metadata = xmeta: keyname1=keyvalue1^ keyname2=keyvalue2^ ... int searchimages_metadata_select(xxrec_t *xxrec) // 18.01 { int searchmeta_test1(cchar *keyval, cchar matchtype, cchar *searchvals); char *keyvals[5]; int ii, jj, cc, nx = 0, pass = 1; cchar *pps, *ppf; char *file = xxrec->file; char *xmeta = xxrec->xmeta; // indexed metadata list char xkey[Mxmeta][60], xval[Mxmeta][100]; pps = xmeta; // unpack indexed metadata for (ii = 0; ii < Mxmeta; ii++) { // to xkey[], xdata[] ppf = strchr(pps,'='); if (! ppf) break; cc = ppf-pps; if (cc > 59) break; strncpy0(xkey[ii],pps,cc+1); pps = ppf + 1; ppf = strchr(pps,'^'); if (! ppf) break; cc = ppf - pps; if (cc > 99) break; strncpy0(xval[ii],pps,cc+1); pps = ppf + 1; while (*pps == ' ') pps++; } nx = ii; // xmeta keys found for (ii = 0; ii < Nsearchkeys; ii++) { // loop search keys for (jj = 0; jj < nx; jj++) { // find xmeta key if (strmatchcase(searchkeys[ii],xkey[jj])) { // if found, test xmeta data pass = searchmeta_test1(xval[jj],matchtype[ii],searchvals[ii]); // against search criteria if (! pass) return 0; // fail, no more testing needed break; } } if (jj == nx && keyindexed[ii]) { // search key indexed, not present pass = searchmeta_test1(0,matchtype[ii],searchvals[ii]); // test for "null" select criteria if (! pass) return 0; // fail } } if (allkeysindexed) return 1; // all search keys indexed, pass exif_get(file,(cchar **) searchkeys,keyvals,Nsearchkeys); // get metadata from image file for (ii = 0; ii < Nsearchkeys; ii++) { // loop search keys pass = searchmeta_test1(keyvals[ii],matchtype[ii],searchvals[ii]); // test image data if (! pass) break; // fail, no more testing needed } for (ii = 0; ii < Nsearchkeys; ii++) // bugfix: memory leak 18.01 if (keyvals[ii]) zfree(keyvals[ii]); return pass; // all values pass } // test a single metadata key/value against select criteria int searchmeta_test1(cchar *keyval, cchar matchtype, cchar *searchvals) { int nth; cchar *pps, *ppf; float fkey, fval; if (! keyval) // no metadata present { if (strmatch(searchvals,"null")) return 1; // search for empty data, pass return 0; // fail } if (*searchvals <= ' ') return 1; // no search values, pass for (nth = 1; ; nth++) // loop all search values { pps = strField(searchvals,',',nth); // comma delimiter if (! pps) return 0; // no more, no match found if (matchtype == 'm') { // key matches any value if (strmatchcase(keyval,pps)) return 1; // match not case sensitive } else if (matchtype == 'c') { // key contains any value ppf = strcasestr(keyval,pps); // match not case sensitive if (ppf) return 1; // found match } else if (matchtype == '=') { // numeric key equals any value fkey = atof(keyval); fval = atof(pps); if (fkey == fval) return 1; // found match } else if (matchtype == '>') { // numeric key >= one value fkey = atof(keyval); fval = atof(pps); if (fkey >= fval) return 1; // found match } else if (matchtype == '<') { // numeric key <= one value fkey = atof(keyval); fval = atof(pps); if (fkey <= fval) return 1; // found match } else return 0; // should not happen } return 0; } // Report selected metadata using a gallery window layout // with image thumbnails and selected metadata text. int searchimages_metadata_report() { using namespace navi; cchar *keys1[7] = { exif_date_key, iptc_rating_key, iptc_keywords_key, exif_city_key, exif_country_key, iptc_caption_key, exif_comment_key }; cchar *keys[20]; char *file, *kvals[20]; char text1[2*indexrecl], text2[200]; int Nkeys, ii, jj, cc; if (Gmdlist) printz("*** memory leak Gmdlist[] \n"); // 18.01 cc = Nfiles * sizeof(char *); // allocate metadata list Gmdlist = (char **) zmalloc(cc); // Nfiles = curr. gallery files memset(Gmdlist,0,cc); // Gmdlist = corresp. metadata list for (ii = 0; ii < 7; ii++) // set first 6 key names, fixed keys[ii] = keys1[ii]; for (ii = 0; ii < Nsearchkeys; ii++) // remaining key names from user keys[ii+7] = searchkeys[ii]; Nkeys = 7 + Nsearchkeys; // total keys to extract Gmdrows = Nkeys - 1; // report rows (location/country 1 row) Fbusy_goal = Nfiles; Fbusy_done = 0; for (ii = 0; ii < Nfiles; ii++) // loop image gallery files { zmainloop(20); // keep GTK alive file = gallery(0,"get",ii); if (! file) continue; exif_get(file,keys,kvals,Nkeys); // get the metadata snprintf(text2,200,"%s %s",kvals[3],kvals[4]); // combine location and country if (kvals[3]) zfree(kvals[3]); if (kvals[4]) zfree(kvals[4]); kvals[3] = zstrdup(text2); kvals[4] = 0; for (cc = jj = 0; jj < Nkeys; jj++) // add metadata to report { if (jj == 4) continue; // skip country if (jj == 0 && kvals[0]) kvals[0][4] = kvals[0][7] = '-'; // conv. yyyy:mm:dd to yyyy-mm-dd if (jj == 3) snprintf(text2,200,"Location: %s \n",kvals[jj]); // replace "city" with "location" else snprintf(text2,200,"%s: %s \n",keys[jj], kvals[jj]); strcpy(text1+cc,text2); cc += strlen(text2); } Gmdlist[ii] = zstrdup(text1); // GFlist entry --> Gmdlist entry 18.01 GFlist[ii].mdindex = ii; // (GFlist can be later sorted) for (jj = 0; jj < Nkeys; jj++) // free memory if (kvals[jj]) zfree(kvals[jj]); zfree(file); Fbusy_done++; } Fbusy_goal = Fbusy_done = 0; gallerytype = META; // gallery type = search results/metadata return 0; } /********************************************************************************/ // Convert pretty date/time format from "yyyy-mm-dd" and "hh:mm:ss" to "yyyymmdd" and "hhmmss". // Missing month or day ("yyyy" or "yyyy-mm") is replaced with "-01". // Missing seconds ("hh:mm") are replaced with zero ("hh:mm:00"). // Output user message and return null if not valid. char * pdate_metadate(cchar *pdate) // "yyyy-mm-dd" >> "yyyymmdd" { int monlim[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int cc, year, mon, day; char pdate2[12]; static char mdate[12]; cc = strlen(pdate); if (cc > 10) goto badformat; strcpy(pdate2,pdate); if (cc == 4) // conv. "yyyy" to "yyyy-01-01" strcat(pdate2,"-01-01"); else if (cc == 7) // conv. "yyyy-mm" to "yyyy-mm-01" strcat(pdate2,"-01"); if (strlen(pdate2) != 10) goto badformat; if (pdate2[4] != '-' || pdate2[7] != '-') goto badformat; year = atoi(pdate2); mon = atoi(pdate2+5); day = atoi(pdate2+8); if (year < 0 || year > 2999) goto baddate; if (mon < 1 || mon > 12) goto baddate; if (day < 1 || day > monlim[mon-1]) goto baddate; if (mon == 2 && day == 29 && (year % 4)) goto baddate; memcpy(mdate,pdate2,4); // return "yyyymmdd" memcpy(mdate+4,pdate2+5,2); memcpy(mdate+6,pdate2+8,3); return mdate; badformat: zmessageACK(Mwin,ZTX("date format is YYYY-MM-DD")); return 0; baddate: zmessageACK(Mwin,ZTX("date is invalid")); return 0; } char * ptime_metatime(cchar *ptime) // "hh:mm[:ss]" >> "hhmmss" { int cc, hour, min, sec; char ptime2[12]; static char mtime[8]; cc = strlen(ptime); if (cc > 8) goto badformat; strcpy(ptime2,ptime); if (cc == 5) strcat(ptime2,":00"); // conv. "hh:mm" to "hh:mm:00" if (strlen(ptime2) != 8) goto badformat; if (ptime2[2] != ':' || ptime2[5] != ':') goto badformat; hour = atoi(ptime2); min = atoi(ptime2+3); sec = atoi(ptime2+6); if (hour < 0 || hour > 23) goto badtime; if (min < 0 || min > 59) goto badtime; if (sec < 0 || sec > 59) goto badtime; memcpy(mtime,ptime2,2); // return "hhmmss" memcpy(mtime+2,ptime2+3,2); memcpy(mtime+4,ptime2+6,2); return mtime; badformat: zmessageACK(Mwin,ZTX("time format is HH:MM [:SS]")); return 0; badtime: zmessageACK(Mwin,ZTX("time is invalid")); return 0; } // convert yyyy-mm-dd hh:mm[:ss] to yyyymmddhhmmss char * pdatetime_metadatetime(cchar *pdatetime) { char pdate[12], ptime[12]; static char metadatetime[20]; char *pp; int cc; strncpy0(pdate,pdatetime,11); // yyyy-mm-dd strncpy0(ptime,pdatetime+11,9); // hh:mm[:ss] cc = strlen(ptime); if (cc == 5) strcat(ptime,":00"); // hh:mm >> hh:mm:00 else if (cc != 8) return 0; pp = pdate_metadate(pdate); if (! pp) return 0; strncpy0(metadatetime,pp,9); pp = ptime_metatime(ptime); if (! pp) return 0; strncpy0(metadatetime+8,pp,7); return metadatetime; } // Convert metadata date/time "yyyymmddhhmmss" to pretty format "yyyy-mm-dd" and "hh:mm:ss" void metadate_pdate(cchar *metadate, char *pdate, char *ptime) { if (*metadate) { memcpy(pdate,metadate,4); // yyyymmdd to yyyy-mm-dd memcpy(pdate+5,metadate+4,2); memcpy(pdate+8,metadate+6,2); pdate[4] = pdate[7] = '-'; pdate[10] = 0; memcpy(ptime,metadate+8,2); // hhmmss to hh:mm:ss memcpy(ptime+3,metadate+10,2); ptime[2] = ':'; ptime[5] = 0; memcpy(ptime+6,metadate+12,2); ptime[5] = ':'; ptime[8] = 0; } else *pdate = *ptime = 0; // missing return; } // validate a date/time string formatted "yyyy-mm-dd hh:mm[:ss]" // return 0 if bad, 1 if OK // valid year is 0000 to 2099 int datetimeOK(char *datetime) // format changed 18.01 { int monlim[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int cc, year, mon, day, hour, min, sec; cc = strlen(datetime); if (cc != 16 && cc != 19) return 0; if (datetime[4] != '-') return 0; if (datetime[7] != '-') return 0; if (datetime[13] != ':') return 0; if (cc == 19 && datetime[16] != ':') return 0; year = atoi(datetime); mon = atoi(datetime+5); day = atoi(datetime+8); hour = atoi(datetime+11); min = atoi(datetime+14); if (cc == 19) sec = atoi(datetime+17); else sec = 0; if (year < 0 || year > 2099) return 0; if (mon < 1 || mon > 12) return 0; if (day < 1 || day > monlim[mon-1]) return 0; if (mon == 2 && day == 29 && (year % 4)) return 0; if (hour < 0 || hour > 23) return 0; if (min < 0 || min > 59) return 0; if (sec < 0 || sec > 59) return 0; return 1; } /********************************************************************************/ // add input tag to output tag list if not already there and enough room // returns: 0 = added OK 1 = already there (case ignored) // 2 = overflow 3 = bad tag name 4 = null tag int add_tag(char *tag, char *taglist, int maxcc) { char *pp1, *pp2, tag1[tagcc]; int cc, cc1, cc2; if (! tag) return 4; strncpy0(tag1,tag,tagcc); // remove leading and trailing blanks cc = strTrim2(tag1); if (! cc) return 4; if (utf8_check(tag1)) return 3; // look for bad characters if (strpbrk(tag1,",;:")) return 3; strcpy(tag,tag1); pp1 = taglist; cc1 = strlen(tag); while (true) // check if already in tag list { while (*pp1 == ' ' || *pp1 == ',') pp1++; if (! *pp1) break; pp2 = pp1 + 1; while (*pp2 && *pp2 != ',') pp2++; cc2 = pp2 - pp1; if (cc2 == cc1 && strmatchcaseN(tag,pp1,cc1)) return 1; pp1 = pp2; } cc2 = strlen(taglist); // append to tag list if space enough if (cc1 + cc2 + 3 > maxcc) return 2; strcpy(taglist + cc2,tag); strcpy(taglist + cc2 + cc1, ", "); // add delimiter + space if (taglist == meta_tags) Fmetamod++; // image tags were changed return 0; } // add tag "fotoxx" when an edited image file is saved // used by f_save() int add_tag_fotoxx(cchar *file) // 17.04 { char fotoxx[8] = "fotoxx"; add_tag(fotoxx,meta_tags,tagFcc); return 1; } // set meta_size[16] for image size change outside of m_meta_edit() // used by file_save() in case file size is changed void set_meta_size(int ww, int hh) { snprintf(meta_size,16,"%dx%d",ww,hh); return; } // remove tag from taglist, if present // returns: 0 if found and deleted, otherwise 1 int del_tag(char *tag, char *taglist) { int ii, ftcc, atcc, found; char *temptags; cchar *pp; temptags = zstrdup(taglist); *taglist = 0; ftcc = found = 0; for (ii = 1; ; ii++) { pp = strField(temptags,",;",ii); // next tag if (! pp) { zfree(temptags); if (found && taglist == meta_tags) Fmetamod++; // image tags were changed return 1-found; } if (*pp == ' ') continue; if (strmatchcase(pp,tag)) { // skip matching tag found = 1; continue; } atcc = strlen(pp); // copy non-matching tag strcpy(taglist + ftcc, pp); ftcc += atcc; strcpy(taglist + ftcc, ", "); // + delim + blank ftcc += 2; } } // add new tag to recent tags, if not already. // remove oldest to make space if needed. int add_recentag(char *tag) { int err; char *pp, temptags[tagRcc]; err = add_tag(tag,tags_recentags,tagRcc); // add tag to recent tags while (err == 2) // overflow { strncpy0(temptags,tags_recentags,tagRcc); // remove oldest to make room pp = strpbrk(temptags,",;"); if (! pp) return 0; strcpy(tags_recentags,pp+2); // delimiter + blank before tag err = add_tag(tag,tags_recentags,tagRcc); } return 0; } /********************************************************************************/ // Load tags_defined file into tags_deftags[ii] => category: tag1, tag2, ... // Read image_index recs. and add unmatched tags: => nocatg: tag1, tag2, ... void load_deftags() { int tags_Ucomp(cchar *tag1, cchar *tag2); static int Floaded = 0; FILE * fid; xxrec_t *xxrec; int ii, jj, ntags, err, cc, tcc; int ncats, catoverflow; int nocat, nocatcc; char tag[tagcc], catg[tagcc]; char tagsbuff[tagGcc]; char *pp1, *pp2; char ptags[maxtags][tagcc]; // 10000 * 50 = 0.5 MB if (Floaded) return; // use memory tags if already there Floaded++; for (ii = 0; ii < maxtagcats; ii++) // clean memory tags_deftags[ii] = 0; ncats = catoverflow = 0; fid = fopen(tags_defined_file,"r"); // read tags_defined file if (fid) { while (true) { pp1 = fgets_trim(tagsbuff,tagGcc,fid); if (! pp1) break; pp2 = strchr(pp1,':'); // isolate "category:" if (! pp2) continue; // no colon cc = pp2 - pp1 + 1; if (cc > tagcc-1) continue; // category name too long strncpy0(catg,pp1,cc); // (for error message) if (strlen(pp1) > tagGcc-2) goto cattoobig; // all category tags too long pp2++; while (*pp2 == ' ') pp2++; if (strlen(pp2) < 3) continue; // category with no tags while (*pp2) { if (*pp2 == ';') *pp2 = ','; // replace ';' with ',' for Fotoxx pp2++; } tags_deftags[ncats] = zstrdup(pp1); // tags_deftags[ii] ncats++; // = category: tag1, tag2, ... tagN, if (ncats == maxtagcats) goto toomanycats; } err = fclose(fid); fid = 0; if (err) goto deftagsfilerr; } // sort the categories in ascending order for (ii = 0; ii < ncats; ii++) for (jj = ii+1; jj < ncats; jj++) { pp1 = tags_deftags[ii]; pp2 = tags_deftags[jj]; if (strcasecmp(pp1,pp2) > 0) { tags_deftags[ii] = pp2; tags_deftags[jj] = pp1; } } // move category "nocatg" to the end of the list for (ii = 0; ii < ncats; ii++) // 18.01 { pp1 = tags_deftags[ii]; if (strmatchN(pp1,"nocatg:",7)) { for (jj = ii; jj < ncats-1; jj++) tags_deftags[jj] = tags_deftags[jj+1]; tags_deftags[jj] = pp1; break; } } // if not already there, add category "nocatg" to the end of the list pp1 = 0; if (ncats > 0) pp1 = tags_deftags[ncats-1]; // last tag category bugfix 18.01.1 if (pp1 && strmatchN(pp1,"nocatg:",7)) { // already 'nocatg' nocat = ncats - 1; nocatcc = strlen(pp1); pp2 = (char *) zmalloc(tagGcc); // re-allocate max. size tags_deftags[nocat] = pp2; // for following phase strcpy(pp2,pp1); zfree(pp1); } else { nocat = ncats; // add to end of list 18.01 ncats++; tags_deftags[nocat] = (char *) zmalloc(tagGcc); // allocate max. size strcpy(tags_deftags[nocat],"nocatg: "); nocatcc = 8; } // search image index recs for all tags in all images // for tags not found in defined tags list, add to 'nocatg' list for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; pp1 = xxrec->tags; // may be "null," while (true) { while (*pp1 && strchr(",; ",*pp1)) pp1++; // next image tag start if (! *pp1) break; pp2 = strpbrk(pp1,",;"); // end if (! pp2) pp2 = pp1 + strlen(pp1); cc = pp2 - pp1; if (cc > tagcc-1) { pp1 = pp2; continue; // ignore huge tag } strncpy0(tag,pp1,cc+1); // look for tag in defined tags if (find_deftag(tag)) { pp1 = pp2; // found continue; } if (nocatcc + cc + 2 > tagGcc-2) { catoverflow = 1; // nocatg: length limit reached break; } else { strcpy(tags_deftags[nocat] + nocatcc, tag); // append tag to list nocatcc += cc; strcpy(tags_deftags[nocat] + nocatcc, ", "); // + delim + blank nocatcc += 2; } pp1 = pp2; } } if (catoverflow) goto cattoobig; // parse all the tags in each category and sort in ascending order for (ii = 0; ii < ncats; ii++) { pp1 = tags_deftags[ii]; pp2 = strchr(pp1,':'); if (! pp2) { printz("defined tags file format error: %s \n",pp1); // 18.01 continue; } cc = pp2 - pp1 + 1; strncpy0(catg,pp1,cc); pp1 = pp2 + 1; while (*pp1 == ' ') pp1++; tcc = 0; for (jj = 0; jj < maxtags; jj++) { if (! *pp1) break; pp2 = strchr(pp1,','); if (pp2) cc = pp2 - pp1; else cc = strlen(pp1); if (cc > tagcc-1) cc = tagcc-1; strncpy0(ptags[jj],pp1,cc+1); pp1 += cc + 1; tcc += cc; while (*pp1 == ' ') pp1++; } ntags = jj; if (ntags == maxtags) goto toomanytags; HeapSort((char *) ptags,tagcc,ntags,tags_Ucomp); pp1 = tags_deftags[ii]; tcc += strlen(catg) + 2 + 2 * ntags + 2; // category, all tags, delimiters pp2 = (char *) zmalloc(tcc); tags_deftags[ii] = pp2; // swap memory zfree(pp1); strcpy(pp2,catg); pp2 += strlen(catg); strcpy(pp2,": "); // pp2 = "category: " pp2 += 2; for (jj = 0; jj < ntags; jj++) // add the sorted tags { strcpy(pp2,ptags[jj]); // append tag + delim + blank pp2 += strlen(pp2); strcpy(pp2,", "); pp2 += 2; } *pp2 = 0; } return; toomanycats: zmessageACK(Mwin,"more than %d categories",maxtagcats); if (fid) fclose(fid); return; cattoobig: zmessageACK(Mwin,"category %s is too big",catg); if (fid) fclose(fid); return; toomanytags: zmessageACK(Mwin,"category %s has too many tags",catg); if (fid) fclose(fid); return; deftagsfilerr: zmessageACK(Mwin,"tags_defined file error: %s",strerror(errno)); return; } // compare function for tag sorting int tags_Ucomp(cchar *tag1, cchar *tag2) { return strcasecmp(tag1,tag2); } // write tags_deftags[] memory data to the defined tags file if any changes were made void save_deftags() { int ii, err; FILE *fid; fid = fopen(tags_defined_file,"w"); // write tags_defined file if (! fid) goto deftagserr; for (ii = 0; ii < maxtagcats; ii++) { if (! tags_deftags[ii]) break; err = fprintf(fid,"%s\n",tags_deftags[ii]); // each record: if (err < 0) goto deftagserr; // category: tag1, tag2, ... tagN, } err = fclose(fid); if (err) goto deftagserr; return; deftagserr: zmessageACK(Mwin,"tags_defined file error: %s",strerror(errno)); return; } // find a given tag in tags_deftags[] // return: 1 = found, 0 = not found int find_deftag(char *tag) { int ii, cc; char tag2[tagcc+4]; char *pp; strncpy0(tag2,tag,tagcc); // construct tag + delim + blank cc = strlen(tag2); strcpy(tag2+cc,", "); cc += 2; for (ii = 0; ii < maxtagcats; ii++) { pp = tags_deftags[ii]; // category: tag1, tag2, ... tagN, if (! pp) return 0; // not found while (pp) { pp = strcasestr(pp,tag2); // look for delim + blank + tag + delim if (! pp) break; if (strchr(",;:", pp[-2])) return 1; // cat: tag, or priortag, tag, pp += cc; // | | } // pp pp } return 1; } // add new tag to tags_deftags[] >> category: tag1, tag2, ... newtag, // returns: 0 = added OK 1 = not unique (case ignored) // 2 = overflow 3 = bad name 4 = null/blank tag // if tag present under another category, it is moved to new category int add_deftag(char *catg, char *tag) { int ii, cc, cc1, cc2; char catg1[tagcc], tag1[tagcc]; char *pp1, *pp2; if (! catg) strcpy(catg1,"nocatg"); else strncpy0(catg1,catg,tagcc); cc = strTrim2(catg1); // remove leading and trailing blanks if (! cc) strcpy(catg1,"nocatg"); if (utf8_check(catg1)) goto badcatname; // look for bad characters if (strpbrk(catg1,",;:\"")) goto badcatname; if (! tag) return 4; strncpy0(tag1,tag,tagcc); // remove leading and trailing blanks cc = strTrim2(tag1); if (! cc) return 4; if (utf8_check(tag1)) goto badtagname; // look for bad characters if (strpbrk(tag1,",;:\"")) goto badtagname; del_deftag(tag1); // delete tag if already there cc1 = strlen(catg1); for (ii = 0; ii < maxtagcats; ii++) // look for given category { pp1 = tags_deftags[ii]; if (! pp1) goto newcatg; if (! strmatchN(catg1,pp1,cc1)) continue; // match on "catname:" if (pp1[cc1] == ':') goto oldcatg; } newcatg: if (ii == maxtagcats) goto toomanycats; cc1 = strlen(catg1) + strlen(tag1) + 6; pp1 = (char *) zmalloc(cc1); *pp1 = 0; strncatv(pp1,cc1,catg1,": ",tag1,", ",null); // category: + tag + delim + blank tags_deftags[ii] = tags_deftags[ii-1]; // move "nocatg" record to next slot tags_deftags[ii-1] = pp1; // insert new record before save_deftags(); return 0; oldcatg: // logic simplified pp2 = pp1 + cc1 + 2; // char following "catname: " cc1 = strlen(tag1); while (true) { pp2 = strcasestr(pp2,tag1); // look for "tagname, " if (! pp2) break; // not found if (strchr(",;",pp2[cc1])) return 1; // found, tag not unique pp2 += cc1; // keep searching } cc2 = strlen(pp1); // add new tag to old record if (cc1 + cc2 + 4 > tagGcc) goto cattoobig; pp2 = zstrdup(pp1,cc1+cc2+4); // expand string zfree(pp1); tags_deftags[ii] = pp2; strcpy(pp2+cc2,tag1); // old record + tag + delim + blank strcpy(pp2+cc2+cc1,", "); save_deftags(); return 0; badcatname: zmessageACK(Mwin,"bad category name"); return 3; badtagname: zmessageACK(Mwin,"bad tag name"); return 3; toomanycats: zmessageACK(Mwin,"too many categories"); return 2; cattoobig: zmessageACK(Mwin,"too many tags"); return 2; } // delete tag from defined tags list, tags_deftags[] // return: 0 = found and deleted, 1 = not found int del_deftag(char *tag) { int ii, cc; char tag2[tagcc+4]; char *pp, *pp1, *pp2; if (! tag || *tag <= ' ') return 1; // bad tag strncpy0(tag2,tag,tagcc); // construct tag + delim + blank cc = strlen(tag2); strcpy(tag2+cc,", "); cc += 2; for (ii = 0; ii < maxtagcats; ii++) { pp = tags_deftags[ii]; if (! pp) return 1; // not found while (pp) { pp = strcasestr(pp,tag2); // look for prior delim or colon if (! pp) break; if (strchr(",;:", pp[-2])) goto found; // cat: tag, or priortag, tag, pp += cc; // | | } // pp pp } found: for (pp1 = pp, pp2 = pp+cc; *pp2; pp1++, pp2++) // eliminate tag, delim, blank *pp1 = *pp2; *pp1 = 0; return 0; } // Stuff text widget "deftags" with all tags in the given category. // If category "ALL", stuff all tags and format by category. void deftags_stuff(zdialog *zd, cchar *acatg) { GtkWidget *widget; int ii, ff, cc; char catgname[tagcc+4]; char *pp1, *pp2; widget = zdialog_widget(zd,"deftags"); textwidget_clear(widget); for (ii = 0; ii < maxtagcats; ii++) { pp1 = tags_deftags[ii]; if (! pp1) break; pp2 = strchr(pp1,':'); if (! pp2) continue; cc = pp2 - pp1; if (cc < 1) continue; if (cc > tagcc) continue; strncpy0(catgname,pp1,cc+1); if (! strmatch(acatg,"ALL")) { ff = strmatch(catgname,acatg); if (! ff) continue; } strcat(catgname,": "); textwidget_append(widget,1,catgname); // "category: " in bold text pp2++; if (*pp2 == ' ') pp2++; if (*pp2) textwidget_append(widget,0,pp2); // "cat1, cat2, ... catN," textwidget_append(widget,0,"\n"); } return; } // Stuff combo box "defcats" with "ALL" + all defined categories void defcats_stuff(zdialog *zd) { char catgname[tagcc+2]; int ii, cc; char *pp1, *pp2; zdialog_cb_clear(zd,"defcats"); zdialog_cb_app(zd,"defcats","ALL"); for (ii = 0; ii < maxtagcats; ii++) { pp1 = tags_deftags[ii]; if (! pp1) break; pp2 = strchr(pp1,':'); if (! pp2) continue; cc = pp2 - pp1; if (cc < 1) continue; if (cc > tagcc) continue; strncpy0(catgname,pp1,cc+1); zdialog_cb_app(zd,"defcats",catgname); } return; } // report tags defined and not used in any image file void tag_orphans(GtkWidget *parent) { FILE *fid; xxrec_t *xxrec; int ii, jj, cc; int Ndeftags; char **deftags; char usedtag[tagcc], tagsbuff[tagGcc]; char *pp1, *pp2; deftags = (char **) zmalloc(maxtags * sizeof(char *)); // allocate memory Ndeftags = 0; fid = fopen(tags_defined_file,"r"); // read tags_defined file if (fid) { while (true) { pp1 = fgets_trim(tagsbuff,tagGcc,fid); if (! pp1) break; pp1 = strchr(pp1,':'); // skip over "category:" if (! pp1) continue; cc = pp1 - tagsbuff; if (cc > tagcc) continue; // reject bad data (manual edit?) pp1++; for (ii = 1; ; ii++) { // get tags: tag1, tag2, ... pp2 = (char *) strField(pp1,",;",ii); if (! pp2) break; if (strlen(pp2) < 3) continue; // reject bad data if (strlen(pp2) > tagcc) continue; deftags[Ndeftags] = zstrdup(pp2); Ndeftags++; } } fclose(fid); } for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; pp1 = xxrec->tags; // image tags if (! pp1) continue; if (strmatchN(pp1,"null",4)) continue; while (true) { while (*pp1 && strchr(",; ",*pp1)) pp1++; // next image tag start if (! *pp1) break; pp2 = strpbrk(pp1,",;"); // end if (! pp2) pp2 = pp1 + strlen(pp1); cc = pp2 - pp1; if (cc > tagcc-1) { pp1 = pp2; continue; // ignore huge tag } strncpy0(usedtag,pp1,cc+1); // used tag, without delimiter for (jj = 0; jj < Ndeftags; jj++) // find in defined tags if (strmatch(usedtag,deftags[jj])) break; if (jj < Ndeftags) { // found zfree(deftags[jj]); Ndeftags--; while (jj < Ndeftags) { // defined tag is in use deftags[jj] = deftags[jj+1]; // remove from list and pack down jj++; } } pp1 = pp2; } } popup_report_open(parent,"unused tags",200,200); for (ii = 0; ii < Ndeftags; ii++) popup_report_write(0,"%s \n",deftags[ii]); popup_report_write(0,"%d unused tags \n",Ndeftags); for (ii = 0; ii < Ndeftags; ii++) zfree(deftags[ii]); zfree(deftags); return; } /********************************************************************************/ // image file EXIF/IPTC data >> memory data: // meta_pdate, meta_rating, meta_tags, meta_comments, meta_caption, // meta_location, meta_country, meta_lati, meta_longi void load_filemeta(cchar *file) { int ii, jj, cc, nkey; char *pp; cchar *exifkeys[100] = { exif_date_key, iptc_keywords_key, iptc_rating_key, exif_size_key, exif_comment_key, iptc_caption_key, exif_city_key, exif_country_key, exif_lati_key, exif_longi_key }; char *ppv[100], *imagedate, *imagekeywords, *imagestars, *imagesize; char *imagecomms, *imagecapt; char *imageloc, *imagecountry, *imagelati, *imagelongi; strncpy0(p_meta_pdate,meta_pdate,15); // save for use by edit_metadata strncpy0(p_meta_rating,meta_rating,4); // [Prev] button strncpy0(p_meta_tags,meta_tags,tagFcc); strncpy0(p_meta_caption,meta_caption,exif_maxcc); strncpy0(p_meta_comments,meta_comments,exif_maxcc); strncpy0(p_meta_location,meta_location,100); strncpy0(p_meta_country,meta_country,100); strncpy0(p_meta_lati,meta_lati,20); strncpy0(p_meta_longi,meta_longi,20); *meta_tags = *meta_pdate = *meta_comments = *meta_caption = 0; strcpy(meta_rating,"0"); *meta_location = *meta_country = *meta_lati = *meta_longi = 0; nkey = 10; // add keys for indexed metadata 18.01 for (ii = 0; ii < Mxmeta; ii++) { // from exifkeys[10]; if (! xmeta_keys[ii]) break; exifkeys[nkey] = xmeta_keys[ii]; nkey++; } exif_get(file,exifkeys,ppv,nkey); // get metadata from image file imagedate = ppv[0]; imagekeywords = ppv[1]; imagestars = ppv[2]; imagesize = ppv[3]; imagecomms = ppv[4]; imagecapt = ppv[5]; imageloc = ppv[6]; imagecountry = ppv[7]; imagelati = ppv[8]; imagelongi = ppv[9]; if (imagedate) { exif_tagdate(imagedate,meta_pdate); // EXIF date/time >> yyyymmddhhmmss zfree(imagedate); } if (imagekeywords) { for (ii = 1; ; ii++) { pp = (char *) strField(imagekeywords,",;",ii); if (! pp) break; if (*pp == ' ') continue; cc = strlen(pp); if (cc >= tagcc) continue; // reject tags too big for (jj = 0; jj < cc; jj++) if (pp[jj] > 0 && pp[jj] < ' ') break; // reject tags with control characters if (jj < cc) continue; add_tag(pp,meta_tags,tagFcc); // add to file tags if unique } zfree(imagekeywords); } if (imagestars) { meta_rating[0] = *imagestars; if (meta_rating[0] < '0' || meta_rating[0] > '5') meta_rating[0] = '0'; meta_rating[1] = 0; zfree(imagestars); } if (imagesize) { strncpy0(meta_size,imagesize,15); zfree(imagesize); } if (imagecomms) { strncpy0(meta_comments,imagecomms,exif_maxcc); zfree(imagecomms); } if (imagecapt) { strncpy0(meta_caption,imagecapt,exif_maxcc); zfree(imagecapt); } if (imageloc) { // geotags strncpy0(meta_location,imageloc,99); zfree(imageloc); } else strcpy(meta_location,"null"); // replace missing data with "null" if (imagecountry) { strncpy0(meta_country,imagecountry,99); zfree(imagecountry); } else strcpy(meta_country,"null"); if (imagelati) { strncpy0(meta_lati,imagelati,12); zfree(imagelati); } else strcpy(meta_lati,"null"); if (imagelongi) { strncpy0(meta_longi,imagelongi,12); zfree(imagelongi); } else strcpy(meta_longi,"null"); for (ii = 0; ii < Mxmeta; ii++) { // get indexed metadata if any 18.01 if (! xmeta_keys[ii]) break; if (xmeta_data[ii]) zfree(xmeta_data[ii]); if (ppv[ii+10]) xmeta_data[ii] = ppv[ii+10]; else xmeta_data[ii] = zstrdup("null"); } Fmetamod = 0; // no pending changes return; } // add metadata in memory to image file EXIF/IPTC data and image_index recs. void save_filemeta(cchar *file) { cchar *exifkeys[100] = { exif_date_key, iptc_keywords_key, iptc_rating_key, exif_size_key, exif_comment_key, iptc_caption_key, exif_city_key, exif_country_key, exif_lati_key, exif_longi_key }; int nkey, ii; cchar *exifdata[100]; char imagedate[24]; *imagedate = 0; if (*meta_pdate) tag_exifdate(meta_pdate,imagedate); // yyyymmddhhmmss >> EXIF date/time exifdata[0] = imagedate; // update file EXIF/IPTC data exifdata[1] = meta_tags; exifdata[2] = meta_rating; exifdata[3] = meta_size; exifdata[4] = meta_comments; exifdata[5] = meta_caption; if (*meta_location < ' ' || strmatch(meta_location,"null")) // geotags exifdata[6] = ""; else exifdata[6] = meta_location; // if "null" erase EXIF if (*meta_country < ' ' || strmatch(meta_country,"null")) exifdata[7] = ""; else exifdata[7] = meta_country; if (*meta_lati < ' ' || strmatch(meta_lati,"null") || *meta_longi < ' ' || strmatch(meta_longi,"null")) exifdata[8] = exifdata[9] = ""; else { exifdata[8] = meta_lati; exifdata[9] = meta_longi; } nkey = 10; // add keys for indexed metadata 18.01 for (ii = 0; ii < Mxmeta; ii++) { // from exifkeys[10]; if (! xmeta_keys[ii]) break; if (! xmeta_data[ii]) xmeta_data[ii] = zstrdup("null"); exifkeys[nkey] = xmeta_keys[ii]; exifdata[nkey] = xmeta_data[ii]; nkey++; } exif_put(file,exifkeys,exifdata,nkey); // write EXIF update_image_index(file); // update image index file if (zdexifview) meta_view(0); // live EXIF/IPTC update Fmetamod = 0; // no pending changes return; } // update image index record (replace updated file data) // meta_xxxx data in memory >> image index record void update_image_index(cchar *file) { int ii, err, xcc; char xmetarec[1000]; xxrec_t xxrec; STATB statb; err = stat(file,&statb); if (err) { zmessageACK(Mwin,Bfilenotfound); return; } memset(&xxrec,0,sizeof(xxrec_t)); // new metadata record to make xxrec.file = (char *) file; // image filespec compact_time(statb.st_mtime,xxrec.fdate); // convert file date to "yyyymmddhhmmss" strncpy0(xxrec.pdate,meta_pdate,15); // photo date, "yyyymmddhhmmss" xxrec.rating[0] = meta_rating[0]; // rating '0' to '5' stars xxrec.rating[1] = 0; strncpy0(xxrec.size,meta_size,15); // 2345x1234 if (*meta_tags) // tags xxrec.tags = meta_tags; if (*meta_caption) // user caption xxrec.capt = meta_caption; if (*meta_comments) // user comments xxrec.comms = meta_comments; if (*meta_location <= ' ') strcpy(meta_location,"null"); // geotags if (*meta_country <= ' ') strcpy(meta_country,"null"); if (*meta_lati <= ' ') strcpy(meta_lati,"null"); // "null" for location/country is searchable if (*meta_longi <= ' ') strcpy(meta_longi,"null"); xxrec.location = meta_location; xxrec.country = meta_country; if (strmatch(meta_lati,"null") || strmatch(meta_longi,"null")) // 17.01 xxrec.flati = xxrec.flongi = 0; else { xxrec.flati = atof(meta_lati); // 17.01 xxrec.flongi = atof(meta_longi); if (xxrec.flati < -90.0 || xxrec.flati > 90.0) xxrec.flati = xxrec.flongi = 0; if (xxrec.flongi < -180.0 || xxrec.flongi > 180.0) xxrec.flati = xxrec.flongi = 0; } xcc = 0; for (ii = 0; ii < Mxmeta; ii++) { // add indexed metadata if any 18.01 if (! xmeta_keys[ii]) break; if (strlen(xmeta_keys[ii]) + strlen(xmeta_data[ii]) > 100) continue; // impractical for image search strcpy(xmetarec+xcc,xmeta_keys[ii]); // construct series xcc += strlen(xmeta_keys[ii]); // "keyname=keydata^ " xmetarec[xcc++] = '='; strcpy(xmetarec+xcc,xmeta_data[ii]); xcc += strlen(xmeta_data[ii]); strcpy(xmetarec+xcc,"^ "); xcc += 2; if (xcc > 895) { printz("file metadata exceeds record size: %s \n",file); break; } } if (xcc > 0) xxrec.xmeta = zstrdup(xmetarec); else xxrec.xmeta = zstrdup("null"); put_xxrec(&xxrec,file); // update image index gallery(file,"update",0); // update gallery record return; } // delete given image file from image index recs. void delete_image_index(cchar *file) { put_xxrec(null,file); return; } /********************************************************************************/ // Convert a location [country] to earth coordinates using the // MapQuest geocoding service. // (incomplete names may be completed with a bad guess) cchar * web_geocode(zdialog *zd) { int err; static char lati[20], longi[20]; char outfile[100], URI[300]; char *pp1, *pp2, buffer[200]; char location[100], country[100]; float flati, flongi; FILE *fid; cchar *notfound = ZTX("not found"); cchar *badinputs = ZTX("location and country required"); cchar *query = "http://open.mapquestapi.com/geocoding/v1/address?" "&key=Fmjtd%7Cluub2qa72d%2C20%3Do5-9u700a" "&maxResults=1" "&outFormat=csv"; zdialog_fetch(zd,"location",location,100); // get zdialog inputs zdialog_fetch(zd,"country",country,100); if (*location <= ' ' || *country <= ' ') return badinputs; *location = toupper(*location); // capitalize *country = toupper(*country); snprintf(outfile,100,"%s/web-data",tempdir); snprintf(URI,299,"\"%s&location=%s,%s\"",query,location,country); err = shell_quiet("wget -T 10 -o /dev/null -O %s %s",outfile,URI); if (err == 4) err = ECOMM; // replace "interrupted system call" if (err) return strerror(err); fid = fopen(outfile,"r"); // get response if (! fid) return notfound; pp1 = fgets(buffer,200,fid); pp1 = fgets(buffer,200,fid); fclose(fid); if (! pp1) return notfound; printz("web geocode: %s \n",buffer); pp2 = (char *) strField(pp1,",",4); // look for returned location name if (! pp2 || ! *pp2) return notfound; pp2 = (char *) strField(pp1,",",7); if (! pp2) return notfound; strncpy0(lati,pp2,20); pp2 = (char *) strField(pp1,",",8); if (! pp2) return notfound; strncpy0(longi,pp2,20); err = validate_latlong(lati,longi,flati,flongi); if (err) return notfound; pp1 = strchr(lati,'.'); // keep max. 4 decimal digits if (pp1) *(pp1+5) = 0; pp1 = strchr(longi,'.'); if (pp1) *(pp1+5) = 0; zdialog_stuff(zd,"lati",lati); // stuff output to zdialog zdialog_stuff(zd,"longi",longi); return 0; } /********************************************************************************/ // Initialize for geotag functions. // Load geolocations data into memory from image index table. // Returns no. geolocations loaded. int init_geolocs() { char location[100], country[100]; float flati, flongi; double time0, time1; int yn, cc, ii, jj; xxrec_t *xxrec; if (Ngeolocs) return Ngeolocs; // already done if (Findexvalid == 0) { yn = zmessageYN(Mwin,Bnoindex); // no image index, offer to enable if (yn) index_rebuild(2,0); else return 0; } time0 = get_seconds(); Ffuncbusy = 1; cc = (Nxxrec+1) * sizeof(geolocs_t *); // get memory for geolocs table geolocs = (geolocs_t **) zmalloc(cc); // room for Nxxrec entries geolocs[0] = (geolocs_t *) zmalloc(sizeof(geolocs_t)); // insure one entry geolocs[0]->location = zstrdup("null"); geolocs[0]->country = zstrdup("null"); geolocs[0]->flati = 0; geolocs[0]->flongi = 0; Ngeolocs = 1; // populate geolocs from image index table for (ii = 0; ii < Nxxrec; ii++) // loop all index recs { xxrec = xxrec_tab[ii]; strncpy0(location,xxrec->location,100); // 17.01 strncpy0(country,xxrec->country,100); flati = xxrec->flati; flongi = xxrec->flongi; if (strmatch(location,"null") && strmatch(country,"null")) continue; // ignore missing location if (Ngeolocs) { jj = Ngeolocs - 1; // eliminate sequential duplicates if (strmatch(location,geolocs[jj]->location) && strmatch(country,geolocs[jj]->country) && flati == geolocs[jj]->flati && flongi == geolocs[jj]->flongi) continue; } jj = Ngeolocs++; // fill next entry in table geolocs[jj] = (geolocs_t *) zmalloc(sizeof(geolocs_t)); geolocs[jj]->location = zstrdup(location); geolocs[jj]->country = zstrdup(country); geolocs[jj]->flati = flati; geolocs[jj]->flongi = flongi; } if (Ngeolocs > 1) HeapSort((char **) geolocs, Ngeolocs, geolocs_compare); // sort for (ii = 0, jj = 1; jj < Ngeolocs; jj++) // eliminate duplicates { if (strmatch(geolocs[jj]->location,geolocs[ii]->location) && strmatch(geolocs[jj]->country,geolocs[ii]->country) && geolocs[jj]->flati == geolocs[ii]->flati && geolocs[jj]->flongi == geolocs[ii]->flongi) { zfree(geolocs[jj]->country); // free redundant entries zfree(geolocs[jj]->location); zfree(geolocs[jj]); } else { ii++; // count unique entries if (ii < jj) geolocs[ii] = geolocs[jj]; // pack down the table } } Ngeolocs = ii + 1; // final geolocs table size time1 = get_seconds() - time0; Ffuncbusy = 0; printz("%d images, %d locations %.3f secs. \n",Nxxrec,Ngeolocs,time1); return Ngeolocs; } // Compare 2 geolocs records by country, location, latitude, longitude // return <0 0 >0 for rec1 < == > rec2. int geolocs_compare(cchar *rec1, cchar *rec2) { float diff; int ii; geolocs_t *r1 = (geolocs_t *) rec1; geolocs_t *r2 = (geolocs_t *) rec2; ii = strcmp(r1->country,r2->country); if (ii) return ii; ii = strcmp(r1->location,r2->location); if (ii) return ii; if (r1->flati == 0 && r1->flongi == 0) { // sort missing lat/long last 17.01 if (r2->flati == 0 && r2->flongi == 0) return 0; else return +1; } diff = r1->flati - r2->flati; if (diff < 0) return -1; if (diff > 0) return +1; diff = r1->flongi - r2->flongi; if (diff < 0) return -1; if (diff > 0) return +1; return 0; } /********************************************************************************/ // find a geolocation from partial zdialog inputs and user choice of options // zdialog widgets: location, country, lati, longi // location and country are inputs (may be partial leading strings) // all four widgets are outputs (found location and geocoordinates) int find_location(zdialog *zd) { int find_location_dialog_event(zdialog *zd, cchar *event); zdialog *zd2; cchar *pp; GtkWidget *parent; int cc, ii, jj, kk, Nmatch, zstat, zoomlev; int flocation = 0, fcountry = 0; char location[100], country[100], text[200]; char lati[20], longi[20], *matches[20][2]; float flati1 = 999, flati2 = -999; float flongi1 = 999, flongi2 = -999; float flatic, flongic, kmrange, fmpp; init_geolocs(); // if not already zdialog_fetch(zd,"location",location,100); // get dialog inputs zdialog_fetch(zd,"country",country,100); if (*location > ' ' && ! strmatch(location,"null")) flocation = 1; // one of these must be present if (*country > ' ' && ! strmatch(country,"null")) fcountry = 1; if (! flocation && ! fcountry) return 0; *location = toupper(*location); // capitalize *country = toupper(*country); for (ii = Nmatch = 0; ii < Ngeolocs; ii++) // search for exact location match { if (flocation && ! strmatchcase(location,geolocs[ii]->location)) continue; if (fcountry && ! strmatchcase(country,geolocs[ii]->country)) continue; strncpy0(location,geolocs[ii]->location,100); // save matching location strncpy0(country,geolocs[ii]->country,100); goto found_location; } for (ii = kk = Nmatch = 0; ii < Ngeolocs; ii++) // search for partial location match { if (flocation) { cc = strlen(location); if (! strmatchcaseN(location,geolocs[ii]->location,cc)) continue; } if (fcountry) { cc = strlen(country); if (! strmatchcaseN(country,geolocs[ii]->country,cc)) continue; } for (jj = 0; jj < Nmatch; jj++) // reject duplicate match { if (strmatch(geolocs[ii]->location,matches[jj][0]) && (strmatch(geolocs[ii]->country,matches[jj][1]))) break; } if (jj < Nmatch) continue; matches[Nmatch][0] = geolocs[ii]->location; // save match matches[Nmatch][1] = geolocs[ii]->country; if (Nmatch == 20) return 0; // >20 matches >> no match Nmatch++; // count matches if (Nmatch == 1) kk = ii; // note first match } if (Nmatch == 0) return 0; // no matches if (Nmatch == 1) { // one match strncpy0(location,geolocs[kk]->location,100); // save matching location strncpy0(country,geolocs[kk]->country,100); goto found_location; } parent = zd->widget[0].widget; // 17.01 zd2 = zdialog_new(ZTX("choose location"),parent,BOK,Bcancel,null); // multiple matches, start dialog zdialog_add_widget(zd2,"comboE","locations","dialog",0,"space=5"); for (ii = 0; ii < Nmatch; ii++) { // list locations to choose from snprintf(text,200,"%s | %s",matches[ii][0],matches[ii][1]); zdialog_cb_app(zd2,"locations",text); } zdialog_resize(zd2,300,100); zdialog_set_modal(zd2); // 17.08 zdialog_run(zd2,find_location_dialog_event,"mouse"); // run dialog, wait for completion zdialog_cb_popup(zd2,"locations"); // open combo box list zstat = zdialog_wait(zd2); if (zstat != 1) { // no choice made zdialog_free(zd2); return 0; } zdialog_fetch(zd2,"locations",text,200); pp = strField(text,'|',1); if (pp) strncpy0(location,pp,100); // user choice, location and country pp = strField(text,'|',2); if (pp) strncpy0(country,pp,100); strTrim2(location); strTrim2(country); zdialog_free(zd2); found_location: zdialog_stuff(zd,"location",location); // return location data to zdialog zdialog_stuff(zd,"country",country); for (ii = 0; ii < Ngeolocs; ii++) // search for location & country { if (strmatchcase(location,geolocs[ii]->location) && strmatchcase(country,geolocs[ii]->country)) { if (geolocs[ii]->flati == 0 && geolocs[ii]->flongi == 0) continue; // ignore missing values if (geolocs[ii]->flati < flati1) flati1 = geolocs[ii]->flati; // save range of geocoordinates found if (geolocs[ii]->flati > flati2) flati2 = geolocs[ii]->flati; if (geolocs[ii]->flongi < flongi1) flongi1 = geolocs[ii]->flongi; if (geolocs[ii]->flongi > flongi2) flongi2 = geolocs[ii]->flongi; } } if (flati1 == 999) { // no match, return nulls zdialog_stuff(zd,"lati","null"); zdialog_stuff(zd,"longi","null"); return 0; } if (flati1 == flati2 && flongi1 == flongi2) { // one match, return geocoordinates snprintf(lati,20,"%.4f",flati1); // reformat with std. precision snprintf(longi,20,"%.4f",flongi1); zdialog_stuff(zd,"lati",lati); zdialog_stuff(zd,"longi",longi); return 1; } flatic = 0.5 * (flati1 + flati2); // multiple matches flongic = 0.5 * (flongi1 + flongi2); // center of enclosing rectangle kmrange = earth_distance(flati1,flongi1,flati2,flongi2); // length of diagonal if (kmrange > 100) kmrange = 100; for (zoomlev = 12; zoomlev < 20; zoomlev++) // loop small to large scale { fmpp = netmapscale(zoomlev,flatic,flongic); // meters per pixel at zoom level fmpp = 0.001 * fmpp * 100.0; // km span of 100 pixels if (fmpp < kmrange) break; // stop when kmrange > 100 pixels 17.01 } netmap_zoomto(flatic,flongic,zoomlev); // map click --> stuff zdialog lat/long return 0; } // dialog event function - get chosen location/country from multiple choices int find_location_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"locations")) zd->zstat = 1; return 1; } /********************************************************************************/ // Update geolocations table geolocs[*] // // inputs: location, country, latitude, longitude // return value: 0 OK, no geotag revision (incomplete data) // 1 OK, no geotag revision (matches existing data) // 2 OK, geotag new location/lat/long added // -1 error, lat/long bad int put_geolocs(zdialog *zd) { char location[100], country[100]; char lati[20], longi[20]; float flati, flongi; int ii, err, cc, nn, found = 0; zdialog_fetch(zd,"location",location,100); // get location and geocoordinates zdialog_fetch(zd,"country",country,100); if (*location <= ' ' || strmatch(location,"null")) return 0; // quit here if location not complete if (*country <= ' ' || strmatch(country,"null")) return 0; *location = toupper(*location); // capitalize *country = toupper(*country); zdialog_stuff(zd,"location",location); zdialog_stuff(zd,"country",country); zdialog_fetch(zd,"lati",lati,20); zdialog_fetch(zd,"longi",longi,20); err = validate_latlong(lati,longi,flati,flongi); if (err) { // 1 = missing, 2 = bad if (err == 2) { zmessageACK(Mwin,ZTX("bad latitude/longitude: %s %s"),lati,longi); return -1; } strcpy(lati,"null"); // replace missing data with "null" strcpy(longi,"null"); flati = flongi = 0; // earth coordinates missing value } else { snprintf(lati,20,"%.4f",flati); // reformat with std. precision snprintf(longi,20,"%.4f",flongi); } for (ii = 0; ii < Ngeolocs; ii++) // search geotags for location { if (! strmatchcase(location,geolocs[ii]->location)) continue; // case-insensitive compare if (! strmatchcase(country,geolocs[ii]->country)) continue; if (! strmatch(location,geolocs[ii]->location)) { zfree(geolocs[ii]->location); // revise capitalization geolocs[ii]->location = zstrdup(location); } if (! strmatch(country,geolocs[ii]->country)) { zfree(geolocs[ii]->country); geolocs[ii]->country = zstrdup(country); } if (flati == geolocs[ii]->flati && flongi == geolocs[ii]->flongi) found++; } if (found) return 1; geolocs_t *geolocsA = (geolocs_t *) zmalloc(sizeof(geolocs_t)); geolocs_t **geolocsB; geolocsA->location = zstrdup(location); // new geolocs record geolocsA->country = zstrdup(country); geolocsA->flati = flati; geolocsA->flongi = flongi; cc = (Ngeolocs + 1) * sizeof(geolocs_t *); geolocsB = (geolocs_t **) zmalloc(cc); for (ii = 0; ii < Ngeolocs; ii++) { // copy geolocs before new geoloc 17.01 nn = geolocs_compare((cchar *) geolocs[ii], (cchar *) geolocsA); if (nn > 0) break; geolocsB[ii] = geolocs[ii]; } geolocsB[ii] = geolocsA; // insert new geolocs for ( ; ii < Ngeolocs; ii++) // copy geolocs after new geoloc 17.01 geolocsB[ii+1] = geolocs[ii]; zfree(geolocs); // geolocs --> new table geolocs = geolocsB; Ngeolocs += 1; return 2; } /********************************************************************************/ // validate and convert earth coordinates, latitude and longitude // return: 0 OK // 1 both are missing ("null") // 2 invalid data // if status is > 0, 0.0 is returned for both values int validate_latlong(char *lati, char *longi, float &flati, float &flongi) { int err; char *pp; if ((! *lati || *lati == ' ' || strmatch(lati,"null")) && (! *longi || *longi == ' ' || strmatch(longi,"null"))) goto status1; // both missing if ((! *lati || *lati == ' ' || strmatch(lati,"null")) || (! *longi || *longi == ' ' || strmatch(longi,"null"))) goto status2; // one missing pp = strchr(lati,','); // replace comma decimal point if (pp) *pp = '.'; // with period pp = strchr(longi,','); if (pp) *pp = '.'; err = convSF(lati,flati,-90,+90); // convert to float and check limits if (err) goto status2; err = convSF(longi,flongi,-180,+180); if (err) goto status2; if (flati == 0.0 && flongi == 0.0) goto status2; // reject both = 0.0 return 0; status1: flati = flongi = 0.0; // both missing return 1; status2: // one missing or invalid flati = flongi = 0.0; return 2; } /********************************************************************************/ // compute the km distance between two earth coordinates float earth_distance(float lat1, float long1, float lat2, float long2) { float dlat, dlong, mlat, dist; dlat = fabsf(lat2 - lat1); // latitude distance dlong = fabsf(long2 - long1); // longitude distance mlat = 0.5 * (lat1 + lat2); // mean latitude mlat *= 0.01745; // radians dlong = dlong * cosf(mlat); // longitude distance * cos(latitude) dist = sqrtf(dlat * dlat + dlong * dlong); // distance in degrees dist *= 111.0; // distance in km return dist; } /********************************************************************************/ // generate a list of files and geocoordinates from the current gallery file list int get_gallerymap() // 17.01 { int ii, jj, cc; xxrec_t *xxrec; if (gallerymap) { // free prior gallerymap for (ii = 0; ii < Ngallerymap; ii++) zfree(gallerymap[ii].file); zfree(gallerymap); gallerymap = 0; } cc = sizeof(gallerymap_t); gallerymap = (gallerymap_t *) zmalloc(navi::Nfiles * cc); for (jj = 0, ii = navi::Nsubdirs; ii < navi::Nfiles; ii++) // loop gallery files { xxrec = get_xxrec(navi::GFlist[ii].file); // look up in xxrec_tab if (! xxrec) continue; gallerymap[jj].flati = xxrec->flati; // 17.01 gallerymap[jj].flongi = xxrec->flongi; gallerymap[jj].file = zstrdup(navi::GFlist[ii].file); jj++; } Ngallerymap = jj; return Ngallerymap; } /********************************************************************************/ // choose to mark map locations for all images or current gallery only void m_set_map_markers(GtkWidget *, cchar *) // 17.01 { F1_help_topic = "set_map_markers"; zdialog *zd; int zstat, showall = 0; /*** _____________________________ | Set Map Markers | | | | (o) mark all image files | | (o) mark current gallery | | | | [apply] | |_____________________________| ***/ zd = zdialog_new(ZTX("Set Map Markers"),Mwin,Bapply,null); zdialog_add_widget(zd,"radio","all","dialog",ZTX("mark all image files")); zdialog_add_widget(zd,"radio","gallery","dialog",ZTX("mark current gallery")); zdialog_stuff(zd,"all",1); zdialog_stuff(zd,"gallery",0); zdialog_restore_inputs(zd); zdialog_resize(zd,200,0); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,null,"mouse"); zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"all",showall); // show all images zdialog_free(zd); if (showall) { if (gallerymap) { // free gallerymap for (int ii = 0; ii < Ngallerymap; ii++) zfree(gallerymap[ii].file); zfree(gallerymap); gallerymap = 0; } } else get_gallerymap(); // show gallery images only if (FGWM == 'W') Fpaint2(); if (FGWM == 'M') netmap_paint_dots(); return; } /********************************************************************************/ // Map Functions for local file maps (W view) // Maps of any scale can be user-installed. // Mercator projection is assumed (but unimportant for maps < 100 km). namespace filemap { char mapname[100]; int mapww, maphh; // map width, height float mflati[2]; // latitude range, low - high float mflongi[2]; // longitude range, low - high } int filemap_position(float flati, float flongi, int &mx, int &my); // earth coordinates > map position int filemap_coordinates(int mx, int my, float &flati, float &flongi); // map position > earth coordinates void find_filemap_images(float flati, float flongi); // find images within range of geolocation /********************************************************************************/ // load the default world map or a map chosen by the user void m_load_filemap(GtkWidget *, cchar *menu) { using namespace filemap; int load_filemap_dialog_event(zdialog *zd, cchar *event); char mapindex[200], mapfile[200]; char buff[200]; cchar *pp; zdialog *zd; int err, zstat, yn; FILE *fid; float flati1, flati2, flongi1, flongi2; STATB statb; F1_help_topic = "worldmap_view"; if (Findexvalid == 0) { yn = zmessageYN(Mwin,Bnoindex); // no image index, offer to enable if (yn) index_rebuild(2,0); else return; } if (Findexvalid == 1) zmessage_post_bold(Mwin,2,Boldindex); // warn, index missing new files if (! init_geolocs()) return; // insure geolocations are loaded snprintf(mapindex,200,"%s/maps_index",maps_dirk); // check map index file exists err = stat(mapindex,&statb); // (from fotoxx-maps package) if (err) goto nomapsinstalled; if (menu && strmatch(menu,"default")) { strcpy(mapname,"World.jpg"); // use default world map goto load_map; } fid = fopen(mapindex,"r"); // open map index file if (! fid) goto nomapsinstalled; F1_help_topic = "choose_worldmap"; zd = zdialog_new(ZTX("choose map file"),Mwin,Bcancel,null); // start map chooser dialog zdialog_add_widget(zd,"combo","mapname","dialog",0,"space=5"); while (true) { pp = fgets_trim(buff,200,fid,1); // get map file names if (! pp) break; pp = strField(pp,",",1); if (! pp) continue; zdialog_cb_app(zd,"mapname",pp); // add to dialog popup list } fclose(fid); snprintf(mapindex,200,"%s/maps_index",user_maps_dirk); // look for user map index file err = stat(mapindex,&statb); if (err) goto choose_worldmap; fid = fopen(mapindex,"r"); // open user map index if (fid) { while (true) { pp = fgets_trim(buff,200,fid,1); // get map file names if (! pp) break; pp = strField(pp,",",1); if (! pp) continue; zdialog_cb_app(zd,"mapname",pp); // add to dialog popup list } fclose(fid); } choose_worldmap: if (*mapname && Wstate.fpxb) // show current map if any zdialog_stuff(zd,"mapname",mapname); zdialog_resize(zd,300,100); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,load_filemap_dialog_event,"mouse"); // run dialog, get user choice zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); // cancel return; } zdialog_fetch(zd,"mapname",mapname,100); // user choice zdialog_free(zd); load_map: snprintf(mapfile,200,"%s/%s",maps_dirk,mapname); // check map file exists err = stat(mapfile,&statb); if (err) { snprintf(mapfile,200,"%s/%s",user_maps_dirk,mapname); // check user maps also err = stat(mapfile,&statb); } if (err) goto mapfilemissing; // not found snprintf(mapindex,200,"%s/maps_index",maps_dirk); // read map index again fid = fopen(mapindex,"r"); if (! fid) goto nomapsinstalled; while (true) { pp = fgets_trim(buff,200,fid,1); // find chosen map file if (! pp) break; pp = strField(buff,",",1); if (! pp) continue; if (strmatch(pp,mapname)) break; } fclose(fid); if (pp) goto get_lat_long; // found snprintf(mapindex,200,"%s/maps_index",user_maps_dirk); // read user map index again fid = fopen(mapindex,"r"); if (! fid) goto nomapsinstalled; while (true) { pp = fgets_trim(buff,200,fid,1); // find chosen map file if (! pp) break; pp = strField(buff,",",1); if (! pp) continue; if (strmatch(pp,mapname)) break; } fclose(fid); if (! pp) goto mapfilemissing; // not found in either index get_lat_long: flati1 = flati2 = flongi1 = flongi2 = 0; pp = strField(buff,",",2); // get map earth coordinates range if (! pp) goto latlongerr; // and verify data OK err = convSF(pp,flati1,-80,+80); if (err) goto latlongerr; pp = strField(buff,",",3); if (! pp) goto latlongerr; err = convSF(pp,flati2,-80,+80); if (err) goto latlongerr; pp = strField(buff,",",4); if (! pp) goto latlongerr; err = convSF(pp,flongi1,-200,+200); if (err) goto latlongerr; pp = strField(buff,",",5); if (! pp) goto latlongerr; err = convSF(pp,flongi2,-200,+200); if (err) goto latlongerr; if (flati2 < flati1 + 0.001) goto latlongerr; // require map range > 100m if (flongi2 < flongi1 + 0.001) goto latlongerr; printz("load filemap: %s \n",mapname); // no errors, commit to load map free_filemap(); // free prior map Ffuncbusy = 1; zmainloop(); Wstate.fpxb = PXB_load(mapfile,1); // load map file (with diagnostic) Ffuncbusy = 0; if (! Wstate.fpxb) return; mapww = Wstate.fpxb->ww; // save map pixel dimensions maphh = Wstate.fpxb->hh; mflati[0] = flati1; // save map earth coordinates range mflati[1] = flati2; mflongi[0] = flongi1; mflongi[1] = flongi2; m_zoom(null,"fit"); return; nomapsinstalled: zmessageACK(Mwin,ZTX("fotoxx-maps package not installed \n" "(see https://kornelix.net)")); return; mapfilemissing: zmessageACK(Mwin,ZTX("map file %s is missing"),mapname); return; latlongerr: zmessageACK(Mwin,ZTX("map latitude/longitude data unreasonable \n" " %.3f %.3f %.3f %.3f"),flati1,flati2,flongi1,flongi2); return; } // dialog event and completion function int load_filemap_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"mapname")) zd->zstat = 1; return 1; } /********************************************************************************/ // Convert latitude and longitude into map position px/py. // Return 0 if OK, +N if error (off the map). int filemap_position(float flati, float flongi, int &px, int &py) { using namespace filemap; float flati1, flati2, flongi1, flongi2; float zww, qy, qy2; flati1 = mflati[0]; // map latitude low - high range flati2 = mflati[1]; flongi1 = mflongi[0]; // map longitude low - high range flongi2 = mflongi[1]; px = py = 0; if (flati < flati1 || flati >= flati2) return 1; // flati/flongi outside map limits if (flongi < flongi1 || flongi >= flongi2) return 1; px = (flongi - flongi1) / (flongi2 - flongi1) * mapww; // return px position zww = mapww * 360.0 / (flongi2 - flongi1); // width for -180 to +180 longitude flati1 = flati1 / RAD; // convert to radians flati2 = flati2 / RAD; flati = flati / RAD; qy2 = (zww/2/PI) * (log(tan(flati2/2 + PI/4))); // flati2 distance from equator qy = (zww/2/PI) * (log(tan(flati/2 + PI/4))); // flati distance from equator py = qy2 - qy; // return py position if (px < 2 || px > mapww-3) return 1; // out of bounds if (py < 2 || py > maphh-3) return 1; // includes margins for red dot return 0; } // Convert map position px/py into latitude and longitude. // Return 0 if OK, +N if error (off the map). int filemap_coordinates(int px, int py, float &flati, float &flongi) { using namespace filemap; float flati1, flati2, flongi1, flongi2; float zww, qy, qy2; flati = flongi = 0; if (px < 0 || px > mapww) return 1; // px/py outside map size if (py < 0 || py > maphh) return 1; flati1 = mflati[0]; // map latitude low - high range flati2 = mflati[1]; flongi1 = mflongi[0]; // map longitude low - high range flongi2 = mflongi[1]; flongi = flongi1 + (1.0 * px / mapww) * (flongi2 - flongi1); // return longitude zww = mapww * 360.0 / (flongi2 - flongi1); // width for -180 to +180 longitude flati1 = flati1 / RAD; // convert to radians flati2 = flati2 / RAD; qy2 = (zww/2/PI) * (log(tan(flati2/2 + PI/4))); // lat2 distance from equator qy2 = qy2 - py; // py distance from equator qy = fabsf(qy2); flati = 2 * atan(exp(2*PI*qy/zww)) - PI/2; if (qy2 < 0) flati = -flati; flati = flati * RAD; // return latitude return 0; } // paint red dots corresponding to image locations on map void filemap_paint_dots() { int ii, err; int mx, my, dx, dy; float flati, flongi, radius; float plati = 999, plongi = 999; if (! Wstate.fpxb) return; // no map loaded cairo_t *cr = draw_context_create(gdkwin,draw_context); // 17.04 radius = map_dotsize / 2; cairo_set_source_rgb(cr,1,0,0); if (gallerymap) // use gallerymap[] if present 17.01 { // mark gallery images on map for (ii = 0; ii < Ngallerymap; ii++) { flati = gallerymap[ii].flati; flongi = gallerymap[ii].flongi; if (flati == plati && flongi == plongi) continue; // skip repititions plati = flati; plongi = flongi; err = filemap_position(flati,flongi,mx,my); if (err) continue; dx = Cstate->mscale * mx - Cstate->morgx + Cstate->dorgx; dy = Cstate->mscale * my - Cstate->morgy + Cstate->dorgy; if (dx < 0 || dx > Dww-1) continue; if (dy < 0 || dy > Dhh-1) continue; cairo_arc(cr,dx,dy,radius,0,2*PI); cairo_fill(cr); } } else { for (ii = 0; ii < Ngeolocs; ii++) // mark all image locations on map { flati = geolocs[ii]->flati; flongi = geolocs[ii]->flongi; err = filemap_position(flati,flongi,mx,my); if (err) continue; dx = Cstate->mscale * mx - Cstate->morgx + Cstate->dorgx; dy = Cstate->mscale * my - Cstate->morgy + Cstate->dorgy; if (dx < 0 || dx > Dww-1) continue; if (dy < 0 || dy > Dhh-1) continue; cairo_arc(cr,dx,dy,radius,0,2*PI); cairo_fill(cr); } } draw_context_destroy(draw_context); // 17.04 return; } /********************************************************************************/ // Respond to mouse movement and left clicks on filemap image. // Set longitude and latitude, and location and country. // Show images near clicked location. void filemap_mousefunc() { int err, mx, my, px, py, ii, minii; char *location, *country; float flati, flongi, glati, glongi; float dist, mindist; float mscale = Cstate->mscale; int capturedist = (map_dotsize + 2) / 2; // mouse - marker capture distance int Fusedot = 0; static char *ploc = 0; char text[20]; zdialog *zd = zd_mapgeotags; if (checkpend("edit busy block")) return; // check nothing pending if (Cstate != &Wstate) return; // view mode not world maps if ((Mxdrag || Mydrag)) return; // pan/scroll - handle normally if (RMclick) return; // zoom - fit window, handle normally if (LMclick && mscale < 1) return; // handle normally if not full size if (! Wstate.fpxb) return; mx = Mxposn; // mouse position, image space my = Myposn; err = filemap_coordinates(mx,my,flati,flongi); if (err) return; // off the map dist = mindist = 999999; minii = 0; for (ii = 0; ii < Ngeolocs; ii++) // find nearest location/country { glati = geolocs[ii]->flati; dist = (flati - glati) * (flati - glati); if (dist > mindist) continue; glongi = geolocs[ii]->flongi; dist += (flongi - glongi) * (flongi - glongi); // degrees**2 if (dist > mindist) continue; mindist = dist; minii = ii; } ii = minii; glati = geolocs[ii]->flati; // closest known place glongi = geolocs[ii]->flongi; location = geolocs[ii]->location; country = geolocs[ii]->country; err = filemap_position(glati,glongi,px,py); // corresp. map image position dist = sqrtf((px-mx) * (px-mx) + (py-my) * (py-my)); dist = dist * mscale; // (mouse - map) in pixels if (dist <= capturedist) Fusedot = 1; // mouse is within marker dot if (LMclick) // left mouse click { LMclick = 0; poptext_window(0,0,0,0,0,0); // remove popup if (zd) // stuff calling dialog { if (Fusedot) { // click within dot zdialog_stuff(zd,"location",location); // use dot location data zdialog_stuff(zd,"country",country); zdialog_stuff(zd,"lati",glati,"%.5f"); // 5 decimal places 18.01 zdialog_stuff(zd,"longi",glongi,"%.5f"); } else { zdialog_stuff(zd,"lati",flati,"%.5f"); // use clicked geocoordinaes only zdialog_stuff(zd,"longi",flongi,"%.5f"); } zdialog_send_event(zd,"geomap"); // activate calling dialog m_viewmode(0,&PFGWM); // restore prior view mode 7.10 } else if (location) find_filemap_images(flati,flongi); // show images in range of location else { snprintf(text,20,"%.5f %.5f",flati,flongi); // show coordinates poptext_window(text,MWIN,Mwxposn,Mwyposn,0.1,3); } } else if (location && Fusedot) { // mouse movement, no click if (! ploc || ! strmatch(location,ploc)) { poptext_window(location,MWIN,Mwxposn,Mwyposn,0.1,1); // popup the city/location name at mouse ploc = location; } } else if (ploc) { poptext_window(0,0,0,0,0,0); // remove popup ploc = 0; } return; } /********************************************************************************/ // find images within the marker size, show gallery of images. // privat function for filemap_mousefunc(), called when a location is clicked void find_filemap_images(float flati, float flongi) { int ii, nn = 0; int x1, y1, x2, y2; int capturedist = (map_dotsize + 2) / 2; // mouse - marker capture distance float glati, glongi, grange; xxrec_t *xxrec; FILE *fid; filemap_position(flati,flongi,x1,y1); // target map pixel location fid = fopen(searchresults_file,"w"); // open output file if (! fid) { zmessageACK(Mwin,"output file error: %s",strerror(errno)); return; } if (gallerymap) // show gallery images at location 17.01 { for (ii = 0; ii < Ngallerymap; ii++) // loop all gallery files { zmainloop(100); // keep GTK alive glati = gallerymap[ii].flati; // file geocoordinates glongi = gallerymap[ii].flongi; filemap_position(glati,glongi,x2,y2); // image map pixel location grange = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); // target - image pixel distance if (grange < 1.5 * capturedist) { // within distance limit, select fprintf(fid,"%s\n",gallerymap[ii].file); // output matching file nn++; } } } else { for (ii = 0; ii < Nxxrec; ii++) // show all images at location { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; glati = xxrec->flati; // 17.01 glongi = xxrec->flongi; filemap_position(glati,glongi,x2,y2); // image map pixel location grange = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); // target - image pixel distance if (grange < 1.5 * capturedist) { // within distance limit, select fprintf(fid,"%s\n",xxrec->file); // output matching file nn++; } } } fclose(fid); if (! nn) { poptext_mouse(Mwin,ZTX("No matching images found"),10,0,0,3); return; } free_resources(); navi::gallerytype = SEARCH; // search results gallery(searchresults_file,"initF",0); // generate gallery of matching files gallery(0,"paint",0); m_viewmode(0,"G"); return; } /********************************************************************************/ // free memory lorge memory used for filemap image // used by edit_setup() to maximize available memory void free_filemap() { if (Wstate.fpxb) PXB_free(Wstate.fpxb); Wstate.fpxb = 0; return; } /********************************************************************************/ // net maps using libchamplain (M view) // working map sources: "net-mapnik" "net-transportmap" namespace netmaps { GtkWidget *mapwidget = 0; ChamplainView *mapview = 0; ChamplainMapSourceFactory *map_factory = 0; ChamplainMapSource *map_source = 0; ChamplainMarkerLayer *markerlayer = 0; ChamplainMarker *marker[maximages]; ClutterColor *markercolor; ChamplainRenderer *renderer; ChamplainMapSource *error_source; ChamplainNetworkTileSource *tile_source; ChamplainFileCache *file_cache; ChamplainMemoryCache *memory_cache; ChamplainMapSourceChain *source_chain; // char *mapbox_access_key; // in fotoxx.h cchar *mapbox_license_text = ""; cchar *mapbox_license_uri = "https://www.mapbox.com/tos/"; cchar *mapbox_access_uri = "https://api.mapbox.com/v4/mapbox.light/#Z#/#X#/#Y#@2x.jpg70?access_token="; int mapbox_tile_size = 512; int mapbox_min_zoom = 1; // for 512x512 tiles int mapbox_max_zoom = 17; int mapbox_file_cache = 400000000; // 400 MB 17.04 int mapbox_memory_cache = 400; // 400 tiles } void netmap_mousefunc(GtkWidget *, GdkEventButton *, void *); // mouse click function for net map void find_netmap_images(float flati, float flongi); // find images at clicked position // menu function - choose net map source void m_netmap_source(GtkWidget *, cchar *) { using namespace netmaps; int netmap_source_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int zstat, nn; char temp[200]; F1_help_topic = "netmap_source"; /*** ________________________________ | Set Map Source | | | | [_] mapnik (default) | | ----------------------------- | | [x] mapbox | | Access Key [________________] | | | | [ OK ] [Cancel] | |________________________________| ***/ zd = zdialog_new(ZTX("Set Map Source"),Mwin,BOK,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"check","mapnik","hb1","mapnik (default)","space=3"); zdialog_add_widget(zd,"hsep","hsep1","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"check","mapbox","hb2","mapbox","space=3"); zdialog_add_widget(zd,"hbox","hb4","dialog"); zdialog_add_widget(zd,"label","labkey","hb4","Access Key","space=3"); zdialog_add_widget(zd,"zentry","key","hb4","","space=3|expand"); zdialog_restore_inputs(zd); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,netmap_source_dialog_event,"mouse"); zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"mapnik",nn); if (nn) netmap_source = zstrdup("mapnik"); zdialog_fetch(zd,"mapbox",nn); if (nn) netmap_source = zstrdup("mapbox"); zdialog_fetch(zd,"key",temp,200); // save key in parameters file mapbox_access_key = zstrdup(temp); // immediately save_params(); if (mapwidget) gtk_widget_destroy(mapwidget); // remove prior map if any markerlayer = 0; m_load_netmap(0,0); zdialog_free(zd); return; } // dialog event and completion function int netmap_source_dialog_event(zdialog *zd, cchar *event) { if (strstr("mapnik mapbox",event)) { // unset and set two checkboxes zdialog_stuff(zd,"mapnik",0); zdialog_stuff(zd,"mapbox",0); zdialog_stuff(zd,event,1); } return 1; } /********************************************************************************/ // initialize for net maps void m_load_netmap(GtkWidget *, cchar *) { using namespace netmaps; int yn; char mapbox_total_uri[200]; F1_help_topic = "netmap_view"; if (Findexvalid == 0) { yn = zmessageYN(Mwin,Bnoindex); // no image index, offer to enable if (yn) index_rebuild(2,0); else return; } if (Findexvalid == 1) zmessage_post_bold(Mwin,2,Boldindex); // warn, index missing new files if (! init_geolocs()) return; // failed Ffuncbusy = 1; if (markerlayer) { // refresh map markers netmap_paint_dots(); // 17.01 Ffuncbusy = 0; return; } mapwidget = gtk_champlain_embed_new(); // libchamplain map drawing area if (! mapwidget) goto fail; gtk_container_add(GTK_CONTAINER(Mvbox),mapwidget); mapview = gtk_champlain_embed_get_view(GTK_CHAMPLAIN_EMBED(mapwidget)); if (! mapview) goto fail; if (strmatch(netmap_source,"mapnik")) // mapnik map source { map_factory = champlain_map_source_factory_dup_default(); map_source = champlain_map_source_factory_create_cached_source(map_factory,"osm-mapnik"); champlain_view_set_min_zoom_level(mapview,3); } else if (strmatch(netmap_source,"mapbox")) // mapbox map source { renderer = CHAMPLAIN_RENDERER(champlain_image_renderer_new()); map_factory = champlain_map_source_factory_dup_default(); strcpy(mapbox_total_uri,mapbox_access_uri); strcat(mapbox_total_uri,mapbox_access_key); tile_source = champlain_network_tile_source_new_full ( "mapbox", "mapbox", mapbox_license_text, mapbox_license_uri, mapbox_min_zoom, mapbox_max_zoom, mapbox_tile_size, CHAMPLAIN_MAP_PROJECTION_MERCATOR, mapbox_total_uri, renderer); error_source = champlain_map_source_factory_create_error_source(map_factory,256); file_cache = champlain_file_cache_new_full(mapbox_file_cache, null, renderer); memory_cache = champlain_memory_cache_new_full(mapbox_memory_cache, renderer); source_chain = champlain_map_source_chain_new(); champlain_map_source_chain_push(source_chain, error_source); champlain_map_source_chain_push(source_chain, CHAMPLAIN_MAP_SOURCE(tile_source)); champlain_map_source_chain_push(source_chain, CHAMPLAIN_MAP_SOURCE(file_cache)); champlain_map_source_chain_push(source_chain, CHAMPLAIN_MAP_SOURCE(memory_cache)); map_source = CHAMPLAIN_MAP_SOURCE(source_chain); } else { zmessageACK(Mwin,"unknown map source: %s \n",netmap_source); Ffuncbusy = 0; return; } champlain_view_set_map_source(mapview,map_source); // mapnik or mapbox markerlayer = champlain_marker_layer_new_full(CHAMPLAIN_SELECTION_SINGLE); if (! markerlayer) goto fail; champlain_view_add_layer(mapview,CHAMPLAIN_LAYER(markerlayer)); champlain_marker_layer_set_selection_mode(markerlayer,CHAMPLAIN_SELECTION_NONE); markercolor = clutter_color_new(255,0,0,255); gtk_widget_add_events(mapwidget,GDK_BUTTON_PRESS_MASK); // connect mouse events to net map G_SIGNAL(mapwidget,"button-press-event",netmap_mousefunc,0); G_SIGNAL(mapwidget,"button-release-event",netmap_mousefunc,0); G_SIGNAL(mapwidget,"motion-notify-event",netmap_mousefunc,0); netmap_paint_dots(); // paint map markers where images 17.01 Ffuncbusy = 0; return; fail: zmessageACK(Mwin,"net/libchamplain failure"); Ffuncbusy = 0; return; } // paint red dots corresponding to image locations on map void netmap_paint_dots() // 17.01 { using namespace netmaps; float flati, flongi; float plati = 999, plongi = 999; champlain_marker_layer_remove_all(markerlayer); if (gallerymap) // use gallerymap[] if present 17.01 { // mark gallery images on map for (int ii = 0; ii < Ngallerymap; ii++) { flati = gallerymap[ii].flati; // image geocoordinates flongi = gallerymap[ii].flongi; if (flati == plati && flongi == plongi) continue; // skip repititions plati = flati; plongi = flongi; marker[ii] = (ChamplainMarker *) champlain_point_new_full(map_dotsize,markercolor); champlain_location_set_location(CHAMPLAIN_LOCATION(marker[ii]),flati,flongi); champlain_marker_layer_add_marker(markerlayer,marker[ii]); } } else { for (int ii = 0; ii < Ngeolocs; ii++) // mark all images on map { flati = geolocs[ii]->flati; flongi = geolocs[ii]->flongi; marker[ii] = (ChamplainMarker *) champlain_point_new_full(map_dotsize,markercolor); champlain_location_set_location(CHAMPLAIN_LOCATION(marker[ii]),flati,flongi); champlain_marker_layer_add_marker(markerlayer,marker[ii]); } } gtk_widget_show_all(mapwidget); return; } /********************************************************************************/ // map zoom-in on location of a selected image file void m_netmap_zoomin(GtkWidget *, cchar *menu) { using namespace netmaps; static char *file = 0; float flati, flongi; xxrec_t *xxrec; if (file) zfree(file); file = 0; if (clicked_file) { // use clicked file if present file = clicked_file; clicked_file = 0; } else if (curr_file) // else current file file = zstrdup(curr_file); else return; xxrec = get_xxrec(file); if (! xxrec) return; flati = xxrec->flati; // 17.01 flongi = xxrec->flongi; if (flati == 0 && flongi == 0) return; netmap_zoomto(flati,flongi,12); return; } // map zoom-in on specified location with specified zoom level void netmap_zoomto(float flati, float flongi, int zoomlev) { using namespace netmaps; m_viewmode(0,"M"); if (! mapview) m_load_netmap(0,0); champlain_view_center_on(mapview,flati,flongi); champlain_view_set_zoom_level(mapview,zoomlev); return; } // get current map scale (meters/pixel) at given zoom level and geocoordinates float netmapscale(int zoomlev, float flat, float flong) { using namespace netmaps; float fmpp = champlain_map_source_get_meters_per_pixel(map_source,zoomlev,flat,flong); return fmpp; } /********************************************************************************/ // Respond to mouse clicks on net map image. void netmap_mousefunc(GtkWidget *widget, GdkEventButton *event, void *) { using namespace netmaps; int mx, my, px, py; int ii, minii; int capturedist = (map_dotsize + 2) / 2; // mouse - marker capture distance int Fusedot = 0; char *location, *country; float flati, flongi, glati, glongi; float dist, mindist; static char *ploc = 0; static int downtime; char text[20]; zdialog *zd = zd_mapgeotags; if (! mapview) return; // net map not available mx = event->x; // mouse position my = event->y; flati = champlain_view_y_to_latitude(mapview,my); // corresp. map coordinates flongi = champlain_view_x_to_longitude(mapview,mx); dist = mindist = 999999; minii = 0; for (ii = 0; ii < Ngeolocs; ii++) // find nearest location/country { glati = geolocs[ii]->flati; dist = (flati - glati) * (flati - glati); if (dist > mindist) continue; glongi = geolocs[ii]->flongi; dist += (flongi - glongi) * (flongi - glongi); // degrees**2 if (dist > mindist) continue; mindist = dist; minii = ii; } ii = minii; glati = geolocs[ii]->flati; // nearest known location (dot marker) glongi = geolocs[ii]->flongi; location = geolocs[ii]->location; country = geolocs[ii]->country; px = champlain_view_longitude_to_x(mapview,glongi); // corresp. map location py = champlain_view_latitude_to_y(mapview,glati); dist = sqrtf((px-mx) * (px-mx) + (py-my) * (py-my)); // distance in pixels if (dist <= capturedist) Fusedot = 1; // mouse is within marker dot if (event->type == GDK_BUTTON_PRESS) { downtime = event->time; return; } if (event->type == GDK_BUTTON_RELEASE) // detect button click 18.01 { // press/release < 600 ms if (event->time - downtime > 600) return; poptext_window(0,0,0,0,0,0); // remove popup if (zd) // stuff calling dialog { if (Fusedot) { // click within dot zdialog_stuff(zd,"location",location); // use nominal dot location data zdialog_stuff(zd,"country",country); zdialog_stuff(zd,"lati",glati,"%.5f"); // 5 decimal places 18.01 zdialog_stuff(zd,"longi",glongi,"%.5f"); } else { zdialog_stuff(zd,"lati",flati,"%.5f"); // use clicked geocoordinates only zdialog_stuff(zd,"longi",flongi,"%.5f"); } zdialog_send_event(zd,"geomap"); // activate calling dialog m_viewmode(0,&PFGWM); // restore prior view mode 7.10 } else if (event->button == 1 && location) // left click on marker find_netmap_images(flati,flongi); // show images for location else if (event->button == 3) { // right click snprintf(text,20,"%.5f %.5f",flati,flongi); // show coordinates poptext_window(text,MWIN,mx,my,0.1,3); } return; } downtime = 0; // mouse motion if (location && Fusedot) { if (! ploc || ! strmatch(location,ploc)) { poptext_window(location,MWIN,mx,my,0.1,1); // popup the location name at mouse ploc = location; } } else if (ploc) { poptext_window(0,0,0,0,0,0); // remove popup ploc = 0; } return; } // find images within the marker size, show gallery of images. // privat function for netmap_mousefunc(), called when a location is clicked void find_netmap_images(float flati, float flongi) { using namespace netmaps; int ii, nn = 0; int x1, y1, x2, y2; int capturedist = (map_dotsize + 2) / 2; // mouse - marker capture distance float glati, glongi, grange; FILE *fid; xxrec_t *xxrec; x1 = champlain_view_longitude_to_x(mapview,flongi); // target map pixel location y1 = champlain_view_latitude_to_y(mapview,flati); fid = fopen(searchresults_file,"w"); // open output file if (! fid) { zmessageACK(Mwin,"output file error: %s",strerror(errno)); return; } if (gallerymap) // show gallery images at location 17.01 { for (ii = 0; ii < Ngallerymap; ii++) // loop all gallery files { zmainloop(100); // keep GTK alive glati = gallerymap[ii].flati; // image geocoordinates glongi = gallerymap[ii].flongi; x2 = champlain_view_longitude_to_x(mapview,glongi); // image map pixel location y2 = champlain_view_latitude_to_y(mapview,glati); grange = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); // mouse - image pixel distance if (grange < 1.5 * capturedist) { // within distance limit, select fprintf(fid,"%s\n",gallerymap[ii].file); // output matching file nn++; } } } else // show all images at location { for (ii = 0; ii < Nxxrec; ii++) { zmainloop(100); // keep GTK alive xxrec = xxrec_tab[ii]; glati = xxrec->flati; // 17.01 glongi = xxrec->flongi; if (glati == 0 && glongi == 0) continue; x2 = champlain_view_longitude_to_x(mapview,glongi); // image map pixel location y2 = champlain_view_latitude_to_y(mapview,glati); grange = sqrtf((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); // mouse - image pixel distance if (grange < 1.5 * capturedist) { // within distance limit, select fprintf(fid,"%s\n",xxrec->file); // output matching file nn++; } } } fclose(fid); if (! nn) { poptext_mouse(Mwin,ZTX("No matching images found"),10,0,0,3); return; } free_resources(); navi::gallerytype = SEARCH; // search results gallery(searchresults_file,"initF",0); // generate gallery of matching files gallery(0,"paint",0); m_viewmode(0,"G"); return; } /********************************************************************************/ // Save current map location (center and scale) with a given name, // or retrieve a previously saved map location. namespace netmap_locs_names { zdialog *zdnetmaploc = 0; char locname[60]; double loclati = 0, loclongi = 0; int loczoom = 12; char netmaplocfile[200]; char buff[100]; } // menu function void m_netmap_locs(GtkWidget *, cchar *) // 17.08 { using namespace netmap_locs_names; int netmap_locs_dialog_event(zdialog *zd, cchar *event); void netmap_locs_clickfunc(GtkWidget *, int line, int pos, int kbkey); zdialog *zd; GtkWidget *mtext; cchar *pp; FILE *fid; F1_help_topic = "netmap_locs"; snprintf(netmaplocfile,200,"%s/netmap_locations",get_zhomedir()); // net map locations file /*** ________________________________ | Net Map Locations | | ______________________________ | || || || map location name 1 || || long map location name 2 || scrolling window || map location name 3 || || ... || ||______________________________|| | | | map location: [______________] | text entry for location name | | | [add] [delete] [done] | |________________________________| [location] empty until filled-in or a location from the list is clicked [add] current location is added to list or replaced [delete] current location is deleted from list location position and scale is from current map location location list is kept in alphabetic order ***/ if (zdnetmaploc) return; // already active zd = zdialog_new(ZTX("Net Map Locations"),Mwin,Badd,Bdelete,Bdone,null); zdnetmaploc = zd; zdialog_add_widget(zd,"scrwin","scroll","dialog",0,"expand"); zdialog_add_widget(zd,"text","mtext","scroll",0,"expand"); zdialog_add_widget(zd,"hbox","hbvn","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labvn","hbvn",ZTX("map location:"),"space=3"); zdialog_add_widget(zd,"zentry","locname","hbvn","","space=3"); zdialog_resize(zd,200,300); zdialog_run(zd,netmap_locs_dialog_event,"parent"); mtext = zdialog_widget(zd,"mtext"); // map location list in dialog textwidget_clear(mtext); fid = fopen(netmaplocfile,"r"); // map location list file if (fid) { while (true) { pp = fgets_trim(buff,100,fid,1); // read location | lati | longi | zoom if (! pp) break; pp = strField(buff,'|',1); // isolate location if (! pp) continue; if (strlen(pp) < 2) continue; textwidget_append(mtext,0,"%s \n",pp); // write into dialog list } fclose(fid); } textwidget_scroll(mtext,0); // scroll to top textwidget_set_callbackfunc(mtext,netmap_locs_clickfunc); // connect click function return; } // dialog event and completion callback function int netmap_locs_dialog_event(zdialog *zd, cchar *event) { using namespace netmaps; using namespace netmap_locs_names; int ff, nn; cchar *pp, *pp2; GtkWidget *mtext; FILE *fidr; if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) // [add] new map location record { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"locname",locname,60); if (strTrim2(locname) < 2) { zmessageACK(Mwin,ZTX("supply a reasonable name")); return 1; } loclati = champlain_view_get_center_latitude(mapview); // get current map location loclongi = champlain_view_get_center_longitude(mapview); loczoom = champlain_view_get_zoom_level(mapview); snprintf(buff,100,"%s|%.4f|%.4f|%d",locname,loclati,loclongi,loczoom); // prepare new location rec. linedit_open(netmaplocfile); // 17.08 ff = 0; while (true) // read next location record { pp = linedit_get(); if (! pp) break; nn = strcasecmp(locname,pp); // compare new location with location record if (! ff && nn < 0) { // new location < location record linedit_put(buff); ff = 1; } linedit_put(pp); // write location record } if (! ff) linedit_put(buff); // insert new location last linedit_close(); goto update_dialog; } if (zd->zstat == 2) // [delete] selected map location record { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"locname",locname,60); linedit_open(netmaplocfile); // 17.08 while (true) // read next location record { pp = linedit_get(); if (! pp) break; pp2 = strField(pp,'|',1); // get location name if (! pp2) continue; if (strmatch(locname,pp2)) continue; // omit deleted location linedit_put(pp); // write location record } linedit_close(); goto update_dialog; } zdialog_destroy(zd); // [done] or [x] zdnetmaploc = 0; return 1; update_dialog: mtext = zdialog_widget(zd,"mtext"); // map location name list in dialog textwidget_clear(mtext); // clear list fidr = fopen(netmaplocfile,"r"); // update dialog list from file if (! fidr) return 1; while (true) { pp = fgets_trim(buff,100,fidr,1); // read location | lati | longi | zoom if (! pp) break; pp = strField(buff,'|',1); // isolate location if (! pp) continue; if (strlen(pp) < 2) continue; textwidget_append(mtext,0,"%s \n",pp); // write into dialog list } fclose(fidr); textwidget_scroll(mtext,0); // scroll to top return 1; } // get clicked location name and set corresponding map location and zoom level void netmap_locs_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) { using namespace netmap_locs_names; cchar *pp1, *pp2; FILE *fidr; zdialog *zd = zdnetmaploc; if (! zd) return; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } pp1 = textwidget_line(widget,line,1); // get clicked line, highlight if (! pp1 || ! *pp1) return; textwidget_highlight_line(widget,line); strTrim2(locname,pp1); zdialog_stuff(zd,"locname",locname); fidr = fopen(netmaplocfile,"r"); // open/read netmap locs file if (! fidr) { zmessageACK(Mwin,strerror(errno)); return; } while (true) // read next location record { pp2 = fgets_trim(buff,100,fidr); if (! pp2) break; pp2 = strField(buff,'|',1); if (strmatch(locname,pp2)) break; // found matching record } fclose(fidr); if (! pp2 || ! strmatch(locname,pp2)) goto notfound; loclati = loclongi = loczoom = 0; pp1 = strField(buff,'|',2); // get map location data from record if (! pp1) goto baddata; loclati = atof(pp1); if (loclati <= -90 || loclati >= +90) goto baddata; pp1 = strField(buff,'|',3); if (! pp1) goto baddata; loclongi = atof(pp1); if (loclongi <= -180 || loclongi >= +180) goto baddata; pp1 = strField(buff,'|',4); if (! pp1) goto baddata; loczoom = atoi(pp1); if (loczoom < 1 || loczoom > 20) goto baddata; netmap_zoomto(loclati,loclongi,loczoom); // set this map location return; notfound: printz("net map location not found: %s \n",locname); return; baddata: printz("net map location invalid: %s %.4f %.4f %d \n", locname,loclati,loclongi,loczoom); return; } /******************************************************************************** Functions to read and write exif/iptc or other metadata *********************************************************************************/ // get EXIF/IPTC metadata for given image file and EXIF/IPTC key(s) // returns array of pointers to corresponding key values // if a key is missing, corresponding pointer is null // returned strings belong to caller, are subject for zfree() // up to 100 keynames may be requested per call // returns: 0 = OK, +N = error int exif_get(cchar *file, cchar **keys, char **kdata, int nkeys) { char *pp, geostring[20]; char *inputs[120], *outputs[100]; // higher limits 18.01 int cc, ii, jj, err; float geofloat; uint ucc; if (nkeys < 1 || nkeys > 99) zappcrash("exif_get nkeys: %d",nkeys); cc = nkeys * sizeof(char *); // clear outputs memset(kdata,0,cc); inputs[0] = (char *) "-m"; // options for exiftool inputs[1] = (char *) "-s2"; inputs[2] = (char *) "-n"; inputs[3] = (char *) "-fast"; // -fast2 loses maker notes jj = 4; for (ii = 0; ii < nkeys; ii++) // build exiftool inputs { cc = strlen(keys[ii]); // -keyname if (! cc) { printz("exif_get() null key \n"); // bugfix 17.04 return 1; } inputs[jj] = (char *) zmalloc(cc+2); inputs[jj][0] = '-'; strcpy(inputs[jj]+1,keys[ii]); if (strmatchcase(keys[ii],"location")) // make "location" = "city" strcpy(inputs[jj]+1,"city"); jj++; } inputs[jj] = zstrdup(file); // filename last jj++; err = exif_server(jj,inputs,outputs); // get exif outputs if (err) return 1; // exif_server() failure for (ii = 4; ii < jj; ii++) // free memory zfree(inputs[ii]); for (ii = 0; ii < nkeys; ii++) // search outputs { pp = outputs[ii]; // keyname: keyvalue if (! pp) break; for (jj = 0; jj < nkeys; jj++) { if (strmatchcase(keys[jj],"location")) { // "location" comes back "city" if (! strmatchcaseN(pp,"city",4)) continue; if (strlen(pp) > 6) kdata[jj] = zstrdup(pp+6); } else if (strmatchN(keys[jj],"GPSLatitude",11)) { // round to 5 decimal places 18.01 if (! strmatchN(pp,"GPSLatitude",11)) continue; err = convSF(pp+13,geofloat); if (err > 1) continue; snprintf(geostring,20,"%.5f",geofloat); kdata[jj] = zstrdup(geostring); } else if (strmatchN(keys[jj],"GPSLongitude",12)) { // round to 5 decimal places 18.01 if (! strmatchN(pp,"GPSLongitude",12)) continue; err = convSF(pp+14,geofloat); if (err > 1) continue; snprintf(geostring,20,"%.5f",geofloat); kdata[jj] = zstrdup(geostring); } else { ucc = strlen(keys[jj]); // look for matching input keyname if (! strmatchcaseN(pp,keys[jj],ucc)) continue; if (strlen(pp) > ucc+2) kdata[jj] = zstrdup(pp+ucc+2); } } zfree(pp); } return 0; } /********************************************************************************/ // create or change EXIF/IPTC metadata for given image file and key(s) // // command: // exiftool -m -overwrite_original -keyname="keyvalue" ... "file" // // NOTE: exiftool replaces \n (newline) in keyvalue with . (period). int exif_put(cchar *file, cchar **keys, cchar **kdata, int nkeys) { int ii, jj, cc; char *inputs[40]; if (nkeys < 1 || nkeys > 30) zappcrash("exif_put nkeys: %d",nkeys); // higher limit 18.01 inputs[0] = (char *) "-m"; // exiftool options inputs[1] = (char *) "-overwrite_original"; // -P preserve date removed jj = 2; for (ii = 0; ii < nkeys; ii++) // build exiftool inputs { if (! kdata[ii]) continue; // skip missing data bugfix 17.04.3 cc = strlen(keys[ii]) + strlen(kdata[ii]) + 3; inputs[jj] = (char *) zmalloc(cc); inputs[jj][0] = '-'; // -keyname=value if (strmatchcase(keys[ii],"location")) { // make "location" = "city" strcpy(inputs[jj]+1,"city"); cc = 4; } else { strcpy(inputs[jj]+1,keys[ii]); cc = strlen(keys[ii]); } inputs[jj][cc+1] = '='; strcpy(inputs[jj]+cc+2,kdata[ii]); jj++; if (strmatchcase(keys[ii],"GPSLatitude")) { // take care of latitude N/S if (*kdata[ii] == '-') inputs[jj] = zstrdup("-GPSLatitudeRef=S"); else inputs[jj] = zstrdup("-GPSLatitudeRef=N"); jj++; } if (strmatchcase(keys[ii],"GPSLongitude")) { // and longitude E/W if (*kdata[ii] == '-') inputs[jj] = zstrdup("-GPSLongitudeRef=W"); else inputs[jj] = zstrdup("-GPSLongitudeRef=E"); jj++; } } inputs[jj] = zstrdup(file); // last input is filename jj++; exif_server(jj,inputs,0); // outputs discarded for (ii = 2; ii < jj; ii++) // free memory zfree(inputs[ii]); return 0; } /********************************************************************************/ // copy EXIF/IPTC data from one image file to new (edited) image file // if nkeys > 0: added keys[] to be replaced with new values kdata[] // exiftool -m -tagsfromfile file1 -all -xmp -iptc -icc_profile // [-keyname=newvalue ...] file2 -overwrite_original int exif_copy(cchar *file1, cchar *file2, cchar **keys, cchar **kdata, int nkeys) { char *inputs[40]; // revised int cc, ii, jj, nfixed; if (nkeys > 30) zappcrash("exif_copy() nkeys %d",nkeys); // higher limit 18.01 inputs[0] = (char *) "-m"; // -m (suppress warnings) inputs[1] = (char *) "-tagsfromfile"; // -tagsfromfile inputs[2] = zstrdup(file1); // file1 inputs[3] = (char *) "-all"; // -all inputs[4] = (char *) "-xmp"; // -xmp inputs[5] = (char *) "-iptc"; // -iptc inputs[6] = (char *) "-icc_profile"; // -icc_profile nfixed = jj = 7; // no. fixed inputs for (int ii = 0; ii < nkeys; ii++) // -keyname=keyvalue { if (! kdata[ii]) continue; // skip missing data bugfix 17.04.3 cc = strlen(keys[ii]) + strlen(kdata[ii]) + 3; inputs[jj] = (char *) zmalloc(cc); *inputs[jj] = 0; strncatv(inputs[jj],cc,"-",keys[ii],"=",kdata[ii],null); jj++; } inputs[jj++] = zstrdup(file2); // file2 inputs[jj++] = (char *) "-overwrite_original"; // -overwrite_original exif_server(jj,inputs,0); zfree(inputs[2]); // free memory for (ii = nfixed; ii < nfixed+nkeys; ii++) zfree(inputs[ii]); return 0; } /******************************************************************************** int exif_server(int Nrecs, char **inputs, char **outputs) Server wrapper for exiftool for put/get exif/iptc data. This saves perl startup overhead for each call (0.1 >> 0.01 secs). Input records: -opt1 -opt2 ... -keyword1=value1 -keyword2=value2 ... filename.jpg (null pointer) Returned: list of pointers to resulting output records. There are <= Nrecs outputs corresponding to keyword inputs. These are formatted keyword: text-string-value. If < Nrecs output, unused outputs are null pointers. These are subject to zfree(). If outputs = null, outputs are discarded. Returns 0 if OK, +N if error. First call: Starts exiftool with pipe input and output files. Subsequent calls: The existing exiftool process is re-used so that the substantial startup overhead is avoided. To kill the exiftool process: exif_server(0,0,0). There are two servers which can run parallel if called by two threads. The exiftool perl process has a significant memory leak that can reach // bugfix 18.01 gigabytes if Fotoxx image index function must process 100K files or more. Solution: exit perl process after 1000 calls and start over with next call. *********************************************************************************/ int exif_server(int Nrecs, char **inputs, char **outputs) { int exif_server1(int Nrecs, char **inputs, char **outputs); // 4 parallel server processes int exif_server2(int Nrecs, char **inputs, char **outputs); int exif_server3(int Nrecs, char **inputs, char **outputs); int exif_server4(int Nrecs, char **inputs, char **outputs); static int busy1 = 0, busy2 = 0, busy3 = 0, busy4 = 0; int err; if (! Nrecs) { exif_server1(Nrecs,inputs,outputs); // kill all exiftool processes exif_server2(Nrecs,inputs,outputs); exif_server3(Nrecs,inputs,outputs); exif_server4(Nrecs,inputs,outputs); return 0; } while (true) { if (resource_lock(busy1)) { // use resource locks 18.01 err = exif_server1(Nrecs,inputs,outputs); resource_unlock(busy1); return err; } if (resource_lock(busy2)) { err = exif_server2(Nrecs,inputs,outputs); resource_unlock(busy2); return err; } if (resource_lock(busy3)) { err = exif_server3(Nrecs,inputs,outputs); resource_unlock(busy3); return err; } if (resource_lock(busy4)) { err = exif_server4(Nrecs,inputs,outputs); resource_unlock(busy4); return err; } zsleep(0.001); } } int exif_server1(int Nrecs, char **inputs, char **outputs) // exiftool process 1 { static int fcf = 1; // first call flag static int Ncalls = 0; static FILE *fid1 = 0, *fid2 = 0; // exiftool input, output files static char input_file[100] = ""; int ii, cc, err; char *pp, command[100]; char outrec[exif_maxcc]; // single output record if (! Nrecs || Ncalls > 1000) // kill exiftool if requested { // or after 1000 calls 18.01 if (fcf) return 0; // never started fid1 = fopen(input_file,"a"); if (fid1) { fprintf(fid1,"-stay_open\nFalse\n"); // tell it to exit fclose(fid1); // fflush after fclose } remove(input_file); if (fid2) pclose(fid2); fid2 = 0; fcf = 1; // restart if called again if (! Nrecs) return 0; // return only if kill request 18.01 } if (fcf) // first call only { snprintf(input_file,100,"%s/exiftool_input1",tempdir); // only difference in serverN NOTE fid1 = fopen(input_file,"w"); // start exiftool input file if (! fid1) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 1; } snprintf(command,100,"exiftool -stay_open True -@ %s; exit",input_file); // exit shell when exit exiftool 18.01 fid2 = popen(command,"r"); // start exiftool and output file if (! fid2) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); fclose(fid1); return 2; } fcf = 0; Ncalls = 0; } Ncalls++; // count calls 18.01 for (ii = 0; ii < Nrecs; ii++) fprintf(fid1,"%s\n",inputs[ii]); // write to exiftool, 1 per record err = fprintf(fid1,"-execute\n"); // tell exiftool to process if (err < 0) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 3; } fflush(fid1); // flush buffer if (outputs) { cc = Nrecs * sizeof(char *); // clear outputs memset(outputs,0,cc); } for (ii = 0; ii < 99; ii++) // get ALL exiftool outputs { pp = fgets_trim(outrec,exif_maxcc,fid2,1); if (! pp) break; if (strncmp(outrec,"{ready}",7) == 0) break; // look for output end if (outputs && ii < Nrecs) outputs[ii] = zstrdup(outrec); // add to returned records } return 0; } int exif_server2(int Nrecs, char **inputs, char **outputs) // exiftool process 2 { static int fcf = 1; // first call flag static int Ncalls = 0; static FILE *fid1 = 0, *fid2 = 0; // exiftool input, output files static char input_file[100] = ""; int ii, cc, err; char *pp, command[100]; char outrec[exif_maxcc]; // single output record if (! Nrecs || Ncalls > 1000) // kill exiftool if requested { // or after 1000 calls 18.01 if (fcf) return 0; // never started fid1 = fopen(input_file,"a"); if (fid1) { fprintf(fid1,"-stay_open\nFalse\n"); // tell it to exit fclose(fid1); // fflush after fclose } remove(input_file); if (fid2) pclose(fid2); fid2 = 0; fcf = 1; // restart if called again if (! Nrecs) return 0; // return only if kill request 18.01 } if (fcf) // first call only { snprintf(input_file,100,"%s/exiftool_input2",tempdir); // only difference in serverN NOTE fid1 = fopen(input_file,"w"); // start exiftool input file if (! fid1) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 1; } snprintf(command,100,"exiftool -stay_open True -@ %s; exit",input_file); // exit shell when exit exiftool 18.01 fid2 = popen(command,"r"); // start exiftool and output file if (! fid2) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); fclose(fid1); return 2; } fcf = 0; Ncalls = 0; } Ncalls++; // count calls 18.01 for (ii = 0; ii < Nrecs; ii++) fprintf(fid1,"%s\n",inputs[ii]); // write to exiftool, 1 per record err = fprintf(fid1,"-execute\n"); // tell exiftool to process if (err < 0) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 3; } fflush(fid1); // flush buffer if (outputs) { cc = Nrecs * sizeof(char *); // clear outputs memset(outputs,0,cc); } for (ii = 0; ii < 99; ii++) // get ALL exiftool outputs { pp = fgets_trim(outrec,exif_maxcc,fid2,1); if (! pp) break; if (strncmp(outrec,"{ready}",7) == 0) break; // look for output end if (outputs && ii < Nrecs) outputs[ii] = zstrdup(outrec); // add to returned records } return 0; } int exif_server3(int Nrecs, char **inputs, char **outputs) // exiftool process 3 { static int fcf = 1; // first call flag static int Ncalls = 0; static FILE *fid1 = 0, *fid2 = 0; // exiftool input, output files static char input_file[100] = ""; int ii, cc, err; char *pp, command[100]; char outrec[exif_maxcc]; // single output record if (! Nrecs || Ncalls > 1000) // kill exiftool if requested { // or after 1000 calls 18.01 if (fcf) return 0; // never started fid1 = fopen(input_file,"a"); if (fid1) { fprintf(fid1,"-stay_open\nFalse\n"); // tell it to exit fclose(fid1); // fflush after fclose } remove(input_file); if (fid2) pclose(fid2); fid2 = 0; fcf = 1; // restart if called again if (! Nrecs) return 0; // return only if kill request 18.01 } if (fcf) // first call only { snprintf(input_file,100,"%s/exiftool_input3",tempdir); // only difference in serverN NOTE fid1 = fopen(input_file,"w"); // start exiftool input file if (! fid1) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 1; } snprintf(command,100,"exiftool -stay_open True -@ %s; exit",input_file); // exit shell when exit exiftool 18.01 fid2 = popen(command,"r"); // start exiftool and output file if (! fid2) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); fclose(fid1); return 2; } fcf = 0; Ncalls = 0; } Ncalls++; // count calls 18.01 for (ii = 0; ii < Nrecs; ii++) fprintf(fid1,"%s\n",inputs[ii]); // write to exiftool, 1 per record err = fprintf(fid1,"-execute\n"); // tell exiftool to process if (err < 0) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 3; } fflush(fid1); // flush buffer if (outputs) { cc = Nrecs * sizeof(char *); // clear outputs memset(outputs,0,cc); } for (ii = 0; ii < 99; ii++) // get ALL exiftool outputs { pp = fgets_trim(outrec,exif_maxcc,fid2,1); if (! pp) break; if (strncmp(outrec,"{ready}",7) == 0) break; // look for output end if (outputs && ii < Nrecs) outputs[ii] = zstrdup(outrec); // add to returned records } return 0; } int exif_server4(int Nrecs, char **inputs, char **outputs) // exiftool process 4 { static int fcf = 1; // first call flag static int Ncalls = 0; static FILE *fid1 = 0, *fid2 = 0; // exiftool input, output files static char input_file[100] = ""; int ii, cc, err; char *pp, command[100]; char outrec[exif_maxcc]; // single output record if (! Nrecs || Ncalls > 1000) // kill exiftool if requested { // or after 1000 calls 18.01 if (fcf) return 0; // never started fid1 = fopen(input_file,"a"); if (fid1) { fprintf(fid1,"-stay_open\nFalse\n"); // tell it to exit fclose(fid1); // fflush after fclose } remove(input_file); if (fid2) pclose(fid2); fid2 = 0; fcf = 1; // restart if called again if (! Nrecs) return 0; // return only if kill request 18.01 } if (fcf) // first call only { snprintf(input_file,100,"%s/exiftool_input4",tempdir); // only difference in serverN NOTE fid1 = fopen(input_file,"w"); // start exiftool input file if (! fid1) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 1; } snprintf(command,100,"exiftool -stay_open True -@ %s; exit",input_file); // exit shell when exit exiftool 18.01 fid2 = popen(command,"r"); // start exiftool and output file if (! fid2) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); fclose(fid1); return 2; } fcf = 0; Ncalls = 0; } Ncalls++; // count calls 18.01 for (ii = 0; ii < Nrecs; ii++) fprintf(fid1,"%s\n",inputs[ii]); // write to exiftool, 1 per record err = fprintf(fid1,"-execute\n"); // tell exiftool to process if (err < 0) { zmessageACK(Mwin,"exif_server: %s \n",strerror(errno)); return 3; } fflush(fid1); // flush buffer if (outputs) { cc = Nrecs * sizeof(char *); // clear outputs memset(outputs,0,cc); } for (ii = 0; ii < 99; ii++) // get ALL exiftool outputs { pp = fgets_trim(outrec,exif_maxcc,fid2,1); if (! pp) break; if (strncmp(outrec,"{ready}",7) == 0) break; // look for output end if (outputs && ii < Nrecs) outputs[ii] = zstrdup(outrec); // add to returned records } return 0; } /********************************************************************************/ // convert between EXIF and fotoxx tag date formats // EXIF date: yyyy:mm:dd hh:mm:ss 20 chars. // tag date: yyyymmddhhmmss 16 chars. // void exif_tagdate(cchar *exifdate, char *tagdate) { int cc; memset(tagdate,0,15); cc = strlen(exifdate); if (cc > 3) strncpy(tagdate+0,exifdate+0,4); if (cc > 6) strncpy(tagdate+4,exifdate+5,2); if (cc > 9) strncpy(tagdate+6,exifdate+8,2); if (cc > 12) strncpy(tagdate+8,exifdate+11,2); if (cc > 15) strncpy(tagdate+10,exifdate+14,2); if (cc > 18) strncpy(tagdate+12,exifdate+17,2); tagdate[14] = 0; return; } void tag_exifdate(cchar *tagdate, char *exifdate) { int cc; memset(exifdate,0,20); cc = strlen(tagdate); strcpy(exifdate,"1900:01:01 00:00:00"); if (cc > 3) strncpy(exifdate+0,tagdate+0,4); if (cc > 5) strncpy(exifdate+5,tagdate+4,2); if (cc > 7) strncpy(exifdate+8,tagdate+6,2); if (cc > 9) strncpy(exifdate+11,tagdate+8,2); if (cc > 11) strncpy(exifdate+14,tagdate+10,2); if (cc > 13) strncpy(exifdate+17,tagdate+12,2); exifdate[19] = 0; return; } /******************************************************************************** Functions to read and write image index file on disk and update the image index memory table (xxrec table). *********************************************************************************/ // Get the image index record for the given image file. // Returns pointer to xxrec or null if not found. // Returned xxrec fields are NOT subjects for zfree() xxrec_t * get_xxrec(cchar *file) { int ii, jj, kk, rkk, last; if (! file || *file != '/') return 0; if (! Findexvalid) return 0; // index not valid if (! Nxxrec) return 0; // no data ii = Nxxrec / 2; // next table entry to search jj = (ii + 1) / 2; // next increment last = Nxxrec - 1; // last entry rkk = 0; while (true) // binary search { kk = strcmp(xxrec_tab[ii]->file,file); // compare table entry to file if (kk > 0) { ii -= jj; // too high, go back in table if (ii < 0) break; } else if (kk < 0) { ii += jj; // too low, go forward in table if (ii > last) break; } else return xxrec_tab[ii]; // matched - return xxrec jj = jj / 2; // reduce increment if (jj == 0) { jj = 1; // step by 1 element if (! rkk) rkk = kk; // save last direction else { if (rkk > 0 && kk < 0) break; // if direction change, fail if (rkk < 0 && kk > 0) break; } } } return 0; // not found } /********************************************************************************/ // minimized version of get_xxrec() for use by gallery window. // fdate[16], pdate[16] and size[16] are provided by caller for returned data. // returns 0 if found, 1 if not (get_xxrec() diagnoses) int get_xxrec_min(cchar *file, char *fdate, char *pdate, char *size) { xxrec_t *xxrec; xxrec = get_xxrec(file); if (! xxrec) { strcpy(fdate,"null"); strcpy(pdate,"null"); strcpy(size,"no index"); return 1; } strncpy0(fdate,xxrec->fdate,15); if (strmatch(xxrec->pdate,"null")) strcpy(pdate,"null"); else strncpy0(pdate,xxrec->pdate,15); strncpy0(size,xxrec->size,15); return 0; } /********************************************************************************/ // Add or update the image index record for the given image file. // If xxrec is null, delete the table entry for the file. // Append new index record data to the image index file on disk. // (previous record, if any, is now superceeded but still present) // Return 0 if success, +N if error (diagnosed). int put_xxrec(xxrec_t *xxrec, cchar *file) { char indexfile[200]; int ii, iix, nn, err; int Finsert, Fdelete, Freplace, Fdone; xxrec_t *xxrec_old, *xxrec_new = 0; FILE *fid; STATB statb; if (! file || *file != '/') { zmessageACK(Mwin,"put_xxrec() file: %s",file); return 0; } if (! Findexvalid) return 1; // image index not valid if (! Nxxrec) return 1; // no data if (xxrec) { err = stat(file,&statb); // check image file exists if (err || ! S_ISREG(statb.st_mode)) { zmessageACK(Mwin,"file %s not found \n",file); return 0; } } xxrec_old = get_xxrec(file); // old image index record, if any if (! xxrec && ! xxrec_old) return 0; // nothing to do Finsert = Fdelete = Freplace = Fdone = 0; if (! xxrec && xxrec_old) Fdelete = 1; // delete old record if (xxrec && ! xxrec_old) Finsert = 1; // insert new record if (xxrec && xxrec_old) Freplace = 1; // replace old record if (Finsert || Freplace) { xxrec_new = (xxrec_t *) zmalloc(sizeof(xxrec_t)); // make new xxrec with data from caller memset(xxrec_new,0,sizeof(xxrec_t)); xxrec_new->file = zstrdup(file); compact_time(statb.st_mtime,xxrec_new->fdate); // refresh file mod date/time if (xxrec->pdate[0]) strcpy(xxrec_new->pdate,xxrec->pdate); else strcpy(xxrec_new->pdate,"null"); if (xxrec->size[0]) strcpy(xxrec_new->size,xxrec->size); else strcpy(xxrec_new->size,"null"); if (xxrec->rating[0]) strcpy(xxrec_new->rating,xxrec->rating); else strcpy(xxrec_new->rating,"0"); if (xxrec->tags) xxrec_new->tags = zstrdup(xxrec->tags); else xxrec_new->tags = zstrdup("null"); if (xxrec->capt) xxrec_new->capt = zstrdup(xxrec->capt); else xxrec_new->capt = zstrdup("null"); if (xxrec->comms) xxrec_new->comms = zstrdup(xxrec->comms); else xxrec_new->comms = zstrdup("null"); xxrec_new->location = zstrdup(xxrec->location); // 17.01 xxrec_new->country = zstrdup(xxrec->country); xxrec_new->flati = xxrec->flati; xxrec_new->flongi = xxrec->flongi; xxrec_new->xmeta = xxrec->xmeta; // 18.01 } iix = -1; for (ii = 0; ii < Nxxrec; ii++) // find target position in table { nn = strcmp(file,xxrec_tab[ii]->file); if (nn > 0) continue; if (nn == 0) { if (Finsert) goto badbug; // insert duplicate iix = ii; // entry to delete or replace break; } if (nn < 0) { if (Fdelete || Freplace) goto badbug; // not found iix = ii; // place to insert new entry break; // (current entry will be next) } } if (iix < 0 && (Fdelete || Freplace)) goto badbug; // not found if (iix < 0) iix = Nxxrec; // add to end of table if (Nxxrec == maximages) { zmessageACK(Mwin,"exceed %d max files, cannot continue",maximages); quitxx(); } if (Finsert) { for (ii = Nxxrec; ii > iix; ii--) // make empty slo1 xxrec_tab[ii] = xxrec_tab[ii-1]; xxrec_tab[iix] = xxrec_new; // insert new entry Nxxrec++; } if (Fdelete) { zfree(xxrec_tab[iix]->file); // delete old entry zfree(xxrec_tab[iix]->tags); zfree(xxrec_tab[iix]->capt); zfree(xxrec_tab[iix]->comms); zfree(xxrec_tab[iix]->location); // 17.01 zfree(xxrec_tab[iix]->country); zfree(xxrec_tab[iix]->xmeta); zfree(xxrec_tab[iix]); // 18.01 Nxxrec--; for (ii = iix; ii < Nxxrec; ii++) // pack down xxrec_tab[ii] = xxrec_tab[ii+1]; } if (Freplace) { zfree(xxrec_tab[iix]->file); // replace old entry zfree(xxrec_tab[iix]->tags); zfree(xxrec_tab[iix]->capt); zfree(xxrec_tab[iix]->comms); zfree(xxrec_tab[iix]->location); // 17.01 zfree(xxrec_tab[iix]->country); zfree(xxrec_tab[iix]->xmeta); // 18.01 zfree(xxrec_tab[iix]); xxrec_tab[iix] = xxrec_new; // replace with new entry } if (Finsert || Freplace) { snprintf(indexfile,200,"%s/indexC",index_dirk); fid = fopen(indexfile,"a"); // append new record to image index file if (! fid) goto file_err; nn = fprintf(fid,"file: %s\n",file); // file path name if (! nn) goto file_err; nn = fprintf(fid,"date: %s %s\n",xxrec_new->pdate,xxrec_new->fdate); // photo and file date record if (! nn) goto file_err; nn = fprintf(fid,"size: %s\n",xxrec_new->size); // size record if (! nn) goto file_err; nn = fprintf(fid,"stars: %s\n",xxrec_new->rating); // rating record if (! nn) goto file_err; nn = fprintf(fid,"tags: %s\n",xxrec_new->tags); // tags record if (! nn) goto file_err; nn = fprintf(fid,"capt: %s\n",xxrec_new->capt); // caption record if (! nn) goto file_err; nn = fprintf(fid,"comms: %s\n",xxrec_new->comms); // comments record if (! nn) goto file_err; nn = fprintf(fid,"gtags: %s^ %s^ %.4f^ %.4f\n", // 17.01 xxrec_new->location, xxrec_new->country, xxrec_new->flati, xxrec_new->flongi); if (! nn) goto file_err; nn = fprintf(fid,"xmeta: %s\n",xxrec_new->xmeta); // 18.01 if (! nn) goto file_err; nn = fprintf(fid,"END\n"); // EOL 18.01 if (! nn) goto file_err; err = fclose(fid); if (err) goto file_err; } return 0; file_err: zmessageACK(Mwin,"image index write error\n %s",strerror(errno)); if (fid) fclose(fid); fid = 0; return 3; badbug: zmessageACK(Mwin,"bug in put_xxrec() \n %s",file); return 4; } /********************************************************************************/ // Read image index files sequentially, return one index record per call. // Set ftf = 1 for first read, will be reset to 0. // Returns xxrec or null for EOF or error. // Returned xxrec_t and its allocated pointers are subject to zfree(). // Used by index_rebuild() function. xxrec_t * read_xxrec_seq(int &ftf) { xxrec_t *xxrec = 0; char indexfile[200]; static FILE *fid = 0; static char buff[indexrecl]; cchar *pp, *pp2; float flati, flongi; if (ftf) // initial call { ftf = 0; snprintf(indexfile,200,"%s/indexC",index_dirk); fid = fopen(indexfile,"r"); if (! fid) return 0; // no index file ? *buff = 0; // insure no leftover data } while (true) // read to next "file: " record 18.01 { pp = fgets_trim(buff,indexrecl,fid); if (! pp) { fclose(fid); // EOF return 0; } if (strmatchN(pp,"file: ",6)) break; } xxrec = (xxrec_t *) zmalloc(sizeof(xxrec_t)); // allocate returned xxrec memset(xxrec,0,sizeof(xxrec_t)); xxrec->file = zstrdup(buff+6); // image file name while (true) // get recs following "file" record { pp = fgets_trim(buff,indexrecl,fid); if (! pp) break; if (strmatchN(pp,"END",3)) break; // end of recs for this file 18.01 else if (strmatchN(pp,"date: ",6)) { pp += 6; pp2 = strField(pp,' ',1); // EXIF (photo) date, yyyymmddhhmmss if (pp2) strncpy0(xxrec->pdate,pp2,15); pp2 = strField(pp,' ',2); if (pp2) strncpy0(xxrec->fdate,pp2,15); // file mod date, yyyymmddhhmmss } else if (strmatchN(pp,"stars: ",7)) { // rating, "0" to "5" stars xxrec->rating[0] = *(pp+7); xxrec->rating[1] = 0; } else if (strmatchN(pp,"size: ",6)) // size, "NNNNxNNNN" strncpy0(xxrec->size,pp+6,15); else if (strmatchN(pp,"tags: ",6)) // tags xxrec->tags = zstrdup(pp+6); else if (strmatchN(pp,"capt: ",6)) // caption xxrec->capt = zstrdup(pp+6); else if (strmatchN(pp,"comms: ",7)) // comments xxrec->comms = zstrdup(pp+7); else if (strmatchN(pp,"gtags: ",7)) { // geotags 17.01 pp += 7; pp2 = strField(pp,"^",1); if (pp2) xxrec->location = zstrdup(pp2); else xxrec->location = zstrdup("null"); pp2 = strField(pp,"^",2); if (pp2) xxrec->country = zstrdup(pp2); else xxrec->country = zstrdup("null"); flati = flongi = 999; pp2 = strField(pp,"^",3); if (pp2) flati = atof(pp2); pp2 = strField(pp,"^",4); if (pp2) flongi = atof(pp2); if (strmatch(xxrec->location,"null")) flati = flongi = 0; if (strmatch(xxrec->country,"null")) flati = flongi = 0; if (flati < -90.0 || flati > 90.0) flati = flongi = 0; if (flongi < -180.0 || flongi > 180.0) flati = flongi = 0; xxrec->flati = flati; xxrec->flongi = flongi; } else if (strmatchN(pp,"xmeta: ",7)) // indexed metadata 18.01 xxrec->xmeta = zstrdup(pp+7); } if (! xxrec->pdate[0]) // supply defaults for missing items strcpy(xxrec->pdate,"null"); if (! xxrec->rating[0]) strcpy(xxrec->rating,"0"); if (! xxrec->size[0]) strcpy(xxrec->size,"null"); if (! xxrec->tags) xxrec->tags = zstrdup("null"); if (! xxrec->capt) xxrec->capt = zstrdup("null"); if (! xxrec->comms) xxrec->comms = zstrdup("null"); if (! xxrec->location) // 17.01 xxrec->location = zstrdup("null"); if (! xxrec->country) xxrec->country = zstrdup("null"); if (strmatch(xxrec->location,"null") || strmatch(xxrec->country,"null")) xxrec->flati = xxrec->flongi = 0; if (! xxrec->xmeta) // 18.01 xxrec->xmeta = zstrdup("null"); return xxrec; } /********************************************************************************/ // Write the image index files sequentially, 1 record per call // Set ftf = 1 for first call, will be reset to 0. // Set xxrec = 0 to close file after last write. // Returns 0 if OK, otherwise +N (diagnosed). // Used by index_rebuild() function. int write_xxrec_seq(xxrec_t *xxrec, int &ftf) { static FILE *fid = 0; char indexfile[200]; int nn, err; if (ftf) // first call { ftf = 0; snprintf(indexfile,200,"%s/indexC",index_dirk); // create image index file fid = fopen(indexfile,"w"); if (! fid) goto file_err; } if (! xxrec) { // EOF call if (fid) { err = fclose(fid); fid = 0; if (err) goto file_err; } return 0; } nn = fprintf(fid,"file: %s\n",xxrec->file); // output: filename record if (! nn) goto file_err; if (! xxrec->pdate[0]) strcpy(xxrec->pdate,"null"); // missing EXIF date << "null" nn = fprintf(fid,"date: %s %s\n",xxrec->pdate,xxrec->fdate); // date: yyyymmddhhmmss yyyymmddhhmmss if (! nn) goto file_err; if (xxrec->rating[0]) nn = fprintf(fid,"stars: %c\n",xxrec->rating[0]); // stars: N else nn = fprintf(fid,"stars: 0\n"); if (! nn) goto file_err; if (xxrec->size[0]) // size: 5000x3000 nn = fprintf(fid,"size: %s\n",xxrec->size); else nn = fprintf(fid,"size: null\n"); if (xxrec->tags) nn = fprintf(fid,"tags: %s\n",xxrec->tags); // tags: aaaaa, bbbbb else nn = fprintf(fid,"tags: null, ""\n"); if (! nn) goto file_err; if (xxrec->capt) nn = fprintf(fid,"capt: %s\n",xxrec->capt); // caption: text else nn = fprintf(fid,"capt: null\n"); if (! nn) goto file_err; if (xxrec->comms) nn = fprintf(fid,"comms: %s\n",xxrec->comms); // comments: text else nn = fprintf(fid,"comms: null\n"); if (! nn) goto file_err; nn = fprintf(fid,"gtags: %s^ %s^ %.4f^ %.4f\n", // 17.01 xxrec->location, xxrec->country, xxrec->flati, xxrec->flongi); if (! nn) goto file_err; if (xxrec->xmeta) nn = fprintf(fid,"xmeta: %s\n",xxrec->xmeta); // xmeta: name1^ name2^ ... 18.01 else nn = fprintf(fid,"xmeta: null\n"); if (! nn) goto file_err; nn = fprintf(fid,"END\n"); // EOL 18.01 if (! nn) goto file_err; return 0; file_err: zmessageACK(Mwin,"image index write error\n %s",strerror(errno)); if (fid) fclose(fid); fid = 0; return 2; } fotoxx-18.01.1/f.process.cc0000644000175000017500000032520313222767271014100 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit - Process Menu functions m_batch_convert batch rename, convert, resize, move batch_sharp_func callable sharpen function m_batch_upright find rotated files and upright them m_batch_deltrash delete or trash selected files m_batch_raw convert RAW files using libraw or Raw Therapee m_scriptfiles script files for batch editing m_burn_DVD burn images to DVD/Blue-Ray disc m_duplicates find all duplicated images m_export_filelist select files and generate a file list (text) m_export_images select files and export to a directory *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) #include /********************************************************************************/ // Batch file rename, convert, resize, move namespace batch_convert { int Fsametype, Fsamesize, maxww, maxhh; int Fdelete, Fcopymeta, Fupright, Fsharpen, Freplace; int Fplugdate, Fplugseq, Fplugname; char newloc[500], newname[200], newext[8]; int baseseq, addseq; int amount, thresh; int Foverlay, ovpos, ovwidth; int Fovconst, ovscrww, ovscrhh; char *ovfile = 0; }; // menu function void m_batch_convert(GtkWidget *, cchar *) { using namespace batch_convert; int batch_convert_dialog_event(zdialog *zd, cchar *event); int batch_sharp_func(PXM *pxm, int amount, int thresh); zdialog *zd; int zstat; char *infile, *outfile, tempfile[300]; char *inloc, *inname, *inext; char *outloc, *outname, *outext; char *tempname, seqplug[8], seqnum[8]; char plugyyyy[8], plugmm[4], plugdd[4]; char **oldfiles, **newfiles; char *pp, *pp1, *ppv[2]; int ii, jj, cc, err; int ww, hh, bpc, delinput; int Noldnew; char orientation; float scale, wscale, hscale; PXM *pxmin, *pxmout; STATB statdat; char fdate[16], pdate[16], size[16]; cchar *exifkey[1] = { exif_orientation_key }; cchar *exifdata[1]; GdkPixbuf *ovpxb; GError *gerror = 0; float imageR, screenR; int pww, phh, orgx, orgy, nc, rs; int px1, py1, px2, py2; uint8 *pix0, *pix1; float *pix2, f1, f2; F1_help_topic = "batch_convert"; if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** ______________________________________________________________ | Batch Convert | | | | [Select Files] N files selected | | | | New Name [________________________________________________] | | Sequence Numbers base [_____] adder [____] | | New Location [__________________________________] [browse] | | New File Type (o) JPG (o) PNG (o) TIF (o) no change | | Max. Width [____] Height [____] [x] no change | | [x] Delete Originals [x] Copy Metadata [x] Upright | | [x] Sharpen amount [___] threshold [___] | | | | [x] Overlay Image [open] Width % [ 23 ] | | Overlay Position [_] [_] [_] [_] [_] [_] [_] [_] [_] | | [x] make constant width for screen [_____] [_____] | | | | [proceed] [cancel] | |______________________________________________________________| ***/ zd = zdialog_new(ZTX("Batch Convert"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); zdialog_add_widget(zd,"button","files","hbf",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hbf",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbname","dialog"); zdialog_add_widget(zd,"label","labname","hbname",ZTX("New Name"),"space=5"); zdialog_add_widget(zd,"zentry","newname","hbname",0,"expand|size=30"); zdialog_add_widget(zd,"label","space","hbname",0,"space=60"); // push back oversized entry zdialog_add_widget(zd,"hbox","hbseq","dialog"); zdialog_add_widget(zd,"label","labseq","hbseq",ZTX("Sequence Numbers"),"space=5"); zdialog_add_widget(zd,"label","space","hbseq",0,"space=8"); zdialog_add_widget(zd,"label","labbase","hbseq",ZTX("base"),"space=5"); zdialog_add_widget(zd,"zentry","baseseq","hbseq",0,"size=5"); zdialog_add_widget(zd,"label","space","hbseq","","space=3"); zdialog_add_widget(zd,"label","labadder","hbseq",ZTX("adder"),"space=5"); zdialog_add_widget(zd,"zentry","addseq","hbseq",0,"size=3"); zdialog_add_widget(zd,"label","space","hbseq",0,"space=60"); // push back oversized entries zdialog_add_widget(zd,"hbox","hbloc","dialog"); zdialog_add_widget(zd,"label","labloc","hbloc",ZTX("New Location"),"space=5"); zdialog_add_widget(zd,"zentry","newloc","hbloc",0,"expand"); zdialog_add_widget(zd,"button","browse","hbloc",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbtyp","dialog"); zdialog_add_widget(zd,"label","labtyp","hbtyp",ZTX("New File Type"),"space=5"); zdialog_add_widget(zd,"radio","jpg","hbtyp","JPG","space=6"); zdialog_add_widget(zd,"radio","png","hbtyp","PNG","space=6"); zdialog_add_widget(zd,"radio","tif","hbtyp","TIF","space=6"); zdialog_add_widget(zd,"radio","sametype","hbtyp",ZTX("no change"),"space=6"); zdialog_add_widget(zd,"hbox","hbwh","dialog"); zdialog_add_widget(zd,"label","labw","hbwh",ZTX("max. Width"),"space=5"); zdialog_add_widget(zd,"zentry","maxww","hbwh","1000","size=5"); zdialog_add_widget(zd,"label","space","hbwh",0,"space=5"); zdialog_add_widget(zd,"label","labh","hbwh",Bheight,"space=5"); zdialog_add_widget(zd,"zentry","maxhh","hbwh","700","size=5"); zdialog_add_widget(zd,"check","samesize","hbwh",ZTX("no change"),"space=12"); zdialog_add_widget(zd,"label","space","hbwh",0,"space=30"); // push back oversized entries zdialog_add_widget(zd,"hbox","hbopts","dialog"); zdialog_add_widget(zd,"check","delete","hbopts",ZTX("Delete Originals"),"space=3"); zdialog_add_widget(zd,"check","copymeta","hbopts",ZTX("Copy Metadata"),"space=5"); zdialog_add_widget(zd,"check","upright","hbopts",ZTX("Upright"),"space=5"); zdialog_add_widget(zd,"hbox","hbsharp","dialog"); zdialog_add_widget(zd,"check","sharpen","hbsharp",ZTX("Sharpen"),"space=3"); zdialog_add_widget(zd,"label","space","hbsharp",0,"space=8"); zdialog_add_widget(zd,"label","labamount","hbsharp",Bamount,"space=3"); zdialog_add_widget(zd,"zspin","amount","hbsharp","0|400|1|100"); zdialog_add_widget(zd,"label","space","hbsharp",0,"space=8"); zdialog_add_widget(zd,"label","labthresh","hbsharp",Bthresh,"space=3"); zdialog_add_widget(zd,"zspin","thresh","hbsharp","0|100|1|20"); zdialog_add_widget(zd,"hsep","ovsep","dialog","","space=3"); zdialog_add_widget(zd,"hbox","hbov1","dialog"); // overlay image zdialog_add_widget(zd,"check","ckov","hbov1",ZTX("Overlay Image"),"space=3"); zdialog_add_widget(zd,"button","imgov","hbov1",Bopen,"space=3"); zdialog_add_widget(zd,"label","space","hbov1",0,"space=5"); zdialog_add_widget(zd,"label","labww","hbov1",ZTX("Width %"),"space=3"); zdialog_add_widget(zd,"zspin","ovwidth","hbov1","1.0|100.0|0.5|10.0","space=3"); zdialog_add_widget(zd,"label","space","hbov1",0,"space=5"); zdialog_add_widget(zd,"hbox","hbov2","dialog"); zdialog_add_widget(zd,"label","labpos","hbov2",ZTX("Position"),"space=3"); zdialog_add_widget(zd,"imagebutt","ovTL","hbov2","overlayTL.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovTC","hbov2","overlayTC.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovTR","hbov2","overlayTR.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovCL","hbov2","overlayCL.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovCC","hbov2","overlayCC.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovCR","hbov2","overlayCR.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovBL","hbov2","overlayBL.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovBC","hbov2","overlayBC.png","size=20|space=2"); zdialog_add_widget(zd,"imagebutt","ovBR","hbov2","overlayBR.png","size=20|space=2"); zdialog_add_widget(zd,"hbox","hbov3","dialog"); zdialog_add_widget(zd,"check","ovconst","hbov3",ZTX("Make constant size for screen:"),"space=3"); zdialog_add_widget(zd,"label","space","hbov3","","space=3"); zdialog_add_widget(zd,"label","ovlabww","hbov3",Bwidth,"space=3"); zdialog_add_widget(zd,"zentry","ovscrww","hbov3","","size=5|space=3"); zdialog_add_widget(zd,"label","space","hbov3","","space=3"); zdialog_add_widget(zd,"label","ovlabhh","hbov3",Bheight,"space=3"); zdialog_add_widget(zd,"zentry","ovscrhh","hbov3","","size=5|space=3"); zdialog_add_ttip(zd,"newname",ZTX("plugins: (year month day old-name sequence) $yyyy $mm $dd $oldname $s")); zdialog_stuff(zd,"sametype",1); // same file type zdialog_stuff(zd,"samesize",1); // same size zdialog_stuff(zd,"delete",0); // delete originals - no zdialog_stuff(zd,"copymeta",0); // copy metadata - no zdialog_stuff(zd,"upright",1); // upright rotation - yes zdialog_stuff(zd,"sharpen",0); // sharpen - no zdialog_stuff(zd,"amount",100); zdialog_stuff(zd,"thresh",20); zdialog_stuff(zd,"ovconst",0); // overlay constant size - no zdialog_stuff(zd,"ovscrww",zfuncs::monitor_ww); // screen pixel size zdialog_stuff(zd,"ovscrhh",zfuncs::monitor_hh); gallery_select_clear(); // clear selected file list *newloc = 0; oldfiles = newfiles = 0; Noldnew = 0; Foverlay = 0; ovpos = 0; Fovconst = 0; zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,batch_convert_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); if (zstat != 1) goto cleanup; // canceled if (! GScount) goto cleanup; free_resources(); // no curr. file if (Fdelete) { cc = GScount * sizeof(char *); // input files are to be deleted: oldfiles = (char **) zmalloc(cc); // reserve space to hold list of newfiles = (char **) zmalloc(cc); // old/new filespecs to update albums } popup_report_open(Mwin,"Processing files",600,300); // status monitor popup window for (ii = 0; ii < GScount; ii++) // loop selected files { zmainloop(); // keep GTK alive infile = GSfiles[ii]; // input file popup_report_write(0,"\n"); popup_report_write(0,"%s \n",infile); // log each input file parsefile(infile,&inloc,&inname,&inext); // parse directory, filename, .ext if (! inloc || ! inname || ! inext) continue; outloc = zstrdup(inloc); // initial output = input file outname = zstrdup(inname); outext = zstrdup(inext,4); cc = strlen(outloc) - 1; // remove trailing '/' if (outloc[cc] == '/') outloc[cc] = 0; if (*newname) { zfree(outname); outname = zstrdup(newname); // may contain $-plugins } if (Fplugname) // insert old file name { cc = strlen(outname) - 8 + strlen(inname); if (cc < 1) cc = 1; tempname = zstrdup(outname,cc); repl_1str(outname,tempname,"$oldname",inname); // ...$oldname... >> ...inname... zfree(outname); outname = tempname; } if (Fplugdate) // insert EXIF date { get_xxrec_min(infile,fdate,pdate,size); // get pdate, yyyymmddhhmmdd strncpy0(plugyyyy,pdate,5); // yyyy strncpy0(plugmm,pdate+4,3); // mm strncpy0(plugdd,pdate+6,3); // dd tempname = zstrdup(outname,8); repl_Nstrs(outname,tempname,"$yyyy",plugyyyy,"$mm",plugmm,"$dd",plugdd,null); zfree(outname); outname = tempname; } if (Fplugseq) // insert sequence number { pp = strcasestr(outname,"$s"); // find $s... in output name if (pp) for (cc = 1; pp[cc] == pp[1] && cc < 6; cc++); // length of "$s...s" 2-6 chars. strncpy0(seqplug,pp,cc+1); jj = baseseq + ii * addseq; // new sequence number snprintf(seqnum,8,"%0*d",cc-1,jj); // 1-5 chars. tempname = zstrdup(outname,8); repl_1str(outname,tempname,seqplug,seqnum); // ...$ssss... >> ...1234... zfree(outname); outname = tempname; } if (*newloc) { // new location was given zfree(outloc); outloc = zstrdup(newloc); } if (Fsametype) { if (strcasestr(".jpg .jpeg",inext)) strcpy(outext,".jpg"); // new .ext from existing .ext else if (strcasestr(".png",inext)) strcpy(outext,".png"); else if (strcasestr(".tif .tiff",inext)) strcpy(outext,".tif"); else strcpy(outext,".jpg"); // unknown >> .jpg } else strcpy(outext,newext); // new .ext was given cc = strlen(outloc) + strlen(outname) + strlen(outext) + 4; outfile = (char *) zmalloc(cc); snprintf(outfile,cc,"%s/%s%s",outloc,outname,outext); zfree(outloc); zfree(outname); zfree(outext); pxmin = PXM_load(infile,0); // read input file if (! pxmin) { popup_report_write(1,ZTX("file type not supported: %s \n"),inext); // 18.01 zfree(outfile); continue; } popup_report_write(0,"%s \n",outfile); // log each output file if (*newloc) { err = stat(outfile,&statdat); // check if file exists in new location if (! err) { popup_report_write(1,"%s \n",Bfileexists); zfree(outfile); continue; } } if (Fupright) // upright image if turned { exif_get(infile,exifkey,ppv,1); // get EXIF: Orientation if (ppv[0]) { orientation = *ppv[0]; // single character zfree(ppv[0]); } else orientation = '1'; // if missing assume unrotated pxmout = 0; if (orientation == '6') pxmout = PXM_rotate(pxmin,+90); // rotate clockwise 90 deg. if (orientation == '8') pxmout = PXM_rotate(pxmin,-90); // counterclockwise 90 deg. if (orientation == '3') pxmout = PXM_rotate(pxmin,180); // 180 deg. if (pxmout) { PXM_free(pxmin); // input image unrotated pxmin = pxmout; } } bpc = f_load_bpc; // input file bits/color ww = pxmin->ww; // input file size hh = pxmin->hh; if (Fsamesize) pxmout = pxmin; // same size, output = input else { wscale = hscale = 1.0; if (ww > maxww) wscale = 1.0 * maxww / ww; // compute new size if (hh > maxhh) hscale = 1.0 * maxhh / hh; if (wscale < hscale) scale = wscale; else scale = hscale; if (scale > 0.999) pxmout = pxmin; // no change else { ww = ww * scale; hh = hh * scale; pxmout = PXM_rescale(pxmin,ww,hh); // rescaled output file PXM_free(pxmin); // free memory } } if (Fsharpen) // auto sharpen output image batch_sharp_func(pxmout,amount,thresh); if (Foverlay) // add overlay image { pww = ww * 0.01 * ovwidth; // overlay width, % image width if (Fovconst) { // make overlay width constant for imageR = 1.0 * ww / hh; // images with variable ww/hh screenR = 1.0 * ovscrww / ovscrhh; if (imageR < screenR) pww = pww * screenR / imageR; // image < screen, increase width } ovpxb = gdk_pixbuf_new_from_file_at_scale(ovfile,pww,-1,1,&gerror); // read and scale overlay image if (! ovpxb) { popup_report_write(1,"overlay file error: %s \n",ovfile,gerror->message); zfree(outfile); PXM_free(pxmout); gerror = 0; continue; } pww = gdk_pixbuf_get_width(ovpxb); // get actual overlay pixbuf data phh = gdk_pixbuf_get_height(ovpxb); nc = gdk_pixbuf_get_n_channels(ovpxb); rs = gdk_pixbuf_get_rowstride(ovpxb); pix0 = gdk_pixbuf_get_pixels(ovpxb); orgx = orgy = -1; if (ovpos == 1) orgx = orgy = 0; // top left if (ovpos == 2) { orgx = (ww-pww)/2; orgy = 0; } // top center if (ovpos == 3) { orgx = ww-pww; orgy = 0; } // top right if (ovpos == 4) { orgx = 0; orgy = (hh-phh)/2; } // center left if (ovpos == 5) { orgx = (ww-pww)/2; orgy = (hh-phh)/2; } // center center if (ovpos == 6) { orgx = ww-pww; orgy = (hh-phh)/2; } // center right if (ovpos == 7) { orgx = 0; orgy = hh-phh; } // bottom left if (ovpos == 8) { orgx = (ww-pww)/2; orgy = hh-phh; } // bottom center if (ovpos == 9) { orgx = ww-pww; orgy = hh-phh; } // bottom right if (orgx < 0) orgx = 0; if (orgy < 0) orgy = 0; if (orgx + pww > ww) pww = ww - orgx; if (orgy + phh > hh) phh = hh - orgy; for (py1 = 0; py1 < phh; py1++) // loop all pixels in overlay image for (px1 = 0; px1 < pww; px1++) { pix1 = pix0 + py1 * rs + px1 * nc; // copy-from overlay pixel px2 = orgx + px1; // copy-to image pixel py2 = orgy + py1; pix2 = PXMpix(pxmout,px2,py2); if (nc > 3) f1 = pix1[3] / 256.0; // visible part else f1 = 1; f2 = 1.0 - f1; pix2[0] = f1 * pix1[0] + f2 * pix2[0]; // combine overlay and image pix2[1] = f1 * pix1[1] + f2 * pix2[1]; pix2[2] = f1 * pix1[2] + f2 * pix2[2]; } g_object_unref(ovpxb); // free overlay pixbuf ovpxb = 0; } pp1 = strrchr(outfile,'/'); // get filename.ext if (pp1) pp1++; else pp1 = outfile; snprintf(tempfile,300,"%s/%s",tempdir,pp1); // temp file for EXIF/IPTC copy pp1 = strrchr(outfile,'.'); if (strmatch(".tif",pp1)) // write output file to temp file err = PXM_TIFF_save(pxmout,tempfile,bpc); else if (strmatch(".png",pp1)) err = PXM_PNG_save(pxmout,tempfile,bpc); else err = PXM_ANY_save(pxmout,tempfile); if (err) { popup_report_write(1,"%s \n",ZTX("cannot create new file")); zfree(outfile); PXM_free(pxmout); continue; } if (Fcopymeta) // copy EXIF/IPTC if requested { if (Fupright) { // if image possibly uprighted exifdata[0] = ""; // remove exif:orientation exif_copy(infile,tempfile,exifkey,exifdata,1); // (set = 1 does not work) } else exif_copy(infile,tempfile,0,0,0); // else no EXIF change } err = copyFile(tempfile,outfile); // copy tempfile to output file 17.08 if (err) popup_report_write(1,"%s \n",strerror(err)); remove(tempfile); // remove tempfile delinput = 0; // figure out if input file can be deleted if (Fdelete) delinput = 1; // user says yes if (err) delinput = 0; // not if error if (strmatch(infile,outfile)) delinput = 0; // not if overwritten by output if (delinput) { // delete input file remove(infile); delete_image_index(infile); // remove from image index delete_thumbnail(infile); // delete thumbnail, file and cache } if (! err) { load_filemeta(outfile); // update image index for output file update_image_index(outfile); } if (delinput) { // if input file was deleted, oldfiles[Noldnew] = zstrdup(infile); // mark for updating albums newfiles[Noldnew] = zstrdup(outfile); Noldnew++; } zfree(outfile); PXM_free(pxmout); } if (Noldnew) { // update albums for renamed/moved files popup_report_write(0,"%s \n",ZTX("updating albums ...")); conv_albums(oldfiles,newfiles,Noldnew); } popup_report_write(0,"\n"); popup_report_write(0,"%s \n",Bcompleted); cleanup: if (Noldnew) { for (ii = 0; ii < Noldnew; ii++) { zfree(oldfiles[ii]); zfree(newfiles[ii]); } zfree(oldfiles); zfree(newfiles); Noldnew = 0; } Fblock = 0; gallery(navi::galleryname,"init",0); // refresh file list 18.01 gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // repaint from same position return; } // dialog event and completion callback function int batch_convert_dialog_event(zdialog *zd, cchar *event) { using namespace batch_convert; char *pp, badplug[20]; char countmess[80]; char *ploc, *ofile, *oname = 0; int ii, cc, yn, err; GdkPixbuf *ovpxb; GError *gerror = 0; if (strmatch(event,"files")) // select images to convert { gallery_select_clear(); // clear selected file list zdialog_show(zd,0); // hide parent dialog gallery_select(); // get list of files to convert zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"browse")) { zdialog_fetch(zd,"newloc",newloc,500); ploc = zgetfile(ZTX("Select directory"),MWIN,"folder",newloc); // new location browse if (! ploc) return 1; zdialog_stuff(zd,"newloc",ploc); zfree(ploc); } if (strstr("maxhh maxww",event)) // if max width/height changed, zdialog_stuff(zd,"samesize",0); // reset "no change" if (strmatch(event,"samesize")) { // if "no change" set, zdialog_fetch(zd,"samesize",Fsamesize); // clear max width / height if (Fsamesize) { zdialog_stuff(zd,"maxww",""); zdialog_stuff(zd,"maxhh",""); } } if (strmatch(event,"imgov")) { // [open] overlay image file if (ovfile) ofile = gallery_select1(ovfile); // 17.08 else ofile = gallery_select1(saved_areas_dirk); // 17.08 if (! ofile) return 1; if (image_file_type(ofile) != IMAGE) { // check it is an image file zmessageACK(Mwin,ZTX("unknown file type")); return 1; } if (ovfile) zfree(ovfile); ovfile = ofile; } if (strmatch(event,"ovTL")) ovpos = 1; // overlay position = top left if (strmatch(event,"ovTC")) ovpos = 2; // top center if (strmatch(event,"ovTR")) ovpos = 3; // top right if (strmatch(event,"ovCL")) ovpos = 4; // center left if (strmatch(event,"ovCC")) ovpos = 5; // center center if (strmatch(event,"ovCR")) ovpos = 6; // center right if (strmatch(event,"ovBL")) ovpos = 7; // bottom left if (strmatch(event,"ovBC")) ovpos = 8; // bottom center if (strmatch(event,"ovBR")) ovpos = 9; // bottom right if (zd->zstat != 1) return 1; // wait for [proceed] zd->zstat = 0; // dialog active until inputs OK zdialog_fetch(zd,"newname",newname,200); // new file name zdialog_fetch(zd,"baseseq",baseseq); // base sequence number zdialog_fetch(zd,"addseq",addseq); // sequence number adder zdialog_fetch(zd,"newloc",newloc,500); // new location (directory) zdialog_fetch(zd,"maxww",maxww); // new max width zdialog_fetch(zd,"maxhh",maxhh); // new max height zdialog_fetch(zd,"samesize",Fsamesize); // keep same width/height zdialog_fetch(zd,"sametype",Fsametype); // keep same file type zdialog_fetch(zd,"delete",Fdelete); // delete originals zdialog_fetch(zd,"copymeta",Fcopymeta); // copy metadata zdialog_fetch(zd,"upright",Fupright); // upright rotation zdialog_fetch(zd,"sharpen",Fsharpen); // auto sharpen zdialog_fetch(zd,"amount",amount); // sharpen amount zdialog_fetch(zd,"thresh",thresh); // sharpen threshold zdialog_fetch(zd,"ckov",Foverlay); // overlay image zdialog_fetch(zd,"ovwidth",ovwidth); // overlay width 1-100% zdialog_fetch(zd,"ovconst",Fovconst); // flag, make overlay size constant zdialog_fetch(zd,"ovscrww",ovscrww); // for this screen width zdialog_fetch(zd,"ovscrhh",ovscrhh); // and this screen height if (! GScount) { zmessageACK(Mwin,Bnofileselected); return 1; } strTrim2(newname); if (! blank_null(newname)) { Fplugdate = Fplugseq = Fplugname = 0; // validate plugins pp = newname; while ((pp = strchr(pp,'$'))) { if (strmatchN(pp,"$yyyy",5)) Fplugdate = 1; // $yyyy else if (strmatchN(pp,"$mm",3)) Fplugdate = 1; // $mm else if (strmatchN(pp,"$dd",3)) Fplugdate = 1; // $dd else if (strmatchN(pp,"$s",2)) Fplugseq = 1; // $s...s (seqeunce number cc) else if (strmatchN(pp,"$oldname",8)) Fplugname = 1; // $oldname else { for (cc = 0; pp[cc] > ' ' && cc < 20; cc++); strncpy0(badplug,pp,cc); zmessageACK(Mwin,ZTX("invalid plugin: %s"),badplug); return 1; } pp++; } if (! Fplugname && ! Fplugseq) { zmessageACK(Mwin,ZTX("you must use either $s or $oldname")); return 1; } if (Fplugseq && (baseseq < 1 || addseq < 1)) { zmessageACK(Mwin,ZTX("$s plugin needs base and adder")); return 1; } if (! Fplugseq && (baseseq > 0 || addseq > 0)) { zmessageACK(Mwin,ZTX("base and adder need $s plugin")); return 1; } } strTrim2(newloc); // check location if (! blank_null(newloc)) { cc = strlen(newloc) - 1; if (newloc[cc] == '/') newloc[cc] = 0; // remove trailing '/' err = check_create_dir(newloc); // create if needed if (err) return 1; } if (! Fsametype) { // file type change strcpy(newext,".jpg"); // get new .ext zdialog_fetch(zd,"png",ii); if (ii) strcpy(newext,".png"); zdialog_fetch(zd,"tif",ii); if (ii) strcpy(newext,".tif"); } if (! Fsamesize && (maxww < 20 || maxhh < 20)) { zmessageACK(Mwin,ZTX("max. size %d x %d is not reasonable"),maxww,maxhh); return 1; } if (Foverlay) { if (! ovfile) { // overlay image file zmessageACK(Mwin,ZTX("specify overlay image file")); return 1; } ovpxb = gdk_pixbuf_new_from_file(ovfile,&gerror); // verify image file if (! ovpxb) { zmessageACK(Mwin,gerror->message); return 1; } g_object_unref(ovpxb); oname = strrchr(ovfile,'/'); // filename.png if (! oname) oname = ovfile; oname++; if (! ovpos) { // overlay position zmessageACK(Mwin,ZTX("specify overlay position")); return 1; } } Freplace = 0; if (*newname <= ' ' && *newloc <= ' ' && Fsametype) Freplace = 1; /** Convert NN image files 0 Rename to xxxxxxx 1 Convert to .ext 2 Resize within NNxNN 3 Output to /.../... 4 Copy Metadata Upright Sharpen 5 Overlay Image: xxxxxxxxxx 6 *** Delete Originals *** 7 *** Replace Originals *** 8 PROCEED? 9 **/ char mess0[60], mess1[100], mess2[60], mess3[60], mess4[550], mess5[80], mess6[200], mess7[60], mess8[60], mess9[40]; char warnmess[1000]; *mess0 = *mess1 = *mess2 = *mess3 = *mess4 = *mess5 = *mess6 = *mess7 = *mess8 = *mess9 = 0; snprintf(mess0,60,ZTX("Convert %d image files"),GScount); if (*newname) snprintf(mess1,100,"\n %s %s",ZTX("Rename to"),newname); if (! Fsametype) snprintf(mess2,60,"\n %s %s",ZTX("Convert to"),newext); if (! Fsamesize) snprintf(mess3,60,"\n %s %dx%d",ZTX("Resize within"),maxww,maxhh); if (*newloc) snprintf(mess4,550,"\n %s %s",ZTX("Output to"),newloc); if (Fcopymeta || Fupright || Fsharpen) strcat(mess5,"\n "); if (Fcopymeta) { strcat(mess5,ZTX("Copy Metadata")); strcat(mess5," "); } if (Fupright) { strcat(mess5,ZTX("Upright")); strcat(mess5," "); } if (Fsharpen) { strcat(mess5,ZTX("Sharpen")); strcat(mess5," "); } if (Foverlay) { snprintf(mess6,200,"\n %s: %s",ZTX("Overlay Image"),oname); } if (Fdelete) snprintf(mess7,60,"\n %s",ZTX("*** Delete Originals ***")); if (Freplace) snprintf(mess8,60,"\n %s",ZTX("*** Replace Originals ***")); snprintf(mess9,40,"\n\n%s",ZTX("PROCEED?")); snprintf(warnmess,1000,"%s %s %s %s %s %s %s %s %s %s", mess0,mess1,mess2,mess3,mess4,mess5,mess6,mess7,mess8,mess9); yn = zmessageYN(Mwin,warnmess); if (! yn) return 1; zd->zstat = 1; // [proceed] return 1; } /********************************************************************************/ // callable sharpen function for batch_convert and batch_RAW_convert // amount: 0 to 400 strength of applied algorithm // thresh: 0 to 100 contrast level below which sharpen is diminished namespace batch_sharp_names { PXM *pxm1, *pxm2; int BSamount, BSthresh; } int batch_sharp_func(PXM *pxm, int amount, int thresh) // 14.06 { using namespace batch_sharp_names; void * batch_sharp_wthread(void *arg); pxm2 = pxm; // output pxm1 = PXM_copy(pxm2); // input BSamount = amount; BSthresh = thresh; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(batch_sharp_wthread,&Nval[ii]); wait_wthreads(); // wait for completion PXM_free(pxm1); return 1; } void * batch_sharp_wthread(void *arg) // worker thread function { using namespace batch_sharp_names; float *pix1, *pix2; int px, py; float amount, thresh; float b1, b1x, b1y, b2x, b2y, b2, bf, f1, f2; float red1, green1, blue1, red2, green2, blue2; int index = *((int *) arg); amount = 1 + 0.01 * BSamount; // 1.0 - 5.0 thresh = BSthresh; // 0 - 100 for (py = index + 1; py < pxm1->hh; py += NWT) // loop all image pixels for (px = 1; px < pxm1->ww; px++) { pix1 = PXMpix(pxm1,px,py); // input pixel pix2 = PXMpix(pxm2,px,py); // output pixel b1 = pixbright(pix1); // pixel brightness, 0 - 256 if (b1 == 0) continue; // black, don't change b1x = b1 - pixbright(pix1-3); // horiz. brightness gradient b1y = b1 - pixbright(pix1-3 * pxm1->ww); // vertical f1 = fabsf(b1x + b1y); if (f1 < thresh) // moderate brightness change for f1 = f1 / thresh; // pixels below threshold gradient else f1 = 1.0; f2 = 1.0 - f1; b1x = b1x * amount; // amplified gradient b1y = b1y * amount; b2x = pixbright(pix1-3) + b1x; // + prior pixel brightness b2y = pixbright(pix1-3 * pxm2->ww) + b1y; // = new brightness b2 = 0.5 * (b2x + b2y); b2 = f1 * b2 + f2 * b1; // possibly moderated bf = b2 / b1; // ratio of brightness change if (bf < 0) bf = 0; if (bf > 4) bf = 4; red1 = pix1[0]; // input RGB green1 = pix1[1]; blue1 = pix1[2]; red2 = bf * red1; // output RGB if (red2 > 255.9) red2 = 255.9; green2 = bf * green1; if (green2 > 255.9) green2 = 255.9; blue2 = bf * blue1; if (blue2 > 255.9) blue2 = 255.9; pix2[0] = red2; pix2[1] = green2; pix2[2] = blue2; } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Batch upright image files. // Look for files rotated 90˚ (according to EXIF) and upright them. char **bup_filelist = 0; int bup_filecount = 0; int bup_allfiles; void m_batch_upright(GtkWidget *, cchar *) { int batch_upright_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int zstat; char *infile, *tempfile; char *pp1, *pp2, *ppv[1]; int ii, err, bpc; char orientation; PXM *pxmin, *pxmout; cchar *exifkey[1] = { exif_orientation_key }; cchar *exifdata[1]; F1_help_topic = "batch_upright"; if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** ___________________________________ | Batch Upright | | | | [Select Files] N files selected | | [x] Survey all files | | | | [proceed] [cancel] | |___________________________________| ***/ zd = zdialog_new(ZTX("Batch Upright"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); zdialog_add_widget(zd,"button","files","hbf",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hbf",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbaf","dialog"); zdialog_add_widget(zd,"check","allfiles","hbaf",ZTX("Survey all files"),"space=5"); bup_filelist = 0; bup_filecount = 0; zdialog_run(zd,batch_upright_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); if (zstat != 1) goto cleanup; // canceled if (! bup_allfiles && ! bup_filecount) goto cleanup; // nothing selected free_resources(); // no curr. file popup_report_open(Mwin,"Processing files",500,200); // status monitor popup window if (bup_allfiles) // "survey all files" selected { bup_filelist = (char **) zmalloc(Nxxrec * sizeof(char *)); for (ii = 0; ii < Nxxrec; ii++) bup_filelist[ii] = zstrdup(xxrec_tab[ii]->file); bup_filecount = Nxxrec; } for (ii = 0; ii < bup_filecount; ii++) // loop selected files { zmainloop(); // keep GTK alive infile = bup_filelist[ii]; // input file popup_report_write(0,"%s \n",infile); // log each output file exif_get(infile,exifkey,ppv,1); // get EXIF: Orientation if (! ppv[0]) continue; orientation = *ppv[0]; if (orientation == '1') continue; // not rotated pxmin = PXM_load(infile,0); // read input file if (! pxmin) { popup_report_write(1,"%s \n",ZTX("file cannot be read")); continue; } pxmout = 0; if (orientation == '6') pxmout = PXM_rotate(pxmin,+90); // rotate clockwise 90 deg. if (orientation == '8') pxmout = PXM_rotate(pxmin,-90); // counterclockwise if (orientation == '3') pxmout = PXM_rotate(pxmin,180); // 180 deg. PXM_free(pxmin); if (! pxmout) continue; // not rotated tempfile = zstrdup(infile,12); // temp file needed for EXIF/IPTC copy pp1 = strrchr(infile,'.'); pp2 = strrchr(tempfile,'.'); strcpy(pp2,"-temp"); strcpy(pp2+5,pp1); bpc = f_load_bpc; // input file bits/color if (strmatch(f_load_type,"tif")) err = PXM_TIFF_save(pxmout,tempfile,bpc); else if (strmatch(f_load_type,"png")) err = PXM_PNG_save(pxmout,tempfile,bpc); else err = PXM_ANY_save(pxmout,tempfile); PXM_free(pxmout); if (err) { popup_report_write(1," upright failed \n"); zfree(tempfile); continue; } exifdata[0] = ""; // remove exif:orientation exif_copy(infile,tempfile,exifkey,exifdata,1); // (set = 1 does not work) err = copyFile(tempfile,infile); // copy temp file to input file 17.08 if (err) popup_report_write(1,"%s \n",strerror(err)); remove(tempfile); // remove tempfile zfree(tempfile); if (! err) { load_filemeta(infile); // update image index update_image_index(infile); popup_report_write(0," uprighted \n"); } } popup_report_write(0,"%s \n",Bcompleted); cleanup: if (bup_filecount) { // free memory for (ii = 0; ii < bup_filecount; ii++) zfree(bup_filelist[ii]); zfree(bup_filelist); bup_filelist = 0; bup_filecount = 0; } Fblock = 0; return; } // dialog event and completion callback function int batch_upright_dialog_event(zdialog *zd, cchar *event) { char countmess[80]; int ii; if (strmatch(event,"files")) // select images to convert { if (bup_filecount) { for (ii = 0; ii < bup_filecount; ii++) // free prior list zfree(bup_filelist[ii]); zfree(bup_filelist); bup_filelist = 0; bup_filecount = 0; } gallery_select_clear(); // clear selected file list zdialog_show(zd,0); // hide parent dialog gallery_select(); // get new list zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); zdialog_stuff(zd,"allfiles",0); if (! GScount) return 1; bup_filelist = (char **) zmalloc(GScount * sizeof(char *)); // copy selected files for (ii = 0; ii < GScount; ii++) bup_filelist[ii] = GSfiles[ii]; bup_filecount = GScount; GScount = 0; } if (zd->zstat != 1) return 1; // wait for [proceed] zdialog_fetch(zd,"allfiles",bup_allfiles); // get "survey all" option if (! bup_allfiles && ! bup_filecount) { // nothing selected zmessageACK(Mwin,Bnofileselected); zd->zstat = 0; // keep dialog active } if (bup_allfiles && bup_filecount) { zmessageACK(Mwin,ZTX("cannot select both options")); zd->zstat = 0; } return 1; } /********************************************************************************/ // Batch delete or trash image files. int bdt_option; // 1/2 = delete/trash void m_batch_deltrash(GtkWidget *, cchar *) { int batch_deltrash_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int zstat, ii, err, gstat; char *file; GError *gerror = 0; GFile *gfile = 0; STATB statb; F1_help_topic = "batch_delete_trash"; if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** ___________________________________ | Batch Delete/Trash | | | | [Select Files] N files selected | | (o) delete (o) trash | | | | [proceed] [cancel] | |___________________________________| ***/ zd = zdialog_new(ZTX("Batch Delete/Trash"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); zdialog_add_widget(zd,"button","files","hbf",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hbf",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbdt","dialog"); zdialog_add_widget(zd,"label","labdel","hbdt",ZTX("delete"),"space=5"); zdialog_add_widget(zd,"radio","delete","hbdt",0); zdialog_add_widget(zd,"label","space","hbdt",0,"space=10"); zdialog_add_widget(zd,"label","labtrash","hbdt",ZTX("trash"),"space=5"); zdialog_add_widget(zd,"radio","trash","hbdt",0); gallery_select_clear(); // clear selected file list bdt_option = 2; zdialog_stuff(zd,"delete",0); zdialog_stuff(zd,"trash",1); zdialog_run(zd,batch_deltrash_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_fetch(zd,"delete",bdt_option); // get delete/trash option if (! bdt_option) bdt_option = 2; zdialog_free(zd); if (zstat != 1) goto cleanup; // canceled if (! GScount) goto cleanup; free_resources(); // no curr. file popup_report_open(Mwin,"Processing files",500,200); // status monitor popup window for (ii = 0; ii < GScount; ii++) // loop selected files { zmainloop(); // keep GTK alive file = GSfiles[ii]; // log each file popup_report_write(0,"%s \n",file); err = stat(file,&statb); // file exists? if (err || ! S_ISREG(statb.st_mode)) { popup_report_write(1,"file not found \n"); continue; } if (bdt_option == 1) { // delete file err = remove(file); if (err) { popup_report_write(1,"%s \n",strerror(err)); continue; } } if (bdt_option == 2) { // move file to trash gfile = g_file_new_for_path(file); gstat = g_file_trash(gfile,0,&gerror); // move file to trash g_object_unref(gfile); if (! gstat) { popup_report_write(1,"%s \n",gerror->message); continue; } } delete_image_index(file); // delete file in image index delete_thumbnail(file); // delete thumbnail, file and cache } popup_report_write(0,"%s \n",Bcompleted); cleanup: Fblock = 0; gallery(navi::galleryname,"init",0); // refresh file list 18.01 gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // repaint from same position return; } // dialog event and completion callback function int batch_deltrash_dialog_event(zdialog *zd, cchar *event) { char countmess[80]; int ii; if (strmatch(event,"files")) // select images to convert { gallery_select_clear(); // clear selected file list zdialog_show(zd,0); // hide parent dialog gallery_select(); zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"delete")) { // delete radio button zdialog_fetch(zd,"delete",ii); if (ii) bdt_option = 1; zdialog_stuff(zd,"trash",0); } if (strmatch(event,"trash")) { // trash radio button zdialog_fetch(zd,"trash",ii); if (ii) bdt_option = 2; zdialog_stuff(zd,"delete",0); } if (zd->zstat != 1) return 1; // wait for [proceed] if (! GScount) { // nothing selected zmessageACK(Mwin,Bnofileselected); zd->zstat = 0; // keep dialog active } return 1; } /********************************************************************************/ // convert multiple RAW files to tiff, jpeg, or png using libraw or Raw Therapee namespace batch_raw { int Fuselibraw, Fuserawtherapee; char location[400]; cchar *filetype; int bpc; float resize; int Fsharpen; int amount, thresh; }; void m_batch_raw(GtkWidget *, cchar *menu) // consolidate two batch raw funcs 18.01 { using namespace batch_raw; int batch_raw_dialog_event(zdialog *zd, cchar *event); zdialog *zd; cchar *title = ZTX("Batch Convert RAW Files (Raw Therapee)"); char *tiffile, *infile, *outfile, *pp; int zstat, ii, err; FTYPE ftype; int cc, ww2, hh2; PXM *pxm1, *pxm2; F1_help_topic = "batch_raw_therapee"; if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** _____________________________________________________________________________ | [x] [-] [_] Batch Convert RAW Files | | | | (o) use libraw (o) use Raw Therapee | 18.01 | [Select Files] N image files selected | | output location [________________________________________________] [Browse] | | output file type (o) JPEG (o) PNG-8 (o) PNG-16 (o) TIFF-8 (o) TIFF-16 | | resize (o) 1.0 (o) 3/4 (o) 2/3 (o) 1/2 (o) 1/3 | | [x] Sharpen amount [___] threshold [___] | | [proceed] [cancel] | |_____________________________________________________________________________| ***/ zd = zdialog_new(title,Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbuse","dialog",0,"space=2"); zdialog_add_widget(zd,"radio","uselibraw","hbuse",ZTX("use libraw"),"space=5"); zdialog_add_widget(zd,"radio","userawtherapee","hbuse",ZTX("use Raw Therapee"),"space=5"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=2"); zdialog_add_widget(zd,"button","files","hb1",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hb1",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbout","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labout","hbout",ZTX("output location"),"space=5"); zdialog_add_widget(zd,"zentry","location","hbout",0,"space=5|expand"); zdialog_add_widget(zd,"button","browse","hbout",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labtype","hb2",ZTX("output file type"),"space=5"); zdialog_add_widget(zd,"radio","JPEG","hb2","JPEG","space=5"); zdialog_add_widget(zd,"radio","PNG-8","hb2","PNG-8","space=5"); zdialog_add_widget(zd,"radio","PNG-16","hb2","PNG-16","space=5"); zdialog_add_widget(zd,"radio","TIFF-8","hb2","TIFF-8","space=5"); zdialog_add_widget(zd,"radio","TIFF-16","hb2","TIFF-16","space=5"); zdialog_add_widget(zd,"hbox","hbsize","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labsize","hbsize",ZTX("resize"),"space=5"); zdialog_add_widget(zd,"label","space","hbsize",0,"space=5"); zdialog_add_widget(zd,"radio","1.0","hbsize","1.0","space=5"); zdialog_add_widget(zd,"radio","3/4","hbsize","3/4","space=5"); zdialog_add_widget(zd,"radio","2/3","hbsize","2/3","space=5"); zdialog_add_widget(zd,"radio","1/2","hbsize","1/2","space=5"); zdialog_add_widget(zd,"radio","1/3","hbsize","1/3","space=5"); zdialog_add_widget(zd,"hbox","hbsharp","dialog",0,"space=2"); zdialog_add_widget(zd,"check","sharpen","hbsharp",ZTX("Sharpen"),"space=3"); zdialog_add_widget(zd,"label","space","hbsharp",0,"space=8"); zdialog_add_widget(zd,"label","labamount","hbsharp",ZTX("amount"),"space=3"); zdialog_add_widget(zd,"zspin","amount","hbsharp","0|400|1|100"); zdialog_add_widget(zd,"label","space","hbsharp",0,"space=8"); zdialog_add_widget(zd,"label","labthresh","hbsharp",ZTX("threshold"),"space=3"); zdialog_add_widget(zd,"zspin","thresh","hbsharp","0|100|1|20"); gallery_select_clear(); // clear selected file list *location = 0; zdialog_stuff(zd,"JPEG",1); // compensate GTK bug zdialog_stuff(zd,"uselibraw",1); zdialog_restore_inputs(zd); // get prior inputs if any zdialog_run(zd,batch_raw_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); if (zstat != 1) goto cleanup; if (! GScount) goto cleanup; if (Fuserawtherapee && ! Frawtherapee) { // 18.01 zmessageACK(Mwin,ZTX("Raw Therapee not installed")); goto cleanup; } m_viewmode(0,"F"); popup_report_open(Mwin,"Converting RAW files",500,200); // status monitor popup window rawfile = 0; for (ii = 0; ii < GScount; ii++) // loop all RAW files { zmainloop(); // keep GTK alive rawfile = GSfiles[ii]; // filename.raw popup_report_write(0,"%s \n",rawfile); // write to log window ftype = image_file_type(rawfile); if (ftype != RAW) { popup_report_write(1," unknown RAW file type \n"); continue; } if (Fuselibraw) // 18.01 { pxm1 = RAW_PXM_load(rawfile); // make PXM image from RAW file if (! pxm1) { popup_report_write(1," raw conversion failed \n"); // conversion failed continue; } } if (Fuserawtherapee) // 18.01 { err = shell_ack("rawtherapee-cli -t -b16 -Y -c \"%s\"",rawfile); // convert to rawfilename.tif if (err) { popup_report_write(1," conversion failed: %s \n",strerror(err)); continue; } tiffile = raw_to_tiff(rawfile); // rawfilename.tif pxm1 = PXM_load(tiffile,0); // load tiff-16 output file if (! pxm1) { strcat(tiffile,"f"); // look for both .tif and .tiff pxm1 = PXM_load(tiffile,0); // load tiff-16 output file } if (! pxm1) { // make PXM image from tiff file popup_report_write(1," no Raw Therapee .tif output \n"); zfree(tiffile); continue; } remove(tiffile); // delete tiff file on disk zfree(tiffile); } if (resize < 1.0) // resize down if wanted { ww2 = resize * pxm1->ww; hh2 = resize * pxm1->hh; pxm2 = PXM_rescale(pxm1,ww2,hh2); PXM_free(pxm1); pxm1 = pxm2; if (! pxm1) { popup_report_write(1," resize failed \n"); continue; } } if (Fsharpen) // sharpen if wanted batch_sharp_func(pxm1,amount,thresh); outfile = zstrdup(rawfile,5); // output file name = RAW file name pp = strrchr(outfile,'.'); // rename: *.tif *.jpg *.png if (pp) strcpy(pp,filetype); if (strmatch(filetype,".tif")) // output .tif file, 8/16 bits err = PXM_TIFF_save(pxm1,outfile,bpc); else if (strmatch(filetype,".png")) // output .png file, 8/16 bits err = PXM_PNG_save(pxm1,outfile,bpc); else err = PXM_ANY_save(pxm1,outfile); // output .jpg file, 8 bits PXM_free(pxm1); if (err) { popup_report_write(1," file type conversion failed \n"); zfree(outfile); continue; } exif_copy(rawfile,outfile,0,0,0); // copy metadata from RAW file if (*location && ! samedirk(location,outfile)) { infile = zstrdup(outfile); // copy to new location zfree(outfile); pp = strrchr(infile,'/'); // /raw-location/filename.ext cc = strlen(pp); // | outfile = zstrdup(location,cc+1); // pp strcat(outfile,pp); // /new-location/filename.ext err = copyFile(infile,outfile); // copy to new location if (err) popup_report_write(1," %s \n",strerror(err)); remove(infile); // remove tempfile zfree(infile); } f_open(outfile,0,0,0); // open converted file update_image_index(outfile); popup_report_write(0,"%s \n",outfile); // write output file to log window zfree(outfile); } popup_report_write(0,"%s \n",Bcompleted); cleanup: // clean up and return rawfile = 0; Fblock = 0; gallery(navi::galleryname,"init",0); // refresh file list 18.01 gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // repaint from same position return; } // dialog event and completion callback function int batch_raw_dialog_event(zdialog *zd, cchar *event) { using namespace batch_raw; int ii, err, cc; char countmess[80], *ploc; if (strmatch(event,"files")) // select images to convert { gallery_select_clear(); // clear selected file list zdialog_show(zd,0); // hide parent dialog gallery_select(); // get list of files to convert zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); // stuff count into dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"browse")) { zdialog_fetch(zd,"location",location,400); if (*location <= ' ' && topdirks[0]) strncpy0(location,topdirks[0],400); ploc = zgetfile(ZTX("Select directory"),MWIN,"folder",location); // new location browse if (! ploc) return 1; zdialog_stuff(zd,"location",ploc); zfree(ploc); } if (zd->zstat != 1) return 0; // wait for [proceed] zd->zstat = 0; // keep dialog active until inputs OK zdialog_fetch(zd,"uselibraw",Fuselibraw); // 18.01 zdialog_fetch(zd,"userawtherapee",Fuserawtherapee); if (! GScount) { // no RAW files selected zmessageACK(Mwin,Bnofileselected); return 1; } zdialog_fetch(zd,"location",location,400); // output location strTrim2(location); if (! blank_null(location)) { cc = strlen(location) - 1; if (location[cc] == '/') location[cc] = 0; // remove trailing '/' err = check_create_dir(location); // create if needed if (err) return 1; } zdialog_fetch(zd,"JPEG",ii); // file type, color depth if (ii) { filetype = ".jpg"; bpc = 8; } zdialog_fetch(zd,"PNG-8",ii); if (ii) { filetype = ".png"; bpc = 8; } zdialog_fetch(zd,"PNG-16",ii); if (ii) { filetype = ".png"; bpc = 16; } zdialog_fetch(zd,"TIFF-8",ii); if (ii) { filetype = ".tif"; bpc = 8; } zdialog_fetch(zd,"TIFF-16",ii); if (ii) { filetype = ".tif"; bpc = 16; } // use .tif for both 8/16 bpc resize = 1.0; zdialog_fetch(zd,"1.0",ii); // resize option if (ii) resize = 1.0; zdialog_fetch(zd,"3/4",ii); if (ii) resize = 0.75; zdialog_fetch(zd,"2/3",ii); if (ii) resize = 0.666667; zdialog_fetch(zd,"1/2",ii); if (ii) resize = 0.50; zdialog_fetch(zd,"1/3",ii); if (ii) resize = 0.333333; zdialog_fetch(zd,"sharpen",Fsharpen); // sharpen option zdialog_fetch(zd,"amount",amount); zdialog_fetch(zd,"thresh",thresh); zd->zstat = 1; // dialog complete return 1; } /********************************************************************************/ // script files: // build a script file with one or more predefined edit functions // execute the script file for selected image files namespace script { int dialog_event(zdialog *zd, cchar *event); int script_start(); int script_close(); int script_select(); int script_run(); zdialog *zd; char *scriptfile = 0; // script filespec char scriptname[100]; // script file name FILE *fid = 0; // script file FID char **imagefiles = 0; // selected image files int Nfiles = 0; // image file count } // menu function void m_scriptfiles(GtkWidget *, cchar *menu) { using namespace script; char countmess[80]; F1_help_topic = "script_files"; /*** _________________________________________ | Script Files | | | | script file [_______________________] | | | | [start] begin making a script file | | [close] finish making a script file | | [select] NN image files selected | | [run] execute a script file | | | | [cancel] | |_________________________________________| ***/ zd = zdialog_new("Script Files",Mwin,Bcancel,null); zdialog_add_widget(zd,"hbox","hbsf","dialog"); zdialog_add_widget(zd,"label","labsf","hbsf",ZTX("script file"),"space=5"); zdialog_add_widget(zd,"zentry","scriptname","hbsf",0,"space=5|expand"); zdialog_add_widget(zd,"hbox","hbv","dialog"); zdialog_add_widget(zd,"vbox","vb1","hbv",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vb2","hbv",0,"homog|space=5"); zdialog_add_widget(zd,"button","start","vb1",Bstart); zdialog_add_widget(zd,"button","close","vb1",Bclose); zdialog_add_widget(zd,"button","select","vb1",Bselect); zdialog_add_widget(zd,"button","run","vb1",Brun); zdialog_add_widget(zd,"hbox","hbs","vb2"); zdialog_add_widget(zd,"label","labstart","hbs",ZTX("begin making a script file")); zdialog_add_widget(zd,"hbox","hbc","vb2"); zdialog_add_widget(zd,"label","labclose","hbc",ZTX("finish making a script file")); zdialog_add_widget(zd,"hbox","hbf","vb2"); zdialog_add_widget(zd,"label","fcount","hbf",Bnofileselected); zdialog_add_widget(zd,"hbox","hbr","vb2"); zdialog_add_widget(zd,"label","labrun","hbr",ZTX("execute a script file")); if (scriptfile) zdialog_stuff(zd,"scriptname",scriptname); if (Nfiles) { snprintf(countmess,80,Bfileselected,Nfiles); zdialog_stuff(zd,"fcount",countmess); } zdialog_run(zd,dialog_event,"parent"); return; } // dialog event and completion function int script::dialog_event(zdialog *zd, cchar *event) { using namespace script; F1_help_topic = "script_files"; if (zd->zstat) { // cancel zdialog_free(zd); if (fid) fclose(fid); fid = 0; Fscriptbuild = 0; return 1; } if (strmatch(event,"start")) { script_start(); } if (strmatch(event,"close")) script_close(); if (strmatch(event,"select")) script_select(); if (strmatch(event,"run")) script_run(); return 1; } // start building a script file // if Fscriptbuild is active, edit_done() saves edit settings in script file int script::script_start() { using namespace script; char *file, *pp; if (checkpend("all")) return 0; if (Fscriptbuild) { zmessageACK(Mwin,ZTX("script already started")); return 0; } file = zgetfile(ZTX("start a new script file"),MWIN,"save",edit_scripts_dirk,1); if (! file) return 0; fid = fopen(file,"w"); // open script file if (! fid) { zmessageACK(Mwin,strerror(errno)); return 0; } pp = strrchr(file,'/'); // script file name if (! pp || strlen(pp) > 99) { zmessageACK(Mwin,"script file name too big"); return 0; } if (scriptfile) zfree(scriptfile); scriptfile = file; strncpy0(scriptname,pp+1,100); fprintf(fid,"script file: %s\n",scriptname); // write header record Fscriptbuild = 1; zdialog_stuff(zd,"scriptname",scriptname); // put script name into dialog zmessageACK(Mwin,ZTX("perform edits to be included in the script file")); return 1; } // this function is called from edit_done() when Fscriptbuild is active int addscript(editfunc *CEF) { using namespace script; int err; fprintf(fid,"menu: %s\n",CEF->menuname); // write "menu: menu name" if (CEF->zd) { err = edit_save_widgets(CEF,fid); // write widget settings if (err) { zmessageACK(Mwin,ZTX("script file error")); return 0; } } zmessageACK(Mwin,ZTX("%s added to script"),CEF->funcname); return 0; } // complete a script file under construction // and save the script file with assigned name int script::script_close() { using namespace script; if (! Fscriptbuild) { zmessageACK(Mwin,ZTX("no script file was started")); return 0; } fprintf(fid,"menu: end"); fclose(fid); fid = 0; Fscriptbuild = 0; zmessageACK(Mwin,ZTX("script file closed")); return 1; } // select image files to process with a script file int script::script_select() { using namespace script; int ii; char countmess[80]; gallery_select(); // get new file list if (! GScount) return 0; if (Nfiles) { // free prior list for (ii = 0; ii < Nfiles; ii++) zfree(imagefiles[ii]); zfree(imagefiles); } Nfiles = GScount; // get selected files imagefiles = (char **) zmalloc(Nfiles * sizeof(char *)); for (ii = 0; ii < Nfiles; ii++) imagefiles[ii] = zstrdup(GSfiles[ii]); if (Nfiles) { snprintf(countmess,80,Bfileselected,Nfiles); // update dialog file count zdialog_stuff(zd,"fcount",countmess); } else zdialog_stuff(zd,"fcount",Bnofileselected); return Nfiles; } // execute a script file // returns 0 if OK, +N if error int script::script_run() { using namespace script; char *imagefile, *file, *pp, *pp2; char buff[100], menuname[40]; char *newfilevers; int ii, jj, err; int retstat = 1; // failure if (Fscriptbuild) script_close(); // close automatically if (! Nfiles) { zmessageACK(Mwin,Bnofileselected); return 1; } file = zgetfile(ZTX("open script file"),MWIN,"file",edit_scripts_dirk,1); if (! file) return 1; pp = strrchr(file,'/'); // get file name if (! pp || strlen(pp) > 99) goto badfile; fid = fopen(file,"r"); // open script file if (! fid) { zmessageACK(Mwin,ZTX("script file: %s \n %s"),file,strerror(errno)); goto badfile; } pp2 = fgets_trim(buff,100,fid,1); // read "script file: scriptname" if (! pp2) goto badfile; if (! strmatchN("script file: ",pp2,13)) goto badfile; if (! strmatch(pp2+13,pp+1)) goto badfile; fclose(fid); if (scriptfile) zfree(scriptfile); // save script file scriptfile = file; strncpy0(scriptname,pp+1,100); // save script name zdialog_stuff(zd,"scriptname",scriptname); // put script name into dialog for (ii = 0; ii < Nfiles; ii++) // loop image files to process { imagefile = GSfiles[ii]; err = f_open(imagefile,0,0,1,0); if (err) { zmessageACK(Mwin,ZTX("open failure: %s \n %s"),imagefile,strerror(errno)); goto cleanup; } fid = fopen(scriptfile,"r"); // open script file if (! fid) goto cleanup; pp = fgets_trim(buff,100,fid,1); // discard "script file: scriptname" if (! pp) goto badfile; while (true) // process script file { pp = fgets_trim(buff,100,fid,1); // read "menu: menuname" if (! pp) goto badfile; if (! strmatchN(pp,"menu: ",6)) goto badfile; strncpy0(menuname,pp+6,40); if (strmatch(menuname,"end")) break; // end of script for (jj = 0; menutab[jj].menu; jj++) // convert menu name to menu function if (strmatch(menuname,menutab[jj].menu)) break; if (! menutab[jj].menu) { zmessageACK(Mwin,ZTX("unknown edit function: %s"),menuname); goto cleanup; } menutab[jj].func(0,menutab[jj].arg); // call the menu function if (CEF && CEF->zd) { err = edit_load_widgets(CEF,fid); // read and load dialog settings if (err) { zmessageACK(Mwin,ZTX("load widgets failed: %s"),menuname); goto cleanup; } zdialog_send_event(CEF->zd,"apply"); // finish edit zdialog_send_event(CEF->zd,"done"); } } newfilevers = file_new_version(curr_file); // get next avail. file version name if (! newfilevers) goto cleanup; if (strmatch(curr_file_type,"RAW")) { // if RAW, substitute tif-16 strcpy(curr_file_type,"tif"); curr_file_bpc = 16; } err = f_save(newfilevers,curr_file_type,curr_file_bpc); // save file zfree(newfilevers); err = f_open_saved(); // open saved file version if (err) { zmessageACK(Mwin,ZTX("open failure: %s \n %s"),imagefile,strerror(errno)); goto cleanup; } } retstat = 0; // success goto cleanup; badfile: zmessageACK(Mwin,ZTX("script file format error: %s"),file); cleanup: if (fid) fclose(fid); fid = 0; return retstat; } /********************************************************************************/ // burn images to DVD/BlueRay optical disc namespace burn_images { zdialog *zd; char mydvd[60]; }; // menu function void m_burn_DVD(GtkWidget *, cchar *) // overhauled { using namespace burn_images; int burn_dialog_event(zdialog *zd, cchar *event); FILE *fid; char *pp; int ii, Fdvd, Ndvd, zstat; cchar *growisofs = "growisofs -Z %s -iso-level 4 -r -path-list %s 2>&1"; // growisofs command char burnlist[200], buffer[200]; char dvddev[20][4], dvddesc[40][4], dvddevdesc[60][4]; // up to 4 DVD/BR devices if (! Fgrowisofs) { zmessageACK(Mwin,ZTX("growisofs not installed")); return; } if (checkpend("all")) return; // check nothing pending F1_help_topic = "burn_DVD"; Fdvd = Ndvd = 0; *mydvd = 0; gallery_select_clear(); // clear selected file list fid = popen("lshw -class disk","r"); // find all DVD/BR devices if (! fid) { zmessageACK(Mwin,strerror(errno)); return; } while (true) { pp = fgets_trim(buffer,200,fid,1); if (! pp) break; if (strstr(buffer,"*-")) { // start of some device if (strstr(buffer,"*-cdrom")) Fdvd = 1; // start of DVD/BD device else Fdvd = 0; continue; } if (! Fdvd) continue; // ignore recs for other devices if (strstr(buffer,"description:")) { pp = strstr(buffer,"description:"); // save DVD/BD description pp += 12; strncpy0(dvddesc[Ndvd],pp,40); continue; } if (strstr(buffer,"/dev/")) { pp = strstr(buffer,"/dev/"); // have /dev/sr0 or similar format if (pp[7] < '0' || pp[7] > '9') continue; pp[8] = 0; strcpy(dvddev[Ndvd],pp); // save DVD/BD device Ndvd++; if (Ndvd == 4) break; continue; } } pclose(fid); if (Ndvd < 1) { zmessageACK(Mwin,ZTX("no DVD/BlueRay device found")); return; } for (ii = 0; ii < Ndvd; ii++) // combine devices and descriptions { // for use in GUI chooser list strcpy(dvddevdesc[ii],dvddev[ii]); strcat(dvddevdesc[ii]," "); strcat(dvddevdesc[ii],dvddesc[ii]); } /*** ______________________________________ | Burn Images to DVD/BlueRay | | | | [Select Files] NNN files selected | | [Select device] [_____________|v] | | | | [Start] [Cancel] | |______________________________________| ***/ zd = zdialog_new(ZTX("Burn Images to DVD/BlueRay"),Mwin,Bstart,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); zdialog_add_widget(zd,"button","files","hbf",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hbf",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbdvd","dialog",0); zdialog_add_widget(zd,"label","labdvd","hbdvd",ZTX("Select device"),"space=5"); zdialog_add_widget(zd,"combo","dvd","hbdvd",0,"expand"); for (int ii = 0; ii < Ndvd; ii++) // put available drives into list zdialog_cb_app(zd,"dvd",dvddevdesc[ii]); zdialog_resize(zd,300,0); zdialog_run(zd,burn_dialog_event,"parent"); // run dialog, wait for response zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat != 1) return; if (! GScount) return; // no files selected snprintf(burnlist,200,"%s/burnlist",get_zhomedir()); // write files to burnlist file fid = fopen(burnlist,"w"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return; } for (ii = 0; ii < GScount; ii++) fprintf(fid,"%s\n",GSfiles[ii]); fclose(fid); if (! *mydvd) return; // no device selected pp = strstr(mydvd," "); // remove description if (pp) *pp = 0; snprintf(command,200,growisofs,mydvd,burnlist); // start burn process popup_command(command,600,400,Mwin); return; } // dialog event and completion function int burn_dialog_event(zdialog *zd, cchar *event) { using namespace burn_images; char countmess[80]; if (strmatch(event,"files")) // select images to burn { gallery_select_clear(); // clear selected file list zdialog_show(zd,0); // hide parent dialog gallery_select(); // get list of files to convert zdialog_show(zd,1); snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"dvd")) zdialog_fetch(zd,"dvd",mydvd,60); // get user device choice return 1; } /********************************************************************************/ // find all duplicated files and create corresponding gallery of duplicates void m_duplicates(GtkWidget *, const char *) { int duplicates_dialog_event(zdialog *zd, cchar *event); PIXBUF *pxb; GError *gerror; FILE *fid = 0; int Nfiles = 0, Ndups = 0; // image file and duplicate counts char **files = 0; // image file list char **thumbs = 0; // 64x64 thumbnail images int *tcc = 0; // thumbnail actual width x height uint8 *pix; zdialog *zd; int thumbsize, pixdiff, pixcount; char text[100], gfile[100], *pp; int zstat, ii, jj, kk, cc, err, ww, hh, ndup; int m1, m2, sum, diff; F1_help_topic = "find_duplicates"; if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** ____________________________________________ | Find Duplicate Images | | | | thumbnail size [ 64 ] | | pixel difference [ 2 ] pixel count [ 2 ] | | Thumbnails: nnnnnn Duplicates: nnn | | | | /topdirk/subdirk1/subdirk2/... | | imagefile.jpg | | [proceed] [cancel] | |____________________________________________| ***/ zd = zdialog_new(ZTX("Find Duplicate Images"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbthumb","dialog"); zdialog_add_widget(zd,"label","labthumb","hbthumb",ZTX("Thumbnail size"),"space=3"); zdialog_add_widget(zd,"zspin","thumbsize","hbthumb","32|256|32|64","space=3"); zdialog_add_widget(zd,"hbox","hbdiff","dialog"); zdialog_add_widget(zd,"label","labdiff","hbdiff",ZTX("pixel difference"),"space=3"); zdialog_add_widget(zd,"zspin","pixdiff","hbdiff","1|20|1|2","space=3"); zdialog_add_widget(zd,"label","space","hbdiff",0,"space=8"); zdialog_add_widget(zd,"label","labsum","hbdiff",ZTX("pixel count"),"space=3"); zdialog_add_widget(zd,"zspin","pixcount","hbdiff","1|999|1|10","space=3"); zdialog_add_widget(zd,"hbox","hbcount","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labcount1","hbcount","Thumbnails:","space=3"); zdialog_add_widget(zd,"label","labcount2","hbcount",0); zdialog_add_widget(zd,"label","space","hbcount",0,"space=5"); zdialog_add_widget(zd,"label","labdup1","hbcount",ZTX("Duplicates:")); zdialog_add_widget(zd,"label","labdup2","hbcount","0"); zdialog_add_widget(zd,"hbox","hbdirk","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labdirk","hbdirk"," ","space=8"); zdialog_add_widget(zd,"hbox","hbfile","dialog"); zdialog_add_widget(zd,"label","labfile","hbfile"," ","space=8"); zdialog_restore_inputs(zd); zdialog_resize(zd,300,0); zdialog_run(zd,duplicates_dialog_event,"parent"); err = find_imagefiles(thumbdirk,2+16,files,Nfiles); // all thumbnails, all levels 18.01 if (err) { zmessageACK(Mwin,strerror(errno)); goto cleanup; } if (Nfiles < 10) { zmessageACK(Mwin,ZTX("only %d image thumbnails found"),Nfiles); goto cleanup; } snprintf(text,100,"%d",Nfiles); // stuff thumbnail count in dialog zdialog_stuff(zd,"labcount2",text); zstat = zdialog_wait(zd); // wait for user inputs if (zstat != 1) goto cleanup; zdialog_fetch(zd,"thumbsize",thumbsize); // thumbnail size to use zdialog_fetch(zd,"pixdiff",pixdiff); // pixel difference threshold zdialog_fetch(zd,"pixcount",pixcount); // pixel count threshold cc = Nfiles * sizeof(uint8 *); thumbs = (char **) zmalloc(cc); memset(thumbs,0,cc); cc = Nfiles * sizeof(int); tcc = (int *) zmalloc(cc); Ffuncbusy = 1; for (ii = 0; ii < Nfiles; ii++) // screen out thumbnails not { // within top image directories pp = thumb2imagefile(files[ii]); if (! pp) { zfree(files[ii]); files[ii] = 0; continue; } for (kk = 0; kk < Ntopdirks; kk++) { cc = strlen(topdirks[kk]); if (pp[cc] != '/') continue; if (strmatchN(pp,topdirks[kk],cc)) break; } zfree(pp); if (kk < Ntopdirks) continue; zfree(files[ii]); files[ii] = 0; } for (ii = 0; ii < Nfiles; ii++) // get thumbnails in memory { if (! files[ii]) continue; if (Fkillfunc) goto cleanup; kk = thumbsize; gerror = 0; pxb = gdk_pixbuf_new_from_file_at_size(files[ii],kk,kk,&gerror); if (! pxb) { printz("file: %s \n %s",files[ii],gerror->message); zfree(files[ii]); files[ii] = 0; continue; } ww = gdk_pixbuf_get_width(pxb); // actual thumbnail dimensions hh = gdk_pixbuf_get_height(pxb); // ( <= 64x64) pix = gdk_pixbuf_get_pixels(pxb); cc = ww * hh * 3; thumbs[ii] = (char *) zmalloc(cc); memcpy(thumbs[ii],pix,cc); tcc[ii] = cc; g_object_unref(pxb); zmainloop(10); // keep GTK alive } for (ii = 0; ii < Nfiles; ii++) // replace thumbnail filespecs { // with corresp. image filespecs if (! files[ii]) continue; pp = thumb2imagefile(files[ii]); zfree(files[ii]); files[ii] = pp; } snprintf(gfile,100,"%s/search_results",tempdir); // open file for gallery output fid = fopen(gfile,"w"); if (! fid) goto filerror; Fbusy_goal = Nfiles; // set up progress monitor Fbusy_done = 0; for (ii = 0; ii < Nfiles; ii++) // loop all thumbnails { if (Fkillfunc) goto cleanup; Fbusy_done++; if (! files[ii]) continue; pp = strrchr(files[ii],'/'); if (! pp) continue; *pp = 0; zdialog_stuff(zd,"labdirk",files[ii]); // update directory and file name zdialog_stuff(zd,"labfile",pp+1); // in dialog *pp = '/'; zmainloop(10); // keep GTK alive zsleep(0.001); // deliberate slowdown for (jj = ndup = 0; jj < Nfiles; jj++) // loop all thumbnails { if (jj == ii) continue; if (! files[jj]) continue; if (tcc[ii] != tcc[jj]) continue; // look for matching thumbnails for (kk = sum = 0; kk < tcc[ii]; kk++) { m1 = thumbs[ii][kk]; m2 = thumbs[jj][kk]; diff = abs(m1 - m2); if (diff < pixdiff) continue; if (++sum > pixcount) break; } if (kk < tcc[ii]) continue; // no match gerror = 0; // thumbnails match pxb = gdk_pixbuf_new_from_file_at_size(files[jj],64,64,&gerror); // check image is readable if (pxb) g_object_unref(pxb); if (! pxb || gerror) { // no, ignore matching zfree(files[jj]); // "broken file" thumbnails files[jj] = 0; continue; } if (ndup == 0) fprintf(fid,"%s\n",files[ii]); // first duplicate, output file name fprintf(fid,"%s\n",files[jj]); // output duplicate image file name zfree(files[jj]); files[jj] = 0; ndup++; Ndups++; snprintf(text,100,"%d",Ndups); // update duplicates found in dialog zdialog_stuff(zd,"labdup2",text); } } fclose(fid); fid = 0; free_resources(); navi::gallerytype = SEARCH; // generate gallery of duplicate images gallery(gfile,"initF",0); gallery(0,"paint",0); // position at top m_viewmode(0,"G"); cleanup: zdialog_free(zd); if (fid) fclose(fid); for (ii = 0; ii < Nfiles; ii++) if (files[ii]) zfree(files[ii]); if (thumbs) for (ii = 0; ii < Nfiles; ii++) if (thumbs[ii]) zfree(thumbs[ii]); if (files) zfree(files); if (thumbs) zfree(thumbs); if (tcc) zfree(tcc); fid = 0; Fbusy_goal = 0; Ffuncbusy = 0; Fkillfunc = 0; Fblock = 0; return; filerror: zmessageACK(Mwin,"file error: %s",strerror(errno)); goto cleanup; } // dialog event and completion function int duplicates_dialog_event(zdialog *zd, cchar *event) { if (! zd->zstat) return 1; // wait for user input if (zd->zstat != 1) Fkillfunc = 1; // [cancel] or [x] return 1; // [proceed] } /********************************************************************************/ // Select files and output a file containing the selected file names. namespace imagelist_images { zdialog *zd; char outfile[300]; }; // menu function void m_export_filelist(GtkWidget *, cchar *) { using namespace imagelist_images; int export_filelist_dialog_event(zdialog *zd, cchar *event); FILE *fid; int ii, zstat; cchar *title = ZTX("Create a file of selected image files"); if (checkpend("all")) return; // check nothing pending F1_help_topic = "export_filelist"; /*** ____________________________________________ | Create a file of selected image files | | | | [Select Files] NNN files selected | | Output File [__________________] [Browse] | | | | [Proceed] [Cancel] | |____________________________________________| ***/ zd = zdialog_new(title,Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbif","dialog",0,"space=3"); zdialog_add_widget(zd,"button","infiles","hbif",Bselectfiles,"space=3"); zdialog_add_widget(zd,"label","Nfiles","hbif",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbof","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labof","hbof",ZTX("Output File"),"space=3"); zdialog_add_widget(zd,"zentry","outfile","hbof",0,"size=30|space=5"); zdialog_add_widget(zd,"button","browse","hbof",Bbrowse,"space=5"); gallery_select_clear(); // clear selected file list if (*outfile) zdialog_stuff(zd,"outfile",outfile); zdialog_run(zd,export_filelist_dialog_event,"parent"); // run dialog, wait for response retry: zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"outfile",outfile,300); // get output file from dialog if (! GScount) { zmessageACK(Mwin,ZTX("no input files selected")); zd->zstat = 0; goto retry; // no input files } if (! *outfile) { zmessageACK(Mwin,ZTX("no output file selected")); zd->zstat = 0; goto retry; // no input files } fid = fopen(outfile,"w"); // open output file if (! fid) { zmessageACK(Mwin,strerror(errno)); // error zd->zstat = 0; goto retry; // no input files } zdialog_free(zd); for (ii = 0; ii < GScount; ii++) // write input file names to output fprintf(fid,"%s\n",GSfiles[ii]); fclose(fid); zmessageACK(Mwin,Bcompleted); return; } // dialog event and completion function int export_filelist_dialog_event(zdialog *zd, cchar *event) { using namespace imagelist_images; char *file; char countmess[80]; if (strmatch(event,"infiles")) // select image files { gallery_select_clear(); // clear selected file list gallery_select(); // get list of files to convert snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"Nfiles",countmess); } if (strmatch(event,"browse")) { file = zgetfile(ZTX("Output File"),MWIN,"save",outfile,0); if (file) zdialog_stuff(zd,"outfile",file); else zdialog_stuff(zd,"outfile",""); } return 1; } /********************************************************************************/ // Export selected image files to another directory with optional downsizing. // Only the metadata relevant for web photo services is copied. namespace export_images_names { char tolocation[500]; int Fsamesize; int maxww, maxhh; }; // menu function void m_export_images(GtkWidget*, cchar *menu) // overhauled 17.08 { using namespace export_images_names; int export_images_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int ii, cc, err, ww, hh, zstat; PXM *pxmin, *pxmout; char *infile, *outfile, *pp; float scale, wscale, hscale; #define NK 10 cchar *keys[NK] = { exif_date_key, iptc_keywords_key, exif_copyright_key, exif_comment_key, exif_usercomment_key, iptc_caption_key, exif_city_key, exif_country_key, exif_lati_key, exif_longi_key }; char *kdata[NK]; F1_help_topic = "export_images"; /*** __________________________________________________ | Export Image Files | | | | [Select Files] N files selected | | To Location [________________________] [Browse] | | Max. Width [____] Height [____] [x] no change | | | | [proceed] [cancel] | |__________________________________________________| ***/ zd = zdialog_new(ZTX("Export Image Files"),Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); zdialog_add_widget(zd,"button","files","hbf",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","fcount","hbf",Bnofileselected,"space=10"); zdialog_add_widget(zd,"hbox","hbloc","dialog"); zdialog_add_widget(zd,"label","labloc","hbloc",ZTX("To Location"),"space=5"); zdialog_add_widget(zd,"zentry","toloc","hbloc",0,"expand"); zdialog_add_widget(zd,"button","browse","hbloc",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbwh","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labw","hbwh",ZTX("max. Width"),"space=5"); zdialog_add_widget(zd,"zentry","maxww","hbwh","1000","size=5"); zdialog_add_widget(zd,"label","space","hbwh",0,"space=5"); zdialog_add_widget(zd,"label","labh","hbwh",Bheight,"space=5"); zdialog_add_widget(zd,"zentry","maxhh","hbwh","700","size=5"); zdialog_add_widget(zd,"check","samesize","hbwh",ZTX("no change"),"space=12"); zdialog_add_widget(zd,"label","space","hbwh",0,"space=30"); // push back oversized entries zdialog_restore_inputs(zd); // preload prior location zdialog_resize(zd,400,0); zdialog_run(zd,export_images_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); if (zstat != 1) return; popup_report_open(Mwin,"exporting files",500,200); for (ii = 0; ii < GScount; ii++) // loop selected files { infile = GSfiles[ii]; // input filespec popup_report_write(0,"%s \n",infile); pp = strrchr(infile,'/'); // construct output filespec if (! pp) continue; cc = strlen(pp) + 3; outfile = zstrdup(tolocation,cc); strcat(outfile,pp); pxmin = PXM_load(infile,0); // read input file if (! pxmin) { popup_report_write(1," %s \n",ZTX("file type not supported")); zfree(outfile); continue; } ww = pxmin->ww; // input file size hh = pxmin->hh; if (Fsamesize) pxmout = pxmin; // same size, output = input else { wscale = hscale = 1.0; if (ww > maxww) wscale = 1.0 * maxww / ww; // compute new size if (hh > maxhh) hscale = 1.0 * maxhh / hh; if (wscale < hscale) scale = wscale; else scale = hscale; if (scale > 0.999) pxmout = pxmin; // no change else { ww = ww * scale; hh = hh * scale; pxmout = PXM_rescale(pxmin,ww,hh); // rescaled output file PXM_free(pxmin); // free memory } } err = PXM_ANY_save(pxmout,outfile); // write output file if (err) popup_report_write(1," %s \n",ZTX("cannot create new file")); PXM_free(pxmout); err = exif_get(infile,keys,kdata,NK); // copy EXIF data exif_put(outfile,keys,(cchar **) kdata,NK); zfree(outfile); } popup_report_write(0,"COMPLETED \n"); return; } // dialog event and completion callback function int export_images_dialog_event(zdialog *zd, cchar *event) { using namespace export_images_names; int cc, err; char countmess[80]; char *ploc; STATB stbuff; if (strmatch(event,"files")) // select files to export { gallery_select_clear(); // clear selected file list gallery_select(); // get list of files to convert snprintf(countmess,80,Bfileselected,GScount); // update dialog zdialog_stuff(zd,"fcount",countmess); } if (strmatch(event,"browse")) { zdialog_fetch(zd,"toloc",tolocation,500); ploc = zgetfile(ZTX("Select directory"),MWIN,"folder",tolocation); // new location browse if (! ploc) return 1; zdialog_stuff(zd,"toloc",ploc); zfree(ploc); } if (! zd->zstat) return 1; // wait for dialog completion if (zd->zstat != 1) return 1; // [cancel] if (! GScount) { // [proceed] zmessageACK(Mwin,Bnofileselected); // no files selected zd->zstat = 0; // keep dialog active return 1; } zdialog_fetch(zd,"toloc",tolocation,500); // get output location err = stat(tolocation,&stbuff); if (err || ! S_ISDIR(stbuff.st_mode)) { zmessageACK(Mwin,ZTX("location is not a directory")); zd->zstat = 0; } cc = strlen(tolocation) - 1; // remove trailing '/' if present if (tolocation[cc] == '/') tolocation[cc] = 0; zdialog_fetch(zd,"samesize",Fsamesize); // get resize options zdialog_fetch(zd,"maxww",maxww); zdialog_fetch(zd,"maxhh",maxhh); return 1; } fotoxx-18.01.1/f.gallery.cc0000644000175000017500000052117013222767271014062 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit - directory navigation and thumbnail gallery functions gallery create/update/search/paint an image gallery navi:: gallery_comp gallery sort compare function gallery_memory save and recall gallery sort and scroll position gallery_paint paint thumbnail gallery gallery_paintmeta same, for metadata report gallery_listview same, for list view report dir_filecount paint - get directory subdir and image file counts draw_text paint - write text over thumbnail images gallery_navibutts add directory navigation buttons menufuncx gallery menu: sort/zoom/scroll ... gallery_scroll scroll gallery in rows and pages navibutt_clicked open new gallery from clicked directory newtop open new gallery from clicked TOP entry newalbum open new gallery from clicked album entry gallery_sort choose gallery sort order and sort gallery mouse_event process gallery thumbnail clicks gallery_dragfile process thumbnail dragged gallery_dropfile process thumbnail dropped (add new file, arrange album) KBaction process gallery KB actions: zoom, page, top, end gallery_memory save/recall gallery sort and position set_gwin_title update the gallery window title bar gallery_getnext get previous/next file from curr_file in current gallery get_rootname get file root name without version and extension prev_next_gallery get gallery's previous or next gallery file_position get gallery file position image_file_type determine file type (directory, image, RAW, thumbnail, other) thumb2imagefile get corresponding image file for given thumbnail file image2thumbfile get corresponding thumbnail file for given image file update_thumbnail_file refresh thumbnail file if missing or stale get_cache_thumbnail get thumbnail from cache, create and add if missing make_thumbnail_pixbuf make thumbnail pixbuf from image file delete_thumbnail delete thumbnail disk file preload_thumbs preload thumbnails beyond gallery into thumbnail cache gallery_popimage popup a larger image from a clicked thumbnail gallery_select1 single gallery file selection function and dialog gallery_select multiple gallery file selection function and dialog m_edit_bookmarks edit bookmarks m_goto_bookmark jump to a gallery/bookmark position m_alldirs folding directory tree, click for gallery view m_sync_gallery set gallery from current image file m_show_hidden dummy menu for KB shortcut "Show Hidden Files" *********************************************************************************/ #define EX extern // disable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ /*** defined in fotoxx.h typedef struct { current gallery file list in memory char *file; /directory.../filename char fdate[16]; file date: yyyymmddhhmmss char pdate[16]; photo date: yyyymmddhhmmss int mdindex; index to metadata list Gmdlist[*] } GFlist_t; ***/ namespace navi { #define maxgallerylevs 60 // max gallery navigation levels #define TEXTWIN GTK_TEXT_WINDOW_TEXT // GDK window of GTK text view #define NEVER GTK_POLICY_NEVER #define ALWAYS GTK_POLICY_ALWAYS #define thumbxx 5 // thumbx array size int thumbx[5] = { 512, 360, 256, 180, 128 }; // thumbnail sizes GFlist_t *GFlist; // gallery file list int Nfiles = 0; // gallery file count (images + subdirs)) int Nsubdirs = 0; // gallery subdirectory count int Nimages = 0; // gallery image file count char **Gmdlist = 0; // corresp. metadata list int Gmdrows = 0; // text rows in metadata list char *galleryname = 0; // directory or file list name GTYPE gallerytype; // gallery type: directory, recent, etc. GSORT gallerysort; // gallery sort: f.name, f.date, p.date GSEQ galleryseq; // gallery sort: ascending/descending GtkWidget *gallerybutt[60]; // gallery navi buttons [aaa] [bbb] ... char *gallerypath[60]; // corresp. directory names aaa bbb ... GtkWidget *gallerylabel = 0; // gallery label (album name ...) int gallerypainted = 0; // gallery is finished painting int xwinW, xwinH; // gallery window current size int thumbsize = 256; // initial thumbnail image size int thumbW, thumbH; // gallery window thumbnail cell size int fontsize = 10; // font size for gallery text int texthh; // vertical space req. for metadata text int xrows, xcols; // gallery window thumbnail rows, cols int margin = 5; // cell margin from left and top edge int genthumbs = 0; // count newly generated thumbnails int scrollposn = 0; // scroll position int maxscroll; // max. scroll position int galleryposn = 0; // scroll-to file position (Nth) char *drag_file = 0; // selected thumbnail (file) char *drag_gallery = 0; // drag from gallery int drag_posn = -1; // drag from position int gallery_scrollgoal = -1; // gallery scroll goal position int gallery_scrollspeed = 0; // gallery scroll speed pixels/second int Fpreload_thumbs_thread = 0; // thumbnail preload thread active 17.04 int Fpreload_thumbs_block = 0; // thumbnail preload thread blocked 18.01 // private functions int gallery_comp(cchar *rec1, cchar *rec2); // gallery record compare for sort options int gallery_paint(GtkWidget *, cairo_t *); // gallery window paint function int gallery_paintmeta(GtkWidget *Gdrawin, cairo_t *cr); // same, for metadata report int gallery_listview(GtkWidget *Gdrawin, cairo_t *cr); // same, for list view report void dir_filecount(char *dirname, int &ndir, int &nfil); // get subdir and image file counts void draw_text(cairo_t *cr, char *text, int x, int y, int ww); // draw text in gallery window void gallery_navibutts(); // create navigation buttons in top panel void menufuncx(GtkWidget *, cchar *menu); // function for gallery window buttons void gallery_scroll(int position, int speed); // start gallery slow scroll to position int gallery_scrollfunc(void *); // gallery scroll timer function void navibutt_clicked(GtkWidget *, int *level); // set gallery via click navigation button void newtop(GtkWidget *widget, GdkEventButton *event); // function for [TOP] button 17.04 void newtop_menu_event(GtkWidget *, cchar *); // [TOP] menu response function void newalbum(GtkWidget *widget, GdkEventButton *event); // function for [Album] button 17.04 void newalbum_menu_event(GtkWidget *, cchar *); // [Album] menu response function void gallery_sort(); // choose sort order and sort gallery void mouse_event(GtkWidget *widget, GdkEvent *event, void *); // gallery window mouse event function char * gallery_dragfile(); // start drag-drop, set the file void gallery_dropfile(int mousex, int mousey, char *file); // accept drag-drop file at position int KBpress(GtkWidget *, GdkEventKey *, void *); // gallery window key press event } using namespace zfuncs; using namespace navi; /******************************************************************************** char * gallery(cchar *filez, cchar *action, int Nth) public function to create/update image gallery (thumbnail window) Make a scrolling window of thumbnails for a directory or list of files Handle window buttons (up row, down page, open file, etc.) Call external functions in response to thumbnail mouse-clicks. filez: image file or directory, or file with list of image files filez action Nth ----- ------ --- file init * directory gallery, file name sort (subdirs first, last version option) file initF * file list gallery, no sort * sort -2 sort and position via gallery_memory() * sort -1 sort and position from current params file paint N paint, position = file (use N if valid for file) 0 paint N paint, position = N 0 paint -1 paint, no change in position file insert N add file to gallery at position N (directory or unsorted album) * delete N delete Nth file in gallery list * get N return file N in gallery list or null if N > last file update * update gallery file params (GFlist[*]) from image index in memory * get1st * return 1st image file in gallery or null if none Returned values: get or get1st: filespec others: null The returned file belongs to caller and is subject for zfree(). thumbnail click functions: gallery_Lclick_func() default function (open file) gallery_Rclick_popup() default function (popup menu) gallery_select_Lclick_func() gallery_select active gallery_select_Rclick_func() gallery_select active bookmarks_Lclick_func edit bookmarks active *********************************************************************************/ char * gallery(cchar *filez, cchar *action, int Nth) // overhauled 18.01 { char *file, *file2; char *pp, *pp1, *pp2, buff[XFCC]; char fdate[16], pdate[16], size[16]; char **Flist; int err, ii, jj, kk; int cc, cc1, cc2, fposn; int NF, flags; FTYPE ftype; FILE *fid; STATB statbuf; if (strmatchN(action,"init",4)) // init or initF { if (! filez && gallerytype == GDIR) filez = galleryname; // refresh gallery if (! filez) return 0; if (GFlist && Gmdlist) { for (ii = 0; ii < Nfiles; ii++) { // free prior metadata list kk = GFlist[ii].mdindex; if (Gmdlist[kk]) zfree(Gmdlist[kk]); } zfree(Gmdlist); Gmdlist = 0; } if (GFlist) { for (ii = 0; ii < Nfiles; ii++) // free prior gallery list zfree(GFlist[ii].file); zfree(GFlist); GFlist = 0; } cc = maximages * sizeof(GFlist_t); GFlist = (GFlist_t *) zmalloc(cc); // gallery file list Nfiles = Nsubdirs = Nimages = 0; // no files yet galleryname = zstrdup(filez); // may be directory or file } if (strmatch(action,"init")) // initz. from given file or directory { err = stat(galleryname,&statbuf); if (err) { pp = (char *) strrchr(galleryname,'/'); // bad file, check directory part if (! pp) return 0; pp[0] = 0; // 17.08 err = stat(galleryname,&statbuf); if (err) return 0; // give up, empty file list } if (S_ISREG(statbuf.st_mode)) { // if a file, get directory part pp = (char *) strrchr(galleryname,'/'); if (! pp) return 0; pp[0] = 0; // 17.08 } gallerytype = GDIR; // gallery type = directory cc = strlen(galleryname) - 1; // remove trailing '/' 17.08 if (cc > 1 && galleryname[cc] == '/') galleryname[cc] = 0; // but not if root directory '/' 18.01 flags = 1 + 8; // images + directories, one level if (Fshowhidden) flags += 4; // include hidden files err = find_imagefiles(galleryname,flags,Flist,NF); // find all image files in directory 18.01 if (err) { zmessageACK(Mwin,strerror(errno)); return 0; } for (ii = 0; ii < NF; ii++) { file = Flist[ii]; /// if (strmatch(file,galleryname)) { // skip self directory removed 18.01 /// zfree(file); /// continue; /// } pp = strrchr(file,'/'); if (pp && (strmatch(pp,"/.") || strmatch(pp,"/.."))) { zfree(file); continue; } if (Nfiles == maximages) { // too many files zmessageACK(Mwin,Btoomanyfiles,maximages); break; } ftype = image_file_type(file); if (ftype == FDIR) { // subdirectory GFlist[Nfiles].file = file; // add to file list GFlist[Nfiles].file[0] = '!'; // if directory, make it sort first GFlist[Nfiles].fdate[0] = 0; // no file date GFlist[Nfiles].pdate[0] = 0; // no photo date Nfiles++; Nsubdirs++; } else if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) { // supported file 17.08 err = stat(file,&statbuf); if (err) continue; GFlist[Nfiles].file = file; // add to file list err = get_xxrec_min(file,fdate,pdate,size); // file date, photo date, size strcpy(GFlist[Nfiles].fdate,fdate); strcpy(GFlist[Nfiles].pdate,pdate); Nfiles++; Nimages++; } else { zfree(file); // thumbnail or other kind of file continue; } } if (Flist) zfree(Flist); gallerysort = FNAME; // sort GFlist by file name ascending galleryseq = ASCEND; galleryposn = 0; if (Nfiles > 1) HeapSort((char *) GFlist, sizeof(GFlist_t), Nfiles, gallery_comp); if (Flastversion) // last versions only (user setting) { ii = Nfiles - Nimages; // skip directories jj = ii + 1; kk = 0; while (jj < Nfiles) { pp1 = strrchr(GFlist[jj].file,'.'); // /.../filename.vNN.ext if (pp1 && pp1[-4] == '.' && pp1[-3] == 'v') { // | cc1 = pp1 - GFlist[jj].file - 3; pp2 = strrchr(GFlist[ii].file,'.'); if (! pp2) cc2 = 0; else { cc2 = pp2 - GFlist[ii].file + 1; // /.../filename.ext if (pp2[-4] == '.' && pp2[-3] == 'v') cc2 -= 4; // | } if (cc1 == cc2 && strmatchN(GFlist[jj].file,GFlist[ii].file,cc1)) { zfree(GFlist[ii].file); // if match to "/.../filename." GFlist[ii] = GFlist[jj]; // replace with later version jj++; kk++; continue; } } ii++; GFlist[ii] = GFlist[jj]; jj++; } Nfiles -= kk; Nimages -= kk; } curr_file_count = Nimages; // gallery image file count gallerypainted = 0; return 0; } if (strmatch(action,"initF")) // gallery from given file list { if (gallerytype == TNONE || gallerytype == GDIR) // gallery type from caller: zappcrash("gallery() initF gallerytype %d",gallerytype); // SEARCH META RECENT NEWEST ALBUM fid = fopen(galleryname,"r"); // open file if (! fid) return 0; while (true) // read list of files { file = fgets_trim(buff,XFCC-1,fid,1); if (! file) break; err = stat(file,&statbuf); // check file exists if (err) continue; ftype = image_file_type(file); if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) // unsupported image file type 18.01 printz("unknown file type: %s \n",file); GFlist[Nfiles].file = zstrdup(file); // add to file list get_xxrec_min(file,fdate,pdate,size); // file date, photo date, size strcpy(GFlist[Nfiles].fdate,fdate); strcpy(GFlist[Nfiles].pdate,pdate); Nfiles++; if (Nfiles == maximages) { zmessageACK(Mwin,Btoomanyfiles,maximages); break; } } fclose(fid); Nimages = Nfiles; // no directories curr_file_count = Nimages; // gallery image file count gallerysort = SNONE; // no initial sort galleryseq = QNONE; galleryposn = 0; gallerypainted = 0; return 0; } if (strmatch(action,"sort")) // sort the gallery file list { if (Nth == -2) gallery_memory("get"); // recall prior sort and posn. if (Nfiles > 1 && gallerysort != SNONE) // sort using current or prior params HeapSort((char *) GFlist, sizeof(GFlist_t), Nfiles, gallery_comp); gallerypainted = 0; return 0; } if (strmatch(action,"paint")) // paint gallery window { if (filez) Nth = file_position(filez,Nth); // use or get valid Nth for filez if (Nth >= 0) galleryposn = Nth; // filez position or caller Nth gtk_widget_queue_draw(Gdrawin); // draw gallery window gallerypainted = 0; return 0; } if (strmatch(action,"insert")) // insert new file into list { fposn = Nth; // file position from caller if (fposn < 0) fposn = 0; // limit to allowed range if (fposn > Nfiles) fposn = Nfiles; if (Nfiles == maximages-1) { // no room zmessageACK(Mwin,Btoomanyfiles,maximages); return 0; } for (ii = Nfiles; ii > fposn; ii--) // create hole in list GFlist[ii] = GFlist[ii-1]; GFlist[fposn].file = zstrdup(filez); // put new file in hole get_xxrec_min(filez,fdate,pdate,size); // file date, photo date, size strcpy(GFlist[Nfiles].fdate,fdate); strcpy(GFlist[Nfiles].pdate,pdate); Nfiles++; Nimages++; gallerypainted = 0; return 0; } if (strmatch(action,"delete")) // delete file from list { fposn = Nth; // file position from caller must be OK if (fposn < 0 || fposn > Nfiles-1) return 0; Nfiles--; if (*GFlist[fposn].file == '!') Nsubdirs--; // reduce directory count else Nimages--; // reduce image file count zfree(GFlist[fposn].file); // remove GFlist record for (ii = fposn; ii < Nfiles; ii++) // close the hole GFlist[ii] = GFlist[ii+1]; // gcc bug workaround removed if (Gmdlist) { kk = GFlist[fposn].mdindex; // remove corresp. Gmdlist data 18.01 if (Gmdlist[kk]) zfree(Gmdlist[kk]); Gmdlist[kk] = 0; } gallerypainted = 0; return 0; } if (strmatch(action,"get")) // return Nth file in gallery { fposn = Nth; // file position from caller must be OK if (fposn < 0 || fposn > Nfiles-1) return 0; file2 = zstrdup(GFlist[fposn].file); // get Nth file file2[0] = '/'; // restore initial '/' err = stat(file2,&statbuf); if (! err) return file2; zfree(file2); return 0; } if (strmatch(action,"update")) // update file from image index { // see f.meta.cc for (ii = 0; ii < Nfiles; ii++) { if (! strmatch(filez,GFlist[ii].file)) continue; get_xxrec_min(filez,fdate,pdate,size); // file date, photo date, size strcpy(GFlist[ii].fdate,fdate); strcpy(GFlist[ii].pdate,pdate); } return 0; } if (strmatch(action,"get1st")) // return 1st image file in gallery { for (Nth = 0; Nth < Nfiles; Nth++) { if (GFlist[Nth].file[0] == '!') continue; // subdirectory file2 = zstrdup(GFlist[Nth].file); // get Nth file err = stat(file2,&statbuf); if (! err) return file2; zfree(file2); } return 0; } zappcrash("navigate %s",action); // invalid action return 0; } // private function, gallery sort compare // directories sort first and upper/lower case is ignored int navi::gallery_comp(cchar *rec1, cchar *rec2) { int nn; GFlist_t *grec1, *grec2; grec1 = (GFlist_t *) rec1; grec2 = (GFlist_t *) rec2; if (grec1->file[0] == '!') { // directory sort 17.01 if (grec2->file[0] != '!') return -1; // directory :: image file nn = strcasecmp(grec1->file,grec2->file); // directory :: directory if (nn) return nn; nn = strcmp(grec1->file,grec2->file); // if equal, use utf8 compare return nn; } else if (grec2->file[0] == '!') return +1; // image file :: directory if (galleryseq == DESCEND) { // descending, switch inputs grec1 = (GFlist_t *) rec2; grec2 = (GFlist_t *) rec1; } switch (gallerysort) { case FNAME: { // file name nn = strcasecmp(grec1->file,grec2->file); // ignore case if (nn) return nn; nn = strcmp(grec1->file,grec2->file); // if equal, use utf8 compare return nn; } case FDATE: { // file mod date/time nn = strcmp(grec1->fdate,grec2->fdate); if (nn) return nn; goto tiebreak; } case PDATE: { // photo date/time nn = strcmp(grec1->pdate,grec2->pdate); // (EXIF DateTimeOriginal) if (nn) return nn; goto tiebreak; } default: return 0; tiebreak: // tie breaker for date sort nn = strcasecmp(grec1->file,grec2->file); // file name without case if (nn) return nn; nn = strcmp(grec1->file,grec2->file); // if equal, use utf8 compare return nn; } } // private function // paint gallery window - draw thumbnail images and limited text int navi::gallery_paint(GtkWidget *drwin, cairo_t *cr) { GdkRGBA rgba; PIXBUF *pxbT; FTYPE ftype; double x1, y1, x2, y2; int ii, nrows, row, col; int textlines; int row1, row2, ww, hh, cc; int drwingW, drwingH; int thumx, thumy; int ndir, nfil; char *pp, *fspec, *fname, p0; char *ppd, fdirk[40]; char text[200], fdate[20], pdate[20], size[16]; gallerypainted = 0; if (! galleryname) return 1; set_gwin_title(); // main window title = gallery name gallery_navibutts(); // set navigation buttons on top panel rgba.red = 0.00392 * GBrgb[0]; // window background color rgba.green = 0.00392 * GBrgb[1]; // 0 - 255 --> 0.0 - 1.0 rgba.blue = 0.00392 * GBrgb[2]; rgba.alpha = 1.0; gdk_cairo_set_source_rgba(cr,&rgba); cairo_paint(cr); if (gallerytype == META) { // metadata report gallery_paintmeta(drwin,cr); return 1; } if (thumbsize == 0) { // list view report 17.08 gallery_listview(drwin,cr); return 1; } for (ii = 0; ii < thumbxx; ii++) // bugfix 17.08.2 if (thumbsize == thumbx[ii]) break; // tolerate bad parameter value if (ii >= thumbxx) { // inherited from prior release ii = 2; thumbsize = thumbx[ii]; } fontsize = appfontsize - 1; // 18.01 if (ii == thumbxx-1) fontsize = appfontsize - 2; if (Findexvalid == 0) textlines = 1; // file name only else textlines = 2; // file name / date + size if (gallerytype != GDIR) textlines++; // add directory name above file name texthh = textlines * 1.6 * fontsize + 4; // vertical space required thumbW = thumbsize + 10; // thumbnail cell size thumbH = thumbsize + texthh + thumbsize/24 + 10; xwinW = gtk_widget_get_allocated_width(Gscroll); // drawing window size xwinH = gtk_widget_get_allocated_height(Gscroll); xrows = int(0.1 + 1.0 * xwinH / thumbH); // get thumbnail rows and cols that xcols = int(0.1 + 1.0 * xwinW / thumbW); // (almost) fit in window if (xrows < 1) xrows = 1; if (xcols < 1) xcols = 1; nrows = (Nfiles+xcols-1) / xcols; // thumbnail rows, 1 or more if (nrows < 1) nrows = 1; drwingW = xcols * thumbW + margin + 10; // layout size for entire gallery drwingH = (nrows + 1) * thumbH; // (+ empty row for visual end) if (drwingH <= xwinH) drwingH = xwinH + 1; // at least window size + 1 gtk_widget_get_size_request(drwin,&ww,&hh); // current size if (ww != drwingW || hh != drwingH) gtk_widget_set_size_request(drwin,-1,drwingH); // needs to change maxscroll = nrows * thumbH; // too far but necessary if (maxscroll < xwinH) maxscroll = xwinH; // compensate GTK bug gtk_adjustment_set_upper(Gadjust,maxscroll); gtk_adjustment_set_step_increment(Gadjust,thumbH); // scrollbar works in row steps gtk_adjustment_set_page_increment(Gadjust,thumbH * xrows); // and in page steps if (galleryposn >= 0) { // new target file position (Nth) scrollposn = galleryposn / xcols * thumbH; // scroll position for target file if (scrollposn > maxscroll) scrollposn = maxscroll; // >> top row of window gtk_adjustment_set_value(Gadjust,scrollposn); gtk_widget_queue_draw(drwin); galleryposn = -1; // disable galleryposn return 1; } scrollposn = gtk_adjustment_get_value(Gadjust); // save gallery sort and position 18.01 galleryposn = (scrollposn + thumbH/2) / thumbH * xcols; gallery_memory("put"); galleryposn = -1; // disable cairo_clip_extents(cr,&x1,&y1,&x2,&y2); // window region to paint row1 = y1 / thumbH; row2 = y2 / thumbH; for (row = row1; row <= row2; row++) // draw file thumbnails for (col = 0; col < xcols; col++) // draw all columns in row { ii = row * xcols + col; // next file if (ii >= Nfiles) goto endloops; // exit 2 nested loops fspec = GFlist[ii].file; // directory/file name p0 = *fspec; // replace possible '!' with '/' *fspec = '/'; pp = strrchr(fspec,'/'); // get file name only if (pp) fname = pp + 1; else fname = fspec; strcpy(fdirk,"/"); if (gallerytype != GDIR) { // files from mixed directories if (pp && pp > fspec) { for (ppd = pp-1; ppd >= fspec && *ppd != '/'; ppd--); // get last directory level 17.08 cc = pp - ppd + 1; // (include null to be added) if (cc > 40) cc = 40; strncpy0(fdirk,ppd,cc); } } thumx = col * thumbW + margin; // drawing area position thumy = row * thumbH + margin; if (curr_file && strmatch(fspec,curr_file)) { // yellow background for curr. image cairo_set_source_rgb(cr,1,1,0.5); cairo_rectangle(cr,thumx-3,thumy-3,thumbW-3,texthh); cairo_fill(cr); } ftype = image_file_type(fspec); // directory/image/RAW file if (ftype == FDIR) { // directory dir_filecount(fspec,ndir,nfil); // get subdir and file counts snprintf(text,200,"%s\n%d + %d images",fname,ndir,nfil); // dir name, subdirs + image files } else // 18.01 { get_xxrec_min(fspec,fdate,pdate,size); // filename, file/photo date, size if (gallerysort == FDATE) pp = fdate; else pp = pdate; // use file or photo date based on sort if (strmatch(pp,"null")) pp = (char *) "undated"; else { memcpy(pp+17,pp+12,2); // :ss added 18.01 memcpy(pp+14,pp+10,2); // yyyymmddhhmmss to yyyy-mm-dd hh:mm:ss memcpy(pp+11,pp+8,2); memcpy(pp+8,pp+6,2); memcpy(pp+5,pp+4,2); pp[19] = 0; pp[16] = pp[13] = ':'; pp[10] =' '; pp[4] = pp[7] = '-'; } if (Findexvalid == 0) { if (gallerytype == GDIR) strncpy0(text,fname,200); // file name only else snprintf(text,200,"%s\n%s",fdirk,fname); // directory name, file name } else { if (gallerytype == GDIR) snprintf(text,200,"%s\n%-16s %s",fname,pp,size); // text is filename, date, size else snprintf(text,200,"%s\n%s\n%-16s %s",fdirk,fname,pp,size); // add directory in front } draw_text(cr,text,thumx,thumy,thumbW-5); // paint text first thumy += texthh; // position thumbnail below text } pxbT = get_cache_thumbnail(fspec,thumbsize); // get thumbnail if (pxbT) { ww = gdk_pixbuf_get_width(pxbT); ww = (thumbsize - ww) / 4; // shift margin if smaller width gdk_cairo_set_source_pixbuf(cr,pxbT,thumx+ww,thumy); cairo_paint(cr); // paint } if (ftype == FDIR) { // directory thumy += thumbsize/3 + 10; // overlay thumbnail with text draw_text(cr,text,thumx+ww+thumbW/6,thumy,thumbW-5); } *fspec = p0; // restore '!' } endloops: preload_thumbs(ii); // preload following thumbnails 17.04 gallerypainted = 1; return 1; } // private function // paint metadata report - draw thumbnail images + metadata int navi::gallery_paintmeta(GtkWidget *drwin, cairo_t *cr) { PIXBUF *pxbT; double x1, y1, x2, y2; int ii, kk, nrows, row; int row1, row2, ww, hh; int drwingW, drwingH; int thumx, thumy, textww; char *fspec, p0; if (thumbsize < 256) thumbsize = 256; // 17.08 for (ii = 0; ii < thumbxx; ii++) // bugfix 17.08.2 if (thumbsize == thumbx[ii]) break; // tolerate bad parameter value if (ii >= thumbxx) { // inherited from prior release ii = 2; thumbsize = thumbx[ii]; } fontsize = appfontsize; // 18.01 thumbW = thumbsize + 10; // thumbnail layout size thumbH = thumbsize + 20; texthh = Gmdrows * fontsize * 1.8 + 20; // space for metadata text if (texthh > thumbH) thumbH = texthh; xwinW = gtk_widget_get_allocated_width(Gscroll); // drawing window size xwinH = gtk_widget_get_allocated_height(Gscroll); xrows = int(0.1 + 1.0 * xwinH / thumbH); // get thumbnail rows fitting in window if (xrows < 1) xrows = 1; xcols = 1; // force cols = 1 nrows = Nfiles; // thumbnail rows if (! nrows) nrows = 1; // 17.04 drwingW = xwinW; // layout size for entire file list if (drwingW < 800) drwingW = 800; drwingH = (nrows + 1) * thumbH; // last row if (drwingH < xwinH) drwingH = xwinH; gtk_widget_get_size_request(drwin,&ww,&hh); // current size if (ww != drwingW || hh != drwingH) gtk_widget_set_size_request(drwin,-1,drwingH); // needs to change maxscroll = nrows * thumbH; // too far but necessary if (maxscroll < xwinH) maxscroll = xwinH; // compensate GTK bug gtk_adjustment_set_upper(Gadjust,maxscroll); gtk_adjustment_set_step_increment(Gadjust,thumbH); // scrollbar works in row steps gtk_adjustment_set_page_increment(Gadjust,thumbH * xrows); // and in page steps if (galleryposn >= 0) { // new target file position (Nth) scrollposn = galleryposn / xcols * thumbH; // scroll position for target file if (scrollposn > maxscroll) scrollposn = maxscroll; // >> top row of window gtk_adjustment_set_value(Gadjust,scrollposn); gtk_widget_queue_draw(drwin); galleryposn = -1; // disable galleryposn return 1; } scrollposn = gtk_adjustment_get_value(Gadjust); // save gallery sort and position 18.01 galleryposn = (scrollposn + thumbH/2) / thumbH * xcols; gallery_memory("put"); galleryposn = -1; // disable cairo_clip_extents(cr,&x1,&y1,&x2,&y2); // window region to paint row1 = y1 / thumbH; row2 = y2 / thumbH; textww = drwingW - thumbW - 2 * margin; // space for text right of thumbnail for (row = row1; row <= row2; row++) // draw file thumbnails { ii = row; // next file if (ii >= Nfiles) break; fspec = GFlist[ii].file; p0 = *fspec; // replace possible ! with / *fspec = '/'; thumx = margin; // drawing area position thumy = row * thumbH + margin; pxbT = get_cache_thumbnail(fspec,thumbsize); // get thumbnail if (pxbT) { gdk_cairo_set_source_pixbuf(cr,pxbT,thumx,thumy); cairo_paint(cr); } draw_text(cr,fspec,thumbW+margin,thumy,textww); // write filespec to right of thumbnail if (Gmdlist) { // write corresp. metadata if any 18.01 kk = GFlist[ii].mdindex; draw_text(cr,Gmdlist[kk],thumbW+margin,thumy+20,textww); // bugfix (kk=0 omitted) 18.01 } *fspec = p0; // restore '!' } gallerypainted = 1; return 1; } // private function // paint gallery window - list view, small thumbnail, more metadata text int navi::gallery_listview(GtkWidget *drwin, cairo_t *cr) // 17.08 { PIXBUF *pxbT; FTYPE ftype; double x1, y1, x2, y2; int LVthumbsize; int ii, nrows, row; int row1, row2, ww, hh; int drwingW, drwingH; int thumx, thumy, textww; int ndir, nfil; char *fspec, p0; xxrec_t *xxrec; char fdate[20], pdate[20], *pp; char *rating, *tags, *capt, *comms, *location, *country; char text[1000], blank[4] = " "; LVthumbsize = 128; // 18.01 fontsize = appfontsize; thumbW = LVthumbsize + 10; // thumbnail layout size thumbH = LVthumbsize + 20; texthh = 4 * fontsize * 1.8 + 20; // space for 4 lines of metadata text if (texthh > thumbH) thumbH = texthh; xwinW = gtk_widget_get_allocated_width(Gscroll); // drawing window size xwinH = gtk_widget_get_allocated_height(Gscroll); xrows = int(0.1 + 1.0 * xwinH / thumbH); // get thumbnail rows fitting in window if (xrows < 1) xrows = 1; xcols = 1; // force cols = 1 nrows = Nfiles; // thumbnail rows if (! nrows) nrows = 1; drwingW = xwinW; // layout size for entire file list if (drwingW < 800) drwingW = 800; drwingH = (nrows + 1) * thumbH; // last row if (drwingH < xwinH) drwingH = xwinH; gtk_widget_get_size_request(drwin,&ww,&hh); // current size if (ww != drwingW || hh != drwingH) gtk_widget_set_size_request(drwin,-1,drwingH); // needs to change maxscroll = nrows * thumbH; // too far but necessary if (maxscroll < xwinH) maxscroll = xwinH; // compensate GTK bug gtk_adjustment_set_upper(Gadjust,maxscroll); gtk_adjustment_set_step_increment(Gadjust,thumbH); // scrollbar works in row steps gtk_adjustment_set_page_increment(Gadjust,thumbH * xrows); // and in page steps if (galleryposn >= 0) { // new target file position (Nth) scrollposn = galleryposn / xcols * thumbH; // scroll position for target file if (scrollposn > maxscroll) scrollposn = maxscroll; // >> top row of window gtk_adjustment_set_value(Gadjust,scrollposn); gtk_widget_queue_draw(drwin); galleryposn = -1; // disable galleryposn return 1; } scrollposn = gtk_adjustment_get_value(Gadjust); // save gallery sort and position 18.01 galleryposn = (scrollposn + thumbH/2) / thumbH * xcols; gallery_memory("put"); galleryposn = -1; // disable cairo_clip_extents(cr,&x1,&y1,&x2,&y2); // window region to paint row1 = y1 / thumbH; row2 = y2 / thumbH; textww = drwingW - thumbW - 2 * margin; // space for text right of thumbnail for (row = row1; row <= row2; row++) // draw file thumbnails { ii = row; // next file if (ii >= Nfiles) break; fspec = GFlist[ii].file; p0 = *fspec; // replace possible ! with / *fspec = '/'; thumx = margin; // drawing area position thumy = row * thumbH + margin; if (curr_file && strmatch(fspec,curr_file)) { // yellow background for curr. image cairo_set_source_rgb(cr,1,1,0.5); cairo_rectangle(cr,thumx,thumy,xwinW,fontsize*1.6); cairo_fill(cr); } pxbT = get_cache_thumbnail(fspec,LVthumbsize); // get thumbnail if (pxbT) { gdk_cairo_set_source_pixbuf(cr,pxbT,thumx,thumy); // paint thumbnail cairo_paint(cr); } ftype = image_file_type(fspec); // file type if (ftype == FDIR) { // subdirectory dir_filecount(fspec,ndir,nfil); // get subdirectory and file counts snprintf(text,1000,"%s \n %d directories + %d images \n", // paint 2 lines: fspec, ndir, nfil); // directory name, file counts draw_text(cr,text,thumbW+margin,thumy,textww); } else // 18.01 { xxrec = get_xxrec(fspec); if (xxrec) { strncpy0(fdate,xxrec->fdate,20); strncpy0(pdate,xxrec->pdate,20); rating = xxrec->rating; tags = xxrec->tags; capt = xxrec->capt; comms = xxrec->comms; location = xxrec->location; country = xxrec->country; } else { // bugfix 17.08.1 strcpy(fdate,blank); strcpy(pdate,blank); rating = blank; rating = tags = capt = comms = location = country = blank; } pp = fdate; // file date if (strmatch(pp,"null")) strcpy(pp,"undated"); else { memcpy(pp+17,pp+12,2); // :ss added 18.01 memcpy(pp+14,pp+10,2); // yyyymmddhhmmss to yyyy-mm-dd hh:mm:ss memcpy(pp+11,pp+8,2); memcpy(pp+8,pp+6,2); memcpy(pp+5,pp+4,2); pp[19] = 0; pp[16] = pp[13] = ':'; pp[10] =' '; pp[4] = pp[7] = '-'; } pp = pdate; // photo date if (strmatch(pp,"null")) strcpy(pp,"undated"); else { memcpy(pp+17,pp+12,2); // :ss added 18.01 memcpy(pp+14,pp+10,2); // yyyymmddhhmmss to yyyy-mm-dd hh:mm:ss memcpy(pp+11,pp+8,2); memcpy(pp+8,pp+6,2); memcpy(pp+5,pp+4,2); pp[19] = 0; pp[16] = pp[13] = ':'; pp[10] =' '; pp[4] = pp[7] = '-'; } snprintf(text,1000, "%s \n photo date: %s file date: %s rating: %s location: %s %s \n" " tags: %s \n caption: %s \n comments: %s", fspec, pdate, fdate, rating, location, country, tags, capt, comms); draw_text(cr,text,thumbW+margin,thumy,textww); // paint text } *fspec = p0; // restore '!' } gallerypainted = 1; return 1; } // private function // find the number of subdirs and image files within a given directory void navi::dir_filecount(char *dirname, int &ndir, int &nfil) { char *file, **flist; int ii, cc, err, NF; int dcount = 0, fcount = 0; FTYPE ftype; #define NC 1000 // cache capacity static int ftf = 1; static char *dirnamecache[NC]; // cache for recent directory data static time_t modtimecache[NC]; static int dcountcache[NC]; static int fcountcache[NC]; static int pcache = 0; struct stat statb; if (ftf) { ftf = 0; cc = NC * sizeof(char *); memset(dirnamecache,0,cc); } ndir = nfil = 0; err = stat(dirname,&statb); if (err) return; for (ii = 0; ii < NC; ii++) { if (! dirnamecache[ii]) break; if (strmatch(dirname,dirnamecache[ii]) && statb.st_mtime == modtimecache[ii]) { ndir = dcountcache[ii]; nfil = fcountcache[ii]; return; } } err = find_imagefiles(dirname,1+8,flist,NF); // image files + dirs, one level 18.01 if (err) { zmessageACK(Mwin,strerror(errno)); return; } for (ii = 0; ii < NF; ii++) { file = flist[ii]; ftype = image_file_type(file); zfree(file); if (ftype == FDIR) dcount++; // directory count else if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) fcount++; // image file count 17.08 } if (NF) zfree(flist); ii = pcache++; if (pcache == NC) pcache = 0; if (dirnamecache[ii]) zfree(dirnamecache[ii]); dirnamecache[ii] = zstrdup(dirname); modtimecache[ii] = statb.st_mtime; ndir = dcountcache[ii] = dcount; nfil = fcountcache[ii] = fcount; return; } // private function // write text for thumbnail limited by width of thumbnail void navi::draw_text(cairo_t *cr, char *text, int px, int py, int ww) { static PangoFontDescription *pfont = 0; static PangoLayout *playout = 0; static int pfontsize = -1; static char thumbfont[12] = ""; if (fontsize != pfontsize) { // adjust for curr. font size pfontsize = fontsize; snprintf(thumbfont,12,"sans %d",fontsize); if (pfont) pango_font_description_free(pfont); // free memory pfont = pango_font_description_from_string(thumbfont); if (playout) g_object_unref(playout); // free memory playout = pango_cairo_create_layout(cr); pango_layout_set_font_description(playout,pfont); } pango_layout_set_width(playout,ww*PANGO_SCALE); // limit width to avail. space pango_layout_set_ellipsize(playout,PANGO_ELLIPSIZE_END); pango_layout_set_text(playout,text,-1); cairo_move_to(cr,px,py); cairo_set_source_rgb(cr,0,0,0); pango_cairo_show_layout(cr,playout); return; } // private function // create a row of navigation buttons in gallery top panel void navi::gallery_navibutts() { char labtext[100]; int ii, cc, max = maxgallerylevs; char *pp1, *pp2; for (ii = 0; ii < max; ii++) { // clear old navi buttons if any if (gallerypath[ii]) { zfree(gallerypath[ii]); gallerypath[ii] = 0; gtk_widget_destroy(gallerybutt[ii]); } } if (gallerylabel) gtk_widget_destroy(gallerylabel); // clear gallery label if any gallerylabel = 0; if (gallerytype == SEARCH) sprintf(labtext,"search results"); // search results if (gallerytype == META) sprintf(labtext,"search results"); // search results (metadata report) if (gallerytype == RECENT) sprintf(labtext,"recent images"); // recent images if (gallerytype == NEWEST) sprintf(labtext,"newest images"); // newest images if (gallerytype == ALBUM) { // album gallery pp1 = strrchr(galleryname,'/'); if (pp1) pp1++; else pp1 = galleryname; snprintf(labtext,100,"album: %s",pp1); // album: album-name } if (gallerytype != GDIR) { // not a directory gallery gallerylabel = gtk_label_new(labtext); // show gallery label gtk_box_pack_start(GTK_BOX(Gpanel),gallerylabel,0,0,0); gtk_widget_show_all(Gpanel); return; } ii = 0; pp1 = galleryname; while (true) // construct new buttons { pp2 = strchr(pp1+1,'/'); // /aaaaaa/bbbbbb/cccccc if (pp2) cc = pp2 - pp1; // | | else cc = strlen(pp1); // pp1 pp2 gallerypath[ii] = (char *) zmalloc(cc); strncpy0(gallerypath[ii],pp1+1,cc); // bbbbbb gallerybutt[ii] = gtk_button_new_with_label(gallerypath[ii]); gtk_box_pack_start(GTK_BOX(Gpanel),gallerybutt[ii],0,0,3); G_SIGNAL(gallerybutt[ii],"clicked",navibutt_clicked,&Nval[ii]); pp1 = pp1 + cc; // next directory level /cccccc if (! *pp1) break; // null = end if (*pp1 == '/' && ! *(pp1+1)) break; // / + null = end if (++ii == max) break; // limit of directory levels } gtk_widget_show_all(Gpanel); return; } // private function - menu function for gallery window // - scroll window as requested // - jump to new file or folder as requested void navi::menufuncx(GtkWidget *, cchar *menu) { int ii, scroll1, scroll2; if (FGWM != 'G') return; // 18.01 gallery_scroll(-1,0); // stop scrolling if (! gallerypainted) return; // wait for pending paint scrollposn = gtk_adjustment_get_value(Gadjust); // current scroll position if (strmatch(menu,ZTX("GoTo"))) { F1_help_topic = "bookmarks"; m_goto_bookmark(0,0); return; } if (strmatch(menu,ZTX("Sort"))) { // choose sort order and sort gallery F1_help_topic = "sort_gallery"; gallery_sort(); return; } if (strmatch(menu,ZTX("Zoom+"))) { // next bigger thumbnail size for (ii = 0; ii < thumbxx; ii++) if (thumbsize == thumbx[ii]) break; if (ii == 0) return; thumbsize = thumbx[ii-1]; galleryposn = scrollposn / thumbH * xcols; // keep top row position gallery(0,"paint",-1); // 17.08 return; } if (strmatch(menu,ZTX("Zoom-"))) { // next smaller for (ii = 0; ii < thumbxx; ii++) if (thumbsize == thumbx[ii]) break; if (ii >= thumbxx-1) thumbsize = 0; // no thumbs, list view else thumbsize = thumbx[ii+1]; if (thumbsize < 256 && gallerytype == META) // min. for metadata report 17.08 thumbsize = 256; galleryposn = scrollposn / thumbH * xcols; // keep top row position gallery(0,"paint",-1); // 17.08 return; } if (strmatch(menu,ZTX("Row Up"))) { // scroll 1 row up scroll1 = scrollposn / thumbH * thumbH; scroll2 = scroll1 - thumbH; if (scroll2 < 0) scroll2 = 0; gallery_scroll(scroll2,3000); return; } if (strmatch(menu,ZTX("Row Down"))) { // scroll 1 row down scroll1 = scrollposn / thumbH * thumbH; scroll2 = scroll1 + thumbH; if (scroll2 > maxscroll) scroll2 = maxscroll; gallery_scroll(scroll2,3000); return; } if (strmatch(menu,ZTX("Page Up"))) { // scroll 1 page up scroll1 = scrollposn / thumbH * thumbH; scroll2 = scroll1 - thumbH * xrows; if (scroll2 < 0) scroll2 = 0; gallery_scroll(scroll2,5000); return; } if (strmatch(menu,ZTX("Page Down"))) { // scroll 1 page down scroll1 = scrollposn / thumbH * thumbH; scroll2 = scroll1 + thumbH * xrows; if (scroll2 > maxscroll) scroll2 = maxscroll; gallery_scroll(scroll2,5000); return; } if (strmatch(menu,ZTX("First"))) { // jump to top scrollposn = 0; galleryposn = scrollposn / thumbH * xcols; gallery(0,"paint",-1); // 17.08 return; } if (strmatch(menu,ZTX("Last"))) { // jump to bottom scrollposn = maxscroll; galleryposn = scrollposn / thumbH * xcols; gallery(0,"paint",-1); // 17.08 return; } printz("unknown gallery function: %s \n",menu); // 18.01 return; } // private function // scroll gallery page up or down to goal scroll position // position: N goal scroll position, 0 to maxscroll // -1 stop scrolling immediately // speed: N scroll N pixels/second void navi::gallery_scroll(int position, int speed) { if (position < 0) { // stop scrolling gallery_scrollgoal = -1; gallery_scrollspeed = 0; return; } if (gallery_scrollgoal < 0) { // start scrolling gallery_scrollgoal = position; gallery_scrollspeed = speed; g_timeout_add(4,gallery_scrollfunc,0); // 4 millisec. timer period 17.01 return; } gallery_scrollgoal = position; // continue scrolling with gallery_scrollspeed = speed; // possible goal/speed change return; } // private function // timer function, runs every 4 milliseconds int navi::gallery_scrollfunc(void *) { static float cumscroll = 0; if (gallery_scrollgoal < 0) { // stop scrolling gallery_scrollspeed = 0; cumscroll = 0; return 0; } if (FGWM != 'G') { // not gallery view gallery_scrollgoal = -1; // stop scrolling gallery_scrollspeed = 0; cumscroll = 0; return 0; } if (scrollposn == gallery_scrollgoal) { // goal reached, stop gallery_scrollgoal = -1; gallery_scrollspeed = 0; cumscroll = 0; return 0; } cumscroll += 0.004 * gallery_scrollspeed; // based on 4 millisec. timer 17.01 if (cumscroll < 4.0) return 1; // not yet 4 pixels 17.01 if (scrollposn < gallery_scrollgoal) { // adjust scroll position scrollposn += cumscroll; if (scrollposn > gallery_scrollgoal) scrollposn = gallery_scrollgoal; } if (scrollposn > gallery_scrollgoal) { scrollposn -= cumscroll; if (scrollposn < gallery_scrollgoal) scrollposn = gallery_scrollgoal; } gtk_adjustment_set_value(Gadjust,scrollposn); cumscroll = 0; return 1; } // private function // gallery top panel directory button clicked, open corresponding directory void navi::navibutt_clicked(GtkWidget *widget, int *lev) { char gallerydir[XFCC], *pp; gallery_scroll(-1,0); // stop scrolling pp = gallerydir; for (int ii = 0; ii <= *lev; ii++) { *pp = '/'; strcpy(pp+1,gallerypath[ii]); pp = pp + strlen(pp); } gallery(gallerydir,"init",0); // new gallery 18.01 gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint return; } // private function - [TOP] button: select new top directory void navi::newtop(GtkWidget *widget, GdkEventButton *event) // 17.04 { static GtkWidget *popmenu = 0; gallery_scroll(-1,0); // stop scrolling if (popmenu) gtk_widget_destroy(popmenu); popmenu = create_popmenu(); for (int ii = 0; ii < Ntopdirks; ii++) // insert all top image directories add_popmenu_item(popmenu,topdirks[ii],newtop_menu_event,0,0); add_popmenu_item(popmenu,"ALL",newtop_menu_event,0,0); add_popmenu_item(popmenu,"/",newtop_menu_event,0,0); add_popmenu_item(popmenu,"HOME",newtop_menu_event,0,0); add_popmenu_item(popmenu,"Desktop",newtop_menu_event,0,0); add_popmenu_item(popmenu,"Fotoxx home",newtop_menu_event,0,0); add_popmenu_item(popmenu,"Saved Areas",newtop_menu_event,0,0); add_popmenu_item(popmenu,ZTX("recent images"),newtop_menu_event,0,0); add_popmenu_item(popmenu,ZTX("newest images"),newtop_menu_event,0,0); popup_menu(Mwin,popmenu); return; } void navi::newtop_menu_event(GtkWidget *, cchar *menu) // menu event function 17.04 { char dirk[200]; if (! menu) return; strncpy0(dirk,menu,200); if (strmatch(menu,"ALL")) { m_alldirs(0,0); return; } if (strmatch(menu,ZTX("recent images"))) { m_recentfiles(0,0); return; } if (strmatch(menu,ZTX("newest images"))) { m_newfiles(0,0); return; } if (strmatch(menu,"HOME")) // user home directory strncpy0(dirk,getenv("HOME"),200); if (strmatch(menu,"Desktop")) // user desktop snprintf(dirk,200,"%s/Desktop",getenv("HOME")); if (strmatch(menu,"Fotoxx home")) // fotoxx home directory strncpy0(dirk,get_zhomedir(),200); if (strmatch(menu,"Saved Areas")) // saved areas directory 17.04 snprintf(dirk,200,"%s/saved_areas",get_zhomedir()); gallery(dirk,"init",0); // new gallery 18.01 gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint return; } // private function - [Album] button: select new album void navi::newalbum(GtkWidget *widget, GdkEventButton *event) // 17.04 { static GtkWidget *popmenu = 0; cchar *findcomm = "find -L \"%s\" -type f"; char *albums[100]; char *file, *pp; int contx = 0, count = 0; gallery_scroll(-1,0); // stop scrolling while ((file = command_output(contx,findcomm,albums_dirk))) // find all album files { if (count > 99) { zmessageACK(Mwin,Btoomanyfiles,100); break; } pp = strrchr(file,'/'); if (! pp) continue; albums[count] = zstrdup(pp+1); zfree(file); count++; } if (file) command_kill(contx); if (! count) { zmessageACK(Mwin,ZTX("no albums found")); return; } if (count > 1) // sort album names HeapSort(albums,count); if (popmenu) gtk_widget_destroy(popmenu); popmenu = create_popmenu(); for (int ii = 0; ii < count; ii++) // insert all top image directories add_popmenu_item(popmenu,albums[ii],newalbum_menu_event,0,0); popup_menu(Mwin,popmenu); return; } void navi::newalbum_menu_event(GtkWidget *, cchar *menu) // menu event function 17.04 { char albumfile[200]; snprintf(albumfile,200,"%s/%s",albums_dirk,menu); // show the album gallery album_show(albumfile); return; } // private function // dialog to choose gallery sort order and sort the gallery void navi::gallery_sort() { zdialog *zd; int zstat, nn; if (gallerytype == ALBUM) { zmessageACK(Mwin,ZTX("Albums cannot be sorted")); // 18.01 return; } cchar *resetmess = ZTX(" Reset all galleries\n to file name ascending"); /*** ________________________________ | Gallery Sort | | | | (o) File Name | | (o) File Mod Date/Time | | (o) Photo Date/Time (EXIF) | | (o) ascending (o) descending | | | | [x] reset all galleries | | to file name ascending | | | | [Apply] | |________________________________| ***/ zd = zdialog_new(ZTX("Gallery Sort"),Mwin,Bapply,null); // user dialog zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"label","space","hb1",0,"space=2"); zdialog_add_widget(zd,"vbox","vb1","hb1"); zdialog_add_widget(zd,"radio","filename","vb1",ZTX("File Name")); zdialog_add_widget(zd,"radio","filedate","vb1",ZTX("File Mod Date/Time")); zdialog_add_widget(zd,"radio","photodate","vb1",ZTX("Photo Date/Time (EXIF)")); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"radio","ascending","hb2",ZTX("ascending"),"space=4"); zdialog_add_widget(zd,"radio","descending","hb2",ZTX("descending"),"space=2"); zdialog_add_widget(zd,"hbox","hbreset","dialog",0,"space=5"); zdialog_add_widget(zd,"check","reset","hbreset",resetmess,"space=4"); zdialog_stuff(zd,"filename",1); // default, file name ascending zdialog_stuff(zd,"filedate",0); zdialog_stuff(zd,"photodate",0); zdialog_stuff(zd,"ascending",1); zdialog_stuff(zd,"descending",0); zdialog_stuff(zd,"reset",0); if (gallerysort == FNAME) { // GTK radio buttons not reliable 18.01 zdialog_stuff(zd,"filename",1); // (vbox works, hbox does not) zdialog_stuff(zd,"filedate",0); zdialog_stuff(zd,"photodate",0); } if (gallerysort == FDATE) { zdialog_stuff(zd,"filename",0); zdialog_stuff(zd,"filedate",1); zdialog_stuff(zd,"photodate",0); } if (gallerysort == PDATE) { zdialog_stuff(zd,"filename",0); zdialog_stuff(zd,"filedate",0); zdialog_stuff(zd,"photodate",1); } if (galleryseq == ASCEND) { zdialog_stuff(zd,"ascending",1); zdialog_stuff(zd,"descending",0); } if (galleryseq == DESCEND) { zdialog_stuff(zd,"ascending",0); zdialog_stuff(zd,"descending",1); } zdialog_set_modal(zd); // 17.08 zdialog_run(zd,0,"mouse"); // run dialog, wait for completion zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"filename",nn); // get user choice if (nn) gallerysort = FNAME; zdialog_fetch(zd,"filedate",nn); if (nn) gallerysort = FDATE; zdialog_fetch(zd,"photodate",nn); if (nn) gallerysort = PDATE; zdialog_fetch(zd,"ascending",nn); if (nn) galleryseq = ASCEND; else galleryseq = DESCEND; zdialog_fetch(zd,"reset",nn); // reset all gallery sort memory 18.01 if (nn) { // (revert to file name ascending) gallery_memory("reset"); gallerysort = FNAME; galleryseq = ASCEND; } zdialog_free(zd); gallery(0,"sort",-1); // sort the gallery 18.01 gallery(0,"paint",0); // paint, position = 0 return; } // private function // mouse event function for gallery window - get selected thumbnail and file // user function receives clicked file, which is subject for zfree() void navi::mouse_event(GtkWidget *widget, GdkEvent *event, void *) { GdkEventButton *eventB; PIXBUF *pxbT; int evtype, mousex, mousey, mousebutt; int row, col, nrows, tww, thh, marg; int Nth, poswidth, posheight, err, ftype; static int Fmyclick = 0; // 17.08 char *filez = 0; STATB statb; if (! Nfiles) return; // empty gallery if (! gallerypainted) return; // not initialized eventB = (GdkEventButton *) event; evtype = eventB->type; mousex = int(eventB->x); mousey = int(eventB->y); mousebutt = eventB->button; if (mousex < margin) return; if (mousey < margin) return; KBcontrolkey = KBshiftkey = KBaltkey = 0; if (eventB->state & GDK_CONTROL_MASK) KBcontrolkey = 1; if (eventB->state & GDK_SHIFT_MASK) KBshiftkey = 1; if (eventB->state & GDK_MOD1_MASK) KBaltkey = 1; // button 1 + Alt handled by window manager row = (mousey - margin) / thumbH; // find selected row, col col = (mousex - margin) / thumbW; if (thumbsize) { poswidth = (mousex - margin) - thumbW * col; // mouse position within thumbnail poswidth = 100 * poswidth / thumbsize; // 0-100 = left to right edge posheight = (mousey - texthh - margin) - thumbH * row; posheight = 100 * posheight / thumbsize; // 0-100 = top to bottom edge } else poswidth = posheight = 0; if (! xcols) return; nrows = 1 + (Nfiles-1) / xcols; // total thumbnail rows, 1 or more if (col < 0 || col >= xcols) return; // mouse not on a thumbnail if (row < 0 || row >= nrows) return; Nth = xcols * row + col; // mouse at this thumbnail (image file) if (Nth >= Nfiles) return; filez = zstrdup(GFlist[Nth].file); // file (thumbnail) at mouse posn. *filez = '/'; if (evtype == GDK_BUTTON_PRESS) { gallery_scroll(-1,0); // stop scrolling Fmyclick = 1; // button press is mine 17.08 if (drag_file) zfree(drag_file); drag_file = 0; ftype = image_file_type(filez); // save for poss. drag-drop if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) { // 17.08 drag_file = zstrdup(filez); // save file and position in gallery drag_posn = Nth; } goto cleanup; } if (evtype == GDK_BUTTON_RELEASE) { gallery_scroll(-1,0); // stop scrolling if (! Fmyclick) goto cleanup; // ignore unmatched button release 17.08 Fmyclick = 0; // (from vanished popup window) err = stat(filez,&statb); if (err) goto cleanup; // file is gone? if (S_ISDIR(statb.st_mode)) { // if directory, go there gallery(filez,"init",0); // new gallery 18.01 gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint goto cleanup; } if (clicked_file) zfree(clicked_file); // save clicked file and gallery position clicked_file = zstrdup(filez); clicked_posn = Nth; clicked_width = poswidth; // normalized 0-100 clicked_height = posheight; if (thumbsize) { pxbT = get_cache_thumbnail(filez,thumbsize); // get thumbnail image if (pxbT) { tww = gdk_pixbuf_get_width(pxbT); // thumbnail width and height thh = gdk_pixbuf_get_height(pxbT); marg = (thumbsize - tww) / 4; // allow for margin offset poswidth -= 100 * marg/thumbsize; clicked_width = poswidth * thumbsize / tww; // clicked position is relative clicked_height = posheight * thumbsize / thh; // to thumbnail dimensions if (clicked_width < 0) clicked_width = 0; if (clicked_width > 100) clicked_width = 100; if (clicked_height < 0) clicked_height = 0; if (clicked_height > 100) clicked_height = 100; } } if (mousebutt == 1) { // left click if (zd_gallery_select) gallery_select_Lclick_func(Nth); // send to gallery_select() else if (zd_gallery_select1) gallery_select1_Lclick_func(Nth); // send to gallery_select1() 17.08 else if (zd_edit_bookmarks) bookmarks_Lclick_func(Nth); // send to bookmarks editor else if (zd_ss_imageprefs) ss_imageprefs_Lclick_func(Nth); // send to slide show editor else if (KBshiftkey) popup_image(filez,MWIN,1,0); // popup enlarged image else if (zdexifview) meta_view(0); // EXIF/IPTC data view window else if (zdcopymove) m_copy_move(0,0); // copy/move dialog else if (zdrename) m_rename(0,0); // rename dialog else if (zddeltrash) m_delete_trash(0,0); // delete/trash dialog else if (zdeditmeta) m_meta_edit(0,0); // edit metadata dialog else if (zdeditanymeta) m_meta_edit_any(0,0); // edit any metadata dialog else if (zddeletemeta) m_meta_delete(0,0); // delete metadata dialog else if (zdupright) m_upright(0,0); // upright image else gallery_Lclick_func(Nth); // open the file goto cleanup; } if (mousebutt == 2) { // middle click ftype = image_file_type(clicked_file); if (ftype == IMAGE) gallery_popimage(); if (ftype == RAW) gallery_popimage(); } if (mousebutt == 3) { // right click if (zd_gallery_select) gallery_select_Rclick_func(Nth); // send to gallery_select() else gallery_Rclick_popup(Nth); // send to gallery thumbnail popup menu goto cleanup; } } cleanup: zfree(filez); return; } // this function is called if a drag-drop is initiated from the gallery window char * navi::gallery_dragfile() { drag_gallery = galleryname; return drag_file; } // this function is called if a drag-drop file is dropped on the gallery window void navi::gallery_dropfile(int mousex, int mousey, char *file) { int err, top, mpos, speed; int row, col, nrows, Nth; int poswidth; if (gallerytype != GDIR && gallerytype != ALBUM) return; // reject others (search, recent ...) if (! file) // drag motion underway { top = gtk_adjustment_get_value(Gadjust); // mouse vertical posn in window mpos = 100 * (mousey - top) / xwinH; // 0 - 100 if (mpos < 15 && top > 0) { // mouse position from window top to bottom if (mpos < 0) mpos = 0; // 0 .... 15 .......... 85 .... 100 speed = 200 * (15 - mpos); // corresponding scroll speed gallery_scroll(0,speed); // 4000 ... 200 ... 0 ... 200 ... 4000 } // down down up up if (mpos > 85 && top < maxscroll) { if (mpos >= 100) mpos = 100; speed = 200 * (mpos - 85); gallery_scroll(maxscroll,speed); } if (mpos >= 15 && mpos <= 85) // stop scrolling in mid-window range gallery_scroll(-1,0); return; } gallery_scroll(-1,0); // stop scrolling 17.04 if (gallerytype == GDIR) // directory gallery, add new file { err = copyFile(file,galleryname); // 17.08 zfree(file); // 18.01 if (err) { zmessageACK(Mwin,strerror(err)); return; } gallery(0,"init",0); // refresh gallery 18.01 gallery(0,"sort",-2); // sort, keep position gallery(0,"paint",-1); // paint return; } row = (mousey - margin) / thumbH; // find drop location: row, col col = (mousex - margin) / thumbW; if (col < 0) col = 0; if (col >= xcols) col = xcols-1; if (xcols) nrows = 1 + (Nfiles-1) / xcols; // total thumbnail rows, 1 or more else nrows = 1; if (nrows < 1) nrows = 1; // 17.04 if (row < 0) row = 0; if (row >= nrows) row = nrows-1; if (thumbsize) { poswidth = (mousex - margin) - thumbW * col; // mouse position within thumbnail poswidth = 100 * poswidth / thumbsize; // 0-100 = left to right edge } else poswidth = 0; Nth = xcols * row + col; // mouse at this thumbnail (image file) if (poswidth > 50) Nth++; // drop after this position if (Nth > Nfiles) Nth = Nfiles; // last + 1 17.04 if (drag_gallery && strmatch(galleryname,drag_gallery)) { // drag-drop in same gallery album_movefile(drag_posn,Nth); // move file position in gallery drag_gallery = 0; } else album_pastefile(file,Nth); // insert new file at position album_show(); return; } // Private function - respond to keyboard navigation keys. // KBpress() for main window calls this function when G view is active. int navi::KBaction(cchar *action) { int ii; int row1, row2, rowf; gallery_scroll(-1,0); // stop scrolling if (strmatch(action,"Zoom-in")) { menufuncx(0,"Zoom+"); return 1; } if (strmatch(action,"Zoom-out")) { menufuncx(0,"Zoom-"); return 1; } if (strmatch(action,"Up")) { menufuncx(0,"Row Up"); return 1; } if (strmatch(action,"Down")) { menufuncx(0,"Row Down"); return 1; } if (strmatch(action,"First")) { menufuncx(0,"First"); return 1; } if (strmatch(action,"Last")) { menufuncx(0,"Last"); return 1; } if (strmatch(action,"Page_Up")) { menufuncx(0,"Page Up"); return 1; } if (strmatch(action,"Page_Down")) { menufuncx(0,"Page Down"); return 1; } if (strmatch(action,"Left")) { // left arrow - previous image 18.01 m_prev(0,0); row1 = scrollposn / thumbH; rowf = curr_file_posn / xcols; if (rowf < row1) menufuncx(0,"Row Up"); return 1; } if (strmatch(action,"Right")) { // right arrow - next image 18.01 m_next(0,0); row1 = scrollposn / thumbH; row2 = row1 + xwinH / thumbH - 1; rowf = curr_file_posn / xcols; if (rowf == 0) menufuncx(0,"First"); if (rowf > row2) menufuncx(0,"Row Down"); return 1; } if (strmatch(action,"Delete")) { // delete key - delete/trash dialog 18.01 m_delete_trash(0,0); return 1; } if (strmatch(action,"Show Hidden")) { Fshowhidden = 1 - Fshowhidden; gallery(0,"init",0); gallery(0,"paint",-1); return 1; } // check for menu function shortcut // 18.01 for (ii = 0; ii < Nmenus; ii++) { if (! menutab[ii].menu) continue; // ignore separator 'menu' if (strmatchcase(action,ZTX(menutab[ii].menu))) break; } if (ii == Nmenus) { printz("shortcut not found: %s \n",action); return 1; } menutab[ii].func(0,menutab[ii].arg); // call the menu function return 1; } // save and restore gallery sort and top file position #define RGmax 100 // max. recent galleries to remember typedef struct { // gallery memory data int galleryposn; // top file position (scroll position) int gallerysort; // sort galleryname/filedate/photodate int galleryseq; // sort ascending/descending char *galleryname; // gallery name /.../filename } gallerymem_t; gallerymem_t gallerymem[RGmax]; // array of gallery memory gallerymem_t Tgallerymem; int NGmem; // current entries <= RGmax void gallery_memory(cchar *action) // 18.01 { FILE *fid; char buff[XFCC], *pp; int ii, nn, err; if (strmatch(action,"reset")) // clear gallery memory data { NGmem = 0; return; } if (strmatch(action,"load")) // load gallery memory from file { // at Fotoxx startup time NGmem = 0; fid = fopen(gallerymem_file,"r"); // open gallery memiry file if (fid) { for (ii = 0; ii < RGmax; ii++) { pp = fgets_trim(buff,XFCC,fid); // read gallery memory record if (! pp) break; // NNNNNN N N /directory/name/... err = convSI(pp,nn,0,999999); // NNNNNN = gallery top file position if (err) break; gallerymem[ii].galleryposn = nn; err = convSI(pp+7,nn,0,3); // N = 0/1/2/3 = sort if (err) break; // none / f.name / f.date / photo-date gallerymem[ii].gallerysort = nn; err = convSI(pp+9,nn,0,2); // N = 0/1/2 = none / ascend / descend if (err) break; gallerymem[ii].galleryseq = nn; pp += 11; // gallery name (directory) if (*pp != '/') break; gallerymem[ii].galleryname = zstrdup(pp); } fclose(fid); NGmem = ii; // memory entry count } return; } if (strmatch(action,"save")) // save gallery memory at shutdown { fid = fopen(gallerymem_file,"w"); if (! fid) return; for (ii = 0; ii < NGmem; ii++) fprintf(fid,"%06d %1d %1d %s\n", gallerymem[ii].galleryposn, gallerymem[ii].gallerysort, gallerymem[ii].galleryseq, gallerymem[ii].galleryname); fclose(fid); return; } if (! galleryname) return; if (strmatch(action,"get")) // get gallery data from memory { for (ii = 0; ii < NGmem; ii++) // search for gallery in memory if (strmatch(galleryname,gallerymem[ii].galleryname)) break; if (ii < NGmem) { galleryposn = gallerymem[ii].galleryposn; // found, restore top file posn and sort gallerysort = (GSORT) gallerymem[ii].gallerysort; galleryseq = (GSEQ) gallerymem[ii].galleryseq; } else { // not found, use defaults if (gallerytype == GDIR) { gallerysort = FNAME; galleryseq = ASCEND; galleryposn = 0; } else { gallerysort = SNONE; galleryseq = QNONE; galleryposn = 0; } } return; } if (strmatch(action,"put")) // save gallery data in memory { if (! galleryname) return; for (ii = 0; ii < NGmem; ii++) // search for gallery in memory if (strmatch(galleryname,gallerymem[ii].galleryname)) break; if (NGmem == 0 || ii == NGmem) // not found { if (NGmem == RGmax) { // gallery memory full zfree(gallerymem[ii-1].galleryname); // remove last entry NGmem--; } for (ii = NGmem; ii > 0; ii--) // push all entries up gallerymem[ii] = gallerymem[ii-1]; // to free entry [0] NGmem++; // one more entry gallerymem[0].galleryname = zstrdup(galleryname); // entry [0] is mine } else if (ii > 0) // found at entry [ii] { Tgallerymem = gallerymem[0]; // exchange entries [0] and [ii] gallerymem[0] = gallerymem[ii]; gallerymem[ii] = Tgallerymem; } gallerymem[0].galleryposn = galleryposn; // update entry [0] gallerymem[0].gallerysort = gallerysort; gallerymem[0].galleryseq = galleryseq; return; } zappcrash("gallery_memory() %s",action); // bad action return; } // set the window title for the gallery window // window title = gallery name void set_gwin_title() { char *pp, title[200]; if (FGWM != 'G') return; if (gallerytype == GDIR) snprintf(title,200,"DIRECTORY %s %d files",galleryname,Nfiles); else if (gallerytype == SEARCH || gallerytype == META) snprintf(title,200,"SEARCH RESULTS %d files",Nimages); else if (gallerytype == ALBUM) { pp = strrchr(galleryname,'/'); if (! pp) pp = galleryname; else pp++; snprintf(title,200,"ALBUM %s %d files",pp,Nimages); } else if (gallerytype == RECENT) strcpy(title,"RECENT FILES"); else if (gallerytype == NEWEST) strcpy(title,"NEWEST FILES"); else strcpy(title,"UNKNOWN"); gtk_window_set_title(MWIN,title); return; } // Return previous or next image file from curr_file in the gallery file list. // If lastver is set, only the last edit version is returned. // (gallery must be sorted by file name (version sequence)). // Returns null if no previous/next file found. // returned file is subject for zfree(). char * gallery_getnext(int index, int lastver) // overhauled { char * get_rootname(char *file); char * prev_next_gallery(int index); int Nth; char *rootname1 = 0, *rootname2 = 0; // file names without .vNN and .ext char *file = 0, *filever = 0; Nth = curr_file_posn; if (index == +1) // get next file { while (true) { Nth += 1; if (Nth >= Nfiles) { // no more files this gallery if (filever) break; // return last file version goto retnull; // no more files } if (file) zfree(file); file = gallery(0,"get",Nth); // get next file if (! file) goto retnull; if (! lastver) goto retfile; // return all versions if (! filever) { filever = file; // potential last version file = 0; rootname1 = get_rootname(filever); // save rootname continue; } if (rootname2) zfree(rootname2); rootname2 = get_rootname(file); if (! strmatch(rootname1,rootname2)) break; // new rootname, filever was last version zfree(filever); filever = file; // save last file with same rootname file = 0; } if (file) zfree(file); file = filever; goto retfile; } if (index == -1) // get previous file { if (curr_file) rootname1 = get_rootname(curr_file); // current file rootname while (true) { Nth -= 1; if (Nth < Nsubdirs) goto retnull; // no more files if (file) zfree(file); file = gallery(0,"get",Nth); // get previous file if (! file) goto retnull; if (! lastver) goto retfile; // return all versions if (! rootname1) goto retfile; // no current file - return previous file if (rootname2) zfree(rootname2); rootname2 = get_rootname(file); if (! strmatch(rootname1,rootname2)) goto retfile; // new rootname, return this file } } retnull: if (file) zfree(file); file = 0; retfile: if (rootname1) zfree(rootname1); if (rootname2) zfree(rootname2); return file; } // get the root file name of a file: /.../filename without .vNN and .ext char * get_rootname(char *file) { char *rootname, *pp; rootname = zstrdup(file); // /.../filename.ext (or) filename.vNN.ext pp = strrchr(rootname,'.'); // | | if (! pp) return rootname; // pp pp if (strmatchN(pp-4,".v",2)) pp -= 4; *pp = 0; return rootname; } // Find the previous or next gallery from the current gallery. // (previous/next defined by subdirectory sequence in parent directory) // returned gallery is subject for zfree(). char * prev_next_gallery(int index) // overhauled { int nn, Nth; char *parentdir = 0, *olddir = 0, *newdir = 0, *file = 0; if (gallerytype != GDIR) goto errret; // gallery not a physical directory olddir = zstrdup(galleryname); // olddir = current gallery / directory if (! olddir) goto errret; nn = strlen(olddir) - 1; if (olddir[nn] == '/') olddir[nn] = 0; parentdir = zstrdup(olddir); // get parent directory for ( ; nn && parentdir[nn] != '/'; nn--) if (! nn) goto errret; parentdir[nn] = 0; gallery(parentdir,"init",0); // gallery = parent for (Nth = 0; Nth < Nsubdirs; Nth++) { // find olddir in parent if (file) zfree(file); file = gallery(0,"get",Nth); if (! file) goto errret; if (strmatch(file,olddir)) break; } Nth += index; // previous or next directory if (Nth < 0 || Nth >= Nsubdirs) goto errret; newdir = gallery(0,"get",Nth); if (newdir) goto okret; errret: if (newdir) zfree(newdir); newdir = 0; okret: if (olddir) { gallery(olddir,"init",0); // restore old directory 18.01 gallery(0,"sort",-2); // recall sort and position zfree(olddir); } if (parentdir) zfree(parentdir); if (file) zfree(file); return newdir; } // Get file position in gallery file list. // If Nth position matches file, this is returned. // Otherwise the list is searched from position 0. // Position 0-last is returned if found, or -1 if not. int file_position(cchar *file, int Nth) // 18.01 { int ii; if (! Nimages) return -1; if (! file) return -1; if (Nth >= Nsubdirs && Nth < Nfiles) if (strmatch(file,GFlist[Nth].file)) return Nth; for (ii = Nsubdirs; ii < Nfiles; ii++) if (strmatch(file,GFlist[ii].file)) break; if (ii < Nfiles) return ii; return -1; } // Determine if a file is a directory or a supported image file type FTYPE image_file_type(cchar *file) { int err, xcc, tcc; static int ftf = 1, tdcc = 0; cchar *ppx; char ppx2[8], *ppt; STATB statbuf; if (! file) return FNF; err = stat(file,&statbuf); if (err) return FNF; if (S_ISDIR(statbuf.st_mode)) return FDIR; // directory if (! S_ISREG(statbuf.st_mode)) return OTHER; // not a regular file if (ftf) { if (thumbdirk && *thumbdirk == '/') tdcc = strlen(thumbdirk); myRAWtypes = zstrdup(" "); // clear cache of found file types myVIDEOtypes = zstrdup(" "); ftf = 0; } if (tdcc && strmatchN(file,thumbdirk,tdcc)) return THUMB; // fotoxx thumbnail ppx = strrchr(file,'.'); if (! ppx) return OTHER; // no file .ext xcc = strlen(ppx); if (xcc > 6) return OTHER; // file .ext > 6 chars. strcpy(ppx2,ppx); // add trailing blank: ".ext " strcpy(ppx2+xcc," "); if (strcasestr(imagefiletypes,ppx2)) return IMAGE; // supported image type if (strcasestr(myRAWtypes,ppx2)) return RAW; // one of my RAW types if (strcasestr(myVIDEOtypes,ppx2)) return VIDEO; // one of my VIDEO types 17.08 if (strcasestr(RAWfiletypes,ppx2)) { // found in list of known RAW types tcc = strlen(myRAWtypes) + xcc + 2; ppt = (char *) zmalloc(tcc); // add to cache of my RAW types strcpy(ppt,ppx2); strcpy(ppt+xcc+1,myRAWtypes); zfree(myRAWtypes); myRAWtypes = ppt; return RAW; } if (! Fvideo) return OTHER; // missing dependencies for videos 17.08 if (strcasestr(VIDEOfiletypes,ppx2)) { // found in known VIDEO types 17.08 tcc = strlen(myVIDEOtypes) + xcc + 2; ppt = (char *) zmalloc(tcc); // add to cache of my VIDEO types strcpy(ppt,ppx2); strcpy(ppt+xcc+1,myVIDEOtypes); zfree(myVIDEOtypes); myVIDEOtypes = ppt; return VIDEO; } return OTHER; // not a known image file type } /********************************************************************************/ // Given a thumbnail file, get the corresponding image file. // Returns null if no image file found. // Returned file is subject for zfree(). // image file: /image/dirk/file.xxx // thumb dirk: /thumb/dirk // thumb file: /thumb/dirk/image/dirk/file.xxx.jpeg char * thumb2imagefile(cchar *thumbfile) // simplified { STATB statb; int err; uint cc; char *imagefile; if (! thumbdirk || *thumbdirk != '/') { // 18.01 zmessageACK(Mwin,"no thumbnail directory"); quitxx(); } cc = strlen(thumbdirk); if (cc > strlen(thumbfile) - 12) { // /thumbdirk/imagedirk/file.xxx.jpeg printz("invalid thumbfile: %s \n",thumbfile); return 0; } imagefile = zstrdup(thumbfile+cc); // /imagedirk/file.xxx.jpeg cc = strlen(imagefile); imagefile[cc-5] = 0; // /imagedirk/file.xxx err = stat(imagefile,&statb); // check file exists if (! err) return imagefile; // return image file zfree(imagefile); // not found return 0; } // Given an image file, get the corresponding thumbnail file. // The filespec is returned whether or not the file exists. // Returned file is subject for zfree(). char * image2thumbfile(cchar *imagefile) // simplified { int cc1, cc2; char *thumbfile; if (! thumbdirk || *thumbdirk != '/') { // 18.01 zmessageACK(Mwin,"no thumbnail directory"); quitxx(); } cc1 = strlen(thumbdirk); cc2 = strlen(imagefile); thumbfile = (char *) zmalloc(cc1+cc2+6); strcpy(thumbfile,thumbdirk); // /thumb/dirk strcpy(thumbfile+cc1,imagefile); // /thumb/dirk/image/dirk/file.xxx strcpy(thumbfile+cc1+cc2,".jpeg"); // /thumb/dirk/image/dirk/file.xxx.jpeg return thumbfile; } /********************************************************************************/ // check if thumbnail file is missing or stale. // returns 1 if new thumbnail not needed, 0 if new thumbnail needed. int thumbnail_OK(cchar *imagefile) // 18.01 { int err, ftype; STATB statf, statb; char *thumbfile; err = stat(imagefile,&statf); if (err) return 1; // file does not exist ftype = image_file_type(imagefile); if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) return 1; // not an image file or RAW file thumbfile = image2thumbfile(imagefile); // get thumbnail file for image file if (! thumbfile) return 0; // should not happen err = stat(thumbfile,&statb); // thumbfile exists and up to date ? if (! err && statb.st_mtime >= statf.st_mtime) return 1; // yes return 0; } /********************************************************************************/ // create or replace thumbnail file if missing or stale. // returns 1 if new thumbnail file created, 0 if not. int update_thumbnail_file(cchar *imagefile) { STATB statb; PIXBUF *thumbpxb = 0; GError *gerror = 0; int err; char *thumbfile, *pp; if (thumbnail_OK(imagefile)) return 0; thumbfile = image2thumbfile(imagefile); // get thumbnail file for image file if (! thumbfile) return 0; // should not happen pp = strrchr(thumbfile,'/'); // check if thumbnail directory exists *pp = 0; err = stat(thumbfile,&statb); *pp = '/'; if (err) { // no pp = thumbfile; while (true) { pp = strchr(pp+1,'/'); // check each directory level if (! pp) break; *pp = 0; err = stat(thumbfile,&statb); if (err) { err = mkdir(thumbfile,0750); // if missing, try to create if (err) { printz("create thumbnail: %s %d %s \n", // if unable, print error once only imagefile,errno,strerror(errno)); zfree(thumbfile); return 0; } } *pp = '/'; } } thumbpxb = make_thumbnail_pixbuf(imagefile,thumbfilesize); // generate thumbnail pixbuf from image file if (! thumbpxb) return 0; gdk_pixbuf_save(thumbpxb,thumbfile,"jpeg",&gerror,"quality","80",null); // save as .jpeg file in thumbnail directory g_object_unref(thumbpxb); return 1; } /********************************************************************************/ // Get thumbnail image (pixbuf) for given image file. // Use cached thumbnail image if available and not stale. // Use thumbnail file if available and not stale. // Create or replace thumbnail file if missing or stale. // Add thumbnail file to cache if not already there. // Return thumbnail image or null if invalid image file or other error. // Returned image (pixbuf) is a REFERENCE to cached thumbnail. // 17.04 // 10000 x 256 x 200 x 3 = 1.53 GB namespace thumbnail_cache { int cachesize = 10000; // max thumbnails cached in memory 17.01 int maxhash = 10 * cachesize; // hash table = 10 * cache size int hashw = 20; // hash search width typedef struct { char *imagefile; PIXBUF *pixbuf; time_t mtime; int size; } thumbtab_t; char **filetab; // cached imagefiles int *indextab; // corresp. thumbnail indexes thumbtab_t *thumbtab; // corresp. cached thumbnails int nextcache = 0, ftf = 1; int thumbnail_cache_lock = 0; } PIXBUF * get_cache_thumbnail(cchar *imagefile, int size) // reworked 17.01 { using namespace thumbnail_cache; PIXBUF *thumbpxb = 0; GError *gerror = 0; int Fii, Tii, Pii; int ii, cc, err; FTYPE ftype; time_t mtime; STATB statf, statb; char *thumbfile, *purgefile; static PIXBUF *dirthumb = 0; // cached directory thumbnail static PIXBUF *brokenthumb = 0; // cached broken-image thumbnail static int dirsize = -1; // pixbuf size if (ftf) // first call { ftf = 0; cc = (maxhash + hashw) * sizeof(char *); // allocate table space and clear filetab = (char **) zmalloc(cc); // +hashw to avoid wraparound logic memset(filetab,0,cc); cc = (maxhash + hashw) * sizeof(int); indextab = (int *) zmalloc(cc); memset(indextab,-1,cc); cc = cachesize * sizeof(thumbtab_t); thumbtab = (thumbtab_t *) zmalloc(cc); memset(thumbtab,0,cc); } if (! size) return 0; // 17.08 err = stat(imagefile,&statf); // check file exists if (err) return 0; mtime = statf.st_mtime; // last modification time ftype = image_file_type(imagefile); if (ftype == FDIR) { // directory "folder" image 17.04 if (size != dirsize || ! dirthumb) { if (dirthumb) g_object_unref(dirthumb); dirthumb = make_thumbnail_pixbuf(imagefile,size); dirsize = size; } return dirthumb; } if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { // not an image or RAW or VIDEO file 18.01 if (size != dirsize || ! brokenthumb) { if (brokenthumb) g_object_unref(brokenthumb); brokenthumb = make_thumbnail_pixbuf(imagefile,size); dirsize = size; } return brokenthumb; } Tii = -1; // no thumbnail cache position Fii = strHash(imagefile,maxhash); // find imagefile in filetab for (ii = 0; ii < hashw; ii++) { if (filetab[Fii+ii] == 0) continue; // skip empty position if (strmatch(imagefile,filetab[Fii+ii])) break; // found } if (ii == hashw) { // not found within hashw table positions for (ii = 0; ii < hashw; ii++) if (filetab[Fii+ii] == 0) break; // get first empty position if (ii == hashw) goto bug0; Fii += ii; // use this position for new entry goto add_to_cache; } Fii += ii; // filetab entry Tii = indextab[Fii]; // corresp. thumbtab entry if (Tii == -1) goto bug1; // must exist if (! strmatch(imagefile,thumbtab[Tii].imagefile)) goto bug2; // must match if (size != thumbtab[Tii].size) goto add_to_cache; // thumbtab is not caller size if (mtime != thumbtab[Tii].mtime) goto add_to_cache; // thumbtab is stale return thumbtab[Tii].pixbuf; // return reference to cache 17.04 add_to_cache: // not found in cache while (! resource_lock(thumbnail_cache_lock)) // stop re-entrance 17.04 zsleep(0.001); thumbfile = image2thumbfile(imagefile); // get thumbnail file for image file if (! thumbfile) goto ret0; // should not happen err = stat(thumbfile,&statb); if (err || statb.st_mtime < statf.st_mtime) // thumbfile exists and up to date ? update_thumbnail_file(imagefile); // no, create or refresh thumbnail file if (size <= thumbfilesize) // small thumbnail thumbpxb = gdk_pixbuf_new_from_file_at_size(thumbfile,size,size,&gerror); // make from thumbnail file else thumbpxb = make_thumbnail_pixbuf(imagefile,size); // large, make from image file if (! thumbpxb) goto ret0; if (Tii == -1) { // add new thumbtab entry nextcache++; // next cache slot (oldest) if (nextcache == cachesize) nextcache = 0; Tii = nextcache; } purgefile = thumbtab[Tii].imagefile; // prior occupant of thumbtab if (purgefile) { g_object_unref(thumbtab[Tii].pixbuf); // free pixbuf Pii = strHash(purgefile,maxhash); // find purgefile in filetab for (ii = 0; ii < hashw; ii++) { if (filetab[Pii+ii] == 0) continue; // skip empty position if (strmatch(purgefile,filetab[Pii+ii])) break; // found } if (ii == hashw) goto bug3; // not found Pii += ii; zfree(filetab[Pii]); // free filetab entry filetab[Pii] = 0; indextab[Pii] = -1; // free indextab entry zfree(purgefile); } thumbtab[Tii].imagefile = zstrdup(imagefile); // add tumbnail image thumbtab[Tii].pixbuf = thumbpxb; thumbtab[Tii].size = size; thumbtab[Tii].mtime = mtime; filetab[Fii] = zstrdup(imagefile); // add filetab and indextab entries indextab[Fii] = Tii; resource_unlock(thumbnail_cache_lock); // unlock 17.04 return thumbtab[Tii].pixbuf; // return reference to cache 17.04 bug0: { printz("get_cache_thumbnail() hash table failure \n"); goto ret0; } bug1: { printz("get_cache_thumbnail() indextab entry is missing \n"); goto ret0; } bug2: { printz("get_cache_thumbnail() indextab and thumbtab filename no match \n"); goto ret0; } bug3: { printz("get_cache_thumbnail() purgefile not found in filetab \n"); goto ret0; } ret0: resource_unlock(thumbnail_cache_lock); // unlock 17.04 return 0; } // Make a thumbnail pixbuf from the image file. // File can be a regular supported image file (jpeg etc.) // or a supported RAW file type. // Returned image (pixbuf) is subject for g_object_unref(). PIXBUF * make_thumbnail_pixbuf(cchar *imagefile, int size) { PIXBUF * make_thumbnail_pixbuf_raw(cchar *rawfile, int size); PIXBUF * make_thumbnail_pixbuf_video(cchar *rawfile, int size); FTYPE ftype; PIXBUF *thumbpxb = 0; GError *gerror = 0; static int ftf = 1; static char folderthumb[300], brokenthumb[300]; if (ftf) { ftf = 0; strcpy(folderthumb,zfuncs::zimagedir); // folder icon strcat(folderthumb,"/folder.png"); strcpy(brokenthumb,zfuncs::zimagedir); // broken thumbnail icon strcat(brokenthumb,"/broken.png"); } ftype = image_file_type(imagefile); if (ftype == FDIR) // directory file thumbpxb = gdk_pixbuf_new_from_file_at_size(folderthumb,size,size,&gerror); else if (ftype == IMAGE) // supported image type (jpeg etc.) thumbpxb = gdk_pixbuf_new_from_file_at_size(imagefile,size,size,&gerror); else if (ftype == RAW) { // supported RAW file type ////if (! pthread_equal(pthread_self(),zfuncs::tid_main)) return 0; // removed 18.01 thumbpxb = make_thumbnail_pixbuf_raw(imagefile,size); // let main() create thumbnail } else if (ftype == VIDEO) { // supported VIDEO file type 17.08 if (! pthread_equal(pthread_self(),zfuncs::tid_main)) return 0; // preload_thumbs() thread caller thumbpxb = make_thumbnail_pixbuf_video(imagefile,size); // let main() create thumbnail } if (thumbpxb) return thumbpxb; printz("cannot make thumbnail: %s \n",imagefile); // use broken image thumbnail 18.01 if (gerror) printz(" %s \n",gerror->message); gerror = 0; thumbpxb = gdk_pixbuf_new_from_file_at_size(brokenthumb,size,size,&gerror); return thumbpxb; } // Make a thumbnail pixbuf from a supported RAW file type PIXBUF * make_thumbnail_pixbuf_raw(cchar *rawfile, int size) { PIXBUF *thumbpxb = 0; PXB *rawpxb; int ww1, hh1, ww2, hh2; rawpxb = RAW_PXB_load(rawfile); if (! rawpxb) return 0; ww1 = rawpxb->ww; hh1 = rawpxb->hh; if (ww1 > hh1) { ww2 = size; hh2 = ww2 * hh1 / ww1; } else { hh2 = size; ww2 = hh2 * ww1 / hh1; } thumbpxb = gdk_pixbuf_scale_simple(rawpxb->pixbuf,ww2,hh2,BILINEAR); PXB_free(rawpxb); return thumbpxb; } // Make a thumbnail pixbuf from a supported VIDEO file type PIXBUF * make_thumbnail_pixbuf_video(cchar *videofile, int size) // 17.08 { PIXBUF *thumbpxb = 0; PXB *videopxb; int ww1, hh1, ww2, hh2; videopxb = PXB_load(videofile); if (! videopxb) return 0; ww1 = videopxb->ww; hh1 = videopxb->hh; if (ww1 > hh1) { ww2 = size; hh2 = ww2 * hh1 / ww1; } else { hh2 = size; ww2 = hh2 * ww1 / hh1; } thumbpxb = gdk_pixbuf_scale_simple(videopxb->pixbuf,ww2,hh2,BILINEAR); PXB_free(videopxb); return thumbpxb; } // Remove thumbnail from disk (for deleted or renamed image file). void delete_thumbnail(cchar *imagefile) { int err; STATB statf; char *tpath; tpath = image2thumbfile(imagefile); if (! tpath) return; // 18.01 err = stat(tpath,&statf); // remove from disk if (! err && S_ISREG(statf.st_mode)) remove(tpath); zfree(tpath); return; } // look ahead of gallery page and preload thumbnails into cache // targfile = next gallery file number beyond current gallery page int preload_thumbs_targfile = 0; void preload_thumbs(int targfile) // 17.04 { void * preload_thumbs_thread(void *); if (Fpreload_thumbs_block) return; // 18.01 if (! thumbsize) return; // 17.08 preload_thumbs_targfile = targfile; if (Fpreload_thumbs_thread) return; Fpreload_thumbs_thread = 1; start_detached_thread(preload_thumbs_thread,0); return; } // thread function - refresh thumbnails following target image in gallery void * preload_thumbs_thread(void *) // 17.04 { int targfile; targfile = preload_thumbs_targfile; while (true) { if (FGWM != 'G') break; if (targfile > Nfiles-1) break; if (targfile > preload_thumbs_targfile + 40) break; get_cache_thumbnail(GFlist[targfile].file,thumbsize); targfile++; } Fpreload_thumbs_thread = 0; pthread_exit(0); return 0; } /********************************************************************************/ // popup a new window with a larger image of a clicked thumbnail void gallery_popimage() { static int ftf = 1, ii; static char *popfiles[20]; // last 20 images if (ftf) { ftf = 0; // initz. empty file memory for (ii = 0; ii < 20; ii++) popfiles[ii] = 0; ii = 0; } if (! clicked_file) return; ii++; // use next file memory position if (ii == 20) ii = 0; if (popfiles[ii]) zfree(popfiles[ii]); popfiles[ii] = clicked_file; // save clicked_file persistently clicked_file = 0; // reset clicked_file popup_image(popfiles[ii],MWIN,1,512); // popup window with image return; } /********************************************************************************/ // select one image file by clicking a gallery thumbnail // returned file is subject for zfree() char * gallery_select1(cchar *gdirk) // 17.08 { zdialog *zd; int zstat; static char filename[XFCC]; char *cdirk; char fgwm[4]; fgwm[0] = FGWM; // save current view mode fgwm[1] = 0; cdirk = curr_dirk; // and gallery directory if (gdirk) { gallery(gdirk,"init",0); // switch to caller's gallery 18.01 gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint } m_viewmode(0,"G"); zd = zdialog_new(ZTX("Image File"),Mwin,Bdone,Bcancel,null); // dialog to query file name zd_gallery_select1 = zd; zdialog_add_widget(zd,"label","labtip","dialog",ZTX("select thumbnail")); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labf","hbf",ZTX("Image File"),"space=3"); zdialog_add_widget(zd,"zentry","filename","hbf",0,"space=3|expand"); zdialog_resize(zd,400,0); zdialog_run(zd,0,"parent"); // run dialog and wait for completion zstat = zdialog_wait(zd); // via mouse click function below zdialog_fetch(zd,"filename",filename,XFCC); // get selected file zdialog_destroy(zd); zd_gallery_select1 = 0; if (gdirk && cdirk) { gallery(cdirk,"init",0); // restore view mode 18.01 gallery(0,"sort",-2); } m_viewmode(0,fgwm); // may paint if (zstat != 1) return 0; // no selection if (*filename != '/') return 0; return zstrdup(filename); } void gallery_select1_Lclick_func(int Nth) // called by gallery mouse function { char *imagefile = 0; int ftype; if (! zd_gallery_select1) return; // should not happen if (Nth < 0) return; imagefile = gallery(0,"get",Nth); // get file at clicked position if (! imagefile) return; ftype = image_file_type(imagefile); // must be image or RAW file if (ftype != IMAGE && ftype != RAW) { zfree(imagefile); return; } zdialog_stuff(zd_gallery_select1,"filename",imagefile); // stuff file name in dialog zfree(imagefile); return; } /********************************************************************************/ // Select multiple image files from thumbnail gallery window. // GSfiles[*]: list of selected image files // Pre-selected files are passed in the same list, which is updated. // The dialog shows the list of files selected and can be edited. // Returned status: 0 = OK, 1 = user cancel, 2 = internal error namespace galselnames { int dialog_event(zdialog *zd, cchar *event); int find_file(cchar *imagefile); void insert_file(cchar *imagefile); void remove_file(cchar *imagefile); void Xclick_func(int Nth, char LR); void callbackfunc(GtkWidget *textwidget, int line, int posn, int KBkey); void showthumb(); GtkWidget *drawarea = 0; GtkWidget *Ftext = 0; int currline; int Nselect = 0; static char matchtext[20]; char *imagefile; }; void gallery_select_clear() { for (int ii = 0; ii < GScount; ii++) zfree(GSfiles[ii]); GScount = 0; return; } int gallery_select() { using namespace galselnames; int ii, kk; /*** ________________________________________________________ | Select Files | | _________________________ ________________________ | | | | | | | | | list of selected files | | image of current file | | | | | | selected in file list | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |_________________________| |________________________| | | | | [Delete] [Insert] [Clear] [Add All] | | [Done] [Cancel] | |________________________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Select Files"),Mwin,Bdone,Bcancel,null); zd_gallery_select = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand|space=3"); zdialog_add_widget(zd,"scrwin","scrwin","hb1",0,"expand"); zdialog_add_widget(zd,"text","files","scrwin"); zdialog_add_widget(zd,"frame","fr12","hb1",0,"space=5"); // for thumbnail - added later zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"button","delete","hb2",Bdelete,"space=8"); zdialog_add_widget(zd,"button","insert","hb2",Binsert,"space=8"); zdialog_add_widget(zd,"button","clear","hb2",Bclear,"space=8"); zdialog_add_widget(zd,"button","addall","hb2",Baddall,"space=8"); Ftext = zdialog_widget(zd,"files"); // disable file list text wrap textwidget_set_callbackfunc(Ftext,callbackfunc); // get mouse clicks and KB keys GtkWidget *frame = zdialog_widget(zd,"fr12"); // drawing area for thumbnail image drawarea = gtk_drawing_area_new(); gtk_widget_set_size_request(drawarea,256,258); gtk_container_add(GTK_CONTAINER(frame),drawarea); G_SIGNAL(drawarea,"draw",showthumb,0); // 18.01 zdialog_resize(zd,700,0); // start dialog zdialog_run(zd,dialog_event,"save"); // keep relative position textwidget_clear(Ftext); Nselect = 0; currline = -1; imagefile = 0; for (ii = 0; ii < GScount; ii++) // add pre-selected files to dialog { textwidget_append(Ftext,0,"%s\n",GSfiles[ii]); Nselect++; } m_viewmode(0,"G"); // open gallery window if (gallerytype == TNONE) { // if no gallery, start with 18.01 gallery(topdirks[0],"init",0); // top directory gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint } zdialog_wait(zd); // wait for dialog completion if (zd->zstat != 1) { // cancelled zdialog_free(zd); // kill dialog zd_gallery_select = 0; // selected files unchanged return 1; } gallery_select_clear(); // clear gallery_select() file list for (ii = kk = 0; ii < Nselect; ii++) // get list of files from dialog { imagefile = textwidget_line(Ftext,ii,1); if (! imagefile || ! *imagefile) continue; GSfiles[kk] = imagefile; // /.../filename.ext kk++; if (kk == GSmax) { zmessageACK(Mwin,ZTX("exceed %d selected files"),GSmax); break; } } GScount = kk; // selected files zdialog_free(zd); // kill dialog zd_gallery_select = 0; return 0; } // gallery getfiles dialog event function int galselnames::dialog_event(zdialog *zd, cchar *event) { using namespace galselnames; char *ftemp; static char *deletedfiles[100]; // last 100 files deleted static int Ndeleted = 0; int ii, Nth; FTYPE ftype; if (strmatch(event,"delete")) // delete file at curr. position { if (! Nselect) return 1; if (currline < 0) return 1; if (Ndeleted == 100) { // capacity reached zfree(deletedfiles[0]); // remove oldest entry for (ii = 0; ii < 99; ii++) deletedfiles[ii] = deletedfiles[ii+1]; Ndeleted = 99; } ftemp = textwidget_line(Ftext,currline,0); // file (text line) to delete (keep \n) if (! ftemp) return 1; deletedfiles[Ndeleted] = ftemp; // save deleted file for poss. insert Ndeleted++; textwidget_delete(Ftext,currline); // delete from dialog list Nselect--; if (currline > Nselect-1) currline = Nselect - 1; textwidget_highlight_line(Ftext,currline); // next line showthumb(); // show thumbnail } if (strmatch(event,"insert")) // insert first deleted file { // at current position if (! Ndeleted) return 1; textwidget_insert(Ftext,0,currline,"%s",deletedfiles[0]); Nselect++; currline++; textwidget_highlight_line(Ftext,currline); // next line showthumb(); // update thumbnail zfree(deletedfiles[0]); // remove file from the deleted list for (ii = 0; ii < Ndeleted-1; ii++) deletedfiles[ii] = deletedfiles[ii+1]; Ndeleted--; } if (strmatch(event,"clear")) { // clear all files textwidget_clear(Ftext); Nselect = 0; currline = -1; showthumb(); // blank thumbnail } if (strmatch(event,"addall")) // insert all files in image gallery { for (Nth = 0; ; Nth++) { ftemp = gallery(0,"get",Nth); // next file if (! ftemp) break; ftype = image_file_type(ftemp); // must be image or RAW file if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { zfree(ftemp); continue; } textwidget_append(Ftext,0,"%s\n",ftemp); // append - could be insert zfree(ftemp); Nselect++; } currline = Nselect - 1; // position is last file showthumb(); } return 1; } // See if image file is in the file list already or not. // Return the last matching line number or -1 if not found. int galselnames::find_file(cchar *imagefile) { using namespace galselnames; int line; char *ftemp; for (line = Nselect-1; line >= 0; line--) { ftemp = textwidget_line(Ftext,line,1); // get file without \n if (! ftemp) continue; if (strmatch(ftemp,imagefile)) { zfree(ftemp); return line; } zfree(ftemp); } return -1; } // add image file to list at current position, set thumbnail = file void galselnames::insert_file(cchar *imagefile) { using namespace galselnames; int ftype; ftype = image_file_type(imagefile); // must be image or RAW file if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) return; textwidget_insert(Ftext,0,currline,"%s\n",imagefile); Nselect++; currline++; textwidget_highlight_line(Ftext,currline); showthumb(); // update thumbnail return; } // remove image file at last position found, set thumbnail = next // called when gallery thumbnail is right-clicked void galselnames::remove_file(cchar *imagefile) { using namespace galselnames; int line; line = find_file(imagefile); // find last instance if (line < 0) return; currline = line; showthumb(); zmainsleep(0.5); textwidget_delete(Ftext,currline); Nselect--; if (currline > Nselect-1) currline = Nselect - 1; textwidget_highlight_line(Ftext,currline); showthumb(); return; } // called from image gallery window when a thumbnail is clicked // add image file to list at current position, set thumbnail = file void gallery_select_Lclick_func(int Nth) // left click, add { galselnames::Xclick_func(Nth,'L'); zfree(clicked_file); clicked_file = 0; return; } void gallery_select_Rclick_func(int Nth) // right click, find and remove { galselnames::Xclick_func(Nth,'R'); zfree(clicked_file); clicked_file = 0; return; } void galselnames::Xclick_func(int Nth, char LR) { using namespace galselnames; char *imagefile; FTYPE ftype; static int pNth = -1; // previously clicked file int nn, incr; if (! zd_gallery_select) return; // should not happen if (Nth < 0) return; // gallery gone ? imagefile = gallery(0,"get",Nth); // get file at clicked position if (! imagefile) { pNth = -1; return; } ftype = image_file_type(imagefile); // must be image or RAW file if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { // 17.08 zfree(imagefile); pNth = -1; return; } if (LR == 'R') { // right click, unselect remove_file(imagefile); zfree(imagefile); return; } if (! KBshiftkey) // no shift key { pNth = Nth; // possible start of range insert_file(imagefile); // insert file at current position zfree(imagefile); return; } if (KBshiftkey) // shift key, end of range { if (pNth < 0) return; // no start of range, ignore if (pNth > Nth) incr = -1; // range is descending else incr = +1; // ascending for (nn = pNth+incr; nn != Nth+incr; nn += incr) // add all files from pNth to Nth { // excluding pNth (already added) imagefile = gallery(0,"get",nn); if (! imagefile) continue; ftype = image_file_type(imagefile); // only image and RAW files if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { zfree(imagefile); continue; } insert_file(imagefile); zfree(imagefile); } pNth = -1; // no prior return; } return; } // process mouse or key click in files window: // set new current position and set thumbnail = clicked file void galselnames::callbackfunc(GtkWidget *widget, int line, int posn, int KBkey) { int cc, nn; if (line >= 0) { // line clicked textwidget_highlight_line(Ftext,line); // highlight currline = line; showthumb(); *matchtext = 0; return; } if (KBkey == GDK_KEY_F1) { // key F1 pressed, show help showz_userguide(F1_help_topic); return; } if (! Nselect) return; if (KBkey >= 0xfd00) { if (KBkey == GDK_KEY_Up) currline--; // KB arrow key navigation if (KBkey == GDK_KEY_Down) currline++; if (KBkey == GDK_KEY_Page_Up) currline -= 10; if (KBkey == GDK_KEY_Page_Down) currline += 10; if (KBkey == GDK_KEY_Home) currline = 0; if (KBkey == GDK_KEY_End) currline = Nselect - 1; if (currline < 0) currline = 0; if (currline > Nselect-1) currline = Nselect - 1; textwidget_highlight_line(Ftext,currline); // highlight line *matchtext = 0; } else { // other keys - look for matching text cc = strlen(matchtext); if (cc >= 19) return; matchtext[cc] = KBkey; matchtext[cc+1] = 0; nn = textwidget_find(widget,matchtext,currline); // highlight matching text if (nn >= 0) currline = nn; } showthumb(); return; } // show thumbnail for file at current position in select list void galselnames::showthumb() { using namespace galselnames; GdkWindow *gdkwin; PIXBUF *thumbnail = 0; static draw_context_t draw_context; cairo_t *cr; if (! Nselect) return; if (currline < 0) return; gdkwin = gtk_widget_get_window(drawarea); cr = draw_context_create(gdkwin,draw_context); imagefile = textwidget_line(Ftext,currline,1); // get curr. image without \n if (imagefile) thumbnail = get_cache_thumbnail(imagefile,256); // get thumbnail if (thumbnail) { cairo_set_source_rgb(cr,1,1,1); // white background cairo_paint(cr); gdk_cairo_set_source_pixbuf(cr,thumbnail,0,0); // + thumbnail cairo_paint(cr); } else { cairo_set_source_rgb(cr,1,1,1); // white background only cairo_paint(cr); } draw_context_destroy(draw_context); return; } /********************************************************************************/ // edit bookmarks namespace bookmarknames { #define maxbmks 50 char *bookmarks[maxbmks]; // bookmark names and files int Nbmks; // count of entries int bmkposn; // current entry, 0-last zdialog *zd_goto_bookmark; GtkWidget *textwidget; } void bookmarks_load(); void bookmarks_callbackfunc(GtkWidget *, int line, int pos, int kbfunc); int bookmarks_dialog_event(zdialog *zd, cchar *event); void bookmarks_refresh(); void m_edit_bookmarks(GtkWidget *, cchar *) { using namespace bookmarknames; zdialog *zd; cchar *bmk_add = ZTX("Click a list position. Click a gallery thumbnail for the new bookmark.\n" // 18.01 "Bookmark for thumbnail will be added. Change the name and press [Rename]."); /*** _______________________________________________ | Edit Bookmarks | | | | Click list position. Click thumbnail to add. | |-----------------------------------------------| | bookmarkname1 /topdir/.../filename1.jpg | | bookmarkname2 /topdir/.../filename2.jpg | | bookmarkname3 /topdir/.../filename3.jpg | | bookmarkname4 /topdir/.../filename4.jpg | | bookmarkname5 /topdir/.../filename5.jpg | | bookmarkname6 /topdir/.../filename6.jpg | |-----------------------------------------------| | [bookmarkname...] [rename] [delete] | | [done] | |_______________________________________________| ***/ F1_help_topic = "bookmarks"; if (zd_edit_bookmarks) return; // already busy if (zd_goto_bookmark) return; if (checkpend("all")) return; zd = zdialog_new(ZTX("Edit Bookmarks"),Mwin,Bdone,null); zd_edit_bookmarks = zd; zdialog_add_widget(zd,"hbox","hbtip","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labtip","hbtip",bmk_add,"space=5"); zdialog_add_widget(zd,"hbox","hbbmk","dialog",0,"space=5|expand"); zdialog_add_widget(zd,"frame","frbmk","hbbmk",0,"space=5|expand"); zdialog_add_widget(zd,"scrwin","scrwin","frbmk"); // 17.01 zdialog_add_widget(zd,"text","bmklist","scrwin"); zdialog_add_widget(zd,"hbox","hbname","dialog",0,"space=5"); zdialog_add_widget(zd,"zentry","bmkname","hbname",0,"space=5|size=30"); zdialog_add_widget(zd,"button","rename","hbname",Brename,"space=5"); zdialog_add_widget(zd,"button","delete","hbname",Bdelete,"space=5"); textwidget = zdialog_widget(zd,"bmklist"); // connect mouse to bookmark list textwidget_set_callbackfunc(textwidget,bookmarks_callbackfunc); bookmarks_load(); // load bookmarks from bookmarks file bookmarks_refresh(); // update bookmarks list in dialog zdialog_resize(zd,500,400); zdialog_run(zd,bookmarks_dialog_event,"save"); // run dialog, parallel m_viewmode(0,"G"); // show current gallery return; } // load bookmarks list from bookmarks file void bookmarks_load() { using namespace bookmarknames; int err; char buff[XFCC], bmkfile[200]; char *pp, *pp2; FILE *fid; STATB stbuff; Nbmks = 0; err = locale_filespec("user","bookmarks",bmkfile); // read bookmarks file if (! err) { fid = fopen(bmkfile,"r"); if (fid) { while (true) { pp = fgets_trim(buff,XFCC,fid,1); // next bookmark rec. if (! pp) break; if (strlen(pp) < 40) continue; pp2 = strchr(pp+32,'/'); // verify bookmark if (! pp2) continue; err = stat(pp2,&stbuff); if (err) continue; bookmarks[Nbmks] = zstrdup(pp); // fill bookmark list if (++Nbmks == maxbmks) break; } fclose(fid); } } bmkposn = Nbmks; // next free position return; } // mouse click function to select existing bookmark from list void bookmarks_callbackfunc(GtkWidget *, int line, int pos, int kbkey) { using namespace bookmarknames; char bookmarkname[32]; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } if (! zd_edit_bookmarks) return; if (Nbmks < 1) return; if (line < 0) line = 0; if (line > Nbmks-1) line = Nbmks-1; bmkposn = line; strncpy0(bookmarkname,bookmarks[bmkposn],31); strTrim(bookmarkname); zdialog_stuff(zd_edit_bookmarks,"bmkname",bookmarkname); return; } // mouse click function to receive clicked thumbnails for new/revised bookmarks void bookmarks_Lclick_func(int Nth) { using namespace bookmarknames; char *imagefile, *newbookmark; char *pp, bookmarkname[32]; int cc; if (! zd_edit_bookmarks) return; if (Nth < 0) return; // gallery gone ? imagefile = gallery(0,"get",Nth); // get file at clicked position if (! imagefile) return; pp = strrchr(imagefile,'/'); // get file name or last subdirk name if (! pp) return; // to use as default bookmark name strncpy0(bookmarkname,pp+1,31); // max. 30 chars. + null cc = strlen(imagefile) + 34; // construct bookmark record: newbookmark = (char *) zmalloc(cc); // filename /directories.../filename snprintf(newbookmark,cc,"%-30s %s",bookmarkname,imagefile); zfree(imagefile); if (Nbmks == maxbmks) { // if list full, remove first zfree(bookmarks[0]); Nbmks--; for (int ii = 0; ii < Nbmks; ii++) bookmarks[ii] = bookmarks[ii+1]; } if (Nbmks == 0) bmkposn = 0; // 1st bookmark --> 0 else bmkposn++; // else clicked position + 1 if (bmkposn < 0) bmkposn = 0; if (bmkposn > Nbmks) bmkposn = Nbmks; for (int ii = Nbmks; ii > bmkposn; ii--) // make hole to insert new bookmark bookmarks[ii] = bookmarks[ii-1]; bookmarks[bmkposn] = newbookmark; // insert Nbmks++; bookmarks_refresh(); // update bookmarks list in dialog zdialog_stuff(zd_edit_bookmarks,"bmkname",bookmarkname); return; } // dialog event and completion function int bookmarks_dialog_event(zdialog *zd, cchar *event) { using namespace bookmarknames; char bmkfile[200]; char bookmarkname[32]; FILE *fid; int cc; if (strmatch(event,"delete")) // delete bookmark at position { if (bmkposn < 0 || bmkposn > Nbmks-1) return 1; for (int ii = bmkposn; ii < Nbmks-1; ii++) bookmarks[ii] = bookmarks[ii+1]; Nbmks--; zdialog_stuff(zd,"bmkname",""); // clear name field bookmarks_refresh(); // update bookmarks list in dialog } if (strmatch(event,"rename")) // apply new name to bookmark { if (bmkposn < 0 || bmkposn > Nbmks-1) return 1; zdialog_fetch(zd,"bmkname",bookmarkname,31); // get name from dialog cc = strlen(bookmarkname); if (cc < 30) memset(bookmarkname+cc,' ',30-cc); // blank pad to 30 chars. bookmarkname[30] = 0; strncpy(bookmarks[bmkposn],bookmarkname,30); // replace name in bookmarks list bookmarks_refresh(); // update bookmarks list in dialog } if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) // done { locale_filespec("user","bookmarks",bmkfile); // write bookmarks file fid = fopen(bmkfile,"w"); if (! fid) zmessageACK(Mwin,ZTX("unable to save bookmarks file")); else { for (int ii = 0; ii < Nbmks; ii++) fprintf(fid,"%s\n",bookmarks[ii]); fclose(fid); } } for (int ii = 0; ii < Nbmks; ii++) // free memory zfree(bookmarks[ii]); zdialog_free(zd); zd_edit_bookmarks = 0; return 1; } // private function to update dialog widget with new bookmarks list void bookmarks_refresh() { using namespace bookmarknames; char bookmarkline[XFCC+32]; char blanks[33] = " "; int cc; if (! zd_edit_bookmarks && ! zd_goto_bookmark) return; textwidget_clear(textwidget); // clear bookmarks list for (int ii = 0; ii < Nbmks; ii++) { // write bookmarks list strncpy0(bookmarkline,bookmarks[ii],31); cc = utf8len(bookmarkline); // compensate multibyte chars. strncat(bookmarkline,blanks,32-cc); strcat(bookmarkline,bookmarks[ii]+32); textwidget_append(textwidget,0,"%s\n",bookmarkline); } return; } // select a bookmark and jump gallery to selected bookmark // connected to gallery menu "GoTo" button void m_goto_bookmark(GtkWidget *, cchar *) { using namespace bookmarknames; void goto_bookmark_callbackfunc(GtkWidget *, int line, int pos, int kbkey); int goto_bookmark_dialog_event(zdialog *zd, cchar *event); zdialog *zd; /*** Gallery Button: [GoTo] _______________________________________________ | Go to Bookmark | |-----------------------------------------------| | bookmarkname1 /topdir/.../filename1.jpg | | bookmarkname2 /topdir/.../filename2.jpg | | bookmarkname3 /topdir/.../filename3.jpg | | bookmarkname4 /topdir/.../filename4.jpg | | bookmarkname5 /topdir/.../filename5.jpg | | bookmarkname6 /topdir/.../filename6.jpg | | | | [edit bookmarks] | |_______________________________________________| ***/ F1_help_topic = "bookmarks"; if (zd_edit_bookmarks) return; // already busy if (zd_goto_bookmark) return; zd = zdialog_new(ZTX("Go To Bookmark"),Mwin,ZTX("Edit Bookmarks"),null); zd_goto_bookmark = zd; zdialog_add_widget(zd,"hbox","hbbmk","dialog",0,"space=5|expand"); zdialog_add_widget(zd,"frame","frbmk","hbbmk",0,"space=5|expand"); zdialog_add_widget(zd,"scrwin","scrwin","frbmk",0,"space=5|expand"); // 17.01 zdialog_add_widget(zd,"text","bmklist","scrwin"); textwidget = zdialog_widget(zd,"bmklist"); // connect mouse to bookmark list textwidget_set_callbackfunc(textwidget,goto_bookmark_callbackfunc); bookmarks_load(); // get bookmarks from bookmarks file bookmarks_refresh(); // update bookmarks list in dialog zdialog_resize(zd,400,300); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,goto_bookmark_dialog_event,"mouse"); // run dialog return; } // dialog event and completion function int goto_bookmark_dialog_event(zdialog *zd, cchar *event) { using namespace bookmarknames; int zstat = zd->zstat; if (! zstat) return 1; // wait for completion zdialog_free(zd); zd_goto_bookmark = 0; if (zstat == 1) m_edit_bookmarks(0,0); // [edit bookmarks] button return 1; } // mouse click function to receive clicked bookmarks void goto_bookmark_callbackfunc(GtkWidget *, int line, int pos, int kbkey) { using namespace bookmarknames; char *file; int err; STATB sbuff; if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help 18.01 showz_userguide(F1_help_topic); return; } if (checkpend("all")) return; if (! zd_goto_bookmark) return; bmkposn = line; // get clicked line if (bmkposn < 0 || bmkposn > Nbmks-1) return; file = bookmarks[bmkposn] + 32; err = stat(file,&sbuff); if (err) { zmessageACK(Mwin,Bfilenotfound); return; } f_open(file,0); // open bookmarked image file gallery(file,"init",0); // go to gallery and file position gallery(file,"paint",0); m_viewmode(0,"G"); zdialog_free(zd_goto_bookmark); // kill dialog zd_goto_bookmark = 0; return; } /********************************************************************************/ // generate a clickable list of all image directories // show gallery for any directory clicked namespace alldirs { #define maxdirs 10000 int Nlines = 0, Ndirks = 0; int currline = 0; typedef struct { char *name; // /dir1/dir2/.../dirN directory name int Nsubs; // subdirectory count 0-N int8 lev; // directory level 0-N, top/sub/sub ... int8 exp; // directory status 0/1 = collapsed/expanded int16 line; // textwidget line -1 = not displayed } dlist_t; dlist_t *dlist; int drecl = sizeof(dlist_t); GtkWidget *scrollwin, *textwidget; int compfunc(cchar *rec1, cchar *rec2); void callbackfunc(GtkWidget *, int line, int pos, int kbkey); void writetext(); } // menu function void m_alldirs(GtkWidget *, cchar *) // 17.01 { using namespace alldirs; zdialog *zd; int ii, jj, cc, pcc, NF, err; char *dir, *pdir, **Flist; F1_help_topic = "all_directories"; /*** ________________________________ | All Directories | | ____________________________ | | | | | | | [+] topdir1 | | | | [-] topdir2 | | | | subdir1 | | | | [+] subdir2 | | | | subdir3 | | | | [+] topdir3 | | | | ... | | | |____________________________| | | | | [Done] | |________________________________| ***/ if (dlist) goto zdialog; // already done 18.01 cc = drecl * maxdirs; dlist = (dlist_t *) zmalloc(cc); // memory for directory list memset(dlist,0,cc); Ndirks = 0; for (ii = 0; ii < Ntopdirks; ii++) // loop all top image directories { dlist[Ndirks].name = topdirks[ii]; Ndirks++; if (Ndirks == maxdirs) break; err = find_imagefiles(topdirks[ii],8+16,Flist,NF); // directories, all levels 18.01 if (err) { zmessageACK(Mwin,strerror(errno)); continue; } for (jj = 0; jj < NF; jj++) // add to directories list { dlist[Ndirks].name = zstrdup(Flist[jj]); Ndirks++; if (Ndirks == maxdirs) break; } if (NF) zfree(Flist); if (Ndirks == maxdirs) break; } if (Ndirks > 1) HeapSort((char *) dlist,drecl,Ndirks,compfunc); // sort alphabetically for (ii = 0; ii < Ndirks; ii++) // loop all directories { dir = dlist[ii].name; // this directory name for (jj = ii-1; jj >= 0; jj--) { // search backwards for parent pdir = dlist[jj].name; // previous directory name pcc = strlen(pdir); if (strmatchN(dir,pdir,pcc) && dir[pcc] == '/') break; // this dir = prev dir + /... } if (jj >= 0) { // parent found dlist[ii].lev = dlist[jj].lev + 1; // level = parent level + 1 dlist[jj].Nsubs++; // add parent subdir count } else dlist[ii].lev = 0; // no parent, level = 0 } for (ii = 0; ii < Ndirks; ii++) // loop all directories dlist[ii].exp = 0; // expand = no zdialog: zd = zdialog_new("All Directories",Mwin,Bdone,null); // make report window zdialog_add_widget(zd,"scrwin","scroll","dialog",0,"expand"); zdialog_add_widget(zd,"text","dlist","scroll"); zdialog_resize(zd,300,300); zdialog_run(zd,null,"mouse"); scrollwin = zdialog_widget(zd,"scroll"); textwidget = zdialog_widget(zd,"dlist"); textwidget_set_callbackfunc(textwidget,callbackfunc); // connect mouse click function writetext(); // write top directories to window currline = 0; // first entry zdialog_wait(zd); // wait for dialog complete zdialog_free(zd); return; } // sort compare function int alldirs::compfunc(cchar *rec1, cchar *rec2) { dlist_t *dir1 = (dlist_t *) rec1; dlist_t *dir2 = (dlist_t *) rec2; int nn; nn = strcasecmp(dir1->name,dir2->name); if (nn) return nn; nn = strcmp(dir1->name,dir2->name); return nn; } // directory list mouse click function void alldirs::callbackfunc(GtkWidget *textwidget, int line, int pos, int kbkey) { int ii; char *pline, *pp; if (line < 0) // KB key { if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help showz_userguide(F1_help_topic); return; } line = currline; if (kbkey == GDK_KEY_Up) line--; // KB arrow key navigation if (kbkey == GDK_KEY_Down) line++; if (line < 0) line = 0; if (line > Nlines-1) line = Nlines - 1; } pline = textwidget_line(textwidget,line,1); // textwidget line: ... [x] dirname if (! pline || ! *pline) return; textwidget_highlight_line(textwidget,line); currline = line; for (ii = 0; ii < Ndirks; ii++) // find dlist[] rec corresponding if (line == dlist[ii].line) break; // to textwidget line clicked if (ii == Ndirks) return; if (kbkey == GDK_KEY_Return) { // Enter key, look for [+] or [-] 18.01 pp = strchr(pline,'['); if (pp) pos = pp - pline; // simulate click on [*] } if (pos > 0) // clicked position { pp = strchr(pline,'['); if (pp) { // [+] or [-] button present if (pos < (pp-pline)) return; // click position before button if (pos >= (pp-pline) && pos <= (pp+2-pline)) { // on the button if (pp[1] == '+') dlist[ii].exp = 1; // set expand flag if (pp[1] == '-') dlist[ii].exp = 0; writetext(); // refresh textwidget textwidget_highlight_line(textwidget,line); return; } } } gallery(dlist[ii].name,"init",0); // directory name clicked gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // show gallery return; } // write all visible directories to text window void alldirs::writetext() { #define SCRWIN GTK_SCROLLED_WINDOW(scrollwin) GtkAdjustment *vadjust; double vposition; GdkWindow *gdkwin; int ii = 0, jj, line = 0; char *pp, indent[100]; cchar *expbutt; memset(indent,' ',100); gdkwin = gtk_widget_get_window(textwidget); // stop window updates until done gdk_window_freeze_updates(gdkwin); vadjust = gtk_scrolled_window_get_vadjustment(SCRWIN); // get vertical scroll position vposition = gtk_adjustment_get_value(vadjust); textwidget_clear(textwidget); // clear textwidget while (ii < Ndirks) // loop all directories { jj = dlist[ii].lev * 4; // indent 4 blanks per directory level if (jj > 99) jj = 99; indent[jj] = 0; if (dlist[ii].Nsubs == 0) expbutt = " "; // no subdirs, no expand button else if (dlist[ii].exp) expbutt = "[-]"; // prepare [+] or [-] else expbutt = "[+]"; pp = strrchr(dlist[ii].name,'/'); if (! pp) continue; textwidget_append(textwidget,0,"%s %s %s \n",indent,expbutt,pp+1); // ... [x] dirname indent[jj] = ' '; dlist[ii].line = line; // text line for this directory line++; if (dlist[ii].exp) { // if directory expanded, continue ii++; continue; } for (jj = ii + 1; jj < Ndirks; jj++) { // not expanded, find next directory if (dlist[jj].lev <= dlist[ii].lev) break; // at same or lower level dlist[jj].line = -1; // no text line for skipped directories } ii = jj; } Nlines = line; gtk_adjustment_set_value(vadjust,vposition); // restore vertical scroll position gdk_window_thaw_updates(gdkwin); // complete window updates return; } /********************************************************************************/ // set the gallery from the current image file physical directory void m_sync_gallery(GtkWidget*, cchar *menu) { F1_help_topic = "sync_gallery"; if (! curr_file) return; gallery(curr_file,"init",0); // new gallery gallery(curr_file,"paint",0); // position at curr. file m_viewmode(0,"G"); return; } /********************************************************************************/ // dummy menu entry as target for "Show Hidden Files" KB shortcut void m_show_hidden(GtkWidget *, cchar *) // 18.01 { KBaction("Show Hidden"); return; } fotoxx-18.01.1/data/0000755000175000017500000000000013222767271012573 5ustar micomicofotoxx-18.01.1/data/tags_defined0000644000175000017500000000021413222767271015127 0ustar micomicopeople: Mary, John, relatives, places: Yellowstone, NY City, Italy, things: monument, building, garden, events: Danube cruise, 2012 Italy, fotoxx-18.01.1/data/colorwheel.jpg0000644000175000017500000006553313222767271015454 0ustar micomicoJFIFExifMM*V^(if%02310100Fotoxx:resize| NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 768 768 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((((((((((?廋= .>uNpXz㡣bM](W5]/é3C`RV*!!Daشٿ' M|Kqȸoi!)aHmGQqUPH<Ν=K[9nbK\GQ)gv'9$KKBn5d]dUԌ WV<3h־!m#܅%,7 us5ۖgwbkȫ(y& ڲbR-h7n-~' :Km!\y Gkc/ Z5o@='M6O#I^;5{(+ȩd}\y+~VeK(eRb/E=ڹm['2ÚHňIi,Js}$)kIĔVe_sei>J#dC.2Yw :pN +y[k$ QpFgJҵs:7(MC;)c:tZSg|) #/f/i<';hHw~o*b VQ['|Q6ށ\\ṣL!k0e}YgymR{_M׉]Gm]P%>W`hL*a3qf M](IYES$(((((((((((((((((1F*kKIobI&qqD؜rI'ï[Z~ eEm7!P9Yy;0C c*8h9I>eN0:~1ψ\唩mn`; c|5w»ൻ^d@c G0# 8rN^F.ǩck^U?]|:DfU9o.:]rhj MWXD$60d2}>s]Miewil(cŎPe<􌙨:ksݟ&[FZ)>Td -\d+ɜl}4dSe*P =*mp TLi֡u92JiZaYFE(Zv.3X2 ZpZp+E O PfmU9Sb܄ )1Yd^/ldYVk)JlQfMnV~͟ |sN-yHpm#m|cZ0S]lKO 3t.oZ@߆[GM8&!0Ŭ'~cYa=Ke#9|BV26[}dž)ޮWS,'LqF+迈tEӏiFŝl1KŌ?ʣ% z_uCKiFJKo*8n]((((((((((((((o5ψ:2V s f EI`d׹cZf]5mH| s;# }OH‹/0|=fh 1 ?|F;]ZWݵB*U@cE#cm_!/~|%gY<Gm('zTp A`v,:Y7ŝMK/53C,iXX(SLuh֏j@ey*EB^eHe'x@^ltFE'CPZ5'TdSu@VjZ󦎘*&eB\3ZQevF*R+F鑑vJ5qi` zE"dٓ`pZp*fM%8-=Vlj)NilȌ%<f א_Z}ƛ1SUPF <A ^ {Az6׏-Jv]?t9B[U4qMRp#_/n=gV/7[ѵ>JmX,HRHA5@Z~NNJ(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@(RhQs{GIV+şθ|ia1%Q]'QEQEQEQEQEQEQEQERܿOڟ R~3m/F_vy W?>kN˖'+<م7/k<]J+{ |7}%IPl>n67wί{-qq)HrIR\Iu3+93jDB4ce|%5y=W* YA[GѩUҧUZ4R.ihUTVE!JniJW oZ:lU [=0J_ ^GR4Y)%<%F}cA\\ۯu^19er&P:xu ƂA?ɟY__,,>"6kCh4(((((()v3Be]YH|;g&2A{ʍq#o8liNIF;Xl5L]XѢ&uOegX1eƇOFC=dg*Q , ђ\u33Y䚩->*Tn~唲 5eZEVh"ue*fFsɢԋ)LN* ţU"D*1ZţE 4bghȈFVJSs^uHQMUJ"WVT$gș.hȕVTNHΑjѕqU%Z "UF** T$T3DW&v&7o*F)+E\rJ:ٌ)T3\g4Ԩ*ȵ#RET9 T)Ux2%ss TR2GX9 H#X3Y\":SOX6dD#,~85"NJyU`G͌ܳwL)`|(¨t#jQ4g?y/v.n3Tc5n6Ab]ըSըKFEۥYHZVMZCU YhHԪ1ҡՅh!T)/J7搯^*]q\U"tEb^u"b:"U%Jёj+@VJ*)9OF**OB nNjjhvĎ=Eq(J&UI=G2 QN*eZ&="#Z\9&"蔑jksI$|T*%N4) TQ)*+iLbREw0s"XY*U.D HT"X6dWXA5ab,RYb5"ÚS:W2s)^iZviZƟF`%]+Yɹl9A AZZ(QEQEQE(\9~xmkRHQkx%pHrHTAG~"x]SR[>1C,XVI*Cޫ뺥Ρy)̎$ױὤcxs.Ujc:6[>&VnyqgD*OR~67Ui)j{UEVTjɣE"f'5V6ՃFETj`sҰ6zRi@Y錈T2%Z)QםVd&Q:*b\YRj;#.TMjRkѧ#*dr2c5JU5GNF{5-[y+ƨ0*{ӱzke/5:/Jjza) b5ՄJ9%!ȢS}jk8!Q*-$i$)%N1V:81< c"Tș)HeڤH*eGR^*GSy49,u*SU*DqH́bUac?jW2u *x+*pڭ,T5 ȿR'\&TtV*j^=hЙ2UeNSGּ =r2e^jZ%Q> |xzfH9~XUY+ªF2 ۊWD>e-NH53rڬFX;)|iY>i%Z:8!#Z(:u6q` NXDi;RԎ,t ksJc>*tf$}(92$HjdHw2$(LqS0b+ H{P`U ,Y*2!":pQz{]ǿz+_hSK-€^2%E)Qv NE(ᗆ vS/Kxu_3Q.fk9ne!sʥ]`yňUX=72F0|82s-i\.&21%se*S0+(kۨy&qqD؜P9$1_z< ģDLhH%睋 v26H?;~ɾ]KǗ^,}>_1o]1oO̜""+r3<63^YF#{ M\Qk>7q}R4#zޭF͔ըQ=Z67QAVz̎JhjFj9+29xqɓPjFoYJ͎LUfѢUz΍ެՄ~3*Ga sIFE"LCR'2"dBW6Ҽʰ;a36XSGZG4]kiƚ:7(HHэrbcYI)Q5j90f$ &1AqYQV)V eG.{ը#E#V)jrVDRr)k6jr'dKY4hj&jmY?[+Q5R4#5a*-V9Ix̔ ) dSTr+NT)IVXUV,ׅ^NHEqyj^De@rm B0O 95WbMl~Xх{쩣owHkig*Ld[9\ Fy+^ M}mLO | 𥯔b.& [^ SC/q1OqfU$5aeާGHaE5b7a$FISztce#E$Y-XIh6R4㒬JƊFRը嬘)6(Kޱ75 )PW"azk6R6a%b-hA-`Ѣ沠~ d,jhGȫ UXr:MP f/jF5ׁ 2eڨkvH3TgӥTORH}rh*W!cէT ԫ\{TlhR e 5f8+ɪI+Zj*v漺 ꕣGXެo^d:x1b;z*VcG)Z9'T5f8}pUꕖ !)jtڥWHjeڬ5(YT+$^*Ú{T# T+,TZXqODTYӄX ԱXENȊlrڝU/Q59W˥^_4d~UV~|eмmbi&lu͢ p\Ml8U_Lƾ"?)+ouy }JPOI-QPgV0X` c;)ROT_O~$zZ{u /qq3%,*$ ѵѢZIk[æF;:05B_J3߳"XEmp坶=`Ns޽/&s˓M~ӻC()jU4oΧFJEjTѪtje| fFuG*2=;)LTPGHLI1SsSQcU3I%1Yi&X^E+sI9qMc5j9sPѢ 9XPWhsv zsZ`i[͊ɢysӷzjK)LڷlЄMZvM#PRc<մLיV'DjI+aȪ_=z43[5QoMj_'Se9RumϥL~M]bvj;oj*VG"QUȭqVc*8gXX۞v;ojpJ'j=*ve-Q8Xb;|vioV5G$ڧH=CS$VmUX8jT59TԂ/jԂ :U"VV/jEH*xgKH呣ByZmuoCg \[[F ōRU q@ E~*_Fz?3FEbm-hoe#S,&c1 DFO|g59,٨jVVVיRI0>' 8P_9T-Bٶ]ҿvZSVʬ)fDzZVZez#TiHRdUjv5S-#S4z= )RN:ITQUzV4S/$b99a%+)K-XjIx 'J= zk)qjR桡Cvjѷڰ o6+k[MZk[M9dNm'JfYzW,?hoۚЋd[Iԁ^}H@hh*Xy|+X˒ު5$=+qPSfXۚҭGm_1Z&TڭkW!\ּ*rvn+_j[N':1*Zڬ%p'kS+E-}UYDrToTo\G;RX*Uڮ*EhUnTX=E TXi"6dD4Z RU6#"ȋڏ.Q* ]d󕼺_.ledWFʱ;V,9 Q)I;1_g5ai ?[H =sC/,3Yj=WO¿7?౞QAe&0< nH$cgk*xߚUVZ=[Lm U5тZŅ$h~޶k[Kҹy9m/5{Sڴ\9IWsӷjDl'+iWjoiZYWR'x5-=tEN+q9]s9mEmҜ-9#X[z@E]^$dꔄt}SjKTZRu%̪"i_5g˥Y</^jǗ>%̭҈}yub *m4ldØ&ڛ`gdØm&Zm!J1BHGa"J|Glڏ,kw? 1k)džc}ƾn\%m7gs+WgXwMz_ᇃ']w9t5ٌ9-Gk=+qCN%52*fSФX^*ZS؝[5B(ojv-L5H UU S*T٪SJi HUPԡSbiO54mUSFؤнyD՟sVlPҁeB*."ط~m-bۿJҷ|bq'ۛYy[k[IҰIxfZjIt֓I뗵k͊kMثOz³En\-erc|2u+[=n qQi WAkhvq2;{JJ Xjj#XzUcּ:*)VR&G+i|*ъLWڕ>إLs\rr+GSf;)ivX ]ҟږ9oIia7m&u/Y0ʹ ²cRHF :I}+;"z+ 0͟QU%}F+ZSyzO2ŏnY_!|~?1J^" xcJo3Y+S.|Mj?f(ĆG~~Z~p>v2qkht52*e@lV ?4jv}(-< ♺ |CwQcRRwRn;sH'5)HZBqMȤ&cy4BԇŎⓚnbqLW0א?onY[^7e6_R{˄9!2I\a^D՗~Vvci/=#˘_H$`XFG͎%Tssg{@Xe_Cr>S!teVs ݝk_n*Wٞ:7㹫 sXQs^%C5  6с\ML*U.}km8/`|iHmVMKDi 3gi 9vGErS]g͆f%JńRTqS)H9ɒ TjPjұԫPZMRD 5t(Ji 0utH&"Z3O&C*%^XbUڪ1uq 1hsFZ6 fBqV-Ҭk[lc۷JԶnkg4VZұ-XdVp+*haZ"d^MVsJk'J׷`[IZ F*!^z\nwҨn?Zޱ#zՔ/٥TKTr=kgNuJ.m%L>2בQq5"{:{s5&t)sR ޥYLHYA6+1f$VjųE#DL)JT-erԙtIN*%l"ߙK8$Qr!값IW/97ѼVMW1>B(ߚͲwQ) MjDSs›ڹ|ā_XoៅX WwZ<]eSAMׅ>tç%xC_?Vj64}ceK$m~x8*@xK|1o1/v' H%xbefKkwP̲9 z_Om"6W+־YYGYj nb^qT\ӦO:nژHxPCS!Hs&ZyTԀfL^ ԫִHPx8#PjEQRb)5ypj*׊HNsʩ2jUWަ)ҨLb3UERYU,׭\Z*O -4~͚ ["6;!{Ǘ0f@ϫe[7'v~S`}w2U2ԫPS'4)-2)Ka L WCS!uLzTP!JeW@^9d=*UҦAғg;d*xJGQPل2N(0$EXߥBak&i2dPjtZ>*c5yH:VU4)n:Qkl唋qSqWكeJqQybfF٪Q*7"EQ=RCSWX.MF;ȸS+UDjzOJh`6j52xzT[GUTGUūЄThH^\vEŐԪMZVD댋RJJfԋ'RJb5R.VJT-dѪxKx5D==dExINTJiD$bN)D+$`ST2>8{UTTK=ԝ|&l ~v`05sT0i VGRo2xDt}l쑩VGzڥ[̒K+gbrX$I I{VOM++Q] ( (=3w| i1𮸋mIp ,=].9]i :R8 #}-SPhڝė>(` i,*>d'z$b8*{ v[zvQT'6bˑVP!9(5n3*m^UBq*xڪ#T+ǬtDSTժd5Glq52* x5Aղ*TjOWOBaU|DbjP^-X$ZVWUVח8m^W檣uEqR+@StFEY*S3FEL?&+&TJx *EhbĕDIOMX"ӄ5O̧ 3PW/#Iy$PY䑂(Oȸy|'4Ľ<'>c~}_}Ѱ|;t+ ^|NʶFc?svFsӂOxߞQ\(4 ( ( ( 爵 k:ƕpm5+){y†Ђz͢P>:ƕM:QY3-֖L'11 nq@?Fk G<= t唡p2RT=<%Irã^M wB6wqRx2A_9:_|fct%(jty,ASsSi\f>MNUJ$YN*tjtle LTȬ9ҬWU&i#5U6a:UF2xQZVV*+Ī2ʚMUVUŭ,bUԀבV'|$ZSRR+דR'ddYRUVy}zx(1A|S,l[35PKNsX4j\b$ޜ$FEŖ$~e4 Q/&\=ApA8 >+/[L/,< yaGAbdoc~vɂƙy5ms )uW|1t|3 <,^f!jž\YiqSj9ZCZxyY!y9YJFjsY6rȰa* ,XXN1X6rȰZ*q1eFjU=+LŖPNZ3VJ~xA9ް uBlϭJ4+5 ( ( ( ( ( ( ( (^𱲳ǎ$ռF+ "WK پPrV 3[R:3SѕZP kjZ4lWk$weB{B#?PAH AU |F[CkC}!Rxb<C+m:5ַɵM:+^OOHQ2r8в 4|a^t581*f|ԋ՘JVM-jU59$[՘Rըڱ9d[CV#5R3VQLeXCUcn*t<2ej TC&ZFոjej ԨjLi[+TכPԨU[[˨oʵHUP:JPTGLYm_pzڈ,1USWQeNjpjM"Ȓ8+hH֪I#e"ztQMwwwo[V(a\,@:HxIxG]E6bgDdW&8Pu՘8f.2hZ"]IU'uNFFTdgDHʍ 0AWSNyVOlZՔjsVc=)yr.f35$]CXhxyi3\CV4< ̚-tDl _\dXS+UTz+ԪdjZfƑi[Z+qR+s^uBl5=[X==^ʆȴRUSR+{חPd5<=UR6feT?4GDYi_]n>h2p_u(ҸfTޠM0URz3\/ -~(TkyV{ QX+!n%C QVϒnj%J7gu֯wq< ^| x*\>Ӭ]H'pz!Uw?.|ck\axoiu I(ϊ췆Pj+ޗnϵei|w|Lƽ}ZYէ^ **s٢DIES(((((((((((((SԺ敧gOܗwwcTOn^7`^'EiN>h;3i׏%Xpx'ľD~~ר TAx3j^r?vGp23m!X)?J+q`I}&1slZY `D#ynk%U3q5XY[L(xcß۶\HKeV5ہH+Am5#nн `A IV#Vuχ#]}qYFQEXғt#N6AEVEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPx_o;^hl6RԂ`8#)ݡ47ßnxw-wbKbws,"*c-^П `znvk}Rʲ@I8%q3]0Ն19.Z5O]&; ÜcYg2@èJֿ8`~<0OjW)-Ll̻XNL X$|~֍[zlTkG*==5=^U>ivMUw}oa] c*F':wNj"Մ8LX>6rztǽqʅIlyc=?asN _:7M^6}WDcK﷎3яuK_|Wy^12]HrR8\V]V ??>nlQTZ/ -׈u Yvak\>-xn:W^_R?nݩ'$䓟),OU=-Z?ڧ-. -犧ȒGfQYV%9wt9> «=Jd@1I&FS> S_FIi<.YL yyW=)(I+ )(((((((((fotoxx-18.01.1/data/KB-shortcuts20000644000175000017500000000106313222767271015130 0ustar micomicoF File View G Gallery View W World Map View M Net Map View S Sync Gallery + Zoom-in - Zoom-out Z Zoom-1x toggle R Rename Image File Ctrl+S Save File K Keyboard Shortcuts L Grid Lines T Trim/Rotate 2 Cycle 2 Previous Files 3 Cycle 3 Previous Files U Undo Shift+U Redo C Retouch Combo V View Metadata (short) fotoxx-18.01.1/data/README0000644000175000017500000000036313222767271013455 0ustar micomicohome-defaults/* --------------- This directory contains files that are copied into the user/Fotoxx home directory ONLY IF they do not exist. They may be customized by the user. user/Fotoxx home directory: normally /home//.fotoxx fotoxx-18.01.1/data/widgets.css0000644000175000017500000000103113222767271014746 0ustar micomico/*** This file reduces the size of some GTK widgets by reducing unnecessary padding. You may make changes or comment-out text to restore the standard GTK widgets. Do not delete the file, since it would be automatically restored. ***/ /*** disabled modifications window, box * { background-color: #DDDDDD; } ***/ .button { padding: 0px 4px 0px 4px; /* top right bottom left */ } button { padding: 0px 4px 0px 4px; } scale { /* slider control */ padding: 6px 6px; } fotoxx-18.01.1/data/quickstart-ca.html0000644000175000017500000002462513222767271016245 0ustar micomico

Inici rápid de Fotoxx

 

Menú Ajuda > Guía de l'usuario. Si us plau, llegiu les primeres págines. F1 per ajuda desde qualsevol punt.

Sincronitzar arxius : funciona després de la primera instal.lació i després només si cal. La sincronització torna les cerques més ràpides.

Zoom : clic dret/esquerra o amb la roda del ratolí. Moure/fixar la pantalla augmentada : arrossegueu el ratolí per la imatge.

 

Pestanyas :
  F conté la imatge actual.



  G (Galería) miniatures del directori o col.lecció actuals o resultats de cerca. Clic en una miniatura per

  veure-la a tamanya complert a la pestanya F. Navegui clicant noms de directori a la part superios de la finestra.

 

  W és un mapa mundial. Clic sobre una localitat per veure les imatges relacionades.


Botons de la barra d'eines

Obrir sel.lecciona una imatge com a imatge actual a la pestanya F.
Desar desa una imatge sobre l'arxiu original, com una nova versió, o amb un nou non d'arxiu.
Anterior/Següent veura la imatge anterio/següent en el directori o col.lecció actual o en el resultat d'una cerca
Desfer/refer

Veure l'estat abans/després d'una edició o una sèrie complerta d'edicions.

 

Editar una imatge:
Obri una imatge. Sel.leciioni un menu (p.ex. Retocar > Gamma). 

Ajusti els controls del diàleg o arrossegui la corba - vegi com canvia la imatge. Premi (Fet) per acabar.
Actuar de la mateixa manera per fer mès edicions. Obri el botó [Desar] i desprès sel.leccioni [Nova versió]

 

Menú Eines: preferències d'usuari, funcions en lot (moure, reanomenar, convertir, processar RAW)

 

Menú Metadades:
Afegir dades a les imatges : títols, comentaris, etiquetes (paraules clau), geoetiquetes (localitats)...
Cercar imatges utilitzan aquests criteris, noms d'arxiu o directori, o qualsevol altre matadada EXIF/IPTC


Menú Árees: sel.ĺeccioni objectes o àrees per editar separadament. Copiar i enganxar entre imatgeso dintre de la mateixa imatge


Menús de edición

matge voltejargirar, tallar/retallar, redimensionar, voltejar, escriure un text
Retocar

brillantor, color, contrast, gamma, mapeigg de tons, "pintar" edicions de retoc

Reparar enfocar, suavitzr, suprimir soroll, ulls vermells, esborrat intel.ligent, pintar/clonar, corretgir franges de color
Color balanç de blancs, canviar colors, blanc i negre, color/positiu/negatiu, convertir perfil de color
Deformar corretgir prespectiva, adressar corbes, aplanar pàgies de llibre, diversos tipus de deformació
Efectes posteritzar, convertir en un dibuix o pintura, relleu, mosaïc, punts, vinyeta, texturitzar
Combinar combinar múltiples imatges en un HDR, HDF, pila, panorama, fotomontatge
Complements
usar qualsevol altre programa editor d'imatges (p.ex. GIMP, ImageMagic...) com una funció d'edició de Fotoxx


fotoxx-18.01.1/data/quickstart-en.html0000644000175000017500000002017713222767271016262 0ustar micomico quickstart

Fotoxx Quick Start

Menu: Help > User Guide
Please read the first few pages. In Fotoxx, use key F1 to get help for whatever you are doing.
  
Index Image Files - This runs for initial installation (slow) and afterwards as needed (fast).
Creates an index of key image data. This makes thumbnail galleries and image searching fast.
  
Image Zoom:  Left / right mouse click, or mouse wheel. 
Image Scroll / Pan:
  Drag the zoomed image with the mouse.

View Modes - These three buttons select the viewing mode    

Image File: View the current image using the entire window.
Gallery: View a thumbnail gallery of the current directory, image album or search results. Navigate the directories containing your image files. Click thumbnail to view full size.
Maps: View a world map, zoom in or out to any scale, click on a marked location to view a gallery of photos from that location. Requires good internet connection.

Menus

File: Open an image file to view or edit. Print, delete, trash, view a gallery of recently seen images or newest images. Open a RAW file.
Save: Save a modified image to the original file, new file version, or new file name.
Open previous or next file (right / left mouse click). Move sequentially within a gallery.

Metadata: Edit captions, comments, ratings, tags (keywords), geotags (locations) ...
Search images using these criteria, file and directory names, dates, or any metadata.

Areas: Select image areas to edit separately. Copy and paste within or across images.

Edit: trim / crop, rotate, resize, brightness, color, contrast, tone mapping, add text ...

Repair: sharpen, clean noise, fix red eyes, adjust color, paint, clone ...

Warp: fix perspective, straighten curves, various types of warping ...

Effects: posterize, make a drawing or painting, emboss, tiles, dots, cartoon, vignette ...
Undo / Redo: Left / right click for before / after view, within an edit or for each completed edit. Middle click: see all past edits, view image at selected edit stage.
Combine: combine multiple images for HDR, HDF, stack, panorama, montage.

Process: batch convert, export, tags, geotags, metadata, search.
Tools: index images, user settings, keyboard shortcuts, various utilities

Help: Read the user guide, check release notes for recent changes.

To Edit an Image
Open an image (click thumbnail). Select an edit menu (e.g. Edit > Retouch Combo).
Adjust the dialog controls or drag the curve. Watch the image change. Press [Done].
Do additional edits likewise. Press [Save] and then select [New Version].

fotoxx-18.01.1/data/custom_kernel/0000755000175000017500000000000013222767271015445 5ustar micomicofotoxx-18.01.1/data/custom_kernel/denoise30000644000175000017500000000026513222767271017104 0ustar micomicokernsize == 3 cell0101 == 1 cell0201 == 2 cell0301 == 1 cell0102 == 2 cell0202 == 4 cell0302 == 2 cell0103 == 1 cell0203 == 2 cell0303 == 1 fmul == 0.0625 fadd == 0 end fotoxx-18.01.1/data/custom_kernel/emboss50000644000175000017500000000064413222767271016751 0ustar micomicokernsize == 5 cell0101 == -1 cell0201 == -1 cell0301 == 0 cell0401 == 0 cell0501 == 0 cell0102 == -1 cell0202 == -1 cell0302 == 0 cell0402 == 0 cell0502 == 0 cell0103 == 0 cell0203 == 0 cell0303 == 1 cell0403 == 0 cell0503 == 0 cell0104 == 0 cell0204 == 0 cell0304 == 0 cell0404 == 1 cell0504 == 1 cell0105 == 0 cell0205 == 0 cell0305 == 0 cell0405 == 1 cell0505 == 1 fmul == 1 fadd == 0 end fotoxx-18.01.1/data/custom_kernel/emboss3-B&W0000644000175000017500000000027013222767271017316 0ustar micomicokernsize == 3 cell0101 == -2 cell0201 == -2 cell0301 == -1 cell0102 == -1 cell0202 == 0 cell0302 == 1 cell0103 == 1 cell0203 == 2 cell0303 == 2 fmul == 1.5 fadd == 200 end fotoxx-18.01.1/data/custom_kernel/blur50000644000175000017500000000064513222767271016426 0ustar micomicokernsize == 5 cell0101 == 1 cell0201 == 2 cell0301 == 3 cell0401 == 2 cell0501 == 1 cell0102 == 1 cell0202 == 2 cell0302 == 4 cell0402 == 2 cell0502 == 1 cell0103 == 2 cell0203 == 3 cell0303 == 9 cell0403 == 3 cell0503 == 2 cell0104 == 1 cell0204 == 2 cell0304 == 4 cell0404 == 2 cell0504 == 1 cell0105 == 1 cell0205 == 2 cell0305 == 3 cell0405 == 2 cell0505 == 1 fmul == 0.0175 fadd == 0 end fotoxx-18.01.1/data/custom_kernel/sharpen30000644000175000017500000000027513222767271017117 0ustar micomicokernsize == 3 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0102 == -1 cell0202 == 16 cell0302 == -1 cell0103 == -1 cell0203 == -1 cell0303 == -1 fmul == 0.125 fadd == 0 end fotoxx-18.01.1/data/custom_kernel/colors30000644000175000017500000000027413222767271016757 0ustar micomicokernsize == 3 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0102 == -1 cell0202 == 9 cell0302 == -1 cell0103 == -1 cell0203 == -1 cell0303 == -1 fmul == 10 fadd == -700 end fotoxx-18.01.1/data/custom_kernel/last-used0000644000175000017500000000064413222767271017275 0ustar micomicokernsize == 5 cell0101 == -1 cell0201 == -1 cell0301 == 0 cell0401 == 0 cell0501 == 0 cell0102 == -1 cell0202 == -1 cell0302 == 0 cell0402 == 0 cell0502 == 0 cell0103 == 0 cell0203 == 0 cell0303 == 1 cell0403 == 0 cell0503 == 0 cell0104 == 0 cell0204 == 0 cell0304 == 0 cell0404 == 1 cell0504 == 1 cell0105 == 0 cell0205 == 0 cell0305 == 0 cell0405 == 1 cell0505 == 1 fmul == 1 fadd == 0 end fotoxx-18.01.1/data/custom_kernel/emboss30000644000175000017500000000026313222767271016744 0ustar micomicokernsize == 3 cell0101 == 2 cell0201 == 1 cell0301 == 0 cell0102 == 1 cell0202 == 1 cell0302 == -1 cell0103 == 0 cell0203 == -1 cell0303 == -2 fmul == 1 fadd == 0 end fotoxx-18.01.1/data/custom_kernel/sharpen50000644000175000017500000000067613222767271017126 0ustar micomicokernsize == 5 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0401 == -1 cell0501 == -1 cell0102 == -1 cell0202 == -1 cell0302 == -1 cell0402 == -1 cell0502 == -1 cell0103 == -1 cell0203 == -1 cell0303 == 40 cell0403 == -1 cell0503 == -1 cell0104 == -1 cell0204 == -1 cell0304 == -1 cell0404 == -1 cell0504 == -1 cell0105 == -1 cell0205 == -1 cell0305 == -1 cell0405 == -1 cell0505 == -1 fmul == 0.0625 fadd == 0 end fotoxx-18.01.1/data/custom_kernel/darken30000644000175000017500000000026313222767271016720 0ustar micomicokernsize == 3 cell0101 == 0 cell0201 == 0 cell0301 == 0 cell0102 == 0 cell0202 == 1 cell0302 == 0 cell0103 == 0 cell0203 == 0 cell0303 == 0 fmul == 0.8 fadd == -0 end fotoxx-18.01.1/data/custom_kernel/sketch30000644000175000017500000000027213222767271016735 0ustar micomicokernsize == 3 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0102 == -1 cell0202 == 8 cell0302 == -1 cell0103 == -1 cell0203 == -1 cell0303 == -1 fmul == 6 fadd == 300 end fotoxx-18.01.1/data/custom_kernel/color lines0000644000175000017500000000026413222767271017603 0ustar micomicokernsize == 3 cell0101 == -2 cell0201 == -2 cell0301 == -1 cell0102 == -1 cell0202 == 0 cell0302 == 1 cell0103 == 1 cell0203 == 2 cell0303 == 2 fmul == 2 fadd == 0 end fotoxx-18.01.1/data/custom_kernel/emboss5+color0000644000175000017500000000064713222767271020066 0ustar micomicokernsize == 5 cell0101 == -1 cell0201 == -1 cell0301 == 0 cell0401 == 0 cell0501 == 0 cell0102 == -1 cell0202 == -1 cell0302 == 0 cell0402 == 0 cell0502 == 0 cell0103 == 0 cell0203 == 0 cell0303 == 1 cell0403 == 0 cell0503 == 0 cell0104 == 0 cell0204 == 0 cell0304 == 0 cell0404 == 1 cell0504 == 1 cell0105 == 0 cell0205 == 0 cell0305 == 0 cell0405 == 1 cell0505 == 1 fmul == 2 fadd == -100 end fotoxx-18.01.1/data/custom_kernel/brighten30000644000175000017500000000026213222767271017255 0ustar micomicokernsize == 3 cell0101 == 0 cell0201 == 0 cell0301 == 0 cell0102 == 0 cell0202 == 1 cell0302 == 0 cell0103 == 0 cell0203 == 0 cell0303 == 0 fmul == 1.2 fadd == 8 end fotoxx-18.01.1/data/custom_kernel/shake30000644000175000017500000000026613222767271016552 0ustar micomicokernsize == 3 cell0101 == 2 cell0201 == 2 cell0301 == 2 cell0102 == 2 cell0202 == -12 cell0302 == 2 cell0103 == 2 cell0203 == 2 cell0303 == 2 fmul == 0.333 fadd == 0 end fotoxx-18.01.1/data/custom_kernel/outlines50000644000175000017500000000067113222767271017323 0ustar micomicokernsize == 5 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0401 == -1 cell0501 == -1 cell0102 == -1 cell0202 == -1 cell0302 == -1 cell0402 == -1 cell0502 == -1 cell0103 == -1 cell0203 == -1 cell0303 == 24 cell0403 == -1 cell0503 == -1 cell0104 == -1 cell0204 == -1 cell0304 == -1 cell0404 == -1 cell0504 == -1 cell0105 == -1 cell0205 == -1 cell0305 == -1 cell0405 == -1 cell0505 == -1 fmul == 1 fadd == 0 end fotoxx-18.01.1/data/custom_kernel/outlines30000644000175000017500000000027013222767271017314 0ustar micomicokernsize == 3 cell0101 == -1 cell0201 == -1 cell0301 == -1 cell0102 == -1 cell0202 == 8 cell0302 == -1 cell0103 == -1 cell0203 == -1 cell0303 == -1 fmul == 1 fadd == 0 end fotoxx-18.01.1/data/metadata_short_list0000644000175000017500000000056413222767271016555 0ustar micomicoMake Model Creator Copyright Comment Description Caption-Abstract User Comment Orientation Flash Flash Mode Focal Length in 35mm Format Shooting Mode Exposure Time F Number Metering Mode Exposure Compensation ISO White Balance Focus Mode Light Source Color Space ICCProfileName Image History Date Time Original Location Country GPS Latitude GPS Longitude Keywords Rating fotoxx-18.01.1/data/slideshow-tone.oga0000644000175000017500000004157213222767271016240 0ustar micomicoOggSk\K(Nvorbis"VWOggSk\Kr-vorbisXiph.Org libVorbis I 20070622vorbis$BCV@B*c:!B)B!$C:5cGdBɁАU@WPrI-sWq s gq %s9r1sWr)-sGqsGqsm1r9s Rr5sgr %s gq s5r9s9s9s1s9sn1s9s9s9s 4d(( @qGK$  YHHXfi&z(*iʲ,˲. HPQp Yd`(8XYP GM$<<<<<<<  Y (dBCV@!CR\ BCBC)%cSA!|={АUa8$!b'Dq !$Xy$݃B{˹{ 4d B!B)RH)b)s1 2蠓N:ɤN:$Rk)SLc֜sA)c1c1c1# YdA!RH)r1BCVER$Gr$G$ɒ,I<˳<˳;2ϏsfVJ_wCW֑yK}Vf_T7oVu|M@s.a*>{-6<`Pn{6JuT_ko2dLm(#m松YZ Ⱥ4sӹG={Vr#Id%X3ᄄv_-}gߺDYh%+ 3lTy"w@l@`q2qoڧd8X{P?X`dn <>ilO`9Pz׊u׆MSZ dJMi;m AedU Q^ n@q'AA7 (PYz\UG&FK saF> 9k @' E~: uCl8,$ CӖ+`a_7aL zך`٣D.^B@[@@^el!kj i4$ګ G.x7 )2</$@~_Kudך* ~_A@"aC`E'r{/݇@[+4 Bj 9*Ψ;z|c FMϵbfR`[=$`k B kO5Ȼفmޫ h"O #z\[y$SO?TI1` 2>Kՠ@30* (߃g p}3 ^' Novz u6p`Ti^O Uv "e_ʎ9C=7QLH>u-ХI ( 0\w2T{W!MK08 0|}2>c~3w*@c@L s dsqa[흋pT"͛.kO( e&w'7|u-&ΌgQz$Ew½Z̶T\ҁDL#y:uysu[ʷɷR?.Rl#̅-6}\3$w7Z M!+y/xzy U $KU\}  {.NmqsH *x:Fk7 >  ~M$gWϝU|6 h @(0N*;wK#1P24k)5|Wp tt8@pJW*ddl)6l!>ofnTy`{SPDeNN0?'F`~>7 `3BE ~:ǀbaJUmxs/ Ș78@D5"ASG.1*v;tN `{ 7ٚ  >08q(@~jłZU-& A k0OOSy2N͚NEC" bo\7p>Zlr7@:u)eB G%D-D<.x~mSA@`~K|5t`sE8HQP@z ZGG^Qϝњ`ZޡUK@] W&Ht3[?]$ `_+Գm/ހ!}m-n<` @ < \%NYᶰEq,M % gInzQ۷uh%{eg=z`wa'd!(ND"\.)KI]pa &I!A`.E=Au{U&33%lݺiNd_}a][am8ϵ'%}7~2L&!mw6m?g۾,IX.IڳVוּ_+7ktnX=0A% 'Ym'<{oy^^j?imjNRBe/_ۣcMO߹w s"aoɩB4J#ޛ{zb|Zzlbܡ`i9Nւ f\'(KTg_3ޜg *=Vǘ!#Ns@l(ۼbj jZp:<>.S_0;ފPi3T@y}(`+Q QطEP%`彥SFJx%sjZQ@ ^W<ygAs 0/؞;p`] i.3Qy= RD4ڶ:,ރ>Ub5B\1ʕjU(l/mfx\zǕAA_j `@ޚS) ,9}̠!%EFl83Aﺵ_Ov`m7)E1o|3pf2 +hG{sM׆s=k%0HF2P=҉ځ@)rO#Ԧ1n   HOҏ\Fv|,UjTmYE6h&Y?Y_|% .9.]r q0-)FD;mފE Xj-@+4pFwp< y=ҽ`jBT֒kI;5za8} g٪$50M܆zݡ `MrOos%" .* (<.,_~{]h@"7@&l[K͂_'\@r@'pzp^jM,YM0=kYٖcm@,Ⲳ֋?쪶ʷ?eWֻ: W*3 ?lxuwz 80͆_o9>J] 2Uuw$T| bh @/{"5v]=6;txFbPUUյZ;@ې[KIyM)h 28p_WpP0 w)Jd 6-%2ͣK`gPn8D\Ӷ/hNwX=zi@e/-.^3`1@~ zĭ=F;6&oNYZd!x| X)(veDzl];G>:xIZ@@`N@?7OggStk\K$ro|v~{|~yt$[U{6*ش<y_c^ MR!i>o|+Ox!A%~b, \՞ydaQYx/O`+2PU @`0y*GxhU0Y9o/\H0` ?}pnmse2AGl KA~S`ca0y; ST2L۬@DPijE|K= |푭?d뵒̀jK&$"%ZtΪ >!@Y?=BU`-t>`6iYY}v=vxt%Z(l^ @mKe7ou?<{Ӡ HUG:~y,ű@rudfF|`zoG,X" j0S7rlkyF'@`.,| /&p"="GA+HE0eƖ|~NS`@Ah(vn*?]0,+ޛ7`\@>gTHpn#8vhz\@tM >X': Z{tz;]W+ܬ{b8?wZLH;doϽn3v<"7W W zvg=Y:0؛q (? GD]"|} Xz/۷5mRQ@*d`U`qo l``mp@@L8: s  ziotwDi@%pe]@ hM@N9DTfS8k 覽rP F*XXW?߀?g&Z最Ձ ,wҫx7ET/p<(s)zg!G0(-0Ji)|`c@p1#>2{nGuP^e@??mІ|m+pCkP 1ߎi AΈA)Xf۽F 6%7Zۜ& MADI/!8g4ry)CMO`0 ,/ tZtK/ zfm.xygчU-]06>{wЯzĴ @DAkA7kٳ3aV!|`4@ Z`ԯx:Sw @|f9\`&>L4@hrdž KwҁdxI/{Bd/wݍ[EdK}Uwkxm2D ~x95`Ihț&,ۗ W@{="ʉz.ѯ?m,)rWi?F)'32g~3 %`3C7 C@x&: tc͸a I? CDa"@Yޕyɠ {=I?K@ D `{v׬stˢZh#ej8{Ep>x9*;G3p|l\TdDPtZiGh[(@J}#'}鵝tYE$ڟ!& hչ魮0E#W(nbNqh819dfs0_'*C$%1"r~{mtTm8l|PŢiJ@_ j+2{f?KcDFS1+%&8f&#x`@b7Ij$ߐ6_}7W3vj |" b%l;0'oPIt"~D(9DJ¿Ezknz̾.;3c-;Ax IFv/3V>Iy.w~H1"""l2|O|ރvG 3N%.W/fXn}yULQө{<_$NοΞkׯ_LIy3;pȸK|d0GD~xmJ_/$˗f_۬&IbdDDDp!Amy-jo̍`g>VpPy8}i@?sӞ9g` /2DDDDS:;ŞZ* Zk1K޹m;`ڼu ׎Nnpf4RmUܚ^fbV:t>=޾| gܿ3ud` ^xy H M]HN2G*# w;qkׂK m$7-Z̧$B | 1-~P`h@fao5=;͞{h[? 7|t;˟oD8M{[P$Ļ98(xS0/HU</$IN$ݯ7'&A˸'7H^-Hiw O&^PP4W"9az52u4jhi)9Cָ3n&S ݹ-i:{Y5˔?A>#(TS^ |`7~az`CD1";΀ }AMyjRIɳlj'1oΙ@{wٕRP27Ofת[-/`=lmyRdwefxG`u3oKq7>\0 cQ$[:D#" ~y+CTrY|#^z4"\>-I>)GĚpK#ϓ ^^V[&ȩ'.;,}ƌ x y@? tΰu`$ĈNgA٩J~p@>pc[4Ǔl;&"t(go=j :4e.w\wo]_Mwy}ݞ;]X}nLϜuVJq0OggSk\K^\$bpxH$>60j@!"r!F%^!紩r)/Z-1h/#?6o ̡G|g7'۝Nww"Qhrw66}[.rz1Mjto^ƋȩC~xRpx Ep>0 !Ĉ$/S/Si(A?XE}5~D4px[0TcXT }{ ܢ&]̚g|CKɚ?zl7u930k7nYI^g x>x\j\*^|`:P9]oD-$"Ɉ#"r_:['p-L, NnۛMKk]4bO|@$4el5)*/y޳ϸdsw#^:sqIޘOH4o$_G#Z{T9Fhw"U+؄˱ 1 @ q1DHke # f +}0rʼng :G?) GǜVyfJH9cX3JZD;>%xŶvEfZ^Y}}o}M_vއ"5 ł @?^B50>I"FDr"]wkhDޏQVzg_scz6tr?X1^潛c~z(;,X]8׃@5{0pyp{@ &&U$dA?lT\&$bDy-#G;w]i6(U5q> NЯu[(t`N]g?V~M#Z/ _HIW?1Y޹F\ڻuG8Qwg`wp>C,m02""FĈ>zߜ%Us9ΥZyJXA-JWR$P{g~1nL?/fX$d;w/'N_nc4V._x# vBȻ@ \ }I\KaH1"r( 5IiQ=xET$ɵj C1 ϖVM7t'r~Y<ͺԩqzŤe~Pks2}3bTvJ<|WwypY3"bRI"M4_ EugTQPx&M?{{q3#?KѫҟG g0=dU$ڰ\%EK{}+AxobD: X7̜ʺ3kf^y1@Vu'p>0Da@UDΈGPi_+c̊ D;WRS})ђ˟&k=z5G~I)QlҜrSVhM i7HO_TT:O1.fܿiЄjcj R9v0wy)&mZrDDs>/r(R/.˚c`;"<;|Uܸ{^~;fޭI;澙!XߟY߼HfݘHges,'h՜o y@-81o*5^zm1[s'G :U:DS-xFY,!HI4_q33v)i^?"tMrME*R@VtjCO]%B&&7+/[7-tYon5>%?[?QɁ0wyI808R#"""^~W[F8wVW} }?hJEdIIJ*UuqgiMܐؙYӹ1˓mާ;O!`4},j:IgK?Ww~odv;I#v0wyp> a!*4D(w>즜Dz/~"d09Mj@_>Q jyOK[bEo|w~{M\[cHBw6!~YrusFgΞ_ьAa>v0y%C[~^`DbĈ@b㎺oGފgl"TʮH+ _!.7_a/f\cw%Y)to_n5\lh.YsilO!j117.{%촹e9;w:i ^yA0 ༀٯ f~y'&RI>q_*Z4.6mFRFKB^"Ж6~寣Ѽ#JOR*MxLBaܿe k$ߚ~ $E\%d-9wg4,_atb$3r!YlpN' f܁y}asw>Dh_l3Q]_I^mu]VN>u9RE(JEoΑx5|1s@Lvͧ/'毒sLa?݌/7w\Pkp[r en&ehg0  $#"O㕗;oG/uQHn AAN/c 4>i_cDb'4FN +mҷ"-/$$.ş&%WK~v< 01 SF8∈CL}P kHcڴ~W7fpJw @if&R7}2˹y2n?w.Xw.4X}OJEJtgV7M'])1F\5~y0 s ã_3,bRc0)@Iף96L0c1U+-Nl4l3z+ ĕ#^'=^g $F3}2: Ƣ#?s^ Z`wJw\Zľ{zr̘o\u^yT<`_ljD!I$!"hwāc@B* kg I]dsio^J)w'3i]덷ٽ}C ~gx< cH%0Q.}  BC䑾K&}{nB'D r7KM0'y3f:;Z͜p_dernߴ<~A >lhCDDDDv;8O:UP0 =fOG3lL~b$ͧZn.1MU[ZgeML^nf5KWkؙ?J[%o.ݶ'TV_i By/@p^߭aqR%IFP2cN"z>?VT%YA؁B15nN%00#Vox8wHԱϟvqok{"Aӟ_6yށAy/$p$ʬ>f_`Yݒ~yDO|!4i2)|h'%@k疮c]ğ%H_{3IV΄1 O5r皛Lֽ?zW{ZN3I.zk&DDDDĈ H{ʂu/ ѴAu{}W*#}~$XP"ܚ8N2cs̴ssL.jr&cN$IezFOܨ/F=#Y9(&)P _B2IDDDDDP=M^Idy+IIגO1va 14厦|= Ӓ`l殚AqC>aYό۽+sh 2#%2R^F4zf:493Z̐܁wFQps0Ŝ~5}fz瘞.랻ի Fotoxx User Guide
Fotoxx User Guide  v.18.01  best window size ==>

Review recent changes to this document.
 
 

Overview

Description
Fotoxx is a free open source Linux application for editing photos and managing a large collection. The goal of Fotoxx is to meet the needs of serious photographers while remaining fast and easy to use. Fotoxx has a rich set of editing, repair, and special effects functions. Image adjustments are displayed instantly in a full-size image, allowing interactive optimization. Fotoxx can be used to manage a large image collection so that finding desired images is fast and easy. Albums can be easily created and arranged. Images can be viewed by clicking location markers on an interactive world map. A detailed list of Fotoxx capabilities is available below.
 
Hardware Requirements
Fotoxx works best on a strong PC, e.g. 2+ GHz multi-core CPU, 8+ GB RAM. A weaker PC will generally work, but may be slow for some functions and unable to edit large images. A monitor smaller than HD (1920x1080) may feel confining for some functions. The monitor should have accurate color (most do not).
 
Software Requirements
Most recent releases of popular Linux distributions will work (Debian, Ubuntu, Fedora, Suse, Arch ...). This must be 64-bit Linux. Fotoxx Debian packages are built and tested using Ubuntu. A source tarball and make file is provided to build Fotoxx for Linux flavors that use other package formats or have incompatible libraries.
 
License and Warranty
Fotoxx is licensed under the GNU General Public License v3. Fotoxx source code is free to use, modify, and share with others. Fotoxx is not warranted for any purpose, but if you find a bug, I will try to fix it.
 
Origin and Contact
Fotoxx originates from the author's web site: kornelix.net.
If you have questions, suggestions, or a bug to report, you may contact me.
 
Downloads
The latest source code and install packages (debian format) are available at kornelix.net. Fotoxx packages are also available on many other web sites and Linux distributions. Some of these are quite old and should be avoided. It is better to use the above link. If there is a problem, it is easier to fix if you are using the current release.

Updates and Bug Fixes
Fotoxx is updated periodically with new or improved capabilities and bug fixes. Serious bugs are generally corrected with a day or two of being known, if possible, and the kornelix.net web site is updated with a new release. Other web sites and Linux distribution repositories may or may not update for bug fixes. To be notified of new releases, subscribe to the blog kornelix.blogspot.com (a few messages per year).

Optional Package - fotoxx-maps
This is a set of geographic maps covering the world. They show image locations as markers (red dots) that can be clicked to display a corresponding image gallery. Modern cameras with GPS receivers automatically add geotags (earth coordinates) to photos, and these are used by Fotoxx to locate images on a map. Geotags can also be added to images individually or with a batch utility. You can add maps of your own at any scale, and your images will populate the new maps automatically. A source tarball and Debian package are available as described above. If you have a good internet connection, you may not need this package: Fotoxx can also use interactive and scalable maps obtained as needed from an internet map service.
 
User Guide
Fotoxx is easy to use but unconventional. To avoid getting stuck, please read the introductory sections of this user guide. The rest can be referenced as needed during use, by using the F1 key.
 
Translations
Translations of the user interface are available for French, German, Spanish, Catalan, Italian, Portuguese.
If you can help with translations, review the topic Translations.
 

Fotoxx Capabilities

  • Thumbnail browser/navigator with variable size thumbnails and list view.
  • Camera RAW file conversion, single or batch, with retention of 16 bits per color.
  • Internal processing in 24 bits per color (float), image file output in 8 or 16 bits.
  • A comprehensive set of image edit, retouch and repair functions are available:
    brightness, color, contrast, trim/crop, detail enhancement, resize, rotate, sharpen,
    de-noise, paint, clone, red eyes, text, warp, HDR, panorama, montage  ...
  • A large set of special effects and arty transformations is available.
  • Select image objects or areas, edit separately from background, copy and paste.
  • Rapid visual feedback using the full image or a selected zoom-in area.
  • Metadata editing and reporting (tags, dates, captions, geotags, any metadata ... )
  • Batch editing: record a series of edits on one image, execute on many images.
  • Batch tools for file renaming, resizing, converting, adding and revising metadata.
  • Search images using any metadata and directory/file names or partial names.
  • Generate a table of image locations and date groups, click for gallery of images.
  • Generate an image calendar, click on year or month for a gallery of images.
  • Find images by clicking markers on a scalable world map (internet map source).
  • Use locally stored maps (world, continents, nations, cities). Add custom maps.
  • Create albums with selected images. Arrange sequence by drag and drop.
  • Slide show: use various animated transitions and slow pan/zoom in or out.
  • Mashup: arrange images and text in an arbitrary layout using the mouse.
  • Print an image at any scale. Printer color calibration utility.
  • Show video files (first frame) like regular images, click to play video.
  • Comprehensive user guide and help popups via F1 key.
 
 
Table of Contents

Fotoxx Overview
description, prerequisites, license, downloads, capabilities
Fotoxx Usage
    Quick Start 1-page overview document
    User Guide comprehensive Fotoxx usage guide (this document)
    Installation Fotoxx software installation procedure
    Initialization Fotoxx initialization procedure
    Window Views and Menus top-level windows and associated menus
        File View file view window (view image files)
        Gallery View gallery view window (view thumbnails)
        World Map View world map view window (local map files)
        Net Map View net map view window (internet world map)
    File Drag and Drop
file drag and drop methods
    General Image Edit Procedures
overview - how to edit image files
        Edit Workflow
alternative edit methods - simple and complex
        Dialog Mouse Ownership
dialog-mouse control and interaction
        Dialog Completion Buttons
dialog completion options
        Custom Dialog Widgets
how to use custom dialog widgets
        Curve Editing
how to manipulate curves used in edit functions
        Batch Editing
overview of batch editing functions
    Select Images from Galleries
file selection procedure used for many functions
    Menu Shortcuts
popup menus, keyboard shortcuts, favorites menu
    Managing a Large Collection
collection management and image searching
    Right-Click Popup Menus
popup menu functions for file and gallery views
    Keyboard Shortcuts
keyboard shortcuts for file/gallery/map views
    Mouse Functions
mouse functions for file/gallery/map views
    Command Line Parameters
Fotoxx startup options using command line parameters
    Top Panel Status Information
details of information in the file view top panel
File View Menus

    Favorites Menu
graphic popup menu with user-selected functions, icons, and layout
    File Menu

        New Window
start new Fotoxx session in a separate window
        Sync Gallery
open gallery view from current file view image
        Recent Images
open gallery view of most recently seen images
        Newest Images
open gallery view of newest images in the collection
        Open Image File
find and open an image file for viewing or editing
        Cycle 2 Previous Files
cycle through the two most recently seen image files
        Cycle 3 Previous Files
cycle through the three most recently seen image files
        Open RAW File
open a RAW file for viewing or editing
        View 360° Panorama
view a 360° panorama with a rotating viewpoint and wrap-around
        New Blank Image
create a new blank image with specified size and color
        Blank Window
blank or restore (toggle) the current image window
        Rename Image File
rename an image file
        Copy/Move Image File
copy or move an image file to another directory
        Copy Image File to Desktop
copy an image file to the desktop
        Copy to Clipboard
copy an image file to the clipboard
        Copy to Image Cache
copy an image file to the image cache
        Show on Net Map
show an image file position in Net Map View
        Delete/Trash Image File
delete or trash an image files
        Print Image File
print an image file
        Print Calibrated Image
print an image file using printer color calibration data
    Save to Disk Button
save an image file (replace, new version, new file name)
    Previous/Next Image Button
show the previous or next image in the current gallery
    Metadata Menu
 
        View Metadata
list image metadata, short or long form
        Tags Overview
image tags (keywords) used for image searching
        Geotags Overview
image geotags (location data) used for image searching
        Edit Metadata
edit commonly used metadata
            Manage Tags Dialog
manage user-defined tags
            Adding Geotags
add image geotags from user inputs or web service
        Edit Any Metadata
edit any metadata (add, change)
        Delete Metadata
delete specified metadata or all metadata
        Show Captions on Image show file name, captions, comments in image corner
    Area Menu

        Overview
select area - concepts and usage
        Select Area
selecting an image object or area for separate editing
        Find Area Gap
find gap in hand-drawn area outline
        Select Hairy
select a complex area, such as hair or plants
        Show/Hide Area
show or hide area outlines
        Enable/Disable Area
enable or disable an area for editing
        Invert Area
invert an area (exchange outside/inside areas)
        Unselect Area
remove an area
        Copy and Paste Area
copy an area and insert somewhere else later
        Open and Save Area
save an area to a file, open and insert elsewhere later
    Undo/Redo Button undo or redo current edit or any edits to current image
    Edit Menu

        Trim/Rotate
Trim unwanted margins, rotate or level an image
        Upright
Upright an image rotated 90° or 180°
        Resize Image Change the image pixel dimensions
        Voodoo1
Automatic 1-click enhancement that may work
        Voodoo2
Automatic 1-click enhancement that may work
        Retouch Combo
Edit brightness, contrast, color, saturation
        Edit Brightness
Edit brightness distribution, rebalance dark and bright areas
        Gradients Increase local contrast to enhance details
        Flatten
Flatten brightness within local zones to enhance details
        Retinex Rescale RGB brightness values to remove color cast and fog/haze
        Mirror Image
Mirror an image horizontally or vertically
        Paint Image Mouse-paint with a color, brush size, and opacity
        Clone Image Mouse-paint a copy of one image area over another
        Blend Image Blend or blur image pixels by painting with the mouse
        Add Text
Write text on an image with special effects
        Add Lines
Add lines and arrows to an image
        Paint Edits
Paint a retouch function gradually, using the mouse
        Leverage Edits
Apply a retouch function using brightness or color as a regulator
        Plugins
Use another image edit application as a Fotoxx edit function
    Repair Menu

        Sharpen Sharpen a blurred image
        Blur
Blur an image or image area
        Denoise
Reduce noise (speckles) in low-light images
        Red Eyes Remove red eyes from flash photos
        Color Mode Convert to black & white, color, negative, positive, sepia ...
        Color Depth Reduce the number of colors (posterize)
        Shift Colors Gradually shift RGB colors to GBR or BRG
        Color Saturation Increase or reduce color saturation (intensity)
        Adjust RGB/CMY Adjust image colors using RGB or CMY adjustments
        Adjust HSL Use HSL to select and change image colors
        Zonal Colors Fix a false color cast that varies within an image
        Match Colors Match the colors in one image to those in another image
        Color Fringes Reduce chromatic aberation causing color fringes
        Smart Erase Remove power lines, trash, other small image spoilers
        Brightness Ramp Add a brightness/color ramp across an image
        Remove Dust Remove dust spots on images scanned from old slides
        Anti-Alias Remove pixelation (jaggies) in low resolution image
        Stuck Pixels Fix bright/dark 'stuck' pixels from camera sensor defects
        Paint Transparency
Paint increasing transparency using the mouse
        Add Transparency
Add transparency based on image brightness or color
    Warp Menu

        Unbend
Fix images having curved lines that should be straight
        Fix Perspective
Straighten an object photographed from below or aside
        Warp Area
Warp a selected image area by pulling with the mouse
        Unwarp Closeup
Reverse distortions of a close-up face photo (e.g. big nose)
        Warp Curved
Warp entire image or area using the mouse - curvy warp
        Warp Linear
Warp image by pulling with the mouse - straight lines preserved
        Warp Affine
Warp image by pulling with the mouse - parallel lines preserved
        Flatten Book Page
Flatten and straighten a photographed book page
        Spherical Projection
Curve and image into a sphereoid shape
        Selective Rescale
Rescale image down, leaving selected areas unchanged
        Make Waves
Warp an image with a wave pattern
        Twist Image
Twist an image around a chosen center point
    Effects Menu

        Sketch
Convert a photo into a simulated sketch
        Cartoon
Convert a photo to a cartoon-like drawing
        Line Drawing
Convert a photo to a simulated line drawing
        Color Drawing
Convert a photo to a simulated color drawing
        Graduated Blur
Graduated image blur depending on contrast
        Embossing
Convert a photo to a simulated embossing (3D effect)
        Tiles
Convert a photo into simulated square tiles
        Dots
Convert a photo into a Roy Lichtenstein style dot matrix
        Painting
Convert a photo into a simulated painting
        Vignette
Change brightness or color radially around a chosen center
        Texture
Add texture to an image or selected area
        Pattern
Add a background pattern to an image
        Mosaic
Create a mosaic using tiles from all available images
        Custom Kernel
Edit and apply a custom convolution kernel to an image
        Directed Blur
Blur an image area in a single direction via mouse drag
        Blur Background
Select foreground areas, blur the background (bokeh, tilt-shift)
        Alien Colors
add random strange colors to an image
    Combine Menu

        High Dynamic Range
Combine images for an extended brightness range
        High Depth of Field
Combine images for an extended depth of sharp focus
        Stack / Paint
Combind imges to remove transient cars, tourists, etc
        Stack / Noise
Combine images to reduce noise by averaging
        Panorama
Stitch together a series of images horizontally
        Vertical Panorama
Stitch together a series of images vertically
        PT Panorama
Stitch together a series of images using Panorama Tools
        Montage
Join many images into a compact table format
        Mashup
Arrange multiple images and formatted text in a layout
    Process Menu

        Batch Convert Files
copy, move, resize, rename, convert, add text ...
        Batch Upright
scan for rotated images and upright them
        Batch Delete/Trash
delete or trash selected images
        Batch RAW
convert RAW files to JPEG/PNG/TIFF, 8 or 16 bit color
        Script Files
record edits to a script file, apply script to multiple selected files
        Burn Images to DVD/BRD
create a DVD or BlueRay disc with selected image files
        Find Duplicate Images
search entire collection and find duplicated images
        Export File List
create a list of selected files (e.g. for use in a shell script)
        Export Image Files
export selected files to a directory (e.g. for web upload)
        Batch Add/Remove Tags
add or remove tags for selected images
        Batch Rename Tags
rename tags for selected or all images
        Batch Photo Date/Time
change photo dates or times, or shift time zone
        Batch Add/Change Metadata
add or revise metadata for selected or all images
        Batch Report Metadata
report metadata for selected or all images
        Batch Geotags
add or revise geotags for selected or all images
        Image Locations/Dates
list image locations and date groups, click for gallery
        Image Timeline
table of year/month/image counts, click for gallery
        Search Images
search images using any metadata or file name data
    Tools Menu

        Index Image Files
1-shot job to find and index all image files
        User Settings
user settings and preferences
        Keyboard Shortcuts
show and revise keyboard shortcuts for menu functions
        Brightness Graph
show a brightness distribution graph for the current image
        Grid Lines
show or remove grid lines, set line count or spacing
        Line Color
set color for select area outlines
        Show RGB
show RGB values at mouse location or for selected points
        Magnify Image
turn mouse into a magnifying glass over image
        Dark/Bright Pixels
tool to highlight clipping
        Monitor Color
tool to adjust monitor brightness and contrast
        Monitor Gamma
tool to adjust monitor gamma
        Change Language
change the language of the menus and dialogs
        Missing Translations
list missing translations for the current language
        Color Profile Convert color profile (e.g. sRGB <--> Adobe RGB)
        Calibrate Printer
calibrate printer color - compensate for color shifts
        Uninstall Appimage
remove the appimage package completely
        Show Resources
show CPU time and current memory usage
    Help Menu
show user guide, recent changes, log file, web site
Gallery View Menu

    Favorites Menu
(same as above, duplicated for convenience)
    Gallery Menu

        Sync Gallery
open gallery view from current file view image
        All Directories
show all image file directories in a tree structure
        Bookmarks add bookmarks, view list of bookmarks, go to bookmark
        Manage Albums
create and arrange user-defined image collections
        Update Album Files
update albums from updated images
        Replace Album File
exchange an image file in selected albums
        Slide Show
create a slide show with animated transitions and zooms
    Metadata Menu (same as above, duplicated for convenience)
    Gallery View Buttons

        Zoom-in / zoom-out
zoom the gallery thumbnails from zero (text only) to huge
        Sort Gallery
sort gallery by file name, file date or EXIF photo date
        Scroll Gallery
scroll gallery in pages back or forward, jump to start or end
    Combine Menu
(same as above, duplicated for convenience)
    Process Menu
(same as above, duplicated for convenience)
    Tools Menu
(same as above, duplicated for convenience)
    Help Menu
(same as above, duplicated for convenience)
World Map Menus
maps using local map files
    Images by Map Location
click on map marker for gallery of images at location
    Choose Map
choose from available maps (USA, France, NYC, etc.)
    Installing Custom Map Files
create and install custom map files
    Set Map Markers
show all map markers or only those for current gallery
    Help Menu (same as above, duplicated for convenience)
Net Map Menus
maps using an internet map source
    Images by Map Location click on map marker for gallery of images at location
    Net Map Source
set web source for map
    Net Map Locations
define or go to named map location (position and scale)
    Set Map Markers show all map markers or only those for current gallery
    Help Menu (same as above, duplicated for convenience)
Other Topics

    Video Files
show and play video clips included in image collection
    Web Service Photo Upload
how to select and upload a batch of images to Flickr, etc.
    Organizing Images for Search
options for image organization and searching
    Translations
adding or updating a translation
    Recent Changes
list of recent user guide changes
    Technical Notes
some technical information about Fotoxx
 
 
 

Fotoxx Usage
 

Quick Start

This 1-page document is shown when Fotoxx is started the first time.
It is also available via the menu Help > Quick Start.


User Guide
(this document)
Fotoxx is easy to use but unconventional. To avoid confusion, please read the first few pages of this guide. The rest can be used for reference as needed. The user guide is available in the menu Help > User Guide. When using Fotoxx, press the F1 key at any time to view information for the current or last used menu function.
 
Fotoxx uses the term directory, which is the equivalent of folder in Windows land. The terms file and image and image file mean a single file of type JPEG, PNG, TIFF, RAW, etc. containing a single image (photo or other graphic image).


Installation
To install Fotoxx, try the appropriate package first: packages. This may work with one click.
If not, you must install from source code. This is not difficult. Instructions are here: tarballs


Initialization - Image File Index
Fotoxx needs to know where all your image files are located (directory and file names) and their embedded metadata (dates, tags (keywords), geotags, captions, comments, ratings). This data is indexed for fast searching. Fotoxx also creates thumbnail images so that the gallery windows (thumbnail pages) will work fast. Fotoxx does not modify or copy your image files - it only reads them to make the image file index and thumbnails. The file space required is typically 2% of your image collection size.
 
When Fotoxx starts the first time, you must provide information for the initial file indexing process.







top image directories (one or more)











thumbnail directory
 
extra metadata items to include in the index
 
Top image Directories
These are the top directories containing your image files, e.g. /home/<user>/Pictures  or similar. Subdirectories underneath your top directories are automatically included, to any depth. Use the [select] button to locate and add your top image directories, one or more. Other files may be mixed with your image files.
 
Thumbnails
This is the directory where thumbnail files will be placed. These are generally less than 2% as large as your image files (30K bytes compared to megabytes). You can use the supplied default or choose another location. Indexing will run faster if this is on a separate physical disk from the image files.
 
Index Time Required
If you have many thousands of images, the initial index function may need substantial time. The speed can range from 800 to 6000 images per minute, depending on processor speed, disk speed, and average image file size. The low speed (800/min.) would be expected for large JPEG files (5 MB) on a slow disk (5400 rpm). The high speed (6000/min.) would be expected for a strong computer with 4+ processor cores and a solid state disk (SSD).

When you add new image files to your collection, the next Fotoxx startup will index only the new images, at this same speed. If there are no new image files, startup will be fast. For more details, see Index Image Files.
 
Metadata Items
The following metadata is included in the index by default: directory and file names, photo date/time, file creation or modification date/time, rating (1-5 stars), tags (keywords used for searching), caption, comment, geotags (country and city or location names, geocoordnates or latitude/longitude). Searching a large image collection for one or more of these items is almost instantaneous.

You can also choose up to 20 other metadata items to include in the file index. You may not need this. You can skip this initially and add items later if needed. See Index Image Files for more details.
 
Note: You can bypass indexing if you hate this idea (probably misguided, but the customer is always right). See the topics Index Image Files and User Settings. Search and map functions will be disabled if there is no index, and thumbnail galleries will be slow.
 
 
 
Window Views and Menus 

 
Use these buttons (window top left) to switch among the viewing modes:

Image File View: the current image file fills the entire window

Gallery View: thumbnail gallery (directory or other image grouping)

World Map View: local maps with clickable markers to display corresponding images

Net Map View: internet maps with clickable markers to display corresponding images

Each view has a row of menu buttons on the left side. These are top-level menus. When clicked, available menu functions are shown in a popup list. Hover the mouse over a menu to see a popup description.
 
File View
 
•  Zoom an image in/out with the left/right mouse buttons or mouse wheel.
  •  Pan and scroll a zoomed image using a mouse left-drag.
  •  Use the menus (left side buttons) to edit the image and perform utility functions.
  •  The window title bar shows the current image file and directory path.
  •  The top panel above the image shows image and edit status information.
  •  Right-click anywhere on image for a popup menu of commonly used functions.
  
Gallery View
  •  Use the menus to navigate within the gallery, change the thumbnail size, etc.
  •  Thumbnail size is variable from tiny to huge, or a list view with key metadata.
  •  Click on the directory buttons at the top to go up to higher directories.
  •  Lower directories appear as folder thumbnails in the gallery. Click to go there.
  •  The [TOP] button shows a drop-down list of all top directories. Click any to go there.
      (also included:  /,  user home, Desktop, Fotoxx home, recent images, newest images)
  •  [TOP] > ALL shows a full directory hierarchy, allowing direct access to any directory.
  •  Click a gallery thumbnail: show the clicked image at full window size (File View).
  •  Right-click a thumbnail for a popup menu of commonly used functions.
  
World Map View
  •  Available only if the optional fotoxx-maps package is installed.
  •  The initial map is a very large world map (136 megapixels).
  •  Click anywhere to enlarge that map area to full size.
  •  Left-drag to pan and scroll the enlarged map.
  •  Right-click to collapse the map to fit in the window.
  •  If your image files contain geotags, the map will show corresponding markers
  •  Click on a marker to get a gallery view of the corresponding images.
  •  You can select from many supplied maps, and add your own maps.
 
Net Map View
  •  A world map from the internet can show any place on earth at any scale.
  •  Markers are present for images with geotags.
  •  Click on a marker to get a gallery view of the corresponding images.
  •  No extra packages or setups are required - works out of the box.
  •  Needs a fast and reliable internet connection for good response time.

The paragraphs below provide more detail about each of the viewing modes.
 
 
File View
The current image file is shown, filling the entire window. Click the arrow button on the left or right side to move to the previous or next image in the gallery. The current image file is a key concept in Fotoxx. This is the file that most of the menu functions will operate on. Other buttons in this view show popup menus which are used to modify the image or perform utility functions.


current view = image file view

change to gallery view

change to world map view

change to Net Map view


favorites - custom graphic popup menu

open, rename, print, delete, trash ...
save, save as new version or new file

open previous or next image file (left or right mouse click)

edit tags, captions, ratings, geotags ... search images

select areas for separate edit, copy, paste
undo or redo edit steps (left or right mouse button)

trim, rotate, resize, brightness, color, contrast, add text ...

sharpen, denoise, red eyes, fix color, paint, clone

fix perspective, warp, warp image ...

special effects, arty transforms
combine images: HDR, HDF, stack, panorama, montage, mashup
process - batch move / convert / modify files, search functions

index images, user settings, edit KB shortcuts, magnify ...

help, user guide, change log, edit translations ...
  
 
Zoom Image in and out, pan and scroll a zoomed image
To zoom the image in the main window, left-click a position on the image. The image will grow with each click and the clicked position will move to the center. A right-click will restore the image to fit within the window. To pan or scroll a zoomed image, left-drag the mouse across the image. The image can move with the mouse or in the opposite direction (like scroll bars), depending on a user setting (menu Tools > User Settings). The movement may be 1:1 with the mouse, or may be magnified for faster movement. The mouse wheel may also be used to zoom the image in or out. The middle mouse button (wheel) will make a zoomed image re-center at the mouse position.
 
 
Gallery View
All image files in the current directory are shown as thumbnails. You can scroll through the thumbnails and navigate to other directories. The arrow buttons allow scrolling forward or back. You can also scroll with the mouse wheel or scroll bar. Use the zoom buttons to change the thumbnail size. Clicking on a thumbnail will change to file view and display the image full size. This image is now the "current image". Pressing the gallery button in file view will show the gallery view, with the current image scrolled to the top row. The directory path is shown at the top of the gallery window, with one button per directory level. Click one of the buttons to go to that directory. Its subdirectories will be shown as folder thumbnails. Click one of them to go to that directory. Click the [Top] button to choose another top image directory (if more than one), the root directory ( / ) your home directory (/home/<user>), a gallery of the most recently viewed images, or a gallery of the newest images added to your collection.


change to image file view

current view = gallery view

change to world map view

change to Net Map view


favorites - custom popup graphic menu
sync gallery, albums, slide show

larger thumbnails

smaller thumbnails

sort gallery images by file name, file date, or photo date

go up to gallery top

go down to gallery end

go up one page

go down one page
combine images: HDR, HDF, stack, panorama, montage, mashup

process - batch move / convert / modify files, search functions
index images, user settings, edit KB shortcuts, magnify ...

help, user guide, change log

There are several types of galleries:
  •  Directory: all the image files in a single directory (folder)
  •  Search results: images found by a search function, in various directories
  •  Images in an Album: these may be located in various directories
  •  Recent Files: the most recently viewed or edited images
  •  Newest Files: the images most recently added or modified in the image collection
  
The gallery window title bar will show the directory name (path), the album name, or Search Results or Recent Files or Newest Files. If the gallery corresponds to a directory, buttons for navigating to parent directories are shown in the top panel. The other gallery types have only the buttons for Album (select an album) and TOP (go to another directory).
 
If the current gallery is not a directory gallery (e.g. Recent Files), you can navigate to a directory by using the [TOP] button in the navigation bar above the thumbnails.
 
The [Sort] button is used to sort the thumbnails by file name, file modification date, or photo date (from EXIF data). The displayed date is photo date unless the sort is by file date. Ascending or descending order can be chosen. Directory galleries retain their sort order and scroll position when viewed later (i.e. when other galleries are viewed in-between). The [Sort] button dialog has a button to restore all galleries to their default order: file name ascending. Albums retain the order they were made with. They cannot be sorted.
 
A gallery thumbnail has a right-click menu with some commonly used functions. One of these is Popup Image, which creates a popup window with a larger image that can be rapidly zoomed to any size with the mouse wheel. Many popup windows can be open at once. This is useful for comparing multiple photos of the same subject or multiple edited versions.

Popup Image Manipulation:
  •  Click thumbnail with middle mouse button: popup image appears
  •  Click thumbnail with shift + left mouse button: popup image appears
  •  Mouse scroll wheel: zoom the image bigger or smaller
  •  Key F11: make popup image full screen, or return to prior size
  •  Left mouse button: kill the popup image
  •  Escape key: kill the popup image
 
 
World Map View
Initially a world map is displayed. A left click on any area will expand that area to a much larger size, and a right click will return to the whole map view. The mouse wheel also works. Markers are shown where there are images with a corresponding geotag location. The markers can be clicked to show a gallery view of the corresponding images.


change to image file view

change to gallery view

current view = world map

change to Net Map view


choose from available maps (Europe, USA, France ...)

set map markers, all images or current gallery only

The Choose Map menu offers a selection of large-scale continental and country maps, and you can also install other maps (countries, cities, parks, etc.). These all work the same way. Markers are shown where there are images with a corresponding geotag location. The markers can be clicked to show a gallery view of the corresponding images. The Markers button allows you to restrict the markers to images the current gallery (directory, album, search results, newest, recent).

The initial maps are provided in a separate package: fotoxx-maps. Fotoxx-maps is large - almost 100 MB of maps are provided. You can also add your own maps, explained in the topic Installing Custom Map Files.
 

Net Map View
This view is similar to the above world map view. The functionality is generally superior, but it depends on having a fast and reliable internet connection. An internet map service is used. Initially a world map is displayed. Use the mouse wheel (or double-click) to zoom-in to any location. Shift the map center using mouse drag. The entire world is represented and can be viewed at any scale down to street-level. You can choose from two map sources, one completely open and one requiring a key (free for normal usage volume). If your internet connection is slow or unreliable, it may be best to use the previous World Map method. This is also the only way to make a custom map.
 

change to image file view

change to gallery view

change to world map view

current view = Net Map

choose internet map source

set map markers, all images or current gallery only
select named map view (location and zoom level)
 

File Drag and Drop
Fotoxx accepts drag-drop of image files to F-view or G-view mode. In F-view, the file is opened. In G-view, the result depends on the type of gallery. If the gallery is a directory, the file is added to the directory and the gallery will show the new file in its sorted position, by file name. If the gallery is an album, the file is added to the album in the position where it is dropped. This works also if a text string that is a valid file name is dropped.
 

General Image Edit Procedures
The image in file view mode (the current image) can be modified with the edit functions in the menus Edit, Repair, Warp, Effects, Combine. These functions modify the current image in memory and as seen in the window. You can use these functions in any order, and the changes are accumulated. When finished editing, use [Save] to save the modified image back to the same file, save to a new file version (e.g. filename.v01.jpg), or input a new file name and/or directory. Image edit dialogs have sliders, spin buttons, or editable curves that immediately update the image. The reaction time depends on the size of the image, the complexity of the function and the speed of your PC. This is typically less than second for most edit functions on a strong PC.
 
Click the undo/redo button on the left or right side to undo or redo the currently active edit function. After an image has had one or more edits applied, the undo/redo button can be used to go back to prior edit steps or forward to the last edit step. The middle mouse button shows a popup list of all edits done to the current image, and you can select any step to go back to. The image can then be re-edited from this step.
 


Edit Workflow 
You can minimize the time needed to process multiple images if you understand the following:
  • Choosing a new edit function will automatically complete a prior active edit.
    The new edit starts with the end result of the prior edit, and an undo/redo position is created.
  • Opening a new image file during an edit function will automatically cancel the edit.
    The edit function is restarted with the new image.
  • The [Save] button can be used during an active edit to save the current image status.
    The edit function restarts automatically, and an undo/redo position is created.
  • Some frequently used functions have a [Prev] button which recalls previous settings,
    making it easier to process multiple photos needing the same or similar adjustments.
Example Workflow for the initial rotate and trim (crop) of a new batch of photos.
You can process one photo every few seconds (+ think time).
  1.  Copy files from the camera memory card to a directory.
  2.  Open the first file.
  3.  Menu: Edit > Trim/Rotate.
Repeat steps 4-7 for each image. The Trim/Rotate dialog remains open.
  4.  Level the image if needed (drag the right edge).
  5.  Trim the image by dragging the trim borders to suit.
  6.  Press [Save] and choose "new version".
  7.  Press [Next] to edit the next file. Press [Next] again to skip over.

Simple Workflow
Most of the time you can just edit the JPEG file that comes out of the camera. Use the following more complex procedure only if you see "color bands" or "posterization" after editing the image, an indication that JPEG 8-bit color is limiting the image quality.
 
Complex Workflow
To edit with deep color (more than 8 bits), you can start with RAW files from your camera.
There are three options for processing RAW files:
  • Open a RAW file directly with Fotoxx: use the [Open] button or click the gallery thumbnail. The RAW file may now be edited normally. Save the edited image file as type TIFF or PNG with 8- or 16-bit color, or JPEG with 8-bit color. You cannot save the edited image as a RAW file type. Opening a RAW file may need several seconds, especially on a weak PC. Saving an edited image as TIFF or PNG is slower than JPEG.
  • Open the RAW file with the File > Open RAW menu, or right-click the gallery thumbnail and use the open RAW popup menu. This opens a specialized RAW editor (currently Raw Therapee). Save the file in the RAW editor as a TIFF-16 file, which will pass back to Fotoxx for further editing if wanted.
  • Use the Process > Batch RAW menu. You can select many RAW files and convert all of them to type TIFF or PNG with 8- or 16-bit color, or JPEG with 8-bit color. You may then select and edit these files with Fotoxx.
RAW files may have more color depth than 8 bits, especially if the camera is new and very expensive. Conversion into a PNG or TIFF file with 16-bit color will preserve the additional color depth available in the RAW file. The higher color depth reduces the risk of visible color bands when edit functions are used that can radically shift the brightness distribution. When finished editing, convert the final file to JPEG (quality level 70 or greater) to reduce the final file size to 10% or less. Note that editing in deep color is more important than having deep color in the final image. It is very hard to see any difference between a 16-bit TIFF/PNG file and a high quality JPEG made from that same file. To preserve the possibility of re-editing the image later, keep the RAW file, which is smaller than the TIFF or PNG file.
 


Edit Dialog - Mouse Ownership
Some edit dialogs use the mouse to reference or alter the image in the main window. There may be more than one such dialog active at the same time. The mouse is also used to zoom and scroll the image, and you may need to do this while using a dialog. Therefore it is important to understand who owns the mouse (which dialog, or main window) and how to change the ownership:
  • The mouse is owned by the dialog that was last clicked or used. Mouse clicks and drags on the image are inputs to this dialog and DO NOT zoom or scroll the image.
  • If you hold the CTRL key down while clicking or dragging the mouse, the image will zoom or pan/scroll, and active dialogs are not affected.
  • The mouse wheel can always be used to zoom-in/out on any part of the image.

Edit Dialog completion buttons
mostly work as follows:
    [Proceed] - proceed with lengthy task based on dialog inputs.
    [Apply] - apply settings from dialog to image, leave dialog active.
    [Done] - same as [Apply], but the dialog is closed.
    [Cancel] - discard image changes and close the dialog.
 

Custom Dial
og Widgets for data input
A few non-standard smaller widgets are used to reduce the size of dialogs (and the amount of image area they cover).
They work almost like the standard Gnome widgets for numeric data entry, text data entry, and buttons.

A few standard GTK widgets have also been downsized by reducing unnecessary "padding". This is controlled by the file widgets.css in the Fotoxx home directory (default /home/<user>/.fotoxx). You can modify this file if desired. You can also delete or comment-out the contents, but do not delete the file, since it would be replaced the next time Fotoxx is started.
 
 
Curve Editing

Some image edit functions use editable curves. You can manipulate the curves to change some property of the image depending on some other property. The example here shows a brightness curve, whereby you can change brightness depending on brightness (e.g. brighten dark image areas without changing bright areas). Generally, the X-axis of the curve represents the input property (brightness in this example) and the Y-axis the output property (also brightness). The curves can be moved (pulled) with the mouse. "Up" increases the effect and "down" decreases the effect. An anchor point (black dot) is added to the curve wherever it is pulled, and this point remains fixed for subsequent pulls: the curve will continue to go through this point as other parts of the curve are pulled. Anchor points can also be dragged. Delete an anchor point by right-clicking it. 
 
 
Batch Editing
There are several batch functions in the Process menu to speed some common tasks.
You can select any number of image files (link) and execute the batch function for all of them.

Batch functions can be used for the following tasks:
  • Rename files, using any combination of old name, new name, sequence number, photo date.
  • Convert file types (e.g. .png to .jpeg)
  • Find and upright photos made with the camera turned 90 degrees
  • Resize files (e.g. reduce for web upload or e-mail)
  • Copy or move files to another location
  • Convert RAW files to tiff, png, or jpeg
  • Add/change/remove tags or geotags
  • Set image dates/times or shift times to change the time zone
  • Delete files or move them to Trash
  • Script Files: perform multiple edits on one image, save as a script file
    which can then be applied to any number of images in batch mode.
  • Burn selected image files to a DVD or BlueRay disc.
  • Find duplicated image files.
 
 
Selecting Image Files from Gallery Windows
This procedure is used for all functions that operate on multiple image files (combine menu, process menu, others). It is explained once here, and this topic is linked from each of the functions using this procedure.

The dialog box is used to select image files. Behind it is a gallery window for the current directory, album, or search results. To select an image, click its thumbnail and the image file will be added to the list in the dialog. Other options are shown below. You can navigate the gallery window to other directories by clicking on the directory names in the top panel, or clicking on thumbnails representing subdirectories. You can choose images from any gallery in any order. The list of image files can also be manipulated to change the sequence or remove images added by mistake. Click on a file in the list to show its thumbnail in the dialog and also set the current list position. The next image file added will be inserted at this position. If the [delete] button is pressed, the current list position will be deleted, and if the [insert] button is pressed, the last deleted image file will be inserted at the current position. The last 100 images deleted are saved and can be re-inserted anywhere: each use of the [insert] button removes one image from the saved list and inserts it at the current position. To move images to a new position in the list: click the image file (its thumbnail will be shown), click [delete]. Repeat to delete more images. Click an image file to set the insert position, then click [insert]. The first deleted image will be inserted before the selected image. Repeat to insert more images from the deleted list. The [add_all] button will add all the image files in the current gallery. You can select individual images from the gallery, or use [add all] and then delete unwanted images. A single image may also be added multiple times to the list. Image search functions output to a gallery window, and these can be used for selecting images just like a gallery from a physical directory. The keyboard up/down arrow keys may be used to step through the file list and view the thumbnails - a faster way to review the list for files to remove. The page up/down keys and the home/end keys also work. You may also enter characters from the keyboard, and the list is searched for matching names and highlighted. This is a fast way to locate a desired file or directory in the list.
 
Thumbnail click rules:
  •  left click: add the image at the current list position.
  •  right click: remove the image from the list, if present.
  •  Shift + left click: add all images from the last image added up to the clicked image.
      (works in either direction, first to last or last to first)
  

Menu Shortcuts

The Fotoxx menus are large. You may need time to get used to them and remember where the functions are located. There are three shortcut methods available for frequently used functions:
  • Right-click the main window or gallery window thumbnail.
    A popup menu appears with some commonly used menu functions.
  • Keyboard shortcuts - these are documented in a table below.
    You can also add your own shortcuts for menu functions you choose.
  • Favorites menu - a graphic popup menu with your custom contents. You can add text and/or icons that link to any menu functions you choose. You can arrange these as you wish in a layout window. You can leave this window open and access any of these functions with a single mouse click.

Managing a Large Image Collection
You can use Fotoxx to manage a huge image collection and still be able to quickly find the images you want. Some effort to organize the images is required. Search methods include directory and file names (or partial names), image (photo) dates, file dates, image ratings, tags (keywords) (labels for persons, places, objects, events ...), captions and comments, and geotags (location names and latitude/longitude). This is done in a standards-compliant manner so that data can be shared with other applications. Options for how to organize a large image collection can be found in the topic Organizing Images.
  

Right-Click Popup Menus

These menus appear when the image view window or a gallery thumbnail is right-clicked with the mouse. These are the most frequently used functions and are available as popup menus for convenience. Most of these are also contained in one of the left-side main menus for image view and gallery view, as shown above.
 
 File View popup menu
right click on current image in File View mode to get the following menu
 View Metadata
Show short form metadata report
 Edit Metadata
Edit key metadata items: photo date/time, rating, tags, captions, comments, geotags
 Edit Any Metadata
Edit any metadata item
 Rename
Change the file name
 Copy/Move to Location
Copy or move the image file to another location
 Copy to Desktop
Copy the image file to the desktop (monitor background)
 Copy to Clipboard
Copy the image file to the clipboard (for other apps to paste)
 Copy to Image Cache Add the image file to the file cache for later pasting into an album
 Replace Album File
Update album(s) after creating a new version of an image file
 Upright
Upright the image that is turned 90 degrees
 Trim/Rotate
Trim (crop) the image, level the image or turn 90 degrees
 Resize
Resize (rescale) the image width and height
 Voodoo 1 and Voodoo 2
Limited automatic image enhancement, two varieties
 Retouch Combo
Adjust brightness, color, contrast, saturation, black point, white balance
 Edit Brightness
Adjust the brightness distribution (flatten, broaden, change black and white points)
 Flatten
Enhance contrast and brighten shadows, especially image areas with low contrast
 Gradients
Enhance contrast and apparent brightness range by increasing brightness gradients
 Select Area
Select an image object or area for separate editing
 Show on Net Map
Go to Net Map view, zoom-in to image location (if earth coordinates present)
 Delete/Trash ...
Delete the image file or move it to the wastebasket


 Gallery View popup menu
right click on thumbnail image to get the following menu
 Popup Image
Show image in a larger window - resizable, movable, persistent until canceled.
Zoom in/out using the mouse wheel. When zoomed small it disappears.
Shift + left mouse click on a thumbnail: shortcut for Popup Image.
 View Metadata
Show short form metadata report
 Edit Metadata Edit key metadata items: photo date/time, rating, tags, captions, comments, geotags
 Edit Any Metadata Edit any metadata item
 Rename
Change the file name
 Copy/Move to Location
Copy or move the image file to another location
 Copy to Desktop
Copy the image file to the desktop (monitor background)
 Copy to Clipboard Copy the image file to the clipboard (for other apps to paste)
 Copy to Image Cache
Add the image file to the file cache for later pasting into an album
 Replace Album File Update album(s) after creating a new version of an image file
 Upright Upright the image that is turned 90 degrees
 Show on Net Map Go to Net Map view, zoom-in to image location (if earth coordinates present)
 Delete/Trash Delete the image file or move it to the wastebasket
   

Keyboard Shortcuts

Keyboard shortcuts are available for most functions. The notation "Alt+G" means press and hold the Alt key, then press the G key. Most of these can be changed, and new shortcuts can be added (see KB-shortcuts function). The following table is based on the default key assignments. Those that can be customized by the user are indicated.
  
 General
custom

 F / G / W / M  keys Yes
Change view mode: image File, Gallery, World map, Net Map
 F1 function key
Display user guide for current or prior function
 F10 function key
Toggle main window to full-screen and back. Menu and top panel remain.
 F11 function key

Same as F10, but without menu and top panel. Useful for image viewing.
 Escape key
Exit from an active dialog, Exit Fotoxx



 Image File View


 left / right arrows
Previous / next image (link)
 '+'  or  '='  key
Yes
Zoom image larger (zoom amount adjustable in User Settings) (keypad also)
 ' - '  key
Yes
Zoom image smaller to fit within window (keypad also)
 Z Yes
Toggle: zoom image to 100% / fit image in window
 S
Yes
Sync Gallery: set gallery from current image file
 X Yes
Magnify Image (also in slide show)
 2 Yes
cycle through the two most recently seen image files
 3
Yes
cycle through the three most recently seen image files
 U Yes
Undo current edit, or undo one edit step in the current image
 Shift+U Yes
Redo current edit, or redo one edit step in the current image
 R Yes
Rename Image File
 K Yes
View and edit keyboard shortcuts
 L Yes
Grid Lines on/off (toggle)
 T Yes
Trim / Rotate Image
 C Yes
Retouch Combo
 P
Yes
Toggle: Pause or resume a running slide show
 B
Yes
Toggle: Blank or restore the image (also in slide show)
 N
Yes
Proceed to the next image (with animation) in slide show
 Ctrl+S
Yes
Save current file
 V
Yes
View Metadata (short report)



 Gallery View


 Home / End keys
move to first / last page of image gallery
 Page Up / Down

move to previous / next page of image gallery
 up / down arrows
move up / down by one row of image gallery
 left / right arrows
move the current image (highlighted thumbnail)
 '+'  or  '='  key Yes
larger thumbnail size (keypad also)
 ' - '  key Yes
smaller thumbnail size (keypad also)
 Ctrl+H

toggle the display of hidden files and directories
 

Mouse Functions
 Image File View

 left click Zoom-in: magnify image, center at click position
 right click If image is zoomed: restore to window size. If not, popup menu with common functions.
 mouse wheel Zoom image in or out depending on wheel direction
 left drag on image Pan/scroll zoomed image, same direction or magnified opposite direction (like scroll bars)
 mouse + Ctrl key
Mouse acts on main window instead of an active dialog, e.g. Select Area.


 Gallery View

 left click thumbnail
Change to F-view, show full-size image
 right click thumbnail
Show popup menu with common functions
 middle click thumbnail
Pop up a larger image which can be zoomed in or out using the mouse wheel
 scroll mouse wheel
Scroll the gallery page up or down


 World Map View

 left click
If map zoomed small to fit window, zoom map large at clicked location
 right click
Zoom map small to fit window
 left drag
If map is zoomed large, drag map with the mouse
 left click on marker
Change to Gallery View, show all images with geotags at corresponding location
 mouse wheel
Can be used for zooming as described above


 Net Map View

 mouse wheel
Zoom the map in (2x) or out (0.5x)
 left click on marker
Change to Gallery View, show all images with geotags at corresponding location
 left drag
Drag map with the mouse
 

Command Line Parameters (long and short forms)
 file or directory path

initial image file or directory (gallery) to open
 -album "album name"
 -a
initial album (gallery) to open
 -ver  -v
output release version and exit
 -recent
 -r
show a gallery of recently seen image files, most recent at the top
 -new 
 -n
show a gallery of the newest image files (from Index Image Files)
 -prev   -p
show the last file viewed in the previous session
 -blank
 -b
show a blank window
 -lang lc_RC
 -l
language code (+ opt. region code) to use for GUI (de, de_AT)
 -menu "func name"  -m
startup menu function - Fotoxx will start with this function active.
 -index N

disable image indexing for faster startup (see below)
 -home /.../fotoxx_home

use an alternate location for user data instead of /home/<user>/.fotoxx
   
-index N  parameter
This command line parameter can be used at Fotoxx startup to partly or completely disable image indexing to obtain a faster startup time. N is 0 or 1 or 2, as explained in the topic User Settings.

Note:
the first Fotoxx startup after a reboot may be slow if your image collection is very large. Subsequent startups will be much faster since file directories are now cached in memory. For very fast startup, you can bypass indexing entirely with -index 0. Search and map functions will be disabled until you allow the indexing process to complete normally.
 
-home  parameter
The default location for user settings and data (tag names, bookmarks, albums ...) is
/home/<user>/.fotoxx. The image index file is also here. The command:  $ fotoxx -home /some/directory...  will expect all this data to be in the designated directory. This allows you to have multiple image collections that are managed separately. If Fotoxx is started for the first time with a -home parameter, the initial image file indexing process will begin. You can preserve existing data and avoid re-indexing if you copy the files in /home/<user>/.fotoxx to the new home directory before starting fotoxx with the -home parameter.


Redirect Fotoxx home directory:
/home/<user>/.fotoxx-home
The default location for Fotoxx home is  /home/<user>/.fotoxx  (like other Linux applications). This directory is where user settings and data are kept. If you have multiple Linux OS's on your computer, and you want them to share the same Fotoxx data, or if you want your Fotoxx data to be retained when you make a "clean install" of Linux, there is an easy way to do this. This is an alternative to the -home method described above. Create the file /home/<user>/.fotoxx-home  as a one-line text file which contains the file name of the intended new fotoxx home directory. When fotoxx starts, fotoxx will look here for the user data. Copy the data from /home/<user>/.fotoxx to this directory before starting Fotoxx, to preserve all current data and avoid re-indexing. This directory would typically be on a mounted volume to keep it isolated from Linux installations. Putting /home on a separate volume is a common method to do this, but this has bad implications for multiple Linux installations sharing this same /home data: Linux applications with different release versions may expect different data here, and this can lead to application bugs and even crashes.
  

Top Panel Status Information

The top panel has status information about Fotoxx and the current image file:
CPU 123%  MB 123  2345x1234x8  3.45MB  56%  edits: 3  blocked  area active  dialog open  busy 45%
CPU 123% current Fotoxx CPU load for all threads (e.g. 200%  ==>  2 CPU cores are fully loaded)
 MB 123
current real memory usage
 2345x1234x8
image width x height x depth (bits per color)
 3.45M image file size (updated when a modified image is saved)
 56% zoom status, image % size
 edits: 3 3 edits have been made and can be reversed with the [undo] button
 blocked
some menu functions are blocked until the current function is completed
 area active   a select area is present and enabled - edits are confined within the area
 dialog open a dialog is waiting for user input (if not visible, look behind other windows)
 busy 45%
some long running functions show this simple percent completion value
 
 
 
 

File View Menus

The following are the menus and buttons seen in the File View mode.
 
 

Favorites (File View > Favorites)

This is a graphic popup menu which you can populate with your frequently used functions and arrange them on the window using the mouse. An initial popup window (left image) is supplied. Right click an empty space on the window to define a new menu entry. Right click an existing entry to modify it. Use the resulting dialog (right image) to define or change the menu entry. The example above has mostly text menu entries, but the example 'warp' entry was given an icon.
 
 menu text text for the popup menu - optional if a menu icon is used
 menu func the Fotoxx function to use - the exact menu name
 menu icon menu icon - /directory.../filename.png - optional if a menu text is used
 icon size if an icon is used, its size can be 24x24 to 64x64 pixels
 close window checkbox: option to close the popup window when this menu is selected

Left drag a menu entry to move it somewhere else on the popup window. The popup window can be resized to fit the contained menu entries. Left click a menu entry to select the menu. If "close window" was checked, the popup window will close. All menu settings and icon files are saved in a configuration file whenever the popup window is closed. The configuration file and saved icons are located in the directory:
   /home/<user>/.fotoxx/favorites.
You can use either the English menu names or their translations for your locale. The menu names must exactly match the Fotoxx menus, but case is not significant. The menu text may include "\n" to represent a newline character.

Icon library: Icons for many image edit functions can be found here:
  /usr/share/fotoxx/icons/edit-funcs
 
 
 

File Menu (File View > File)
 
 
New Window (File View > File > New Window)
Start a new instance of Fotoxx in a new window, slightly offset for visibility. This is useful to compare images or to work with more than one image at a time. Both windows can be used to edit images. The new window will initially have an unmodified version of the current image file. If the same image file is edited in both windows, neither instance will see the changes made by the other, and the final result is the file saved last.
 

Sync Gallery (Gallery View > Menu > Sync Gallery)
Replace the current gallery (recent files, search results ...) with the directory of the current image file: the image file shown in the File View window.
 
 
Recent Images (File View > File > Recently Seen Images)
The 100 most recently seen image files (viewed or edited) are shown in a gallery, from which you can select files to view or edit.
 
 
Newest Images (File View > File > Newest Images)
The 1000 most recently added or modified image files are shown in a gallery, from which you can select files to view or edit. You are given a choice of using the EXIF photo date or the file modification date to determine the newest images. If the EXIF date is chosen, image files having no EXIF date are ignored.
 
 
Open Image File (File View > File > Open Image File)
This function changes to gallery view. Click on a thumbnail to open the image file, or navigate to another directory and select an image file. The selected file is opened in the Fotoxx main window where you can view or edit the image file using the menus and buttons. The main window title bar always shows the file name and directory of the current image file.

Camera RAW files can also be opened. This may need a few seconds depending on file size and processor speed.  You can proceed to edit the RAW file like any other image file. RAW files are also included in thumbnail galleries, as long as a .jpeg thumbnail image can be extracted from the RAW file (normally true). When saving a RAW file, you must specify a type TIFF or PNG (8- or 16-bit color), or JPEG (8-bit). The RAW file type (file extension) must be included in the list of known RAW file types in Tools > User Settings.
 
 
Cycle 2 Previous Files (File View > File > Cycle 2 Previous Files)
Go back to the previously opened image file, also if this is in a different directory. This differs from the button [Prev/Next] which goes to the previous or next image file in the current gallery (directory, search results, album). This function retains the current image zoom size and position, which is ideal for rapidly comparing two edited versions of the same image. Zoom-in on the area to compare, and use this function to switch back and forth between the two images. The keyboard shortcut key [2] is assigned by default.
 
Cycle 3 Previous Files
(File View > File > Cycle 3 Previous Files)
This works the same way, but the last 3 image files are rotated. The default KB shortcut is [3].
 
 
Open RAW File (rawtherapee) (File View > File > Open RAW (Raw Therapee))
Select and open a RAW camera file using RawTherapee. A file open dialog will start. Select a RAW file. RawTherapee will now open with the selected file, and you can edit the file normally with RawTherapee. When finished, save the file as TIFF-16 and exit RawTherapee. This file is now opened in Fotoxx and can be further edited, saved as JPEG, etc. The RAW file type (file extension) must be included in the list of known RAW file types in Tools > User Settings.
 

 
View 360° Panorama (
File View > File > View 360° Panorama)
The current file is opened with a special viewer for 360° panorama image files.
The file width is assumed to correspond to 360°.
Use the mouse and keyboard as follows to control the view:
    •  Mouse-drag the image - it will turn through 360° and wrap-around at the ends
    •  Left and right mouse-click will zoom-in and zoom-out
    •  Use the left and right arrow keys to pan quickly
    •  Keys '1' and '2' select two alternative projection types (not the keypad keys)
    •  Use the 'Q' key or Escape to exit the special viewer
Panoramas with less than 360° can also be viewed, but the wrap-around will show a false joining of the two ends.
This function can be used to view Google 'photosphere' images.

Gnome EOG can also be used to view Google photosphere images.
EOG needs a special setup, and EOG only works for proper photosphere images having the required metadata.
  1.  go to 
https://github.com/Aerilius/eog_panorama
  2.  download the zip file and unpack
  3.  copy the folder 
'eog_panorama'  to  ~/.local/share/eog/plugins/
  4.  install packages 
eog, exiv2, gir1.2-gexiv2-0.10  (debian package names)
  5.  start EOG with the command:  $ eog
  6.  in menu  preferences > plugins,  enable  Panorama Viewer
 
 
New Blank Image (File View > File > New Blank Image)

Create a blank image with specified pixel dimensions and color. This can be used as a background for cutouts taken from other images (via Select Area) and annotation text (via Add Text). Input a file name, choose a background color, and set the desired pixel dimensions. See also the Mashup function.
 

Blank Window
(File View > File > Blank Window) 
Toggle: blank or restore the current window. The current image file is saved and restored when the function is executed again. Assign a keyboard shortcut to have a "one button" blank and restore function.
 

Rename Image File (File View > File > Rename Image File)

Rename the current file (file view), or the file of a clicked thumbnail (gallery view). Enter a new name and press [apply]. This function can also automate the process of renaming a series of image files using a root name (e.g. an event or place name) and a sequence number. Open the first image file in the series, input a new name, and press the [apply] button. Use the [next] button to move to the next file if wanted. You can use the same name again by pressing the [previous name] button and then add a suffix or sequence number. Press the [add 1] button to increment the sequence number. A file version number (.vNN) in the previous file name is not copied, and if the file being renamed has a version number, it will be retained.
   

Copy/Move Image File (File View > File > Copy/Move to Location)

The current file (file view), or the file of a clicked thumbnail (gallery view), is copied to the given location. This location can be entered directly or chosen by a file open dialog if the [browse] button is used. Select the copy option to copy the file and leave the source file in place, creating a duplicate. Select the move option to move the file from the original location to the new location.
 

Copy Image File to Desktop (File View > File > Copy to Desktop)
The current file (file view), or the file of a clicked thumbnail (gallery view), is copied to the desktop (/home/<user>/Desktop).
 

Copy Image File to Clipboard
(File View > File > Copy to Clipboard)
The current file (file view), or the file of a clicked thumbnail (gallery view), is copied to the clipboard where other applications can access the image (if paste from clipboard is supported).
 

Copy Image File to Image Cache
(File View > File > Copy to Image Cache)
The current file (file view), or the file of a clicked thumbnail (gallery view), is copied to the image cache. The image cache accumulates images that can be later pasted into an Album. See Manage Albums.
 

Show Image on Net Map
(File View > File > Show on Net Map)
Fotoxx changes to Net Map View and the location of the current image is shown.
 
 
Delete/Trash Image File (File View > File > Delete/Trash Image File)

The current file (file view), or the file of a clicked thumbnail (gallery view), is deleted or moved to trash, depending on the option selected. Fotoxx uses the Linux desktop standard for trash. If this works, trashed image files go into the standard trash location and can be recovered later if wanted. Otherwise, Fotoxx puts trashed images into a desktop directory named "fotoxx-trash". You can delete it or move it to your Linux-specific trash. If both standard and desktop trash do not work, you must trash the files outside of Fotoxx. If you have multiple files to remove, you can leave the dialog open to avoid opening it for each file selected. If another file is opened, the file name in the dialog updates itself. In gallery view, if another thumbnail is clicked, the dialog updates itself.
   
 
Print Image File (File View > File > Print Image)

The print menu brings up a standard Page Setup dialog where you can select a printer, a paper size, and orientation. After using the [apply] button, another dialog starts for entering paper margins and an image scale. The margins can be used to shrink the image or shift it on the page. Image scale can be set in the range 5-100%, where 100% means print the maximum size image that fits within the margins. Smaller values will shrink the image proportionally. The actual print size (image width and height) is updated in the dialog as margins and scale are changed, and this can be used to reach a desired printed image size. After the margins dialog, a Print dialog starts for the actual printing. This includes paper type and quality inputs, and a preview of the printed layout which can be accepted or rejected.
 


Print Calibrated Image  (File View > File > Print Calibrated Image)
This function works like Print Image File described above, but before printing you are asked to supply a calibration file name which is used to adjust image colors prior to printing. The purpose is to compensate for color distortions caused by the printer. See the topic calibrate_printer for details on how to create a calibration file.
 
  

Save to Disk (File View > Save)

In the first dialog, select one of the three options: new version, new file name, or replace file.
 
New Version: Save the current image file with a new version number. File names with version numbers are formatted "filename.vNN.ext" where NN is a version number 01 to 99.  The 4 characters .vNN are inserted between the file name and extension. If the file name has no versions, version .v01 will be created. If file versions are already present on disk, then the next higher version number is used. If the file is a JPEG file, the default quality is used (this value can be set in User Settings). A keyboard shortcut can be assigned to this function if desired.
 
New File: The 2nd dialog shown above will open to save the current image file to a selected file, which can be the original file, another existing file, or a new file. An edited image file can be saved in formats JPEG, PNG and TIFF. JPEG is normally the best option, since these are compressed to reduce space. You can choose a JPEG quality value in the range 1-100. Lower values give smaller files and less image quality. Values above 70 are generally hard to distinguish from 100 (highest quality, largest file size). PNG files are compressed without any loss of quality and are larger than JPEG files of the highest quality. TIFF files are uncompressed and larger than JPEG or PNG. TIFF and PNG files may be saved with 8 or 16 bits per color. The 16-bit formats only makes sense for files converted from a RAW format having more than 8 bits per color. It is rare that the difference between 8 and 16 bits per color can be seen with the eye. However, an image with higher bits has more latitude when the brightness level or distribution is altered with a program like Fotoxx. PNG-16 files are smaller than TIFF-16 but slower to save due to the compression process. Saving a file as TIFF or PNG can be quite slow for a large image and a slow computer. If an image has transparency information (e.g. a Warp function creates a non-rectangular image shape with transparent peripheral areas), you should save the image as a PNG file if you want the transparent areas to be preserved for some later operation. If you use JPEG, these areas will be black and opaque. JPEG does not support transparency.
 
If make current is checked, the saved file (new file name) will become the current file. The source file (old file name) remains unchanged. If not checked, the file is saved with the new name, but the current file remains the source file (old name). In either case, the edit history is retained (i.e. Undo and Redo will still work).
 
Replace: Save the current image file back to itself. If a JPEG file, the default quality is used (this value can be set in User Settings).
 
File sizes for a 15-megapixel image are roughly as follows (depending on image detail).
The jpeg numbers are the quality value given when the file is saved to disk.
tiff-16 tiff-8 png-16 png-8 jpeg-100 jpeg-90 jpeg-80 jpeg-70
112 MB 38 MB 78 MB 21 MB 8 MB 2 MB 1 MB 0.7 MB
 
The default JPEG quality is used unless you change the value in the New File dialog. The default value can be set in User Settings (initially 90). You will not be able to see a difference between a file saved with quality 90 and one with 100, but the difference in file space is huge. The Technical Notes section describes potential loss of image quality from repeated open, edit and save of JPEG images. At the default quality of 90 this issue can be generally ignored.
 
 


Previous/Next Image Button (File View > Prev/Next)
Keyboard left and right arrow keys
Click with the left or right mouse button to open the previous or next image file in the current gallery. If the current image has unsaved edits, you are warned and given the option to cancel this function. If you proceed, the edits are lost. You must save an edited image before moving to another image. The keyboard left and right arrow keys have the same function.
 
Directory Jump
If you attempt to go beyond the first or last image in the current gallery (directory), a popup message informs you. If you try again to move in the same direction, the last image file in the next preceding gallery, or the first image file in the next following gallery, will be opened. If there is no preceding or following gallery, or if it has no image files, then nothing is done other than a popup notification. Note that only the next preceding or following directory in the parent directory of the current directory (gallery) is searched. There is no extended search into further directories or subdirectories. If the current gallery is not a directory (e.g. the Newest Images gallery), then there is no preceding or following gallery.
 
 


Metadata Menu (File View > Metadata)
 
Metadata means text data that is stored inside an image file. Digital cameras create some data automatically, such as date and time, technical data about the camera and photo parameters, and location data (if the camera has a GPS receiver). Other data can be added by the user, such as captions, comments, ratings, and tags (keywords that can be used to search images, e.g. persons, places, things, events).

There are several alternatives for organizing a large image collection so that it can be easily searched. It would be good to review these before choosing an organization system.
 
 
View Metadata (short) (File View > Metadata > View Metadata (short))
View Metadata (long) (File View > Metadata > View Metadata (long))

The View Metadata functions will display available metadata for the current image file. EXIF metadata contains the date and time of a photo, shutter speed, focal length, pixel dimensions, etc. Digital cameras store this data inside the image. IPTC metadata contains tags (from Fotoxx, Photoshop ...) and captions (frequently found in published images). If an image is edited and then saved, the metadata is updated and stored with the new image. The View Metadata short report outputs the most commonly needed data, including the photo date and time, user-assigned tags and star rating, comments, caption, and a history of Fotoxx edit functions that have been applied to the image. The long report reports all metadata available. The [Extras] button on the short report opens the dialog shown on the right, where you can add extra items to the short report. Any item you see in the long report can be added to the short report. Click an item in the left column to add it to the right column and to the report. Click an item in the right column to remove it. Click the entry "Other Item ..." to type-in any item name not contained in the existing list, which is limited to the most likely needed items.

Fotoxx uses the following EXIF/IPTC data items for image editing and searching:
 
Key Name Fotoxx data input method
Date/Time Original Edit Metadata function - image date
Keywords Edit Metadata function - image tags
Rating Edit Metadata function - image stars
User Comments Edit Metadata function - comments
Caption-Abstract Edit Metadata function - caption
Geotags Edit Metadata function - location, geocoordinates
Image History
Fotoxx edit functions add to this list
any key Edit Any Metadata, Delete Metadata
 
There are also several batch functions for entering data into many image files at once. These are described below.
 
 
Tags Overview
The Edit Metadata function (below) is used to add tags to an image. Read this overview first.

Image files can have identification or classification tags (keywords, labels) assigned to them. These can be used to search a large image collection for those images having desired tags. Typical tags: the main subject of a photo, the associated event, the location, the persons or things contained, etc. Tags reside inside the image metadata (IPTC:keywords). Tags are normally one word, but a short phrase with imbedded blanks or other delimiters can be used. Commas and semicolons are recognized internally as delimiters (separators) between tags, and therefore cannot be used within a tag. A compound tag like arizona scenery is allowed, but you should use two tags instead for more flexibility: you can search for images having either tag or both tags.

Regardless of the physical organization of your images (directory and file names), tags can be used to create other organizations. All images having a desired tag or tags can be found quickly and displayed in an image gallery window, where you can further review the images and choose those for viewing, editing, or changing their tags. If you have used directory and file names in a meaningful way, you can search for images using these names as well as tags. You can also search images by date, rating, location, and other metadata items. These need not be duplicated in tags. See Search Images below.

Managed Tag System
This is appropriate if you are starting from near nothing and you are able to plan your tag system before adding tags to your images. In this system, you create a limited number of tag categories (e.g. people, places, things, events, travels, art ...). You then plan the tags or types of tags that will go into each category. Tags are created and assigned to a category as needed during the process of tagging images. An image is tagged by pointing and clicking on the list of available tags, which is organized by category and alphabetically within category. If a new tag is needed, it is created before it can be assigned to an image. The total number of tags should be less than about 500, to keep the list small enough for rapid visual location of tags to click on. Searching images by tags is also done by pointing and clicking on the list of available tags.
 
managed tag system advantages
  • prevent inconsistent tag names (e.g. landscape, scenery)
  • prevent alternate spellings and typos (e.g. susan, susy, scenery, scenrey)
  • prevent tags that logically include other tags (e.g. landscape, lake)
     (this can be planned and deliberate, but should not happen by accident)
  • searching is reliable because tags do not have the above errors
 
A large tag list (over 500) slows down the process of tagging images due to the time needed to visually find the tag in the long list (possibly in a scrolled window). This problem is reduced somewhat because a few most recently used tags are shown separately in the edit dialog, where they can be easily seen and chosen. Since a series of photos made at the same time will likely share many tags, adding tags to such a series is made easier and faster.
 
If tags are broadly defined and fewer in number, search results for tags will be larger, but using the search results (image gallery window) to find a smaller subset of images can be quite fast. Physical file organization is preserved: image files located together in their directories will also appear together in search results.
 
Images downloaded from the internet often contain tags. These of course have no organization and are collectively chaotic. If you use a managed tag system, it is best to review such images and clean up the tags to conform with your system, or delete them. Whatever tags are present will be automatically added under the category "nocatg". If you notice strange tags in your tag list, use Search Images by tag to find the images needing tag deletion or renaming.
 
Random Tag System
You may prefer to invent tags as needed with no particular system in mind. Or you may already have thousands of tags, making a conversion to a managed tag system difficult (but not impossible: Fotoxx has a function to mass convert tag names). In this case, you can simply type tags into your images, creating new tags as needed. There is still a limited capability to keep tags organized: existing tags matching the characters you input are shown as soon as there are only a few possible matches. Example: you type "lan" and a list of existing matching tags is shown: landscape, landscapes, Langley ...  If one of these is your intention, you click on it to select the tag. If not, you keep on typing and eventually press Enter to create a new tag. When searching images for tags, you can type desired tag names or pick them from the list of available tags. Available tags matching the first few letters you type are shown, and you can pick from this list. You cannot enter a search tag that does not exist in your images.
 
 
Geotags Overview
The Edit Metadata function (below) is used to add geotags to an image. Read this overview first.
  
Modern cameras can record the location of each photo, using an internal GPS receiver. Latitude, longitude, city or location, and country are recorded in the EXIF metadata of the image JPEG or RAW file. The Edit Metadata function also allows location data to be entered or revised. Locations may also be specified by clicking on a map. There are three functions that can find images for a specified location or region: Search Images: find images by location name (also multiple names and partial matches). Image Locations: find all images for a country, a country and location, or a country, location and date range. Images by Map Location: click on a map to show all images within a range of the clicked location.
 
 
Edit Metadata (File View > Metadata > Edit Metadata)
The Edit Metadata function is used to add metadata to an image.
Please read Tags Overview and Geotags Overview (above) before using Edit Metadata.


























Edit Metadata is used to edit the most frequently used metadata: image date and time, rating, caption, comments, location data, and tags. The dialog initially shows existing data for the current image. After making additions or changes, press [Apply] to update the image file. There is no automatic file versioning for metadata changes, but if you want a new version, use File Save > New Version before using [Apply].

The dialog shows the metadata for the current image in File View, or for a clicked thumbnail in Gallery View. The dialog updates itself when a new image is opened or new thumbnail is clicked.

The date of the image, if available, is shown as Image Date. This may be entered if missing, or changed. You can enter a full date in the format yyyy-mm-dd or a shorter format yyyy or yyyy-mm. A missing month/day is logically equivalent to 01/01 when used as a low limit for searching, or 12/31 when used as a high limit. The [Prev] button fills-in the date from the previous data entered. This is to allow easy dating of a series of images. If time is important, you can include a time using the format hh:mm[.ss].

You may enter a caption, a comment, and a  stars rating for the image.

If the location data is available from any previous image file, enter the first few characters and press [Find] to get a list of locations to choose from. For the first time entry of a location, or to add location data to many files at once, see the topic Adding Geotags below.

Existing tags are shown in Image Tags. Available tags are shown in the Defined Tags window below. One of these tags can be added to the image by clicking it. A tag can be deleted from the image by clicking it in Image Tags. Tags recently added are shown in Recent Tags. This is a convenience to make adding tags to a new batch of images easier, assuming that many of the same tags will be used repeatedly. Point and click the same way.

If the list of defined tags is long, it may be easier to type the desired tag into Enter New Tag. Existing tags matching what you have typed so far will appear in Matching Tags, and you can point and click one of these to add the tag to the image. If the input tag is new (no matching tag is shown), press [Add] when the tag is complete. It will be added to the image and to the list of defined tags under the category "nocatg".

If you are using tag categories, you can select a category, and only those tags will be shown in the list of defined tags. If your tags list is huge, this can reduce the list to a manageable size for pointing and clicking.

The [Apply] button writes the data to the image file and to the metadata index file used for searching images.

The [Prev] button can be used to load all available data from the previous image viewed or edited. This can be used to speed-up the processing of a group of images sharing much of the same data.
 
The [Manage Tags] button starts the Manage Tags dialog.
  

Manage Tags Dialog

This dialog starts from the button [manage tags] in the Edit Metadata dialog. You can also assign categories to tags to help organize them and locate them more quickly when adding tags to images. They are optional and they play no role in tag searching: only the tag is stored in an image, not its category. Typical categories are people, places, things, events, scenery, buildings, art, etc. To add a new tag with a new category, enter the category and the tag, then click [create]. If the category is blank, the tag will be assigned to "nocatg". To assign a tag to a different category, click a category (bold text) or enter a new one, click the tag, and press [create]. The tag will move from the old to the new category. To delete a tag, click the tag and press [delete]. Tags used in images but not assigned to a category will appear under "nocatg".

Note: a newly created tag is appended to the end of the tag list for its category. The next time Fotoxx is started, all categories and their tag lists are sorted alphabetically, except that "nocatg" is always last.

Use the [orphan tags] button to list tags that are defined but not assigned to any images. These may be deleted if no use is planned.
 

Adding Geotags
The Edit Metadata dialog shows the location data for the current image, if any. For an image with missing or incorrect location data, enter a location name (city, park, museum ...) and use the [Find] button to either complete the data in the dialog, or get a list of matching locations to choose from (e.g. London, United Kingdom and London, Canada). The list of locations comes from your image files, so a location will not be known until it is assigned to an image for the first time. Partial matches are found, so you can usually enter a leading substring, e.g. "hono" for Honolulu. Use the [Apply] button to enter the data into the EXIF metadata for the current image, and also into the metadata index file for later searching by location. Use the [Prev] button to fill the dialog data with the last location used. If the [Find] button does not find a location (it is not present in any other image), you can use the [Web] button to find the location data from an internet web service (MapQuest Open for now, but this could change). The location data is completed and returned into the dialog. The web service names are not standardized in format or language, so check the returned data for reasonableness and change the spelling and capitalization if needed. The [Apply] button will add the location data to the image, and this location will be available for future use by the [Find] button. If the [Web] button fails, you can find the location using an internet service, and enter the location data into the dialog.

Here are two web sites to look-up a location/country (there are many others):
   http://www.worldatlas.com/aatlas/findlatlong.htm
   http://brainoff.com/geocoder

Note for non-English locales: If a comma is used for a decimal point in latitude/longitude, this is accepted but converted to a period internally. The web service always returns periods.

Blank the latitude and longitude if you want to save only the location and/or country name in the image file. If the latititude/longitude data is changed from the values returned by [Find], the new values are saved for this image file. If a location is saved without latitude/longitude, there will be no location marker on a map, and finding photos for this location by clicking on a map will not work (Images by Map Location). The two other method to find photos by location, Search Images and Image Locations, will still work.

Note: The EXIF keyword "city" is used to mean any location name: city, park, museum, lake, etc. Fotoxx uses the term "location" to avoid confusion (e.g. city = Yellowstone Park). Internally, "location" is converted into "city" for metadata storage or retrieval.

If you change to one of the map views and click on a location, this location will be inserted into the Edit Metadata dialog. If you click within a marker (red dot), the location name and latitude/longitude for that marker will be used. If you click outside a marker, the clicked latitude/longitude will be used, and the location name will not be changed.

If you use the [Find] button to set the dialog location data from a known location, there are two possible outcomes: if there is only one geocoordinate (latitude and longitude) associated with the location, this is returned into the dialog. If there are multiple geocoordinates, the Net Map is shown and zoomed-in to a scale where all markers for all geocoordinates associated with this location are shown. Click within one of the markers to choose the corresponding geocoordinate, or click outside any marker to set a new geocoordinate for this image file. The location name now has a newly associated geocoordinate.

You can use Batch Add Geotags (see below) to quickly add location data to many image files at once.
 
Summary:
  •  Enter (or change) a location name (possibly abbreviated) in the dialog.
  •  Use [Find] to find the location and auto-fill country, latitude, longitude.
  •  If there are multiple matches, choose from the list.
  •  If there are too many matches, add more letters or supply the country and try again.
  •  If there are zero matches (not found), try the [Web] button (country is required).
  •  If still not found, add more letters to the location.
  •  If still not found, use one of the above web services and input the data manually.
  •  Use [apply] to update the image file and make the location available for future use.
  •  If there are multiple geocoordinates for a given location and country, click on a map
       marker to select, or click elsewhere to add a new geocoordinate for this location.
 
 
Edit Any Metadata (File View > Metadata > Edit Any Metadata)

This is a dialog for editing any metadata for the current image file. The most likely metadata key names are listed. Click one of these to retrieve the current key value. Change the value if wanted and press [Save]. The metadata is updated. To edit a key name not in the list, enter the key name and press keyboard "enter" to retrieve the present value, if any. Then enter or change the value and press [Save]. You may enter the key name in lower case and with or without spaces between the words, e.g. "Bits per Sample" and "bitspersample" will both work. To see all present keys and data, use View Metadata (long).
 
 
Delete Metadata (File View > Metadata > Delete Metadata)
Specify the key name to delete, or select All. The metadata is deleted. Use All to clean an image of any identifying information that might be in there. Some keys are not deletable, e.g. File Name.
 

 
Show Captions on Image (File View > Metadata > Show Captions on Image)
Show the metadata items IPTC Caption and EXIF User Comments at the top of each image displayed. This menu is a toggle switch - the display of captions and comments is set on and off alternatively. If neither is available, nothing is displayed. If only one is available, it is displayed. If both are available, they are displayed on two lines. The lengths are truncated at 200 characters. To see up to 1000 characters, use the View Metadata function. If Show Captions is switched ON, captions and comments are also displayed during a Slide Show.
 
 
 

Area Menu (File View > Areas)
 
 
Overview 
Edit functions normally apply to the entire image, but it is possible to edit part of an image (an "area") and leave the rest unchanged. If an image area has been selected, then most edit functions will work only within this area. Some functions ignore a selected area. An area may be selected before starting an edit function, or while an edit function is active. The selected area is immediately active, prior edits are retained, and future edits will apply only within the area. If another edit function is started, the selected area remains active, so it is possible to carry out a series of edits on one area.

"Layers" in Photoshop and Gimp are "areas" in Fotoxx. Instead of selecting something from the image, making a separate layer from the selection, performing edit functions on the layer and finally merging the layers, you select something in the image and perform edit functions on the selection, with WYSIWYG feedback during the edit. Areas can also be saved to a file, loaded and pasted into other images, and edited there.
 
 
Select Area (File View > Areas > Select Area)

The Select Area dialog is started with the menu Areas > Select. Select one of the 8 methods (explained below). Each method selects image areas in a different way. You can change methods at any time, and the selected areas are accumulated. An outline of the selected image area(s) is shown as you add or remove areas from the selection. The [Finish] button is used to make the area ready for subsequent image edits within the area. The [Hide] button removes the area outline, giving you better visibility of image edits and area edge blending. Use the [Show] button to show the area outline. The select area dialog can be exited and re-started later to modify an existing area or start a new one.

Line Color
The color used for the mouse selection circle and the area outline can be changed at any time by clicking one of the color buttons. This allows you to maintain good contrast during area selection, regardless of the image brightness or color.

Methods
The following methods are used to enclose one or more image spaces that will belong to the final area. These methods may be used in any sequence to define spaces that are either joined or detached.
 Rectangle  Drag the mouse to enclose a rectangular area.
 Ellipse  Drag the mouse to enclose an elliptical area.
 Freehand Draw    Drag and click the mouse to draw lines that outline an enclosed space.
 Follow Edge   Click or drag along the edge of an image feature to draw lines that follow the edge.
 Replace  Drag the mouse near an area edge-line to move the edge to the mouse.
 Select area
 within mouse   
 Left/right drag to select/unselect all pixels within the mouse circle.
 Selection is independent of image colors.
 Select one matching
 color within mouse
 Click on the image to select a color. Left/right drag to select/unselect pixels
 inside the mouse circle that match the selected color within "match level".
 Select all matching
 colors within mouse
 Left/right drag to select/unselect pixels surrounding the mouse circle that match
 the color of any pixels inside the mouse circle, within "match level".
 Controls for mouse
 selection methods
 "mouse radius" sets the size of a selection circle around the mouse pointer.
 "match level" sets the color match (0-100%) required for image pixel selection.

The following paragraphs explain the details of each method.

Rectangle
Drag the mouse from one corner to the opposite corner of the desired rectangular area to select. A rectangle is drawn to enclose the area. Right-click to delete and start over. Repeat the process to select more rectangular areas.

Ellipse
This works the same as rectangle selection, except that the area enclosed is an ellipse.

Freehand Draw
Drag the mouse (left button down) to draw a freehand (curvy) line, or left-click to connect a straight line from the last point drawn to the point clicked. Continue around the target area until it is surrounded with connected curves and lines. Right click to remove previous lines (mistakes). A right click will remove the previous clicked or dragged line, up to 50 pixels. Right click repeatedly to remove more. A new clicked line will always connect to the end of the previous line. A new dragged line will connect to the previous line if it is started close to the end of that line. If it is started elsewhere, a disconnected line will be drawn. You can start a new drag from far away and draw back to meet the previous line. If a clicked line connects to an undesired point (i.e. you don't want to connect to the last line drawn), right click to erase it and then use drag to start a new sequence of lines. A right-button drag can be used to erase small segments: right-drag closely along a line to erase it, then left-drag to re-draw the line. At the end, an area must be fully enclosed, with no gaps. Lines that overlap a little at the ends are OK. Gaps can be difficult to find and correct, so work at 100% image size or greater and be careful. A series of lines automatically connected with left clicks will not leave gaps, but deviation from this sequence is likely to create gaps. To reduce the possibility of gaps, use deliberate overlaps when manually connecting lines. There is a gap detection utility described below - Find Area Gap.

Follow Edge
High-contrast pixels (likely image feature edges) between the last point drawn and a newly clicked position are found and connected. This is effective for clear edges that are not too irregular. Fuzzy and ragged edges may not work well and freehand draw will be needed if high precision is necessary. The rules for connecting lines are the same as explained above. Dragging the mouse instead of clicking works like freehand draw. There is a tool for very irregular edges described below - Select Hairy.

Replace
Drag the mouse near and along an existing area edge-line. The line will be erased and redrawn at the mouse pointer. This is a faster way to make a small adjustment in an existing line.

Select area within mouse
Left click or drag will select the pixels enclosed by the mouse circle. A right click will unselect the last selection (repeat to unselect more). A right drag will unselect the enclosed pixels.

Select one matching color within mouse
Click on the image to select a color. The color is shown on the color button. You can also use the button to set a color directly. Left/right drag to select/unselect pixels within the mouse circle that match the selected color within match level. Adjust the match level down/up to match a greater/lesser range of colors.

Select all matching colors within mouse
Left/right drag to select/unselect pixels inside the mouse circle. Pixels beyond the mouse circle are also included if they meet these conditions: 1) their color matches any color inside the mouse circle, within the current match level. 2) they are within search range of the mouse pointer. This is a factor of mouse radius, e.g. if mouse radius is 20 and search range is 3, then the search range is 60 pixels from the mouse pointer. Drag the mouse over new areas you want to include. Watch the selected area expand into areas with colors matching those inside the mouse circle. If you go too far, right click to remove the last selection. Repeat if needed to remove more previous selections. Reduce the radius or increase the match level to gain finer control - the selection will expand more slowly and stay closer to the mouse circle. A small radius and high match level can be used to follow along an edge and select pixels up to the edge with good precision. Change to a larger radius and/or lower match level to select larger areas after the fine work is complete. Right drag acts as an unselect: pixels inside the mouse circle and matching pixels within the search range are unselected. If a selection goes too far, it is often easy to correct this by unselecting from outside the selected area. You may need some practice to get a feeling for this and be able to work efficiently.

Mouse Radius and Match Level
These two controls apply only to the select within mouse methods described above. Mouse radius defines the size of a circle around the mouse pointer. Pixels within the circle are selected, or they provide a set of colors for matching and selecting pixels outside the circle. Match level defines a degree of match (0-100%) to select pixels based on their color and brightness. 0 means anything matches, and 100 means a perfect match is required.
 
Summary
  left drag select pixels inside mouse circle and those with matching colors within search range
  right click undo previous selection, repeat to unselect more
  right drag unselect pixels inside mouse circle and those with matching colors within search range
 
Blend Width
Edits made within an area can be blended with the surrounding image over a distance called blend width. At the edge of the selected area, the image is the original (unedited) image. At a distance of blend width from any edge inside the area, the image is the edited image. For distances in-between, the pixels are a mix of original and edited pixels with a gradual transition. Use the Blend Width control to set the blend width for the current or subsequent edit functions. Zero blend width gives a hard edge to the area edit. Increasing blend width makes the edges of the edit more gradual and harder to distinguish from the original image. Changing the value for the first time after editing an area will cause the edge distance to be calculated for each pixel in the area. This is normally fast (a few seconds), but it may take minutes if the area is large and has a complex geometry (a very long edge). Whenever an area is re-edited or inverted, the edge calculation is discarded and must be repeated if blending is wanted. If the edge of a selected area is within 4 pixels of the image edge, it is no longer considered an edge for blending. If a selected area includes a portion of the image edge, and you do not want blending along this edge (the normal case), be sure the edge of the area is within 4 pixels of the image edge.

Edge Creep
An area that has been finished can be expanded or contracted in 1-pixel steps. The area remains finished, but blend width is no longer valid and must be repeated if needed. This can be helpful to reduce edge effects when an area selected by matching colors is edited in a way that changes its brightness. Selection by color may leave a narrow band of underselected or overselected pixels along an edge where color has become mixed with background. Expanding or contracting the area a pixel or two can produce a cleaner looking edge.

Show/Hide
Use [Hide] to hide the area outlines. This is useful when editing the image/area, to better see the effects of the edit without interference from the area outlines. Use [Show] to show the outlines and resume editing the area.

Finish

An area is not effective for edits until it is successfully finished. The finish process finds all the areas enclosed by your drawn outlines. When you are finished outlining areas, use the [Finish] button to complete the process. A popup dialog will ask you to click the mouse inside each enclosed area in sequence. The enclosed areas are temporarily colored so you can verify that the final result is what you intended. Press [keep] when all selected areas have been clicked and correctly colored. Press [Undo] if a colored area does not match your intention - the original area outline is restored. If the outline of an enclosed area has a gap, the pixel selection and coloring process will "leak out" and areas outside the enclosure will be colored. In this case, press [Undo] to go back to the original outline. Find the gap in the outline and close it, then try [Finish] again. See the function Find Area Gap below for a fast method to find gaps.

Any enclosed area can be selected, even those not explicitly outlined - if you have a donut with a hole, you can select the donut, the hole, or both.

Areas selected using one of the select within mouse methods are automatically finished whenever you click inside any enclosed area. These areas are mapped during the selection process, whereas areas selected with one of the line drawing methods are mapped only when you click inside them.

If you draw a line from one image side to another, you can click on either side of the line to make an area of all pixels on that side of the line. Example: draw a horizontal line on the boundary between sky and land. Click above the line to select sky, below the line to select land.

Disable/Enable
Disable the current area and keep the data so that it can be re-activated later. This allows you to alternate edits within a selected area and edits for the entire image.

Invert
This function inverts an existing area: the entire image is selected except for the existing area. Using the function two times returns the original selected area. Inverting a selected area invalidates the edge calculation which will be repeated if edge blending is selected.

Unselect
Discard the current area permanently.
 

Find Area Gap (File View > Areas > Find Area Gap)
If an area outline was created by freehand drawing using multiple strokes, it is easy to leave small gaps in the outline that are not visible below 400% zoom. An attempt to finish such an area will fail because the mapping of the interior pixels will leak out through the gap and cover large areas not intended as part of the area. If the Finish function fails, use this function to find the gap. Click somewhere on the outline of the failed area. The outline will be slowly re-drawn in one direction from the clicked position, until an "end pixel" is found. This is where the gap is. The outline is then drawn in the opposite direction until the gap is encountered. You can see the gap location clearly. Zoom-in on the gap and close it using freehand draw, then test again to see if the full outline can be drawn without stopping. This function can be used in parallel with the Select Area function (both dialogs active).
 

Select Hairy (File View > Areas > Select Hairy)

 


  
  
colors to select
  
  
colors to deselect
This is an area selection function for complex boundary cases like hair or plants, where selection by color may not work well, and manual selection would be very tedious. The objective is to deselect background pixels, making them transparent, while keeping foreground pixels opaque. The resulting "cutout" can be copied and pasted where needed, without the background.

This function is not very intuitive, and you will likely need practice to become fast and effective.

Follow these steps:
  • Choose a window background color (User Settings) that contrasts with the complex area to be selected. In the example above, a white background was chosen to contrast with the dark hair. The background color can be changed during the selection process when different colors are being selected.
  • Use Select Area to select the entire area to be copied, including the complex boundary areas.
  • Start this function, Select Hairy.
  • Set a suitable mouse radius for painting over the boundary areas.
  • Left-click on an area representative of the colors to be selected, but away from the irregular edge. About 100 pixels will be sampled from the clicked position. The selected colors are shown in the corresponding dialog color box.
  • Right-click on a background area to choose colors to be deselected. These are shown in the other color box.
  • The colors to select or deselect can be changed at any time by left or right clicks on the image.
  • Choose a mode: select, deselect, or parallel (both options checked). Most of the time you will likely use both.
  • Left drag over the boundary to select/deselect pixels. Deselected pixels become transparent and the background color will show through. Selected pixels will remain opaque, or become opaque if they were previously deselected.
  • Right drag to restore the original pixels, to correct errors.
  • The select and deselect sliders determine the selection and deselection sensitivity. Move right for a more narrow match to the colors shown in the box, move left to select similar colors more widely. If the foreground and background colors are close, you will need to use narrow color matching to separate them.
  • When done, save the completed area with the Copy button (for subsequent Paste Area) or the Save button (for subsequent Open Area File).
  • To paste the area into another position or image, use the menu Paste Area or Open Area File
When the area is pasted into the target image, the paste dialog can be used to further blend the edges if needed. The pasted area remains active and can be edited with almost any edit function.
 
 
Area Show/Hide (File View > Areas > Show Area, Hide Area)
Show or hide the outline of the current area. Hiding the area is useful when the area is being modified with one of the edit functions. This makes it easier to judge the effects of the edit. These are also available as buttons in the Select Area dialog.
 
 
Area Enable/Disable (File View > Areas > Enable Area, Disable Area)
Disable the current area and keep the data so that it can be re-activated later (Enable menu). This allows you to alternate edits within a selected area and edits for the entire image. These are also available as buttons in the Select Area dialog.
 
 
Area Invert (File View > Areas > Invert Area)
Invert an existing area: the entire image is selected except for the existing area. Using the function two times returns the original selected area. Inverting a selected area invalidates the edge calculation which must be repeated if edge blending is desired. This is also available as a button in the Select Area dialog.
 
 
Area Unselect (File View > Areas > Unselect Area)
Permanently discard the current area. This is also available as a button in the Select Area dialog.
 

Area Copy and Paste (File View > Areas > Copy Area, Paste Area)
A selected area can be saved to a cache file using the menu Select > Copy Area. This area can be pasted into the same or another image using Select > Paste Area. Click and drag to position the pasted area. The dialog controls can be used to resize, rotate, and change the brightness of the pasted area. The edge blend control allows you to blend the area edges into the background image if wanted.
 

Area Open and Save (File View > Areas > Open Area File, Save Area File) 
A selected area can be saved to an image file using the menu Save Area File. You are asked to supply a file name. A PNG file is created. The PNG file has an alpha channel (a 4th 'color') for transparency information. The image is a rectangle enclosing the selected area. Selected pixels are opaque, and others are transparent. These files reside in  /home/<user>/.fotoxx/saved_areas  by default, but you can save them anywhere.

Use the menu Open Area File to paste a saved area onto the current image file. The background image will show through the transparent parts of the pasted area. Click and drag to position the area. The dialog controls can be used to resize, rotate, and change the brightness of the pasted area. The edge blend control allows you to blend the area edges into the background image if wanted.
 
  


 
Undo/Redo Button (File View > Undo/Redo)
If an edit function is active and the image has been changed:
  •  left mouse click will undo the current edit
  •  right mouse click will redo the edit
This allows you to rapidly compare the "before" and "after" images for the current edit function.
 
If no edit function is active, but one or more edits were made to the current image:
  •  left mouse click will undo one edit step per click
  •  right mouse click will redo one edit step per click
  •  if combined with the A-key, undo/redo ALL edits (compare original and final images)
  •  middle mouse click pops-up a list of all edit steps - select any step to return to
  

 
 

Edit Menu (File View > Edit)
 
 
Trim (Crop) and Rotate Image (File View > Edit > Trim/Rotate)









This function is used to remove unwanted image margins and/or rotate to upright or level the image.

When the dialog opens, a selection rectangle is placed over the image. The areas outside this rectangle are darkened and represent the parts of the image that will be removed. Drag any side or corner of the rectangle to move that side or corner. The dialog box shows the current width/height ratio of the selection rectangle. If the box lock ratio is checked, then moving a side or corner will also cause another side or corner to move, so that the ratio is maintained. You can also drag from the middle of the rectangle to shift the whole rectangle without changing its dimensions. You can use the width and height spin buttons to adjust the pixel dimensions (or type-in new values), and the selection rectangle will adjust to these.

You can use the keyboard arrow keys to move a side or corner of the selection rectangle in 1-pixel steps. The last side or corner moved with the mouse is the one that is moved with the keyboard.

The [Max] button resets the trim rectangle to the full image size (useful if you want only to rotate the image). The [Invert] button will invert the width/height ratio (e.g. 2.0 to 0.5). The [Prev] button retrieves the width and height values last used for a previous image (useful for setting multiple images to the same size, e.g. to fit a monitor or beamer).

The [Auto] button will automatically set the trim rectangle to omit transparent margins left over from composite and warp functions. These functions leave transparent margins where images did not overlay or were bent away from the edge. [Auto] tries to find a maximum rectangle within these margin areas. This may or may not be the desired margins, so you can keep them or move them with the mouse before committing with the [Done] button.

The six ratio buttons allow you to choose a preset width/height ratio. You can change the ratio button names and the corresponding ratios with the button [customize] which starts a new dialog shown on the right. Enter desired button labels in the first row, and corresponding width/height ratios in the second row (the default names are the same as the ratios, except for "gold"). The [gold] button uses the golden ratio, about 1.62:1. You do not have to keep it.

To level a slanted image, use the mouse to drag the right edge up or down until the image looks level. Use the 90º and 180º buttons to upright an image made with the camera turned. The degrees control can be used to set any angle, -180 to +180 degrees. No resolution is lost with 90 degree rotation. For other angles, the loss of resolution is about 1/2 pixel. The level button can automatically level the image if the camera used provides EXIF 'roll angle'.

A left-click on the image will add vertical and horizontal guide lines to help with image leveling. Use right-click to remove them.
 
 
Upright (File View > Edit > Upright)

This dialog is a faster way to upright an image that is turned 90 or 180 degrees.
The Upright button will take the correct action automatically if the image EXIF data is correct (normally yes).
 

 
Resize Image (File View > Edit > Resize)  (also called rescale) 

This function resizes the image to a new pixel width and height. You can input the new dimensions directly or choose a percent change. Buttons are present for setting the new size to a simple ratio of the original size. Using one of these will minimize loss of resolution. The [Prev] button recalls the previous size, a convenience if multiple images are being set to the same size. If the lock ratio box is checked, the current width/height ratio will be preserved if either width or height is changed. The change is made immediately, but the image will look the same unless it becomes smaller than the window. The image file size in the top panel is not updated until the modified image is saved.
 
 
Voodoo 1 (File View > Edit > Voodoo 1)
This is a fast automatic image enhancement with limited capability. This is sometimes effective and "good enough" for rapidly processing many photos. There is no dialog - the modification is simply done when the menu is selected. Reject the change with the [undo] button if desired. The modification consists of a slight flattening of the brightness distribution, an expansion of the brightness range if less than the full range is used, and a slight increase in the color saturation, more for darker areas of the image than brighter areas. The effect is sometimes minimal or even negative.
 
Voodoo 2
(File View > Edit > Voodoo 2)
This is an alternative automatic enhancement, using the flatten method described below.
  
 
Retouch Combo (File View > Edit > Retouch Combo)
This function handles all aspects of adjusting image brightness and color: overall brightness and contrast, brightness curves (overall and per color), color saturation, color temperature, white balance and black level.

 

brightness curves, overall and by RGB color
horizontal line is the neutral value (no change)
 
This example shows an decrease in brightness for
darker image areas, and an increase for brighter areas
(i.e. more contrast)
 
 


Use the sliders for brightness and contrast to optimize
the image. This may be adequate for most photos.

To fine tune brightness and contrast, edit the curves
using the mouse.

amplifier
increases or decreases the effect of the brightness edit curves
brightness
moves the entire curve up or down
contrast
moves the curve lower and upper parts in opposite directions
low color/high
increases or decreases color saturation
warmer/cooler
adjusts color temperature (reddish <--> blueish)
dark areas/bright
apply color changes to darker/all/brighter image areas
brightness distribution
show a brightness distribution graph in the curve edit window
click to set white balance
click on a gray or white point to adjust the white balance for the whole image
click to set black point
click on a dark point to set this color as black - this is subtracted from all image colors
Settings File
dialog settings can be saved in a file and loaded later for use with other images

After making initial adjustments using the sliders, you can fine tune brightness and contrast by editing the curves with the mouse to change which parts of the image have increased or decreased brightness or contrast. The ALL curve adjusts all colors, and the RGB curves adjust individual colors. Use ALL first, then make revisions using RGB.

Dark - Bright image areas
This modifies the operation of the color adjustments to affect primarily darker or brighter image areas. Leave in the middle to adjust all areas equally.

Brightness Distribution
If checked, a brightness distribution graph is drawn inside the curve edit window. The graph is live and changes as the dialog controls are changed. The edit curve overlays the graph and may still be edited.
 
Click for white balance or black level
If checked, mouse clicks on the image are used to set a black point and/or white balance (otherwise clicks on the image will zoom the image as usual). When a spot is clicked for while balance, the RGB colors will be shifted to make this spot pure gray or white. This is the easiest way to correct a photo with an overall color tint because of bad lighting. You can use the warmer-cooler slider after clicking, and this correction will be added to the prior result. When a dark spot is clicked for black level, the RGB values are used as a black set point, and image RGB colors will be shifted down to make this spot black. This is one way to reduce fogginess in a photo, or make the background night sky look black instead of gray.

Settings File
Load button - load all dialog settings (including the curves) from a file chosen by the user
Save button - save all dialog settings to a file chosen by the user
This can help speed up processing when the same or similar settings can be used for multiple photos made under the same conditions.

Buttons
Reset - set all controls back to a neutral position - image is also reset
Prev - set all controls to the values used for the previous image
Done - finish the edit, close the dialog, save the control settings
Cancel - cancel the edit, reset the image, close the dialog

You can use the [Prev] button when processing a series of images made under the same lighting and therefore needing the same or nearly the same adjustments.
  
 
Edit Brightness Distribution (File View > Edit > Edit Brightness)



With this function you can directly alter the shape of the brightness distribution. Move the sliders and watch the image to find the optimum settings.

Cutoff: If the distribution is low or zero at the dark or bright end, you can stretch the distribution to make it extend more into the dark or bright end, or both.

Flatten
: This is a fast and easy way to compensate for a common limitation in photos: the brightness range is inadequate, or areas of the image have nearly the same brightness and details are lost. Pixel brightness is redistributed so that each brightness level is more equally represented. Technically, the brightness distribution is made more uniform (flatter). You can flatten any or all three ranges of brightness.

Stretch: The selected low/mid/high brightness region is broadened, which necessarily squeezes adjacent areas. For example, if you broaden the low brightness region, darker areas of the image will have more contrast at the expense of mid- and high brightness areas.
  

 
Gradients (File View > Edit > Gradients)


Gradients increases the apparent brightness range of an image by increasing local contrast. It is especially useful to improve HDR images, but can also be applied to any image. Gradients increases the contrast between nearby pixels without increasing the overall contrast. It relies on the nature of human vision: contrast within a small angle is perceived more strongly than contrast over a large angle. Gradients can bring out subtle details (low contrast) that would otherwise be hard to notice. Other methods can also be used: Retouch Combo can increase the contrast for a selected brightness range (at the expense of others). Flattening the brightness distribution can spread the available contrast (brightness range) more evenly. These methods operate globally: all pixels of a given brightness are processed the same. Gradients processes pixels relative to surrounding pixels, and is more effective at enhancing detail and the perceived brightness range. 

In the dialog, the graphic curve determines how much local contrast is increased depending on initial local contrast. The left end of the x-axis corresponds to low-contrast pixels and the right end high-contrast pixels. Raise the left side of the curve to increase the contrast of low-contrast pixels (but this will also enhance low-level noise). The Amplify slider below the curve regulates the internal algorithmic calculation, from no contrast amplification on the left to full amplification on the right. If moved too far to the right, the image may show ugly artifacts, so push it back until these disappear. The curve can be dragged with the mouse and its effect on the image will show up in a second or so (depending on image size and CPU speed). The Amplify slider also needs time to show up in the image. If more contrast is wanted, raise the curve. If uniform areas (e.g. sky) become mottled, pull the left end of the curve down to reduce amplification for low-contrast pixels. In some cases it will be best to select different areas of the image and process them separately, e.g. more conservative for sky, more aggressive for textured surfaces like stone walls and vegetation.
 

Flatten (File View > Edit > Flatten)

















Flatten enhances visible detail in areas having poor contrast. The revised brightness for a pixel is based on the brightness distribution for nearby zones. A larger zones count calculates new pixel values from closer areas. Flatten controls the strength of the effect, and deband moderates darker or brighter image areas. This function can amplify noise in uniform areas like sky. If the deband control is insufficient, use Select Area and Denoise. Alternatively, use Select Area beforehand to select sky (or other areas to omit) and then invert the selection prior to using Flatten.
 
Technical Explanation (optional)
The image is divided into area zones according to the input zone count. Each pixel is adjusted based on the 9 closest zones, the 3x3 zones surrounding the zone of the pixel. More zones means smaller and closer zones. The brightness of a pixel is compared to the brightness distributions of the nearby zones, and the brightness is adjusted up or down in the direction that would flatten the distribution of these zones. The influence of the 9 zones are weighted based on their distance from the pixel being calculated. The influence of the leftmost zones goes to zero for a pixel on the right edge of its zone. The same is true for the topmost zones, etc. This prevents abrupt transitions that could be visible. The nature of human vision hides the radical alterations in pixel brightness, since the eye also judges the brightness of a spot based on its surroundings. A larger number of zones will make each pixel brightness adjustment depend on areas closer to the pixel.
  


Retinex
(File View > Edit > Retinex)

There are two functions here, Global Retinex (top panel) and Zonal Retinex (middle panel).

Global Retinex can improve color and contrast for images with extreme fog/haze or color cast (e.g. a 100 year-old photo). Try the [Auto] button first. The image is searched to find the brightest and darkest RGB colors present. The darkest RGB values are assumed to represent black, and the brightest are assumed to represent white. The black values are subtracted from all pixels and the resulting RGB values are scaled up so that the white values are at maximum. Of course this may not be initially optimum. The spin buttons are set to the dark and white RGB limits found, and you can changes these to optimize the resulting image. You can choose your own black and white points by selecting the corresponding option in the dialog and clicking on the image. The multiplier spin buttons are set to 1.0 by default, and you can change these values to adjust the relative brightness of each RGB color. In the above example, the 1st image is the original, the 2nd image was made using the [Auto] button, and the 3rd image was made by selecting a bright spot on the turtle's shell as the white point and the shadow under the front left foot as the dark point. The rightmost column of spin buttons will adjust all buttons in the same row together - use the mouse wheel or the KB up/down arrow keys.
The algorithm was derived from concepts first published by Edwin Land in the 1970s.

Zonal Retinex
can increase visible details in dark image areas and other areas having low contrast. The brightness range of a zone around each pixel is used to rescale the pixel brightness as though the entire zone were rescaled to cover the entire 0-255 brightness range. Zonal Retinex can be used to modify the result of Global Retinex, or it can be used without Global Retinex. Zonal Retinex can be quite slow, especially for zone size > 100. 

The blend slider is used to mix the input image and the retinex image in any ratio. The above cathedral image is about 70% original and 30% retinex.  The reduce bright slider is used to attenuate the effect for brighter image areas, especially sky, where Retinex can produce strange looking results.

 
  
Mirror Image (File View > Edit > Mirror) 
Choose either horizontal or vertical mirror from the dialog. The image is reversed (mirrored) vertically or horizontally. Repeating the mirror restores the original image. Doing both a horizontal and vertical mirror is the same as a 180 degree rotation.
  

Paint Image (File View > Edit > Paint Image)


This function paints over the image with selected colors, using the mouse.

The Paint Color button shows the current color. Click this button to choose a color using the standard Gnome color chooser dialog. You can also shift + left-click on the image to choose a color from the image.

The palette button opens the dialog shown in the middle. Here you can select an image file where you have saved colors for recall. Click on the image to select the color at that position. The image shown above is the default. You can use any image as a color source.

The HSL button opens an HSL color chooser shown on the right. This dialog can represent any possible color. Move the sliders to get the color you want, and press Select.

The brush size control sets a circle around the mouse pointer which shows the area being painted or erased. Left drag on the image to paint with the current color. Right drag over a previously painted area to erase (undo the painting). The opacity controls determine how rapidly the color is applied (or erased) at the center and edges of the mouse circle. 100% opacity applies the full color immediately, and a low value allows you to gradually change the color using multiple drags (analogous to spray painting from a distance). Erase also works this way: use 100% opacity to immediately erase, and a low value to erase gradually.

NOTE: zoom the image to 100% or more when using a small brush. If the mouse steps are larger than the image pixels and a small brush is being used, some pixels may be skipped by the mouse and cannot be painted.

If paint over transparent areas is selected, painting over transparent areas will reduce or eliminate the transparency, depending on the opacity controls. If this option is not selected, only opaque areas of the image are painted.

The [undo-last] button reverses the last paint or erase operation, and this can be repeated to remove many recent edits. Each new mouse drag operation is a unit of work that can be separately reversed. The memory for undo is limited, so only the most recent paint and erase steps are kept. [undo all] will put the image back in its initial condition.

You can use the zoom image buttons to zoom the image larger or smaller as needed. If drag image is selected, you can pan or scroll a zoomed image by dragging it with the mouse. This stops the drag from painting or erasing.

If a select area is active, the painting is confined within the area.

Wacom Tablet Operation
You can use a Wacom tablet instead of a mouse. Dragging the stylus in contact with the tablet surface will paint as described above. If one of the stylus buttons is held down while dragging, the operation will be erase instead of paint. You can also set paint or erase mode using the radio buttons in the dialog. Holding down a stylus button is cumbersome, which is why the radio buttons are there. Adding pressure to the stylus will increase the opacity, so you can make lighter and darker strokes without adjusting the opacity controls.
 

Clone Image (File View > Edit > Clone Image)

This function paints image areas by copying from elsewhere in the image. This method can be used to erase an unwanted object, replacing it with background taken from elsewhere.

Shift + left click on the image to select a source, then drag over the image area to be painted. The source area is painted over the dragged area, immediately or gradually, depending on the opacity settings.

The brush size control sets a circle around the mouse pointer which shows the area being painted or erased. Left drag on the image to paint, or right drag over a previously painted area to erase (undo the painting). The opacity controls determine how quickly the image is modified (or erased) at the center and edges of the circle. 100% opacity paints fully and immediately, whereas a low value allows you to gradually paint using multiple drags. Erase also works this way: use 100% to immediately erase, and a low value to erase gradually.

The [undo-last] button reverses the last paint or erase operation, and this can be repeated to remove many recent edits. Each new mouse drag operation is a unit of work that can be separately reversed. The memory for undo is limited, so only the most recent paint and erase steps are kept. [undo all] will put the image back in its initial condition.

If paint over transparent areas is selected, painting over transparent areas will reduce or eliminate the transparency, depending on the mouse opacity controls and the gradual paint setting. If this option is not selected, only the opaque part of the image is painted.

If a select area is active, the painting is confined within the area.
 

Blend Image (File View > Rdit > Blend Image)

Blend image pixels together (smoothen, blur) by painting with the mouse. The paintbrush radius control sets a circle around the mouse pointer which shows the image area being blended. Left drag to blend, right drag to restore. The strength controls determine how fast the image is blended or restored, at the center and edges of the circle.

If a Select Area is active, only the selected areas are affected.
 
  
 
Add Text to Image (File View > Edit > Add Text)

  
  


This function writes text directly on the image. Enter the text into the dialog. Multiple lines can be used. After entering the text, left-click the mouse where you want the text on the image. Click or drag to move the text elsewhere. Right click to remove the text. Use the [Font] button to select a different font. Use the [Size] control to increase or decrease the text size. Use the [Angle] control to change the slant angle of the text. The other controls allow you to provide a background color around the text, a text outline color, and a shadow effect. You can select the color and transparency for all of these. The width control adjusts the width of outlines and shadows. The shadow angle control sets the slant angle of the shadow.

You can initialize the text from any available metadata in the image file. Enter the metadata key (e.g. "user comments") and press [Fetch]. You can abbreviate key names, e.g. "usercomments" instead of "User Comments".

The [Open] and [Save] buttons start a file chooser dialog with which you can load or save all text data from or to a file. All the items in the dialog are loaded or saved, so you can keep a collection of often-used text strings and settings.
 
The buttons at the bottom work as follows:
Clear
Clear the text and metadata fields to blank.
Replace
Image with added text replaces (overwrites) the current image file
+Version
Image with added text is saved as a new file version
Next
Open the next sequential image file and place the same text at the same position.
Apply
Complete the edit and start over. A new text string can now be added to the image.
Done
Complete the edit and exit the dialog. The edited image file must be explicitly saved.
Cancel
Abandon the edit. Previous use of [Apply] is not reversed.
 
To add the same text to a series of images: prepare and position the text, press [Replace] or [+Version], then [Next], then [Replace] or [+Version], then [Next] ...
 
Making a Watermark: Use a text transparency of 70% or more and a background transparency of 100%. The text should be faint but readable. To add a "relief" effect, use Select Area to put a box around the text and Embossing to give the text an appearance of depth.    
  
 
Add Lines/Arrows to Image (File View > Edit > Add Lines)

This function writes lines or arrows directly on the image. Enter a line length and width into the dialog, and select an arrow head if wanted. Left-click the mouse where you want to place it on the image. Drag the ends of the line/arrow to position it on the image. Right click to remove it. The dialog controls allow you to provide a background color, an outline color, and a shadow effect. You can select the color and transparency for all of these. The width control adjusts the width of outlines and shadows. The shadow angle control sets the slant angle of the shadow. The [apply] button makes the current line/arrow permanent, so you can start a new one without leaving the dialog. 

The [Open] and [Save] buttons start a file chooser dialog with which you can load or save all line attributes from or to a file.
 
 
Paint Edits (File View > Edit > Paint Edits) 

Use this function in combination with some other edit function. Start an edit function and leave the controls in a neutral position. Then start Paint Edits. Specify a mouse radius and power factors for the mouse center and radius edge. The mouse pointer will be surrounded by a circle with the specified radius. When the mouse is dragged over an area of the image, the current retouch function is applied within the circle. The strength of the function is regulated by the power factors. Typically you will use a high value at the center and zero at the edge, meaning that the strength of the edit will be maximum at the center, changing gradually to zero at the edge of the circle. As you drag the mouse over the same area repeatedly, the edits are slowly accumulated. For example, if the edit function is Retouch Combo, and the brightness curve is moved upward (brighten), then the image will slowly brighten in the area where the mouse is dragged. This is called 'dodge and burn' in some image editors.

Use the [undo] and [redo] buttons to monitor the change, which may be hard to notice at first. Set the center power to 100 to make faster changes (with less fine control). Use a left-button drag to weaken the edit or ultimately erase it. When done using one edit function in one or more image areas, use the [done] button on the edit dialog to complete the edit. Use the [reset area] button on the Paint Edits dialog to erase the active area that is now left over from the mouse dragging. If you leave this area active and start a new edit function, the results may be strange (the new function applies immediately to previously painted areas).

A suggested approach is: (1) start an edit function and make the initial settings, (2) start the Paint Edits dialog, (3) drag the mouse over the desired areas and watch the effect, (4) adjust the edit settings, (5) alternate between the previous two steps. (6) Exit from the edit function, then from Paint Edits.

This method to "paint" edits incrementally can improve selected areas of an image quickly and easily. It works with the following edit functions: Retouch Combo, Flatten, Gradients, Sharpen, Denoise, Adjust HSL, Color Depth. Others may be added in the future.
  
 
Leverage Edits (File View > Edit > Leverage Edits)

It is sometimes effective to apply an edit function "leveraged" by some image attribute, e.g. apply noise reduction to darker areas of the image while leaving brighter areas alone. To do this, use Leverage Edits. Choose brightness or contrast as the lever. This may be for a single RGB color or all colors. The editable graph controls how subsequent edits are applied to the image. The x-axis is the selected lever, from minimum to maximum value. The y-axis value governs how strongly an edit function affects a pixel having the value on the x-axis. Example: apply Gradients primarily to dark pixels: Start Gradients, then start start Leverage Edits and drag the curve so that high values are on the left (dark pixels) and low values are in the middle and on the right (bright pixels). You can edit the leverage curve or the Gradients curve while watching the resulting image.

This function can be used with the same list of edit functions listed in the Paint Edits topic above.
 
 
Plugins (File View > Edit > Plugins)

The Plugins menu is on the left. The top entry Edit Plugins leads to the dialog on the right. In this dialog you can define menu names and associated commands for using other image edit programs within Fotoxx. These menus are added to the Plugins menu. The example shown is a menu named "auto-gamma" which starts the command "mogrify -auto-gamma %s" (an ImageMagick function). The "%s" is a placeholder where Fotoxx will insert the name of a temporary copy of the current file in Fotoxx. The called edit program must process the file and replace it with the edited version. Normally this is done by using the program's File > Save menu. Afterwards, you can use the Fotoxx [Undo] and [Redo] buttons to check the results, perform additional edits with Fotoxx, or use [Save] to save the edited image. The image passed by Fotoxx to the external program is a TIFF file with 16 bits per color. Most programs can read this file but may use only 8 bits. When finished using the external program, save the image back to itself using the File > Save menu, and then exit the program. Fotoxx will pick up the revised file and use it as though the edit had been done in Fotoxx. Note that in Gimp you must use the File > Export menu to save the image back to the original input file (File > Save produces an .xcf file).

To add a new plugin, input a menu name and the corresponding command in the Edit Plugins dialog and press the [Add] button. Wherever %s is placed in the command, the file to process will be inserted. Some commands may expect an input and output file to be specified. In this case supply %s in both positions (the output file replaces the input file). You may omit %s if the command does not edit an image file. A warning is given, which you can ignore if this is really your intention.

To modify an existing plugin, select the menu name from the drop-down list. The corresponding command will be shown. Modify the command and press [Add]. You can remove a plugin by selecting it and then pressing the [Remove] button.

A few examples are provided in the initial Fotoxx installation:
Menu Name
command line
Gimp
gimp %s
auto-gamma
mogrify -auto-gamma %s
Gthumb
gthumb %s

The plugin menu is saved in the file
/home/<user>/.fotoxx/plugins  which you can modify with a text editor if desired. This is the only way to change the sequence of the menu entries. Be careful not to screw up the format.
 
 
 

Repair Menu (File View > Repair)
 
 
Sharpen Image (File View > Repair > Sharpen)

This function has four methods to sharpen a blurry image.

Unsharp mask
: a fast and effective method also found in other image editing apps.
A technical description can be found via Google.

Gradient
: steepens brightness transition areas directly, somewhat like the Gradients function.

Kuwahara
: small neighborhoods of pixels above, below, left and right of a pixel are compared to each-other. The pixel is given the mean color of the neighborhood with the smallest variance in brightness. This forces pixels on a blurry edge to move to one side of the edge or the other. Edges are made very sharp, but fine details can be lost.

Median diff: pixel brightness is compared to the median of pixels within radius. The brightness is revised up or down for pixels above or below the neighborhood median. The magnitude of the revision is proportional to the brightness difference and the input values dark and light. The effect is similar to unsharp mask, but the "halo" side-effect is greatly reduced. This method is much slower than the others, especially if a large radius is used. This is because pixel brightness values must be sorted in order to find the median value.

The radius value limits the distance over which pixels around an edge are changed. It should be small for images that are slightly fuzzy and larger for poorer images. Amount controls the strength of the modification. Threshold suppresses changes to low-contrast pixels: a higher values reduces the amplification of low-level irregularities (image noise, uneven skin tones, etc.).

Choose the method, set the parameters, press [apply] and wait a few seconds to see the result. Make changes and repeat the process until satisfied. You can go back and forth among the methods to compare which is best for a given image. Use Select Area to operate on different parts of an image with different methods and parameters.
 
 
Blur image (File View > Repair > Blur)
This function can be used to blur an image. Each pixel is mixed with neighboring pixels to reduce the differences, making edges fuzzy. Enter a value for blur radius and press [apply] to see the results. A small value mixes each pixel with its nearest neighbors and larger values mix more distant pixels. The contribution from each pixel decreases with distance, so the nearest pixels have the greatest contribution. This function is useful to smooth mottled skin tones. You can use select area to restrict the blurred areas.
  
 
Denoise Image (File View > Repair > Denoise)

This function reduces the noise in photos taken under poor lighting conditions, making uniform surfaces appear speckled. It also works for scanned prints, as in the example here. Multiple methods are provided because the best method varies with noise characteristics. Mixing methods (using one and then another) is often helpful. Choose the method, set the radius or threshold parameter, and press [apply]. Each new [apply] uses the modified image from the previous [apply], so each use will have increasing impact. With a large image, some methods may be slow. To save time, select a small area and experiment with the different methods and settings until you make a decision, then apply the chosen method to the entire image.

The dark areas slider can be used to restrict the process to darker image areas, which are most prone to noise. If left at the right end, all areas are processed.

Here is a short technical description of each method:
 Flatten
Pixels are compared to the mean and sigma of pixels within a radius.
Those outside one sigma are moved slightly back toward the mean.
 Median Pixels are set to the median value of their neighbors within a radius.
 Top Hat Detect outliers by comparison with surrounding pixels at a distance.
The distance is increased in steps from 1 pixel to the radius limit. Outliers are flattened slightly.
 Wavelets 
RGB brightness (with noise) is converted into a series of wave functions that
nearly sum to brightness and represent an approximation with less noise.

The wavelets algorithm was adapted from code found in a Gimp plug-in.
The initial version was written by Dave Coffin for the program Dcraw

The [measure] button starts the lower dialog to measure the actual noise level. Move the mouse over the image to show the RGB noise levels within the mouse circle (radius 10, about 300 pixels). This must be a featureless area so that noise is the only variation present. A gray sky is a good source, or a badly out-of-focus image area. To measure camera sensor noise, use a RAW image (JPEG images are processed inside the camera to reduce noise). The graph shows pixel deviations from the regional mean. The solid line corresponds to the mean, and the dotted lines are at brightness levels +5 and -5 from the mean, on a scale of 0-255. The center to edge axis is logarithmic from 0 to 10, with 7 at the midpoint. The numbers at the bottom show the mean RGB brightness and noise levels for the area within the mouse.

More information about camera noise in RAW images can be found in the technical notes.
  

 
Red Eyes (File View > Repair > Red Eyes)
This function reduces the red-eye effect from electronic flash photos. Two methods are provided. The first is faster but may not handle difficult cases. The second method is more robust but also needs more time and care. To use the first function, left-click on a red-eye one or more times until satisfied. If the darkened area is too small or off-center, do a right-click to undo the change and then left-click more precisely on the center of the red-eye. If a red-eye cannot be fixed correctly, right-click to undo the change and then use the second method. The second method can better handle difficult cases where the red-eye is only slightly red and the color difference with the eyelids is too little for the automatic algorithm to distinguish. Place the cursor over the center of the red eye. Hold the left mouse button and drag the cursor down and to the right. A dotted ellipse will appear enclosing the red eye. Repeat if needed to get the red eye centered in the ellipse (roughly). Note that the shape of the ellipse depends on the direction of the drag, which can allow more precise enclosure of only the red-eye. Left-click inside the ellipse repeatedly while watching the red eye darken, and stop when it is dark enough. If you go too far, the eyelids may start to darken. Right-click to undo and repeat if necessary.
 
 
 
Color Mode (File View > Repair > Color Mode)

Use this function to make a black and white or color negative, or convert a negative image into a positive image, or convert to sepia coloring (for an aged photo effect).

Select one of the buttons:
    reset - return the image to the original state
    black/white positive - convert a color image to black and white
    black/white negative - convert to black and white and invert brightness
    color negative - replace each RGB color with its compliment
    RGB -> GBR - red/green/blue colors are replaced with green/blue/red colors
    sepia - convert to a modified black and white for an aged photo effect

The slider can be used to apply the effect incrementally, from 0% (no change) to 100%.

Each button acts on the current state of the image, e.g. pressing [color negative] twice makes a color positive (i.e. no change from the original).

Color negative: Each RGB color is replaced with the maximum value minus the RGB color value. For example, if the RGB colors (% of maximum) are 20/40/60, then the negative color is 80/60/40. Doing this twice brings back the original colors. For pure RGB colors, red becomes cyan, green becomes magenta, and blue becomes yellow.
 

  
Color Depth (File View > Repair > Color Depth)
This function changes the normal 16 bits per RGB color to any value between 1 and 16 bits per color. At 8 bits per color, there are 16.8 million total color combinations. At 4 bits per color there are only 4096 total colors. Use 1-4 bits for an interesting "poster" effect.
 
 
Shift Colors (File View > Repair > Shift Colors)

This function can be used to correct colors or convert an image into false colors. Choose any of the three RGB colors and move the slider left or right from the center. One of the two other colors will be substituted in a graduated manner. For example, you can gradually substitute green or blue for the color red. The All slider shifts all colors together. Do this first to find a first optimum, then shift the RGB colors individually.
 

Color Saturation (File View > Repair > Color Saturation
There is one slider control to adjust color intensity or saturation from zero (black and white image) to 100%.
Retouch Combo also includes an adjustment for color intensity.
  
 
Adjust RGB/CMY (File View > Repair > Adjust RGB/CMY)

This function is used to change overall brightness and contrast, or that of selected colors. The settings are retained within and across Fotoxx sessions, so this function can be used to process multiple photos made under the same lighting conditions and needing the same (or nearly same) adjustments. Use the [reset] button to restore all inputs to neutral values.
 Brightness  Increase or decrease overall image brightness
+Red - Cyan  etc.
 Increase or decrease the brightness of one color
 and change complimentary color in the opposite direction
 Contrast
 Increase or decrease the overall image contrast
 Red, etc.
 Increase or decrease the contrast of individual RGB colors
   
 
Adjust HSL
(File View > Repair > Adjust HSL)

Change a selected range of colors in an image using an HSL color chooser (Hue, Saturation, Lightness). This function can be used to fix color problems, e.g. a false color cast in the entire image or in certain colors, or an overexposed sky (too white). If you are not familiar with the HSL color model, I suggest you read the Wikipedia article about this.

Begin by selecting a target image color to match and adjust, using shift + left-click on the image. This color will be the center of a range of colors that will be selected for adjustment. This range can be very narrow or wide, depending on other dialog settings. Select what color attributes will be matched using the checkboxes for hue, saturation, and lightness. Each of these will narrow the range of selected colors. If none are checked, all colors are selected. Match Level can be used to further widen the range of selected colors. 100% means only closely matching colors are selected.

The Output Color controls (hue, saturation, lightness) are used to set the new output color which will replace the selected input colors. You can also set this color from the image, using shift + right-click. The resulting color will be a mix of the original color and the new color. The new color part is determined by the Adjustment slider which can be set from 0 to 100%. Use the HSL checkboxes to determine which HSL components of input color are replaced with the corresponding HSL components of the output color. Use the sliders for output color HSL to adjust the new color. The output color is shown in the smaller box on the left, which is updated as you move the sliders.

Move the sliders and watch the live image updates to optimize the result. This is not very intuitive, and practice will help.

Hint:
begin by matching on hue and saturation, and replacing only hue - this means that the output color saturation and lightness will be copied from the original image colors, and only the hue will be replaced.

This function (like most others) can be used with Select Area to restrict the change to selected image areas.

 
 
Zonal Colors (File View > Repair > Zonal Colors)

This function can be used to make complex color corrections, whereby different parts of the image need different corrections. Select up to 9 control points on the image by clicking them with the mouse. The points are added to the list in the dialog window and to the image window, with labels 'A' to 'I'. The current RGB values are shown (or EV or OD units if selected). Change the RGB/EV/OD values in the dialog, and the image will be changed to match. Each pixel in the image is influenced by all the control points in the dialog, with the closer control points having more influence than those farther away. The slider Blend determines how widely the control points spread their influence. If delta is checked, the values shown are the deltas (differences) from the original image.


Match Colors (File View > Repair > Match Colors)

This function matches the colors in one image to those in another. A small spot, determined from a mouse click, is sampled from each image. The spot on the 2nd image will be made to have the same average color (RGB values) as the spot from the 1st image. The factors used to make the RGB values the same are then applied to all the pixels in the 2nd image. The most common use is to remove a color cast from an image by marking a spot on the image that should have a given color which was taken from another image. 

Procedure: The dialog lists 5 steps to take in sequence. (1) Set a radius for the spot sample. The mouse cursor will have a circle of this radius which is the spot area that will be sampled. (2) Open the 1st image (press the [open] button for a file open dialog). If the current image is already the one you want, this step can be skipped. (3) Click on the image to take a color sample from the spot area enclosed by the mouse circle. You can change the radius and click again if wanted. (4) Open the 2nd image by pressing the [open] button. (5) Click on the image at the spot you want to match the spot color from the 1st image. The image colors will change within a second or two. You can change the radius and click on another spot if wanted, and the colors will change accordingly. Click the dialog [done] or [cancel] button to finish.
 

 
Color Fringes (File View > Repair > Color Fringes)

This function is used to reduce chromatic aberration. Look carefully at the left photo, taken from inside a church. It has color fringes that were mostly eliminated in the processed image to the right (these images are 400% size and not very sharp). Color fringes can appear along high-contrast edges, especially in the outer image areas where lens distortions are usually greatest. To get rid of them, zoom the image to a maximum size and center on an area with color fringes. Move the slider controls slowly while watching the image, and leave them where the color fringes are minimized. To speed up the response time, select a small area first, optimize the color fringes in this area, then remove the area before pressing [done] so that the entire image will get the final corrections. The corrections are scaled so that the maximum correction is at the image edges and the correction at the center is zero.
 

 
Smart Erase (File View > Repair > Smart Erase)

This function can be used to erase small objects that can spoil a good photo, such as power lines, trash on the ground, a sign, etc. The unwanted object is replaced with pixels taken from the surrounding area. This is sometimes very effective (side-effects almost invisible), and sometimes not. It works best for small or narrow objects in the photo. Radius controls the size of a circle around the mouse pointer, defining the area to select and erase. Drag the mouse to enclose all or part of the object to be removed. Left-drag selects and right-drag un-selects. Press [Erase] to erase the selected area, replacing the pixels with the nearest pixels from outside the selection. If the selection was not precise enough, use [Undo], adjust the selected area, and [Erase] again. Repeated selections and erasures will accumulate until you use [New_Area] to start a new selection. The prior erased areas are now fixed and [Undo] will only work for the current selection. As with all edit functions, the main menu buttons [Undo] and [Redo] can be used to review all changes. It is likely best to work with an image zoomed to 200% or more. The Blur control adds blur to the replacement pixels. This can reduce visible side-effects, since the replacement pixels may be sharper or have more contrast than the surroundings. Change the Blur setting and repeat the [Erase] button. A blur of 0.5 or 1 pixel is usually effective. The [show] and [hide] buttons can be used to show the outline of the current selection or hide it to better judge the results after erasing.
 

 
Brightness Ramp (File View > Repair > Brightness Ramp)


This function varies brightness or color across the image. You can use this to compensate for uneven lighting or a color cast that varies across the image. The direction of change is determined by drawing a line on the image. Create the line by clicking on the image, then drag either end to set the direction wanted. In the example above, green is increased in the direction of the line. Edit the All curve first to adjust overall brightness (all colors), then adjust the individual RGB color curves if needed. The image reacts quickly to both line changes and curve edits.

 
 
Remove Dust (File View > Repair > Remove Dust)

Images made from dusty scanned slides can have many small dark spots - shadows of the dust on the slides. Historical photos from the internet often have a similar problem. This function can be used to remove the majority of such spots. Move the three sliders until the maximum number of dust spots are painted red, then press the [erase] button to erase them. Press [red] to bring back the red view, then you can adjust the sliders again and press [erase]. The "spot size limit" slider limits the size of the spots that will be erased. The "max. brightness" slider sets a threshold for ignoring spots that are not dark enough. The "min. contrast" slider screens out spots having low contrast with their surroundings. This process is usually a compromise. If the settings are not optimal, small features like tree leaves can be erased, or large spots may be left in place. Different parts of the image may need different settings, e.g. sky can be treated more aggressively than a building wall. You can simply use Erase Dust multiple times with different settings as needed to get all the dust spots. Or you can use select area to process the image in sections. If some spots are persistent, you can treat them manually with Smart Erase: set a small mouse radius and click on each spot to remove it. Spots from fibers (long and thin) are usually not removed automatically, but Smart Erase can be effective here.
 

Anti-Alias (File View > Repair > Anti-Alias)

This function can reduce pixelation (aka "jaggies" or "stair steps") in a low-resolution image. Press the [apply] button to make the change, then [done]. The result is sharper than using the Blur function. It may be useful to apply sharpening afterwards.

Note: this tool is only effective for pixelation with single-pixel "steps". If the image has been resized larger (steps are larger than 1 pixel) the algorithm does not work. It thinks the steps are legitimate because they are big.
 
 
Stuck Pixels (File View > Repair > Stuck Pixels)

Camera sensors may have defects causing isolated pixels to be always bright or always dark. This may be one RGB color or all of them. I have seen a case where a group of 3x3 pixels was always too red. This function can find such pixels in an image and repair them by substituting neighboring pixels.

Select the defect sizes to search for: 1 pixel, 4 pixels in a 2x2 block, or 9 pixels in a 3x3 block. The defects found are surrounded by small circles which you can toggle between write, black and red using Line Color. Zoom-in to inspect these and determine if they are real defects. Use the contrast control to precisely select the defects. If set too low, small high-contrast spots in the image may be erroneously selected. If set too high, real defects may be missed. Use the [apply] button to erase the defects in the current image. You can apply the function many times using different settings if needed.

The currently shown (encircled) defective pixels can be saved to a file by using the [save] button. This file can be used later to fix the defects in any image made by the same camera: use the [open] button, select the saved defects file, then use the [apply] button to fix the current image. Using a saved defects file from one image to fix the defects in another image will only work if the two images have never been resized, rotated, or trimmed. This is necessary because the defective pixels in the two images must have the same locations. If more than one contrast setting or pixel group selection is needed to accurately find all the defects in one image, you can save the respective defect files and combine them manually into one file. Use any text editor for this.

I suggest you make a test images to find defects: Make a photo of a medium gray surface (e.g. gray sky). This image can be used to find both bright and dark stuck pixels.
 
 
Paint Transparency (File View > Repair > Paint Transparency)

Paint transparent or semi-transparent areas on an image. Such areas are useful in the Fotoxx Mashup function, where images or background underneath a transparent area can show through. Other image editors (e.g. Gimp) can also use transparent areas.

The paintbrush radius control sets a circle around the mouse pointer which shows the area being transformed. Left drag on the image to increase the transparency, right drag to decrease. If gradual paint is checked, the strength controls determine how rapidly the transparency changes at the center and edges of the circle. If gradual paint is not checked, transparency is set to 100% (left drag) or 0% (right drag) for the entire area covered by the mouse circle.

If a Select Area is active, only the selected areas are affected.

An image file having transparency information must be saved as a TIF or PNG file. JPEG files do not support transparency.
 

Add Transparency (File View > Repair > Add Transparency)

Add transparency to an image based on brightness or a chosen color. Areas of the image with greater brightness, or greater match with the chosen color, will become more transparent. The reverse will happen if Invert Match is checked. Click on the image to set the color to match. You can match based on any combination of hue, saturation, and lightness. Match Level selects the degree of match required for maximum transparency. At the minimum setting (left end), the entire image will be transparent. The Transparency slider regulates the amount of transparency applied (zero at the minimum setting). Threshold sets a match level below which the transparency is zero.
 
 
 

Warp Menu (File View > Warp)
 
 
Unbend Image (File View > Warp > Unbend)


Panoramas of nearby subjects (typically buildings or interior rooms) may show straight lines that are curved, or buildings that are slanted. Warping of the images was necessary in the panorama process in order for the images to fit together. The Unbend function can be used afterwards to straighten the panorama image if needed. Vertical and horizontal dotted lines are drawn over the image, showing the unbend axes. Click or drag the mouse near the end of a line to move it. If values in the four input controls are changed, the image is warped in the manner indicated by the corresponding four icons. Increase or decrease the values and repeat until satisfied. Move the axes to change the centers of warping.

See also Warp Image for another method of correcting image curving and perspective.
 
 
Fix Perspective (File View > Warp > Fix Perspective)







  


This function can be used to straighten a photo made from an offset angle. The image on the left is the original photo, taken from right of center to avoid reflections. The image on the right is the straightened version. This function can also be used to straighten a building photographed from street-level or from the side.

Click on the four corners of the tetrahedron that you want to make into a rectangle (in the above case, the four corners of the painting or frame), then select [Apply]. Use [Reset] to go back and try again if needed. The clicked corners are marked with small white boxes containing "A, B, C, D". The upper left corner of each box precisely marks the image position. Clicking near a box will move it to the new position. The [Trim] button will automatically trim the image at the selected corners. The trim and straighten can be undone in sequence with the undo button. You can use the keyboard arrow keys to move the corner markers in 1-pixel steps. The arrow keys work on the last corner clicked or moved.
 
 
Warp Image (distort)

 
 
Warp area (File View > Warp > Warp area)
This function can be used to make distortions within an image. You can select an image area and drag the mouse to stretch this area with respect to the rest of the image. The image is like rubber. If the mouse drag begins within the selected area, then the area is warped within its current boundaries - the movement is maximum at the mouse pointer and declines to zero at the edges of the selected area. If the mouse drag begins outside the selected area, the area edges near the mouse can be pulled out beyond the original area boundary. Many mouse drags of different lengths and directions can be combined to achieve the desired results. When finished, you can select another area and do some more warping, or select [done] to exit the function.
 

Unwarp Closeup (File View > Warp > Unwarp Closeup)

Closeup face photos are often distorted, because areas closer to the camera are larger in the photo than areas farther away. This function can be used to reverse the distortion. Use Select Area to select the face first (does not need to be accurate). Click the image near the center of distortion (above example: between the eyes). Move the slider to optimize. If the face is turned away from the camera, experiment to find the best center.
 
 
Warp curved (File View > Warp > Warp curved)
This function is useful to correct perspective problems (see also Unbend). Drag the image from any position, using the mouse. The entire image will be pulled or pushed in the direction of the mouse, but areas near the mouse are moved more than more distant areas. You can straighten curved lines or deliberately curve the image. The control warp span determines the radius of warping around the mouse. 1.0 means the full image is warped, and smaller values confine the warp to smaller areas around the mouse.
 
 
Warp linear (File View > Warp > Warp linear)
This function is useful to correct perspective problems (see also Unbend). Drag the image from any position, using the mouse. This function works over a broader area than the curved warp and causes less image curvature. To minimize the addition of curvature, drag only from the image corners.
 
 
Warp affine (File View > Warp > Warp affine)
This function can be used to warp an image in interesting ways. Drag the image from a corner or edge using the mouse. The changes are purely linear so straight lines remain straight. This transform is called "affine". Technical details can be found with Google.
 
 
Flatten Book Page (File View > Warp > Flatten Book Page)

This function can flatten a photographed page from a book. If the book is thick, the pages bend downward at the binding, and the photographed text is squeezed together. This function straightens the the page and stretches the text.
 
Photo Procedure
First, make the photo as good as possible to minimize the needed corrections. The page curvature can be reduced by holding the book half-opened. Place the camera over the center of the page, so that the top and bottom edge curves look roughly equal. Use lots of illumination to increase the depth of field, to insure the curved-down part of the page remains in sharp focus. Two persons working together can photograph 1-2 pages per minute.
 
Fotoxx Procedure
First, trim the image, keeping all of the page but little more. Rotate the page if needed. Use Fix Perspective to make the page more rectangular if needed. Start the Flatten function. Click the mouse along the top edge, creating visible marker dots at the clicked points. After 4+ points are available, a curved line is drawn through the points. Add more points and drag the points as needed to make the line conform closely to the page edge. Repeat for the bottom edge. Press [flatten] to flatten the page. The edges should now be straight (or straight enough). The text near the binding is still squeezed together. Unsqueeze the text by pulling the top and bottom sliders. The text is spread out in a way that is proportional the the slope of the top and bottom page edges, so the area near the binding is stretched the most. The [undo] button restores the unmodified image and the marker dots, which can now be adjusted for a revised attempt.
 

Spherical Projection (File View > Warp > Spherical Projection)
 
Make a spherical projection of an image. Drag the mouse on the image to change the center of the projection (defaults to midpoint). The flatten control modifies the projection gradually from a sphere (left) to a flat image (right). The magnify control magnifies the resulting image up to 2x. If you want the margins to be transparent, be sure to save the file as .png instead of .jpg (JPEG files do not support transparency).
 

Selective Rescale (File View > Warp > Selective Rescale)




This function rescales an image to a smaller size, leaving selected areas unchanged. The goal is to increase the relative size of the area of interest. The image above was reduced, but the boat in the foreground was left unchanged. Image reduction may be in width or height or both. First, select the areas that are to be preserved using Select Area. Press the [proceed] button. The selected area is erased and the mouse cursor changes into a drag cursor. Pull the image inward from the upper left corner. This operation may be repeated as needed until you are satisfied. Press the [done] button. This function works best when the selected area(s) are small in comparison to the entire image. The pixel rows and columns that intersect the selected areas are not changed. Other areas are compressed as the image is pulled inward.
 

Make Waves (File View > Effects > Make Waves)

This function distorts an image into a wave pattern, as if it were being viewed through turbulent water. The dialog allows you to change the mean horizontal and vertical wavelengh, amplitude and variance. The "perspective" input allows the wavelengths to gradually lengthen from top to bottom. Like most effects, this function also works within a selected area.
 

Twist Image (File View > Effects > Twist Image)

This function twists the image around a point chosen with a mouse click or drag. Click the desired position and move the Twist slider the amount wanted. You can also drag the mouse around the image to change the center of twisting. The Center slider untwists the central area relative to the rest. The Angle slider rotates the entire image.
 
 
 

Effects Menu (File View > Effects)
 
 
Sketch (File View > Effects > Sketch)

This function transforms a photo into something like a sketch. Dark pixels are aggregated into fewer pixels, leaving vacated areas brighter. Contrast can also be used as a proxy for dark pixels. Threshold can be used to filter the input image by brightness. Clip Level is used to filter the output to further reduce isolated or marginally dark pixels. Choose colors for foreground and background. Two algorithms are provided. Results may look more interesting with one or the other. Reducing the size of the input image may also give more interesting results.
 

Cartoon (File View > Effects > Cartoon)

Transform a photo into a cartoon-like drawing. Black lines are drawn over the "edges" of objects in the image, wherever the brightness or color changes abruptly. Line Threshold sets the sensitivity for edge detection and line drawing, causing more lines (lower threshold) or fewer lines to be drawn. Line Width makes the lines thicker or thinner. Blur Radius controls a blur function which can make the lines look more curvy or less jagged. Kuwahara Depth controls a sharpen function which can strengthen the image edges.

This function can be quite slow to react if the image is large and if the blur and kuwahara controls are set to high values (10+ seconds to respond). It works faster and better on smaller images, around 2 megapixels or less. I suggest you start with small values (2-3) for kuwahara and blur, then adjust line threshold to optimize the density of the drawn lines. Now change kuwahara and blur in small steps and re-adjust line threshold. If you are working with a large image, I suggest you select a small but important area within the image so that optimization can proceed faster. Then delete the area and do the entire image. You may be close to optimum already.

Other edit functions, applied before or after Cartoon, may enhance the effect. These include color saturation, gradients, color depth, texture, warp curved, graduated blur (3rd image above). Paint and Clone are useful to remove minor flaws manually. Shadows in the image are a particular problem, since they cause lines to be drawn where normally not wanted.
  
 
Line Drawing (File View > Effects > Line Drawing)

This function transforms a photo into a line drawing showing outlines of objects within the image. Edges (sharp transitions in brightness or color) in the image are brightened, and the rest of the image is darkened.

There are three sliding controls. Threshold: how bright an edge must be in order to get enhanced, from "show no edges" at the low end to "show all edges" (even faint ones) at the high end. Width: width of the enhanced edges, from 1-pixel to about 5 pixels. Brightness: brightness of the image itself, from dark (show only the outlines) to full brightness. The black/white checkbox converts the image from color to black and white, and the negative checkbox makes a negative image (colors are replaced with their compliments).
 
 
Color Drawing (File View > Effects > Color Drawing)

This function transforms a photo into a solid color image that looks like an illustration. The Threshold slider separates the image into brighter and darker areas, with the boundary set at a brightness level determined by the slider. The other two sliders regulate the brightness of these two areas. At the settings shown here (Dark Areas pushed left, Bright Areas pushed right), the darker areas are black and the brighter areas as as bright as possible with intense coloration. If the sliders are moved to the opposite sides, the original image is restored. Move the sliders until the image is optimized.

This function can be used to clean up a smudgy blackboard or whiteboard image by separating the writing cleanly from the background (if the background smudges are weaker than the writing). It may help to apply Sharpen to the image beforehand, to increase the contrast of the writing. Use the unsharp mask method with a large radius.
 
 
Graduated Blur (File View > Effects > Graduated Blur)






This is another function to blur an image, but it works differently. In the dialog, you specify a contrast limit and a blur radius. Only pixels with less contrast than the given limit are blurred, and the blur radius ranges from 1 to the given value for pixels with a corresponding contrast ranging from the given limit to zero. In short: low contrast pixels are blurred more than high contrast pixels. This can be used, for example, to smooth skin tones without blurring hair or reducing the sparkle in the eyes. Taken to extremes, it produces a "cartoon" effect, especially when used in combination with other retouch and effects functions.
 
 
Embossing (File View > Effects > Embossing)



This function transforms a photo into a simulated relief or embossed image. The radius setting determines the feature size or level of detail. The depth setting determines how deep the features go into the surface. The upper 60% of this image was embossed.
 
 
Tiles (File View > Effects > Tiles)
This function transforms a photo into an array of large monocolor tiles. You can control the tile size and the thickness of the gap between tiles. This is also called "pixelate" or "pixelize". Use Select Area to confine the transform to a limited area, such as a face.
 
 
Dots (File View > Effects > Dots)








This function transforms a photo into a array of dots, like a comic book picture or Roy Lichtenstein painting. The only control is dot size. Also experiment with using color saturation, color depth, or other functions before and after using Dots.
 
 
Painting (File View > Effects > Painting)
This function transform a photo into something looking more like a painting. It reduces the number of colors, maps each contiguous pixel area having the same color, and then consolidates smaller areas into adjacent larger areas having the best color match.

color depth sets the number of colors to use: 1 = 8 colors, 2 = 64 colors ... 5 = 32768 colors.
patch area goal sets a lower limit for areas that will have their own color: areas smaller than this number of pixels will be absorbed into an adjacent area with the nearest color match.
req. color match sets the minimum color match required for a smaller area to be consolidated into an adjacent larger area: 0 = don't care (maximum consolidation), 100 = perfect match required (no consolidation).
borders determines whether the colored areas will be delineated with a thin black border, like irregular tiles in a mosaic. After using this function, using the Embossing function can add interesting texture to the image.
 
 
Vignette (File View > Effects > Vignette)
This function is used to correct the darkening sometimes seen around a photo's periphery, because less light reaches the edges compared to the center.It can also be used to highlight or colorize an object or area within an image.


Click or drag the mouse on the image to change the vignette center, which is initially at the center of the image.

Select Brightness to change the brightness of the image in a radial pattern: Adjust the left or right end of the curve to change the brightness of the center and edges of the image respectively. You can give a dark surround to a portrait face, or you can fix an image with darkened corners. The curve middle level corresponds to no change. Use lower values to darken and higher values to brighten. The example above gradually darkens the periphery of the image while leaving a broad central area unchanged.

Select Color to add a chosen color to the image in a radial pattern. Curve values of zero represent no change, and higher values add the chosen color to the image. The highest value corresponds to 100% color. Use this function to add a color surround to an image, e.g. surround a face with a gradually increasing color.

Irregular Vignette
You can make a vignette with arbitrary shape as follows:
  • Select Area - select the image area to remain visible
  • Invert the area to select the areas outside the image
  • Set a blend width value for the edge fade-out width
  • Keep the Select Area dialog active
  • Paint Image - paint the outside areas with the desired color
     (the image edges will fade-out over blend width pixels)
  • Adjust the blend width value and paint again until satisfied
  
 
Texture (File View > Effects > Texture)
 
This function adds a textured surface to an image or selected areas within an image. Radius determines the texture pattern size. Strength determines the intensity of the pattern, from almost invisible to dominant.
 
 
Pattern (File View > Effects > Pattern)

Add a background pattern to an image. A small image file (pattern file) is used to cover the current image by duplicating the file, like a tiled wall. This file can be a real pattern (e.g. an image of canvas cloth, a brick wall, a repeating geometry ...), or any other kind of image. The pattern is made semi-transparent, so that the base image appears to be printed over the pattern, or the pattern over the image. Many pattern files can be found using a Google search for "pattern image". Download some of these and trim them if needed to a size around 200-500 pixels. For convenience, add these to the supplied pattern files in /home/<user>/.fotoxx/patterns.

Use the [Browse] button to select a pattern file. The selected pattern will be tiled to cover the base image. The pattern is partly transparent so that the base image shows through. Use Zoom to grow or shrink the pattern size. There are two methods to mix the pattern with the base image. The pattern opacity can be set 0-100%. The base image is used to fill the unused opacity - e.g. if the opacity is set to 30%, then the final image will be 30% pattern and 70% base image. Contrast is used to modify the base image brightness using the pattern brightness as a template. The two parameters can both be used and mixed in any ratio.

If the pattern file contains an image that repeats at fixed intervals both horizontally and vertically, the [Calculate] button can be used to set width and height to match. The result will be a continuous pattern without any edge effects.

If the pattern is irregular and edge effects appear where the duplicated patterns are joined, you may be able to improve this. The two Overlap values determine how much the duplicated patterns overlap at the edges, horizontally and vertically. Add some overlap to mask edge effects.

Select Area can be used to apply a pattern to part of an image, or different patterns to different parts. After applying a pattern to an image, it might be interesting to use Embossing or Gradients to add a 3-D effect to the pattern.

Useful pattern files in LibreOffice: /usr/lib/libreoffice/share/gallery/www-back
 
 
Mosaic (File View > Effects > Mosaic)
Create a mosaic image using tiles made from all your images.





eye
Specify the tile size in pixels (e.g. 36 x 24) and press [Tiles]. Tiles will be created from all of your images (actually the thumbnail images are used since their small size makes the process run much faster). This may take some time (speed is over 100K tiles/min. on a strong PC). If you change the tile dimensions, press [Tiles] again to regenerate the tiles. After the tiles are created, press [Image] to convert the current image into a mosaic using these tiles. This takes only a few seconds. Tiles are chosen by matching the average tile color to the average image color at the tile position. If thousands of images are available and if the range of colors is good enough, the mosaic will turn out quite good. Zoom-in on any part of the image to see the tile images.

The Tile blending slider will cause the image to be blended with the tiles, making it look better if the tiles are a bad color match. If the blending is less than about 50%, the tiles remain almost as clear as before (the eye compensates the false tint).

After a mosaic is created, you can click on any tile to get a larger popup image. This is the full image for the tile, so you can drag the window as large as you like and it will remain sharp. A mosaic image can be saved like any other edited image, but if a saved mosaic is opened, clicking the tiles for a bigger image will not work. Regenerating the mosaic is quite fast, so do this if you want the popups to work.
 
 
Custom Kernel (File View > Effects > Custom Kernel)

Apply a custom convolution kernel to an image. The underlying technology is explained in Wikipedia. Input a kernel size, a multiply factor, an add factor, and fill-in the table values. The values can be saved into a file and retrieved later by using the [Save] and [Load] buttons. [Apply] applies the kernel to the image.
[Reset] restores the original image status. Several kernel example files are available initially. You can modify these and save with a custom name. 

Math: the brightness values for each N-by-N block of pixels in the image are multiplied by the corresponding values in the kernel matrix and added together. This number is multiplied by the multiply factor, and the add factor is added to it. This final value becomes the new brightness level for the image pixel at the center of the N x N block.



 
 

Directed Blur (File View > Effects > Directed Blur)


Pull a location on the image using the mouse. The area around the mouse will move with the mouse and become blurred in this direction, a "1-dimensional" blur. Blur span can be used to broaden or narrow the size of the area being blurred. Intensity determines the strength of the blur, from barely visible to completely blurred.
 

Blur Background (Bokeh) (File View > Effects > Tilt-Shift)

Blur the background image while leaving the foreground sharp. The foreground is defined by using Select Area to select one or more areas that are to remain sharp. If there is no selected area to begin with, the select area dialog is started automatically. After selecting the foreground areas, invert the selection so that the background is now selected - use the [invert] button in the Select Area dialog. This area is the one that will be blurred. There are two blur methods available: Constant blur uses a constant blur radius for all areas blurred. Increase blur with distance uses the minimum blur radius for pixels adjacent to the foreground (i.e. on the edge of the background area), and the maximum blur radius for pixels at the maximum distance away from the foreground. This requires that the edge-distance for all background pixels be calculated, which can take considerable time for a large image. This is done automatically if required. Various settings for blur radius can then be tried without recalculating the edge distances.

Tilt-Shift Effect: You can do this by selecting a horizontal rectangular area to remain sharp.
 

Alien Colors

Repaint an image or selected area with random alien colors. You can control the rough pattern size and the intensity (from barely visible to dominant).
 
 
  

 

Combine Menu (File View > Combine)
 
 
High Dynamic Range (File View > Combine > High Dynamic Range) (HDR)  
Combine (overlay) multiple images of the same subject with different exposure levels. The combined image can show improved visibility of detail in both the darker and brighter areas, in effect using pixels from the brighter images for the darker areas, and from the darker images for the brighter areas. Many digital cameras do exposure bracketing: take multiple shots in quick succession with different exposure levels. You can combine such images to make a better one. If the camera is adjusted manually between shots, take care to keep it level and aim at the same distant point. Some misalignment of the input images can be tolerated. If things move between shots, ghosting cannot be avoided.

Select the HDR menu function and select up to 9 images (link), which must all have nearly the same pixel dimensions. The images are aligned and combined automatically. This needs 10 seconds to a minute or more per image, depending on image size and CPU speed. When done, the combined image is shown, along with a dialog for manual adjustments. The contributions from the input images are shown as a series of editable curves. The horizontal scale represents pixel brightness, from dark to bright. Each curve represents an image which contributes to the pixels. The image contribution at a given brightness level is proportional to the height of its curve at that level. The initial curve for the brightest image will be high on the left and low on the right, meaning a high contribution to dark pixels and a low contribution to bright pixels. The darkest image will be low on the left and high on the right, and the remaining images will be in-between. The curves can be edited by dragging them with the mouse. The corresponding image contributions are changed accordingly, and you can see the results in real-time in the output image. In general, the brightest image should have a higher contribution to the darker pixels, and the darkest image a higher contribution to the brighter pixels. The above example shows two input images and their output image. The curves were adjusted to optimize the brightness for both the foreground and background buildings. It is often useful to apply the gradient function after HDR, to increase the perceived brightness range.
   
 
High Depth of Field (File View > Combine > High Depth of Field) (HDF)
Combine (overlay) multiple photos of the same subject with different focus settings from close to distant. Different parts of the subject are in sharp focus in each image. Combine the images so that all parts of the subject are sharp. This technique is most useful for extreme close-ups.
 
Making the photos: choose a point for the center of the image. Aim the camera at a near object and depress the shutter button 1/2 way to set the focus on this object. Hold the button at the 1/2 position, aim the camera at the chosen center, and snap the photo. Now choose a farther object and do the same. Repeat with increasing focus distance. Hopefully each part of the subject is sharp in at least one photo. The camera position should be very nearly the same for all photos, which can be a challenge when the subject is very close. Camera movement can cause scaling and parallax problems (close objects shifted against distant objects). Such problems may be fixable later in Fotoxx, but this may require considerable time. It is better to avoid the problems.
 
Processing the photos: in Fotoxx, choose the HDF menu function and select up to 9 images (link). The images will now be aligned as well as possible. This needs 10 seconds to a minute or more per image, depending on image size and CPU speed. The output image is an even mix of the aligned input images. A small amount of camera movement between the photos is compensated, but this is limited, and parallax shifts are not compensated at all. When the alignment is complete, a dialog opens. You can select any input image and "paint" with the mouse on any area of the output image. This converts the original image mix to the single selected image for the area being painted. For each area or object in the image, choose an input image that is sharp in that area. The radius of the paintbrush can set larger or smaller, so you can paint large areas quickly and control fine detail when needed. If you have overlapping near and far objects, time and patience will be needed to make all of them sharp. Misalignments can be corrected by selecting the warp option in the dialog. The underlying images can then be dragged and warped with the mouse, and the composite output image is changed accordingly. The warp is limited to the area around the mouse. When a painted area is dragged, the corresponding image is automatically selected and dragged, while areas painted with other images remain fixed. Areas that have not been painted cannot be dragged. Move around to different areas and make incremental drags until all areas are aligned. 

Suggested Workflow: Using paint mode, choose each image in sequence and paint all areas that look sharp in that image. Any boundaries that are not well-aligned will show up clearly as shifts in the edges of objects in the image. Some of these can be made unimportant by changing the image used for painting (if more than one image is sharp enough). Using warp mode, make fine adjustments as needed to eliminate visible shifts.
 
 
Stack/Paint (File View > Combine > Stack/Paint)

Stack (overlay) multiple photos of the same subject taken at different times. Remove tourists and cars that come and go between shots.

Making the photos: Aim the camera at the same distant point and take multiple photos as tourists or cars move in front of the subject. Try to get two or more photos for each area of the subject not obstructed by the transient objects. Hold the camera steady and aim at the same point, so that the images will align accurately.

Processing the photos: In Fotoxx, choose the Stack/Paint menu function and select up to 9 images (link). The images will now be aligned as well as possible. This may need a minute or more per image, depending on image size and CPU speed. The output image (lower left) is an even mix of the aligned input images (top row). When the alignment is complete, the dialog shown above starts. You can select any input image and "paint" with the mouse on any area of the output image. The single selected image is used for area being painted. For each area in the image, choose an input image that is free from the transient objects. The radius of the paintbrush can set larger or smaller, so you can paint large areas quickly and control fine detail when needed.
 
Automatic operation
: press the [Hide] button to see if transient objects can be removed automatically. This will work for areas in the image where multiple photos of the unobstructed background are available (lower middle image). To show all of the transient objects, press the [Show] button (lower right image). This generally works, but not perfectly. Slight image misalignments or lighting differences (esp. moving shadows) may cause the algorithm to select background pixels instead of foreground pixels. You can use the paint method described above to repair the errors manually.
 
 
Stack/Noise (File View > Combine > Stack/Noise)

This function combines 2-9 images (photos) of the same subject. The photos should be nearly the same, except for small offsets caused by a hand-held camera. If the photos were made with a very high ISO setting (low light conditions), the pixels will have considerable noise. By making many photos and averaging them, the noise can be mostly eliminated.
 
Making the photos: Choose a point for the center of the image. Take several photos using the same center and being careful not to shift or rotate the camera too much. The more photos the better. Up to nine can be used with Fotoxx, but you can take more in order to have some to discard if they are not sharp, a common problem with low light conditions and long exposure times.
 
In Fotoxx, chose the Stack/Noise function and select up to nine images (link). They will be combined automatically and shown, and then a dialog will open. The initial output image is a combination of all the selected input images, averaged together. This means that the RGB values for each output pixel are the average of the RGB values for the corresponding input pixels. The following alternatives can be used to possibly reduce the remaining noise a little more. The use median button will change the output pixels from an average of the input pixels to the median of the input pixels (1-3 middle RGB values are averaged, depending on the number of images). This may or may not be better, so switch back and forth to compare (the screen update may need several seconds). The checkboxes for omit low pixel and omit high pixel will cause the lowest and highest RGB input values to be discarded before the average is calculated. This may help to get noise spikes removed from the mix. This has no effect if the median method is selected.
 
  
Panorama Image (File View > Combine > Panorama)


This function stitches 2-4 images together to make a wide-angle image or panorama. The images must overlap by 15% or more, so that the program can find where they match and join them together. Start by selecting 2-4 image files (link). The images are initially joined and shown with a small transparent overlap. A pre-align dialog (above) asks you to drag the images into rough alignment. Drag the images into the correct left to right order. The image to drag may overlap other images. To be clear about which image is being dragged, drag from near the center of the image. After the images are in the correct order, align each image to its left neighbor. It works best to proceed from left to right. Move an image horizontally and vertically into rough alignment with its neighbor to the left, then rotate the image if needed by dragging the bottom edge left or right - the image pivots around the mid-point of its overlap with the image to the left. The fastest method is to align the overlap middle region first, then rotate the right image if needed to bring the upper and lower overlap regions into alignment. Extreme accuracy is not needed. Use the [resize] button to get a bigger combined image after moving them closer together. The images should be correctly curved and fit together well. If they do not, then the lens mm parameter (focal length, 35mm equivalent) needs adjustment. The curvature of the images changes as lens mm is adjusted. The initial value is obtained from the EXIF data if available, and this is normally good enough. You can measure and set lens mm manually using the [search] button described below. If the images have no curvature (e.g. scanned images), use the no curve checkbox to set the lens mm effectively to infinity.

If an image was trimmed so that the greater dimension (width or height) was reduced, then the EXIF focal length is no longer valid, and the EXIF initial value may not work well. A section of an image taken from the middle has an effective focal length greater than the original. Increase the lens mm parameter until the images fit together reasonably well, or use the [search] button to make a more precise determination (described below).

The no auto warp checkbox is normally unchecked. Its purpose is described below.

The manual align checkbox will use the manual pre-alignment as the final alignment. This is for images that have no clear features to match and align the images automatically.

Press [proceed] when pre-alignment is finished, and the program will do fine alignment and join the images. Internally, the images are shifted and rotated and the degree of match is evaluated. This is done with increasing image sizes until the best alignment is found. This may need a minute or more, depending on CPU speed and image size. You can speed up the process greatly if you reduce the input images to 1/2 size. Do this also if the process fails for lack of memory. Panoramas with 4 large images can require 2+ GB of memory during processing.

When fine alignment is complete, the combined image is displayed. The dialog shown here is started for fine adjustment of brightness and color match. You may see a sharp border between images if the images do not have the same brightness and color balance. The [auto color] button can be used to perform an automatic color match, which is often satisfactory by itself. Use this button multiple times to get improved matching at the expense of greater color shifts that could add a false color tint. Restore the original image colors with [file color]. The image selected with the Select Image radio buttons is the starting image for the auto color matching. Reset using [file color] and select a different starting image to see results that may be slightly different. The brightness and RGB color controls allow you to make additional changes to better match the images. Select one of the images with the radio buttons, change the values for brightness and color, and press the [apply] button to see the results. Use [auto color] to match the other images to the one changed. Use [file color] to restore the original values from the input images. The blend width input governs how the images are blended together: at the image joints, the color balance is gradually shifted over this many pixels, to mask imbalances that cannot be fully corrected. The default is 1 pixel, which makes any brightness or color differences look obvious.


If the images do not align perfectly, you may be able to improve the alignment by using the mouse to push the images into alignment. In the final dialog, select mouse warp, select which one of the images with the radio buttons, and then drag the mouse carefully along the edge of the image where it should align with its neighbor, and move the image into alignment. The image is moved locally around the mouse while more distant parts stay fixed. Parallax shifting will require a compromise, since it is generally not possible to keep both foreground and background in alignment.

The flatten image control can be used to "unbend" the image, which will straighten curved lines (e.g. buildings may look curved). A value of zero has no effect and a value of 1.0 will fully flatten the image.

When done, you can use unbend, warp, trim/rotate and other functions for final adjustments.
 

Vertical Panorama
(File View > Combine > Vertical Panorama)
This function works the same as horizontal panorama, except that the images are arranged vertically. To change the order of the images, drag them from near their centers. To rotate an image, drag the right edge up or down. It works best to align from the top down.
 
Panorama Notes
 
Scanned images can be combined if there is enough overlap. Check "no curve" since there is no curvature.
 
Auto Warping
The images are slightly warped in various directions during alignment to find the best match. This is to compensate for shifts in camera horizon or rotation, causing image distortions that reduce the quality of fit. If the overlap area of two images includes a large object that moved in or out between the two photos, the alignment process may go crazy trying to match the images, resulting in alignment that is very poor. By selecting no auto warp you may be able to get the alignment to succeed or have a smaller error. Another option is to select manual align.
 
Setting lens mm Automatically
The [search] button in the panorama pre-alignment dialog initiates an automated search for optimum lens mm. Use a suitable image pair: the subject is 50+ meters away, the images have a low horizon difference and little relative rotation, and there is plenty of high-contrast detail in the overlap area. Input your nominal lens focal length for lens_mm. After doing a decent pre-align, press the [search] button and wait a while for the results. Do this a second time and observe the change. If lens mm remains consistent, you can use it for your panoramas. The search function steps through a range of values for lens_mm and the image alignment offsets for x, y, and theta. It searches for the lens value that give the best alignment results for the given images. The process needs a minute or more, but you only need to do this once to characterize a given camera lens and focal length (zoom setting). Use the [save] button in the 1st dialog to put the focal length back into the image EXIF data.
 
Setting lens mm Manually
Make a panorama image of a brick wall (or any wall with lots of detail). The wall should be 5+ meters away. Take two photos with about 40% overlap. Within the panorama pre-align process, adjust lens_mm until overlapping bricks coincide. When making the two images, be sure to turn the camera on a vertical axis through the lens, minimizing lateral movement and rotation in other axes - otherwise your lens mm may not be optimal. The result should roughly correspond to the nominal focal length of your lens (35mm equivalent). It may be off somewhat (my 27mm lens works best with a lens_mm setting of 29-30mm). I speculate that this is because wide-angle camera lenses are not ideal lenses (pinhole equivalent). Most panoramas will still work well even if the lens_mm setting is off by 10%.
 
Color Matching Problems
If the images in a panorama have a large brightness difference, the automatic color matching may not work well enough. The most common problem is false sky colors. You may be able to improve this by brightening or darkening an input image to more closely match its neighbors. Sky can be easy to fix by selecting the false areas and copying sky from elsewhere in the image (Clone Image). It seems to work better if you brighten the darker image instead of darkening the brighter one. You can do this in the final dialog as described above, or you can do this before starting the panorama.
 
Panorama Limitations
Panoramas including nearby objects can be tricky: when the photos are made, be careful to turn the camera on an axis through the lens, with minimum lateral movement, otherwise the images may align poorly because foreground objects are shifted against the background (parallax). This is not an issue when the subject is 50+ meters away, since a small lateral movement has little impact on the image. Keep the camera level to avoid a large vertical shift (horizon shift), which can cause image distortions that may not be fully corrected. Avoid rotating the camera for the same reason.
 

PT Panorama
(File View > Combine > PT Panorama)
The Panorama Tools utilities (via the Hugin package) have been integrated into Fotoxx under the menu PT Panorama. The user interface is very simple: select (link) the input images in any order and proceed. Everything else is automatic. After a minute or so the finished panorama is shown and is now the current file in Fotoxx. The file name is <first input file>-PT.tif. This is an 8-bit TIFF file and is very large. You can save the file as JPEG and delete the original TIFF to save space. Panorama Tools usually does a fine job, but I have noticed minor alignment errors in some cases, usually too small to notice. It may be possible to eliminate these by using the full features of Hugin directly instead of the automated script used in Fotoxx. They may also be fixed using Mashup - see the related paragraph in Mashup, below.
 

Montage
Select many images and automatically join them into a compact format. This is a fast and easy alternative to Mashup, which is harder to use but offers more flexibility to vary the size and position of each image.

The first dialog is shown at upper left. Frame Width is the width of the frame image that will be created. The height is determined by the number of selected image files and the space required. Frame Margin is the margin size (pixels) around the frame image. Image Columns specifies the number of columns in the frame image (6 in the above example). The number of rows is determined by the number of image files selected. Image Margin is the space between images (pixels). Select the image files to combine (link). Press Proceed when done. The resulting image will be made in a few seconds (or up to a minute for hundreds of images). This image will typically have a very uneven bottom margin, as shown by the top image above.

The Optimize dialog begins automatically. It can rearrange the images so that the bottom margin is more even. Press Start and wait for the image to optimize. You can watch the progress. The function will exit if an optimum is found that makes the bottom margin even within 2 pixels. It may continue to search indefinitely if this goal is not reached. Press Stop to force the optimize function to exit with the best solution found so far. If optimization is successful, you will get even columns as shown in the lower image.
If the bottom edge is still uneven after optimization, you will be asked if you want to add margins to some of the images to make the bottom edge even. This will result in an even bottom edge, but the inside margins will no longer be equal.

TIP
: Optimization works best if the number of images is divisible by the column count, i,e, the same number of images in all columns. It also works better if the number of images per column is at least four (better more). This gives the optimizer enough flexibility to find a solution with an even bottom edge.

The last dialog appears, shown at lower left. You must now assign a unique name to your montage, one that does not conflict with other montages you have made before. A file save dialog is started, and you can save the new montage file anywhere you wish. If the map option is selected, a map of the included images is also created. This map allows you to click on any image in the montage to get a larger popup image which can be zoomed up to the full size of the original image, using the mouse wheel or F11. This will work now and at any time later, even if the montage file is moved, presuming the map file and the original images are still available. The saved montage file is given a special suffix to make it recognizable as a Fotoxx montage file.

The full montage file name is: your-assigned-name (fotoxx montage).jpg.
(if the map option is not selected, then "(fotoxx montage)" is not included in the name).
The map files are stored in  ~/.fotoxx/montage_maps/.

Note that the popup image feature is not standard. It will not work outside of Fotoxx.
 

Mashup (File View > Combine > Mashup)


Arrange multiple images and text in a layout. Images can be added, resized, rotated and moved around by dragging with the mouse. Images can be made entirely or partly transparent in selected areas. Text can be added, moved, resized and rotated. Text attributes can be specified: font, size, color, outline, shadow, transparency.  Lines and arrows can be added. The example here shows some of the possibilities.
 

In the first Mashup dialog, choose a background or layout image where other images will be placed. Choose an existing image or create a new monotone image with a specified size and color. You may also open a previously saved mashup project and continue editing.
 

The 2nd Mashup dialog is a choice: edit images, edit text, edit lines and arrows, rescale to a larger size, save the completed composite image (Done) or abandon the image (Cancel). The first three choices lead to dialogs to perform the respective edits. Each of these return to this dialog when done, so you can add or modify images, text, and lines/arrows in any sequence. Rescale is explained below.
  

Use this dialog to select and place images on the layout and revise their appearance: size, rotation, position, and transparency. An image may be partly or wholly transparent, overall or within specified areas. This means that the background image or an overlapped image can show through the transparent areas. The Add button leads to a gallery file selection dialog (link). Selected images are placed on the layout. After the images are added, click on any image to select it for modifications.  Drag the image from the middle to position the image in the layout. Drag the lower right corner to resize the image. Use the dialog controls to adjust the image. The dialog controls operate on the last image added, clicked or dragged. The Next button will cycle through the images one at a time, flashing the selected image. This can be used when overlapped images make it difficult to select the desired image by clicking it. Scale resizes the image. Angle rotates the image. The Stacking Order buttons raise or lower an image relative to other images - this determines which of two overlapping image will be on top. Base Transparency is used to make an entire image partly transparent. The [paint] button is used to make any part of an image partly or fully transparent, using the Paint dialog below. The [warp] button is used to bend or warp an image, using the Warp dialog below. The black margins checkbox can be used to remove black margins left by other edit functions, e.g. warp. These will be made transparent. The Margins controls can be used to make image edge areas transparent. The Hard margins cut off  image edges. The Blend margins make the edges partly transparent to blend them into the background or overlapped images.
 

"Paint" more or less transparency for selected image areas. Click on an image to select it. The mouse will have a circle around it to show the range of action. Use radius to adjust the size of the circle. Left-drag the mouse over the image to make it transparent in the areas covered by the circle. Right-drag to make it opaque. If Gradual is checked, transparency changes are made slowly as the mouse is dragged. Specify a power value for the mouse center and edges to make the changes faster or slower.
 

Click this dialog to insure it is active, and click on an image to select it. Drag the image with the mouse - the image will warp or stretch locally in the direction of the mouse (like sheet rubber). The area of warping is roughly given by warp span, which is a fraction of the image size. Each drag is a step that adds to previous steps. Recent steps can be reversed with [undo last], and the unbent image can be restored with [undo all].
 

The Edit Text dialog is used for placing text on the layout. Enter some text and press Add. You are asked to click on the layout where the text will be added. Drag it into position, then use the dialog controls to set font, size, color, angle, background color, outline size and color, shadow size and color, and transparencies for each of these. Drag the text to a new position at any time. Click on any existing text to show its properties in the dialog and revise them. Click on a text and press Delete to remove it. When done editing, press Done to return to the 2nd Mashup dialog.
 

The Edit Line/Arrow dialog is used for placing lines or arrows on the layout. It works very much like adding text. Enter a length and width and press Add. You are asked to click on the layout where the line will be added. A line is placed on the layout, or an arrow if one of the Arrow head options is checked. Drag the line/arrow to the desired position. Adjust length, width, angle and attributes (background, outline, shadow) using the dialog. You can also drag either end of the line/arrow to position that end while leaving the other end fixed.
 
You can use the mouse to move and resize objects in the layout:
  •  click the object to select it - it flashes to confirm the selection.
  •  drag from the approximate center to position the object.
  •  drag the image or text lower right corner to resize.
  •  for a line/arrow, drag either end to move that end only.

The keyboard arrow keys can also be used to move the currently selected object in 1-pixel steps.

You can also add images saved by Select Area. In the Edit Images dialog, press [Add] to start the file selection dialog. Use the [Top] button in the gallery window, select 'saved_areas'. Open any of the .png images found there. The original area outline is used, and edge blending works from these edges.
 
Mashup project files
You can save a project and open it later to continue editing. However, the project file will open successfully only if the layout image and all the overlay images are still available in their original locations. The Mashup project is rebuilt using these images and some saved metadata that is in the project file: image and text locations, scales, transparency, etc.
  
Mashup Rescale
Working with a very large layout (over 20 megapixels) can be quite slow, especially if the PC is not very strong. Dragging a large overlay image can be slow and jerky. You can work much faster if you use a smaller layout to build the project, and then make it larger after you are finished. There is no loss of resolution in the final image, since the overlay images and text are regenerated from the inputs (e.g. an image that was scaled to 0.2x in the initial layout is rescaled to 0.6x if the layout is magnified by 3x). The original full size image file is used for this rescale. The Rescale button allows you to magnify the project by 2x, 3x or 4x (4x, 9x or 16x by area). There is also a reset option to restore the original smaller project size. With this method, you can work with a 10 megapixel layout and resize it up to 160 megapixels when done. The larger layout can still be worked on directly afterwards, but it will be much slower.
 
PT Panorama Fix
Mashup can be used to fix minor alignment offsets from PT Panorama. After running PT Panorama, start Mashup (in the same Fotoxx session), and create a flat layout image with a good size for editing (e.g. 3000 pixels wide). Start the dialog for adding images to the layout. Navigate to the directory /tmp/fotoxx-xxx/ where you will find the images left by PT Panorama. They have been color matched and warped to fit together. Add these images to the layout. Check the box to make the black margins transparent. Resize them to fill the layout (all must have the same scale) and move them around to fit together. Zoom the window larger for precise alignment and align the images as well as possible. Small misalignments will persist if the images do not fit perfectly. There may also be brightness or color mismatches at the image edges, but these can be fixed later. Start the Warp Images dialog. Drag the mouse in small steps over a misaligned area to move an image into alignment with its neighbor. Use a small span (e.g. 0.1) to insure that correcting one misalignment does not create others elsewhere. Several cycles of dragging the images in several locations may be needed, but with patience you can make the alignments perfect. If there are visible brightness or color mismatches at image edges, blend these together using the margin blend controls. This should be done only after the alignment has been perfected - otherwise this will cause blurring where the images do not align, making alignment corrections harder to see and control. Lastly, rescale the layout to a larger size if wanted. This does not lose resolution, since the input images are rescaled and the warps are rescaled and re-applied.
 
 


Process Menu (Gallery View > Process)
  
 
Batch Convert Files (Gallery View > Process > Batch Convert)
This function is used to rename, convert, resize, upright and move multiple image files at once. An overlay image can also be added at a selected position (e.g. a title, credit line or copyright notice).

 
Select Files
Select the files to convert, from a gallery window (link).
New Name
Optional new name with optional inserted text (photo date, sequence number)
Sequence Numbers
Starting sequence number and adder for each output file.
New Location
Optional new directory location for the converted files.
new file type
File type for converted images, or "no change".
max. width, height
Output images will fit within these dimensions (ratio is not changed).
delete originals
Delete the input files after successful conversion.
copy metadata
Copy all EXIF and IPTC metadata to the output files.
upright
If an image is rotated 90 degrees, upright it (if status known from EXIF).
sharpen
Sharpen output images using the two supplied parameters
Overlay Image
Add an overlay image at a selected position in the output images. See below.
Make constant size
Make the visual overlay size independent of image dimensions. See Below

Plugins
The new file name may have inserted text: a photo date (from EXIF metadata), a sequence number, or the original file name. Specify what and where to insert with the following text:
 $oldname
The original file name is inserted here
 $s...
A running sequence number is inserted here. See below.
 $yyyy $mm $dd
The photo date (year, month, day) is inserted at these positions

Example: San Francisco $yyyy-$mm-$dd $oldname $sss
The input image file "Golden Gate Bridge.jpg", with a photo date of May 12, 2014, would convert
to the following output file name: "San Francisco 2014-05-12 Golden Gate Bridge 123.jpg".

For the "$s..." plugin, specify the length of the field with the number of 's' characters, e.g. "$ss" will be adequate for sequence numbers up to 99. Leading zeros are added for sequence numbers with fewer digits. Sequence numbers not fitting in the specified length will be as long as needed. Keep in mind that the file name sort order in the galleries is strictly ascii order, so the sequence of numbers 1, 2 ... 9, 10 will sort as 1, 10, 2 ... 9. You can keep the numeric order by specifying a field length of 2 ($ss), resulting in the sequence 01, 02 ... 09, 10. 

Either "$oldname" or "$s..." must be present to insure output file names are unique.
If New Location is missing or unchanged, delete originals is ignored.
If an output file already exists, the input file is not converted.
For an explanation of the sharpen parameters, see the Sharpen function.

Overlay Image
A small overlay image can be added at a selected position in all the output images. Use the Open button to select the image file. The overlay image will be scaled to Width percent of the output image width. The position is selected by clicking one of the Position buttons. If Make constant size is checked, the visual size of the overlay image will be made constant for the selected screen or window dimensions. Without this, a "tall" image that displays with left and right margins would have a visually smaller overlay.

An overlay image can be used to add a title, credit line or copyright to the converted output images. Prepare the overlay image as follows: Use New Blank Image to create a base image to hold the text. The scale does not matter, so make it larger than needed, say 1000 pixels wide. Use Paint Transparency to paint the entire base image transparent. Use Add Text to put the desired text on the base image, using the font, color, and other attributes as desired. Scale the text to fill most of the base image. The text is visible and the base image (background) is transparent. Use Trim to remove excess margins if needed. Save the final image; a convenient location is  ~/.fotoxx/saved_areas, but any directory can be used. This image is now ready to use as an overlay image for Batch Convert.

Albums
If image files are renamed or moved using Batch Convert, and if deletion of the original image files was also specified, then all albums containing any of the input files are updated to reflect the new names and locations.

Preserving File Sequence
If you specify a leading sequence number ($s...), the input file sequence will be preserved at the output location. Thus you can select image files in any order, or use an album with ordered images, and preserve this sequence in the output.
 
 
Batch Upright (Gallery View > Process > Batch Upright)

This function works like Batch Convert Files but only does the upright function. It uprights image files rotated 90 degrees. It depends on EXIF data to know if a file is rotated. It is much faster than Batch Convert Files. You can simply select all candidate files and let it find the ones that are rotated. The search speed is about 3000 image files per minute on a strong PC. There are two options to specify the files to search. Select Files leads to a gallery file selection dialog (link). Survey all files will check all image files in your database and upright those that are rotated.
  
 
Batch Delete/Trash (Gallery View > Process > Delete/Trash)

Select files from a gallery (link).
Select the option to delete or move to trash.
[Proceed]
 
 
Batch RAW (Gallery View > Process > Batch RAW)

This function converts selected RAW image files to JPEG, PNG-8, PNG-16, TIFF-8 or TIFF-16 format, using the library program libraw or the application program Raw Therapee. The PNG and TIFF formats have either 8 or 16 bits per color. RAW files generally have 10-12 bits per color, and noise beyond that. Therefore use a 16-bit format to keep all of the data available from a RAW file. The difference between 8- and 16-bit color is rarely visible, but a higher color depth provides a greater margin for retouch functions that can radically shift the brightness distribution, causing a problem known as "banding" or "posterization".

Use the [Select Files] button to choose one or more RAW image files from a gallery window (link). Choose one of the output formats. Choose one of the downsize options if wanted, and set the auto sharpen parameters if wanted. See the Sharpen function for an explanation of these parameters.

The image files are converted one at a time and displayed in the main window. Depending on the number of files, this can take a long time (a strong PC does about 40 files per minute for most RAW file types and TIFF-16 output). PNG-16 produces much smaller files than TIFF-16 because the files are compressed (with no quality loss). This also needs more time to do the compression work.
  
 
Script Files (Gallery View > Process > Script Files)

Script files allow you to define a set of edits once, and carry them out on as many image files as you wish. This can be useful when many photos of the same subject were made under the same lighting. You can choose one photo and edit it to perfection, recording every function used and adjustment made in the dialogs and curves. Then you can apply these edits to the entire batch of photos. Certainly this will not be as good as editing each photo individually, but it is much faster, and you can always go back and make additional adjustments after viewing the results.

Press [start] to begin a new script file. You will be asked to choose a file name for the script. Now choose an image file and perform your edits. After each edit function is done, a message appears to confirm its addition to the script. All the dialog inputs and curve edits are now saved in the script file. After the last edit, open the script menu again and choose [close] to complete the script.

To execute a script, open the script menu and choose [select]. This is the standard file selection dialog (link). Select the files to edit with the script. Now choose [run]. You will be asked to select a script file to execute. The script starts up and processes all the image files in sequence. You can watch the action as the images fly past. All modified images are saved as new versions, so the original files are not lost. Those you decide you no longer need can be quickly removed using the Batch Delete/Trash function.

If RAW files are processed, the corresponding outputs are TIF-16 files.

Not all edit functions are scriptable. Some edit functions use mouse drags on the image, e.g. Trim/Rotate and all warping functions. These are not scriptable. If you try to use one of these while building the script, you are told this is not possible.

The following functions are currently scriptable. Others are possible but have not been converted for scripting. If there is a need for some function not included, contact me.
Retouch Combo Brightness/Contrast/Color adjustments
Edit Brightness
Reshape the brightness distribution
Flatten Enhance dark areas with low contrast
Gradients Enhance local contrast where weak
Resize Specify width/height or ratio (e.g. 1/2)
Voodoo1 Automatic enhance
Voodoo2 Automatic enhance
Mirror Image
Mirror image horizontally or vertically
Sharpen
Sharpen fuzzy edges
Blur Blur image
Denoise
Reduce image noise
Color Mode
Make image B&W or color, positive or negative, or sepia
Shift Colors Tune colors using a slider
Color Saturation
Change color saturation (intensity)
Adjust RGB/CMY Tune colors using RGB or CMY sliders
Color Profile
Change the color profile, e.g. Adobe RGB <-> sRGB
Color Depth
Reduce the color depth (bits/color) for "poster" effect
Sketch
Convert image to simulated sketch
Cartoon
Convert image to a cartoon
Line Drawing
Convert image to a color line drawing
Color Drawing
Convert image to a color drawing
Graduated Blur
"Soften" the image while retaining details
Embossing
Add a 3D relief effect to an image
Tiles
Convert an image to tiles
Dots
Convert an image to dots (Roy Lichtenstein effect)
Painting
Convert image to a simulated painting
Texture
Add texture to an image (e.g. image on a rug)
Pattern
Add a pattern to an image (e.g. image on a brick wall)
Custom Kernel
Apply a custom convolution kernel to the image
Alien Colors
Add strange coloring to an image
 

Burn Images to DVD/BRD (Gallery View > Process > Burn Images to DVD/BRD)

Select any number of image files and copy ("burn") them to a DVD or BlueRay optical disc.
  1. Insert a blank disc and wait for the initial clatter to finish. A message may appear asking you what to do with the blank disc, or a program may start which the window manager thinks you want to run. Dismiss this window or program.
  2. Start the Fotoxx Burn function after step 1 is complete.
  3. The Select Files button starts a dialog for selecting the image files to be copied (link).
    Select any image files, but stay within the capacity of the disc (DVD 4.7 GB, BlueRay 25 GB).
  4. Select a DVD/BlueRay disc drive to use from the drop-down list (even if there is only one).
  5. Press the Start button. The list of selected image files is sent to growisofs.
    (an optical disc recording utility used by K3b, Brasero, and others).
  6. Progress is shown in a popup window, along with any growisofs error messages.
    The job will fail right away if the selected files do not fit on the selected disc.
The resulting disc is frozen. Leftover space cannot be used later to add more images.
 

Find Duplicate Images (Gallery View > Process > Find Duplicate Images)

This function is used to find duplicated image files anywhere within your image database. A duplicate image file can be an image file that exactly duplicates another image file, or an image file that "almost" duplicates another. For the sake of speed, thumbnail images are compared in memory. Hence it is possible that identical thumbnails are found for which the main images have some minor (likely invisible) differences that do not show up in the thumbnails. An image copy that was reduced to 1/2 size is likely be classified as a duplicate. The thumbnail size can be set from 32 to 256 pixels (max. width or height). A larger size reduces the probability of false positives. The thumbnail size greatly affects the amount of main memory required - e.g. for 100K images, size 32 needs 220 MB, whereas size 64 needs 880 MB. The memory required is roughly size x size x 0.75 x 3 x (image count). Two parameters are used to set the sensitivity for detection of identical or nearly identical images: pixel difference is the RGB value difference below which pixels are considered equal. Set to 1 to detect any pixel difference. pixel count is the number of different pixels below which two images are classified as duplicates. Example: if pixel difference = 3 and pixel count = 100, then images are classified as duplicates if fewer than 100 pixels are different by less than 3. The output is a gallery view, showing each set of duplicate images. Screening 8K images needs about 16 seconds on a strong PC.
 

Export File List  (Gallery View > Process > Export File List)

This function is used to create a file containing a list of image files. These are fully qualified file names, one per line in the text output file. This has no use inside Fotoxx. If you need to select images to feed into a shell script or another program, this is the way. The Select Files button starts a dialog for selecting the image files (link). Use the Browse button to select or define the output file.
  

Export Image Files (Gallery View > Process > Export Image Files)

This function copies selected image files into another directory. This would be typically used for copying files to a thumb drive, uploading files to a photo web service, etc. Select any number of image files by clicking gallery thumbnails (link). Select a directory where the image files will be copied. A limited set of metadata is also copied (relevant for a photo web services like Flickr). These are: photo date, keywords (tags), copyright, caption, comments, and geotag data (location).
 
 
Batch Add/Remove Tags (
Gallery View > Process > Batch Add/Remove Tags)

When adding tags to a large number of images having many of the same tags (i.e. the same event or subject), use this function to speed up the process. In the dialog, use the [select files] button to open a gallery window with thumbnail images from which you can select the image files (link).

The dialog shows two lists of tags: those to be added to the selected image files, and those to be removed. Select whether the tags you will enter subsequently are to be added or removed. You can go back and forth as you select tags.

Available tags are shown in the Defined Tags window below. One of these can be added to the currently selected tag list by clicking it. A tag can be deleted from either tag list by clicking it. Tags recently added are shown in Recent Tags. This is a convenience to make adding tags to a new batch of images easier, assuming that many of the same tags will be used repeatedly. Point and click the same way.

If the list of defined tags is long, it may be easier to type the desired tag into Enter New Tag. Existing tags matching what you have typed so far will appear in Matching Tags, and you can point and click one of these to add the tag to the image. If the input tag is new (no matching tag is shown), press [Add] when the tag is complete. It will be added to the selected tag list and to the list of defined tags under the category "nocatg".

If you are using tag categories, you can select a category, and only those tags will be shown in the list of defined tags. If your tags list is huge, this can reduce the list to a manageable size for pointing and clicking.

The [manage tags] button can be used to define tags with categories, as described above in Edit Metadata.

To replace tags, specify the new names in the add tags list and the old names in the remove tags list. To get a list of images having specific tags (which you want to remove or replace), use the Search Images function to find the images, then start this function and use the [select files] button to add all the image files that you just found, or select any subset.
 

Batch Rename Tags (
Gallery View > Process > Batch Rename Tags)

With this function, you can rename any number of tags and apply these changes to your entire image collection. Click on a tag to rename from the list of defined tags, enter a new name in the Rename to box, then click on the arrow button to add this pair of names to the list on the right side. Repeat for each tag to rename. If you make a mistake or change your mind, click on the pair of names in the list to remove them. Press [Proceed] when done.

A popup window shows the number of image files containing the affected tag names. Press [Yes] to proceed. The update rate is around 500/minute on a strong computer. The next time Fotoxx is started, the image index function will update all thumbnails for the same images, because the file modification date has changed.
 

Batch Photo Date/Time
(Gallery View > Process > Batch Photo Date/Time) 

This function lets you quickly fix a batch of photos that have no photo date/time or an incorrect one.
Use the [select files] button to open a gallery window where you can select the image files to be processed (link).

There are two ways to change photo date/times:
  1. Set a new date/time (left image above)
    • enter a new year only (yyyy) - only the year will be changed in the image files.
    • enter a date only (yyyy-mm-dd) - the date will be changed but not the time.
    • enter a full date/time (yyyy-mm-dd hh:mm:ss) - the image file date/times are fully replaced.
  2. Shift existing date/time (right image)
    Enter + or - values into any of the 6 fields: years, months, days, hours, minutes, seconds.
    Image file date/times will be shifted by the entered values.
Shifted date/time values propagate to the next higher value when necessary:
    Time 08:30 shifted by -50 minutes results in 07:40
    Date 2016-10-20 shifted by +6 months and -30 days results in 2017-03-21 (March has 31 days).
 
If the checkbox test is selected, a report of the old and new date/times for the selected files is produced for your inspection. No changes to the files are made. If you are satisfied that the results are correct, remove the checkbox and run again to update the files. The same set of files will be already loaded into the Select Files dialog.
 
 
Batch Add/Change Metadata (
Gallery View > Process > Batch Add/Change Metadata)

This function can revise metadata for multiple image files. You can add new metadata, revise existing metadata, and delete metadata. Use the [select files] button to open a gallery window with thumbnail images from which you can select the image files to be processed (link). Enter metadata key names and values to be assigned, or leave the value blank to delete this key. Press [apply]. You will get a confirmation message and you can proceed or cancel at this time. A list of commonly used key names is provided on the left. Click one of these to insert it into the first empty key name field in the dialog. This list comes from the file
    /home/<user>/.fotoxx/metadata_short_list
which you can edit to add other keys to the list. The [Full List] button provides information on how to get a list of all available key names.
   
 
Batch Report Metadata (
Gallery View > Process > Batch Report Metadata)

This function can report any metadata for any number of images. The output is a text file which is displayed in the default text editor for your system (e.g. Gedit for a Gnome desktop). You can use the search features of the text editor to find images matching desired metadata, or you can use the text file as input to other programs. Use the [select files] button to open a gallery window with thumbnail images from which you can select the image files to be processed (link). You can navigate through any number of galleries and select any number of image files in each. The [edit] button opens a new dialog (right) where you can select the metadata items to be reported. Click an item in the left column to add it to the right column and to the report. Click an item in the right column to remove it. Click the last item in the left column, "Other Item ...", to type-in any item name not appearing in the list, which contains only the most likely needed items.
 
 
Batch Geotags (
Gallery View > Process > Batch Geotags)

With this function, you can select many images and add the same location data to all of them. Use the [select files] button to select the image files from a gallery window (link). Then get the location data as described above in Edit Metadata, using the buttons [Find], [Web] and [Prev], or one of the map views. Press [proceed] to start the update process. Use this function also to fix inconsistencies in location spellings or latitude/longitude data. If latitude and longitude are left missing, only the location names are updated in the image files. This gives you a way to add missing names, or replace screwy names inserted by a camera GPS receiver. 
 
 
Image Locations (report) (
Gallery View > Process > Image Locations)

This is a fast way to find all photos made at a given location or location and date range. In the dialog, select the desired level of grouping: by country, by country and location, by country and location and date, or by date and country and location. In the last two cases, you can select a date range for grouping of images having nearby dates. A number N will group images together with dates that are N days or less apart from other images in the group (i.e. gaps are <= N days).

A popup window shows all locations found and the dates of photos taken in those locations. The count of photos taken is also shown. In the above example, 29 photos are from Grindlewald in March 2008. Click on a line in the report to get a thumbnail gallery of those images, and from there you can click on any image to view or edit. Note that this method uses only location and country to find the images. Earth coordinates are not used. If you have images with missing or inconsistent earth coordinates for a given location, use this function to get all of them, and then use Batch Geotags to make the earth coordinates consistent.

You can also use the keyboard up/down arrow keys to rapidly step through the report lines and view the gallery for each line. The page up/down keys and the home/end keys also work. You can enter other keys from the keyboard to jump to matching lines in the report. This is a fast way to find a desired location.
 

Image Timeline (report) (
Gallery View > Process > Image Timeline)

This report produces a timeline of image counts by month. Click on a year and month in the report to get a thumbnail gallery of all images with a photo date (EXIF) in the selected month. The example here shows 377 images for May 2016. You can also use the keyboard arrow keys (up/down/left/right) to step quickly through the months or years and view the corresponding galleries.
  

Search Images (report) (
Gallery View > Process > Search Images)
Use the Search Images function to find images having any desired metadata. A metadata index file is used for searching, which makes it possible to search thousands of images per second. The index contains a subset of the EXIF and IPTC metadata in the image files. Some of this data is automatic, created by the camera. Other data, such as tags and star ratings, can be added by the user.

There are two report formats: a gallery of thumbnails showing all images that match the search criteria, or a metadata report which combines thumbnails with metadata text. Here is an example of the metadata report format:

 

Dialogs    

The main dialog is on the left. If the button "search other metadata" is used, the dialog on the right appears. This can be used to select metadata that is not available in the main dialog. Details are given below.

In the main dialog, select which images to search, either all (the entire image database) or current set, meaning the images in the current gallery list, which can be a directory, album, or the results of a prior image search. Then choose what to do with the matching images found: new set means replace the current set with the images found, add means add them to the current set (gallery), and remove means remove them from the current set. To remove images, you must search the current set.

Select the desired report type. The gallery report is a page of thumbnail images, as long as needed to hold all the images that match the search criteria. The metadata report has both thumbnail images and a list of metadata items beside each thumbnail. These are some standard items (date, rating, tags, geotags, captions, comments) and any items you added in the optional search metadata dialog.

Enter your search criteria. Select desired tags, dates, star ratings, text (comments, captions), file or directory names, and location names. More details about these are below.

Press the [proceed] button to perform the search. Matching images are displayed in gallery view mode. Choose images to view or edit by clicking the thumbnails. Navigate this set of searched images using the navigation buttons in the gallery view window, or the [Prev/Next] button in the file view window. You can save the searched images as a permanent album, which can be further edited to add or remove images (see Manage Albums).

Available tags are shown in Defined Tags and can be chosen with point and click. If the list of defined tags is long, it may be easier to type the desired tag into Enter Search Tag. Existing tags matching what you have typed so far will appear in Matching Tags, and you can point and click one of these to add the tag to the search list. If you type a tag with no match (it does not exist in any image), the list of matching tags will be empty.

If you are using tag categories, you can select a category, and only those tags will be shown in the list of defined tags. If your tags list is huge, this can reduce the list to a manageable size for pointing and clicking.

A date range may be entered to restrict the search to images within the date range. Choose photo date (EXIF, from camera) or file date (last file modification date). The format is yyyy-mm-dd. Images are selected which have a date on or after the first date, if present, and on or before the second date, if present. Missing month/day default to 01/01 for the low date limit and to 12/31 for the high date limit. Times may optionally be specified using the format yyyy-mm-dd hh:mm. Missing times default to 00:00 and 23:59. 

A pair of star ratings may be entered to restrict the results to images having a star rating within the given range. A missing low value implies no stars, and a missing high value means the highest rating, 5 stars.

If last version only is checked, image files with multiple versions will be filtered to include only the last version of each file. This depends on the Fotoxx version naming convention: The original file name is normally filename.ext, and edited versions are filename.v01.ext, filename.v02.ext, etc. If no versions are present, the original file is selected. Otherwise, the last available version is selected. If this convention is not followed, then this option will do nothing.

Image directory and file names may be searched. In the field search files, enter any number of names used for your image directories and file names, separated by blanks. An input of [ egypt cairo ] would match all image directory or file names containing either of these strings. Substrings will also match.
 
Image comments and captions may be searched. Enter the words to search for in the dialog search text field, separated by blanks. These will be matched to every word in the comments and captions of all images, and matching images are selected. Substrings will also match.

To search locations, enter one or more location or country names in the search locations field. Only image files with geotags matching one of the entered locations will be selected.

The radio buttons all and any apply to tags, text, file names, and locations. You can select images having ALL the entered strings, or ANY of the entered strings. Example: if the location search field is "new york" and "any" is selected, then images located in New Mexico or York, PA would be included. If "all" is selected, then only images located in New York would be selected.

If you wish to find images with missing data, you can enter "null" as a match value.

Starting with release 17.04, the tag "fotoxx" is automatically added when an image is edited. This enables you to search for edited images by specifying the tag "fotoxx" along with whatever other criteria you need.

Search Metadata Dialog
You may use this dialog to search for "extra" metadata items not present in the main dialog. The items available for any given image file can be shown using View Metadata (long). These include camera make and model, exposure time, F-number, ISO, metering mode, focal length, shooting mode, etc. etc. You can enter shortcut names like "exposuretime" instead of "Exposure Time". You may also enter match criteria, if wanted, so that only the images with matching metadata are reported. For example, if you enter "model" with the match value "DMC-FZ28" (a Panasonic camera) then only the images taken with this camera will be reported.

The matching logic can be selected for each metadata key:
match method
match value(s)
matches
one or more metadata text values to be selected, separated by commas
contains
as above, but any metadata containing these text values will be selected
number =
a single numeric value - equal metadata values will be selected
number =>
a single numeric value - equal or greater metadata values will be selected
number <=
a single numeric value - less or equal metadata values will be selected

Performance (SSD, 3 GHz computer)
Report Type
metadata search items
search speed
report speed
gallery
indexed items only
>1000/sec
>1000/sec
gallery
some non-indexed items
90/sec
>1000/sec
metadata
indexed items only
>1000/sec
130/sec
metadata
some non-indexed items
90/sec
130/sec

Search speed is the speed to screen the entire image collection to find matches. Report speed is the speed at which matching images are output to the report window (thumbnail and reported metadata).

There is a separate topic for image organization options, which explains the options for optimizing image searching.

Album "Search Results"
The output of Search Images is automatically saved in the album Search Results. This is for convenience. You may perform a search and then perform other functions which change the current gallery, replacing the search results. If you need to refer to the previous search, this is instantly available in the album Search Results.
 
 
 
 

Tools Menu (File View > Tools)
 
 
Index Image Files (File View > Tools > Index Image Files)






top image directories, one or more












thumbnail directory
 
extra metadata items to include in file index

Fotoxx needs to know where all your image files are located (directory and file names) and their embedded metadata (dates, tags (keywords), geotags, captions, comments, ratings). This data is indexed for fast searching. Fotoxx also creates thumbnail images so that the gallery windows (thumbnail pages) will work fast. Fotoxx does not modify or copy your image files - it only reads them to make the index and thumbnails. These are typically 2% as large as the JPEG files from a modern camera.

The Index Image Files function runs whenever Fotoxx is started. This function will create missing thumbnails, replace outdated ones, and refresh the metadata index using current data from your image files. This may need significant time if you have many thousands of new images. The speed can range from 800 to 6000 images per minute, depending on processor speed, disk speed, and average image file size. If there are no new image files, indexing completes quickly. Indexing can also be started manually from the Tools menu.

NOTE about indexing speed: The rates stated above are for JPEG files around 5 MB. Larger JPEGs and TIFFs are slower. RAW files and large PNG files may be very slow: 200 per minute or less. You can find some benchmark data for indexing speed in the Technical Notes topic.
 
Image files modified or moved within Fotoxx are taken care of automatically. The Index function is used only for new image files created from outside Fotoxx (e.g. a new batch of photos was added), or for files moved or renamed from outside Fotoxx.
 
Enter your top image directories (e.g. /home/<user>/Pictures). Use the [browse] button to locate and add directories. These directories and any subdirectories containing images will be processed. It does not matter if other files are mixed with the images. The simplest way is to use /home/<user> as the only top directory, but it is better to separate the image files from the 100,000 other files that may be under /home/<user>. Delete an entry by clicking the corresponding X.
 
Enter the thumbnails directory where thumbnail files will be stored. Use the supplied default or set your own location. The directory name must end with .../thumbnails, and this will be added to your selection if needed. The directory is created if not already present. Indexing will run faster if this is on a separate physical disk from the image files.
 
If you have used directory or file names to classify your images, you can make immediate use of these in the Search Images function. If you have saved dates, captions, tags, geotags, titles, or ratings in your image metadata (using other image edit apps), these will also be searchable. After the images have been indexed, searching them by any of these criteria is almost instantaneous. Other items in the image metadata can also be searched, but at a slower speed. See Search Images for details.
 
Index Metadata Items

You may add up to 20 other metadata items to the image file index. These are in addition to the standard items mentioned above. Metadata in this list can be searched as fast as the standard items. You may not need this, but if you sometimes need to search non-standard metadata items, you can add them here and the search speed will be almost instantaneous. Press the [select] button for "extra metadata" to get the above dialog. Click on items in the left list to add them to the right list - the items to be added to the index. Click on an item in the right list to remove it. Press [done] to commit the changes. If you make any changes to this list, your entire image collection will need to be indexed again. This will proceed much faster than the initial indexing, because the thumbnails are already there and do not need to be created. Only the image file index will be refreshed. On a strong computer with SSD disk, the speed is over 6000/minute.
 
Fotoxx Startup Time
If you have a huge image collection, the first startup after a reboot may need significant time, even if there are no new image files. Subsequent startups are faster because file directories are now cached in memory. If startup time is still a problem, you can bypass the indexing. This may be especially useful if you use a file manager (e.g. Nautilus) to start Fotoxx with a selected image file, and you want the image to display instantly. See the User Settings topic immediately below for more information.
 
 
User Settings (File View > Tools > User Settings)
Various user preferences and settings are collected in this dialog.
They are also saved in the file  /home/<user>/.fotoxx/parameters.

 
Startup Display  Determines the initial display when Fotoxx is started:
Recent Files Gallery: the most recently seen image files (gallery display)
Newest Files Gallery: image files most recently added to the image database (gallery)
Specific Gallery: the specified directory gallery
Album Gallery: the specified album gallery
Previous Gallery: the gallery last used in the prior Fotoxx session
Previous Image File: the file last viewed in the prior Fotoxx session
Specific Image File: the specified image file
Blank Window: start with no current image and the first top image directory gallery
Browse
Opens a dialog to browse for the starting directory, album, or image file
Background color
The background color for F-view and G-view windows
MenuText
Main menu font color and background color
Menu Style The menu style: icons only or both text and icons
Dialog font
The font name and size to use in all menus and dialogs (Bold, Italic, etc. are ignored).
Zoomed Image
Drag: image moves with the dragged mouse.
Scroll: image moves against the dragged mouse (like invisible scroll bars).
Fast: movement is magnified so that the entire image can be scrolled with one sweep of the mouse.
Zooms/2x
Choose 1-8 zooms (mouse click or [+] key) for each 2x increase in image size.
JPEG quality The default quality value when saving an image as a jpeg file type.
Curve node
 capture distance
The minimum node separation for edit curves. Also the mouse capture threshold when a node is clicked or dragged. The default is 5% of scale, allowing up to 20 nodes in a curve. If you use a touchpad instead of a mouse, set this value higher to compensate for lower positioning accuracy.
Map marker size
The pixel size of the markers (red dots) marking image locations on maps.
last file version
Show only the last version of image files, for both file view and gallery view (if a directory).
shift image right
If the window is wider than the image, shift the image right to maximize left margin for dialogs.
image index level  
(2 parameters)
0/1/2 = no image index / old index only / old index with search for new image files
This affects startup speed. See below for a complete explanation.
RAW file types The RAW file types recognized. If your camera uses something else, add it to the list and this may work.
You can also shorten the list to those file types you actually use.
video file types
The video file types recognized (e.g. .mpeg .mp4 .h264  etc.)

Image index level
These two parameters govern a tradeoff between fast Fotoxx startup time and the completeness of the image index. A complete image index is required for accurate results from image search and map functions. Building a complete image index when Fotoxx starts can take from 0.1 seconds to 3 hours (if indexing was never done and you have a million image files). The time to start Fotoxx depends on the following factors:
  • The first startup after a reboot needs more time than subsequent startups. Subsequent startups are faster because the index file and the image directories are now cached in memory. First startup time with a 5400 rpm disk is roughly 1 second per 10K image files. Subsequent times are roughly 5x faster. First startup with an SSD disk is roughly 1 second per 40K image files (these numbers assume a strong PC).
  • If there are (new) image files that have never been indexed, they must be found and added to the image index. New files are processed at 800-6000 per minute, depending on computer and disk speed and average image file size.
If you start Fotoxx by selecting a file from your file manager (e.g. click on the file name in Nautilus), you may want to see the image immediately and not wait for indexing. You can control this with the two parameters image index level.
  • The first parameter controls how Fotoxx indexes when started from a launcher or command. The 2nd parameter controls how Fotoxx indexes when started by clicking a file name displayed in Nautilus, etc.
  • A value of '0' means no indexing at all. Startup is very fast. Image search and map functions are disabled. Viewing and editing images and metadata can be done normally.
  • A value of '1' means load the old index file but do not search for new image files to update the index. Startup time is moderately fast. Image search and map functions do work, but any new (not yet indexed) image files are excluded from the outputs.
  • A value of '2' means full indexing: load the old index file, search for new files, merge these two, and update the index file. Startup time depends on how many image files must be indexed and the speed of your computer. Image search and map functions produce complete results.
You can also start the index process manually (Tools > Index Image Files) if you want to use search and map functions with complete results. You do not have to go back to User Settings and change the parameters.
 

Keyboard Shortcuts (File View > Tools > Keyboard Shortcuts)






This function is used to view or change custom keyboard shortcuts. The currently assigned shortcuts are shown in the first window. If you press the [Edit] button, the second dialog is shown, where you can add and change keyboard shortcuts. Enter a new shortcut using the keyboard. You can use the keys A-Z, 0-9, F2-F9, and most of the symbols (# $ & ^ <  etc.). You can combine a key with Ctrl, Alt or Shift: Press and hold Ctrl, Alt or Shift, then press the key, then release both. Select one of the available menu assignments from the list on the right. To remove a shortcut, select it and press [Delete]. Press [Done] when you are finished. [Cancel] will discard all changed.
 
  
Brightness Distribution Graph (File View > Tools > Brightness Graph)

This function opens a small window that shows a brightness distribution graph of the current image in F-view, or the currently selected area of the image. This graph updates immediately for new images or as edit functions change the image. There are four graphs in four colors: red, green, blue graphs are for the respective colors. The black graph is for overall brightness. Use the buttons [All]  [Red] [Green] [Blue] to select the colors to show.
  
 
Grid Lines (File View > Tools > Grid Lines)

This function adds or removes horizontal and vertical lines across the image. The lines are useful when an image must be rotated for horizon alignment, or when an image is unbent or warped to straighten walls or other objects in the image. The settings for x- and y-spacing control the spacing (pixels) between the lines. If the controls for x- and y-count are NOT zero, then the x- and y-spacing values are ignored and the number of lines will be set to these counts. Example: set x- and y-count to 2 lines each in order to divide the image into thirds horizontally and vertically. The x- and y-enable checkboxes can be used to enable and disable the vertical and horizontal lines separately. The keyboard shortcut Alt+G can be used to toggle the grid lines on and off (this shortcut can be changed). If an image is printed with grid lines enabled, the grid lines are also printed. The x- and y-offset controls can be used to shift the grid lines to intersect a desired point in the image.

Several edit functions have a button [grid] which starts this same dialog. The resulting grid line settings are specific to that function only, and will be restored whenever that function is in use.
  
 
Line Color (File View > Tools > Line Color) Change Color of Foreground Lines
Some functions draw lines over the image (Trim/Rotate, Select Area, others). You can change the color of these lines to maximize contrast against the background image. The small dialog can be left open while editing, to conveniently switch among the available colors (black, white, red, green).
 
 
Show RGB (File View > Tools > Show RGB)

When a point on the image is clicked, the RGB values are shown in a dialog window. The last 9 clicked points are displayed. The values have the format xxx.dd, where xxx is the upper 8 bits of the color value and .dd is the lower 8 bits. The range is 0.00 to 255.99. The lower 8 bits are zero unless the image is being edited or the image is a 16-bit TIFF or PNG file. EV (exposure value) is an alternative unit, useful for precise color adjustment. EV is zero for mid-brightness (128). The outputs are updated immediately if the image is being edited. The last nine points clicked are shown. The points are labeled on the image corresponding to the letters A-I in the dialog window. If "delta" is checked and the image is being edited, then the changes are shown instead of the absolute values. If no edit is active, "delta" does nothing.
  RGB  0  1  2  4  8  16  32  64  128  256
  EV  -∞
 -7  -6  -5  -4  -3  -2  -1  0  1
  

Magnify Image (File View > Tools > Magnify Image)

This function magnifies the image in an area around the mouse pointer. Left-drag the mouse around the image to magnify different areas, analogous to viewing a printed image through a magnifying glass. Use the dialog to adjust the radius of the area and the amount of magnification. A mouse click will end the magnify, and a new mouse drag will start it again.
 
 
Dark/Bright Pixels (File View > Tools > Dark/Bright Pixels)
This function is used to highlight the darkest and brightest pixels in an image. Adjust the two sliders to set the brightness thresholds, which are initially 0 for dark pixels and 255 for bright pixels. Pixels with a brightness less than the dark threshold or greater than the bright threshold are highlighted on the image. The image responds quickly to changes in the sliders. You can use this function in parallel with edit functions to control edit results.
 

Monitor Color (File View > Tools > Monitor Color)

Eight color bands are written across the screen with brightness from zero (black) to 100%. You can use this to adjust the brightness of your monitor. The left end of each stripe should be as black as possible, but you should start to see some color within a few mm from the left edge. If the completely black portion is wider than this, adjust the monitor. There are 255 brightness steps from black to 100% (8 bits per color). The steps are too small to distinguish with the eye. This evaluation should be done in a darkened room (with little external light falling on the monitor).
 

Monitor Gamma (File View > Tools > Monitor Gamma)

Gamma determines how RGB brightness values (0-255) are converted into brightness on the monitor. The standard value is 2.2 and this should normally be used for image editing. Adjust the dialog slider until the middle band has the same brightness as the left and right bands at the scale location 2.2. Be far enough from the monitor that you cannot see the fine lines in the chart. The chart only works at 100% size, so do not zoom the chart. The command line utility "xgamma" is required (normally present).

The chart image originates from Norman Koren. The linked web site has more information about this chart.
The chart here in the User Guide has been reduced
  
 
Change Language
(File View > Tools > Change Lanuguage)
This function allows you to change the GUI to one of the available languages. If your language is not available or has missing translations, how about some help? See menu Help > Translations.
  
 
Missing Translations (File View > Tools > Missing Translations)
This function lists all missing translations to a popup window. Translations left as English are not reported, since this is often deliberate, e.g. words like "font" or "icon".
 
 
Color Profile (File View > Repair > Color Profile)
Use this function to change the ICC color profile. If you have images with Adobe RGB color, you can change to sRGB for better colors on a monitor, or for uploading to a web site. Any EXIF embedded color profile is removed, and the EXIF key "ICCProfileName" is set to the new profile. Because the relevant metadata is changed, a new file version is created automatically, and the input file is left unchanged with the old color profile. To view the color changes, use the KB arrow keys to switch back and forth between the old and new images.
  
You may need to install ICC color profiles. In Ubuntu, the package names are icc-profiles and icc-profiles-free.
 
 
Printer Color Calibration (File View > Tools > Calibrate Printer)
This utility may be able to improve the color accuracy of printed images.

How It Works
A chart of known colors is printed on the target printer. Some of the printed colors will be slightly wrong due to printer imperfections. This printed page is scanned into a file, and the colors in this file are compared to the original colors that were sent to the printer. Any differences are errors that are now known. When an image file is printed, these errors are subtracted from the image colors before printing. The printer adds the errors back, leaving a result that is theoretically correct. Practice may be otherwise. My own result was a modest improvement, detailed below.
 
Hopeful Assumptions
  • Your scanner produces accurate colors. Scanners are generally more accurate than printers.
  • The printer color errors are small enough that negating them before printing will cancel
    most of the error. This is less likely if the errors are large.
  • The limited set of colors in the chart (1728) can be used to calculate all other colors.
    (each color adjustment is interpolated from the nearby colors in the chart).
Perform the following steps in sequence, as listed by the dialog window. Each step has instructions to perform the step. All files normally reside in the directory /home/<user>/.fotoxx/printer_color/.
  1. Generate and print the color chart file (printchart.png) on the printer to be calibrated.
    Use a large paper size (A4 or US Letter), vertical paper orientation, no margins.
  2. Scan the printed chart into a PNG file. Use 300 dpi or more to make a large file.
  3. Edit the PNG image to trim off margins left by the scanner. Save the edited image
    as <chosen-name>.png. Use a name indicating the printer settings and type of paper.
  4. Process the edited chart file to create a color map file <chosen-name>.dat.
    Use a name indicating the printer settings and type of paper used.
  5. Print a color-adjusted image. You are asked for the color map file created in step 4.
Once you have made the color map file, you can print any image using step 5 only.
There is also menu function to do step 5 only: Print Calibrated.

Precautions For Best Results
  • Use a large paper size for printing the chart (A4 or US Letter) to make the color tiles as big as possible.
  • Clean the scanner glass before scanning the color chart. Dust spots or smudges will falsify the colors.
  • Scan with a high DPI setting (300+) to make a large chart file. This will make the next step more accurate.
  • Scanners can skew a scanned image into a slightly non-rectangular form. If this happens, you will notice it when trying to trim off the margins: the fat green margin lines will not align perfectly with the trim rectangle, even after slightly rotating the image for best fit. If this happens, use Fix Perspective to square the image first, then trim the margins.
  • When editing the scanned chart image, be sure the darkest row is at the top. Trim off the margins surrounding the color tiles. Use the fat green margin line surrounding the color tiles as a guide: cut off this line exactly, leaving only the tiles. Work with 2x magnification while adjusting the trim rectangle. Accurate trimming is critical. The tile positions are calculated from the final image dimensions, assuming 35 equal columns and 50 equal rows. If some edge tiles are cut short, or if margins are left, then the calculated tile positions will be offset, and the measured colors will be wrong. A small error of 1-2 pixels is tolerated, because the outer 20% of each tile is not used to read the tile color. You can use the keyboard arrow keys to make 1-pixel movements in the trim rectangle. The last corner pulled with the mouse is the corner that the arrow keys will move.
  • If you notice any black or white spots in the scanned chart image (from dust), fix them with Clone Image.
  • The output color map file can be used only for the paper, ink, and printer settings used for the calibration. Any other combination needs its own calibration and color map file. Use the name of the color map file to include this information, e.g. cmyk-glossypaper.dat.
Results (my Canon printer)
    
A standard color chart was scanned and printed on photo paper. The left print had no adjustments. The right print was adjusted. This image has all three charts in one photo. The printer did a fairly good job by itself, but the adjusted print is slightly better. Some colors are more accurate. Others are little changed.
 

 
You may be able to find a real ICC color profile for your printer and its proprietary inks and photo paper. Using this would most likely produce better results. Check the installation CD supplied with your printer, and also the manufacturer's web site. There are also professional services to generate an ICC color profile. The procedure is similar to the one described above: you print a color chart supplied by the service, send the printed chart back to them (along with some money), and receive an ICC profile, a file which you can install. Try a web search for "icc profile service".
  

Uninstall AppImage
(File View > Tools > Uninstall AppImage)
This function will completely remove the AppImage package.
Desktop menu/launcher, icon, and executable file are all deleted.
 
 
Show Resources (File View > Tools > Show Resources)
This is a diagnostic tool to monitor resources, especially memory leaks.
The following data is output to the log file (or terminal window):
  •  process time: CPU time used, since the last time shown
  •  zdialog counts: total dialogs in memory and those still active (visible)
  •  zmalloc counts: memory allocations and releases since the last time shown here
  •  MB: total allocated memory at this time in megabytes
 
 
 

Help Menu (File View > Help  and  Gallery View > Help)
 
Quick Guide
This is a 1-page introductory document with Fotoxx essentials.

User Guide
The user guide (this document) is displayed.

User Guide Changes
This is a summary of recent changes in the User Guide. The intent is to enable you to survey the changes without reading the whole document.

README
Displays the README file distributed with Fotoxx, which may contain new information about installation or dependencies. When you install a new release of Fotoxx, you should look at README and the Change Log to check if there is anything special you need to be aware of.

Change Log
Displays the change log file distributed with Fotoxx, containing details about functional changes, additions, or bug fixes for the current and previous releases.

Log File
Display the current session log file, where some error messages may appear.
This is the same data that is visible when you start Fotoxx from a terminal.

Translations
Displays a short text file which explains how to make a new translation or change an existing one.

Home Page
Shows the Fotoxx home page from the Internet. Look here for program updates (the page named "recent changes"). This page is published via RSS and you can subscribe to get timely notification of changes.

About
This displays a short message about the Fotoxx version number, license, credits, and contact address.
 
 
 
 

Gallery View

 
 
Image Management in Fotoxx

A gallery of thumbnails can represent a file directory, the output of an image search function, one of the built-in galleries (recent files, newest files), or an album (an ordered list of files with a user-given name). Batch Convert can accept files selected from any gallery and output converted files into a directory. The image cache is a holding area for user-selected files that can be later added to an album. Manage Albums (see below) can create new albums from a gallery or the image cache, or select images from galleries and add them to the image cache.
 
 


Gallery Menu (Gallery View > Menu)
 
Sync Gallery (Gallery View > Menu > Sync Gallery)
This is a duplicate of the Sync Gallery function in File View.
 

All Directories (Gallery View > Menu > All Directories) (or [TOP] button > ALL)

All top image directories are shown initially.
Click on [+] to unfold subdirectories, or on [-] to fold them back in. You can click on any directory name to get a gallery list of all the images in that directory. Keep the window open for a convenient means to navigate anywhere in your image collection.


  


Bookmarks  (Gallery View > Bookmarks)


Assign names ("bookmarks") to chosen gallery locations (directory and image file), keep in a list, and use the list to select a name and go instantly to the associated gallery position. There are two parts: an edit dialog to build the list of bookmarks, and a dialog to show the bookmarks and select a location to go to. To assign new bookmarks, select the bookmarks menu and then select [Edit Bookmarks] in the dialog that follows. The Edit Bookmarks dialog is started. Click on a gallery thumbnail to add this location to the bookmark list. The assigned bookmark name will default to the file name. This name appears in an edit field where you can assign a better name. If you select an existing bookmark with a mouse click, its name is shown in the input field. You can change the name or press [delete] to remove the bookmark. New bookmarks are inserted at the last bookmark location selected. To go to a bookmark, select the bookmarks menu. The list of bookmarks is shown. Click on an entry to go there.
 
 
Manage Albums (Gallery View > Menu > Manage Albums)
An album is an arbitrary sequence of images that is manually assembled from existing images. This is one method to make groups of associated images. An album is simply a list of its member image files. The image files themselves are not copied or changed. A given image file can be a member of multiple albums, or may be present more than once within an album. Albums can be used to group images with some shared attributes, such as photos from a vacation trip, photos of a given person taken at different times or events, a "best photos" collection, etc. You can add and remove images in an album and rearrange the order of the images. Once an album is made, you can call it up by name and it becomes a gallery. This gallery works like any other: you can scroll through the gallery, step through the images with the [Prev/Next] button, or edit the images. Keep in mind that editing an image in an album will edit the underlying image file, so any other way to view this image will show the same changes.
 
Image Cache
This is an intermediate storage area used to hold selected images for later insertion into an album. In general, you select image files from gallery windows and add them to the cache, then you make a new album with images from the cache, or you add the cached images to an existing album at a chosen position within the album. After this you can rearrange the images using drag and drop.

Manage Albums Dialog


New
: Start a new album or replace an existing one.

Choose
: Choose an album to view or edit. The gallery window will show the album images.
Use the thumbnail popup menu (below) to add or remove images.

Add:
Add images to the image cache using the standard dialog for selecting images from gallery windows (link). Select image files in any order from any gallery. Selected images are added to the image cache. Add cached images to the album using the popup menu (below).


Clear
: Discard all images in the image cache.

Delete
: Select an album to delete. Image files are not deleted.
 
New Album dialog

Specify an album name, or use the [browse] button to select an existing album to be replaced. Select one of the three options. Option 1 creates the album with no images. Option 2 fills the album from cached images, if any. Option 3 creates the album from the current gallery. This may be a directory, the output of an image search, or an album.
  
Right-click Popup Menu

 
Popup Image Pop-up a large resizable window for the image.
View Metadata Metadata short report for clicked image file.
Edit Metadata
Open dialog to edit primary metadata items.
Edit Any Metadata
Open dialog to edit any metadata items.
Copy to Desktop
Copy the image file to the desktop.
Copy to Clipboard Copy the image to the clipboard (for other apps to paste).
Copy to Image Cache
Add the clicked image to the image cache.
Cut to Image Cache
Remove the image from the album and add it to the image cache.
Paste Image Cache Here (keep)
Paste Image Cache Here (clear)
Click on the left / right side of a thumbnail to insert cached images
before / after the thumbnail. Keep or delete the image cache.
Remove from Album
Remove the clicked image from the album
  
Drag and Drop
You can rearrange images within an album by dragging thumbnails with the mouse. Drag the thumbnail until the mouse cursor changes to a small image of the thumbnail. Continue dragging this image to the position where it should be inserted, and release the mouse button. Position the mouse roughly between the images where the dragged image is to be inserted. If the drag approaches the top or bottom edge of the window, the gallery will scroll to bring more images into view. You can use two instances of Fotoxx to make the selection or movement of many images faster. Drag and drop images from any source gallery on to an album gallery. The source gallery can also be an album or any other gallery. Make a temporary album from the image cache in order to select images in any desired order.
 
Summary
  • Make a new album: Use the Manage Albums [New] button and provide a name.
  • Add images to the image cache: Use the [Add] button to select images from any gallery and add them to the image cache by clicking thumbnails. At any time, from any gallery, right click on a thumbnail and choose the menu "Copy to Image Cache".
  • Add image to an album: Use the [Choose] button to open an album. Right-click on a thumbnail and select one of the Paste menus to insert the image cache. Click on the left / right side of the thumbnail to insert the images before / after the thumbnail.
  • Remove images from an album: Right-click each thumbnail, then select the "Remove from Album" menu.
  • Move images within an album: Right-click each thumbnail to move, select the "Cut to Cache" or "Copy to Cache" menu. Right-click at the target position, select the "Paste Cache Here" menu. To move one image at a time within a single album, you can use mouse drag and drop.
  • Drag and drop can be used with two instances of Fotoxx to move images from any gallery in one instance to an album gallery in the other instance.
TIP: If image files are renamed or moved using Batch Convert, and if deletion of the original image files was also specified, then all albums containing any of the input files are updated to reflect their new names and locations. Therefore, don't just rename directories if they contain images in albums, or manually move image files among directories. Instead, use Batch Convert to move the image files to the new directory, then delete the old directory if it is left empty.
 

Update Album Files (Gallery View > Menu > Update Albums)

Update the image files in selected albums with the latest file versions. If image files are edited and new versions created, albums will still reference the prior versions. This function checks album files for later versions and replaces the album files (links) where needed. You can select any number of albums for processing.
No files are deleted or moved. The file names (or links) within albums are updated.
 

Replace Album File (Gallery View > Menu > Replace Album File)

Replace a designated old image file in all selected albums with a new image file, or add a new file to the albums following the designated old file. If an image file is edited and a new version or new file is created, albums referencing the old file may need updating. Choose any number of albums and specify the old file name and the new file name. No files are deleted or moved. The file names (or links) within albums are updated. After editing and creating a new version of an image file, right click on the image (or its gallery thumbnail) and select this function from the popup menu. The names of the new image file and its previous version are stuffed into the dialog automatically, making it easier to update albums after completing an image update.
 
 
Slide Show (Gallery View > Menu > Slide Show)
With this function you can show an album of images as a slide show.

Use the Manage Albums function to assemble the images for a slide show as an album with an assigned name. This allows you to collect images from anywhere in your image database and order them as desired. In the slide show dialog (top left), press [Select] and choose an album from the list provided. You can use the current gallery for the slide show by pressing the button [use gallery].

Press the Proceed button to start the slide show with the first image (or the current image, if a member of the slide show album). Use the escape key or F11 to exit the slide show and return to the dialog.

Dialog Controls:
Seconds: The standard time each image is shown.
This time can be extended for selected images in the Images Preferences dialog (below).
Clip Limit: Images are scaled to fit the window. If the image and window aspect ratios are different, this will create black margins above and below or left and right of the image. You can optionally make the images expand to fill these margins and cut off the opposite sides of the image (e.g. if the margins are on the left and right, the image is expanded to fill these margins and cut off the top and bottom equally). You control how much of this is allowed with the Clip Limit, which is the percent difference in aspect ratios above which expansion and clipping will not be done. Zero means no clipping is done, 10% means that images with 10% or less difference in aspect ratio will be expanded and clipped.
Music File: An optional music file or playlist that will start when the slide show is started.
Full Screen: If checked, images are shown full-screen without menu, title bar, etc.
Auto-replay: if checked, the slide show will start over after reaching the end.

Keyboard keys can be used to control the slide show. The button [KB controls] shows the current assignments and allows you to change them (top right). The following actions can be assigned:
  •  Pause or resume the slide show (toggle).
  •  Blank the current image and pause the slide show, or restore the image (toggle).
  •  Resume a paused slide show with the next image, including the transition animation.
  •  Magnify the image - show selected image areas at higher magnification (see the Magnify function).
The following KB keys are assigned to actions and cannot be changed:
  •  Escape: Interrupt the slide show. Switch to gallery view to choose an image to resume, if desired.
  •  Arrow keys: You can go back and forward at any time and the slide show will continue from there.

Press [transitions] to start a dialog (bottom right) to select and customize the transitions between images. These include instant replacement, fade-out/fade-in, and many animated methods of image replacement (e.g. the new image expands from the center to replace the old image). Select the transitions to be used and whether they are used randomly or in sequence. The slowdown parameters can be used to slow transitions that may operate too fast on some computers (some may be too slow on slow computers, but this cannot be helped). The preference parameters specify a relative preference which will influence how frequently the transition type is used when a random sequence is selected.

The buttons [load] and [save] allow you to save transition settings in a file and load them later. These files are independent of the slide show files. A slide show will default to the transition settings last used for that slide show, but you can always override these by using the [load] button to load some other settings, or changing them in the dialog.

Press [image files] to start a dialog (bottom left) for image preferences. These are optional. An image is selected for customizing by clicking its thumbnail. The dialog is filled-in with default settings or the previous settings for this image. Enter revisions and press [done], or click on the next image to be customized. If Play Tone is selected, a tone is played when the image appears during the slide show. The Show Caption / Show Comments times determine how long the image caption and comments will be shown above the image. After this time they disappear. Captions and comments are edited with Edit Metadata. The Wait times determine how long the image will wait before and after the optional zoom, until moving on to the next image. These are always used, even if there is no zoom. Zoom type selects zoom-in (image approaches) or zoom-out (image recedes). Zoom size controls how much the image will be zoomed and Steps determines how many steps are made during the zoom. 1.0 means no zoom, and 3.0 is the maximum zoom (image is 3x larger). Use at least 300 steps for a smooth zoom, and more than this if you want it to zoom more slowly. To set the center of the zoom, press the [Zoom Center] button first, then click on the thumbnail image at the desired zoom center. Transition is used to select a transition type to be used from this image to the next image. If transition type "next" is selected, then the normal transition sequence is used, as specified in the Transitions dialog.
 
Sequence of slide show events for each image:
  •  show the image at normal size if no zoom or zoom-in is selected
  •  show the image at zoom size and position if zoom-out is selected
  •  play the tone if specified
  •  show the caption and comments for the specified times (may be zero)
  •  wait for the first interval (may be zero)
  •  perform the zoom-in or zoom-out if specified
  •  wait for the 2nd interval (may be zero)
  •  wait for the standard interval specified in the Slide Show dialog
  •  do the transition to the next image
 
 

Gallery View Buttons 
Zoom-in / zoom-out. Increase or decrease the gallery thumbnail size.
The smallest step changes the gallery format to show each thumbnail with a list of metadata items.
Sort the gallery thumbnails in an alternative sequence. You can sort by file name (default), Photo date/time (from EXIF data), or file modification date/time (file creation or last modification date/time). The sequence may be ascending or descending.

Jump to the beginning or end of the gallery.
Scroll back (up) or forward (down) by one gallery page.
 
Sort Dialog


Choose the sort key and ascending or descending order.

Directory galleries restore their prior sort order and scroll position when opened later.
The Reset checkbox resets all galleries to file name ascending and top position.

An Album gallery cannot be sorted.
Albums retain image sequence they were created with.

 
Scrolling the Gallery
The gallery can be scrolled up and down using the arrow buttons shown above. The keyboard up and down arrow keys, page-up and page-down, and [home] and [end] keys may also be used. Use the right-side scroll bar to move rapidly to any position. Left-click and hold on the scroll bar, over or under the scroll button, and the gallery will scroll rapidly to the click position. Right click and hold on the scroll bar to make the gallery smooth-scroll to the click position. Continue holding the mouse button and move to the side to increase the scroll speed.
 
The following menus are repeated here in the Gallery Menu for convenience:
Metadata
Combine
Process
Tools
Help
 
 

 

Map View Menus
There are two map view methods: World Map view and Net Map view. The differences are explained here.
 


World Map Menus

The World Maps functions use locally stored map files which can be downloaded as explained below. You can also install custom maps and use them like any other map.
  

Images by Map Location

If the separate package fotoxx-maps is installed, a set of geographic maps is available for Fotoxx. These are a world map and maps of each continent. Click the left side menu bar World Maps button to change to world map view mode. A world map is shown at first. Click on any area to get a much larger view of that area. The map image can then be panned and scrolled to any other area by dragging the mouse. Map markers (red dots) show locations having corresponding geotagged images. Click on a marker to get a gallery of images from that location. The window changes to the gallery view and the gallery appears. Press the world maps button and click a new location for a new gallery report.

Map Navigation
: zoom and pan/scroll for a map is slightly different from image files:
  •  mouse wheel forward: zoom the map to full size, centered on the mouse position
  •  mouse wheel backward: shrink the map to fit within the window
  •  left click on a marker: show gallery of images geotagged for that marked location
  •  left click NOT on a marker: zoom the map to full size, centered on the clicked position
  •  right click: shrink the map to fit within the window
  •  keyboard Z key: alternate between full size and shrink to fit
  •  mouse drag: the zoomed map image pans and scrolls like other images

When in world map mode, menus are available for choosing a new map and for searching images.
 

Choose Map

Choose Map allows you to choose any available map. All maps will show markers (red dots) where there are geotagged images. Click on a marker to get a gallery view of all the images at this location. The range of images selected corresponds to the marker size: images located within the marker area on the map are selected.
 

Installing Custom Map Files
 
Obtain the map from any suitable source, e.g. Google Maps or Open Street Map. You need to get the map as a JPEG or PNG file. You can use a screen capture function to create the map file. You can use the Fotoxx Mashup tool to stitch many maps together to make a huge map (e.g. 100 megapixels). These work well with Fotoxx pan and zoom. You can also use a scanner to capture one or more paper maps and accurately stitch them together with Mashup.

To install a map, put the map file into the directory /home/<user>/.fotoxx/user_maps. In this directory you will also need a text file: maps_index. Each map requires a line in this file which specifies the map file name and the latitude-longitude range of the map.
Here is an example maps_index file:
    Hamburg.jpg,  53.455,  53.553,   9.906,  10.067
    Houston.jpg,  29.479,  30.053, -95.786, -94.905

The entries are map file name, low and high latitude, low and high longitude. All values are degrees, separated by commas. Spacing does not matter. Fotoxx assumes a Mercator projection, but this is not significant for maps covering less than 100 km (e.g. city maps).

If you make a custom map, getting accurate latitude and longitude values for the map edges can be tricky. Use the Open Street Map "export" option to view maps with an overlay rectangle labeled with latitude and longitude. Adjust the rectangle to match a corner of a map you have created and record the latitude and longitude values. You need accurate latitude and longitude data for the upper left and lower right corners of the completed map. The precision should be suitable for the scale of the map. 1 degree of latitude corresponds to about 110 km on the earth's surface. 0.001 degree corresponds to 110 meters.


Set Map Markers

Here you may choose to show markers for all images, or only images in the current gallery. You can use the latter capability to show markers for a chosen subset of images, e.g. images from particular file locations, from a particular vacation trip, having particular tags, etc. Use the Search and Album functions to select and screen images to produce the desired set in a gallery. The gallery can also be saved as an album for repeated use.
 
 
  

Net Map Menus

The Net Map functions use an internet service to provide maps from any location at any scale. The Net Map offers superior functionality, but depend on having a fast and reliable internet connection. Markers (red dots) will show the locations for any of your images with geotags. Use the mouse wheel or double-clicks to zoom-in on any location. Drag the map to change the center. Click on a marker to get a gallery view of the images at this location. The range of images selected corresponds to the marker size: images located within the marker area on the map are selected. The marker area varies with the map scale. Nearby markers on a large map will consolidate when the map is scaled back (zoomed out), and clicking the consolidated marker will get all the images of the contained markers.
 

Net Map Source

At this time, two internet map sources are supported: Mapnik and Mapbox. The default is Mapnik. It is entirely open and free and works quite well if your internet connection is good. The one drawback is that location names are in the language script of the location (e.g. Arabic, Chinese ...), so for Westerners these maps are only useful for Western countries using Latin script. Mapbox uses Latin script for all major location names. Mapbox, however, requires an access key, which you must request. These are free for modest usage: up to 50,000 "map tiles" per month, which is adequate for most purposes. To use Mapbox, go to the Mapbox web site, developers page, and follow the instructions. In Fotoxx, use the Net Map Source menu function and select mapbox. You must input your access key the first time only.
 

Net Map Locations


You can save a map location (map center and zoom level) with a given name, and recall the map location later with one click. To save a map location, navigate the map to a location and zoom-in to include what you want within the window. Enter the map location name and press the [Add] button. The new location is added to the list of available locations in the window. To recall a saved map location, simply click on the location name in the list. To delete a map location, click on the name and press the [Delete] button. A few map locations are included by default. You can keep or delete them.

 

The following World Map functions are also available for Net Maps:
   Images by Map Location
   Set Map Markers
 
 
 

Other Topics
  

Video Files
 
Video files (.mpeg, .mp4, etc.) may be mixed-in with your image files. They will show as image files in both file and gallery view. The image shown is the first frame of the video. In file view, the keyboard is used to control the video. The 'P' key will start the video, pause the video, resume the video, or replay a video that has ended. The 'Q' key will quit the video and close the window. These keys cannot be customized (required by player program).
 


Web Service Photo Upload
 
There used to be a dedicated function to upload image files to Flickr, but this stopped working and was removed. It is actually not needed, since Flickr offers a capable upload service directly from the Flickr web site.
 
Most photo websites have the ability to upload multiple image files from a single directory, using only a web browser. Use one of the Fotoxx functions listed below to select and copy image files to the desktop or any other directory. From there, use the photo website's native browser interface to upload the image files.
 

Export Image Files - select image files from multiple locations and copy to a single output directory
Batch Convert - same functionality plus many more options
 
The first function is simple to use and can only downsize and copy image files.
The second function can also downsize and copy, but it has many more features:
  •  rename the files (combine old and new names, sequence numbers, photo dates)
  •  convert to another file type
  •  upright images turned 90 degrees
  •  remove metadata
  •  sharpen
  •  add an overlay image (e.g. copyright text) at designated position
 
 
Organizing Images for Efficient Searching 
 
Relevant links: Edit Metadata, Batch Add/Remove Tags, Batch Rename Tags, Batch GeotagsBatch Convert, Search Images, Image Locations, Images by Map Location, Index Image Files, Manage Albums, Slide Show.
 
The goal is to find all images for given criteria, e.g. photos of a given person at a given place and/or time range, all photos of a given person, photos from a specified location or event, etc. There are several ways to organize an image collection to accomplish this, with advantages and disadvantages you need to understand. The methods listed here (except for albums) are standards compliant and will work with other programs that support the same methods. These are (1) searching based on metadata (dates, ratings, tags (keywords), geotags (locations), captions and comments, and (2) searching based on file names or partial names, including directory (path) names.
 
Fotoxx can search using the following image metadata: photo date, rating (stars), tags (keywords), geotags (location names and earth coordinates), and text appearing in captions or comments. Searching based on file and directory names can also be combined with metadata searching. Any other metadata can also be searched, although not nearly as fast as the items listed, which are duplicated in a special index file for fast searching. A strong computer can search images for the listed metadata items and file names at a speed exceeding 50,000 images per second.

All of the search methods described below can be used in combination. The output of a search function can be the input of another search function (to further narrow the search), and search outputs can be added to prior searches.
 
The following is an overview of the different ways images can be made searchable.
 
Directory and File Names
These can be used as a basic organization that will enable you to find images even if more effective organizations (tags, captions) are not used. The highest physical organization should be by time, because this will naturally group photos together that are related. I suggest using one subdirectory per year named 2001, 2002, etc. This will also prevent any one subdirectory from getting too big. Optionally, image files may be further organized in time sequence by using MM.DD as the start of the file name. The rest of the name can be a topic or event, and a sequence number.
    Example:  /images/2011/08.20 Spitzbergen 23.jpg
This very basic organization allows Fotoxx to find files by searching for years and topics (file names). In the above example, a search for "spitzbergen" or even "spitz" will produce all the images of Spitzbergen. Years can be also be searched and combined with topic searches, e.g. "2012 Paris". The Batch Convert function lets you rename a batch of photos taken at one location or event by specifying a template name like this:
   "$mm.$dd Spitzbergen $ss"
Month and day (from the EXIF photo date) replace $mm and $dd. "Spitzbergen" replaces the camera file name (P00123456), and a sequence number replaces $ss.
 
Captions and Comments
A simple method of organization is to use captions and comments. These are arbitrary text strings that can be added to a series of images in rapid sequence: Start Edit Metadata, open an image, input some text, press [apply], press [next], input some text ...  Captions and comments are two separate inputs but treated logically the same. They are searchable: words appearing in captions and comments can be searched. You can specify persons, location, topic, etc. for each image and then find them again quickly.
 
Tags
(EXIF keywords)
The most powerful tool is tags, but this is also the most demanding of organizational care. You can go through your images sequentially and add tags by clicking on a list of defined tags. New tags can be defined as needed. Images can have many tags, and can be searched using AND/OR combinations of tags, also in combination with other criteria. Tagging is generally fast, needing a few seconds per image. Fotoxx has two methods of adding tags, a "managed" system and a "random" system. In the managed system, you define tag category names and the tags within each category. When adding tags to images, you can point and click from a list of tags organized by category and alphabetically within category. In the random system, you simply create tags as needed while you tag your images, following no particular system and without categories. When you enter the first few characers of a tag, existing tags that match these characters are shown in a pick-list which you can click to complete adding the tag. If there is no match, a new tag is created. Recently used tags are also shown in a list that can be clicked. Photos made at the same time will normally be tagged in sequence, and will also share many of the same tags. The recent tags list helps to speed the tagging process. Use Batch Add/Remove Tags to add the same tags to many images. Batch Rename Tags can be used to rename tags in selected (or all) images. Tags can be searched in any combination, combined with other search criteria such as a date range or location(s).

Note that images downloaded from the Internet may have many tags adhering to no system. You will need to clean these out or redo them to stop them from cluttering your list of defined tags. If you see undesired tags in your list of defined tags, it is easy to find the offending image files and purge or change their tags: use the Image Search function to find the images, using the unwanted tags as search criteria, and feed this list to Batch Add/Remove Tags or Batch Rename Tags. Tags from downloaded files will have no category, which keeps them separate from tags you define (if you are using the managed tags method). Still, you should avoid accumulating thousands of random tags, and it is easy enough to get rid of them whenever they appear.
 
Geotags
Use geotags to assign a city or location and country to your images, and optionally latitude/longitude. This enables all images for a location to be quickly found. If you use a camera with a GPS receiver, geotags are added to the image EXIF data, and location searching is available automatically. Since image dates are also automatic (in EXIF), images can be searched by date range and location without you having to enter any data for each image. You can leave it at this, or add some of the above extras if you accept the extra effort required. My experience so far with in-camera GPS is that the location names are chaotic and you may want to sanitize them (mixed upper/lower case, with/without states or other political subdivisions, mixed languages, etc.). You can fix the mess with a little effort: search for the location name you want to change (e.g. MUENCHEN, BAYERN, DEUTSCHLAND), then process the resulting images with the Batch Geotags function to change the location name (e.g. Munich, Germany). Location names can be searched in any combination.

When you add geotags to an image manually, it is usually sufficient to enter just the city or location name and then press [Find]. If the location has been entered sometime in the past, it will be recalled and all geotag data will be filled-in automatically (city/location name, country, latitude, longitude). This will also work if only a few characters of the name is entered, e.g. "hono" will recall the data for Honolulu, if available. When a location is entered for the first time, enter the city or location name and the country, and press the [Web] button. A web service will usually find the latitude and longitude automatically. If not, you can use Wikipedia or other web services to find the location coordinates and enter them manually.

Images with geotags are also searchable by clicking markers on a world map. The markers are automatically added to the map for all images containing geotag coordinates. The map can be zoomed to any scale from street-level to planet level.
 
Albums
Another method of organization is to use Albums. Choose a name for each album and assign any desired images to the album by clicking thumbnails in gallery pages. The images are not duplicated: the album is simply an ordered list of file names. Albums can be selected by name and viewed as a gallery of thumbnails. These can be rearranged via thumbnail drag and drop. The images can then be viewed sequentially using keyboard arrow keys, randomly by clicking thumbnails, or as a slide show with animated transitions between images. Albums are also implemented in some other photo management apps, but each one is different and incompatible.

The image search function can be used to find images to start a new album, and then images can be added, removed, and rearranged as needed. Images can be added simply by clicking gallery thumbnails as you browse your image collection.

Summary
The following is a summary of some ways to organize a large image collection, with factors to consider when choosing which methods you want to use. In the list below, "search by" specifies which search criteria can be used with each option. Many of the methods below can be combined, and the possible search criteria increases accordingly. Searching by photo date (EXIF) is available with any organization.

Images Organized by Topic
  • images are in a topic-named directory (a very common practice)
  • e.g.  /.../Susan 8th birthday/P00123456.jpg         (file names from camera)
  • e.g.  /.../Paris 2014/Notre Dame 12.jpg               (meaningful file names added)
  • fast implementation: no work if this is your starting point
  • gallery view is by topic only (poor overview of a large image collection)
  • search by: topic (using directory and file names)
  • no overview of available search topics
Images Organized by Year and Topic
  • e.g.  /.../2014/Italy/Rome-12.jpg
  • e.g.  /.../2014/Susan Wedding/P123456.jpg
  • fast implementation: move your topic directories into year directories
  • gallery overview by year and by topic within year
  • search by: topic (using directory and file names)
  • no overview of available search topics
Images Organized by Year, Month, Day and Topic
  • e.g.  /.../2014/08.22 Rome-12.jpg
  • use Batch Convert to format new names from existing groups of image files
  • natural grouping of related images
  • gallery overview by year, in time order, with topics visible
  • search by: topic (using file names)
  • no overview of available search topics
Image Directory and File Names Contain Topics
  • e.g.  /.../travel/2014/Italy/Rome-Susan-Coliseum.jpg
  • many folks have done this, to search for images using a file search utility
  • you may end up with thousands of galleries with a few images each
  • search by: topic (using directory and (partial) file names)
  • no overview of available search topics
Captions and Comments
  • e.g.  "Susan 2014, Coliseum in background"
  • simple and easy to use
  • moderately fast implementation: 10+ seconds per image
  • risk inconsistent names, unreliable search
    (e.g. search "Rome" would miss the example above)
  • search by: captions and comments (any contained word or words)
  • no overview of available search topics
Managed Tags
  • e.g.  "Rome, Italy, Susan, birthday"
  • point and click in a tag list to add tags to images
  • tags have categories for faster visual location of tags to click
  • typing a few characters is usually enough to retrieve a defined tag
  • recent tags are also available for fast reference and re-use while tagging images
  • no inconsistent or redundant tags, no typos to make searches unreliable
  • requires careful planning of tags - later revisions are cumbersome
  • batch add/delete/rename is available for tag revisions
  • search by: tags in any combination
  • good overview of available search topics (tags organized by category)
  • tag categories are not standard - they will not be visible in other applications
Random Tags
  • no tag organizing system, tags created as needed while tagging images
  • legacy tags can be kept unchanged
  • typing a few characters is usually enough to retrieve a tag that has been used before
  • recent tags are also available for fast reference and re-use while tagging images
  • risk inconsistent tags (scenery, landscape), redundant tags (Susan, Sue), and typos
  • search by: tags in any combination
  • overview of available search topics: one large pile of tags in alphabetic order
Geotags
  • e.g.  Rome, Italy, 41.89 N, 12.48 E
  • cameras with GPS store this data automatically in images
  • you can manually add locations (5-10 seconds per image)
  • you can batch add geotags to many images at once
  • inconsistent location names from cameras (fixable using search and batch update)
  • map markers are generated automatically from image geotag data
  • search by: location (city, park, monument ...) and country
  • good overview of available search topics: clickable table of locations [+ date groups]
  • you can also click on a map marker to show a gallery of images at location
Albums
  • album names like this: favorites, best scenery, Italy 2014, Susan childhood
  • albums are a list of contained image files, which are not duplicated
  • make albums using any criteria, containing any images
  • images can be in multiple albums or multiple times within an album
  • images can be arranged in an album by dragging thumbnails to position
  • add images by selecting / dragging gallery thumbnails to album gallery
  • albums are the basis for the slide show function
  • overview of available albums - click on album name to view gallery
  • albums are only usable within Fotoxx (there is no standard for albums)
 
 
Translations 
Translation files for Fotoxx are found here:  /usr/share/fotoxx/locales/translate-xx.po.gz
The file may or may not be compressed (.gz ending). xx is a standard 2-character language code (e.g. de for German) or a combination language and region code (e.g. de_AT for Austrian German). This code normally corresponds to the locale of the computer, as shown by the command echo $LANG. If there is no installed translation file for xx, you can use the dummy translation file for en (English) as a template. Replace the translations of english to english with english to new-language, and save the new file with the right xx code. The translation files are protected and cannot be edited unless you have root privileges. It is better to copy a translation file to a more convenient location for editing, and save the edited file in the standard location when ready to test. Translation files are available for use immediately - no conversion to a binary format is needed since the text files are processed directly by Fotoxx. However, it is advisable to go through the standard GNU translation process to find possible errors. This is explained below.

Translation files are commonly called ".po files" and have the file type ".po" (or ".po.gz" if compressed).
A typical translation in a .po file looks like this:
  msgid "The file name is: \n %s"
  msgstr "Der Dateiname ist: \n %s"

"msgid" is an English text for translation. "msgstr" is the translation (German in this case). The special codes "%s" and "\n" are for inserted text and formatting. A file name will be inserted at "%s", and "\n" starts a new line. In the example above, the displayed file name will start on a new line. In the translation (msgstr line), these codes must match the English codes (msgid line) in both type and sequence.

A missing translation looks like this:
  msgid "The file name is: %s \n"
  msgstr ""

Revising an existing translation
  1. Edit  translate-xx.po  to add or update translations for language "xx".
  2. Replace the original file in /usr/share/fotoxx/locales/
  3. Option: compile the .po file to check for errors: 
     $ msgfmt -v --check-format -o /dev/null translate-xx.po
  4. Run Fotoxx and check how the translations look. If the computer locale is not "xx", start Fotoxx in language "xx" with the command:  $ fotoxx -l xx
  5. Send the modified .po file to me  [contact]  so it can be included in Fotoxx release packages.
Step 3 is optional. The usual binary translation files (.mo) that are output by msgfmt are not needed by Fotoxx. The translation source files (.po) are read directly by Fotoxx, and changes made to a .po file are effective the next time Fotoxx is started. Step 3 is useful to find errors (e.g. missing or non-matching format codes, quote marks, etc.).

Whenever Fotoxx is started from a terminal, missing translations are listed in the terminal window. There is also a function for this: Tools > Missing Translations lists missing translations in a popup window.
 
Problems with long translations
English can be terse compared to other languages (e.g. "undo" is "Rückgängig machen" in German), and this can cause a confusing appearance in the GUI layouts. Therefore try to make dialog labels and buttons short, and look closely at the resulting GUI layout.
 
User Guide translation
The English user guide is normally found here:  /usr/share/fotoxx/data/
The file is a text HTML file, which may be edited with any HTML editor. This is a large document, so expect a week or more of work to translate it. In order to reduce the work, you can use a simple text editor and supply text without images - in this case I will convert to HTML and supply the images. If you make a new or revised translation, please send it to me  [contact]  so it can be included in future releases.
 
 
Recent Changes to User Guide
This section is provided to help you quickly review the changes in the user guide.
Fotoxx version numbers correspond to year and month of release.

v.18.01 (this release)
  • Menus renamed: Zonal Flatten > Flatten, Tone Mapping > Gradients, Flip > Mirror.
  • Replace Album File: A better way to update albums after an image file is updated or added.
  • Video Files: The keyboard keys to control playing a video file were revised.
  • Redirect Fotoxx home: How to move Fotoxx home away from /home/<user>/.fotoxx.
  • Retinex: New tool to fix extreme fog/haze and enhance detail in areas with weak contrast.
  • Batch Photo Date/Time: New tool to fix missing or wrong date/time in photos.
  • Image Locations and Image Timeline reports can be navigated with the KB navigation keys.
    A desired location can be found by entering leading characters from the keyboard.
  • Gallery Selection dialog: selected image files can be reviewed using the KB navigation keys.
    Jump to a desired file or directory name by entering leading characters from the keyboard.
  • Keyboard Shortcuts: the revised GUI reflects additional functionality and flexibility.
  • Gallery Sort: new capabilities are described.
  • Slide Show: customize the keyboard controls.
  • Custom Widgets: some GTK widgets have been downsized. This is reversible if desired.
  • User Settings: the background color for the left-side menu panel is now adjustable.
  • Batch RAW: two functions were consolidated into one, with added options.
  • Search Images: you can select and sort by photo date or file mod date.
  • View 360° Panorama: new function to view 360° panorama images with a rotating viewpoint.
  • Scroll Gallery: a  description of gallery scrolling methods was added.
v.17.08
v.17.04
  • Paint/Clone was separated into two functions, Paint Image and Clone Image.
  • Paint Image: the dialog controls were revised for using with a Wacom tablet,
    and a capability for a custom color palette was added.
  • Clone Image: minor revisions in the user interface.
  • Delete/Trash Image: minor revisions in the user interface.
  • Trim/Rotate Image: minor revisions in the user interface.
  • Rename: minor revisions in the user interface.
  • Copy/Move: minor revisions in the user interface.
  • Montage: combine many selected images into a compact format.
  • Manage Albums: albums can now be sorted, with implications for editing albums.
  • Alien Colors: repaint an image or area with strange random colors.
  • Search Images: edited images can now be a search criteria.
  • Stack/Paint: capability added to show or hide transient objects automatically.
  • Uninstall AppImage: new - completely uninstall an AppImage package.
  • Twist Image: new Effects function - twist an image around a chosen center.
  • Script Files: most of the Effects functions can now be scripted.
  • Sort Gallery: added explanations about sorting albums and metadata reports.
  • Denoise: the measure noise function gives a better characterization of the noise.
  • Edit Functions Index was added
v.17.01
  • Batch Rename Tags: Text edit file for old and new tag names was replaced with a proper GUI.
  • New: Set Map Markers: map markers can come from current gallery or from all image files.
  • Brightness Ramp (was 'Ramp'): both functionality and user interface were improved.
  • New: All Directories: List all image directories, click any for gallery view.
  • Replace Album File: New option to add a new file after designated old file, or replace old file.
  • New: Blend Image: Blend image pixels in areas painted by the mouse.
  • Color Mode: Functionality and user interface have been revised.
  • Adjust HSL: target color selection via mouse click on image.
  • Slide Show: new button to use current gallery (no album selection).
 
 
  
Technical Notes

Additional technical topics can be found on the Fotoxx web site.

Fotoxx Limitations
image files
Fotoxx has been tested with 500K image files and the performace was good. See Benchmarks below.
image size The max. supported image width or height is 20,000 pixels (compile time constant).
The maximum image size is 4 GB (about 240 megapixels).
Images edited in memory have 4 float numbers (16 bytes) per pixel (RGB + alpha).
image edits 99 edits for undo/redo and file version numbering (filename.v01 to filename.v99).
image tags
10,000 tags, 200,000 chars. for all tags, 1000 chars. for one image (compile time constants).
thumbnail
cache
Thumbnail images are cached in main memory to improve gallery performance. Gallery scrolling is slower for the initial pass, and faster thereafter. Revisited galleries are fast unless the cached thumbnails have been replaced.
 
Running out of memory
Fotoxx can require a huge amount of main memory to edit a large image. A 20 megapixel image requires 640 megabytes for the simplest edits, and more for complex edits. Images in memory are not compressed and each RGB pixel is represented by three floating-point numbers and a transparency value (16 bytes total). HDR, Panorama and other composite functions hold all images in memory during alignment and post-process tuning. If you push the memory limits on a small PC, the Linux kernel may kill the Fotoxx process without warning and with no message (this is to keep the operating system itself from failing). What you see is that the Fotoxx window simply vanishes.

Running out of disk space
During a series of image edits, each edit step is saved on disk, and the before/after results can be viewed with the Undo/Redo button. These images use floating point numbers for color values and are not compressed. A 20 megapixel image makes a 320 MB file. If there are 10 edit steps in the undo/redo stack, the required disk space is 3.2 GB. If disk space runs out during an edit session, the program terminates with a message. To avoid this, be sure there is plenty of disk space wherever your /tmp/ directory resides. When you open a new image or quit Fotoxx, the disk space is recovered. If Fotoxx crashes, the space is recovered the next time Fotoxx is started or when the computer is rebooted.
 
Packages required for Fotoxx source build
See the README file for instructions on compiling Fotoxx from source.
 g++ GNU C++ compiler and C libraries
 libgtk3.0-dev  Gnome GTK3/GDK3/Pixbuf/etc. function libraries
 libtiff5-dev tiff library development files
 libpng12-dev png library development files
 liblcms2-dev Little CMS development files
 libchamplain-gtk
Gnome geographic mapping functions
Note: package naming and contents vary by Linux distro (the usual chaos). The above names are valid for Debian-based distros (including Ubuntu). For other distros the names are different. Good luck.
 
Additional programs required or optional for Fotoxx
Fotoxx uses the following programs which are installed separately:
 xdg-utils  req.
open text or html files with user's preferred application
 exiftool  req.
(v. 8.60 or later) read and write image metadata (tags, comments, etc.) 
 Raw Therapee
 opt.
open a RAW file for editing using its native GUI
 growisofs  opt. burn a CD or DVD with selected images
 xgamma  opt.
used for the Monitor Gamma function (adjust monitor gamma)
 fotoxx-maps
 opt.
set of geographic maps to show image locations and report images by mouse click
 hugin
 opt. PT Panorama function uses Panorama Tools (normally packaged with Hugin)
 ffmpeg, totem
 opt.
required for inline viewing of video files
 
zappcrash - backtrace dump after a fatal error
If Fotoxx has a fatal error, it attempts to intercept the error and produce a backtrace dump which appears in a popup window. Please send this information to me so I can fix the error (contact me). A description of what you did immediately before the crash would also be helpful.
 
Preview Mode
Some edit functions use a reduced image size for a faster interactive response time. This is shown on the top panel as "(reduced)". When [done] is pressed, the full-size image is then processed. This is why [done] sometimes takes noticeable time. A monitor-size image (2-4 megapixels) is much faster to process than a 15-40 megapixel image (camera file). This method is used whenever the preview edits can be applied to the full-size image without visible impact (e.g. Trim/Rotate, Warp functions, brightness and color related functions). It cannot be used for some functions (e.g. sharpen, Gradients) because the results for a small image cannot be converted for a larger image.
 
File Size
The file size shown on the top panel while an image is being edited is the original (unedited) file size. The file size for an edited image is not known until the image is saved on disk. In memory the size is (pixels x 16). A 20 megapixel image uses 320 megabytes in memory and typically < 4 megabytes on disk (high quality JPEG). When the edited image is saved, the correct file size is updated on the top panel.
 
Custom Icons
Fotoxx icons are contained in /usr/share/fotoxx/icons. You can change these if wanted.
 
File Types Supported
Fotoxx uses libraries to support reading and writing of image files: the GDK pixbuf library, libpng and libtiff. The file types that can be read and written include JPG/JPEG, PNG, and TIF/TIFF. Three RGB colors with 8 bits per color are supported for all types. PNG and TIFF also support 16 bits per color. Fewer than 8 bits per color and grayscale images are partly supported, and are converted to RGB internally.

Index Files
These can be found in the directory /home/<user>/.fotoxx/image_index/
The file "indexB" contains image file pathnames and metadata items that are indexed for fast searching (dates, ratings, tags, caption, comments, geotags). The file "top_directories" contains a list of the top image directories. These are searched for new image files whenever Fotoxx starts up. The last entry is the thumbnails directory.

Color Depth
8-bit color (256 brightness levels), as supported by JPEG files, is the norm for image files and is generally adequate. A difference of one brightness step (0.4% of the entire range) cannot be seen. A greater color depth than 8 bits can be useful if some part of the brightness range within an image has been greatly expanded using retouch or repair functions. This expansion can lead to visible "banding" or "posterization". If the RAW image is edited instead of the JPEG (and if the RAW image really has more than 8 bits of low-noise color), this problem can be reduced, even if the edited image is converted back to JPEG for final storage. Only the most expensive cameras with large sensors produce RAW files with more than 8 bits of low-noise color at normal light levels (status 2016).

The image below changes gradually from black to white. The color depth is 6 bits (64 brightness levels).
Note that the brightness steps are barely visible.

 
Denoise Function - Noise Measurement
An area of radius 10 pixels around the clicked position is sampled. The RGB brightness levels reported (0-255) are the averages for this area. Within this area, every pixel is measured and compared with the mean of the surrounding 5x5 block of pixels. The RGB noise levels reported are the average differences. If the sampled area has a small gradient in brightness, the measurement is not sensitive to this. This is because the central pixel in a 5x5 block will naturally have the average RGB values of the surrounding pixels (if noise is zero). If a RAW image is measured, a 16-bit TIFF image made from the RAW is used, preserving up to 16 bits of color data if this much is available from the RAW image.
You may be surprised at the typically high noise levels in RAW images. Cameras filter out this noise when making a JPEG image. Expensive cameras with large sensors may still have RAW noise levels > 1 on the scale 0-255 (i.e. noise in bits 7-8), and even for normal ISO values around 100.
 

Alignment Algorithm (HDR, HDF, Stack, Panorama)
Relatively few high-contrast or "edge" pixels are selected to control alignment. The actual pixels used are shown in red during the alignment process, which is also more entertaining. Each image in succession is systematically warped various small amounts and the fit with other images is tested. This is done because two photos made with slightly different horizons or rotations will not fit perfectly with simple translation and rotation. Also camera lenses are not geometrically perfect.

Alpha Channels
Images having alpha channels (transparency information) can be edited, and the alpha channel is preserved if the image is saved as a PNG or TIFF file. JPEG files do not support alpha channels.

Image Deterioration From Repeated Editing
If you save an edited image file and then use this file later to perform additional edits, pixel resolution may be lost. It is better if you do all edits when the image files are first processed, to minimize image deterioration (or go back to the originals if you still have them). Any function that changes image size or shape will reduce resolution about 1/2 pixel. These are the resize, rotate, warp, and all the composite functions. Rotating 90 degrees does not affect resolution. When downsizing an image, using the ratios 1/2, 1/3, 1/4 will give the least loss of resolution. Functions that change brightness and color do not affect resolution.
 
JPEG Compression
If a JPEG file from a digital camera is saved with Fotoxx, you will likely notice a large reduction in file size, even if a high JPEG quality level is used. This is because Fotoxx can afford to invest more processing power in the compression. A camera CPU is not very fast and must save an image quickly to be ready for the next photo, so the processing time available for compression is limited. The CPU of a PC is typically much faster and has more time, so the compression level is higher. The smaller file size does not mean that the quality is less.

Image Deterioration From Repeated Saving of JPEG files
Reading a compressed JPEG image and saving it again can lead to loss of detail and increased image artifacts. The effect seems to be negligible if JPEG "quality" is set to a high value when the image is saved. The image below was saved 10 times using quality=90 (Fotoxx default), each time opening and saving the previous image. Differences can be found if you look hard enough. The images are 2x size and the insets are 5x.

EXIF Errors
Cameras (esp. older ones) do not always produce structurally correct EXIF data, and the program exiftool (used by Fotoxx to manipulate EXIF data) may produce error messages. I have been able to fix these cases by saving the image file on top of itself, which will replace the EXIF data with whatever exiftool was able to read correctly. If desired data gets lost, you can restore it using the Edit Any Metadata function.

Newline characters in user Comments and Captions
When editing metadata Comments or Captions, if you need to align text in columns, you can use the [enter] key to force new lines. These are converted into the string "\n" before being stored in image EXIF/IPTC data, since newline characters are not allowed (exiftool converts them into periods). If the text is viewed or edited again, the "\n" strings are converted back to new lines, so that the original text alignments are restored. This is not standard, so don't expect the text to remain aligned if viewed in Photoshop, etc. If this is a requirement, then do not use the enter key to make new lines when entering long text - just let the text overflow to the next line by itself. In this case, column alignment is not possible.

Installed Files
Foloxx installs files in the following locations.
Repackaging by Linux distros could put them somewhere else, e.g. /opt... instead of /usr...
 /usr/bin/fotoxx the executable program file
 /usr/share/fotoxx/* user guide, translation.po files, icons, default data, etc.
 /usr/share/doc/fotoxx/* change log, man page, README and other documentation files.   

Installed Files - optional fotoxx-maps package
 /usr/share/fotoxx-maps/*
geographic maps data files (112 MB) 
 /usr/share/doc/fotoxx-maps/*
change log, man page, README   

Local Files
Files in Fotoxx home (default
/home/<user>/.fotoxx/)
These are preserved when a new release of Fotoxx is installed.
 add_line/
image lines and arrows saved from Add Lines/Arrows function
 add_text/
image text overlays saved from Add Text function
 albums/ image albums from Manage Albums function
 custom_kernel/ saved custom kernel data files
 edit_scripts/
saved edit script files (for batch editing)
 favorites/ saved data for user-configuration of favorites menu
 image_index/ top directories, thumbnail directory, image index file
 mashup/ saved mashup project files
 montage_maps/
saved image montage maps
 patterns/
saved background patterns
 printer_color/
saved printer color calibration files
 retouch_combo/ saved settings for the Retouch Combo function
 saved_areas/ "cutout" files saved from the Select Area > Save dialog
 saved_curves/ curve data saved from Retouch curve edit dialogs
 slideshows/ user preferences from Slide Show function
 slideshow_trans/
saved slide show transition parameters
 thumbnails/ thumbnail files (default location, user can change this)
 user_maps/
custom map files made by the user
 bookmarks bookmark names and image file locations
 colorwheel.jpg
color selection palette for Paint Image function
 gallery_memory
saved sort order and position for gallery windows
 KB-shortcuts2 user-defined or modified keyboard shortcuts
 logfile Fotoxx outputs that may be relevant for diagnosing problems
 metadata_report
Batch Report Metadata output report - tabular text file
 metadata_report_items
list of metadata items reported by Batch Report Metadata
 metadata_short_list
metadata key names for Batch Add/Change Metadata
 metadata_view_extra
extra items for the metadata short report, added by the user
 netmap_locations
saved locations in net map view
 pagesetup saves page setup data for print function
 parameters setup parameters that are saved across Fotoxx sessions
 plugins saves the plugins menu contents
 printsettings saves print settings data for print function
 README
text file - how to install Fotoxx from the source code tarball
 recent_files a list of the last 100 files opened by Fotoxx, saved when Fotoxx exits
 search_results list of the last image files found with Search Images
 slideshow-tone.oga
a short music tone for slide show
 stuck-pixels
data saved from the Fix Stuck Pixels function
 tags_defined a list of all categories and tags currently used in all images
 translate-xx.po
GUI translation file for language code xx
 widgets.css
styling changes to standard GTK widgets
 zappcrash
if Fotoxx crashes, a backtrace dump is deposited here
 zdialog_inputs saved dialog data for dialogs that recall prior inputs
 zdialog_positions saved dialog window positions (relative to main window)
 
Metadata used by Fotoxx
The following metadata items (stored inside the image files) are used by Fotoxx. These items (and any other metadata) can be viewed or edited using Fotoxx. Images can be searched using these items (or any other metadata) as selection criteria. Those marked "indexed" can be searched very fast, others more slowly (see benchmarks below).
 Metadata section and name  Usage  Indexed
 IPTC  Keywords  tags entered by user   yes
 IPTC  Rating  "star" rating entered by user   yes
 EXIF  ImageSize  pixel width and height, 1234x2345
  yes
 EXIF  DateTimeOriginal  date/time photo was made, or entered by user   yes
 EXIF  ImageHistory  history of edits made by Fotoxx
  no
 EXIF  UserComment  comment text entered by user   yes
 IPTC  Caption-Abstract  caption or abstract text entered by user   yes
 EXIF  FocalLengthIn35mmFormat  camera focal length used, 35mm equivalent   no
 EXIF  City, Country  city/location and country from camera GPS, or entered by user   yes
 EXIF  GPSLatitude, GPSLongitude  earth coordinates from camera GPS, or entered by user   yes
 EXIF  RollAngle
 camera tilt angle - can be used to auto-level an image
  no
 
Fotoxx Benchmarks
Conditions: Fotoxx 18.01, Intel Core i5, 2.8 GHz, 6 processor cores.

Image Index rate: large JPEG files (average 4.1 MB), 7200 rpm disk: 960/min.
Image Index rate: large JPEG files (average 4.1 MB), SSD disk: 6454/min.
 

Startup time by Image Index Level
(Tools > User Settings)
measured with 109K old image files and zero new image files
Disk Type
Index
Level (*)
initial startup
after reboot
subsequent
startups
7200 rpm
 2
 8.5
 2.5
7200 rpm
 1
 0.6
 0.6
7200 rpm
 0
 0.2
 0.2
SSD
 2
 2.9
 2.5
SSD
 1
 0.6
 0.6
SSD
 0
 0.2
 0.2
 
(*) Index level:
   0 = indexing disabled (search functions disabled)
   1 = use current index without updates for new image files
   2 = use current index + search and update for new image files
 
Search speed for indexed metadata:  >100K files/sec.
Search speed for non-indexed metadata, 7200 rpm disk:  3170/min.
Search speed for non-indexed metadata, SSD disk:  4980/min.
 

Dialog Window Positioning
For commonly used dialogs, Fotoxx saves the dialog window position (relative to the main window) and tries to restore the same position the next time the dialog is started. This works, mostly. Sometimes the window manager decides some other location.


Fotoxx Source Code
The C++ source code is available at my web site https://kornelix.net. The code is heavily commented in the hope that others can understand and use the code for their own projects. If you have a technical question about how something works, or a better idea to pass along, you may contact me.
 
Questions, Problems, Bugs
If you have a question or a problem, you may contact me. If there is a traceback dump (zappcrash window), please send this to me. Please explain how to produce the error, if you can.
 
Technical Reference Book
I used the book "Introduction to Image Processing and Analysis" by Russ and Russ, CRC Press. It is clear and concise.
The following functions were adapted from this book:
   flatten brightness distribution
   noise reduction (median smoothing, top hat)
   sharpen (unsharp mask, kuwahara method)
   embossing
   RGB/HSL conversion
 
Acknowledgements
These program libraries and utilities are used within Fotoxx: GTK, libtiff, libpng, liblcms, libraw, libchamplain, exiftool, and many other libraries and tools included in Gnu/Linux. Many individuals have contributed ideas for development, time for testing, and bug reports. Translation credits are in Help > About.
 
 

Index of All Fotoxx Functions
Add Lines to Image
add lines or arrows to an image, set position and style options
Add Text to Image add text, set position and size, set style options
Add Transparency convert a color or brightness level into transparency level
Adding Geotags
add geotags (location name, earth coordinates) to an image
Adjust HSL replace a color tone with another, based on HSL color model
Adjust RGB/CMY adjust color levels based on RGB color model
Alien Colors
repaint an image or area with random strange colors
All Directories
show all image directories at all levels, click any directory for gallery view
Anti-Alias remove pixelated edges from low-resolution images
Area Copy and Paste select image area, copy and paste across images, load and save to file
Area Enable/Disable toggle between area editing and whole image editing
Area Invert exchange inside area <--> outside area
Area Open and Save save area to a file, open and re-use (paste into an image)
Area Show and Hide show or hide area outline during image editing
Area Unselect permanently remove a selected area
Batch Change Metadata add or revise metadata items for selected images
Batch Add/Remove Tags add or remove multiple tags (keywords) for selected images
Batch Convert Files convert size, file type, name, location ... for selected images
Batch Delete/Trash delete or trash selected images
Batch Geotags add or revise location name and coordinates for selected images
Batch Photo Date/Time
change photo dates or times, or shift time zone
Batch RAW process selected RAW images, save as editable files in 8 or 16 bit color
Batch Rename Tags rename multiple tags (keywords) for selected images
Batch Report Metadata output metadata text file for selected metadata items and images
Batch Upright find rotated images and upright them
Blank Window
toggle the window between blank screen and current image
Blend Image
blend image pixels in areas painted with the mouse
Blur Background
select image areas to remain sharp and blur the rest (aka Bokeh)
Blur Image blur an entire image or selected area
Bookmarks add bookmarks to image collection, go to bookmarked location
Brightness Distribution
show a brightness histogram, for all colors or each RGB color
Brightness Ramp
add a brightness or color ramp to an image (fix uneven lighting or color)
Burn Images to DVD/BR burn selected image files to DVD or Blue Ray disc
Cartoon Image
convert an image into a cartoon
Change Language change the user interface language
Choose Map choose a local map file in World Map view (world, continent, country, city ...)
Clone Image
use the mouse to copy from one image area to another
Color Depth set color depth from 1 to 16 bits per RGB color
Color Drawing convert an image into a simulated color drawing
Color Fringes
reduce color fringes (chromatic aberration) in an image
Color Mode change between black/white and color, positive or negative
Color Profile change color profile (e.g. sRGB <--> Adobe RGB)
Color Saturation adjust the color saturation of an image
Copy/Move Image File copy or move an image file to another location
Copy to Clipboard
copy image to clipboard (for subsequent paste into other app)
Copy to Desktop copy an image file to the desktop
Copy to Image Cache
copy an image to image cache (for subsequent addition to album)
Custom Kernel
apply a custom convolution kernel to an image
Custom Map Files
install custom maps for World Map view
Cycle 2 Previous Files
cycle through the two most recently seen image files
Cycle 3 Previous Files
cycle through the three most recently seen image files
Dark/Bright Pixels highlight pixels outside a selected brightness range
Delete Metadata delete selected metadata items or all metadata
Delete/Trash Image File delete an image file or move it to the trash bin
Denoise Image reduce noise in a photo made under low light conditions
Directed Blur blur the image in one direction (perpendicular direction remains sharp)
Dots Image
convert an image into a dot matrix (Roy Lichtenstein effect)
Edit Any Metadata edit any metadata item
Edit Brightness
edit the brightness distribution directly (shift pixel brightness levels)
Edit Metadata edit main metadata items (date/time, tags, rating, location, caption)
Embossing Image
add a 3D relief effect to an image
Export File List export a text file list of selected images (for external program feed)
Export Image Files
export (copy) selected image files to a directory (for web upload ...)
Favorites graphic popup menu with user-selected functions, icons, and layout
Find Area Gap show where there is a gap in a Select Area hand-drawn outline
Find Duplicate Images find duplicate images within the entire image collection
Fix Perspective correct the perspective of a photo made at an angle
Flatten Distribution
flatten brightness distribution within local zones to enhance details
Flatten Book Page flatten a photo of a curvy page from a thick book
Gallery Scroll
scrolling a thumbnail gallery in rows or pages, or with scroll bar
Gallery Sort
changing the sort order of a thumbnail gallery
Gradients enhance image details by increasing brightness gradients in low contrast areas
Graduated Blur preserve high-contrast pixels and blur the rest, scaled by contrast
Grid Lines set grid lines on or off, or change horizontal and vertical counts
High Depth of Field
HDF - combine near- and far-focus photos for extended focus depth
High Dynamic Range
HDR - combine low- and high-brightness photos for extended brightness range
Image Locations/Dates
list image locations and date groups, click on list for gallery of images
Images by Map Location click on a map marker for a gallery of images at that location
Image Timeline Report list image counts by year and month, click on list for gallery of images
Index Image Files installation utility - find all image files and make an index for fast searching
Keyboard Shortcuts show keyboard shortcuts and define custom shortcuts
Leverage Edits regulate the intensity of an edit function using brightness, contrast, or color
Line Color change the color used for image lines (area outlines, mouse circle)
Line Drawing
transform a photo into a color line drawing
Magnify Image
magnify the image at the mouse position - like a magnifying lense
Make Waves image wave warp, vary intensity, wavelength, randomness
Manage Albums create and arrange arbitrary ordered views of images without duplications
Manage Tags create tags (keywords) and categories, for managed image tagging
Mashup photomontage - combine images and text in an arbitrary layout
Match Colors match colors in one image to the colors in another image
Mirror Image invert an image left-right or top-bottom
Missing Translations list the missing translations for a given language
Monitor Color show a color pattern on the monitor for adjusting brightness and contrast
Monitor Gamma show a special image on the monitor for adjusting gamma
Montage join many images together in a compact rectangular format
Mosaic convert an image into a mosaic of image tiles which can be clicked to view
Net Map show image positions on an internet world map, at any scale
Net Map Locations save and recall map locations (center and zoom level)
Net Map Source select optional source (web site) for internet map
New Blank Image create a blank image with a desired color (background for later use)
Newest Images show the newest images based on photo date or file date
New Window create a parallel Fotoxx window
Open Image File standard file open dialog to select an image file to view or edit
Open RAW File open a RAW file to view or edit (tiff file with 16 bits per RGB color)
Organizing for Searching how image organization, naming, and metadata affect search capability
Painting transform an image into a simulated painting
Paint Image
paint a color over an image area with the mouse
Paint Edits "paint" an edit function locally and gradually with the mouse
Paint Transparency "paint" image transparency locally and gradually with the mouse
Panorama Image combine 2-4 images into a wide panorama with automatic edge fitting
Pattern tile the image with a repeating pattern, vary contrast and transparency
Plugins add a menu for an external plugin function, or call the function
Previous/Next Image go to the previous or next image in the current gallery
Print Calibrated Image print an image using a predefined printer color profile
Printer Color Calibration compute a calibrated color profile for a printer
Print Image File print an image with custom margins and scaling
PT Panorama combine images into a panorama using Panorama Tools program
Recent Changes
view recent changes to this document
Recent Images show the most recent images viewed or edited
Red Eyes remove red eyes from photos made with flash lighting
Remove Dust remove dust spots from scanned slides or archaic images
Rename Image File file rename function, easy to use for a series of images
Replace Album File substitute one file for another in selected or all albums
Resize Image change the image scale with presets for 3/4, 2/3, 1/2, 1/3, 1/4
Resources show computer resources currently used
Retinex rescale RGB values to reduce color cast and fog/haze
Retouch Combo edit brightness, contrast, color, color temperature, white balance ...
Save to Disk save modified image to same file, to new version or to new file
Script Files record a series of edits with one file, then execute on many files
Search Images
find images based on date, rating, tags, caption text, location/file name ...
Select Area select image area(s) to be edited separately from background
Select Hairy special tool to select a ragged or hairy area with speed and precision
Selective Rescale shrink an image while leaving a selected area unchanged
Set Map Markers
set the map markers to show all images or only the current gallery
Sharpen Image sharpen fuzzy edges within an image
Shift Colors shift image RGB colors with a sliding scale: GBR <-- RGB --> BRG
Show Captions toggle display of captions and comments in upper left image corner
Show RGB show RGB values at selected image locations, also during edits
Sketch
transform an image into a simulated sketch
Slide Show show album images in sequence with animated transitions and pan/zoom
Smart Erase replace power lines, signs, ground litter, etc. with neighborhood pixels
Spherical Projection project an image into a sphere, variable size and flatness
Stack/Noise combine multiple high ISO noisy photos to make one with reduced noise
Stack/Paint combine photos taken at different moments to eliminate transient objects
Stuck Pixels find and map stuck pixels, use map to heal photos from the same camera
Sync Gallery set the gallery to the directory of the current image file
Technical Notes
various Fotoxx technical topics
Texture add texture to an image by amplification of existing brightness variation
Tiles convert an image into tiles with variable 3D effect
Translations
how to make language translations for the Fotoxx GUI
Trim (crop) and Rotate
combination function to level a tilted image and trim margins
Twist Image
twist an image around a chosen center point
Unbend Images fix perspective for a curved wide-angle image, especially panoramas
Undo/Redo Button step forward or backward through image edit stages, or go to any stage
Uninstall Appimage
completely remove Fotoxx AppImage package
Unwarp Closeup remove distortion from close-up face photos (balloon face, big nose)
Update Albums auto update albums to latest file versions
Upright Image rotate image -90 or +90 degrees (automatic if EXIF orientation available)
User Settings dialog for user settable options
Vertical Panorama combine 2-4 images into a vertical panorama with automatic edge fitting
Video Files
play video files contained in the Fotoxx image collection
View 360° Panorama
view a 360° panorama with a rotating viewpoint and wrap-around
View Metadata brief report of most significant metadata, or full report of everything
Vignette fix camera vignette (dark corners) or design a vignette with any shape
Voodo 1 and Voodo 2 automatic image enhancement (1-click) that often works well enough
Warp Affine warp image with affine transformation (parallel lines remain parallel)
Warp Area warp within a Select Area by dragging the image with the mouse
Warp Curved warp image by mouse dragging, with range from 1-100% of image size
Warp Linear warp image by dragging one corner - straight lines remain straight
Web Service Upload
how to batch upload image files to an online photo service
World Map show image positions using local map files: world, continents, countries, cities ...
Zoom in/out
zoom an image in or out, pan and scroll a zoomed image
Zonal Colors
adjust colors in multiple image areas with adjustable blending


fotoxx-18.01.1/data/quickstart-pt.html0000644000175000017500000001231713222767271016300 0ustar micomico

Menu Ajuda > Manual de Usuário. Leia as primeiras páginas. Use F1 para obter ajuda para qualquer coisa que esteja fazendo.

Sincronizar Arquivos: Roda automaticamente depois da primeira instalação (lento) e outras vezes quando necessário (rápido). Usado para rápida busca por imagens.

Zoom na imagem: Clique direito/esquerdo na imagem, ou use a rodinha do mouse. Rolar/deslocar imagem aproximada: Arraste com o mouse sobre a imagem.

Abras:
  F
contém a imagem atual.

  G contém miniaturas do diretório atual.

W é um mapa mundial: clique numa localidade para ver fotos daquela localidade (se georreferências estiverem presentes nas fotos, veja o menu Metadados). Clique numa miniatura na aba G para selecionar um imagem em tamanho total na aba F. Selecione a aba G para voltar para o diretório, posicionado na imagem da aba F. Use a aba G para navegar por outros diretórios clicando nos nomes dos diretórios no topo.

Botões:
[Abrir] seleciona um arquivo de imagem como imagem atual na aba F
[Salvar] salva uma imagem em si (depois de modificar)
[Salvar+V] salva uma imagem como uma nova versão (ex. nomeImg.v01.jpg)
[Salvar+F] salva uma imagem como um novo arquivo que você seleciona ou dá entrada
[Ant.] e [Prox.] sequencialmente visualiza imagens no diretório atual

Editar uma imagem: Abra a imagem, selecione um menu (ex. Realce > Gama), arraste a curva ou ajuste os controles na janela de diálogo. Verifique as modificações na imagem. Use os botões [desfazer] e [refazer]. Pressione [pronto] para finalizar. Selecione outro menu para adicionar outra edição. Use [desfazer] e [refazer] para múltiplas edições. Finalmente use o botão [salvar] para salvar a imagem modificada no arquivo.

Menu Ferramentas: Configurações de usuário, coleções de imagens (visualizações), slide show, edições em lote (mover, renomear, converter arquivos RAW) …

Menu Metadados: Adiciona nas imagens: legendas, comentários, etiquetas (palavras-chave), georreferências (localidades), outros dados EXIF/IPTC. Procura imagens por etiquetas, datas, avaliações, nomes de arquivo, localidades e outros metadados.

Menu Áreas: Seleciona objetos ou área numa imagem para editar separadamente. Copia e cola dentro e através das imagens.

Menus de Edição:
Transformar:
recortar/aparar, redimensionar, rotacionar, escrever texto, perfis de cores, desempenar, deformar
Realçar: brilho, cor, contraste, gama, mapa sonoro, balanço de branco ...
Reparar: aguçar, desfocar, reduzir ruído, olhos vermelhos, apagador inteligente, pintar/clonar, franjas púrpuras ...
Efeitos: posterizar, converter em gravura ou pintura, relevo, ladrilhos, pontos, desenho
Combinar: combinar múltiplas imagens como HDR, HDF, pilha, panorama
Extensões: usar qualquer programa de edições de imagens (ex. Gimp) como uma função de edição do Fotoxx

fotoxx-18.01.1/data/quickstart-it.html0000644000175000017500000003234613222767271016275 0ustar micomico Fotoxx start page

Presentazione di Fotoxx

Menu: Aiuto > Guida utente
Leggi le pagina inziali. Usa F1 durante l'uso per avere aiuto contestuale.

Sincronizza file (indicizza i dati delle immagini)
Automatico e lento la prima volta ma veloce le successive, rende rapida la ricerca delle immagini.

Per lo zoom: usare clic sinistro o destro, o la rotella.
Per scorrere l'immagine:
trascinare il mouse.

Linguette principali F, G ed W


F. Contiene l'immagine corrente da vedere o modificare.


G. Mostra la galleria di miniature della directory o collezione corrente, o i risultati di una ricerca. Navigare cliccando i nomi in alto.
Cliccare una miniatura per mostrarla nella linguetta F.


W. Mappa del mondo: cliccare un luogo per vedere fotografie di quel luogo (vedi Metadati più avanti).


Pulsanti della barra

Apri

Carica un file come immagine corrente nella linguetta F.

Salva

Salva l'immagine corrente (con eventuali modifiche) nel suo file originario.

Salva+V

Salva l'immagine in nuovo file con versione (es. nomedelfile.v01.jpg).

Salva>N

Save l'immagine in un file a scelta.

Prec / Prossima

Passa all'immagine precedente/successiva della directory/collezione/ricerca corrente.

Annulla / Ripeti

Annulla o ripristina, in sequenza, l'ultima modifica dell'immagine.


Per editare un'immagine
Aprire un'immagine. Selezionare una funzione di edit (es. Ritocco > Curve Gamma).
Regolare i parametri o trascinare la curva. Osservare il cambiamento nell'immagine.
Cliccare [Fatto]. Eseguire similmente altre modifiche.
Cliccare [Salva+V] per salvare l'immagine con una nuova versione.

Menù Strumenti:
Contiene preferenze utente, funzioni per modifiche in massa (sposta, rinomina, converti, processa file RAW...)

Menù Metadati:
Manipola dati aggiuntivi sulle foto: titoli, commenti, parole chiave, geotag (luoghi)...
Ricerca le immagini usando criteri: nomi di file/directory, titoli, date, luoghi...

Menù Aree: Seleziona parti (aree) da modificare separatamente, o per fare copia/incolla.

Menù per le modifiche

Immagine

ritaglia, ridimensiona, ruota, aggiunge scritte, distorce, cambia profili di colore...

Ritocco

luminosità, contrasto, gamma, bilanciamento del bianco, distribuzione del colore...

Correggi

migliora dettaglio, sfuoca, riduce il rumore, gli occhi rossi, frange di colore e altro

Colore

white balance, change colors, black & white, positive / negative, convert color profile

Deforma

corregge la prospettiva, raddrizza linee, appiattisce pagine, deforma variamente.

Effetti

effetto poster, vignetta, artistico, mosaico eccetera

Combina

miscela più immagini per creare panorami o immagini migliorate in profondità di campo e altro

Plugins

permette di usare un editor esterno come se fosse una funzione interna di Fotoxx


fotoxx-18.01.1/data/quickstart-es.html0000644000175000017500000003523413222767271016267 0ustar micomico

Inicio rápido de Fotoxx

 

Menú Ayuda > Guía del usuario. Por favor, lea las primeras páginas. Use F1 para obtener información doquiera que esté

Sincronizar archivos : funciona despues de la primera instalación (lento) y después sólo si es necesario (rápido).
La sincronización vuelve las búsquedas más rápidas.

Zoom : clic derecho/izquierdo, o use la rueda del ratón. Deslizar/fijar la pantalla aumentada: arrastre el ratón por la imagen.

 

Pestañas :
  F contiene la imagen actual.



  G (Galería) las miniaturas del directorio actual, colección o resultado de búsqueda. Clic en una miniatura para verla a

  tamaño completo en la pestaña F. Navegue clicando nombres de directorio en la parte superior de la ventana.

 

  W es un mapa mundial. Clique una localidad para ver las 1mágenbes relacionadas.



Botones de la barra de herramientas


Abrir

selecciona una imagen como imagen actual en la pestaña F

Guardar

guarda una imagen sobre el archivo original, como una nueva versión, o con un nuevo nombre de archivo
Anterior/Siguiente

ver la imagen anterior/siguiente en el directorio o colección actual o en resultados de una búsqueda

Deshacer/rehacer

Ver antes/después de una edición o una serie completa de ediciones

 

Editar una imagen:
Abra una imagen. Seleccione un menú (p.ej. Retocar > Gamma).
Ajuste los controles del diálogo o arrastre la curva – vea el cambio de imagen. Presione [Hecho] para acabar.
Actúe de la misma forma para hacer más ediciones. Abra el botón [Guardar] y después seleccione [Nueva versión]

 

Menú Herramientas: preferencias de usuario, funciones en lote (mover, renombrar, convertir, procesar RAW)

 

Menú Metadatos:
Añadir datos a las imágenes : leyendas, comentarios, etiquetas (palabras clave), geoetiquetas (localidades)...
Buscar imágenes usando estos criterios, nombres de archivo o directorio, o cualquier otro metadadato EXIF/IPTC

 

Menú Áreas: seleccione objetos o áreas para editar separadamente. Copiar y pegar entre imágenes y dentro de la imagen

 

Menús de edición

Imagen

girar, cortar/recortar, redimensionar, voltear, escribir texto

Retocar

brillo, color, contraste, gamma, mapeo de tonos, “pintar” ediciones de retoque

Reparar

enfocar, suavizar, suprimir ruído, ojos rojos, borrado inteligente, pintar/clonar, corregir franjas de color

Color

balance de blancos, cambiar colores, blanco y negro, color/positivo/negativo, convertir perfil de color

Deformar

corregir perspectiva, enderezar curvas, aplanar páginas de libro, varios tipos de deformación

Efectos

posterizar, convertir en un dibujo o pintura, relieve, mosaico, puntos, viñetear, texturizar

Combinar

combinar múltiples imágenes en un HDR, HDF, pila, panorama, fotomontaje

Complementos

usar cualquier otro programa editor de imágenes (p.ej. GIMP, ImageMagic...) como una función de edición de Fotoxx


fotoxx-18.01.1/data/quickstart-de.html0000644000175000017500000002542113222767271016245 0ustar micomico quickstart Fotoxx Schnell-Start

Menu: Hilfe > Benutzeranleitung

Bitte die ersten paar Seiten lesen.  F1 Taste zeigt Hilfe für was immer Sie gerade machen.

Dateien Synchronisieren
(Bilddaten-Verzeichnis erstellen)
Läuft nach installieren (langsam) und danach wenn nötig (schnell). Macht Bildersuchen schnell.

Zoom:
  Rechts / Links Mausklick oder Mausrad.
Scrollen / Schwenken: Maus quer über das gezoomte Bild ziehen.

Knöpfe

 Das aktuelle Bild zum ansehen oder berarbeiten.

 Thumbnails für das aktuelle Verzeichnis, Sammlung, oder Suchergebnis.
 Thumbnail anklicken um ein großes Bild zu sehen oder zu bearbeiten.
 Zu anderen Verzeichnissen navigieren: Namen am oberen Rand anklicken.

 Weltkarte: Ort anklicken um Fotos vom Ort zu sehen (siehe Metadata unten).

 Öffnen
 Bilddatei wählen mit Dialog, um das aktuelle Bild zu sehen oder zu bearbeiten.
 Speichern
 Geändertes Bild speichern mit neuer Version oder Dateiname.
 Vorig / Nächst
 Voriges oder nächstes Bild im aktuellen Verzeichnis öffnen und zeigen.
 Undo / Redo
 Vorher / nachher Ansicht, in einer Bearbeitung oder nach mehreren.

Bild bearbeiten
Bild öffnen. Menu wählen (z.B. Retuschieren > Gamma).
Dialog-Regler justieren oder Kurve ziehen. Bild-Änderungen beobachten.
[Fertig] drücken. Beliebige Änderungen ebenso machen.
[Speich] drücken um als neue Datei oder Version zu speichern.

Werkzeug Menu

Einstellungen, Dia-Show, Stapelfunktionen (umbenennen, convertieren, RAWs verarbeiten ...)

Metadata Menu

Daten im Bilder zufügen: Titel, Kommentare, Tags (Schlagwörter), Geotags (Ortsangabe) ...
Bilder suchen nach beliebigen Kriterien (alle EXIF / IPTC Metadaten zur Verfügung).

Auschnitt Menu

Objekte oder Bereiche im Bild auswählen, um sie gesondert zu bearbeiten.
Kopieren und einfügen im selben oder anderen Bild.

Edit Menus
 Bearbeiten  Drehen, zuschneiden, Größe ändern, Texte zufügen, Helligkeit, Farbe, Kontrast, Tone Mapping ...
 Ausbessern  Schärfen, Entrauschen, rote-Augen entfernen, Farbe anpassen, malen, klonen ...
 Biegen  Perspektive korrigieren, krümmen / entkrümmen, Buchseitenfoto glätten ...
 Effekte  Farbtiefe, wandeln in Zeichnung / Gemälde / Prägung / Fliesen / Cartoon ...
 Verbund  Bilder zusammenbinden zu Panorama, HDR, HDF, Stapel, Montage ...
 Plugins  Andere Bildbearbeitungs-Apps benutzen in Fotoxx (Gimp, ImageMagic ...)

fotoxx-18.01.1/data/quickstart-fr.html0000644000175000017500000005470513222767271016273 0ustar micomico Fotoxx - Démarrage rapide

Fotoxx Démarrage Rapide

Menu: Aide > Guide de l'utilisateur
Bien vouloir prendre connaissance des premières pages. Appuyer sur la touche F1 pour obtenir une aide ponctuelle sur n'importe quelle opération en cours.

Synchronisation des Fichiers (Création d'un index de données d'images)
Lancée après l'installation (lente) et ensuite en fonction des besoins (rapide). Accélère la recherche d'images.

Zoom: avant > clic droit, arrière > clic gauche, avant/arrière > molette de la souris.
Faire défiler/Se déplacer dans l'image: Faire glisser le curseur de la souris, bouton droit ou gauche enfoncé sur l'image agrandie.

Onglets

Onglet F (Fichier) - Image actuelle à afficher ou modifier.

Onglet G (Galerie) - Miniatures du répertoire courant, de la collection ou des résultats de recherche.
Cliquer sur une miniature pour afficher l'image dans l'onglet F.
Naviguer en cliquant sur les noms des répertoires racines

Onglet M - Mappemonde: Cliquer sur un lieu pour afficher les photos correspondantes (voir Métadonnées ci-dessous).


Boutons de la barre d'outils

Ouvrir

Sélectionner un fichier image comme image actuelle dans l'onglet F.

Enregistrer

Remplacer le fichier original par le fichier modifié.
Enregistrer une image comme nouvelle version du fichier (par exemple nomdufichier.v01.jpg).
Enregistrer une image sous un nouveau nom ou type de fichier que vous sélectionnez ou saisissez.

Préc./Suiv.

Afficher l'image précédente/suivante du répertoire courant, d'une collection ou d'un résultat de recherche.

Annuler/Rétablir

Vue de l'état d'une image avant/après modification ou pour chaque modification terminée dans une série d'opérations.

Éditer une image:
Ouvrir une image. Sélectionner un menu d'édition (par exemple Retoucher > Courbes Gamma).
Ajuster les contrôles ou faire glisser la courbe. Aperçu des changements directement dans l'image. Cliquer sur [Valider].
Faire d'autres modifications de la même manière. Cliquer sur [Enregistrer] pour enregistrer une nouvelle version de l'image.

Menu Outils:
Préférences de l'utilisateur, fonctions de traitement par lots (déplacer, renommer, convertir, traiter les fichiers RAW...)

Menu Métadonnées:
Ajouter des données à des images: légendes, commentaires, balises (mots-clés), balises de géolocalisation (lieux)...
Rechercher des images à l'aide des critères suivants: noms de fichier/répertoire, dates, données d'image quelconque...

Menu Zones: sélectionner les zones de travail de l'image séparément. Copier et coller dans et sur toute l'image.

Menus d'édition

Image

Pivoter, rogner/recadrer, redimensionner, retourner (miroir), écrire un texte

Retoucher

Luminosité, couleur, contraste, gamma, mappage de tons, "peindre" toute édition de retouche

Réparer

Netteté, Flou, supprimer le bruit, yeux rouges, effacement intelligent, peinture/duplication, corriger les franges de couleur

Couleur

Balance des blancs, changer les couleurs, noir & blanc, positif/négatif, convertir profil de couleur

Torsion

Corriger la perspective, redresser les courbes, aplatir une page de livre, différents types de déformations

Effets

Postériser, transformer en dessin ou en peinture, estamper, mosaïque, trame de points, bande dessinée, instantané

Combiner

Combiner plusieurs images pour HDR, HDF, empilage, image panoramique, pêle-mêle

Greffons

Utiliser un quelconque éditeur d'image (Gimp, ImageMagick...) comme fonction d'édition de Fotoxx

fotoxx-18.01.1/data/patterns/0000755000175000017500000000000013222767271014433 5ustar micomicofotoxx-18.01.1/data/patterns/cheese.jpg0000644000175000017500000000415413222767271016375 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?74?<~u'$L{OJpR8 Zl,*X`ICzEtPtI+Jdb~'ZWWLmY\%}y5_iZ<)3(8)G([-xj޼~/ $擕F7՞x[Ϲ[VJy漗MѦ-iqת[5RciuFjn̙Eb3SQ> ;&KhF@UNf{Pg1x/>'뺥M+.7' 5[P`cYH&{z:U[AqFąlL$Tp#lCN5 w<-}\W]ekmZmk*/O ◐@WV+Wdc\ǷmW 6Ȩ"F3ֺ Ib *z ,h>UE2KˋO@1'yQ[ tVg,H vqZ%ُMAH 溟B +;rI*j{?=9P5nڙ:5Zi6#=^穥Vk2 8H@?^eew"pHҚAjvl@ N9HƩ4@iO\Ӕ4@4nтN#Si09QyN}j\)č(R$Q/'V̋@R+)$`՛a`桢: Dx=)Q#$uHdB1Pۼ1G;\-RNG8\fW#R\I(dNdSQvc*0;-[xNK8i%|S9]keaӊ?3j>)t0MS$n8ÞV2eϭb3ˆ>v .:[_ƨpF03;}Eq>@c=+Z r;VR˭5jZXثzgXjKG@cZ (ӳGIW{*KhFEH\O,sӧjvJV?fotoxx-18.01.1/data/patterns/wet-turquoise.jpg0000644000175000017500000000470513222767271020000 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ʨp zi 1=4+pL gWVyrm%ۂz}kʮW;4Y2$LĐXz*"VUCr,n'rw yր7Bmz JjniK*?BH@@)֌7($ {~4 VFbUA#J#B9L&YPGA ~PVA!屜lv ӡ;HV$(!As?FQ>q=y;-%Fn?gj c4aMwJ"Y7JFluoCLC A4 Aez).;HH$AOiD!N 21 = 0Xc҄Hz?>ZjI5Ñ:zP"4LlqϽJdዲ|=}i]~Ge8[ \fEprX~floC @bUDW;Ta(oJvW$ǖpUC3{}hӡdyF,<ՃIbVwDc7 m`}1@ vw7 'QH\گe!p7{U=!!_v8-H\|u 1Cȩnq`A險yq#lv@CtumAb=鐺K!r>\P̐cRe18YPKpHQ`_2ABˀ>nކ=>J",DN Q\⪾J,ql㞔FJ`sHiJl18 V<"ԯП~5sM |*2[vr==-'.\\l,\c}^otf* T BM1>Qs1ZF0ĒNv<^6u皖D4J dʌ?oZ@.O o&) a-H zPn2~v?wF;;P#@_%uF[ĕ,z4 ( Pnf.K9RUw( SD#"IxS'h@2Xa"pJ7 dq@{~=)"˜ey6.+ %.%m^VD/\ϯ*H;sQ_ZLg'->ݹ R6E'Gjm[i(Eh#aI#׊R$P$BU`P Pr Sm#$J )95G qfb b#Ir?/΀A`1d~u6[p ۀWGZF`wb!,Pqׯ@2p `0Jq NFz"a:9M 8>S֣y;e O$h;NNݵf`iH# [󥹒 [TMGB,b(oUJIJ@B1-qWK3л=3ڭ1G@6I QqCc,U2Gl|5G!enӫw 繑tik*H8 IH]Iv9|PąJ?'gFxP6g=)@eQ"*##,aT !Iޮwn2e<5@G7Z,QIfS4bKԦ%W$|͐G*FX#8;z #D*}h['W9fo=-Aw|Wu!$TbkF/EodNXEfmd1(b2yYdqb棊 mUfV9Jy f ի?巌fotoxx-18.01.1/data/patterns/pattern_117.jpg0000744000175000017500000003165513222767271017215 0ustar micomicoJFIF 4http://ns.adobe.com/xap/1.0/ 136 136 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ??xoy;{msF{?/g-+yv|޹ݻ'g^ T_,o?|cf\ݻ;}g~&,o?|cf\ݻ;}gM?g?yzv쟽vtuo o׿ ~-ؙq5 uqvBy-?;3IϿsϵgYj~1}Ǖ_̸Uvvnj<' ??~0yoS}~b(o|7`?o#ϛ;d*Ao|7`?o#ϛ;d+Ӽ'o??-0x#>pklo?|cf\ݻ;}gM?g?yzv쟽v&v=N#W?IF[cE#q\|ȿ@Y6^.Ҵoϲ&>Qs>՝ea##WM~#2V=[Ɏ4|#{|y^]z&_Q֯o|7`?o#ϛ;d+Ӽ'o??-0x#>phoOe>g}~__9Ͽ׮s>խ T_oӯzO/?GmW?(2/>|Tb3տb{+k&c_qvݞ=ռ7 Ox{^_Ϲbg_Â~lOo?;??<;˰~9Z/POJK uqɲJ/?/J~g9j+6(MEYr:O/?GmW?(2/>|VOo?;??<;˰~9-[/򿶿k?wnmF{?/g-+yv|޹ݻ'gZ=/CVMVgP>^|Tg?7Ww_o ğh>!ſG>[7J7̋N#>|jV[Mx{$}[:?{fN#Q T_&}+O?3(s\}^+yyU?xoy;{msF{?/g-+yv|޹ݻ'g^ T_,o?|cf\ݻ;}gZRi_BVGF<~Fe۳v{QoC# KJG7wnlWOV& <={>r~W ^_٩7o/}ǒxJ>/G9|Vuy__5ˏX;go~,!/xw>%>>v"&z~3_hZVVGF<~Fe۳v{QoC# KJG7wnlRjc4?5^.Ҵoϲ&>Qs>T80Vfv{?/g-+yv|޹ݻ'gEy__5ˏX;goVGF<~Fe۳v{WOV& <={>r~W Z?i7~GwRҷg۲~qe+O?/?/Rn|3=^_٫<' ??~0yoS}~b)owZ> _o1mk3.?`ݝ۳ڍ&z~3_hZV^|Tg?7Ww_o ğh>!ſG>[7J7̋N#>|jV[Mx{$}[:?{fN#Q T_&}+O?3(s\}^+yyU?xoy;{msE{_&_Q֢?|<7 읟۟G}?׭eU(jM&z~3_hZV!ٿ =VgP>^|V&_Q֣N#W?IF[cE#q\|ȿ@Sk_ bVGF<~Fe۳v{WOV& <={>r~W Y>>\hdLr~W V4 ?aa};)i[?{8Ѳvg}})7>_sZoӯgWxÿo<)>{ ;t -[/򿶿k?wnmF{?/g-+yv|޹ݻ'gF{?/g-+yv|޹ݻ'gEy__5ˏX;go~~C=;~,'&=>|4?5x{?/g-+yv|޹ݻ'gZ6^.Ҵoϲ&>Qs>."z~!x>!/G9|Vuy__5ˏX;go~,!/xw>%>>v"&z~3_hZVVGF<~Fe۳v{QoC# KJG7wnlRjc4?5yޓ ğh>!ſG>[7J7̋e+O?/?/Rn|3=YZ> _o1mk3.?`ݝ۳ڕ迯#G7 읟۟G}?׭eU(jM&z~3_hZV/Af$SZwwgeC/z=ZEZ:^wB'!|Co폴ysR"ywM&+O9-[/򿶿k?wnm]?[$H?m&u\8'dF?@!qs3󼼻9LTĮd!_٫<' ??~0yoS}~b+lҴ>>_{ڵ2?"#$7M?g?yzv쟽vYj~1}Ǖ_̸Uvvnj*jeU(jɲJ/?/J~g9j~,'&=>|4?5F6/CWYj~1}Ǖ_̸Uvvnj^$#A~-?"JQd_O }g.?vnywa;8sT_"uo o׿ ~-ؙq5 uqLY6SZwwgeC/z=R%_ x+DE߷[l_؊4 ?aa};)i[?{8L^)ea##WM~#2V=ֿOZ> _o1mk3.?`ݝ۳ڍ&z~3_hZV{i7~GwRҷg۲~q_i7~GwRҷg۲~qg?7Ww_o _o1mk3.?`ݝ۳ڍ&z~3_hZV>\hdLpkwN#Wi7~GwRҷg۲~qe+O?/?/Rn|3=R'm'?|Cl}ȶn+Ro{+'7 읟۟G}?uy__5ˏX;goIއG?▕;>o\ݓاoEZlҴ>>_{ڠ _o1mk3.?`ݝ۳ڊ'=A[o3?wh贮uSZwwgeC/z=ZEZ:^wB'!|Co폴ysR"yw+_e?Ŗy__5ˏX;goMx{$}[:?{f|#{|y^]z&_Q֪W2_oӯgWxÿo<)>{SZwwgeC/z=ZEZKM&z~3_hZV _o1mk3.?`ݝ۳ڍ&z~3_hZV_s-[/򿶿k?wnmEki7~GwRҷg۲~qg?7Ww_o^|V&_Q֢\j[~xy__5ˏX;goMx{$}[:?{f*ѿ_꾶=N#WxOŞ#^%/`ħgQELUw".IއG?▕;>o\ݓآVGF<~Fe۳v{QE[&M?g?yzv쟽vQO? fotoxx-18.01.1/data/patterns/geometry2.jpg0000744000175000017500000003766413222767271017073 0ustar micomicoJFIFExifMM*JR(iZ02300100Fotoxx:trim_rotate| http://ns.adobe.com/xap/1.0/ 8 240 360 2 2 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ܸ]EZvi䘪۪!޻OE" Q&Ucp#>{SW8,%q=5^ S\6V[rNI)=IS־\gS"m&jJi-.I &?Z:,nUv }^bz6z`TN[hz$bYJ7ug%A=sq5֣ mmb-]]3a'2?=X ''g/޸cmwvD !G$O\}p˲vU2N@{TEIs?|@;T^Eg$9<|k[^[my, H8vNqxl67nAKk9P67]_,9Bg9jW[e||IS5b Jۢ`Shd?;u5v8]^f) g*On3tYluO2Ibx@~p>b~}cw?obiw1T 75M",uwS:1_WEc}"_@|;8u0M^4>Gԭp 'pK|(淋NeQ|?u@>x_bY#B'ՎGZrMծoP_՗<TIyoR]?NOCL&&S_2hm ۂArzdjzeW2oZLLh+3QڸoR%Q&,y2q֟>,&TXNӦᐰYCac%òޝ1М&OWVO+6&J7Q[?jfz!I$.xe1&XXX >u \0KxR#]IԜ̕U,9?/tT-wľ"1OEh8ɬ_>Mvun;žW{O >]hm$um ]`rӟ|ylah6Ƽ<;F\$hB% 007sU+sM"q]ܜl=kF:6Id[vqnюX:aԙȶ =܎@0c^Vowg-/m{mŞY  Q[+uֵv40OgR6f˩0ƿ(,#9r͒7`ɒH V6{ə.ۥ4|zI:P6_D~4 j^#CFaTp.N[5k7(Ҥ+k#$ryֆj~:Ogs>8n^gM xTeN1H>5VDxjz[0nu UcO/ L<+t- %Pς 88ƿ ]_Ao!&{W#dLⷾ nsEAJVi50U|p`$pzI8xo\\M5p }sOh߅d'8~fӽ^jG%[$ dzݽtkY.ɺI|gj;TY-IJ.eٲ C`z gFig_kg9;cBCX./,6!:?S x05 j/{S'β8 d|#uT MGÚ_6To '˘<:jr!9.ěp;c6i:2G]#UkFR-7XjA-d?0=F1޵pq!$]W_2[T6c?J|)u8[mILIvLF3 Ztx~QV]! BxRzJ-KI޲q|f2#ҁ )'7vbx?Q=1rrqc=??EJ o4x3@`L+2tw㯉3\]\Z1nu @Xr3\6y+ S 1˝w'o㏭'.PhRjGlT oG^NvZ{仆ǖr[(HFmG.>t+L^pCHe }m%kEc"y p ½B636NKᤞkkVu ' K^I5qY'wnޭx'SxB&J`n忼H.BI:f(c13?y$g9Y. '0M2شIzwo{s*R^&$spNdƸE^i5G9L2 WϫCwq&& .B(\0'ֵQr՜c_ }RCYGe=7qkh"ȾQ~vRO+_4xGƷZB!fLq>o=$~)jS3i]2#tZD\9sá;sN=_^+ߜr\$@UKdžxt}fMecpr|6F WSJJs4KkkI6HE.$pbF>b#cy{$smeuH|:#wd^j^ȇPK,08@Z>|Cd{pKyi:Ձe9u9qFZY\igIt=G%qofQlVHgɩҼ EHgT,9?N< b]CL&y'œf"4ӦLanZ{K#]jz*j+kiL뙯bY8>bFszOim4h")K< CFH湹uw!1[2]nO^–_@.1c3?@;*VwF܂OΓmSf$NCvS7ɩ r|m A 2K`6euXP֦;lrW`+NX5sVF @ sQf٥vy V2B>|g5|tqge;YϺ*D;ee3!y+4s`qדc_V kxmBz>dLڳNʺ/ltA#?ޔ9һk;I5xP9 j63xP\sl 3v㚷>t5\+0GD :OBhx'NK6=ok*DǑT+Tnz^y+U4]S%Q.FGFI,`WD4Flmغq29g41zPîFw{Fy~I;XVl:w% ecPbӈ* zm^it8nmo5ml_#hE|gy])dW%\cq;׻|9d^NѩF?S s.EԮtoWt8%g`<~b*oYK]R$a<{)$+|k?i]RIgFA6sNBG6RUb1u`<~NMCWԖϣk3pn: b2L(98g mﴽj8 rTWPet~m3vNC ܊ieluD[glź/JWk.JO^)h1 I.s`n8qeլ8؟8vA>kuk;lo׈5h&?듂[t J7Z{@3+=j-=yPj/%;m29N0`k?Eak!ȱmA8%Ab8y; > vgd\qz=/Zy7,?mԮ!3GrɴTT4/2}/OC)"q݈W'=jloa=v!Ij2bFmQpyH26*s׻7>aeVO-'*}pPhDTihn !áOaR<0;o,F:.8ڊF}u1*V^ka C=cud/NFkq BHN(SRlxCAOj~lgff ' Ic<901^e&=09Tr88zzlZNhVm>q:rʊވǡ|=agj< 3 .оqG= <!~{۟j xA\$$In1zυiFZU ,LCMG3x3||AQ1u# X,rr7P튫Qۿ OHcp"f sTn  LnP+cqvg,y/uf>6̶֭wl20p@eRspw%v>xP;gݯ74CHk?F9=w WsjGi.cvv( dl2[ {_tua]r[㫏I*y<~ nZHԅI'+.,|{cͪbc|0$lp38!y5|Im:ځR'zv:KxJ}-mgF$v8 >"ա"Q883n4>ԼOS)8Tzo_@1Yx2lE]pr ʷ}2m߉g伈' >7CDE_GGT,ZWBzӿE2-ukE  Urx67zy,l'm6q 7ǯE8c>SiyyZݹ[`(WʑȿhkS%(l0P>i FXG"TsLt'Мר[H24,#*{d[G4iqu'z- =jSN۞Q=XރM7!{s"*yo#Yx^Dw[@\!y:~ Յ?1l>jEbVGagU umMOTGivZ0 %‘m!1 xO%峺{5dxxvq޼Hށ99Xm5*~6{nFRJϷ)wȷ1X`aqQHOU{?æ,صa5Ps2; -OB\]nC9NpI|O|BѦʐcεf$0w㎊ 8p_GrgF=1sBWw5p.9%އh \L1v,TYj֦XXh`c13x$onG%mR2Re=77OV=SA͊ i#J61\5:!qVgctzm/ 8\g`gL/>ht!"Ӵ+LLx~bĒv^+;Xuo܋jɎ)ۨ=ٮ?L>n`L}.$$'q9i'&3^U4E?x " a"(IddN!@ѾhPê(a1l@9NA,s5Zrso˒2~};ӽMƒ՘UMMaᤓ&)69nN {$.-N&HeHGL=N1][*CojC+[sd3q=+2KG4\[fI9 9koS7Zqp5u;Ԃc^xF)ʃqerIWgwʒ9ּozT%jG!#p3mcס?s%"Zrcr '78=Q7-H4淐6ߜB#' mOL}ԻYvW9AY2e\EG'gx=i܏跚Egl '8 09f[-:|mBoSԘDge`f#܎㭭od$! BQ`R`jkFBLV6`\FIS'9i<_h:RP Is!cw<.*zìzG'~$4rQ9*k:|4eOYHdYYmXݖ Mk(-ty(sqKW Tz׏m^hZ<=bI5|lfu-!V =9g+-w:o|FIt]Wu;G'zߏ$nAZw!gN+Ftܬ2&QMRJXTCaubn_딑I,~֩qG[hZhyi\Fr}3#0Ď[wewv^mNxAj __.*67fW y?Z=Zhw> I'K&N[&;=Ir7g2 8sh_Gq#g7=Jdy7Euq%ff^[Z-̏*7M_a@R)[Wbu{~U]*ѓSYͨxoZm2gTe7 #x|8*KSjv'̣qR龜Y,!Squ$,rO̽Fw20p;U⮡{vmť<ŐqzĶo 5 6ylo{rsL n/D >l|s :UEqM_w6I.IKg;c92>Qpxj-1>#`^st7R2@~"\K91#zr@:bbM&mAu[_E뻑(0*[I%㡶>_kRګzmO90[w|U_ E&Hod<]r峻jYM2[+9LVNc Œc5kP뺌[SZ7QS8O$)a_٦5>|?m:q ƍ{M[KnqsIZpEBrM! }&GR~c7*mZͲ\cW|ozCvTyVp#r瞂)oy[⻍v'A w~q־4?'YmCRHLD'~b$.3|#f5R%+bNqȅ0p{NL\id BB8eHlpI&v[){O~B.^_kV1r]O+ssAo{\3s & |׭dYk.RfF>\c{USI:8fF]ޝ;ݣ|GZ]ڵ2i\Na|W&Q!tbYQ%,s6ײ]獮?66 ݹj qۚQ-%5d/#Ef#?;+_21 Ek],aܩ-(+T]ѐ|:ujtÔ +LsmkN87s%Խw kU6:k3WP*ݿ7OJ& w|k,-e ml)=pNiu* 똩 k\)'bE瓜 ׮xXV `PXH>C W֧qj`gtbsܑzu-cq C2m퓓UKl^#7w)L o'0y0wO$~*ƭ*}8KDj6ywcu>6Bm#svKn]7UA2H/B (u`>8Ft=K R}VBiFEr v*p΀u:o~*bLA8kBC3i'A*Qd$U/oQ"BHltj/ɨ_|?+8m]!qc<cT>(]YMr rF7ңn$Lķq3P;'? 7~cfotoxx-18.01.1/data/patterns/pattern_131.jpg0000744000175000017500000005044413222767271017206 0ustar micomicoJFIF 4http://ns.adobe.com/xap/1.0/ 200 200 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?-WVmkV ½3",GLQBl@gV#IoQG{#eo4 Yᙖ2e մY^'}'S $n*(RGʆ% Rh:݀l W{YX.nl8b rJ!( R! /ˢG[}٧ˀ$ zS8d2`;{qsYkZhj'eVVGБ$wQ0ںlrF*ͽ70<uo"Xr:)AjZ @;H;K/Q4svB` ~)PV$+`=?4:`e+O(A#+P#֘,!A=Oj(C cֆ|N~P;, Xg'I9 d%?ڥ2p3H ;@~T?ޜ K`0.i ±^;ir;vV;yXvS`)P0C 3{t)S+iTPHV;JBF;wJS,8~r03ў= ZAoeg-̲U*JNsӢi> ӼE#P5I|Aq>cN&եo6.].GQkC x`[+Kb픅G<2-2GV |76!Ro'ZK{MVEE%Lp '.dG?]wAX~>5;21 R+fhf!rbFmi{T7\[m.ZiS=óJm (ezWfo?C%8GT ҥV^K.|=#h.wXʆ,!*D}LK5K7:31f0YĈXm̲|M>WUe]vߣZM4O;o-K5M&Mo 9Icl:6V 9nm%K`Maͪ2[={)K#fu+Ǭi˦j$iꒆC+bML#TlM84+CZlu:vE~Q3E1r/ܡB31UB:\YOWҴ:| 3=Ք֖0RC|*y_> X{kc%:K{Z!{ bpг!yg]A8_;1MIѓ˖J7[Nϥ' Mު|V'/5.J \-Y"UL02K1I>>cǞ"Ӥ`&.]k38WDxJpVڕ_V:v0^XX='ʼ~YıgEC eU^;zԊO^K՚.4Lmi@=ڡV!G{i2Cg[v#c#?rH]Ã9dOC.`=?š7*OvWʑF9<o!LpOy*]*2 /_O4Tv#aЩ6B@:f{($)Ld^vB_O(PV1-8ww%sim<灊r(`I݀Fvp;@^0Ky1E{k,X)tPy>_ĐũhW:7ӮJ #bH#<$W|Oxt/V6rͧjHf)nɝ+mx ^?↟qF|YZRhѢjW, )m;o3R=:m`9bɐ2RUsJn͖ X3V I."5ym/_{hbS{7Zzij<]^W P\ݽF,wƾ`7>_WL[.!_OllbK$uA+@+dR`& ʾ@-TJ#OhImn# 6Xq'Q|#k>+Q.wvU]c FϧͫO i;]_xm/i48#1y73y2S-q|kw6 8gNVc2ӯ/<00DA*ʣW/ MX(o?sw׿SaOwRDSB`\PW$Jc-$B׶ W8QYG}NC l6#mZM_J"o "Y󈝲7~ kW反g Ѵ;]$).m0aXCH$fbL1dzl!.4m2IotYZK P/ c~LjZLj[#+i#v0L"$Bߚ+&t _*$+ \^JJCiim$HY܅Vb8UbxԬ0wCn<_MJZ=SZ5C"sW 0uR  Gv?:>+9xƟ W[ Zx {Lk5Y")%y%8r||}#%k̚ċvhzu$"K .X<@2R*׵R/L9pE.n`:dd VR7B\s{%ķ.;$U0HH|aݜ籅Ξ]&dڵZֽmt_ |׵džx![I}uq\֚~$Tb)an# b1S9 JU 익<4%Njϯmqżs"d0=GiuĒrF T#(Av{{ͽ–R_T9H'Ne'3@I`[p˲{O6ú|TjUbx$3\@"$剒@a w$q`99ԻvpQ(%[j䎾qoMWZv:K]RKHL.$s!.\T=ϋS(Nik ![ ~H11+@*wQM:,NGˀ?h HnиiV<-v^M^.w H-W 5!FUw:kNMSb=-i>!!OOusC8 '!R4Y7^KƟu|uMu[_xvP24D )#E{;Y|M%S߈v_KM/ZZ\H ]"csTi]⿄υn/ SEhK%0msRgīiF+r.*54:=[ZZ͝3hW4UcDF\0fӸh\|o|dŪ!eRχ線IGXB6.OI@ǣSyTk3 kiϚkv2㐪ndaŶHSCw>0дK[=cM7RQ +3D%q"GՇi24?/ק^U6*\t 8yl]c֒h*Gִ!6Kc>J> |i9ƳjW7er3^@U)ʒ <18#9bk."81k@CF2vNF0sӟʕPI ܏>i8':TTP]ec=;!ɬK{eDYeŖ%_;e3  ʼDR#p~k<3Jp :y#'ǸA һ?ct٥X7߄N$)-,pKig5+R=:q#k6ٴ".m/%0I4r4z¿\xoK5-E &:T,@^e%JgEI(%d3Q}Xpċots dhk( 0(# F}k> W |t#le6@Ć/~6 /|com5D*N23kup*cbԪJk-UK RJ0"6o;>fV:f]YG[Y%ĐȰF{ΖleIǩ^կu^#_O#Z+Ot%HX z#xONi^ԼեXga0pi*Uw)du#+(NJW;Tn 4ac4ّ;G^(ʴt֟3J|9ƞ+e -I̒7 v[B6Bp -]}gOsuKI Yl%N" `gBWMqwxXLf&E7 /  PLhC*w#kg_ִѤK&ap.gPe; TGok"0 :Rӟu2j^w3sVĉ@#U8@_B9"om yrqWjyl8c\V}`C–$ g?{m7p8~trS : CCE N }tSh}h>YEr 8bUa\iM>imi\ŧj]i7TŸ`i4Vb e+ovo`)`b=[^_G 7Og{m0D-WUKyVDebFR2+IusRw"[A%5$/l#j-qVv3ެ)L (t`F"YxÚ:зxJ.nu*`Z4)J+_VMNO6q3%Hā$lo)l,d񦋀d3d{Wuwi񅻤X$z6$q_f,*mP+zsI;.r<0W?)`$s酲ぎ=l~=y>fݣ9sL\AC__xr8l<i$gF*Hv~iUws秽R0l{P"J|m d3cV!I\qF.Wk>*@Z"F%A4dg< c`9zhl!^y oƟOϳ9g}j6c3#!MіBGM T1>RJkVx$VDG-4+v pq|<ύCuh]nO'QTNh`8 I*A$arDrxZt{~3G ǡ9L YSM?Dom) iyMȏ!**A|ea35TMӨ޹EeC ,a9 8w8J,}3}᷀g<.o̶7a.'ui%R\\\˸ʛ?é$Lsyj3Ki* #;Buߴ [Z5.4[xsb% $̒$:r_> L6v;G^.5е<[MZeY3K}: Fh eAHULUy}u-ךƵ\*[QTdQTes;gvw;$fhȊLJn%P*+Yg tۭK:ڮAGien;Rm}ѱE˽eȖ_纳,kng4Ҕ]ec:ppy]]$GdhNu`AVV AZPL/#"YX)Ք) "?ZzAWDk)^[qGfl0FY!mC= aWc=Z_Yj.6K=/i+"/n̷a@)mf .vto/sQmxɮ>cs3_d?4Ht.wdHuע2J5M]?!|UѼsN4՗U5(n.!D o65 qfRDban0 pH'cxbJ񌶷>Ӭ;[hv%X< ʸb 4N/aY#2tebH ` AS_s wj6JqMf݁~k' Iʁ cy8W* csꦂ6#׽),9w Ӧ(~Px8RJZWi Ylg*Ǘ9*=?0. ʩ8@˴x9 8 )|eB`\o. n?OhSc%=9!nac$9YAV*7gwk?7zguKMVK TB4&{셬zWog,ڵͽͼO.;h O |n"?~Ӻ>.Iq4yrnVT(HP{y|y#a{Y5֫. t^Ia31$(e 5Cke#jv?]VS<4M#1ȆDU<=RM;|Yi⏃5[KTφŌ|兺hv;M Ԭ+y7#|+/{@,xT]\OHa Ռw8q9j*={}[ާv5A.H#̵T)H#Aa[ʀ2ieg{5Zj̺ ͙c!7 v b1%UJeWPMrj57|/G<=si#>#2ލsUQu%dw]++>Ɲ_m_7v~@'į\6lp08<'#' {+kOj&lNO]3ucq/O1Aʪ᎗ -t[Yo ̅Q yͷh14AE9ۀsҼ Z*Z[[<W8y/.$f3!frUAf8UP8 xF#Lܩc^O\>%VAy 2<2[i%`` Je%YI* gWc:i)kl!|N-+wu,J2?c־xixi'4 #eX w([NSO'=|JJE7Cz3.VϖÄw_1MWs٫ץwfoem+V*at^zNYյ+۫VO hW!#VU~'WdHD1 6~o'bWwe+Q }ukȣ(Y#6돛 ~ 4w6&ilX/c]!p #Igv4 `W=1?MlgHqs*r3ǵ zs88JN6Ns;SsMSW-Kq@2z9«j)4ˍWA&K}6hṷSg8 b,A0 e1mik:v—:ƥ* :,1Q-ÀAVy\g\wfi;mX*{&@c.ɐ2-l0S]|mŸCȴoEX4|(Þ!.ZytZnĿ<1 /`[|h*K[V}xO˦-%o.f$ĐwvXcI'kF ey9㧭dxJ];úlL$+rO7Yd,pXbǒXɭZg'c}PI6E]~j^yEf@Ѱdb:84  \~6pN"KȆ5gyQUA,KOlb2|Eqi3iWjbWcx0Ve85xw]"Zv J'-o gcudB&mG2AI2,he߼l6ƒ D#t>${.M+JNuspӼͶ6RIU淓ZBU)e}>_wߧmߕє%ZtԤ_h| 4Z;ѥ}BgZ71C4y7$ރ8[ᱎ(Q"PCE`a+>1k goS[ F-6 32I0 s򵽽pkP!Oo\SNO_<RSBV2{Kq X{H`C`7 TQ_r$n kV5pn9*pB>Z\`W*0FOH7Pʰ>ǥ vgvp3Z"x'' ׎*œ$dc83;o$l;j5ok9b7>pV>lXݯٖviV/ޡۍA JX2_5翴 h. 1Ga"jVDutêhM>+2kf=H&WFj:\-ѳ#&K)n[vpk/ G<4^){[X1Z6@ĉ)1"FL9e&-{|uZi&Kt[F4ojWi mpHI-RIJNqy_qXZP)5E-P(Zb$ 2{I[ ,4oދ_,rH"Yb dHMӭH&-wp4ҖH~,N 3ăjVOpLTq犫_rֵӟz׈oma\\q1 ,q@'T"8#<.06}jܮgZ$36r$,Jrdӧ QPD@چ9<8Ryn~e!qӯ0# w4JlRxOl_W-惐֒ ]g'$ =0 vF~R03qsjRdb<.0s$g 9#@搱 I`gw t'0R9#jI9#09j}hS@<+^]@W;Ko LŘI9$M5Wִ ;M֋C:Eqo .&w h?c~5m-+ C܍X2*d#!R~-mkM+EOmxGDB7XL[\v/;+mk$R=GZ{Wz6{#!!+t8$awT4}.Oŗ-NyǷQR!N܄`G 22:QHbn@=ݦ%s@~Z(2b ڞ60]F8Ttמ (I-F:D웂1\pt.) ?Qn<9][l֤ P4i¹Rq\P[ wH*NȼUUxQΊ)x$4$sEZ_:=)8c@cs%Նxn\/!!ZR/]DU{A7mKsi3蚎C vs:5.)}EŔyT)E1ݮM~#itofotoxx-18.01.1/data/patterns/lawn-artificial.jpg0000644000175000017500000001637413222767271020216 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R]RytĽn!-y:uK! [&C˶Al?:\QI$O&ߺ6)L; 7~m]ìvᢌA@hKtaؕ0"~*! 핵{&Jk~v*!fUd2t*)tg*Q_~\?K.ӭxJ6`VIvWAy*g}5 Vxʒ&륊Rh1fRNO[:ouNӭ4CO%_B8Hm$(I*rpj/mgmg}|QؽFo)"-7>eTQ\Ro{y?0k~{;4mc4L&UNJf\yioZܴb9 HQYrI\o\'ПNjfv3F@něXǐŌƳ~%sw77>ceBH720~TK_ؙ9*s󶞧M&EvzuvqŲ;[wI,>s#'ˤD5ƛc(IHo734EFR{K }?RIO#6|e.@&HۚN߇Zigu%͆I7l^Ps<F O%Keq^սmn~\rxve]2 q.XYMKeSoq $q$7̭J)1?!-#oVGIa,7 Bdڥ)qdE7:og;l՞9o! EWs w$]Y -Tu&t-Fy,-[)|&34TJ9-xGOmcwYαM>B"2*l9u1,AcOYƣ>R#Ia>kn;mY$1Őy7mP[qKO u#}MkM?Nk-tYmY{I]PmY[$եoVAu_h:-Gi㼺HPng}bP@bA$R5!.2I<6Kpųt$h,J/$y∴{KOO=)?W)#)]Iu%5}WkGeÉ'i?t!HTb`gTNY_bk+?[E̫i'tPBzqqZŲC)B4SfRFX|`{/]xZ5X.md^)lӴ`F*dFV<,3ΓMR5)n.Es5irv]w*fr*[3ʰ[ؕa8DQ"3bUapT()r3W?j3NZUl,-0^%7b"d8BTnY! íOšMƱc;gc7ub$me.{tSi֛zÍ68- EYk&scM:$f.r|EK/jZCFt[&B7y 9$/ET' I[}>3մ=_ͧ5@<fSae$)Ú4:hSEɶ,=\i BY,'ľbt6{5#yH]Gn6,6nmZ]QXzQy'a}dYc۵sk[5W֬[LM6#hIo%;V1L6W|5VS2 X]hƯ5QY$،6јuYx77=u6ș|r2ʲA{RU nyhG(rgFe2tW2q286+)[zO &]Sl 9ie2ܴkEC+K$h(bۯ ׷ axnYoM:`uY$` )=5ƳXx3b薏%Yqf-`qt+w\BMȣдo ޤk w)kMb+ X30Tbe?^?عeNK[k7KpSʄ;>B]gD#l,|^h=ӂWuXH `St=n('eC,?2\HB$㛯 ;NOx#C U9U,8sroGhQppZ-߭U<ht[RJh5ź܉!hr/FQj7:WOZr2DˁF/x[%JU]~}#E-unuI ^'ѭ1 233y"1O @4OGլc tlr:"2 \18Zzoxq][j>^=N=LTk^OY!ʉrE%0b$RM}#wZnjo46R8]і)L ll]̪UZ׈5ɷCZ,.c(PC2ΝW\=258~}u5̖sP>K|(*s<_e|J?tbt'T&lO!gHfvэzd~\K IxFrؽiT7[\4$1DV("o26UB 5(r(QQjp-zXvci.y:4+yjѩfV[eB{%,L:͎gs5Imh',q_`n- R+KU&?dĈz:4)*mDuύ>"[QƞG7hY.mr,c˨FwIGײ俯U>[^Eqߊ<@d:iv2_KڱXh8@FY }eM{&S[ii'h;oyfJ{TW6 .z.4A4SrLxjn|XLuW2\Kxc0(S7JVu*F+O=?6fV?VVZU/ W]=u{ȍ̂gQ&hvFsmEj6 onQbSr;]?-9-ψzV)-uS>"+:oܴcĤU%ʪhx|7jtZ#{ EٹG!ۊvm_OL^׶zcvxoZFv!~o͖< +42 J{.u{(WHKTI$ubpnf;K!K'4o k=%ODˏ+#3)ym 0i^l;AsOR*XRFꔹEJWJujկ5kTewnVf滛-|MeWB9ii`@6# L dq6XuA>R{5tX%eF\ *J3B6l 5߈A{ yh|i!)!H<cxY7/䊋;A'm/ݗt:~KHHjzk$V#tm"&+C s\#ڥ}xm [3ռm"$pJ<#$UV{: k) xR[+=;(GP26N]?HգKLp3HmTI%%{Ej7~_m~<^ZjP]ɋc2%̗Pv\ޠc<5xT̈]K,,H w*+9##zO|)h|#%{w n7Hۥ(f$ u;Mյ$:~s&r4qL$eK(%\H ӷE??y--=%qWroi<^ZK"9hF@$!BKM[4m BkC$FeQ!D)=a^XdxJRկG,n|t,ds_(Hު p')F[ NS9lmk]>jEX׵;IJ,~sLCYI`]:-ܗqW7e)Q#Fсl2j维F;w>G5ƚfxB$B5 ڟȳ6xN+nh."e))mۗh~ƥ>ed޿费Kz֬V6 čbHвe&UQ+ܬAt7> КWQowLmO)6r*ǻcd0;g2^wz=Zın(3I 2rA| 6ω/-6^I<#HdX>c $1Qm6_gCuiKsۤw%X/ R2HnUMی ٨R4 `6PգI+k-*YFSvܜ12wJRc8)EOؗL-BW&ޡ!Ah712n;6\uW?kYčp3u%J*%ćxx΅ZO{|:tP;h bD̶`\ \x^4-l4V7\kSsCcg\VZ^lq_mYgEDU>o,1Ǘz>Y,vFi7wXm,4 tR}3 IOw^ K#Z.j(_iԪhSFUZX|W)(_MwC*7"%-*~grHMuo-lne淝fFHVh@ZZ^;x|YCɵ^7͸}r:K{NzHjs5o[|u?fotoxx-18.01.1/data/patterns/aqua.jpg0000644000175000017500000000756713222767271016103 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?u#Ny,cl~@@NKd䎸xw1RO5?]$rd7d^u ۅԫi{VIʎ3S"Gҵdӄkoz%HA[];VХ+'5Vpāx+E2?4+sVKb5巊 Fc9ϿP: iDpMFěV6y]MޛEou{$F$0?y&-ΠciVUWw=WvWrgd@LH+f4N-;}I9 I:ozz$t9_u{,W[c1| &L5;+.B*ۨ^*}\R D۴J0:zNѵ ҸX YuW7^.@I 8Oi:j=7osR{Hu ,c>9} FH ;]dF}?ٱŃLs_NQb(ݍųXnd }-,lw9 SKi_k?n}_Q|:m  g$懠\V\&H e<i/us`,B@83vJ 24L2G=1^A{m0]E=?k::&֕'(/q.^?̦y \^_{v vKCxwZ.<~Lw#׌=̷r$K[5P1ǠZVzݷ~p )79t Rv35٠h 8* };kt$6;_C+Dmd£Gh~צ協n]b(B03 "nQCtf75hc`kpz񞯥a}lт{1j濬xMD+s TIsj##U K·,P؂^G \vkKLRcR_X'g%ol uĖJlc)1>᧴c{1Z탔̭b4u"mO챓@\(=A^ ̗666ww d?#T<#i10đFz[zfCv% rt)6gZx)ΧcUWt^qlɂrZ+KaL?UmnSIN̻Y*Z}q6 8#5yi\ϩ~\ǗmGivnsy5J&)-PJ0#nI^9 XZȒ"p]qӞ7FǨ5!>˥}t)ɺ^\}v5h",o-laRV8fʕ',zָIK,ŏ&J0zַ5SI:{J9?,uUx&jÊ60goiɩݹ<Ʒ5\iL`|L1,gvGՂ+=X$o$P&%_~jl45>grz|ҥҭ,3y>@vOSY:ȵ|mgjfғd>^vą=1+R[oGk7[H3(q5B +7Kh LUsU=Ofotoxx-18.01.1/data/patterns/notes.gif0000644000175000017500000000506513222767271016260 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?<.ʥf 9㻩,t.aK$8] qߥ'($4 !tAFd~XG,}gZmfIZqno Xy}ltQPY^G#,?M}Dϑi}87/sXǿ=kkI[M~=@B3SmǾ WwCpxI[ԙb'n၆F:EZ9FG$#;`C}8PT0EgYQңiXzZ5O>qrtc?>öz=KPae*TgrEKR7wGMT@Ze،. E`,|e ]TwlE,rDc}QET3BJET ߉nOYcHgԵ{D`;* ,z`WAiFy$$ګnGSQZJI :Ef2 + ]:#6.RǩSE0 +ͬ|URYirYuw H6 #}&Qщ;QP0+÷T;75RII7$ ݉zzi06 =9VTؽET (&Ht؍ëeK89=k;֥}.u5Xa++J#Xۖal_/q_[MQP3/Kmu]Zt`vͣc~'VwL"N3֮^+p#G99OZ@DOHYw%HppO@9vw׎jEKXd1m*9? {c<O BFƌW=s~DDbع9ӟnv '(zCn+>[rRعmmqd Y0qԐ3z=*?uۜ~ SjeM*+(>iߜ`Ҁ"M:XXkPej@0W<:g1q^muVn}+s/ 03ڥ2pHv0;A;>\{cҀ(| 2H: . Ïֳ#jʓx#ԂGL⬵1 :\@"EWrv:p9V{|W R'fYI(té]>9u0Fa!|͟Q0k/ubL׸ $I̋?\>Y;E8 F:\sRv~EH2¦=u}xM,H(9tI@!7n]cǵQ:BMI hDI)%$~W7E61f p8cg9 xPɒ 0zO94Ř0PNAc<.QpwPtm :=G+wF,x=sQPy=>44"ˑAIRq235{`fȔ].J½`** :0pA {Vҭd[A_r{r9 Sr C@F0q>jwb srytNuMVpŀ# 6Fk`"FFHӭjKw1iE>GKBx (J-vj 2NӃ=:tSW]}!#~ t3@d9^GpC%PTdzqxۙ03@mk2 %Sp?/dGKx fShA t#("-L(cOu`3zbdS;cnIIT3Oh^ դa"(E-Ս,FSv|g1PçY#+?pF߽}i!rWշ;\K4#xfQ둌t@76!2̨YaE&1M1,  N3#<hǕ/2y_r:l Ƿ@j7f{"m5 H8 }8Hj6(*3n׌}Yy2G>^8jHW; AcQqڀ '*8V ^j9@T3c<Ҹ s0GJč3ʼvzlhܠ- %7)L+^z`.kȜ :)/lJĬ̝A =VX>pz }'iKn@%\-NA$ӟ\أGGptGNqZZLjb$n #T]Nt\d̪\0~ GҴ%R|+g;AGs=? {?ی9Rݱ~8RiBDw#A`Azquk=> $SQ9a~:}1W5tKҁcjq[LUdPI\<㷽kS|!*A\vqj3 <Ӝz*vf,/c{"OnҐn\pH?tf|IQe,YG_~fX*K ҁ[5d|qpO~DC}hfotoxx-18.01.1/data/patterns/pattern.jpg0000644000175000017500000001664713222767271016630 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?/x [ Vɋ \[-Ŵl,A,Xȱ1wE$2+xZxsGt;m:VkM3R j:-%kx1ihIjS$ueDp.#8?ts=|.߫KcKm"s#G,.3fB2|h$i=EXY6~$x^$u3[Zgu5(A2;`TX*SfWzgl%ERԭD'sXiV O*9gAm+SP[_8hZnsI<#-擸cs+Fl|J|9xOTK}qGk[jѡMؒ?V0gJ-{.vkYok>'js.{|:KR4"(aÅecI.x "=JPw|~O7,tد⹾HatՋo=6쑿I"8eeE Eqi6&iEl^`$ qjRgd/I񽝾u>)D:^Ya")Jc1TZ'ź~զtHMj+6M%,Sk'Uυ"ntA<WFX.movG(QKXö/Ԗ V|MjIfs*5ͺ 9*mrj.}'g+ۧ"gM(ۛ* X-ۯo"VYV 7̌>TP<|9VwWzX]OP.48"T`̓\tRҴHBPC_4Bݑv+) KM4,⫭7K66-DKx Jܪ"D mޥpj}ZMT-^{hGvia ܍%l23 'i?ؚAc!kK{1:έ>h7[-«C'ChjQiֺ;V4LMc 1)iI xfG$'{AO H&h^8^C f۵4*n*]t%si)ǧɾ3DҴCS״/$6aV24C,Ti6\᜜a@+1]2-̘&IUB*Lc8mWyJhWwVw2K*Gz*a bUTgqm`Αgw/cI4f KI71eS]6\VV{IJ\˧IhVwz𷌴+B[E"F(I#%w]> iy.s|sj0Bh\`XRXmcemɵ*O. Qp@Vq 25FdH%F%F+Y-9"VUԖ21gPBU_¶De;K!u Ż5%Eo>?<9;!g:WA*EWmU%WiRx/x>:MXj)QD'K7>ц]pNmk ;9u@jӕlrqZ3: iZ7k6w2C.Kc|A@ʠP1&~iNOdV'cxZ5uۙ3UK ȲD2(^rI=t+>כTMnXzf<#F)a1M)w,>%x5m{{ɁyOs DJbrFy/'σ(.cW(3k$r\eIVr`2hg.wz~+pM_٬5?x~;Oj xTRDN \?6z=wM/X׵7k,Ʊ}mrĒmHH*A&Ư/ӵmu ]>t(^K*I伈wby XlRE4]wEyN,jIଚ'OBBmUdkHBc)KInltKOE"ijPHB8l\:AJCEKKKN "Y]Ѯ3J83q 6T t.nn[%innO FVSSwʹ߼m*/ ^5A=j d9.R(|GT6T1# U{{F֭5 Nڜ:cE-]i) BT&Q$m5Fo?o4 ;OŽ"%I""mk^j|mr3ml f|]94=>^.${M'5tB q)j_ؒjwfHmm TH$\mX8+В>&xJHWX?DqL]MgGXT 0)Ep@c7%_ s\{t*PO4MB]N^Ӥ㿍%Y%aW$*;iݪ[^t˱c)w"2#YX[#uνOz0i14\G*RDP̥xT]F\[nXR8Ġ`ulgcq3vsW]:mAhzFOjMJcɧڤbv&d9 G] Ien![n.ciuKy6;fE!XDj*GtԞ{Ԗ+ }ޯ ܷQL`̠6Bi~"~%Jk~SޜbV_7hļrI`.^TsNWk濤Am4]gs=z]X1}5MP5&UrzSƞ <}uM%[:O'XGqYO,V[S?xKӮ oV Qmtr, 1! ;k]joH𖂖L#[$[>u""G*UEe1O)SgrӶb^xAZڍޒMyŪg&w,~HARGM^=-#G(4') >X㍙k1V]~Pԟ>"hv}ꚅ3%՞gb$F%i.BƋ O!>7M̷P+XᕖUUXyh-ݵ8J3)%4|_%[O:\7q^r$Blo2+8J;ɪ?5}{zzm\~-RBfK91BÆ$.Nj)x-k|LөuǻB H@ӅEF%eMbޟJ309($FY[y./WRw'oBנu 5OEo W[%̒h]Ƣ7ڪHK]i7]-{'V}8%q9h$f&&5,8͟k qh,tgQ2$v:Hgo91-*w9|oqgB,n'~{X f8fma9qcOanNX~s|MtQnjnɢ&wZe {ۛj)b>bby.IRr#}P gMv@\3HccGKs"|T'-,R|Aqsv.Eq}<%>͕fFrřV`,~5 kSi;\R<3[G0kKxč"d2:J**Po{_U&V鱗#/v>#֖m#e"\DmIvR27Hۚ^'վxzuޑkKa0ŵ ZE<AH=A,>-in5 NFwdx.S _y4hW~vWS Z$)5_"vpXriE?urpI'm[Ğ𭮗jzgk3b4мlѻ%#A ߎ|]94K."MTD-$K1?t[i/~ H8D9H/$RFQ.,xp9c̦$ۗU˩-<=x3Mtvh^if)b5Sqr#srL%wX,MVYnyG\┴hedV ]{ h6{ }WWmHfgFxacŸ}hF].?1nyb4Hv, )yvv;~ ÙRQkYmz_|\vv1C8 8h8VQ{BR6 ?|2ծtyOy-Ksr?Y Wm6F[qe[x6Mz]͓>2[CҲWBmQmPGB$dFi_ql1>P'&FB)8{o~(M++v⿎ isi _x[d=k ̍ J;!yxwN1^ܽ[]$LLK>J(+K0Y a"hd_,a nT2Lh\%+( =.Ycon]Щ 9$տ|KQ,Sj2[Zjr]H7Mdm&hO,'4?^bslw:M Be9R0tjJo_'CmFD3IX0+^K|H,)2P|߯M+mk۹[7VzŚ5Ɵjzם-XFb0c0K6>!hw~׬gI>(ie oo(XK`>Kse >%bL|^\NO5ƂȰ:-ISݳVz.}|JEX_B"mB[&b#3:1IUd r~rRnfotoxx-18.01.1/data/patterns/rings-green.jpg0000644000175000017500000000637413222767271017367 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?,g gi|mb%}iSVMSTǥOM^ /#W6Enj1tDsxyst9>X^:V ?t?·M,? ?fK42c] lΩS{LR0&\uU⹥a,ڰ՛Ow(^Bwggzr,*ҝ 3]I+8͸=9Sn[@tKf3ZU&%ًyUץ6#_Sh|;i;t+#qn6Ƹ(R&i<l$:#R7A*/آQ ? &M7LFAF 1+VIXrdWHڔhʜF5η]$W˃R'.VѱSG<'%< p$?.]c=X?ZjFuUJ$v=TX@2h3Jm |ֺVRJ>GoJқXwg`c^\RiB ^c/WnDp1ӐzqX^Lx[tf' } ٲaUѿM_G%Fb2Q:ZT4,L [V J:j"By1 Y:3V XЪk\5\YP:d ROПkV֥YmX|$mvDϔbXcwW>zQOGhЪ80'5iyVDL7'+%$='jzGحc Gַ7xLLK rr<}* I㹻5XPM/㱳\,}j*o3Υi;-_k="T8`Г\ׄ4i.Fv~?*嬲j ,y\0O>0͸mt1Ej ;]YBvi1ȹ[9KdhpW^+3Jp\ߊVL|+na[cxW>lg8g7aiQwat~? ?/1? "𴚅ڠeY=jK}der$XԂ1}9]nm0(<ǚj::,*2s_{|y~pfOYV{²:@>ֺ(&=)vX'ZΞЮIDRky3XpKF:b)y[J5^@"iqixQUF zZ6$8%B<봗,RoP7`L?$gۡFz5E L6/U2" rk y;I9isODx ڍ5Kqrҡ.ǁַxUR詇KEY-VT>ZZk~TLC]}yIɗpqإiVgVָ'PGu`H?wꑫx2+e+H;Imx3$ԓ]lՃ7$aHqXZʹL Yg')˕-L"U54%0ÓE rK[洎yAc=k[R"|? cuISQ^gfotoxx-18.01.1/data/patterns/mazes.jpg0000644000175000017500000001623113222767271016257 0ustar micomicoJFIFC     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ڳMzjEu>XV_.~vcޤZzMj('y Z>4UtLQа)o:Iy9 odt7x|FҀ~.jx-ӯ"I uJ$C tBu &V[#vV꒴Q$~nV+>ޜmON)3*QYk *Ǚ,?uj^]WM8SrOm]xgSV[fVbCFwn[E/X-ǒqXy*Sx^)+6{FF}r&tY|Adle.-5j7S0u޺ |ݰ'jƑ5#@ '$?]# 8֩*Em7wIR;3,QP7jm:ImoӃˑV\4[?5"{Ƥ_hiuwpPa^[4yK?Bu2c-1z7!OÚlm,m"nVt;R\Z_IzySR2GIEvZ8q拍;gvAk Ԯ%o3$sȧfJ7Jvz NB6>M^ek{.,tyY'#/kMUEV*;]I?f5ɧ>YZ-ߑlԓ57)rlQĪjD 0A^ ڤR|{6KzxmR(Cg>OV$zݯ_h#Mqt|q lVq;*UQO̬v\µ(-[zE$>|ɶnc~oW6 j ҕ~gʊ>DZmU{5xbVWn-mBN8I䓾megk kE:lvџp=TQx~M/yK"xEX6}V )Y` \w[mԭW"e; ؍T(5ky+8Mhm$n5Λˏ䈁IT]ZyE2 'i$4/Rr 0Gqn|Srm)i{v)9. Yyg] 5=?NTa":Dc<;t{i%Oc?5UUhQ=븚}nJ3ג^{mnY[~a 1G . QQi5:]>}I-,e7 12q4Mo\+6ݦD;U%^Q*9;DSot[=ᰗX7So.ӽ&eN;kdy,V4[},vWL³$:`~Bj3T:i/v˳^zb_~}^Ҙͯ? I}`]u(a `VX>cgV:‹'6gחIaܥֲI趞>k}qlHc oq _t.%լs3d󼝿rkخ*4kkԓnJӷtgII"@oi`wn}kwZ_FkHd+sa[GKR]Ʃc硋Jq~{5n8~amZ7z?5f#E.?¯hś7ZmkTݎ?)J 0U馞ohxCScd&ؤ0/ӡFI-m YIhЂ=ֹ-o컄c*h3\iM e#ocor^ 䢹l~=̜3^c'I~ ̟o*$ mi{kj_L𽘁--ypO cSn$m[] .lCT)FY}˹ 4'䴎\TrN{6zIj4+|M(?XsMuj<">4 ?yʅY6g}-+\`nxRy/z1'*[MZ0"n?U4įs$ŷrO*{c(G(}^>욽+w뫳M2h]qa& yʟM-rWh8!#N23ZՏGt.%Hûc;k3e9(FNO]{1{owMuw C4+GkvKFo_'~tjH..,mN [nA V|#>hm}+%=}|'z gcTKͼ}A_ Ѯ14N;yؼ|ҫ 5+ŧZi\j_FM]. zR {K[E-#Y;Ukqh8okaQ.~{>c.3{ JLg-nm.Vmޘck, Uw~[j>> 4b7,yAXV,),̱8ٮ:͞"iphwncsy ݬ ʿ>mzjpsi^(E"sf4=qGu.{}Ke ,ySJ$u"y,AXŬzv{.gtuRA_3iܫJ.E7>s3C?1HIȯ*xoe/V}<}[vGkX:Z:^kofiYjGtp,Lg(i'f/l"Mkx~nG]*+5u~Hfܬ~Vp}+1>ʔ6Imuh{3ݮVoM<冖Gu,=u-%Ƶct_ !`.~ρ޼#kx0Y͞z"xf8}> ,51ӽT]z1l^Qt ͍jVT7P/Eh%k=b6F_޲CI6;[v$&w0WEW][.]+FOF/'~YK,&d R9Y\U4G'?J11J;SSDﵿ22(n=jW9܍ yp~.j{{;EzS`牌}*YZD׽nŭZE.X!tF8Fb ?հ@oTY.Eғ/&lXW-Ҩie ge'$ u]cZZKǡ2ZݘG>UII6p>o^y,f;mg&ua&1;rITFڎq Lʐ\J1N}N1ӫ+IM+=5v{*dH\>U UvbB.5cr*]lf-( '$=#FZe}1btD F?SǶ^ZKCt`2jt\}xOV&eЭo| gCg,D#z-c6.qיV3j4\٫/w>ZSyeFǘp9Ɔ8?yhUn`Ol"[O'uyn([I3ԣ[ֶnm#WäiwP,fI|eeߑ$:WPNt"(h/^N+k͍ۉdL㕶FjWi7ѹ-QQ#y6ڨrj 4n~W5qg0\Kw,@XV(`cd[zCߕsoJM Э'$x꺮"ն K귳5oq!8c,O%Z֮:ml_ylfKx5k1_c=9n6bK5a9RKjRߓA-u6O3Vp&ݴƎ_McI%1>hޭ7[PC}ܙa ֳZMoZѣ#Mҥ^\z.Wo~[zy2.CMk a5K­샎7UE+3:1JI\C-\O}{ouuhCh';Ұ&&n/e`ӥ%VnI޽npe}7v:\J.`oiچhZM5n|ij7o8ҵʹq-Kó0ޠ 3P;+/P|&禶at]u-ŠFcE">s3xv%|];PU|A2[tړUq;9ޥ3]Wyz4) m.9Wbug F}JIj7%[N钥\zgjԟUe; 9?4lo܁n୾AG<󥽵my%W$Է: n}HvAOl*֏CfRRg啈#*FA6Hh|^k{,Kig+ =Iǯz͸[Dzm[i5Wa2 Uu B/ Ϛ.1d^jX;q֧,g _~2;: W{ր/InRXEd^_NZ呮WhF@+sWti-PjV[,>JxwjvewC0sr{Pl:KuH8UuRZ5{}N=ٷ?9jK}nas,Wd!l4<=94ұjw-6#.3 CMsϔg8 U( |0vbZ'WR(xX3ǥEuEn\/ |3(xjEI2͵N\@wuBגʑoix-ƛ$QXHMG41) y|Aqw`t?3f :IY0`Lϸ9t⤻QrB}j뾅-edLrz`V_ \*.]$1DϷn 2otcVU.?!Z[K|hmۊ*[k4r5D|>sK&Zu.#{b3%.9:圪Ţ*ʓ4&,2V^V5ڬ*m.{1xgı9ǡ{ՙ3^"N@nsZMi; #-.dG;{UK ]Cso!ϙmϮ(Y^!PlkL#Q ^tL3 &q#8}ǭ\mN&-Tw@ivd wd׭:Xt K 0z-BQ6ztwoq-ē! ˴qi_:@p4rJHi&8^jy<=,18%Zi#w <3R܉ ,h֭6B#&4Jq'V++1heUL\{3iV8d^@Ms>DH ?IbLjqnQ ~-E붋QjV> +Xg5˻隝ʇsENԖѮ]Kt'nqj ]6fi v#9s@ c귲$[E # ޯ[zQ!4-JG#ZVgDGSXx7,1'@ =Pt 9.ɂi9gg<ր[]F4'傧;: |tʹ?2'LȢ?*<4eR2׌zry?ʀ5oDml^}9z}yW9*K56[N,d??CקjedV >TUY>4M:TeKm9{֬iz-?Q @1ǭ[rV\!*8dc}@W(0 / =s}{z}C kOoc3u, ş `oQu#KE2tv ] 5T ɷ\``x}2`QqrHY|=jɨ eCBl;Է~ 7r2@CV!!UA>X= ҶLOyQ~q cUO ̚j\.N/ (xcfG Rc'oA@5`8gUkhA{$r` 8>Z ծ#x&,.g Mn&I#nf]Bai2U }:ˏY*>"~'V.|?ogoelI @>[P𾝣*=S真~rhfotoxx-18.01.1/data/patterns/grypaws.gif0000644000175000017500000000270113222767271016616 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Gm[fi)f8QԚkY{}" `;Z6bBRI|8,HiE*́vQ(DQ*bKy0WҀ#((( 륲v3Ͳ:,ʑ#vӻ[w U}<ȉ@" 0Jgާ[kk ʹ'qbz׼DZjF jԧ€7mat#5rVOoZM6ՠerwcQEQEQE ᵁr\>ðkZHg7ditt"(ScM_UME>0\ c TgsADu=BME'Nx亸xmuC(?Z֋ϗbikah@PO@T~[zu&M RI=* l^LM8@TQڊ)F%B2 :uMiυr"-́6^*3_0߰E=gj7em%ĭ:xBҺjJ|4`wZȳӤ^j(oS[1HTv (֩2MK-HM!#H P}EjwvV-p%#hު"o Uje2yef?4ܓ 2((Uݍ6}=@k?PvRTX8QI`Pj[QE[;evc!ʒj 2XSI@Q@Q@6HĨUuŷf^9#NiNAN(Y9{21#9eQZ(fotoxx-18.01.1/data/patterns/brick_wall.jpg0000744000175000017500000005716113222767271017261 0ustar micomicoJFIFHH$ExifII*  (12iZOLYMPUS DIGITAL CAMERA OLYMPUS OPTICAL CO.,LTDC5050ZHHv558-782007:05:11 12:32:35PrintIM0250  ' ''''^''''lt"'@0220|  >|PJ0100 R  2007:05:11 12:32:352007:05:11 12:32:359 Fotoxx:resize|resize| Fotoxx:trim_rotate| Fotoxx:trim_rotate|OLYMP4  ,Lddd 8ISX558[pictureInfo] Resolution=2 [Camera Info] Type=SX558OLYMPUS DIGITAL CAMERA$1' |X''*H"s'1+3=``sIa1w@[1 \YG   2 Fdz&&"&"Vd\ #ASCII(HH    ("&#0$&*+-.-"251,5(,-,  ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzx! ?iȯ3(Y${Wv#&ޓJq܌7qKG\tSYQ0 g=Jl62NJv+#'dӘXybdH 7+KD-UaVlͻI+=Ǩ,2p[TSTĿi1N26:w٬ë&YVج֌f&oFeJ5!2\ZĹǝdCZ(eXҮn#o28QY'?F~$^Z7W\?JU ỺűL^=)s_b=^߿أP f qN[o:wi{݊~Ͽ,Eod(\r1~Tw6ڄS5$dF۞1ޮϱq!(4U/1K+Yc wDK#AqnF S,BL\A!,o~{Rv(k Ze#sJ=6֦y]F mn})]Z-){ m6f08^c4%B㯽Wi8[{$-Hxb$82Ld| /9qԤrAd1di7BHیw= :#GSRI&s$vwږ;م=vOګ-n sM~"X,]d  V I1Uٟ=OR9=hWPsIggwp-w;S\Be)~KO)݃'"؜MIJC2%(ůOhA{D QA})es>1lzsgwZwnۄYRk2g?pq谾[ϝbpc})]^&rD);1⠚A9F%0,sRi8>ȢM,024{$q ˱vva9O\DccTi<4J]ОWGJ4j_ {!d7{fѐy/^)!̜p)="}kAJSJN$L B4ۘ>\FTz(mY"V rIP{qԚlg+Eg6+,1.1 V.$1^x)=,))$0md0;N1P+CVY#gRW]&՜"PddoL?ȵev0 (7Iv)Slc@cqfzwѭcr33oo\dN I$W:l2\Zm"]֯_ig<m=9$ .>S ҚNh hdU,*sn㺚 n'E;x8'~*nW jʢVbHdS\{ q>޴Co YI18?*N)u/ d4cB1s$)䉥rV", jIqE.-Ly<DR2]C:qr So% 'HgO®\{RV9>] ۫ p-ޢKV%wdy{&dktr9y8Lhbe?g?/uzIcJnIonYыKHPp=QVv0Tc cӌT;A% O%3WOE{Xε۵bI>D LMI`jAn$>{ЃV^:kseHcM5ě)BU&wKlŷSNU9B =\X1O< F1A$En*YaAjjI^JIsD<|<՜q4<;]nϯ֗>]5L96W(b0@TK)P =:ª]*]PinX %9ϥR!.(&=VZdE|#Ov%R}YR`e2~;{> dZucdəjmYZԼ~P01,wtNҋ=~f5济[GԌjFWsSͨYoo oTc#~|}=}91RZ dzQg0̉ *%q’3Ǯ?JqOII)oo,drE0=⦾{{{!v@HA'~ڒz?>bK,qLLi/ "[qBM.Y4驒-8sM;J56,,\%{^>5N!`Cܳ[ba[go{{k{@%U636y6zoKtXXx"XEns1ނO.;22cgAOԟA-4p\3ƒ $uS?ٮLDi{+WOBkihc)i21;Qhڸ։skai+C&WxE 1UM/M̒S2'Q3E:K[~R\0A^z=~(TMnJ aXK[$twg` TD#$y##ғqhƢwoB{ci,}#ݿOn])]B}J[ٖH0`daSw@InűkF$َ#gҋY)Օv@W\JwiqqOY&TdI c?7}@:a$k7Ϊ_y?)'(m, ~3We=S *YL k+%@$}WVUm+t21e#OcTSIiDC @E-ŝ3c8'r3Q h#U'*yb9#K"21 `c>5q^/qЂ}:6I7WT`y*M;De[Ϝ$q(\b}Ddq9MG(]x8P5%[[̐%|"b'* RЧNk~D'NS 9Ge1?,fpf#]1,œ+Etc%;Imتgw4rF9Cǧ5mXﭒ [By)$i/hBYUW;}i))n5ř_r_[]NYФ=`'~d裡&\2'卋!e?f\m@ .8^qz9sBNڲ$bB&[,H[j1; Y8@̝2Si> k{rn-rH7 7=yN Mtor"<JesdH*Χo>>V d Ӿ»zI-.Z6<;Wy4ۗD͍Q18{Ѣ OH=;-+k,-$Yu A N6M,rOIKf\f{zWpC>i*]I0?:j*Y==;zu'ܞHGvvW~E3H[擎x<]7tX¶nW׶jղG%ʢUM>I`%9],I.o nybO"G0$}giYid k{Qt UTBoҪ.+xQHQhF0qkFQ4KD8̅xDql$IDmp_ kyy/dž&QXXd_Å`59{զV2\ Zko>Photoshop 3.08BIM 8BIM 8BIMhttp://ns.adobe.com/xap/1.0/ 1 2 3 0 5/1 False False 2 False 0 8 6 1920 2560 2 1 1 C     C   s" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? ⮻ Ō-Y]ā\A+GBu-ORn2JprA+|E%X1#_yI-!, 8FuusqRKd$xYRm +:^u ?Je} #+~kM;V|';1<{YUnI~$a*`)zkqb3(eP?J~7恠>$i-J~pX^{mcτVvZ)^qש|[O,zw4ǹY,dTUh_w3UT:m+r y Ƽ+kO_^+C=i:ȊAM q7oj\J!ܶ7 ZkZgwu ens] 5SWg=YgNP=}u\Ao2 2j5ʜ7~Ri޵]k#@b65ujW\I(*}Cμ)6z5S4V֯#;ts-JWz'\͵g r"kmmTŚ\]BAI\etvrdfHg8vqIKSU0szW⇊hVuE>T}^Q!y/<4o19WK:G 4gloO id1wek9c9*)SMNFs5gN] ~Dn5lDDc?-kĶW"nXgeJ9ͬ E7=rq޵+Nt%t-cB񥄰+冟Hv81+| ,)beXsUF0'cA>:JsxZwzO<=i4f7DvCx1^go̙mkJ#dl$`1V%:TRru#ff_dZ}i}6R,eCg kHZq͘ߺdYs ýͶ ڶiŦ]oe8ƛSku}3-`pƶȢK:'ۅߒmAq\h4hbld\Ӆ6Mi^st1FIdrHq v7;A}3]u!o_:v ץt.4 6 -MkDG%xښMa]IST~eZǂ[~ NgZqm)>cGbⵔ劑o(n{_m,]yd+,J:Z5[g伀<33{㹭_ő.wm G:@W,. /zs\!ZJJŶXmgB'wZt=. ٨%5AN 33y48>$ MI䩊R_oB-I1>uF*G^Aϰ'zbq8~Z. _3nB2 9Փzyk˩p hee?3 n ? A j"ϲH]:8pgAk*g3\F0\;Zteo-Pj"y\NjԷV7U\d׫?G abx=^a# 1hg_]Gk=BD(5E5Jfqmfm۸'-r2;~5q躌bI! jLԼ-- JM'a{$LE, |\,uxԍ%vs״>=^AՌQ񓞃sھZH8{]wq}; )Bp)e=nxMmk\$.r g!ԓV,^sɡ,sYP|CmkxwG0!T&>QPF.L+b.ECo^VĺT$2qMb&.:+扄x Vִ9mi 3W;{m.?7z :su%²c/5"Is,~" WNi޺!TpiMwrFg} |`xxȯh?:yc&"KjV-JXEŶܟrPxö@]ܿIT  Βٶ[' Ru.C/J5Uٯ"5{4ioL~L+3 w'msw]v Ci2ċ$M9{TbMpu 1dp͏αxI[YdiɵWC%FsAb/}䦺xwY4BoyS&s 12HlIs,GI=Ǩ#ܢJqg4Gҷ()>w[oC_>-tО>ej *О3T M[' CIWۏa5Z\,0 k/&UyT|ߐ]~DcK 9ut^sq{q3敚$79#=j)>4_8Sd\\ 3pĝF)}r _KX-mY#SsJi:hWoCYkvP𥶩vJwRsd2#lxxM甄%!n~VlHNyczV.wE;0 #GֺSj.[/TFf| CjtnaWߑ].xo v_yInOQGvKYݸ#Q\@a:+Wll5 jeW_irYLr^%aOx3o hG,:cXUA{2ƍ+d,_)`9ٜƒ?=k*XI>CRr^&.AMeq3#&LYgԴ Ve-qET7|*XO| ˦Zx#C 4͑V ~ Gm*h|P_<.mݻ洼h=W?㛐GFpHKvzu= QsoI x3Z_GLapnVfQ֣|GXW,P#k[_ZfHɩ]緖!ACв'Z0VRyߗu-^i'~,v;<籫V{x?V|Y88y1r_j,=F9|9{ ő }7ץAm{{vTZJAd'V/ ˸`l{jD<)rO>ecwӚ[[  U=ʸ'5Oӵji^I᰽0ry8<9"I. ;Oz' 4<5aup+M"m5o iO$ߏҹ𷍜:[a=91]W$b1:*O[#Y Ǿ*i 13{~dLj|7vB֓x.Q[evKr)ҭ'SO2c/0wesȯqFfΞqצhݖ }~%xXnn CGHԎ2N'/j)"e"R[Ǯ<]GlŅy<ݸUMD,8v'OihyaShYXecM];~xa滇ϔOd+?7^ψ>Px,`FYX ~+ּ=K=Kych[m3dfEү/s6^-ڏtyR)okGóx:[q0F?c('՘ן/n `l\1'B͆a4V9Pj6h ON\骎Kڪ¨>Ts^ T-#x` hsv6̣º2#q2>/?.W_Y m'z֢#Vx}fݾG6zq\;&LWDa}G6v8#dvw$aE>TQI*ibi'NXݹ_IV[^Gb3MLJ5[Na34p߶'{xE lW7rØ4^'d]KqH*~"G"Y+ՠ=ON4T%v/4k 9>zmWRuJN#421'_) V}E$E`jA0/ $ϾU795LMjtL;&%N1 3k %̨Xp`z |=,M 胎p1vgex$>x۟rGQ7MQw[ wo .˷mmsP}u)uivH$nⵆrrK)ԍEGEsvU pp7"~/Y6)jѬElbR8fsTO@x?]6Fm4 +9M G߬GuH?kkay>mOVdL7J&?z<<4hKO#>.ЧԼ#Z}Uy\˞$16NUm2s6v޺=#dw6;zUJ 3'G笤C|UsHN3XH[OwoJŢ6w}k⟈5OC]Ev$r8ۥyLj…5KJYc Tqq d֚`K=JC"N@+CM4{gOe^Xbq޹ICi.{#HV!pߵYեןnY$pFxJB%c\]H8K_#u>;º+*dh bfԴڤR>p ڰW6( ^z~kUѹ5FiV ~RNVݙIb1RZ]&xHɖDR q\ľ% [OT }C8W.umFt)*LÞ2-k3FFIʙ[߀N}zStl:GBn,[iNMeZu 5+X/2 &ϱNP{r'^dоc@'#ZkۍVԐ,R4$lgnJ[^8J\ZjU:aQ0YH{ug%φx.Gw}M/t{k@GhcKZV߃W랕^Ou9P9FyڶTƝ* XU֥CJ{Ѥ=/w5m"5cG:.u?WHFux: vxK糍W>DgzFs +ZVGy,p=9*=%YI;_ ͽ> A??QEypP29VVSVڟ`F {Dzh> ?{Ku!#'Xx;SxZxv^sV>>I$׭L/-#)r}Ho-zok\HcWO0+5xPGacz4_%Ms RRRj4pUyx!fv+\nAoƥ῁ '$UEcdy=k]Hcy- VkZsH"yKm'p99(+ӡF0W{>U?^7#vcEt!|{;^k{BB1 9I& <Ȋ=, ,kg9Am:PN.ufmg2wY9t B\?QXw?9*w~NF^N0aj{O~"ԛ!.Ğ~.Q AGCseʚ8fNO 6 ߮c)Oމ69c[c\tqxO;w]F6IfdL DL_8 =r;SMx#Z4Wܿ xW^!e9̛m$˻S]𦁪IqkV3!`hmMhIRD9]Q=ϭIkDMPqny\I%cԖF87aGm[>?=Ml rzp+_ n^hv; <ъ5[,>ОuG뿩 /BM-<]oz]lݔ,Ь{r"0o\EQo- ~_? "Youok6W.$H;h|11N롏 \f){h<ڕK-h[.Tg+.OC o-k{H}BǏ5e q,'VSq>% ;*-";=%bm2~O@+wkqZiR ۃEiQ4bҾ!~nv) Oj];NJ7ϋWl&q`ttQY/oX}`U O^5Kx#% 4'p~RG =Nz座v]nhJ@hQ##EEi/:uįd]j4G` =QXe_qfotoxx-18.01.1/data/patterns/geometry3.jpg0000744000175000017500000005217513222767271017066 0ustar micomicoJFIF !!    ' ## 0!$(*,,,"150*7&+,*  -$$+)5)1.,5-4,+,,,0-24//*/0,2*./,0,*,2*-/,0,4,*,,0,,,"A!1A"Qa2q#BR3b$CrSs4?!1AQaq"2B#3Rrb$4S ?er9X٤d;j5fF戣3=i[3/|wEcjJIxj|[`8"!X,J}}5D$cQta9- \ DdI,n=~QI!:YLM_o􉄰Ȑ|ǖ<04˷aC[׽{cc7=hPRK<8ԙL#Ƶؒz~qrbn㝰?.E#+SwqDch9Mz cMiW~pۋ-dx8Ds:u:Lҽ%1IP:rTe!} &z9$17ޔǝz|i+\*Xj<_8+N-cl9)g[˥5>o)gy6}8Ss9"aBuXw z`F$pA7]R4Hؓ8>{cᖥ u\$D{8ymh vYͩMEsu{'Xa#R0KY5Hځn,EB⊰ @"Ńߜw459PowN% :OC‚߳ip81OCQtHRڶv:lChY|w] HJyG z`2O l)I~`LZ [u`n8EENJZqge ރftML81x mW{حOeGŅn Gmx [Rh5HxzHD tlSY_;7áTz$}8P84`t׸$HcZ]KQ"'i[{iYR :yMY:b9#7dGGlEoW7 )`<_W#QԀم׿g҇PVִYr3[xPqued^U,d@Si. ^bOex .68vW.-%FHؑ|`ieNꡨ*|h)('01>m=^S#NiҺH Q)7](F䝶-悐dڻ dhF+?At?p~R1_625k>ߟbQ<`LoY5gsxC"XP_oJg)?,rA, dAgUs´#*Cu~i2Hwe 7oQb`vBq8ktÚ*HPi!†q =y54ڨ{Gןm5u@|ڬ7#!2۟ ̲ !FmWIEE.Q17K(phZ41ꕈ,"5hP =B{6Oä81%@>4:CDJI] ,Lͤ%Y"afx^(գlH +n؆^cUyXm}.؎k8eokHi3IK^,gj۰.d TT#R[V(;RtZXTe@ sW6a S!ΒYy_1:3$#[U1M.Xo=/AFbw-VG}(n,){c+_A5HDmU[m: Ƕa~?u)EǝIe K*- pG1b=>SB ?aŗEhWʧpXlޠ68E־"LiŌ>eSud60x[BD kEq)}bVn}3 J!bx(HhH)8 xX%M|Wv7c^2ᢩgSJL*=\2$]hI NTډ<16bt}#B;m9^8aE'a>#njkm:$+N5,B,mBtVS#2@]EK 3][bͽ yᜐ]Ysl9<Λilkjabol#K*l7jmEGB5y޼(M C1 iZ[ɦN=#xCrKuz$=pW=i32E76hlx83)<0%E #6ZP;9;ūxENqK"jBKU%G{aNoHm*6z\BS**gga`@ ۿRΆ6Bɷ!PtaI:%olao= Ψ:icWo^ $w*tcF(jbgI sr$~-Zbyk3IQ*x>P1TuO=cxWȾF#? &r0Un7]#H͉sۘح% RؑVj󿋘xgTrѰ7J$ n0ĒQ9*ud "9Fs&Nh `Ͱl|;y /H}q"5ۛIW;'ȱ+g וy4F`7+aJ꽪7`d:wաPi͑Pc$i m/Q`ߵ ~ -0i%2e*ꅛ#^ n":Ū4a38xyޚ#J;*d"5DygpmAbQ d& /]sö3S:xOpȌ%IOXv{틟BbJ>b'e.;|Yrx8WmMfұ]_[3;B"WEV>uRKzog NfuFNڧ61* W*y'ZGsSeK@6޽~:0~CR} /欱ƒs? Ivv.ҌV:E2/ 1 aA<5bU{4 l`IyJDSZXu[Q=QO ',/pE%v#ER ~gʻo19@Io"&bb ixΗOP;s42#"O`Oqt1W9!BCZ^oH1m<*26s$$ƞ@Ou~ A wk܃aǥb>DO+30oQN|^t:wBm˭tYȎWO̠&q}^qfB²cK8k.9c4Ұj=-~Qms@ժ^>3 6~'ODU %qd2&W죐Ed\x‹8/=+Dކ _F0 6O_۩3&2q3P^6'N$ykvYX†4[0~1Y돚 C(XK?R7iA~'^gO )/5 %<(+}W#AZO *܅_qw>eY׭x櫵🯼͚L!LS:-ؓѡҬ ].C6=~t_*1R3+%UMB톳E$"#M5eG邳R,ZISnN$2* _} C課\9ƻ ;A"60-ӾM(v텟 E"e!"VP^?65j|ʒ޻bٵkQC+"\:d#@R6=ʻW84ӝ'A"5k7fc8c1iVK؁Y"];1a؃M3d]65*!>?~>SN hW?xcO˲3Vlv@@DJӵVߜ1u-xl؏4$ae1 S3e1F(x-*,R6e "@7sK;,_ԑCY0X W@,:"+ 0({|6Ic{+φfLeG?(^4IJn=I/ҡg2RVRKDnEX47ArFȲ&ę^!iw{PCϛvU`,\MZJ2%,`<͹q DžO_uP`( ]þ3waS.!7"Gc!N-7Qƭy~.?DE45p CoƑd0<1  SZ-̹:1DrЙ|?B]5{t00j#S*7j7vksxrm7&w]࠵A Pxj.xrgO5xpZF䛫O{8`2F]RHK(;@32B$b7 }xM\>J#_P(qҥqUxOE`immb;/1 ?٧2⵲%l\n=q@"x1f7ʊټc%|&aw]c5PyxՑ#RU{m#K[SNҖա>x5)2,d]2!ΧA®ӵ &(nAkc7Zo Bc:\8a!'c\M,|տ8U,LU5ARGZ$ Wm F*.< .QKl̩*K履K-6 ۚk1QErޟm,ɍ^0Hmvߜ6!W¥b1s\%+;„B(؝rX˜&3& t\Nw:XIf@#XUsqZP IDHoē 57=۷ 1CPxTY!!.?$ ƒk~qt4O=ۥu,' 0ԫolz>X<&3FeceS,qUNhpCc"ڑϏ{G#ex]k7jt䐙$_*DH/v6xzNji߿ҏZL!MQ5~A|PJ>vp:$X;^tL"UuP-ɣBs%|1kZ-!TF' Wޔx$̬MWw1 a3sv\W}yy;&_cVpzR|ȿCuCnETqCiaƃVFLd79,M:ӹ`T7*`@Z2F¯2!GdHVĕX6NJ"D]:0RSNP !o;fdAaMbmBmQϯ|1ʓFXN\m}͓AP ,THGt=qdVᬞP__Z5텝W4RF@Qi~aW~??"D!T:wаoxufl//~[P(AI<}k+,DSiMdf W텹NiXb*Hp}|XzL#2ycAMߝ1ƣ/B{ ~`o70R,qF|imq#3e_hsH֔'T !Kf,tL{6Tk ?;[v a\Aax,Jd@Ek: 5[w2p }Eij@H&VRJAdžFg$5I Bu Tzbv˂G+n ` =j;N/kc]FhJH:یڐT=mYO}`&zg!* )u߱;aę9 VҧoQaCLG Q~ۏ t4M"ʴ57א={ՇmET(9YM}xн=%l;HUdg 9b4 ^Uo_;NRH5&;V!έSM; |m\rMzǹ!-mmPs3OWϗn ) FyD1cfff0J*n⏽9tG0A6ep>j5~WK^d-ֻkg_C`ձBqk*V+n$ &pouTyC6$}'z4642UH>`$9&퇱@%1SBRT#z8ԀZ'`x@5cm%0$-8S[z>ǒ <4F! ;~>QDƴ̱IR-$`Qo/0#Z ]*𙩂IJ( DYwL69Ū &6m _)|ou lnt*@[A$FHq+=Uf}ks}2\#ylbld3fmos 98z(%1p-I$&;?=[G"Y%T$ѣ01¾zԽ3⼎nv)Zbu+Kvp{dxJ>eV$˞@cH *RiUH" {[9# ҫKj=ۜ )l'l|/5At-X>6(3aFz`LSHW@f ,ugl%}QLf <=w9|j;H*N@|E dD1,іFɪHYNt7]83}^X6T%UV tfid ߿<8*G^C;`\WTL=JЀ=[U>!) X.x -5/Ay v[ 4H=p3LGYU5nN ajdĊF;w%u5tIU] ld#b5v _= bO;MS$ƺW搖 C")`jAx E &{U4@cmRmʄSe {U g/:ʧ1u7`F]z[U@nHOuFmzXT$@#};@2uefa-Y=ӾHZ &`낣$TXvaR m7m!Fx?j=ѿboYV}p,f9|TOv`cWʻaI] v{}8b:r!в] 9Wo\vuw}ywxBق7Num79_IL i(mZȌ} W3_xz`O-'#n#{TzS'( ;U/ i})b!{_Rhp} f4n ' fVVRp/Fo{w8YaScʒPjGPVECF&rK$q4/z7FS02ȠE?=JY>UAe 5ji0Br&.zpjp{E}z׮K W˕dW vMcYI*|N7@|Rf 4LsX'f[o{a \Vx]EvJ6*\c0u+D}mmZY[2witYr9޿QwsU,X9&[PJHG{sOzᖵ} 0/h`KԸqr2Ӥ2'_TLWsu 'Lf(azEXU -17G%"XuчQ Ou$W/bNb*9:d o·cS4 w\e~z};ȞcN9#A,/0]=p&񭸢VHRyruzeSc[bbˤt5hehP<{lWI[O\ĚXC6Bb{ICА^8hGdx 4V^:yҮPJ+q!BRI'(X54G3QN ;Ǥ\mXw](ǒ#nn2xc% [{j"c̊6[$5l/DEjդ vtefyp;n/?! ,6#ɭ<Fٖ0vv'P[~um|? ~ z9c֪nvϨl@JKi AH;oH4=~Vg0`!"<Aj=o0!޿Gez{*I0:LdemJPĥjM|.$ÔEpє;0Dl8}S,13Y!`Z^)L˗kU%]{`El 28˫Ңf^ůfGV cI26{{7-vIQ:^xzRnֈgC&r( B ^3`'=(TLM g}! Q޵먛O6ݵז.6]wu#4X^V -$,^6ܕӶ{_#Ld50@'u>xO–azk6Yꊱn9ӖPE$0 04.Ǯ <$I GNU6A- څqy'ܟdvI[*'YmǏҦ8$$O~BT`9#ʧ֏d"% .OPX|՘_t1sw.@%d`Ck%9e'E8i#3uxQƉ!F|wV0VX;cF끗-iI@h[?-Y OjJHyܐQNk+ 2BbBB ;WZOcH9Ӯ0֦HFsygZu -ae:(V$r,u\G -ýBJb~~ld^6W. 2OޡZl=,qϗ[AukM'U@b8?|zf'2j$ɸ+޹N}5MuWlb#v&M>%m3 G*GÍ'1uQv;Α vM ;V@u29X|븰AodnA]4g֕\$|O|ʊ'Quha[G._m^o|ifMƤKH:Li~)2 K :^)1!yty"pFw<ω4?-z؁smmDE`x~08u'X ~4N)):/QǠ";c1yuQk{c0>GQAʠ/$sF:wjO]">3wD ?o v{T32\C ;LE\Ͻg0Mк<\$O(҆{,4@RCX,y W뀳ڛ\,A%B $Zk 8FGzo?0H$ D kcݽ/QOŪ2uj:MUحbr-KʮtznAU|P{cb i\V8_m2tk?資I wqQ +mcj4cM$[R:w U`{ mwIGQ`@}93I$)m ڪFiX'AD|C ڕr]qDE o`?$蠌8V7Y[x;(V?ISs uڡ蹘bJu"1m~ޘfا+K6%IGƍMnwE'=I o>uEгPjAs9XI+fdGve=mk.QJeajqd2FSVt $<c'sp)I$% ŵ&uK!d~i& 5 oNJ^Y7fHQҚ}k:Jr>iA*i1k"cq@bIH3o]ӚNxkT [d2~uгPJ΄ /z5{_wN~y20aUn{N kR@u\jj ;$ICbYz Ab|)lD@L(q O1K"GfxBB0oyZQŃXwv<؛ 5s$J $F2QJQV(6SX|D#mNՅ쟸0'qnzkՊ(BHcD}9oui q݈?yw !6*wr|C\̑:5Wom=I(J˛EGdNRm<*9*$}J/x._H թoWo=4*8܁~mT*y6Y>Upu  eۉMP+e1'k1Jj\&/AC(GP#cDh? I  f.\YdGȈO~;68$Υ[KKKX+JQW̻,J*o7q"%`.x=3R_#^(,?t| jI6$Z2+4'Hzi2h7ghrʋRX|8\A6Y0ebv EH|7&ʬ,lHAGFWu#C`z_NJun!%HP7?$|5[j{0$|A#1NrMe(lCg2jq\j';|ieT $3Hlj#-} tjxwBp'I?>9-zhi32f$2FjCGJ߬r|4$%RѮ\49IJ([A_P==N&I ~VU6nw.e#(Ud.bT,rIZՔp}q+GeB,uOa *]LL5ҚҸDG6SJPAQxLڭA(fQy QEpH݇rޯ{3BB}l06 zy,,)7cX[ HMbw*wq^&9WZ]`(x=OO1S#"tjeȟ*" ^ܚNȢuR~O>HBA?Bu1hy\ȒIj( V#`V?ְ=JTG^G.X'-fkdS3kxԱ;Cu4d<"zW2f2$`iInM)G%^iIm) ^T:'Akj?ƱX+A`<> :HA|!XI3Hi.{bZ-k+~د|\W\s/_Ǝج-S8ԴTMo)ry`Z@dJ\+MY HÿF[X܀#n%@0GB 6Dn״Www#'ևH]WP3tm=c+,!*Ư>R3@4i#j(ܓȽv,%"[b>-ڲZiSqB53Ҫd51 xїDeW3 ک@$S(܄nt+zCO5;DUCF+VUE<6{wP㿮&0!/?5pƚ)0 @$>K>n7hu5%~S'Ǚ4s>:iY)!X|%#ml@uM:n}pW ߇ʾ8@[K$8U|Ej;mDG"zBmj$qK,Q2^ #f[#mȡ؜yRf40R Mfm$)ȊGR)#aLY)|wH3bD˨칲N럃D(4Ni6~1%;EJ>\7ڬ)a"$ύ뼄 C2}*@Rh1> zbm hf(N'M@#m2~Z`!`e4u}$kzr+!-3N}sy>B ^pmkVV$~°\>,miEǙ @5`آ"|f2Au ol;NaY.޾ ^tYVw #$Aʞ\.2rM<{;nnj’D".*E_1؁'8G ˨Y&q&Z QthoT4NѸ[™>Y8MYgS\K4cg{_>,m^#Z~8^0RBn޴qt SE\a+M94`p6\KN"uT}ǚu8wH(G.iuD=? 9\Lz Da2 WYT|TH]uRbY@-v>הkEOGrY$4Q׬9Xư:-4a$$5_ e'a wkk^bvI%HY%P5ہ+^Owg !coB4HGo.ޜ{Xk`k#>sH{^]sG "x2|3/@l oZ@D4xp5w~1b#K"rkH+]G.eB K$|FE $me4 B;"D z|j3HJ,5ᰰn#`EPάRCӼ02հ1_!'eݸ qBJ6qT.  8?D*J߭@6q QƨD-w6kMŽ5|OkYF]cs^1Ű!#0z/F!iT`'s־" , &`#n{sM'` ܵ_+nđ'Ĉ=Hw0ie"m`B'p8]}]vIb UCdNt̑,G[{{Ax35 v'jO ΐ3R\HXªn @~b<%$+5ZYo]4u;'c{v]s˧V,+WvKz* *)YdХ/nl#co/ Se呤c2;30 H,wھ6P#ߘB|Yb1>`G%47c|Wl8uċ4N-,2?u O½s0NDH昫1X/mO_6p(='8&5kAHrB@LUx?HY!pVc츙S_O:2eUydj3Egsϔb H5c{`cғ/H1ŽMQ}bըr" ?-#{> oX]\P".9eYCmZl}Ftl> Z FxƸT HrvPs.g0&sKrA:"n<~Ⱥ~Už سQ6Hq:IOTig>egX ڶe\fah_1/l@íP*D* l: ("J>\ﷷD>m L(9]I;mdwN@I>2 ~!(UX M!H f|b)E@;}Y48ftEV:96,K[Ab7I`[Y?$ڦc)ea m<M*MUgecIRt5'Ҿ4K*_},RQ$SlĬ.5 9oֲ l)<I{ nyMX(UFϕGgkX[}^S8Q H0oR7p0+,ˬx+k5 L@hHMk*Imޣ` צ7zHplvMkąMETړؑ*vƒT஡4pE,̙qMMpU͔yeX+WW ħt1,F8q^?`JfUL(ͥu1c쑋x:d$ԣO#K~3:1 0Xll˳LbMJv,56tۃ9/#8u$ A"Ai R~fvgHY2USk`vFz!HJAd{ygl3Ǎumx|PŅڋ#Oa|1)ՙ2H7 / iH&v4Q)yU恪;uɚU܂Fmg)ԕ-kO¯Jf:O~)O9$rW]V+5o yYn}ǮιM$^$*_@R v"Nԁj64ٰ=#:m:- _M:@i',lO Qq Rukm,")zAյ[㬱/xN;S҃%*$nHsZK\jb\N".; ۇ) jMKid^L+H؝qS\Щc1K*1,U7 lwf2Mo1 ͉{ؐc10+ŁuWʘ|2>?-2>IgYyvqLɀ~ug?ky'%%5ybE(I Zch>f<`8 0uGvqct5O!m$<1m͹ePf8O`Tp wuy/{Ʊ='S#o?"ʲ:7/c0‰Bm)-fotoxx-18.01.1/data/patterns/sand.jpg0000644000175000017500000001041013222767271016056 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?`GU+]Nn ؁@*I#lrU7g40ʆWlO8P뒧wA$cYLV R;WIr n9g</b""HV)P! :}֥!!v2ۉA\#y#<5˲D*ƨX$ `rwgxc@J7 1# "VYyBBT,A gG1,fI&H-)!7%*#G%vXs"y眇-%Rp܃I8a2pJ˸v~cIɨg *wU#{gh ] }%G}tp[;+%69\( @EhD&ʴ6K/0x`RG4p cb't' xВOxARc J%8:FNzOnԟ]@* GN kiq)%}Jf1A*= 6L\lÂČNv jh MW\D[%\fId;q F%Ko/T@QAG+Ӵ~H;mer[hW98;--*#ʜ0Crr29k>- cmlGT(88C {kav4B@QGB:mqФʺL'oң#\g@]Y]`@B9vpzw_;UCF F{c&dxV: 0_q+ʀspA[2CjF~8JS!$أ|lpBQygG)T1͕\0c2<'!{@Xnses9ɫFd_56 py0y"N 16Tz- 1EUnF\ rO| 9,I1=<u9lc;b.$&tڊ$i@F~\t0x猓 c!$yJ1,0qOթw3Hnx'EN91tmDRB2J\bx#'#E;֥٤0n p-'зNkKpbfnz\AnmQ1%"FAu'qbVΒd!a* '9H n5åmhw6I܍ 8'5T !iQ\T 9peާk`u.@ȳ$fIe$+8!8x'` pD1!P 瀧rGlqbIHF˨Ɍpy0z֮-3]#e6s׾s$C$b2U7p "0Й!d w: @ #$-=BI CI;X6z cfe8L| Lٕ|pOgYK\,][$2#>pH;Ko-$Lr8T;Vƚlj2D3dOPӔ[imdvl䚜/eqo< Yf,Vbd_Ԟ3 H`a7RiKq+[gmүVړv:wԱeqҩVNЮo6}t8l'5-k|/n;FmxNzz׵ #k% ާu+JQ|); ue:Z[я,y'?ZκOHbӽ>֐vҋ&w]=m}Fr8DɌ$W7h'@t;5A2ܴѸʦ OZK[Ƥ]( =ZM"Y?02v9=zYcןċ%=}T_6Em~r~Hc ߠIH |H@$xLǧ^Ն`2%¹yAt:uh^ +{X2 gZ8ng_fyf`P-{m|W3*C|O7|fNJ7vM|c/"ȡQO +{j/{<ђ]X nqK5ăJ"^nqP?\Up&"̋ՋHW$6Z1/1n`F=f[,r41,jvm-qGP/p'zampCq2\w Ŷ~_U0-SqH VBy4].ig{K,x9= gnAw }1F@֙\jzwpnP2) 'LnY'a"[K1OaNKJ4].Y4]AFy.):QLyFs*oQX%yb:؛j̩47Yg @VdXJc\(Np#4è csm*B>D٨A=K+%у̖Z=$f"94$6~w-jCh7K(8AmuisI +JwqZGg]j:I7l_Pݞ-},d<Ҁ,n"?hȬ|&<$w=tQiJAingPJm.ZdKXae% aOV$pYsmkz0~i6'hlfӬQ[XZ@ll{{aY@ZYd 8 d=xu}TSvP6)z3U bZh֑0$Ow($dulsCZ|"k9ocYXpxotaԥ8 ,T>;QWӖ RF:35WYT~c\6KH#|#_2Id=1i:%$16Y6;YŽ0H3gҴ-y`I󂖚`:qzPN 0N֬K{v0I*m5\O~5PBƃIk\e M2h.1RS.$^f81GQ_ʖVrV `\zze~mbt$a";s{:o {B$$t^Z;4Y^9lXi~0no'g 7ڤEzOetɭ tM@[GuyrnaG>~TjOqXP•dC]$3[O9(J; 3F2>4JriaSPYmɩȥU *8kpt鼹f*e‚bVfGbu-L#4ƫ-Ԛ{ʲIJ_NZԟ6q.Osmomq[M:GkPGB0yZiV-6Yf_( gz/zuW:Xe`FP*ƔTG#>o J⤚H]9K3c<j}skoӥ6xau8K .GVm^R]&-"B>fk+NVHNuVb O֟ 4+ ԛY {ziPV:H$'qPi6omwHF>_lsԮeM'M[|P`m[:uƥ⋏661BMdzU+m 5‚МL?>fotoxx-18.01.1/data/patterns/space.jpg0000644000175000017500000000434513222767271016236 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?좊ִKجQS݋"Dc%cpl5 vm΋Y m2OIfV "Hr ERyok\H6i6~RX|_?y6:T8Z./1(C'x(~4\s[\4InifP a 9Scy*<׼cx7c/tG o%Lܜz R4e>rooa{,bREeȱ $##LΣөI)#{T5҉aE?6%]pN!psPKX-l( đQWbծ+,NGkCsr~QE4ES(p&&gaa!E2(UwʫUl%Vw$1֣p}=3cĶ:{ldDX.חp9vM)>U{\I\*H"3J:Q>C+I(+61ȋ$Nc*z2H oxVmQm!<A"gn+axQ,aX]bB°5K no]<(<  @- /fڪ %Yȓ SHvfl1fURmg+ŬDʷ2|r&FFa qL"'2ki#TPbԡ3 ܿ#Ƽ)Zާs\֮9gE I TII4+DPu_So].iSjA˓(QY?Wg: SVV$,ԭQ{=fFaΩ!Gd(Z)@';uQEYMBh-3fPT,oUčV )`J.ߠ|?|WvئxA\I`W_ߊ%6vZ^EID8>~W"䳒y$Yϙmн .MAe()c5N?fotoxx-18.01.1/data/patterns/stone.jpg0000644000175000017500000001311713222767271016270 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?k>kgov Uyv܈"uu 1U9p|3f4.nlW u1i7' JtmZi %搄Yw9ʆTa 8 {;jV+=<10[Ƞdf):Iф],ڄ gp߼Nj' ) [njq,|˽W# oڬ$W%īR覨e-J !x&o8*ʎX0a=Ei59G )Hr+~N8 (-_j,8.bDAm@D%ն6 U MB+&TUUدIª{+ ԼB&D,nm F8GTn pWE/ CR;ȄI&ʅjc9II@KU[IQBWM]A d pTдC62% )e+ ,fYIXn'x`Ndsu$VD. 0Q)%N8ݑެtVKb'nf|aIrn 'iF p* ;_d$#XYd؄q(A)E9tTYko4۴o2H՜ K2QC0nF(#FwsKw4P4sC6wm VRc\tZVؕbni;wè$e  /h:}/t"$"y! *ʥ} pTY 2xvm:;#:[;|'ˑU,] +Q;Y2\ÄY]9`T&h̑iW6is#)8*Z]wi# 6bP1SN$ M#үbDIu [$e#*ea#h݅&4} 3JőFR\$vf,X39ReCK+(cx K 8B馳7).-.Uı012?d!r#ࣗϲ+Y{wIQ_ɽ&a3H.q ju35sڛs36h&c!ԡff8#k(dKCc, ^vi8nHh[_4@ݕqTe|=-_?I-dO%Y}9V-nۡ# ,bE\,F\ZrS Q Y-jw{iO06 nb27N'r #J4튾E\YJLLd\[Dl70JnBNή҂X`Y^<]N^x0\Y^.#X)BM/6g,w[d 43D0)N͹>dg٬L cˑ#1HO  &;J}Fሸ[y "iK0p[oDIvZ-ϺMb7ˆ0X|1=6m#{hn"[EF\ . F KjwyZ#F,Fw/ Oږx'h..9`[pN =rY[Z.-d ;.'@*CBDmn!mB UnKȭ>C\!#,o*|PUKkbm๾Crn$#i@\lJ A{Y[} +ZǪVB3XrwnZRW@-'}RK۸5{#YJH\$1cIJ"A04piJ X bG'HӥK`wIa+$#XRцK,,E2èk}*TP m=͕ad{h"IBπI/`9v$@ho}29n, @cT|d]1VẐF>r=@bKF#X#T`g5==.M4&heX`02H2V!XnNE:u!f V@3`%eu H`.@u 4 .$mly,ٸcmRѐ Io3O /%xjwXЫò !+o^j"S`gQ:K9vɃ!P 8-;Y&xk-GXڙs $dsonZ 6pu(dHg aU2e(]գ Ow\ kF|Zdos+$88&WO֓wFH`Y+9+B;k S$ ,)<Te$We?.oDWb#,m2Iey$ Οoy,r~W1JTlBVB1{um e@ ^DRv# M!3KR% n$J5呱.O@I'9wO gƶ .x!($er nł{9}:GS `%7y" g Zt J(Dwj[̳FeV)c#e%L>e~8xf&H 5RBJ~n\)#99'ޑ-APc B/0w)ml>l@Y*[#P^Rh.< ڰH爮T3n9 H'YfX٥Xoe,H- E&2Z/#9.dL[Gbc}N.m݃$s9`xmo-bNŠڅq[,4ҥuU$n!K dwKqa j2Ki=Omm޽I+bAj-tsql#1IKpȱo^B3 H2ՒT[w'W H\d|sFB3ȯݭ .$VXn|Ĉ!X1 *ӾU(+d'qS"P"A<-sl2e]1 )'ߜdPFBvtfcTQ(rW6ڽX]l%|J/flHu-{QFYu/6^N]R9Cg`JrYCwv221n6a$O ;/BSoMj$D[(UI2wT_y>V"V 2i98 %I$Su(ln!BٸH; 1 aHizvrdM3,.I,ĝCd/b ՎxѬIsLel]%c$*H!wӫG]v+,ZCwY 1E,j)5YIPk28wIz6| JsCi7i"\F, (ɇP)x^ *xJ쁂`Ȳ`xdB51tmgLkZM.(]5HGͷbt jiڥ4 ֍la3gs1 |_ZҵӃmC,Hdۀ,*:sa-G[6E1#(p1W&%{yBI欄ܱڥHITP݌D,IqɕwT6 0ӊ rXuR;F!42G[ yJ [+LXFBɋ0&WRw#[;i*(̲evMwH,1r돣wv.B\1>W r6`CVIx5[i#Del;rhA$#mKwya0yBm Gvvjk\4aP(# g(ZV=]^:j >/.nW+4/ &@FU8`Åfotoxx-18.01.1/data/patterns/confetti.jpg0000644000175000017500000001515013222767271016752 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? <>kY4SSu$ɖb?9)`#$k7XnEi綌^IUW#'Jg Q]۠KGp#\k:cic Bє*Xs+\oؚ\I}?uZ4}<FkV]O7,?uP)QPhrHz>%(;ʋ5[G<[뎶Ƶr@Ĭ[",aW9/5jo{s%y+|8;TlwVn_ 9ȰF-G^i:^jZΣ!Pr0cp?!^G64fv?(aǸ>¾~eĖ~ԮCIQ_E|/.4{EN@;!2y9⸷/ BUh>h6[OKh} )\]ҫ_Tm۪>*K#O.9.aPʰ83yjG4X]F'`RXdh]pT+.?ޝ{es;=> kP`Gvċf~+yဝ/Yt3ϪaG8ؿqc24W'Ҵ{[봂d0y;km|1?p *%ƱXqקx/|SKmF{i=҂z##z =y3,#$H?kυ|1}QdP H*3ѳaFW5Wt]dfxcc=QZ|rZnzi}ϓ~)]_Wۻ]Rwަ)8⍘( ,rI'.C44mbXOE}x ~,'XȻr;`c&ֻt]bJ9GAi{dA_G}~WS8cs^Q|${9/p hA$'V`I}.&Vvp{i|316e.{$-$i"fj1O Z7|wNw\3trQF-+ƺw"\p@o ;(5KInYRG `z GH}-PIoqnH2T PX⿉XIk89OΣZb'3gFk+yw*m\{+]OY:4rsկo*|? ޚf3:OwFIg=j&s8J]1oԅl> *ZEεh+k?SQ{k`TA(G8#'8',~,M{cM.mb<^a@$VY[q֝xJ>j6tdP5-K/ž!REbna I 6{bjsϖ6t3 9{h]7{uOˮX7ͳz갫ZrmVIZ^xW]. M}ixF%G`Q)^ :ljuxvogHjq%Qn#xKIGJ:{hƬFv㷽~y_ CW}մk4 Ni4m,3̍|aن#8&ri=]M}?fVV^ɫ^)9$O—~$fkJ=d`G,̻gWK|KLW L8b1Nק|F|C,C:*> 7ҭ9I˖ZF:F]W4EX 47mY 2lG JulI r1< gq"M'+x;]k]Uݤ>g|˼Vudk/^>kIim3;>ݜkv/S/SW2--ݭ}.gOׂU9M{G̭fgmm xk34w}& WMMj/Yk".ZĮb'=KP,6[mm\q6F #1jªq{4۳|VLrYJrmk{td MAfF cϋk[jַ0[Z XO Y!w b$rZuߊG4?xf=ޡjV[,h*n e9% <9.R=j$\z߂a9d14,\no/k?^>ؙdy[)!xü#3nx#oi<9cmbsz֘@8"q?P]KOonmO+eJa_0nwjkֺn~ùNWTqWknS xVʙ>8xЊOڿ↗k<d[UݭťvN X G-_MkM6vl{T-$ >k |.&i/ .~s`5tȴ2]G[5Frw9$ӜoW4i1Ƒjez)]A?e$ێ~g<-Wza`uܴk} ӢH(q&@~&T)ׇuV+zYOe,.[I̤oklx-Οw-pIG}7SpD*m9_ Mjt0q^:KyƌLJkoFƈ[Or6is%ő!= rq`kpah:mO\wVB5*9%7[// GwawmuG0{Y؂2(s֬j ~u%>ԯrkg&pq>M}^hVk'''q}Xpgvu8$f_Vic7WJ.P-vow}w# AUmG-Zk>%k?F$X%!еrmRi<Ipe ~:76q{,~W2"\wfbs_Ṉ]?m"ڭonV#!%6}q޽LF/jʧK_Wmv6'i%E%fK_|J _mKKKE=ssy(cٰOq_.k/7^BjŸ_:wW+whe\gnK"ыDXcr#xŪ6_QmOB1K}KUɾVcЖ37.T7c h\Z+]iBe,j|bqXTjQSW5BjŸeg8 i$(=EtS-=H?hqf#c.J_y_Q"5|`"Ԕv_W6< ⣀!|5'𮱢k-b{u?|=yE*iF3MXJ5cY7__Zx8o]FgݸZ௉Nr3XN+8:QvJ&K^CǃAxK[xLJ\5 Ğ;BD,biEPFU ւT3W>Kt` d%N w,k&;tFsĞ+3vkwמ)%wXI>]L ͻw)iW6.U 4ӓ}M/Epu?o^RZkRsަ?k|kM^&RyU[U!I ac;8z W#HNns6K9J\k|_}{T׮fՁn@cvq@6mGB.smDZ_ZI䍳sl]wYcޅox'xM8A ɑ$N.N1Lz8G)s")0$Ws?֩>4r{4>$J$=:e =XUq8]fo.lwCp3]Nwf?Ҳ]1쓑ֻLvE,Mv$Q!YĎ4 ?/ZEd"W;FPt0aUJH ^]k"VlAwi 5ėDc$q|c5QQtcuue/i/Rƭ~O&3[^o'ޓHlmXl_5ύӣ֍_\ǫܨ ǀNJyH4)l?ҫ$vmEs?{{s'YgwOX:W)lq]b[:oXc MaXݙۧ"MWMbs}S-p]1JCMu{!"\gƻ73=K͙ UhWnF )>W¸3}ZJ~:$K €kdWk\էmnxLaR(E u奚aN1۱LyM%I< #ƺH'f6| ֟~j-t+UuL5JR^>KZHK>$G cX$ ]G` /Nj';}k9E+/)6yI6޺o\g]k|Hƴ<%v? lOw$nU k FgFPкX*)IWzk90_~4 RI 5Ugb$W<[6¤C˱ ߉yk>+A~(k?5utM$(Q>Ḭ́hJ1zuUGfotoxx-18.01.1/data/patterns/imitation_leather.jpg0000644000175000017500000000643513222767271020646 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?4CwRXg{qb^dY XևLW)" `5\\βUd`z8L v0󇁞 * 럧jȹv-P ''9ӶjI-G! Zַ7Vݴc{@tLAsjLAI}#88?lNilgXN:zqKKI&QmRpu:r6aG;{P;i D#iO|=M2Ip'>}HY'.`^84˫xXqkX2W =>aX#n*D?`ҡ[ٺ#IFK.TV.o娎H\0̋ ]$Fu"FWPGjP.tڦ4`n`sc4*č9 0OπYܫae~1Y %Ƌz`v 3n gWq$,@G9#jK -$8-#FvBZw98)@HG- @ 4v6׺te)nX%OI[iONqNkԱ0lL`K2cwHnᴖ[6k-rGKst{fP[<N\Cm,@ S I= =Q͵øՋwcT p6_| y\I yRec$*ǞU;N)q%텹ʂ\#ۯo[j 4 *7.:m!p?NM:;the:v(+,QdYp UtViEie{;Rí-H c֣4.~mW=\qϽJ51.s*"y :p qH- fF9Ѝ]HlH⑾8>~4mE,rFq!|Hڬ%l \A?j5d%FHdLm%΁>fHBHE7'ƒgYU8?(#:$`U7"eF?Cۼ-"upH>kR[[Y|bQ"Go^M$qlVBzր$q3x%qlcjhf3 !Z }'J 6}^ 5 |ր!D!vPN9)yjkchnU'3+ed9}Q2~=*c%Și9FB/ӁI_HGqs =}9 tG   `ո4V#"eOd׌{ΒSWw<b[IoW^j^Y_FbIa{Ü|=jż~TK;®w zbi#D`|_9P$ $Ũ5w2+گ, Ju܌QU3B6Uq(Yb7q%.8}NyO/[ʪޝdз_gx' ulTNH=?#Kwy>| mYc۹%tD;Zmz\`\Fw(vN4ǜCs DӞ{`⇶m xw +m>9-yH'8kaV ^R9({g>Q"%zu$`*;NK&,]p 9ǹ#km[XHqX/U[&;𳪆y;kgSay}"HԉP\IXX|;*b=č,re~le/-x^$|kǎxC+O $w{[ r K t:`,O$Sa X5wrޓ6%j:j~ '̺z;&˩%hts6m&Xxo&gq[|P~#hXf> m3=xkT2|9UK#SHӭ.t@PoXz>|AԧV}O¡CuGq_Sr+4{"K,&NN:v$zCu M }N++* {nF һZhv oj/5i &]'#{h7wztÖKdf:#YYjv7Nmcln&(+(zx·:n{ j(̋ݷnVBc{<c˞k5gx֦6|#m'0 [2,[k7z}r1z/4vkŻky$*Akv,h65 5BUyu:Y|'۽Cx $>4 F<[wH"fKW=W9a=&2GLQbBfT `xMoh,^Q.{k eRq;WmR $2D-;t5ROxkA}U5].̫33^q {lпv +GbԎ}Ꟊ_CZR\}4|oPmmK[Yg>D;8]H+_V h$VqjgGh/`g-w z~9ֵ Y{U񷅮=F;g`6 r0{Vd |Ik5|DL𜎿Z,(Х̆H-8*FV^|M_^˨[Ki1!`"|Kb[_Ad1&%A"Wnߋ|. g#z?;ڿhc{ڰ,~$fK/v%*6gGz|dwehv[PE}Ŗž2 Uf`=zq>-oD0500 q;1t[fp-.n>i T`89Mu:?|C/\O\Om9Ro:CZ-{Y3w^98:VXk%;\p>'*.>/lXoAZѳ֑p-7&}2EwڗᇊdU[voq]p[qU5O x~">xAdqXvɵuc_BiYjDZYU u9SnE=,ufdIcxcW;7kCÖ>8Q5M$L "`Oץ-7/V$#Ztz7,MPDdr[}3Ti2sXxvh3FF=+ 4V_p#FGC0yg;\M#-Vieq%N=;Pxk_1ѵiw)ee,$v0<;7&b+#؉73=:V ':⍽͝Eh1ev{kJ)/\-._E)@ΞoL&,+pw E(LgלcLc= %ݤ_L=| *O$aGR мl5ţkzeC%s1ph:?[֒hdЇ+ Rxggghv/4q}5 `fA kVPR}9O+Oڜ7գXE0Fh?5 L q0'@OCSx|IǦ:Fͤ$r E\w muF/Arڝک +LgƑy۠a)h|u,u8B'[& J;dR88J KPm!Y\f;钾T fKC(|o knLx!D᪭׎5-Ể+, 1Cցnj4*Ax] #Ilɧܭ-I nA`yV8{?]x٧.dkk"2.8ϯ|6G#м*\Bŗ`SZm-nhkWG nyF l{-)gzSbys :<[RD#k+g6>2f7!W{SE{cS☵aZz"Y[ḑQV>?-1m,zc:Z#+x[OLp~b|Pޛ[]Oogo>c8"SYT@z!HumW&X]+7 dt?); |t%E* w$IK[xQgSUzKn6cš%&=WW0\( z >x?U=Nj܄+2`WZ>ayui^6$}T=J|Zҵt[kYTf _u K 8>'kObi-Un_~jko~{.Gu[Q";04+ǶV:d׶t 3\G9^e3)e1s@xB]x]On@e+ӿZl>xcHL[hPƏ>C[jpʁI;J/Fs5uվop^ 'Mܩ&Կ9}5xWe3 @AeK:>af lc\1d<7|JsEu=s$WaOa5/>!4AytTI#@ƅEzU5`bFx}a<s__jC{i h9T!>q^fVOlH564JxN="BIP}|У>1 ڨm돘sK-bKim`k;Aio}"Yn*~$sK_<GcA(@М(fotoxx-18.01.1/data/patterns/pool.jpg0000644000175000017500000000763013222767271016114 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?k WN_Sx 'ǷVt/ *Yܓ7s>Aej˸֧Ҵ Rrz{uIb"FY_r)URTVhsE.u10Eqj,׎]![xVTFHS5ieviNw}}xҔ}ձnlP6vdW.}URE gDUIHzrxZ\x.i-x5 `N-X$d'XqN4I=.swФ7dOնJ$a8M`\\T{vxXKo=}Ir]f.`%GaA֗0Q}?C]|1"Dۈpwg*<՟YhZӭ"Bdlsa\굤E'eR[ <3`ti5-6)*Hvq8j: @}֭ 41haɍ OUv{MoE xX%%Y%dÚTrSXwc#`FyVm.p*w?^}+nVմtļVvJ]#V{x1iT˩: RP#=jQvl(I6c suB]d'^Mb7Ӷ6@9ik(*kqެjD0%ղkDަ|?֥YT0=Q3I$0O{cṮ25&m&I_K)m.7\mӞ9=*KM]BXI@8?Q\(Ԕtߚ^ۏ7P2ĀQxm{E*8/.Jɷ9ͿM,ؓxA9Oz9a[i?i(jѽxi}ݴ62Z[ $H5c[2ϛ !,^k.4xGvc9Q13zJP7}Qqrz.+>smje;p² mFteC%@1״]dT *Oy?ixQQ[}2K%iZ`O\?j[Z*h7ڵōӬ"W"V'Z测^YZy^Fr=? qF&I0ON+cT Bxe)s2mjьg@GٴC>{:/.o \[Fuij:-r$8ą @'ùgRNd‘EX^_L`[D;S3̺vPQ}N o@~ Mz}sTPd F=+׵Iw+K[{jM=ee19A!Fq%R/܌t:-1c˔&#c*NsP_ǧiZD)o}`HC.@n'b(d`g#Yi$h%hRaN}iz-uYYNKi5T%e@~+$N/na'A G=}kDW!x:h[LD۷sNlsio\\2͑>{bOvq!&VAҠUӠ3yqY6'DkD\jhmi>7)MK#1ebɴu8R6Wz4#HEHeOΨGmB[¤6-tUcYNe8ـyzw}䨿Z/ ̎QlNpREc.xեyaW{'>|Rwf802ݕ8sDv" ٟauRO=G8ՍFJ:hf<0jN%E\ᕹ_ʻ RBY4FaPg׎9յ;Cz!\+ly@n´WXUK]N=3Q99).O~k=ʤ$g# zߔ>(qŒ[+jr7OOӭR״˯ "-Iy#޹T&8I{"ajCMI$#>fGC2sU+e6)v JZcBF˃Ųs^E9N"O2Nj%C/%NV6(tty(L0|{qϰ 1;0ˎTmf/K٠#eA+W4;VLFű1Tevs÷w%F$}im5%d<6W 'ԓTY,.e$4G.?E.e{ªK cTӼЎ[ p{TaAbЈu5)0GO>Z\Cs;,1_ZJ<ڞꚴg>Dj*cp8uڹu6vX z~ Fj^ʄ%’q07=:*Cujq54IM2a:JΗ*V hirr3؏Mҟ@\f~jQOq6rQiu/zDI8{*_iRon~AewƅZ+GFv>4C\'Jm'}#Kӡ/$kL**Tg#Ozi$ay6th"P]|\BhkO$__9fawhi$'`ƫmM{mPrNl]C[tmxjcKGTHw91ڬ\[XiH#ӕJʿ#9m48o⸕w0H<^[~\o BLԥNqUNRqVeC4$*{O_SL!LJ\@|26Rѱ I?* +JReұaSYZ]ߙsλm폚cߦ+BXhM|#^kW7r\H`L>R9*i9Ң,As4:Sqq8*jZ:ެCTdt-Llr@lt Jx#Mu Su Hc%1=Julg h"wH,9g>snBC[h4t5݂*rI2IMYz:vZk-I[q-t{y5gͳyyT6qtfotoxx-18.01.1/data/patterns/wood.jpg0000644000175000017500000001330513222767271016107 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?K&cm$``P[ QV)3!ӭq ^34[9ʒQNAvDtk/3߅Igm!ۉ.he`־pi%*l{p@QNx_$؎R pՕn#%[ݷ$qHŹ0æOgN]jpX Ģܰ B)3XxZ/#Sm0p]N# $` 9m h9ج1Ñ >uku'K SWcIwak3i }0Bb4Wyc8KO+>Nh™ ִ':2vNARqq/6;S_^Q7קiP:]sv;A$ MM5*κ]( v0z䲞TAt3&t}F)8 31fEpM6cJ:tk?0B@=1铪>kF._mbU֣KF:]x !Ж 1rI9 o5yXX:vyP TǮvMGH,rIak1H>ܜ={n91)e(]<3p urm< n+7S^1k⓪پٯMjEu($o`FTlg&ǸCc1k8l ˓GdWXW1ݥR o"0|c*6ǑOJʓiRK,b;8;zP⺓oHʾI'H%i&3GUS~%M%0< &AaAF`=U[KK#%lhK* 2@;.Nwӂ#k6ϴʻ:[nw 9Zk2w=d-wF eX7Zv?l ăD>4 w++@Ԙ1`#~?)9 @=fUڿ47R7r +G:w;i MxwiNNR8G.. wmD˂#xֳiw6mhd]1O]Bl{Oktm :~'R 6v,@ ؒ3щ+xb `F2*6z\.R&1AA9JR*S4ta FUIze}Ŋ  @oQCl(IQ:4hGx`F䅤s:p'V<~@Y,b͡ޱmTqhquZ+ͯQ˵fI\r J Rc&֭.R4C+GԺ |J:s$eIRr^DYh֌c,l >U[] FԼDcv!Q"pD >۔RgyGg?-ŽF0{퓴I^;Kt$@2.C;eXrsytWwk! II{/d>\C Ec` MAh8VKn|') .hbYl<9;ee,XD’sryn ate `4 36p9-jUqekh55}g\MficPXDi$2Ik2?\K&I vDg?6cVQ!О, t260rQxcI95h&~>z"R;]˽s\R#X땜i̴!ͫaPFS#/PXk_2&gy* DgَЁЮm)mFdGRU9H A3*zYQK4v^u]hi)(O (,da2W*VrZYM.Y]KPꁚ0KP2J8>X*;U=Atf+6"j)K+|YlO\Mqo`4["3Uv$rjQ"/QWsbEHF.VLi#-hRA+20dtz~x,6ci ٨^^B8od`:̏ő sB8c$0&.qО{MNku[@3 шl98$qj-G_rv?šm+ wё68>Rin4|VR]H[?9 Kn0f Z?* Gn*4voUgG:ұ!ZfsN1T⣲{km{5Z%Vn#![Alnnў?7L};r4`ˌSIHM2#%Åp"0Xܞ{ľ$ɧ@G1UZV-J'9-xZ]N,~a֤e9UEc)x I$(ogH+M*#f/AUqm{X]rW%\yTg/7${XʬK!GS:MfuG4$U-uU M%YVf@ H/bj@#v:ȉn +w-8V4KB8/=c]eqXd4Gpwss^gBVB 6r˷{#6_ p8<φ_Q;nM'H7F *H (;(`\w1і/-+hZ3xH&Pwm>=x2awxr)\mmۻPxO sobш<1km mdq(: 8y;XN&ehCM$0a`?ʌ}5 3T{f3rF3, .k] #纖Yʡ1Gծ<ȣdvAK`HV+%BiUH8]+ 1I zc䊷 E@>(T2y.@m9m%>H%4 N%IН#9qȮd 2槪O dErVm+R"˨tD~ avS`5IGBHZ6?moH{#jyaɀ'%?xr.ЍW}Q]mB8O#'2L]$- >jNw:`{9{H̵8I[s6>_jAmR @ 2]0#n00z`u-\ג]HW:A*6HmP09tbbıyEԱh<sۜ)sGEi6?fotoxx-18.01.1/data/patterns/gregre.gif0000644000175000017500000000560613222767271016404 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? [.5#Kg4)f}˄!88$ct+S6"#w2sI#Et3*U.ZFQ xHXA֧&9Z!I1ve.J8fFemHV?JjJ'2ZsriG 7;IoQä ~g0ޒ,6{!9d޴@DcJ񭿗֑%_Ƒ`"xeTxgCEOޤm|$y>h_%E#[ ܍+Fw<#NTѳ^qҀDJFb36_*+ߙd,a9`u4m%Ą$o=ƂRH1@7V;čO2 E9S|ZJ>80ΰb'v?1>޿zj0݋pY8*HUb'Uܸ?a*2Ito_M!<\)S:8H)lS֜e@mG*`AN]Qˬ *[9ǸJr$CmlHzuY`pT[@>csCg =Į˵}y~q{bZY>_ON8Ȃ>i`;TDA`cx%YqAa`ŝO~b}wcs k#@ eXf-Y]ģ:°FDdM;͘ \%;/jYtlUiRlH:yCbd*݂)U -h?AԭwR{!ZA;y`m]vdxmFI`cT;YVF@ -8j:TZuΛCYM$&vgvB1FP1S Ep.l}2)(mч71edPR۲K{:hn.5"ºHFe"H$91l2ĬyV%R/+8v>(#ctkRtppn{+ N}4#MZ4Hclq˸+vlZ[;rWg=pH_"?0 3w1b*}^ׯf[u#o{El(QRU%[l67vs$qp"M$/u؜ɷqb6dG#}^;[ hf3Bb(ۣ "(QQAGåumэc%Wʻ[w)\`K2ǵFrU@Q!bC]I,$XI,w!2mdTx5&4ci6!㸑Q^4b+K)ʆ xYD.e4m2QUcn]T^1ۛ^t.<6:iYmI#R[nN 5=NM2f-)dg ,4'  {-lbWW;&F !Pv$t+q^](U,i!kғ1;$, ʒ9' 4۲khJ-p:M.G eLmR1 Vt6Ϫ[EIbKP$Wcpq"n[DKJk $,ލ,8*։yxJmlX^Yȳ^+G#,HϹʅG\I ݿt ooNMKk*79DrJ,s ۱:hZ[u[(m΍HU;i,'U`Kk[ M $r[n#ec`*.21E9bF9!&I-]$!2"|8- sڋV+tEJYgIQo!I0nRSK4K46JH]V3NC!*w3( o2g5VDd@w[Y[٥ƣ\,mjvr02O0cj1a96s=ơzGB$;B;ٕFH˭]}KC%oZ``}"v`5ݴ#ہ$گ֢ݣ 'y'<|uĊK "oK'U{p-.aR!`Wq 凇mr'B\* vXrHF]HcTգkyxnmwsyӬO+BGd6I^ 'kdAXuikSCoTUwe BΓb89&[[O_#@F;q j6h5ӭ?,>I]yChfb)^ɿ_,{D[o카;y.m@f%UIBFkCx)MN<-ѓD14pi;$+~Rۘt ʛڭ|mP>k+Ny \ydY%,0)E>נk2F(YҢv|aYU (v/jWWWϧYOe!dtZE/4fRۛfQtBmD٭͜IȚO2|>TGXecua{wcpc۷?29.Hid1m gJ[M9!#n;q gTinB.t'fd%%H4e{wkwocdeKU, dylLUSb5ꑀ%#ڥpQ9*$[5s;>\Q̚_^X\\VRSd~[1ffaë2a/֚t-!3ݬysrFs[-z%Ү %CYxc)47y(~aIS CqQN/,}' ^8.%u۔N3 ֙I.ZlL%-wg:I5/r3dp2Qu7`It mR*;-Xҩ 3X{{)dki#,Cp$6G+WoY6Ū)[\q(ʤP7i_;4lܷ:߸H'c]Y'v`ׂcZ46cF;X2*fuP[sgv!'lҕ;6,`׺ K<S,TP.cJU8 .k+/YG4k<3j$)RҥBN"[Zr4mH%I:pRM+}p\YEiim.㍕Ycē! e،ٔ+Tf40\Jm$I*E||40Vi/{wz%֗' hϔ70n9f.|56oEtۙ.lM Kl]o|C.qݲ]1߼u"bs 71:#<͋];̀]HN|ϺX9Jɥo>kXuƵ}BKKYgg@Hġ+ 3աj7Rl\VG ˆwFG B[RKS/UzzĈCi-im̊Kpn"$)0^)~._q|i1jMc#O{>ƖVC4ČT:jt7SBjH".Qlel+2 +X$ӫL؀Ϫ'U!cFU!I?d6Lک#)XC4rl;)U)_5Iق|`քt[\$$WhXQTR^tgZ4/jrӛGI~Pa);UHEPK%qd e{g!kҴ_;Jb FT Ow2U74wrZiдF ]ᄊ/έ͌bNO87}i>Fb"aal;B g 6`UY#7@eM2B2H]Wmfin;$Xw#p;F{'FP)m6ؕ03HobcHg(Nqm:2Lf-Q I洌lSy*Wi0eh]YaԞe3yp UN$8nrNh{%pfI  mmYv:}fM ʋ|\ a12I J3_ fotoxx-18.01.1/data/patterns/canvas_blue.jpg0000644000175000017500000001032313222767271017416 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?CLbdvS؏n*^Y˧}*|٩ J|Dށm hǩi(R=I>SHO@JϒZp/<=\J~5W"ZO?a2-̅^* ē[ChY? UOw0zAi&'ץWPJOx}M泴H}1y+א~)v1hYm[*=#=^.Ңn̶Mi }q}Ɲ-x|kވ Ycؾn:zU ]. XѶ{#W?Xuh >G5xrf%48dM!,y*X%ƞ'Z9"\9?Zϖ1 έCo}'N.qެ6Ṗ6DOR]-(5»CI F^MRvxdcT}{ֽa0H[fۂ یυWPe\LC{;g_iW2FF 8BT5O ]-\jWq29>yuoH-qnaŬ-6%dbsT7RӦ{+=[fXf1ç^]j!]v`hi{w=j~1/lb]{X]#ݮ3桮[JI{O]!4[\yΕk{ZގF=Gt7NԑF$jpUWN_i6tw7zbN9F#Zojei/}N:_X5ZF({\Jek;PKM,T_$'ō >0~y_p-g~s(.ҝ#\vO|Oi6{ai&Rt2H>⋤Z a;l9F^n"yĒ.QKZ^@[bi qGW {|pˣq}p +"ZT;t߈c}ڗIYLp>l#kP;ww_p-_jS[^)V3O6_ 4Dvic+ xZ[e94m6b/&Sޝ?2F5Vn["Iy]0e؈$42DUͅgur^{zU$7?&2#_~;VmmG~T (kj^%Em=X?}^raw'e{Tu/t%碩< #?>M01Kj~o\}jӇ]YPڝ{Oj1H,kjDӷ=ٝ*Ig{UmKHLZ} \B}OPi׺EռŲJ}i;++_pŏǷIienem1:s֩OOmz0xM~,#g;/z̓UHD!Ϯ9NUMԢ"{ o-{K8>pj׃n5> u?S`v8'9M Ԟn]F1 c2}IP5eN0V6z${ Z_JW 8[ ddgjEЮ!znk׹7(;m6Iu$<$b&n::M%/5-bb>r6H99t0R_\jm&J+"D(AZ6zr#J-Kވʗ+u42붷Bc%sFZe"K&&r@ ,qUWEGXq\ᇺ Jc;dk{' TB,jgOti8<~u1 h a2fTuFXê˃"0v$uVt_|yt\⋵z[mXg8Z [m{7WRƩf_ -NL StXe%֟9gV|4]#R- l>xin0d{ڦM)m9m|Y4ΎPr0>ԍeexGN01vkfotoxx-18.01.1/data/patterns/lightblue-wet.jpg0000644000175000017500000000730013222767271017711 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?u&^_?PEC,n8:O4dUO֣ؒ0$1î{`Ud s1Ӝ %cl$$vw߷c>ԬFJ F:c#4`(!F_x>}mr9tIlCCut c_\ddt44B#p*Iwae3$m@GQ hP F~iY@>Jrl 6l``r3ۥ{{,iĬk*! $rI8Omy }sp?O1M!V݂P@OφKsuj%QDY+Gy c?\ǧ,Tq{Vơ.v0XP/p7sǧ9 0 'yy`oqi`pp>zT9nrapN~zFgDIثӰxJ'<Ap8gWO'aӜ}ՙа(n@{ulq&/*ꧯ_ջ9 3ϻ*[NrWi,jow'*NN”bYI`_օm# m ϧ?N\*r4fHOedVڶiw\My5Ⱥ; 8hAr;]ۿBպ{#kO>ڔ-zIC;F`NyjmGb#G`}q6EɑI8I?4Ȫ#s~<硦3A*I]8<FiX[@7D1v3ǿnإuq㼵ǁpFO™gfm#z$ׯ^ԯ1;'21>OjF7 mS N \ވAIb`˯뜚kߟ C!p'>uF;thB2z>*}ӏn8(d9I!(#=OhGͲ3u'}~-_FO_O\4yKnr?QcZDB8ʩ=9R#K~gMgiv&MO"`8>uLe{ oL@' Ozv^xcJI]I1BTUX &#k$pc}Ηo!cs05BC~RNI'8Ayfٳ&"l#5u]F ['f МsN:(|0 }1ד1n$%j޴aBվbO۟^Ku",F#%xӭ;Y+HmpN:6:Zl"3F]f':)mRXnU^`6ɂt8')4=ns9.=1Q ʜ `tCo40r[G$Ӡ>ml%DB߹~E:>eT)kGMfotoxx-18.01.1/data/patterns/linen-fine.jpg0000744000175000017500000001073713222767271017172 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?nl5;xUK ZepCgS1ILEqz-#I5:-ߑ$2,SD,ix9$px5rKV.dGk v\>VkygBdn%YmIaEMd`Qt$| 2qM&uZWeXẊ%19y\{S\٬xR6qO,3D+ߑx)v,L|-C0ݵw$du˸tP62k+z"16y#9,]hdcw@%@Ϋ9Ld|WS#&Ay%HFcX/ t kWJ4M:U$TL奊{C+D1K&r sKy9jwc,Rְ\nYHO}"IHREr&bѺ{m-^uin =@fb3qTlb$sw" de*Ux8*C}y4Rw|)с8U5iL˧+H; %6,wuq 4$t$>3VMvwspm_ Xb)KVYL!"(q54 .p m2ۃ{m9BK{|`Ʊ4kxKͦݯy /FaB5ILl17̮>OsҠY|@\J fº@%~SAR6/(&HUDa!K~ *-ܐIq(@݇S I-?,JC Ӭ$b۬[ѤR~\px60OMI:/ r Mk!Մf $; $0,q}Q$E%10A;[Qc6ko[/ mm-Jz `i-x/dU</^#Ăd^dYv*{m#U9ܜ#5f6nJAo M}_O15u0URXP;"d%1) ]"/ⳭlcpEo$z=hca:ի va*ҢV.A^G 4EXGw #t%` =hPfkK9 Hb۪289l:m4P% &]Ĝr2QG߷జhnq2Cmuo,qZ̬cX$La'ހ3 Y䶈8 9J[FU皚<0fNHig(d[QԌ:O҅GhQHH's*'QId/2!Fv&sӨZJLsXHͽ nAӊvF{)aݖG6 d t'x$Ƞ+f<%@Mwq%Ճ@ɍTЮ\g=x,i.VQ8420E,1S$Z8.y5/ō9c/ ER廑#S)SACJι-y@-90Ik-ĦTBzg֬y%HdL pAȪMޝq;K,Q5XǕ|A֮}UXdX 8]9f4-7j#[+0ɹHӣ9bhb,nWpiuݕַ֠r:Oei[v"{U+k1 ծĶW>1(ƟhF;8vyW/0T쎀s Z:^oxluI [rn8$shj&SnQy U9AiXiPG".BU'EWnH'fM,Z\"czm3w<c!*͕#ҧkw4rfEmf2)!TP_j&kmV{w* v?uߵUleUF1[,"`0WC GQik{؍kٷEjM˺EמJʀʢ)f"N@H\[^!BAC7NyS46[}JMլD KfЍџi @K =0Ej+9la\CG䒹qހ(ZGsmt _9GU[# 3ڠ4$aP/ʗ~ 9=*Ο-1H K>qJw!8y_`GҀ3.t=4E ^;p"vɸ |{QuEI4N[Ѣ+| S.dż{Pda @m<Kq]JE1NR fotoxx-18.01.1/data/patterns/canvas2.jpg0000744000175000017500000002105513222767271016476 0ustar micomicoJFIF``C    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?oʛXo#|Mqt2X8jv V\%FQHYn]3+l΋7ɉV?!XYF`"U %5` P:GYnGUg1.Ѭd<0Ewi sX?_J Tؿ0{vYRNP4 vQ CB /Dkӕ\A:ԑO5'#:ӵ96#wAҖv.:+{c(O%~/_ab`T+Dɻ;O#ޅ\/ -ݺP}Q"J#n\Mx7 G E! )rr {$iɺGYu~8N$#P ӽ4Ȟ #\F]ixj|6Nj?1LiPw yhFd&%Sn%u)5sDZEb|4#go$ ?FW5k6yO"I747%l|OZUbݶ#c_oV02P:Mͼǩ\8QpsE<;.Hq\BQ09f$TL+ԧ§q{Q[W;}=ؾe0YB27}*_[t Q`ЬEʔK&YA#2Cot8S+d53X*̪03 Ȋvm̄z_rO9$~`㊘8o8G*5q+Ci.Ò2Y%x#jL l|8PnO5mdmhI2TmʑА=MD$DjM!Ws`V6OE{׊[@3 pGEr&&4PٹysL/X>XD7 ǟs_R=MJWe1Ϯ)dB8s$W'dcƄ".v&֚)FC4'$ T>| yOup/r02?N*`o:VE DE,e꿸icFZKwno9bS4ɼ*M( kw7}ǥ!N]L#䃟4@(U;.=qi[Ɓs9q}Ucsܹo zxfs[m,NIYVBHp^zg Ե$%UI#QVbehI8qsEs"҇eW x3ý `nKjhK1'0 Y.ګ'TJ_q!!|E{u>b9N{8.BS}_ʚ֩W ;&?ÁH>h82)SDdNn JѪ-jXLL_ƛ0`gKyژcgo.Y/Ax97"?IdFXثoL~aj_l;V o.sU0)v4[d*}];1HO&{?/y+ ~鋇{3呍2@򷙐=ZX(V^.hRQVNsg Qwt+>P1. ڬ(?##M!ɦEfP2\PKpv zҷf0%%14"XBGFgn|r5~ӝҿ{'?dQRN@`׌/{ 15 =ARW08ki!KҪHv/0[dI_-": |S2m\$T.|.ǡW/A'F0X(QH*1ѥ9@q74S}_x;ʸPOzPErc9'KҸNPLR8&~"8ˀzc8jolc>1\W9N0i4}vu1trTeC\󎞄R0Mĩ̇+S"Oyr*@m}*V@ >MʼnߌF_lY.Dr_ ֕8K|g9۞ߍ@-%U [,qL]jFy #7=)+^"?e$aק֚y|7U׆d$2'3S|uMJ邟r{gҕ`1A"1"Sn-8bdc!x.ddo9đxc j<9_zv壉 wy("mf[=VDoS9r;`H𸷍N1Ic ) EYl"6Iumq~yI%&Cbjgv&q^acqҩ!\4 s˃jhWY#m۸ >n>H.Krl[kqM/fTދܒIsDqE7p~hSd,{U9eFu:x#haPՋN>{SgCjF:Q@-wQvc?x70KL7HDk{_F04q8`vƒ ַ ;o_ԝDh$K E#` 'S*8PtX`i~[<6n~BHmH'͔;r0Z|S$DZ$#) n-n~aƘ҅ \Q<7x l.Q91 uW`xLN>R?L5]݃e6Rhn(F[h)m9W9)>D$}=vסuXHI䓍> w` o0 >x[n/*}F;R4o 򍬊䱘g (lisz=TX0GF"`t v7z)2Xri>)R]̐By#KAaډG pN7sN@Y\3o?g1i 7;r=)#|b^lEjѨho@C<ڼ`#. gpv#S1]7o9קe7{k@jq\(Z6;qs֯#~i{K*}Tҕ(b/U\~GASah|>xXnNqvZ1Ne62B395y2d[@sla g9 X(y;ExkkSZb}q9F#HL.]Dk1ޘ?)!/iif'ܖM#.qz OALʑ^>KCcCǻE|lO8-M`KAZZ&q˗ӚlgA߻?3yai$"v1PǀFW!(ylщ3M0*H{58MJ՗0ELodaMkekpA PtC NJ5齘1c+D j_'̌mK)]2nNDo 9ځ $-R -~ldZGS{H!KVlnDY-.7(Lv$v;16?* <6tңKЌ I]dť!>[?JhEn-NcpH[l_cw@8r2`>O#q@SpQhU?db7Htu4YF v ␀(ggぃ>nAI*YnVK82;F bdڑm3SSL?RqeV3Vb6_{dwR=1GB`'Sf,wD77)?fmn~l\ߍELF5 eLS7ICݏz?iCxZd_L;_x)U gڠW}‹rv-9 $BVo [r ҰsP4~jǡo8hfM[ɴ94R3"tojX7cM hFpHÓS(a?rB̺j/yQ7+Mam#:VVr3ӎjE >.Sf{q?^kY[kVإ;=3֔&U2N:d9k2,+@ O5VHitm0\o:#9T&%iXAn2N OZ'\(AʐMgaq9,zL鐟3Wk@I"W?#ޥhBt.rCn`MX#Hl(Xp{)%;#tJgي0 ײǓ·Y+=N+ⓤ~pǩa/R\o3J[$v?Jx eκ/y :չ#rnc>sQ"Kk=zQoC2'eN-=psTֻtq'i'is;K̏iaXn0@%RlЀkHm(Q\vQ O^ b6 0La.2f~`ehDN!|BSFs=@[-}& >|qQwd:f˦P"@7g(hiDЇd$b3<{)tc;>知X q&̀`7mfo,n|#h8CnF|#;|blvѩH>qEUa8gxǵ7a#M~NczZ*fI/!fNjA EJ˕!:Xm$wbdRC ,ݨao8ۏO]a& 4 ï>*!2&߳^(a9t[_#)Y}j* Lz:74B[h3˙8бE,oOH>%H.9LJPKߗoF4Ku䬋o¥m&C^Tc#֚VVcq~>c䏗4q]]Oٵ|Sj x9WWX<n9}|m+F:V _lsIx:C E?ws.L/yn6OJ-`v;]e۷=w[g |[:* K3a./s:0=Uo"Z(_rSIkw|̐3t[FxXYWmJh`sS[hҥhde-|~iw=o£0e[lڣrq[+LgG%23p^0nN{T˹nowjAw8۽;IKJvc|&a^t jEvN'?N>S9EGyg=sRTXctӃxnOJ2{NRr ; srK{#c Go#+yz(B>Iܣ^Ÿrv>jGURi 1bwZ锨!$cӵ_ pAo!|ĕF<r-hUu5ٌLi*yYM+h%I]]AHvKI\ړ߃x)/4!l \JdFf]ë Q^̂HC4 ƭL^O'=3T$g%;qR\,mYy s֒[Reb%J^?wܟHBbW3_,7q1gG]8ԯF'mǥ;r6rы˽̠*z9K4,` ;W'QҼ[cÎHK] f Ar*ye-a1 /p9}vݘ 9IxTV2e/nYKw54Ѣ U* ,+d~_ƢiW %|ݍǭ"]>iӟjdَȒ0oA{Sl$kS#={zNdt2\kJ;HYg`|JMQmΊx#wNzT9o<~LC~ΗNU:r=$_2)ZiE 3sҘ.5T@?0wJkKa}*~ Alhk#3>B@zlV.O=𢣎v+}#fotoxx-18.01.1/data/patterns/structure_darkgreen.gif0000644000175000017500000001300313222767271021201 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?#S7oOo:Ld+D۷aW!qOt,m;k#C7roFrHe2yl{-7[o61H R:K*6u ֖X\麶gO,# iQ#e >9C9՟)k W_adS[[[bxVLq'EVR9"H/"уϦ,a?uAvnxsm&kh4PMg18v9"z z&Gd>KܲZ&'=`$K^]KI\Y.KmF9]Dd-)Mps8$DH ` ,Pbnoxg*C0!Q3%PN4FD6n,ͥf̣-QO-Οqi43."$pncYnQ[^EQnm$Wo,y;H*rxֺ(H*ʒDI0ۺ\}$qҪ61EţE,wXzG C( IFw-[\.$ۺeA)5"ޑ=_>p.c-P҆(vʑlwŻ=!&ేGURwbm]Y0(D7RȰ>& Is 'PeZkv6!@ FR/:y #:\WrHmsX֤ӂ ,fBB c:͚m2}q{s ȈHM?vN"EUv;,3Un(ilVdoIgGg hl'r|+} ,(Mk4o$ u6EM_^ӼI%ΞQO$Mऱ: ךIm# jnuO2q*D69^ӟ=Gg]D";a@R91;*5[iPx+RޑKXͪklӥt}|N q]ZicX-a0ȲE"4FJz%ޑi."=I'99*ÎGzsz~[]I}qcuۤq淒Ll]~*piV݉-gvb".9ͤrHU#ry` Oǩڝ-&K[%hFŎuaXHl9黑RKN a;isy!IV\d[ϘHbAEm̀ʅẌfXjUw1JO11qjSwwHwd`aHin/' }alp9تo> {H3=BBO/8VMmT]Ye}u9Ho4^l'O栽`-V.ŦGFp$@OKd S%_r ZPvr_6Kj#28"E*EXӵ |@[AqkCI<.0KmFjDEX{#t&ԙT'2QqA1x`4Vb!ۨO!Y-ɑx4)_f&uaX5yyȐ͌Rn-g42ϩ<Z?+#|ʹr iQU7[1z"Y%ʸib3BYp ʲ榶h4+gkMA#oے};1R@%Jj/ԒI}ZZ*+Gi_Y2jj{ &+EP!2Z,l!%RJz6aayi"HOȒ9D=JG3[跓YjG 66U9>n7rGBW}GU{[9Fk|"XJ_@ǦḌfM5ՕӵuDc d-}*-Ᏸj6 K6 dWP<U Ω5݅ޣ:|34 VfJ}JKkf/-"kwcLFX0 "ƿ6sIln~ciAIL`98i. [H- v2k`B0AR2[o5K 2JK{610PN\x4vy--G%$, A;H>Wn sJoB 6%naݸI#Duec}>KTG* ~d}܃E0q0Mfkǚ~y]g݀zgkx+u7_ aak,ђ۠bećSJkU9X-Log,:E#>FȈ܄7m-eo"@,ܖl Ff4] Qh4*cʜ#+4kx$$ J 9B'y\In-a 40 ֱ-Y{$SZڿ'_m3[(F&Cd"(,"h!u& 2y# 6doCj\GJLWV5I̮A(fDCs [S^д6=J-*fnm \4ACVRTuqc$E{/'2۽BJryŷ# Z=-Fq$V:Zqae&ܜ hd8p$\&I1-I,:(HJbweno_i2kzlX!⸴kXH[~bV\ !hV.-5;͐eh<ŶYf|Ə| >PSQ IJ,֐E S(. |Ofboek0)Eٶ$, yݷ WTqitWP"iVBXשmˍ0@|ӺF٦6! ErC qnkF v \ZkL.iAuon2űXAr]r-Z]62D( mYNwg;TA&aX*N//2IʼFSp<(,ѭc"i"m/ Z:Lba.v܆ 2 5}*։03ZZFLű*dG\ ѺQ{{>+ЬrEF!@+ho;})j\3Y2o-˕r΄[hpFcc&pp\2P`[ ; 5)/gNMZj!Crz {-J-֡ciBH3Y6ĪoQ J\f c:dF-! ͍Ags"M9VDIVYv*4n;XY#[b*8M(iXeڝm༒DA0ʢC\;)@w $({M$ƫ I'ڥbܻ0n#)rXh+eK>k÷dY|YABf ,.~M]Z@[NUf % +@ߐPfv[.YeeRdmȘڭ5QKeoz@sQYc%D)RT B_ir :eq4H#_Ȩ"E(jUg`Y3WX3{$QTc*YͫKyvֺrH5\[Ք^]Ov/Y1#QEfa@zb\%RD?:4b1J2@%2Qu{k.,ۘ7 t݁ U!*n(XQF[4cܝq(ŕE ;G0*(I$ ƌrbm,U\a- h/Z@T8i"}6 #cjj׳]P֯qk$64Cq:C); ݋3=Cu3i-`gL3!j|27RV?m[?ȭJ_pڣ H9-[^Aq=q\k=_bGPv]ʌ ֒ ʫyRmUA/ćoo]J!FdKYR#DA+ ,Vb qae, z$aGF$`H @6o@?bVgI"am?w (l6J[&Q ޏ-KR/${ryJq,TeS@K Hco/on ũcl$,{IC\HLhg<9E ue@#T?se-ދ{%7qG 6% '#*J!$num鸻8~ŵyv4qsu`( U (Pjv=6-e֩id(@09w:!u?. TqQs.[e+=KK)AK8K n ݬ*mkU. ̡v%Ԯ# 2mAvGaD$3o%TDY ؍$& 2&(J"b* 2׼+{E]\\mo |vbr€ -* F)K@t2#ck(p+j6r[HoW]B;s|>wv`@9 ܨCEKۜjL3-,AFH,P엗w6OnO&RRyq)R@!Bx|8OsMo-d&acB1|bRBJ".Yqp~sV`MX!es}s=ך=B]G2Bh%PVU8C&Y-嬟y-!MConr$~a2vl#i/d$B+$y\. ,77.]\YAwv6YYghRBA!# !d" KK)u{Ԟ[) s ,1UYJ"sLJu6|Eב+Lb$6b\QKuw$ De/EyA5dn\,Gh̞ɮv_*˕ge2q`n4>҃7%$2G_ۯQ"p7q+b22 `Ѳ_EbŎ+m#.Ғ39]s ARZzE-`Elټ(9*.4W;e`VUQyp b7`,OovejL/a_0F~ĬNH`i|^ӯnla/u$\r;DO1ҥuf|*Q%!%>v,Ja[7@RF\%U4F{+Xu kVL7n\cH~T;H> 'ѯeNy1 1eq ,+f` uAnYlwZ<6ދr)yZNT1ݕy$N2Xcky<7ZXO rX6 9iBAܸ#ozcK8'H֮Kuc<ul(*G{n @: zBԵY延Erx`PD2T (&C͐ {Ϧػ6V(䉙,ITbs6h+}>n@dP|вl)nY|Pqsk['hmk VޭcX>Hnγi6R m<Ǩk*G$#1}ΥUJ'UZ^P#Z=LWnUR $y܋9$I85K\Yb[vVi0 JQV\>֍.[gv .Ky "A,v,@R:}ig<$}gd(I"VՔqs۽J{610vݛ JN[SNun\oK4G r f#/n!m@y1^ᷕWm$Em',ekBr\(5{70b%NX4buM*ė7SI-0Ė(iSyp[ moӮ;ڵԺZ_@%bU*]Io"!Ӵ+;;Uأ aVP Lmm]:L1 K+CdTx%bƒ l[ !Ϋ}+ukYm䍆Юw|˹~77,zU͆l}SO $+ fE |21sŧLt_ͪ>k<֩G.[1V>m;0`fotoxx-18.01.1/data/patterns/purple.jpg0000644000175000017500000000657713222767271016463 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?$L"71Rt3:O֦mt-COU;ZԂ@sޞVsgpv;yNJ,Bkb>[>99Aqߜڀ'gKT|,X}?S,:VJV~Tēf!5iR cy'@ dq߭3U/qr2v<@ q[2@$sOnmsj'P bx9L_A!'s6(~+X Iw`2編\lajp1FAVN?Wj t C+3`8,m$wsI2b!J JI "DZ #zDdn6vO?J@jn#8n epE⽴bgF`IC㊂m6++Wle[\v+I"-^ -nr) R:GvRio,ɑ߸Oެ[$@BS^j{7X01@a8Fl#sSY\G#ȓ:B+Z6z}:c0s\wF Uȱ7APx먠xMͭ1F 9TUgf(Aܧ8l}h53 Iw,rI8[p@ʀ.j0 OO{%ݷ8f1뚆qk3Ey*0ʸ~M\Q, I'⣷/\$E2dn8z'6w9ҕmݺ՛IWOԭrvPlw$}XR^fn\^`VyP7nMQOِZ#+ZHmTlıd#ǯ]Gw qN6RE#le rsځ n"k|g&Mt{֍ډ'` w,`3̫Cx"$.>D-Rv20bV"ϿZkowmAzz7#:%XnO%p9ӡo8rq(ܠqFXAu Ms0J`d9i/tc #}돔kIi4V+~SFNxv Oo Hwb(HׯS~^Eiڛ%̍b)0?^J b3ܨiQ|Oƛ=M{[J'~Gtfb79&BK3=hlYId | x'.cB@AV$D?<+FUyKy<='7(p sYͅC6 $`?2u#<N-HSʐJ۴rA<ʜqAh;fxZ1D.8& N3 Fr&!č-wVn$X)@P1F:{uIHeG 5$9Sɍ`9۞߃0]@?ٮ.ڟfr%ޝqT_ڏlb-age'?zP䲋WAbky' _`|W@mޡCA߾)eg[$ 5[u[K6uH 28Tl<3(RTCiWtqR&NhK<[FRD·ߎC7n ٞEv(0).{XgKIf6W,HLjm?I|4q[ɀ9h;ѮB<<U{)qϽIucIc,fSGkQ[ҡi-f3)|)ެ==WV[yȮ€"tdU-`dR=:N=47+ q р9'C-Z.pK$j~Gs*iWn 19H - 7akWi8YgߐF+"9!S941n#`7HZ[5x-Mܢt=q9tI}gN`1䁎xAK $sDVDcV$al GUDS2c@fҷik֔$,@niEZYi&h `=E9 )Tm݌~ULDz*%1Gހ Y\b9چQqNzgoJZho)/:Ž8(dDKP@p\cڀ?fotoxx-18.01.1/data/patterns/pebble-light.jpg0000644000175000017500000001062513222767271017477 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?4MMA"' e~z".*"c\GC,ֲ(Ge=Gzu?-ͩea \pU;sy߇ylDX.~Rb+!R1|{ןj}mm*ȁd,k=)ErúfkWvvR[ۑ*l\5K[m!KɁx`$G:xP_x+cr:e&y+KגN=+=_κtinlbslv6/^InE2i.Oh9`Z> t 5ظ2!8z/1 |!KFM3l¶v6= ͷ$XИ4]D@'oRqǥGZkT8%RQK}wc3].:%Ч?ƫytaniy<[^ o`JDۍvM[4MK6+6DxpzNR&&n%EǹSImxVN;5;p9+Kix{Xu}BA*H†6=(|fu[ޓ!E[!,i#QӥcxMW]ni w"m?N1zנ$)B|zt wc6Zٛ9R}nkrˬhzwلr[H$ 89wA6>sE1H SpNMC}KXo-ZE Rm[d<9r9 u}{wZb'iryR,k>9_]7K 0Rܒ$']`f~YiZڽΫ}w1-5>Ü A^s$P-||=A}OSдM"[;2X=gI$%$.@ݷI'\j֞JhwEfR2LbǬj]Zd @PNԥmJnGE6ʲJ3ׁ"tm"[xncs,P~DNjXnd{y#D.|A{Id )eN01U.4Oj&ot4v.唩'#ix3GMeMJ瑁A[𽥵-PA+d#&g O\HǦNg!b2 8Y v)4pnX[n;^(r$3|ym`0%Ln]ʓz5ỿ#_[hz#~@'ºL/:|6ZDr{T>:X2iZz׷4r} )uH wqgiQZqFϡ9mnI.>'үtQ2, Mv6, z}97'Qu}AɁY-AV>EqnN `@M6\ܶ|]k!P,4|۝ c=vs+L pP۟J崝6Úm!L_l;̯,I'=TSz~EDщf3ޘ'bޟ\ME$n!d*Q۹ȮbMBw pDF>n~G4%`&ҾsA\GfxRT񶼺u쑛 :plrvǾ1ޛJusg_\ΥPT9\eͥJi+/ğ1]V @c^ez|a= E2\ą31qFi+bԿmw{wE8}$'"7#GռC>}V:eV1,ehӿ^gSֵ=sIt)T6ԟc]^EW%D+1G֓fd%^ swfǭgO3MiIj(z/5s[ˀHi|s`-Zz}Al#RH =|]RK!!ʔqv(hZTK+HE귕tA Z "Zjn~[2AW$quZVoq\C*"1%rpx#޴|_ZxcWœy Jɭ{qjv+Oiiv[ot e,!GEKS]#;^SPEQ#^FI<b{mY}DK9E̋p Ԛ~.;{Y-;P![!=Ҹ7Ŗ &Z9HicA]{6 @23.. qXfΗE>OCK4 bs6{c$1Ayj:AL6q9P'nru/ ^ؐF ;W\u^J s}|Āq 1jVKDf[+1,Q0^8ֵ[LtiV.^ D2k7^Y"qp_9^ݖ#eFF$kXPE4RH7ya9Z贿 ]JŅw7]E\psj)S>ʖ#Lf+W'dYTTt~ d̻q:zWxszֵaqAldw|YG|*'&v2=N2-+БQaKw.Ѧ.Ȋ>efONEiq,L )~S_l L@yl⠴}(y=I=y4F?fotoxx-18.01.1/data/patterns/fluffy.jpg0000644000175000017500000001050013222767271016424 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?/.!kO$'uہP^"5clBp"!= .#vQ&}n $-Ƨ%jO?WZIEr\ ZTRgN}ς}+Nd=駸`ٿ~Ii9<Qu.lֳ (co3sզI$V%EAOAN:u\+9k`2-H ]BP[!7䫐gG}$3k7"8NcLOl{8}(ⵎ>aWHIqN.SA&Ԯ҈T@ѸLsǡ.#LEOD9cOI>SدB$q=yESMtb̙`S1 ՒL[#'=+nPl,)~-/mdwjHG2#dtkJlxU5ǎCG9sᶅt7onӀJPzNz Gu (|>_|880}V4;a{tv?GO~9}9tU$ٗH N~ꌞ99{Zvpu Z pJp3߃׭hA; $n? |9YDY%[6MPᱎ?Vo\ΐ56l9#zS4fܒwj0#]sNkH7zfYx:a"J>19<$~yǯ}gM[>lϓ989t)^%gtҮ>8>,[^'}UM8Yo fDUIt7 HVP=~2˦ؾߗkRIvI\?Slx\WjD!?xN3V" d%nD(<.>=2O>u,aSG -A 4l??zZ}8.bI$nI99ҫ bd R`Tnc UNWi^^dԯ-P$ntEgj.IQ:GjڅVSxAin8 >J֛Q.HP 19ϷcM|ciiS{tkdsgzRt܋HGu +^3'ҥUf6uxԗTx~\$ rے7*(z{\w4.|E,6PVFFH՛qֱo"kw&3Oԯ< 0Z vS<}zCd%nl;yo2y}ճ0CߛIBct"=:WsywvCہe,YdMMNT u9$) q4'R6/fاˌqߞji׎d\qB3jƥEHl͉0b}ңm-XTm\\8 v^C$ N?L7kj]D6+ rA%C44- ^Bn1I =xJ4+XD<ȱskި.UY=Ixx@کm'*)FIxvyn.]bJD]RБ}7Zf><:v}NӮ\C$*۝|;i0 mJ zNzs"4ֈ׽-=c=FxاIn.>?vCBÏG#4;pNj;[p3s=j./*zۋ(\G4-S?{ޫtЛ%: FkW>/RpH:&翼|@Fc=?^?o,shnZ36C|wp1k9bDNpLu`OOFkN 㶷I.6=z8Ԓj ,.}i¦8FovOb+ܷ6НG 9 W'noMOGU8PF1:ZzO3Us~=E%ܓiV7%VDRIpdc$ q/yyi`fv~jK; k\M@VB;=Gi 6ڕ7N}rGYF%Mč;LfmnfIpGj>tEWP%Ԯl" ;?NZ뒱L.W$* 9 225 88 C     C   X" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?yM{85ηؾ)%c ̑킱0Keξbx'oGipY!bOŶ#|Wd8Ut{vƣj^_x'|Co7b#[erHTYa*/ 80_4{K15,ךsu!a[تx^X# 7W-]KyBQ|V+ur~Hjoes[yuuyE7hy;۳`">:I^Iw6=>2Ǻ"kI\d<"In3+;"9 7,?f9$^4L3GŚ5I7R+ueȢ5`xHT+I$-19 if7^kDjWɄ)gEHUd2_RO xV;˻?^Ei}g#$xX>a-pc=oxv-m|?t GһBvmͽԷ~d~':VrM+۩nXfK$nxVo, vXA*1c$09𶼭{\OY:CͦOç_t.T["v: ݆oR9xV]ږᑌZZ2];Yθ_'gu:oFP$A:?gY ۴Υk.T]JDo-~G h?gEkl7[<=xӗ?/׌k2w=okΒᤆ2b=rI`|V;k??h\ K}5T3_e+$:*MtK<ƃ~&E=׵9nd xl#ԆAߤ^Gnf۩m9I~%;yNeI.sP1N0Ӡ:΢`y}.m߶H0xz5H);OKy>8JgǍ^In5UDAmܾsE u7K'ך͵yQn,H8no0͹ɞ6‚_iaŪol %NI-:˝o ]sŞ XQN6%iI'f ~an4E~DSvIMΊ?gE~K_#e-wTL.nc91;+i&[τv]ϫjlBXY}=irkt^8kHRBE݀N2<=i;yux$k7qKy;4j*];⊻KV17V [dz~MKMmf3}:ߊ'g_n|(lI%CiiQyy*\cͫ+ƪ_|&7jf9»2:]^2}ut.~":/m.c%mњ&k';6r׌ngPew,iqmqo4i5C42(d6ee<A x RuaiXtL٤p d`J ڿgG,f8I]JISwτ~xr_[jm"X.6E%LI"aD%~<wxI [j+[FuEb<@B FQ@S3ן%w_C0^{/_Jok'?gE'0r7S\]_GueqrS/;; +Ղ" ߩ$I9$OZ:?gY~ ,,HI IBFV嗅$Y3OY:<#CMsIug!(ryG ಒ1gCV&#Q^. g?Jg\/Ÿv+4 3zZ}:2Hʪ=N IGtW5Y[[L%H@ \Xd(%v}5V3Wu'O!t_f叅?zA鷍uu,VslY8 ؟p`AaV6#DҾ<z^skGHՒc$W63 b%CFA|I3Aj(|Y:߷OƖ>u}O (r$eb)̍esc#mCMxV]ږᑌZo,?eI~xCDҼjz&X=n]:K,~ƹ<%bl/a ,%**_O>9YA7N M{sUhPI m2@ .Mz}v )_7y,9e,ʼn% =&_6PA S:$c8WwK ٗP}2O QӷOZ|׽y[*Qћ qd/ӮK?2,^\/w>o# e~qg銓μB+_+(4^E]$y QZ4?h`32[ZrcPQ]m\GS$28#~Ƿ 1tׇͭA6%jR'X^2˹Yv"gG,OU G4~,vww, Q6x'H|W| ǿVOS慣j2CjSEw6IHl;11hM:6[dzgZ69(k/σO [24ג^5RU -:3uxŖΣo.H.v g#{ey&WL,K_i6mNHP<O<Jc],|<":(ۄ_#_?xJ,|{]:~s=_K`V߲}o?vymsҾL_<^wJ&:}YfᏅP^xQJTuWYFv6 dVvKg+ڏ+m.uXdݫ(a$6후6<4MSž(ׁBEۿ9w*#2褂t%A&ZxFO,Hlo1*`ydi?I$̨ckbN*W_Ou7z-oYMt\\Npg@9f$ ,X 5sQ-*ξ/njuϱ@v (K WgD6lTէ!{V0Q\ĉG?~0K_xb[ƶ'4cfevEɄ3Pwf rHۏ1qn_ :*u_he֭GݬZtWbm9>_C1  مVr^{O<]^M%xe=Ki?oy:'L8T^:q ix^nPԾէKwOg;mF tT^p$ ?JNq4;YX>xIs{?0kS[Ӽ=օwi+oٻ+&$䐪0nA-.um9..Cv <ÁMKz"8TQ[0\Iwy[|kMwlc$`Iu<&꺒M^(j(VvIUzYRSt[rt 7Śk60ZNx6#X0 PAW:<%LJGmtIziBVqmz,5&m*pRczt׺&%ޏKeq  ^6;\2TT f|ԆkKvɩ 6 c ݷ~VŸv+\Ԏz4(Mj?gEku0tμ;Yθ_'gu:ğ?gTuMJi:nE>t wnǨ,+;h/-.5IJ  Vw$D &Y6 # + |A|NK7f$ַRRѳ*wROxš6h6èܤ<ݑ2Tgkqn;_IJŸv+p qdc'{z:em 5);NKAs8Vy$;;,+6fVQ9u'M'ϡ<)㏁|=?\ce*KaWwBm]OH|Ok 6^!>ӫ]jڌ2B8*dpIOnF\~xO:Hj[.VYoex$9П0Np}vE5xn-$1Ҭx]b6C,3(fo/,ÿfχ=-]_b)O%[bWXp:$=Nrw3lK.ZN`Av:\kOJiiyCg?JwQ' IkwJ. C3 qdƗ$^6;& uFKGN2?AzLkZxJ%+upE`(3S[4J-U] c "W:nAEwawF*ŏYӒGveݩm88ΏYך58o X8$md9xH #S\ov[m$x tγ: ק~-hl=OHmHa˧[X-q`|m~)༖9Uy6Sف]W|-A5kaqks~N$vaH EqZ'x^-oMՎb "liJebxm;JwQ+?8yW|bwF&KY-Ֆ6 gBϜ d3Ӽgcy/C]_M/1UF6 ]+w_,|5YzqmFA]DErc$'\Tߩӌ">X] l0m$gEu]ܙە$tΊ+,~+̝c],袭('Y:(:tΊ(S3ן%w_G>h?gEWv|fotoxx-18.01.1/data/patterns/blocks.jpg0000644000175000017500000000651613222767271016422 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?";StK A2dv8g4y!Ԡ{fZ%m,GIjJQcJӧ%7uWť^B~_Q\ɓ3Vs ΝpȬ k/иܨVNZhIS|ڑ_2zETUn%aI4Rj:}U NqkSg 7ouQn46waF ןZ 8'^ xm%++)]I)m.:è{7znº宩l(*kgaIԓRv۾[I5TQm[vI<AdM ?xu^_Zikq*qwŵ)=6_҄Mz9=Q^Ir'nǩLנ8~+sTmҺ [$Ԟ5p4lҴO6.b={UJvM9vҼ]c^~GUCb<tZ@im+Cg{p?ƸU' v⏔*}ypn7Օc{p\f,#$Pxxt 26B_?xgu}L0@#6+4kk¹PJ--_~Q?8Eˈ!lkGڧz T3Ee[OZVw8MF*zu+kr16~`2#_ϥs^(`FC/nL呔pr4 #W^[cd~[=OePIrEyliK|1YF ߆kfg{qvd|:_w#n88Zs F `NiN{^U+Ԕ)d~F7خ%K 8tGPxzSk$YrEM޶,vr;tN5Kv%EV.1,^ee&":P&(*MáAn3y$G4*sҴNM񞭧xr-1 d)'4nc)RJ Ko}\ (^@n{Kqm4s6s)8U'})KESp{GuklzrqRAq9I`,3+ ^*3S&ch(vN./pw z ҽqpzk-j]Fic}8cv ! ܐ[XqQZiOFe:uoEy^4v.-"B P׸\? .QeX) q}}*X_t)JM%E%\22{NPvk)NSR?1YҮYcgKѐ~x㚮 e; )qiH7#F]֫4pk6VWsGy*#3TwZS׷&~H̹]Fx}7PY_ v֟[\Vg*-V6:}+W$qs~%_3À&z}r:Հ>HG3AkY8@GCZRu_**OU,*p~5L =ѥKRi->ǵi>Q-=mAU*r<#P2Ks!`H=jH?>kSg0@2 Q[ӭu[R$rƪ0u' Y"KXt,-l㕡kxdc+d"au< R8`c5,UiF9jڛ2:,̶mRmϴwj#YK9%Cݘ mDr~ [ RkMlߢGkHÖB4]Kr5Q]6h Ԯqd\R83I#w$V0,Tec/٤ǖB0tѿ_16"@? z柟{үF+fotoxx-18.01.1/data/patterns/lawn.jpg0000644000175000017500000001567113222767271016110 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?wIkNJsY`֋t/eM+R}FXŪYLY.$U]^]*I)m3_j ]NC)AЂxq/^Z^lϣӮ⸚8;'00{kIy? uy+u%k#hRΝEG^o7)Jʦ1-mnS#:{x%ضÂ<ۯkYj2W7l-ذBX !oX-KNDlp]B#b1[P8\Tp^"-tk]hm2Ɩ,&nI4S9GKk|sO Թ}Y{NYu] EԴMZy(5909 (9ekF_6uy3m,GA3_[t_fy퍄7N3mG$0G#qZ}_Uv>-h퍬i1{,YW#MmF.'m7zNRN7ާg}Ss\iٯD3l0d Ur2ErmUxX.VOi3#s=+<,56(]B{ڞ컌h>vRNCpyҡޯ5Z@pR͎QkZ5W|ʗV'k{QMG9)O2=Bb2(ye\Q)L '뺭߇-?Zo3jw{ RߔC9-e9 i>͗REK-Ŵ65R]9 Uk^㨥x^uKdKM-``sokD$#ڨ!<~I/|/ʔTWwww גX;YFՎJfve8 O+ V\yʶh tNqֱZ^ج/hַ)6Nqmje- w~z 0dpwָD'NGo@Z\!W|O_uOi(mt&.p Fh !i[$]/L.7ʄ=8bx5M MNOQn6n.UXdFw!IP Ou\ܣӹMo[g=3Ik׈Ys] I6wH k<^;ҮVIb`HX`r eH$5\#:w.cgFG՚L^&|um;is١\hl il>No߻j%mݷKߴIel VPVg4, eF95kR:u;3λ$Ripi(2dU/ +4񆅧 k Q 󝱹ffU$ gx@>C}ykKx\dA;>RTA址9j!s5gkקQJw~CO zf{Ț|ii 9,JrI+|5BIWt %Ա<>ynN]۾+KMsVgeclǩ}:GF>ґv*vp9Z55 ЭuP5kNP{p+~4ߖHW*m4,isݨzk"e=i',IWg> L F BahZӼ'mƣ6H9Ui"wS msʎzO^6|Z"V~%e{P ҮFP"wΟhҼqXv2 e*pi1UN6}펝.d~uWE>܉!c>fAb͕x?_c,N,h,`;pgxONj)nU^~XC@6aڐO/@A$FUkuK|>esiwr\EYj"DYw0@Eq 4Y|MrڄvщCg|OTOlq]ޕ-ۮgt3XPꬪ7rc˸&N8?P'ӖEynu aEZ"o\mFI1Nrp=[/᤹IS~>f[m_ (Z<żM++`*| !%݁ h>t6{JKTqEh:?{VSr,auL̛p)MWg C^)=/zsrD>i"rzըe94h}:Nd[^[zU·kuo-awtrz9{@~Y6w?KO.YT徟G^vkv66ZZ[t7p;]Im؂RvI"$a U[hϰW–;_ Z;m/)MѾA#sW5/Ou,#ĺe]FDlT y&iZmBAwݨmtX'*wEvRK nQOk+hڶ7rVMGGשYT%U,kHa,+  n*-5k;h]夓la/;vyrbw`p+ԼW,+e&=GS{TZdY]m>^GNѴm_ú`vao4'8=pI:P\_mBqgM%%߯!-އh:ݦx9#סIaa}yFSx88{ⵧ>xeƄweRU3`$Dhmǖ;Zo2/.f  x; noemXaiʙb:,^p3@/AR\#-"Jyp3d**>0|8ӯ5ory_eQ VPX= Vx9dֶZ׻4TSjǩxkO^iwB[l)up"qcY<[]JeMKOSe*S B8q1! ~$EAc Mg+nх*cm##?->NҬn`{'R]AyդAtɒ3ZppiԒc􏉺 EI.7ͦi᭧u?0Ơ(`TA\gk56K]6;Q%6IKԂ\u#8S%݄bÖ֮vi0ʙo! zǂG!S@fu򙂩@f<{x:^+ GK&]̋ Ulfr9|@kxoWԡMf+\a"d(C;Fn If{Egqj1$jzz;۹ݼ+GLi1]o{bη?fotoxx-18.01.1/data/patterns/liquid-blue.jpg0000644000175000017500000000506313222767271017355 0ustar micomicoJFIFC     C   ^^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Iؒ2{z}s]R[bvxnIvxkjN-+ܯzXT&0sużP@e* ]V?avTUx WTiѰOIAKtTi4Zؤ'=Ei?jMVD2ϽANXduo' ٞ$ .O֓twKZ/qka#L ڵuzg}C'JI=r~.4wz9ʰ9_ZUD}Ue'dnKscJ\@jE$j "4Y7 ߚI5|NH}s>cyl풀{U|DH9 sX1kT  |M<҇Yy}Yu/u:;i*OFqJƿ+9$і,q+Hv.Œ=it)-^YLϜPj'b;FBBKk Rql24|CyveDYo NVzի]B4y9wq[8Q1zh̅h>F@1溝'ZiQ\꒔`6OzT O߯J!G2$Fٌn=܁d< c>abeʛ7$ϦFJhq$EZ{}bwj g%R d^iIf]opqZdžVC .A&tD@+bAp pk_eY$#杣xmW9'W?50yTLsQ^$Dm YqM]ϚlGLDQ}Y|NŜn-Cܬn, WRX C HnrHdq.dNFRҸ F6 -d%@oCH,PYi96Ӥ2QGm=$ia)FK֭Ma6ђísK';!#')r#l$Zl« Wf>0ni/ A>.46F7#: :\hZhe\:5V3t_>h-.YC+Mb0<*{eY$!r'}x`1j~v?fotoxx-18.01.1/data/patterns/burlap.jpg0000744000175000017500000006112513222767271016430 0ustar micomicoJFIFExifMM*JR(iZ0230(0100Fotoxx:trim_rotate| Fotoxx:trim_rotate| http://ns.adobe.com/xap/1.0/ 8 300 400 1 1 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?n^RA'{7#^;op("2`5-yfQѧؑ~Z*lg>9]M2QtkwԢ]OQ-SRԡi~#r>]͝߼/<)Jډ >Pfצ6ІWr0ʩOu"o\_eo`L7o:[XgĒ+6Cqw:^kk{,MM_誂[6۰AEWeh֑Ũ $[nrW,l|+5mkYuV%ZC-I"8ژ(#js5vkiwp,rO%G,c#L)gCiu4ߵkv7\4vm#P@A&\V:mz5gfջEwӄLm}Q:mj6-7:0CxڑseZVIԬh/>e@t䈱%sw4B=D[R%ٴ[djn63\WWCj[A`:e /!XΙttK a~U];SE2Mv^kKBye;XFO݊@%G2葄k[pNK񼚸22nZZ,WpEΰnPN~~_m+&f3OQ4d_I,M Aݮ}-TBFB77L{3CkۛMMd5 0oWW?w~r>^"iZ[OCr܇M!Jeju{7R (%LR<˜:A67R'm_RҦy-wK H@x@*}:IF{FW-I#h5IVoYfd7%SLGۨkpG,B}ߙ0 ua&jeי,mIZt*[/c>5-dUQ[=Q./c3ILh廗?yvۖka즱kzCM3A}wgqȰiK%Ddp0fr皻ֈ^6zޯ MI/.Q0U[aVX,pWژVֺv4zDo-iqIvt_4K?u:JPc8fm>(Tȣl|Pլ+9-EAD)<@xHd;LWF.ةm9?@ȣCFHlS=(#.]px[;yƈS)SY6p֚Xi0S{%Q\,e$ڿڎfw7ohMk.6J+ *;MdeFEfkfOY{%or Le ozn޺;[D;Z`-nD-K"#.&b> VmSKmV-Ue=Ӑ'Ίy\d V9|WjSZ-lj#BF ;0c0 3z iZr{wE4l0K-սM;\bۉ>eAV*m}FZx|ލ_Ngo?y5ܦemp[UF55W[;ŵkdX[Ɨ-]v3a|.ızMeK#o'Q;h;TT;X ?It[o<]b΋"U$-m,f];/i{oiihMrvA=)RL3"onW9r{ >B,CwvB\?Ȼ>R[n6ڌM5A\jw+[O A!CF +&AnăImtH%t弆Pxv+\[W; i='7s6א[j7VZCyi*gEO5Kud?}6Ϣi: b- ZmbH&n0"5.iow{X#xĚ.6C(_>&i lKKY[H Lta_<sRI7dMQꬾHKw^8R8{Gdm񌧚۱U/[Լ[=qz\'"'L.lRsF,vwziulWg9h}#W*=%h>Z^qgxZt8Q;naބ~xvM:Gi"+ӄs[l| y#L.X_|OmC d"-lg;m˴|vZ2*XZFZh-](hQ_u!jvM֑__][j0`$:1iWfAZc KcoZݶibM"0ax7PPNrp'n,ὺ(sY_ &*%}ݿ2^]M$6..S xy-]7 +Kmtc-GrM+2o eJ6Akc[z4&lje<5ł}uIPUE\%K1h 4 3Xִk7S췞z4lk.7lW|˂Fr\U}ر .Efi_cV90ZG~*ƁmcAJAVYYIvim/lXp| ˗4׺iov: wSTk{Ǿu[˷7d6C+4UI"i~Y4-<*#XnIE}jYyv[m<ٮm ? 7wVD{|Ksr#fK-ǛeŬX-w-aRi^NӦk=wwg,r`߽ ~.ޝ+*50Cci _dsI,Lɵ QVM<=a^ڸZDh61Z[A237Հu WSC5+cYhwqOn Io&~b$2+nBMh:gKM&em' KBZ7q8cHٗr564|2LJ-$IQʹo.ϐpolKut+Omo.vmUyenT<9G 6-kr[XǣikKˤ)Y>|:zf67%P[mjSP㻶kk6,ͦ:ieSk;3+`֊*ڠO3hkwp4( ۙݞDg.l?.9˴]-n-+>P'H?,.lV֛LXipα(,HdB8wnf%w/[ Mutb쓢&>+|! IwlF~I.̶W,${큥Y!g+vK 7(>0A$^"ҭ<%E.jζcGqw/Ed_hf3EGX"M淩ǩ^+OMq(Ernl?k tҬkn_G$b]!WVfM;i2Z_475+{{=E$̽Τ;ܮd NǨg|I56g}.6o[R_Vյ|0 `'eKmPH~}&Xvэ]^.f=GUM5;O2d;O+D\^:Qk/k3PX4Vn׸ළ|ǂX^I.%ݶhJ%%٫LQcn~2\4z[`kܿ͒[YZMg-ܶڕ^Egq4:n~.€+|%oc4hll;+,4ٯaKx}Ħ\|C*mFw^6JvKS%yd>˸vBˌs_Z<7]KEW ѼMͻ{Rk4ղѴJXãm[(&0[*Hnkiv4/K}46T d(TDww-YE_g Ѥ" 4I`O;Gg%dyjXU?Zd7f-HeMWmn曒.)֩o6^<چˏE+uSvjno~mIInth/yhcRFΜKT',ֵY7:χTnqsqw/wURC|+CѴY-m}3MY&X ȿǝK%(sv%!v~:M٬s5Jfm-Ba"W0!3C_"mj <03Gl$ yΡCc!:huΟi7gRVUӦo*&S+%W1.P4;E<-*KylZDK2k)țq!'v]7W qNgkmieu66WSy¬ÝjMc6zce,"Ҧ9T]K|:q+ݰ||~V:h;FG+ >GؚTarQp'Z][J5 B6lҹ[49gISV 0[A"dZ\WԧmxSѭ涓V A r~xѬIn l۵a{KxԷz=ŋGq]J*'O{x4!xl2m]OÖOAt:ʹDYRhK6/pe*G2yn5\y:dKt5/Onw46VD-drؔ- Z͎×?h]Kb3[Q릕;2 smP V􈮥d':?6 Lpz5'%:ˆmK{UҊ;2fF[k ͻLA8 }X:W }sI->_M"wIVM^[1jEPe†;ӓk%3i};CnWp/&$ȣ; ZST4鮢ў?cGgbYH_2YLnÕ'VS~ӭ4 ܼQkh=Y,<[|ݏwJl=>IeQK>i_/Umߺ޽~j?#7'úOu麎>#jZAahi9^?"pcD|iYi"{vk"U+]#mhձAy4@r[s=ɉg{xɆU1eW  6Yf{c-c ̮*:H0Ǹgu6k"Ap!$ӾJ[|ۗcԥ\ZI%eB i*E ?z>xV/5 F[[[ZauYª+"."g 1dMB-sW{mdmڝA'Rk|6b-ֶm'TwgOA}]*l] oSkKw0M'}Fב,Fl@Hs`ۃ\5P'J_;]kf6ZjGu*ă |Ŷƥ 'DVLf$;M16-f\ŧEiO[ڦOCq$Mmci#leci]ޙݏi`540Ax{QOYMzbF Eg8R +7ėv^)[Yu9(e>co? :4TsFʱgD]J/V+XK&#<:초a%sZ>ws?RXt,ѣztul퍢 vMex&oo/.%[9x46r]k Hq03Fq.[ߜCJ<;FHҴۃ]}OU ,m* U.\hm x f쀴<1+XmöۦMt#!u) Ghۚ]a]*%M+N2Fеmw} Vjvhn]LFcp^8^1 "dLx0fZ[tSV[LRuh# ݴQ˝Ypj( mٹΏîZ5ў-|Ȉo2opT$WSkK6ڄqFu>Xs0kJ܋vO qξY-,>OhCgp]o-_zna|6;Fc֗},Q]Nt{6!_ ~\;F6wU6Vq4.%w6,0dD’Nuu}佺Xj.s$76;<_0_rIGa_OJgiw!V/u[<6K7͜-mCUӼ1k:kVSFwϣ.#Y `]PVj)&hs!?dl [C讑ŶUK4i>wrmwź[j,:Okq~L 2 fN.\3}WǍpkger6R,zVSk-CL6lQCejvX]eE%ـ[[fddBX{8FMs;Yf[55F)5_1I&#dT,cz֟ZZMe|RiIV7ݚB!o܏#|w:w#[Iaɻ]|r7%I?ղG~A]tM kxNqZ^oKo?'\ުiM6.UG=ɶmբo''^ BuM;SAq/t|2]zv|a[uW}[ƚ]Z֫i:}N-CY-QFe; &:Mk<|v&yn/f{=5R<ÿdcV`Km^]J6bd<4G;>ԞH;Z0oJZB:"?+|:jS\:\]Rnxi*C"MֽvzΥy=܈ [[:$]jF(W@(1m}YIuo0kn/]! >n7HQ]yƪ~TW^-n7U]7inaml'im~K .ߗ7{أЬ|4YZk}EԶděE*lP+Iņoܪ`b; D`UW_JM4wz\RM5_RwP>.̥{֮ڴ 7{Zwɘ\HTo)nM{[Z隊Os.>sq:yY[ ZfYڅ;mnbK 4[̹N2w]4\P\{d۹|̠5>?ƭ58(岎M`ݨ6gX@gK?ErGi4e_jC0_GgqL_?k4 ޜ[3 ۽γlld Aw>0LeG%s4.ns[=j m5z֩}#}k_P-~&{ aa mi`2vv?k갳sE\@>ڪhPɱcT|lı(rvlz#L7SiF/N-h2HݎptqY=^@/GEϷ|w*q+`㯚mFI{H/`.okuD?l?;/Þ'<'q-ֽ&godwzs$N/D2M!£2ㅦܝ[9j+o:??P_d E mVyMz[ۗYӶ;WKXg"e ߨ-gQltHnLm&~ğ2ZDP~tfV|6쫖wUUqpm f+ɯ5%7tMCI7QD#,}6o5+\Ej_gv6~eA7okww}&j"{׷E?m"y@Yv^vvku]J]VOi⼅3f3Cv͝zrǡqxmoj5{ S8thXcojxUҦH-eYAqrUc`^LJrv$,R{Qs)_=`m[T(Ϲt.[0 ,2\Y|3]\;eY\zVzN<Em{5`YUÑe[-/ \Kihqis,j2Zs1>ɻxS/~$\ֶ?sYi#X{[9W\n&YSMϏu__eB[ 3RR$0ҫXNZ?C[r;^Yɪ8['}*9N WKI_׮-S dt"]\\7+3;nUjwk_x(uncP5]:hM弻 dokld4Dy,8QO)'1m^yH'5VMFތewc6۾Zb\뚥"n8Pb ZņSݱnk$uھq%< |FXݝQo]G5$$7DJ9{I[[ьe;W醻ͳk//1,HLpGU%|7.-oxP^m&zdV!E>a>mvYkbZV[$x^tp&=|=Ummd7bek~+5eD~sHLHw|Bk/y-?㮝-|)O_}-r KX`6}+Ic8Asŋksb-ɻy]!9WqNSk=z˦ʱE<)$Y [˓r3_wwZN);M&-d[mN!mt#]pL}7 lU7,wíZ[jZ ƞD :]?Όu/C ei06=xHeԁmϩ˩c Y:> {?^8<'|K!?>۹=xnF^{s'qHd>ܬh7M9cWy%"ɻ{,xX!omѼbTmGt;giٻWtGE{{x-U,vy%=b7n+Y:+QryVa(rVQ%G(|<v2vuWm{:\ڨ0q#4lo:RmaYNEߖ ]Ns. u'&MYrY{tstine`yWYn&C*?<}z"Pkl:Z~JB۲ F +)ysmERrF#ӾgxEcBˈUd9G@x%T[4^tN,:2{7͹MsK$k 5"}6nѮW̵E|к_v.(mȺ>qw5V&menɦo!39SkW-KZkzuljʚ]6Cg\lp!2rm>fܛYCiSqw1gilo n7(jemEM>+E ?IP "Zbk9$vShVs}QI//!xll<ﶆdHŗ5yq+^]"Y5,5x(edƍqoawޯ|"M1Gn0*6߻ߕi%Fm~u{; xY[i^lxu+?~*.MMM*GwUގ.UiS :? x|.gpceZkl+n|2J`0'uMMtuMPO宑ç rC0j}Aʭl) Bt|Fv=jeKRWI/Nү7zr^jzqdC4}|$1*0v.n.5xe3@!4Hz G??2m5$ѽI{;.v!h.lWb"ߵ|MnZgm{ YH+9,mCw% gvaV|Gky"\[O>.|}ek&]Y/v?fc{=5 +F jWIoh XCuws11m'`gI'.5E0V+hKv^r*.pOgo;i_I$l`{>eM戯Pi]]G.a}Ao.ͩh):剮pQsYWS}r^×mk+0|n#O$ɑ1fP1^x9ao h( [gqy. !勳eJlmܲ \Ev ~J]A ^$b7KŎb7~Q~^H$#\QKMiJ}›tT" 7gp~iI_[%$M6ڭ[^D)`|[, =֩j -]FGmNZ̈YIWy?RϧmocWGf`O|֠Mt0c6[Ih_ʪ۳a߁*C]e՜m};}Nm840El9%Kn\&q-~%]佹۽[~MVFv'RԘのc/oezs2/.v~A I).{Hn㺋7VZj5۲+ibdڥ о;]Fx nn`a\,W7_%w ߻wͺꌊ- :FmWI3A U -so|zCa<4uK˧MsRc:]ſؾ#A\Z.a,H1x<ͦO :j hb~@P7J>\ծ5V{+[7rݶ>n٬+>3\-Ym֣ -ȍ5/')Y6|qUOELj%aa -D< ں#/URw :ݽ7A$gis3<[wo2]ԧ!KEnOXb/춡esՓ;v(5 kIdwtq2,ίN]`9Y Ij}(-KWu-e6lf0o]ݓkr59D?2mFin#[ CY7nR2k/]]^:7do'XĝW/1VXYVR$gXBa`a~]枫S[>&gk[ >'.%3'Ral;GD#a+Clt%Kb+巆 -.~͟h>Q"wA] ϛ8bi.&/-C|,{QꟺR;;WC尷mRm ',ZKح41N>Owf$5T{N\vFP? ,J jWӭsL`yh6m;Gw殲ɢܻizŧMi \"}F>B*aֵm:[O s4}al`mPĝnNI[B,s'ۅհ7[ E3$*akuUiWb3I}%<5i6l`Y1ٸGwVgAgqw"O+xG)0&ࡉ˟*ELOD2Y$/uf%I&(̩3Ef)Z " 굔>B%Ņ vwSm.&ڽ]B5bO֒^$w* mYE-ڟuw_on vkS<$t(;_!q"kWG-6k'Szjou\iSۻZٜWph[@hb㺆 "?icԥ2>cjk${6rộm(4 i>c[XC[#{jA+dqi#W}~ͣ[E²~+4}ܴCx>^xMT캄6ZL~s7ҕ+;hbԽxv{}AӵƲ㲰APoO$qM.XINV ` z6v}FU2B ukqoۑEr65C^j)h^hXuxmHm/OM }oVOl4!MSM#nYxm5o$L|~eލ66'X$Fwnxv@2Us=\[V4FZTm~ϨQvuR6mېuٵ-/LMkcu8u]b⹎ &Ei$!le)WbW ;H՜f+qkdI Ϧ!KV6{BɽX_>fHҠ8.mm1޶eTQ>empY[i "Gm52ʚ~li}nӐ(.죋VG$..=BMeyMϵܟw}߻LEwicgKi.O%ȵ衋]͎*0Ք1Cu{.rȎ-I>)3. `gs(e|&GNt*+mDEmە[[3 o5'~Y[=<:\E~8 #mS&/AreԐYkݼ&{p)~DاxH3|RkuHԢ,X-8<|q[^(me}-7'DcSt͢qF])r4*Fe$%цw3p8iu5tv",D\bO4׵B *hImwF#1${MY~ogED ]]6Ndyx.%u䯩sTy5b""K{\ElabA3 Z[HĶ\}(ב.UL.IQvɯ4f{5އs;l_lo*lQ(Ps|r|5]FM-|?:|_궰܈`Xvȝ}[KEqs%~%Օ㘜_W\{Vg٬. Zgɴx5yѲ]fn2qnIb5s~g]%ڐ/5"-c6M&Q{TIE,7J_~z5.Zj6VsCۛksmqAB^r;iJu5NF]VJWSc%̿؟p̨WlEZKyex{%ä 6.>og?yW9fq xi4Y]\-uH洛WVI+nftھ B6V3 )q~|ʂ[' crn)5;d7^V"y^!0ĬNn ūOm mcNQ֯:HPD/{3Kw, KB2Fܴ{#(:[h^)l4FĒf*b#mWrmFL}Ʒu=u [4Zm/;͊ #ٱ0x_]WR+ }Msiw#G:_Y @}3/wYK=ݵ:(9"lſw#)j> ėQy_b$;'mZ)̈́U5VўE ;6o.bZI- 6!U?ݡk;K|Kqo6ir^]GE";^KTTlu[hog:KAde`ySj'ެKϚ-J@ޟojI;q8@#X-tKu꦳t4st]cWՎHaT#HJ ;:~oͭ=&(,{do61>gٌ%qE$Bfotoxx-18.01.1/data/favorites/0000755000175000017500000000000013222767271014575 5ustar micomicofotoxx-18.01.1/data/favorites/menu-config-pixbuf-012.png0000644000175000017500000000236213222767271021310 0ustar micomicoPNG  IHDR szzsBIT|dIDATXŗmLe39Hl-a1d(SojrKs%Ul>87[X`6Y( A3H휫99 s_}]ϳ~ FnˁL`&0=* xAʁ ЛPaT]HTQyd#D N' ^ΜB>p8F닑Î@P񑑆s(c<@Hɟzro vG/F'q%&sІu(3N247 :ݎv@sI&^Dm-JtaO|74~ϴo%C,٩r0y54'9 S|eǦi"t:f}_$\3mvnn0?4-myŏ:3x: "+3p;Z6luYvKb)40Dz+k~}1~0h`-`~5}gDܩQTwp|"C[7i2H2EqqLX<0 : *| $.L uACW%ZSgtVeK`ܸհGd^7|^ /,ђpWkßzZYhaɲ߲eSa =s(0Tm˲kajb_0 PSvEO.]O N8X4+s8t7-ⵡfx0:O86>)fGھchB75@kvC'O D}TK'T;Ϋ݁ 6ׯ?*䭭ƴrkG^\.ר\.2{/UЍǨ%n(--Yo݄N] p:ں)7s 7$v21yiiRfj[$$A)D)f?\Ia+Ba+++BpUS\Ct4HP~>Ġb*HOO')) Z8zŔl,ag{x x8r/,|<?_l,<0̛11?a pWuHKle3ȕg~IENDB`fotoxx-18.01.1/data/favorites/menu-config-pixbuf-015.png0000644000175000017500000000536613222767271021322 0ustar micomicoPNG  IHDR szzsBIT|d IDATXW{pTsfw I6yQiDӱ:>it4HhEZ#E SAy8B1 #50@b$!G&tos9:oɲi8$ #iZӂ b,K'PXgrn;ײ2RJlٲ٩'yb[&w:K YjUaaxs9UUﺵf9+ͻyfGCu)KX|@ )&+|ZVVVSRR2n3!tnUeehF̆2Bh{3"Yxq͛FQCUU/H]]t:-⺮sY(0 9T񙧌JwVH$b_O?]Xhɶ[6k]dɒj~?c`0(VVVWTT(s4G DF~ B_7CL29lXܺޞMnz_MM3f̸1˲`&@$^aCQBp8PPP]?A}}=  n6lX8TUFE1weEE$80 aEA(B,!e!\,+s*'a+PTXS"g&vR|^ziߔ)S|@ F8|0 1uTB@(At]Rr9O=UV]xr]ՠ:N'^/l6ɤnݺ+VEQꊎ*D]QH۝l+f\-Ih>Ղ';3IZ@Qk 5j0M$vsBd2gd彨4f+_R g;,x*z"JWր2W50w ~ΦJ⍺{xaٽ77S#>ł/|?Ǘ^X;`etE8E0uRRL<'LfgA"> tEfqT;Xz: K3A  HR0 #p82|O?g:y4"vDX@4">T?u]pD@ÙY98, t]RA)fnGsss˖-1d͸gbTPr7E}6ў/.)M#x<_D:%edR \$Ic H-]oll|oϞ=q߉Gߐwٕ"ڎ[l!&Ub|gϙ6(J  @~s-ݭW\qeϟH$>hE^!H䁰/t.%IlV:hǟ񞾅:(ɔUQH$76Yf/>tbccҊ387 !lvvsfL.ql&I2P>ollnn@h4ZTTΎ kލ;I"QV  VJ( 1@ P64֤vm``0::ddN.//~}P $q'7BB1P( {p&& IENDB`fotoxx-18.01.1/data/favorites/menu-config-pixbuf-006.png0000644000175000017500000000237613222767271021320 0ustar micomicoPNG  IHDR szzsBIT|dIDATXOUΕ-"K@ElTcb4i|>j&IL[F qCcJ6K);3LJJIg9s~ N>UU3 p_^LMMM9<<Q!If_$I.jM099s6=8I󤽽}mۭ\nRlPT<۶W1M!,b߾}/83555`6&&&~mmm=J{w!wnjiiysvvT*Bm mжm~;!mmm+r}yy9|ťD"!W}mL7oN>`}}cڶ$i}Rimnny4T1 ,;vH,˂rf]ו%)0 ~u/H0[( xY' ( zt2*iEtttdt]w] KLSQMpt:b7nZYYY AxBaq#TU}d [\*_ܾ}{R|u]eYgieΝ;hYցRtZ(H&~]H$<ٳgə3g "x~ ՐT0p#.\ĉ, >H?N($bX,v<|6OH|Y4xaFŲ/DBE<& T*E$ITejYE[x=.BTBAi\ni8+[DeFA$<Ӻ:*$,aB_j "j(BA SJɇ=^ |k'LJ$=XnzܼJ ] wlŷ0[ۜTJulDEł别㭻oB9{q9< g3hmx軌cWYXQ"1w׿]x1\0\ q3 :GÕ'+giN͢|{Ǔ^o*pJe3I}ݺ*D\/>"Z&ESt>ֿo?g$D=:rCktOq/P&ˠ ן\=;IM 97#8{'~Ju.\ Վ0\C$kWÊZƵ_]ߌk jVs33 s/ĨS71\k(Q۔*­%'c rMj4Hp#Q#GÐ|0 $nj;5p(}/?3ԺTInǎj?;tf&\rq#}=d\y}ݏ"+ apY X(E=,RLvd8n2&p߮S%EթXF. .yFtg\Gj$rMÏf._~eY͠(wa(Oݺ]+4i{s5t- V&]]-=W FbwnQY|}laV(nFgXYͯpa @ d$?e89ܦ䥥vOvt(+E.и'W7(/FX_dJp@TY(nf$~UU qnKɠ!Lg*LSYz_9Qk\⋫>/"(̱`> Gt\]B%iּpĹlo]T i#XnlEJ_J4YLz@ ʂP3Rʢpc 2 B$kM;RppH+15J2<<<7v޴gV F8v%>M>goo׿I.`)E"NE9mؙ7% Ө߭ӓcs6Bhjw`BoH6jf4C)=a{Rq,ڝ hEgP`@"Kzh/fW!f/Nl`:IfyQ k^TkѸ/ !EּX…kwN B")ZOKB lP+A V`iu k+KJt!tb( =1f^lx } م+7zfms̕gи P_*}/ݸL4ZB*n9A+ 0*prg XȁJr]Gr 1_'71?_pbOvO~o?O@2dGe7x?|NkmwX)3G',doC^Giz+u(8yg݃"'ǃ H@`g<-˃]nD2CZ+dRY4+beQqOf{2L\x޹`&xOz,J҈ȘQ,f_g) L"ZbQ)=5<FH-ʍܾV%Ӊ.O\W:46g1/g9-d.3O'fЂUՊ}bd2®]!5E K+ 9q{WsIR]mUq~5&&7Bg'ӯ]͛"NR###՘Jt: ! dB8cl~En|nN[X,In!p.1@) ۷<B~`p!4+JYZ,RKzB7J0W J]w2-p OO#/+W Ξa٪NSQf3!$HJtwեS<|LRUߡPi~,cccUxK\@בӉ\.@H>yRڿ$\.7N'ɅQ3DD6?E<~ܜ6_;7l>_uǖeȪv8vnXQ 9+ar`dY ;Isfj*,_咎aB=~`1)5<ѯ^?[ ^y3gxh@|z|C"*XQݻǏ4>_lq}?]Ep ҁd`mx-R.BH?CIENDB`fotoxx-18.01.1/data/favorites/menu-config-pixbuf-007.png0000644000175000017500000000423713222767271021317 0ustar micomicoPNG  IHDR ǍsBIT|dVIDATHMnFvybF;fd3irnE^X)kͅd (Ov_;ZC///ض88x<3b&vbIV+|Gj" C%RJ 0 }zwvvf/+8Cf|z%NOOA-ZO缾98g>^}4Mt:f3\%2fq3988DZ~zzoR2Ϲ% CRL&  lۦ( Y8UUQU{{{huEhX.# }0`ZyE//cJ) $IZQJyDQiZ\]]<(xxx I{R![y}-ˢ,K0$"(`v~GǤiZWMI99y^`ض] .6㘪ڜ4&MS(FJY#,*N<<>&s5,Y,Z684M>} exy!enՊ$IjgPkjBk bnKQ8UlJgX5xzzq{{777(%''"dZ1OX,iﹾ&B:6>(j~ CFNǍl"W4 }앗zR6}CJIQee)Y1L|>'c%_fZX,1N'(vż|AѨg&JI|߶a3E9v0n=WlpMzzzfna|5,K44>o}5}gJ'_לPU۶0,d4@c8b1Nspp@YP# !68D䄯_`0d:pttliE1m#6la-&eqR7:M|E髫+:J))R*^E),c4GjǏ^hZضMUU\\\.;;;ݱ7Z|h4lZDZ1 V~!eYՙp3_aH>.Fqvw-h4, ,Ƕ-\׭,"MӍ&\ש~/ۗKZW"ZQF$Y!B^OJTaheEEx 5qqqq2gϿIENDB`fotoxx-18.01.1/data/favorites/menu-config-pixbuf-003.png0000644000175000017500000000173413222767271021312 0ustar micomicoPNG  IHDR sBITOIDATHVMO*=n;N,h#n\` K EWBQ&Laz>IӧO[qDZmqιitZ|S뺶m p8dd21u`D]5MS0 ? D!xa$zpppvv40jpBEӤ(FT*5S> vyh4g,˺]u !$mll۶]8"!WWWz]bu:4M@|˲j7 e 4myy9aEkY|B\ȫIԇLDh|0ƊI$-BĠ~@B 0bLo"q3T8`!m{jcB!"ιaBju !iJ9P$ d2;;;mFaa2Ƃ (/IBժmnFUjjZ1BTUv:p1fB)5M3B!,XB(b⯶usrf)½=՛"06=>> PJKu ȲlLY@@$!0}~Fs~\}(}W "˲z^n!f!шs>?$(zt:ҭl6K)%!4 !(l6|?B`f՛_ 2LRYZZx(TJ$ T*]'beMf!T*1!`㵵 +biHZױȟGa >FIENDB`fotoxx-18.01.1/data/favorites/menu-config0000644000175000017500000000277413222767271016741 0ustar micomicopopup 169 115 441 287 posn 100 12 menu trim\nrotate func Trim/Rotate icon /usr/share/fotoxx/data/favorites/menu-config-pixbuf-000.png size 40 posn 20 12 menu recent\nimages func Recently Seen Images icon /usr/share/fotoxx/data/favorites/menu-config-pixbuf-001.png size 32 posn 100 108 menu retouch\ncombo func Retouch Combo icon /usr/share/fotoxx/data/favorites/menu-config-pixbuf-003.png size 32 posn 180 12 menu edit\nbrightness func Edit Brightness icon /usr/share/fotoxx/data/favorites/menu-config-pixbuf-004.png size 32 posn 180 132 menu gradients func Gradients posn 268 12 menu sharpen func Sharpen icon /usr/share/fotoxx/data/favorites/menu-config-pixbuf-006.png size 32 posn 268 148 menu denoise func denoise icon /usr/share/fotoxx/data/favorites/menu-config-pixbuf-007.png size 32 posn 20 92 menu newest\nimages func newest images posn 20 140 menu sync\ngallery func Sync Gallery posn 180 84 menu flatten func Flatten posn 100 188 menu warp\ncurved func warp curved icon /usr/share/fotoxx/data/favorites/menu-config-pixbuf-011.png size 32 posn 268 76 menu red eyes func Red Eyes icon /usr/share/fotoxx/data/favorites/menu-config-pixbuf-012.png size 32 posn 350 90 menu edit\nmetadata func edit metadata posn 348 12 menu user\nsettings func user settings icon /usr/share/fotoxx/data/favorites/menu-config-pixbuf-015.png size 32 posn 350 190 menu favorites\nhelp func help icon /usr/share/fotoxx/data/favorites/menu-config-pixbuf-016.png size 32 fotoxx-18.01.1/data/favorites/menu-config-pixbuf-016.png0000644000175000017500000000406213222767271021313 0ustar micomicoPNG  IHDR sBITOIDATHVoWe.{=_b;:6I@TEQ*T@B} H ^@BH-Whh Ui6M\'M6-kzgg̜ÚMoaF]c Bg3& [uU6"@Bzɓ4IzceI%a+y㝢ݲ=92 ƠU'u^}crq1TN౾'v?]~91N>_׳8@{l)>~gFOrR.w\ܹlRt)G׮LMҥ(*KK+T !ܩvbߩB!N{MW__Z\'_ɍ%q l.J۷5G6ҳ'f+TJ !,]ס2._v巧\^i~w"B)eh<|s]xMgս`RJ9Jɹ]6DdAK['^it#D$8X ɔWFݥBIfJP@ TBQJEq+ ܸ˺ePhjժeYR)u,w(g`-{RDQB)emfVNG pDT8ᕏ*nTEX-B6җIyeMI"V}D<11X{VK?Ç##Q)Axk2CJ!hF͕Uӣ,U!Mn/9"PHMuHC1_(v._l NO)B}rgt9?LTnPCbKuX`@8$^ ;0fvW5HDish=بB:DHךlԟmDǏ B!kwaaR,..NJJB*++M&VEfff8al͟[S~ނ\H kjGݺu afffTTIj]3::VFLg Fq͗.]JRnK0liiٳgׯ_x~f"#####9vcׯPqqa}}N7nY$`nn"""JJJ&&&jkkB pcW^j4vܙdZi.//_]]w`Xf=z0[>?44dX@"pYIIIj|ϗr4FA~Ν;Zjz< jii r4a `b?߼ywzzh4RE ٳg˖`0(޽k.0ӧOhtܛ6rt:l 8_vk݆Ào0 c˖-þ}pdž؎0Bl6] ZP(B L&SGGI!:88H+ǎ`0BR|~QQ6nht>|xSSSJp~?B׻~㉊bp8w[YYinn6 n•+Wء!$I\. hnmmmiix<_ t ð!i]OV*G211aǏ!n{~~?p||iBhllͶmJ,33r޿hoozeee|>b<{MuOO"399t:aL&Ǐu<<omA?x<;;at:ZN'SSS>yeeɓbSN"LNN666YVp8RN'j4g0Lcc֭F3G0cAIENDB`fotoxx-18.01.1/data/netmap_locations0000644000175000017500000000033313222767271016054 0ustar micomicoBrazil|-13.7100|-55.3052|4 China|34.3434|100.9863|4 Europe|48.0634|12.6562|4 France|46.6834|2.6477|6 Germany|50.7260|10.7336|6 Italy|42.8115|12.6068|6 Spain|40.7556|-4.2188|6 UK|53.7942|-4.2352|6 USA|40.2628|-97.9980|4 fotoxx-18.01.1/f.repair.cc0000644000175000017500000114246513222767271013714 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit - Repair menu functions m_sharpen sharpen an image m_blur blur an image m_denoise remove noise from an image m_redeye remove red-eyes from flash photos m_color_mode convert between color, B&W, sepia, positive, negative m_colordep reduce color depth from 16 ... 1 bits m_shift_colors gradually shift selected RGB colors into other colors m_colorsat adjust color saturation m_adjust_RGB adjust brightness/color using RGB or CMY colors m_adjust_HSL adjust color using HSL model m_zonal_colors shift RGB colors in selected image areas m_match_color adjust image colors to match those in a chosen image m_color_fringes stretch/shrink RGB color planes to remove color fringes m_smart_erase replace pixels inside selected areas with background m_bright_ramp adjust brightness graduaally across the image m_remove_dust remove dust specs from an image m_anti_alias suppress pixel steps (jaggies) from image edges m_stuck_pixels find and fix stuck pixels from camera sensor defects m_paint_transp paint more or less transparency on an image m_add_transp add transparency based on image attributes *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // image sharpen functions namespace sharpen { int ww, hh; // image dimensions int UM_radius, UM_amount, UM_thresh; int GR_amount, GR_thresh; int KH_radius; int MD_radius, MD_dark, MD_light, *MD_britemap; char sharp_function[4] = ""; int brhood_radius; float brhood_kernel[200][200]; // up to radius = 99 char brhood_method; // g = gaussian, f = flat distribution float *brhood_brightness; // neighborhood brightness per pixel VOL int sharp_cancel, brhood_cancel; // avoid GCC optimizing code away editfunc EFsharp; } // menu function void m_sharpen(GtkWidget *, cchar *menu) { using namespace sharpen; int sharp_dialog_event(zdialog *zd, cchar *event); void * sharp_thread(void *); int ii; F1_help_topic = "sharpen_image"; EFsharp.menuname = menu; EFsharp.menufunc = m_sharpen; EFsharp.funcname = "sharpen"; EFsharp.Farea = 2; // select area usable EFsharp.threadfunc = sharp_thread; // thread function EFsharp.Frestart = 1; // allow restart EFsharp.FusePL = 1; // use with paint/lever edits OK EFsharp.Fscript = 1; // scripting supported if (! edit_setup(EFsharp)) return; // setup edit ww = E3pxm->ww; // image dimensions hh = E3pxm->hh; /*** _________________________________________ | Sharpen | | | | [] unsharp mask radius [__] | | amount [__] | | threshold [__] | | | | [] gradient amount [__] | | threshold [__] | | | | [] Kuwahara radius [__] | | | | [] median diff radius [__] | | dark [__] | | light [__] | | | | [reset] [apply] [done] [cancel] | |_________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Sharpen"),Mwin,Breset,Bapply,Bdone,Bcancel,null); EFsharp.zd = zd; zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); // unsharp mask zdialog_add_widget(zd,"vbox","vb21","hb2",0,"space=2"); zdialog_add_widget(zd,"label","space","hb2",0,"expand"); zdialog_add_widget(zd,"vbox","vb22","hb2",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vb23","hb2",0,"homog|space=2"); zdialog_add_widget(zd,"check","UM","vb21","unsharp mask","space=5"); zdialog_add_widget(zd,"label","lab21","vb22",Bradius); zdialog_add_widget(zd,"label","lab22","vb22",Bamount); zdialog_add_widget(zd,"label","lab23","vb22",Bthresh); zdialog_add_widget(zd,"zspin","radiusUM","vb23","1|20|1|2"); zdialog_add_widget(zd,"zspin","amountUM","vb23","1|200|1|100"); zdialog_add_widget(zd,"zspin","threshUM","vb23","1|100|1|0"); zdialog_add_widget(zd,"hsep","sep3","dialog"); // gradient zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vb31","hb3",0,"space=2"); zdialog_add_widget(zd,"label","space","hb3",0,"expand"); zdialog_add_widget(zd,"vbox","vb32","hb3",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vb33","hb3",0,"homog|space=2"); zdialog_add_widget(zd,"check","GR","vb31","gradient","space=5"); zdialog_add_widget(zd,"label","lab32","vb32",Bamount); zdialog_add_widget(zd,"label","lab33","vb32",Bthresh); zdialog_add_widget(zd,"zspin","amountGR","vb33","1|400|1|100"); zdialog_add_widget(zd,"zspin","threshGR","vb33","1|100|1|0"); zdialog_add_widget(zd,"hsep","sep4","dialog"); // kuwahara zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=5"); zdialog_add_widget(zd,"check","KH","hb4","Kuwahara","space=3"); zdialog_add_widget(zd,"label","space","hb4",0,"expand"); zdialog_add_widget(zd,"label","lab42","hb4",Bradius,"space=3"); zdialog_add_widget(zd,"zspin","radiusKH","hb4","1|9|1|1"); zdialog_add_widget(zd,"hsep","sep5","dialog"); // median diff zdialog_add_widget(zd,"hbox","hb5","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vb51","hb5",0,"space=2"); zdialog_add_widget(zd,"label","space","hb5",0,"expand"); zdialog_add_widget(zd,"vbox","vb52","hb5",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vb53","hb5",0,"homog|space=2"); zdialog_add_widget(zd,"check","MD","vb51","median diff","space=5"); zdialog_add_widget(zd,"label","lab51","vb52",Bradius); zdialog_add_widget(zd,"label","lab52","vb52",ZTX("dark")); zdialog_add_widget(zd,"label","lab53","vb52",ZTX("light")); zdialog_add_widget(zd,"zspin","radiusMD","vb53","1|20|1|3"); zdialog_add_widget(zd,"zspin","darkMD","vb53","0|20|1|1"); zdialog_add_widget(zd,"zspin","lightMD","vb53","0|20|1|1"); zdialog_restore_inputs(zd); zdialog_fetch(zd,"UM",ii); // set function from checkboxes if (ii) strcpy(sharp_function,"UM"); zdialog_fetch(zd,"GR",ii); if (ii) strcpy(sharp_function,"GR"); zdialog_fetch(zd,"KH",ii); if (ii) strcpy(sharp_function,"KH"); zdialog_fetch(zd,"MD",ii); if (ii) strcpy(sharp_function,"MD"); zdialog_run(zd,sharp_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion callback function int sharp_dialog_event(zdialog *zd, cchar *event) // reworked for script files { using namespace sharpen; if (strmatch(event,"focus")) return 1; zdialog_fetch(zd,"radiusUM",UM_radius); // get all parameters zdialog_fetch(zd,"amountUM",UM_amount); zdialog_fetch(zd,"threshUM",UM_thresh); zdialog_fetch(zd,"amountGR",GR_amount); zdialog_fetch(zd,"threshGR",GR_thresh); zdialog_fetch(zd,"radiusKH",KH_radius); zdialog_fetch(zd,"radiusMD",MD_radius); zdialog_fetch(zd,"darkMD",MD_dark); zdialog_fetch(zd,"lightMD",MD_light); if (strmatch(event,"apply")) zd->zstat = 2; // from script file if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() sharp_cancel = 0; if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; edit_reset(); return 1; } if (zd->zstat == 2) { // apply zd->zstat = 0; if (*sharp_function) signal_thread(); else zmessageACK(Mwin,Bnoselection); // no choice made 17.04.1 return 1; } if (zd->zstat == 3) { edit_done(0); // done return 1; } sharp_cancel = 1; // cancel or [x] edit_cancel(0); // discard edit return 1; } if (strmatch(event,"blendwidth")) signal_thread(); if (strmatchV(event,"UM","GR","KH","MD",null)) { zdialog_stuff(zd,"UM",0); // make checkboxes like radio buttons zdialog_stuff(zd,"GR",0); zdialog_stuff(zd,"KH",0); zdialog_stuff(zd,"MD",0); zdialog_stuff(zd,event,1); strcpy(sharp_function,event); // set chosen method } return 1; } // sharpen image thread function void * sharp_thread(void *) { using namespace sharpen; int sharp_UM(); int sharp_GR(); int sharp_KH(); int sharp_MD(); while (true) { thread_idle_loop(); // wait for work or exit request if (strmatch(sharp_function,"UM")) sharp_UM(); if (strmatch(sharp_function,"GR")) sharp_GR(); if (strmatch(sharp_function,"KH")) sharp_KH(); if (strmatch(sharp_function,"MD")) sharp_MD(); CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); } return 0; // not executed, stop g++ warning } // image sharpen function using unsharp mask int sharp_UM() { using namespace sharpen; void brhood_calc(int radius, char method); // compute neighborhood brightness void * sharp_UM_wthread(void *arg); int ii, cc; cc = ww * hh * sizeof(float); brhood_brightness = (float *) zmalloc(cc); brhood_calc(UM_radius,'f'); if (sharp_cancel) return 1; if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = ww * hh; Fbusy_done = 0; for (ii = 0; ii < NWT; ii++) // start worker threads start_wthread(sharp_UM_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = 0; zfree(brhood_brightness); return 1; } void * sharp_UM_wthread(void *arg) // worker thread function { using namespace sharpen; int index = *((int *) arg); int px, py, ii, dist = 0; float amount, thresh, bright; float mean, incr, ratio, f1, f2; float red1, green1, blue1, red3, green3, blue3; float *pix1, *pix3; for (py = index; py < hh; py += NWT) // loop all image3 pixels for (px = 0; px < ww; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } amount = 0.01 * UM_amount; // 0.0 to 2.0 thresh = 0.4 * UM_thresh; // 0 to 40 (256 max. possible) pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel bright = pixbright(pix1); if (bright < 1) continue; // effectively black ii = py * ww + px; mean = brhood_brightness[ii]; incr = (bright - mean); if (fabsf(incr) < thresh) continue; // omit low-contrast pixels incr = incr * amount; // 0.0 to 2.0 if (bright + incr > 255) incr = 255 - bright; ratio = (bright + incr) / bright; if (ratio < 0) ratio = 0; red1 = pix1[0]; // input RGB green1 = pix1[1]; blue1 = pix1[2]; red3 = ratio * red1; // output RGB if (red3 > 255) red3 = 255; green3 = ratio * green1; if (green3 > 255) green3 = 255; blue3 = ratio * blue1; if (blue3 > 255) blue3 = 255; if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; red3 = f1 * red3 + f2 * red1; green3 = f1 * green3 + f2 * green1; blue3 = f1 * blue3 + f2 * blue1; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; Fbusy_done++; // track progress if (sharp_cancel) break; } exit_wthread(); return 0; // not executed, avoid gcc warning } // sharpen image by increasing brightness gradient int sharp_GR() { using namespace sharpen; void * sharp_GR_wthread(void *arg); if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = ww * hh; Fbusy_done = 0; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(sharp_GR_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = 0; return 1; } void * sharp_GR_wthread(void *arg) // worker thread function { using namespace sharpen; float *pix1, *pix3; int ii, px, py, dist = 0; int nc = E1pxm->nc; float amount, thresh; float b1, b1x, b1y, b3x, b3y, b3, bf, f1, f2; float red1, green1, blue1, red3, green3, blue3; int index = *((int *) arg); amount = 1 + 0.01 * GR_amount; // 1.0 - 5.0 thresh = GR_thresh; // 0 - 100 for (py = index + 1; py < hh; py += NWT) // loop all image pixels for (px = 1; px < ww; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel b1 = pixbright(pix1); // pixel brightness, 0 - 256 if (b1 == 0) continue; // black, don't change b1x = b1 - pixbright(pix1-nc); // horiz. brightness gradient b1y = b1 - pixbright(pix1-nc * ww); // vertical f1 = fabsf(b1x + b1y); if (f1 < thresh) // moderate brightness change for f1 = f1 / thresh; // pixels below threshold gradient else f1 = 1.0; f2 = 1.0 - f1; b1x = b1x * amount; // amplified gradient b1y = b1y * amount; b3x = pixbright(pix1-nc) + b1x; // + prior pixel brightness b3y = pixbright(pix1-nc * ww) + b1y; // = new brightness b3 = 0.5 * (b3x + b3y); b3 = f1 * b3 + f2 * b1; // possibly moderated bf = b3 / b1; // ratio of brightness change if (bf < 0) bf = 0; if (bf > 4) bf = 4; red1 = pix1[0]; // input RGB green1 = pix1[1]; blue1 = pix1[2]; red3 = bf * red1; // output RGB if (red3 > 255.9) red3 = 255.9; green3 = bf * green1; if (green3 > 255.9) green3 = 255.9; blue3 = bf * blue1; if (blue3 > 255.9) blue3 = 255.9; if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; red3 = f1 * red3 + f2 * red1; green3 = f1 * green3 + f2 * green1; blue3 = f1 * blue3 + f2 * blue1; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; Fbusy_done++; // track progress if (sharp_cancel) break; } exit_wthread(); return 0; // not executed, avoid gcc warning } // sharpen edges using the Kuwahara algorithm int sharp_KH() { using namespace sharpen; void * sharp_KH_wthread(void *arg); if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = ww * hh; Fbusy_done = 0; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(sharp_KH_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = 0; return 1; } void * sharp_KH_wthread(void *arg) // worker thread function { using namespace sharpen; float *pix1, *pix3; int px, py, qx, qy, rx, ry; int ii, rad, N, dist = 0; float red, green, blue, red2, green2, blue2; float vmin, vall, vred, vgreen, vblue; float red3, green3, blue3; float f1, f2; int index = *((int *) arg); rad = KH_radius; // user input radius N = (rad + 1) * (rad + 1); for (py = index + rad; py < hh-rad; py += NWT) // loop all image pixels for (px = rad; px < ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } vmin = 99999; red3 = green3 = blue3 = 0; for (qy = py - rad; qy <= py; qy++) // loop all surrounding neighborhoods for (qx = px - rad; qx <= px; qx++) { red = green = blue = 0; red2 = green2 = blue2 = 0; for (ry = qy; ry <= qy + rad; ry++) // loop all pixels in neighborhood for (rx = qx; rx <= qx + rad; rx++) { pix1 = PXMpix(E1pxm,rx,ry); red += pix1[0]; // compute mean RGB and mean RGB**2 red2 += pix1[0] * pix1[0]; green += pix1[1]; green2 += pix1[1] * pix1[1]; blue += pix1[2]; blue2 += pix1[2] * pix1[2]; } red = red / N; // mean RGB of neighborhood green = green / N; blue = blue / N; vred = red2 / N - red * red; // variance RGB vgreen = green2 / N - green * green; vblue = blue2 / N - blue * blue; vall = vred + vgreen + vblue; // save RGB values with least variance if (vall < vmin) { vmin = vall; red3 = red; green3 = green; blue3 = blue; } } if (sa_stat == 3 && dist < sa_blend) { // if select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); // input pixel red3 = f1 * red3 + f2 * pix1[0]; green3 = f1 * green3 + f2 * pix1[1]; blue3 = f1 * blue3 + f2 * pix1[2]; } pix3 = PXMpix(E3pxm,px,py); // output pixel pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; Fbusy_done++; // track progress if (sharp_cancel) break; } exit_wthread(); return 0; // not executed, avoid gcc warning } // sharpen edges using the median difference algorithm int sharp_MD() { using namespace sharpen; void * sharp_MD_wthread(void *arg); int px, py, ii; float *pix1; MD_britemap = (int *) zmalloc(ww * hh * sizeof(int)); for (py = 0; py < hh; py++) // loop all pixels for (px = 0; px < ww; px++) { pix1 = PXMpix(E1pxm,px,py); // initz. pixel brightness map ii = py * ww + px; MD_britemap[ii] = pix1[0] + pix1[1] + pix1[2]; // R + G + B } if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = ww * hh; Fbusy_done = 0; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(sharp_MD_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = 0; zfree(MD_britemap); return 1; } void * sharp_MD_wthread(void *arg) // worker thread function { using namespace sharpen; int index = *((int *) arg); int rad, dark, light, *britemap; int ii, px, py, dist = 0; int dy, dx, ns; float R, G, B, R2, G2, B2; float F, f1, f2; float *pix1, *pix3; int bright, median; int bsortN[1681]; // radius <= 20 (41 x 41 pixels) rad = MD_radius; // parameters from dialog dark = MD_dark; light = MD_light; britemap = MD_britemap; for (py = index+rad; py < hh-rad; py += NWT) // loop all image3 pixels for (px = rad; px < ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel ns = 0; for (dy = py-rad; dy <= py+rad; dy++) // loop surrounding pixels for (dx = px-rad; dx <= px+rad; dx++) // get brightness values { ii = dy * ww + dx; bsortN[ns] = britemap[ii]; ns++; } HeapSort(bsortN,ns); // sort the pixels median = bsortN[ns/2]; // median brightness R = pix3[0]; G = pix3[1]; B = pix3[2]; bright = R + G + B; if (bright < median) { F = 1.0 - 0.1 * dark * (median - bright) / (median + 100); // 0.91 ... 1.0 R2 = R * F; G2 = G * F; B2 = B * F; if (R2 > 0 && G2 > 0 && B2 > 0) { R = R2; G = G2; B = B2; } } if (bright > median) { F = 1.0 + 0.03 * light * (bright - median) / (median + 100); // 1.0 ... 1.026 R2 = R * F; G2 = G * F; B2 = B * F; if (R2 < 255 && G2 < 255 && B2 < 255) { R = R2; G = G2; B = B2; } } if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; R = f1 * R + f2 * pix1[0]; G = f1 * G + f2 * pix1[1]; B = f1 * B + f2 * pix1[2]; } pix3[0] = R; pix3[1] = G; pix3[2] = B; Fbusy_done++; // track progress } exit_wthread(); return 0; // not executed, avoid gcc warning } // Compute the mean brightness of all pixel neighborhoods, // using a Guassian or a flat distribution for the weightings. // If a select area is active, only inside pixels are calculated. // The flat method is 10-100x faster than the Guassian method. void brhood_calc(int radius, char method) { using namespace sharpen; void * brhood_wthread(void *arg); int rad, radflat2, dx, dy, ii; float kern; brhood_radius = radius; brhood_method = method; if (brhood_method == 'g') // compute Gaussian kernel { // (not currently used) rad = brhood_radius; radflat2 = rad * rad; for (dy = -rad; dy <= rad; dy++) for (dx = -rad; dx <= rad; dx++) { if (dx*dx + dy*dy <= radflat2) // cells within radius kern = exp( - (dx*dx + dy*dy) / radflat2); else kern = 0; // outside radius brhood_kernel[dy+rad][dx+rad] = kern; } } if (sa_stat == 3) Fbusy_goal = sa_Npixel; // set up progress tracking else Fbusy_goal = ww * hh; Fbusy_done = 0; for (ii = 0; ii < NWT; ii++) // start worker threads start_wthread(brhood_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = 0; return; } // worker thread function void * brhood_wthread(void *arg) { using namespace sharpen; int index = *((int *) arg); int rad = brhood_radius; int ii, px, py, qx, qy, Fstart; float kern, bsum, bsamp, bmean; float *pixel; if (brhood_method == 'g') // use round gaussian distribution { for (py = index; py < hh; py += NWT) for (px = 0; px < ww; px++) { if (sa_stat == 3 && sa_mode != mode_image) { // select area, not whole image ii = py * ww + px; // use only inside pixels if (! sa_pixmap[ii]) continue; } bsum = bsamp = 0; for (qy = py-rad; qy <= py+rad; qy++) // computed weighted sum of brightness for (qx = px-rad; qx <= px+rad; qx++) // for pixels in neighborhood { if (qy < 0 || qy > hh-1) continue; if (qx < 0 || qx > ww-1) continue; kern = brhood_kernel[qy+rad-py][qx+rad-px]; pixel = PXMpix(E1pxm,qx,qy); bsum += pixbright(pixel) * kern; // sum brightness * weight bsamp += kern; // sum weights } bmean = bsum / bsamp; // mean brightness ii = py * ww + px; brhood_brightness[ii] = bmean; // pixel value Fbusy_done++; // track progress if (sharp_cancel) break; } } if (brhood_method == 'f') // use square flat distribution { Fstart = 1; bsum = bsamp = 0; for (py = index; py < hh; py += NWT) for (px = 0; px < ww; px++) { if (sa_stat == 3 && sa_mode != mode_image) { // select area, not whole image ii = py * ww + px; // compute only inside pixels if (! sa_pixmap[ii]) { Fstart = 1; continue; } } if (px == 0) Fstart = 1; if (Fstart) { Fstart = 0; bsum = bsamp = 0; for (qy = py-rad; qy <= py+rad; qy++) // add up all columns for (qx = px-rad; qx <= px+rad; qx++) { if (qy < 0 || qy > hh-1) continue; if (qx < 0 || qx > ww-1) continue; pixel = PXMpix(E1pxm,qx,qy); bsum += pixbright(pixel); bsamp += 1; } } else { qx = px-rad-1; // subtract first-1 column if (qx >= 0) { for (qy = py-rad; qy <= py+rad; qy++) { if (qy < 0 || qy > hh-1) continue; pixel = PXMpix(E1pxm,qx,qy); bsum -= pixbright(pixel); bsamp -= 1; } } qx = px+rad; // add last column if (qx < ww) { for (qy = py-rad; qy <= py+rad; qy++) { if (qy < 0 || qy > hh-1) continue; pixel = PXMpix(E1pxm,qx,qy); bsum += pixbright(pixel); bsamp += 1; } } } bmean = bsum / bsamp; // mean brightness ii = py * ww + px; brhood_brightness[ii] = bmean; Fbusy_done++; // track progress if (sharp_cancel) break; } } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // image blur function namespace blur_names { float blur_radius; float blur_weight[202][202]; // up to blur radius = 200 int blur_Npixels; char *blur_flags; VOL int blur_cancel; // GCC inconsistent editfunc EFblur; } // menu function void m_blur(GtkWidget *, cchar *menu) { using namespace blur_names; int blur_dialog_event(zdialog *zd, cchar *event); void * blur_thread(void *); int cc; F1_help_topic = "blur_image"; EFblur.menuname = menu; EFblur.menufunc = m_blur; EFblur.funcname = "blur"; EFblur.Farea = 2; // select area usable EFblur.threadfunc = blur_thread; // thread function EFblur.Frestart = 1; // allow restart EFblur.Fscript = 1; // scripting supported if (! edit_setup(EFblur)) return; // setup edit blur_radius = 0.5; blur_cancel = 0; cc = E3pxm->ww * E3pxm->hh; blur_flags = (char *) zmalloc(cc); // allocate pixel flags zdialog *zd = zdialog_new(ZTX("Blur Radius"),Mwin,Bdone,Bcancel,null); EFblur.zd = zd; zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labrad","hb2",Bradius,"space=5"); zdialog_add_widget(zd,"zspin","radius","hb2","0|200|0.5|0.5","space=5"); zdialog_add_widget(zd,"button","apply","hb2",Bapply,"space=5"); zdialog_run(zd,blur_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion callback function int blur_dialog_event(zdialog * zd, cchar *event) { using namespace blur_names; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else { // cancel blur_cancel = 1; edit_cancel(0); // discard edit } zfree(blur_flags); return 1; } if (strmatch(event,"apply")) { zdialog_fetch(zd,"radius",blur_radius); // get blur radius if (blur_radius == 0) edit_reset(); else signal_thread(); // trigger working thread } return 1; } // image blur thread function void * blur_thread(void *) { using namespace blur_names; void * blur_wthread(void *arg); int dx, dy, cc; float rad, radflat2; float m, d, w, sum; while (true) { thread_idle_loop(); // wait for work or exit request rad = blur_radius - 0.2; radflat2 = rad * rad; for (dx = 0; dx <= rad+1; dx++) // clear weights array for (dy = 0; dy <= rad+1; dy++) blur_weight[dx][dy] = 0; for (dx = -rad-1; dx <= rad+1; dx++) // blur_weight[dx][dy] = no. of pixels for (dy = -rad-1; dy <= rad+1; dy++) // at distance (dx,dy) from center ++blur_weight[abs(dx)][abs(dy)]; m = sqrt(radflat2 + radflat2); // corner pixel distance from center sum = 0; for (dx = 0; dx <= rad+1; dx++) // compute weight of pixel for (dy = 0; dy <= rad+1; dy++) // at distance dx, dy { d = sqrt(dx*dx + dy*dy); w = (m + 1.2 - d) / m; w = w * w; sum += blur_weight[dx][dy] * w; blur_weight[dx][dy] = w; } for (dx = 0; dx <= rad+1; dx++) // make weights add up to 1.0 for (dy = 0; dy <= rad+1; dy++) blur_weight[dx][dy] = blur_weight[dx][dy] / sum; if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = E3pxm->ww * E3pxm->hh; Fbusy_done = 0; cc = E3pxm->ww * E3pxm->hh; // clear flags memset(blur_flags,0,cc); for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(blur_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = 0; CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * blur_wthread(void *arg) // worker thread function { using namespace blur_names; int index = *((int *) arg); int px, py, ii, dist = 0; int dx, dy, adx, ady, rad; int ww = E3pxm->ww, hh = E3pxm->hh; int Npix; int nc = E1pxm->nc; float red, green, blue; float weight1, weight2, f1, f2; float *pix1, *pix3, *pixN; for (py = index; py < hh-1; py += NWT) // loop all image pixels for (px = 0; px < ww-1; px++) { if (blur_cancel) exit_wthread(); // user cancel ii = py * ww + px; if (blur_flags[ii]) continue; // pixel already done, skip blur_flags[ii] = 1; // this one is mine if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel rad = blur_radius; red = green = blue = 0; weight2 = 0.0; Npix = 0; // pixels sampled for (dy = -rad-1; dy <= rad+1; dy++) // loop neighbor pixels within radius for (dx = -rad-1; dx <= rad+1; dx++) { if (px+dx < 0 || px+dx > ww-1) continue; // omit pixels off edge if (py+dy < 0 || py+dy > hh-1) continue; if (sa_stat == 3) { ii = (py+dy) * ww + px+dx; dist = sa_pixmap[ii]; if (! dist) continue; // omit pixels outside area } adx = abs(dx); ady = abs(dy); pixN = pix1 + (dy * ww + dx) * nc; weight1 = blur_weight[adx][ady]; // weight at distance (dx,dy) weight2 += weight1; red += pixN[0] * weight1; // accumulate contributions green += pixN[1] * weight1; blue += pixN[2] * weight1; Npix++; // count pixels sampled } red = red / weight2; // weighted average green = green / weight2; blue = blue / weight2; pix3[0] = red; pix3[1] = green; pix3[2] = blue; Fbusy_done++; // track progress if (Npix < 2000) continue; // mixed pixels are few, no shortcut rad = sqrt(Npix/2000); // 2000->1 8000->2 18000->3 32000->4 ... for (dy = -rad; dy <= +rad; dy++) // loop pixels within rad for (dx = -rad; dx <= +rad; dx++) { if (px+dx < 0 || px+dx > ww-1) continue; // omit pixels off edge if (py+dy < 0 || py+dy > hh-1) continue; ii = (py+dy) * ww + px+dx; if (blur_flags[ii]) continue; // pixel already done, skip blur_flags[ii] = 1; // this one is mine if (sa_stat == 3) { dist = sa_pixmap[ii]; if (! dist) continue; // omit pixels outside area } pix3 = PXMpix(E3pxm,px+dx,py+dy); // set neighbors to same RGB values pix3[0] = red; pix3[1] = green; pix3[2] = blue; Fbusy_done++; // track progress } } if (sa_stat == 3 && sa_blend > 0) // select area has edge blend { for (py = index; py < hh-1; py += NWT) // loop all image pixels for (px = 0; px < ww-1; px++) { ii = py * ww + px; dist = sa_pixmap[ii]; if (! dist) continue; // omit pixels outside area if (dist >= sa_blend) continue; // omit if > blendwidth from edge pix1 = PXMpix(E1pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; } } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // image noise reduction namespace denoise { enum denoise_method { flatten, median, tophat, wavelets } denoise_method; int denoise_radius; float denoise_thresh; float denoise_darkareas; zdialog *zd_denoise_measure; cchar *mformat = " mean RGB: %5.0f %5.0f %5.0f "; cchar *nformat = " mean noise: %5.2f %5.2f %5.2f "; int ww, hh; // image dimensions int8 *Fpix, *Gpix; editfunc EFdenoise; GtkWidget *denoise_measure_drawwin; // 17.04 } // menu function void m_denoise(GtkWidget *, cchar *menu) { using namespace denoise; int denoise_dialog_event(zdialog *zd, cchar *event); void * denoise_thread(void *); cchar *tip = ZTX("Apply repeatedly while watching the image."); cchar *Bmeasure = ZTX("Measure"); F1_help_topic = "denoise"; EFdenoise.menuname = menu; EFdenoise.menufunc = m_denoise; EFdenoise.funcname = "denoise"; EFdenoise.Farea = 2; // select area usable EFdenoise.threadfunc = denoise_thread; // thread function EFdenoise.Frestart = 1; // allow restart EFdenoise.FusePL = 1; // use with paint/lever edits OK EFdenoise.Fscript = 1; // scripting supported if (! edit_setup(EFdenoise)) return; // setup edit ww = E3pxm->ww; // image dimensions hh = E3pxm->hh; /*** _____________________________________________ | Noise Reduction | | Apply repeatedly while watching the image. | | | | (o) Flatten Radius [___] | | (o) Median Radius [___] | | (o) Top Hat Radius [___] | | (o) Wavelets Threshold [___] | | | | dark areas =========[]=========== all areas | | | | [Measure] [Apply] [Reset] [Done] [Cancel] | |_____________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Noise Reduction"),Mwin,Bmeasure,Bapply,Breset,Bdone,Bcancel,null); EFdenoise.zd = zd; zdialog_add_widget(zd,"label","labtip","dialog",tip,"space=3"); zdialog_add_widget(zd,"hbox","hbvb","dialog",0,"space=3"); zdialog_add_widget(zd,"label","space","hbvb",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hbvb",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vb2","hbvb",0,"homog|space=3"); zdialog_add_widget(zd,"label","space","hbvb",0,"space=5"); zdialog_add_widget(zd,"vbox","vb3","hbvb",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vb4","hbvb",0,"homog|space=3"); zdialog_add_widget(zd,"label","space","hbvb",0,"space=2"); zdialog_add_widget(zd,"radio","flatten","vb1"); zdialog_add_widget(zd,"radio","median","vb1"); zdialog_add_widget(zd,"radio","tophat","vb1"); zdialog_add_widget(zd,"radio","wavelets","vb1"); zdialog_add_widget(zd,"label","labflatten","vb2",ZTX("Flatten")); zdialog_add_widget(zd,"label","labmedian","vb2",ZTX("Median")); zdialog_add_widget(zd,"label","labtophat","vb2","Top Hat"); zdialog_add_widget(zd,"label","labwavelets","vb2","Wavelets"); zdialog_add_widget(zd,"label","labradflatten","vb3",Bradius); zdialog_add_widget(zd,"label","labradmedian","vb3",Bradius); zdialog_add_widget(zd,"label","labradtophat","vb3",Bradius); zdialog_add_widget(zd,"label","labwaveletsthresh","vb3",Bthresh); zdialog_add_widget(zd,"zspin","radflatten","vb4","1|9|1|3"); zdialog_add_widget(zd,"zspin","radmedian","vb4","1|9|1|2"); zdialog_add_widget(zd,"zspin","radtophat","vb4","1|9|1|2"); zdialog_add_widget(zd,"zspin","waveletsthresh","vb4","0.0|8|0.1|1"); zdialog_add_widget(zd,"hbox","hbdark","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labdark","hbdark",ZTX("dark areas"),"space=8"); zdialog_add_widget(zd,"hscale","darkareas","hbdark","0|200|1|200","expand"); zdialog_add_widget(zd,"label","laball","hbdark",ZTX("all areas"),"space=8"); zdialog_restore_inputs(zd); zdialog_run(zd,denoise_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion callback function int denoise_dialog_event(zdialog * zd, cchar *event) // reworked for script files { using namespace denoise; void denoise_measure(); int ii; wait_thread_idle(); if (strmatch(event,"apply")) zd->zstat = 2; // from script file if (strmatch(event,"done")) zd->zstat = 4; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 5; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // [measure] zd->zstat = 0; // keep dialog active denoise_measure(); return 1; } if (zd->zstat == 2) { // [apply] zd->zstat = 0; // keep dialog active signal_thread(); return 1; } if (zd->zstat == 3) { // [reset] edit_undo(); // undo edits zd->zstat = 0; // keep dialog active return 1; } if (zd->zstat == 4) edit_done(0); // [done] commit edit else edit_cancel(0); // [cancel] or [x] discard edit if (zd_denoise_measure) { // kill measure dialog bugfix 17.01.1 freeMouse(); zdialog_free(zd_denoise_measure); zd_denoise_measure = 0; } return 1; } zdialog_fetch(zd,"darkareas",denoise_darkareas); if (strmatch(event,"blendwidth")) signal_thread(); if (strstr("flatten median tophat wavelets",event)) { // capture choice zdialog_stuff(zd,"flatten",0); zdialog_stuff(zd,"median",0); zdialog_stuff(zd,"tophat",0); zdialog_stuff(zd,"wavelets",0); zdialog_stuff(zd,event,1); } zdialog_fetch(zd,"flatten",ii); if (ii) { denoise_method = flatten; zdialog_fetch(zd,"radflatten",denoise_radius); } zdialog_fetch(zd,"median",ii); if (ii) { denoise_method = median; zdialog_fetch(zd,"radmedian",denoise_radius); } zdialog_fetch(zd,"tophat",ii); if (ii) { denoise_method = tophat; zdialog_fetch(zd,"radtophat",denoise_radius); } zdialog_fetch(zd,"wavelets",ii); if (ii) { denoise_method = wavelets; zdialog_fetch(zd,"waveletsthresh",denoise_thresh); } return 1; } // image noise reduction thread void * denoise_thread(void *) { using namespace denoise; void * denoise_wthread(void *arg); void * denoise_wavelet_wthread(void *arg); int ii, px, py, dist = 0; float *pix3, *pix9; int nc = E3pxm->nc, pcc = nc * sizeof(float); while (true) { thread_idle_loop(); // wait for work or exit request E9pxm = PXM_copy(E3pxm); // image3 is source, image9 is modified if (denoise_method == wavelets) // wavelet method { for (ii = 0; ii < 3; ii++) // start worker threads for wavelet denoise start_wthread(denoise_wavelet_wthread,&Nval[ii]); // (one thread per RGB color) wait_wthreads(); // wait for completion } else // other method { if (sa_stat == 3) Fbusy_goal = sa_Npixel; else Fbusy_goal = ww * hh; Fbusy_done = 0; for (ii = 0; ii < NWT; ii++) // start worker threads start_wthread(denoise_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = 0; } if (denoise_darkareas < 200) // if brightness threshhold set, { // revert brighter areas for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix3 = PXMpix(E3pxm,px,py); // source pixel pix9 = PXMpix(E9pxm,px,py); // target pixel if (pix3[0] + pix3[1] + pix3[2] > 3 * denoise_darkareas) // revert brighter pixels memcpy(pix9,pix3,pcc); } } paintlock(1); // block window updates PXM_free(E3pxm); // image9 >> image3 E3pxm = E9pxm; E9pxm = 0; paintlock(0); // unblock window updates CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * denoise_wthread(void *arg) // worker thread for methods 1-5 { using namespace denoise; void denoise_flatten(float *pix3, float *pix9); void denoise_median(float *pix3, float *pix9); void denoise_tophat(float *pix3, float *pix9); int index = *((int *) arg); int ii, px, py, rad, dist = 0; float f1, f2; float *pix1, *pix3, *pix9; rad = denoise_radius; for (py = index+rad; py < hh-rad; py += NWT) // loop all image3 pixels for (px = rad; px < ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix3 = PXMpix(E3pxm,px,py); // source pixel pix9 = PXMpix(E9pxm,px,py); // target pixel if (denoise_method == flatten) denoise_flatten(pix3,pix9); if (denoise_method == median) denoise_median(pix3,pix9); if (denoise_method == tophat) denoise_tophat(pix3,pix9); if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); // source pixel pix9[0] = f1 * pix9[0] + f2 * pix1[0]; pix9[1] = f1 * pix9[1] + f2 * pix1[1]; // remove int(*) pix9[2] = f1 * pix9[2] + f2 * pix1[2]; } Fbusy_done++; // track progress } exit_wthread(); return 0; // not executed, avoid gcc warning } // ------------------------------------------------------------------------------ // Flatten: // Flatten outlyer pixels within neighborhood group. // An outlier pixel has an RGB value outside one sigma of // the mean for all pixels within a given radius. void denoise_flatten(float *pix3, float *pix9) { using namespace denoise; int rgb, dy, dx, rad, nn; float nn1, val, sum, sum2, mean, variance, sigma; float *pixN; int nc = E3pxm->nc; rad = denoise_radius; nn = (rad * 2 + 1); nn = nn * nn - 1; nn1 = 1.0 / nn; for (rgb = 0; rgb < 3; rgb++) // loop RGB color { sum = sum2 = 0; for (dy = -rad; dy <= rad; dy++) // loop surrounding pixels for (dx = -rad; dx <= rad; dx++) { if (dy == 0 && dx == 0) continue; // skip self pixN = pix3 + (dy * ww + dx) * nc; val = pixN[rgb]; sum += val; sum2 += val * val; } mean = nn1 * sum; variance = nn1 * (sum2 - 2.0 * mean * sum) + mean * mean; sigma = sqrtf(variance); if (pix3[rgb] > mean + sigma) // if | pixel - mean | > sigma pix9[rgb] = mean + 0.8 * sigma; // flatten pixel else if (pix3[rgb] < mean - sigma) pix9[rgb] = mean - 0.8 * sigma; } return; } // ------------------------------------------------------------------------------ // Median: // Use median RGB brightness for pixels within radius void denoise_median(float *pix3, float *pix9) { using namespace denoise; int dy, dx, rad, ns, rgb; float bsortN[400], *pixN; int nc = E3pxm->nc; rad = denoise_radius; for (rgb = 0; rgb < 3; rgb++) // loop all RGB colors { ns = 0; for (dy = -rad; dy <= rad; dy++) // loop surrounding pixels for (dx = -rad; dx <= rad; dx++) // get brightness values { pixN = pix3 + (dy * ww + dx) * nc; bsortN[ns] = pixN[rgb]; ns++; } HeapSort(bsortN,ns); pix9[rgb] = bsortN[ns/2]; // median brightness of ns pixels } return; } // ------------------------------------------------------------------------------ // Top Hat: // Execute with increasing radius from 1 to limit. // Detect outlier by comparing with pixels along outer radius only. void denoise_tophat(float *pix3, float *pix9) { using namespace denoise; int dy, dx, rad; float minR, minG, minB, maxR, maxG, maxB; float *pixN; int nc = E3pxm->nc; for (rad = 1; rad <= denoise_radius; rad++) for (int loops = 0; loops < 2; loops++) { minR = minG = minB = 255; maxR = maxG = maxB = 0; for (dy = -rad; dy <= rad; dy++) // loop all pixels within rad for (dx = -rad; dx <= rad; dx++) { if (dx > -rad && dx < rad) continue; // skip inner pixels if (dy > -rad && dy < rad) continue; pixN = pix3 + (dy * ww + dx) * nc; if (pixN[0] < minR) minR = pixN[0]; // find min and max per color if (pixN[0] > maxR) maxR = pixN[0]; // among outermost pixels if (pixN[1] < minG) minG = pixN[1]; if (pixN[1] > maxG) maxG = pixN[1]; if (pixN[2] < minB) minB = pixN[2]; if (pixN[2] > maxB) maxB = pixN[2]; } if (pix3[0] < minR && pix9[0] < 254) pix9[0] += 2; // if central pixel is outlier, if (pix3[0] > maxR && pix9[0] > 2) pix9[0] -= 2; // moderate its values if (pix3[1] < minG && pix9[1] < 254) pix9[1] += 2; if (pix3[1] > maxG && pix9[1] > 2) pix9[1] -= 2; if (pix3[2] < minB && pix9[2] < 254) pix9[2] += 2; if (pix3[2] > maxB && pix9[2] > 2) pix9[2] -= 2; } return; } // ------------------------------------------------------------------------------ // Wavelet: // worker thread for wavelets method // do wavelet denoise for one color in each of 3 threads void * denoise_wavelet_wthread(void *arg) { using namespace denoise; void denoise_wavelet(float *fimg[3], uint ww2, uint hh2, float); int rgb = *((int *) arg); // rgb color 0/1/2 int ii, jj; float *fimg[3]; float f256 = 1.0 / 256.0; int nc = E3pxm->nc; if (sa_stat == 3) goto denoise_area; // select area is active fimg[0] = (float *) zmalloc(ww * hh * sizeof(float)); fimg[1] = (float *) zmalloc(ww * hh * sizeof(float)); fimg[2] = (float *) zmalloc(ww * hh * sizeof(float)); for (ii = 0; ii < ww * hh; ii++) // extract one noisy color from E3 fimg[0][ii] = E3pxm->pixels[nc*ii+rgb] * f256; denoise_wavelet(fimg,ww,hh,denoise_thresh); for (ii = 0; ii < ww * hh; ii++) // save one denoised color to E9 E9pxm->pixels[nc*ii+rgb] = 256.0 * fimg[0][ii]; zfree(fimg[0]); zfree(fimg[1]); zfree(fimg[2]); exit_wthread(); return 0; denoise_area: int px, py, pxl, pxh, pyl, pyh, ww2, hh2, dist; float f1, f2; pxl = sa_minx - 16; if (pxl < 0) pxl = 0; pxh = sa_maxx + 16; if (pxh > ww) pxh = ww; pyl = sa_miny - 16; if (pyl < 0) pyl = 0; pyh = sa_maxy + 16; if (pyh > hh) pyh = hh; ww2 = pxh - pxl; hh2 = pyh - pyl; fimg[0] = (float *) zmalloc(ww2 * hh2 * sizeof(float)); fimg[1] = (float *) zmalloc(ww2 * hh2 * sizeof(float)); fimg[2] = (float *) zmalloc(ww2 * hh2 * sizeof(float)); for (py = 0; py < hh2; py++) for (px = 0; px < ww2; px++) { ii = py * ww2 + px; jj = (py + pyl) * ww + (px + pxl); fimg[0][ii] = E3pxm->pixels[nc*jj+rgb] * f256; } denoise_wavelet(fimg,ww2,hh2,denoise_thresh); for (py = 0; py < hh2; py++) for (px = 0; px < ww2; px++) { ii = py * ww2 + px; jj = (py + pyl) * ww + (px + pxl); dist = sa_pixmap[jj]; if (! dist) continue; if (dist < sa_blend) { f1 = sa_blendfunc(dist); f2 = 1.0 - f1; E9pxm->pixels[nc*jj+rgb] = f1 * 256.0 * fimg[0][ii] + f2 * E3pxm->pixels[nc*jj+rgb]; } else E9pxm->pixels[nc*jj+rgb] = 256.0 * fimg[0][ii]; } zfree(fimg[0]); zfree(fimg[1]); zfree(fimg[2]); exit_wthread(); return 0; } // wavelet denoise algorithm // Adapted from Gimp wavelet plugin (and ultimately DCraw by Dave Coffin). // fimg[0][rows][cols] = one color of image to denoise // fimg[1] and [2] = working space // thresh (0-10) is the adjustable parameter void denoise_wavelet(float *fimg[3], uint ww2, uint hh2, float thresh) { void denoise_wavelet_avgpix(float *temp, float *fimg, int st, int size, int sc); float *temp, thold, stdev[5]; uint ii, lev, lpass, hpass, size, col, row; uint samples[5]; size = ww2 * hh2; temp = (float *) zmalloc ((ww2 + hh2) * sizeof(float)); hpass = 0; for (lev = 0; lev < 5; lev++) { lpass = ((lev & 1) + 1); // 1, 2, 1, 2, 1 for (row = 0; row < hh2; row++) // average row pixels { denoise_wavelet_avgpix(temp, fimg[hpass] + row * ww2, 1, ww2, 1 << lev); for (col = 0; col < ww2; col++) fimg[lpass][row * ww2 + col] = temp[col]; } for (col = 0; col < ww2; col++) // average column pixels { denoise_wavelet_avgpix(temp, fimg[lpass] + col, ww2, hh2, 1 << lev); for (row = 0; row < hh2; row++) fimg[lpass][row * ww2 + col] = temp[row]; } thold = 5.0 / (1 << 6) * exp (-2.6 * sqrt (lev + 1)) * 0.8002 / exp (-2.6); stdev[0] = stdev[1] = stdev[2] = stdev[3] = stdev[4] = 0.0; samples[0] = samples[1] = samples[2] = samples[3] = samples[4] = 0; for (ii = 0; ii < size; ii++) { fimg[hpass][ii] -= fimg[lpass][ii]; if (fimg[hpass][ii] < thold && fimg[hpass][ii] > -thold) { if (fimg[lpass][ii] > 0.8) { stdev[4] += fimg[hpass][ii] * fimg[hpass][ii]; samples[4]++; } else if (fimg[lpass][ii] > 0.6) { stdev[3] += fimg[hpass][ii] * fimg[hpass][ii]; samples[3]++; } else if (fimg[lpass][ii] > 0.4) { stdev[2] += fimg[hpass][ii] * fimg[hpass][ii]; samples[2]++; } else if (fimg[lpass][ii] > 0.2) { stdev[1] += fimg[hpass][ii] * fimg[hpass][ii]; samples[1]++; } else { stdev[0] += fimg[hpass][ii] * fimg[hpass][ii]; samples[0]++; } } } stdev[0] = sqrt (stdev[0] / (samples[0] + 1)); stdev[1] = sqrt (stdev[1] / (samples[1] + 1)); stdev[2] = sqrt (stdev[2] / (samples[2] + 1)); stdev[3] = sqrt (stdev[3] / (samples[3] + 1)); stdev[4] = sqrt (stdev[4] / (samples[4] + 1)); for (ii = 0; ii < size; ii++) // do thresholding { if (fimg[lpass][ii] > 0.8) thold = thresh * stdev[4]; else if (fimg[lpass][ii] > 0.6) thold = thresh * stdev[3]; else if (fimg[lpass][ii] > 0.4) thold = thresh * stdev[2]; else if (fimg[lpass][ii] > 0.2) thold = thresh * stdev[1]; else thold = thresh * stdev[0]; if (fimg[hpass][ii] < -thold) fimg[hpass][ii] += thold; else if (fimg[hpass][ii] > thold) fimg[hpass][ii] -= thold; else fimg[hpass][ii] = 0; if (hpass) fimg[0][ii] += fimg[hpass][ii]; } hpass = lpass; } for (ii = 0; ii < size; ii++) fimg[0][ii] = fimg[0][ii] + fimg[lpass][ii]; zfree(temp); return; } // average pixels in one column or row // st = row stride (row length) or column stride (1) // sc = 1, 2, 4, 8, 16 = pixels +/- from target pixel to average void denoise_wavelet_avgpix(float *temp, float *fimg, int st, int size, int sc) { int ii; for (ii = 0; ii < sc; ii++) temp[ii] = 0.25*(2*fimg[st*ii] + fimg[st*(sc-ii)] + fimg[st*(ii+sc)]); for (; ii < size - sc; ii++) temp[ii] = 0.25*(2*fimg[st*ii] + fimg[st*(ii-sc)] + fimg[st*(ii+sc)]); for (; ii < size; ii++) temp[ii] = 0.25*(2*fimg[st*ii] + fimg[st*(ii-sc)] + fimg[st*(2*size-2-(ii+sc))]); return; } // ------------------------------------------------------------------------------ // dialog to measure noise at mouse position void denoise_measure() { using namespace denoise; int denoise_measure_dialog_event(zdialog *zd, cchar *event); GtkWidget *frdraw, *drawwin; char text[100]; cchar *title = ZTX("Measure Noise"); cchar *mousemess = ZTX("Move mouse in a monotone image area."); /*** _______________________________________ | Measure Noise | | | | Move mouse in a monotone image area. | | _____________________________________ | || || || || ||.....................................|| || || || || ||_____________________________________|| drawing area || || || || ||.....................................|| || || ||_____________________________________|| | center edge | | | | mean RGB: 100 150 200 | | mean noise: 1.51 1.23 0.76 | | | | [cancel] | |_______________________________________| ***/ if (zd_denoise_measure) return; zdialog *zd = zdialog_new(title,Mwin,Bcancel,null); // measure noise dialog zd_denoise_measure = zd; zdialog_add_widget(zd,"label","clab","dialog",mousemess,"space=5"); zdialog_add_widget(zd,"frame","frdraw","dialog",0,"expand"); // frame for drawing areas frdraw = zdialog_widget(zd,"frdraw"); drawwin = gtk_drawing_area_new(); // drawing area gtk_container_add(GTK_CONTAINER(frdraw),drawwin); denoise_measure_drawwin = drawwin; zdialog_add_widget(zd,"hbox","hbce","dialog"); zdialog_add_widget(zd,"label","labcen","hbce",Bcenter,"space=3"); zdialog_add_widget(zd,"label","space","hbce",0,"expand"); zdialog_add_widget(zd,"label","labend","hbce",Bedge,"space=5"); snprintf(text,100,mformat,0.0,0.0,0.0); // mean RGB: 0 0 0 zdialog_add_widget(zd,"label","mlab","dialog",text); snprintf(text,100,nformat,0.0,0.0,0.0); // mean noise: 0.00 0.00 0.00 zdialog_add_widget(zd,"label","nlab","dialog",text); zdialog_resize(zd,300,300); zdialog_run(zd,denoise_measure_dialog_event,"save"); // run dialog return; } // dialog event and completion function int denoise_measure_dialog_event(zdialog *zd, cchar *event) { using namespace denoise; void denoise_measure_mousefunc(); if (strmatch(event,"focus")) takeMouse(denoise_measure_mousefunc,dragcursor); // connect mouse function if (zd->zstat) { freeMouse(); // free mouse zdialog_free(zd); zd_denoise_measure = 0; } return 1; } // mouse function // sample noise where the mouse is clicked // assumed: mouse is on a monotone image area void denoise_measure_mousefunc() // 17.04 { using namespace denoise; GtkWidget *drawwin; GdkWindow *gdkwin; cairo_t *cr; zdialog *zd = zd_denoise_measure; char text[100]; int mx, my, px, py, qx, qy, Npix; float *pix3, R; float Rm, Gm, Bm, Rn, Gn, Bn, Ro, Go, Bo; int dww, dhh; float max, xscale, yscale; float rx, ry; float Noise[400][3]; double dashes[2] = { 1, 3 }; if (! E3pxm) return; if (! zd) return; mx = Mxposn; // mouse position my = Myposn; if (mx < 13 || mx >= ww-13) return; // must be 12+ pixels from image edge if (my < 13 || my >= hh-13) return; draw_mousecircle(Mxposn,Myposn,10,0,0); // draw mouse circle, radius 10 Npix = 0; Rm = Gm = Bm = 0; for (py = my-10; py <= my+10; py++) // loop pixels within mouise circle for (px = mx-10; px <= mx+10; px++) // (approx. 314 pixels) { R = sqrtf((px-mx)*(px-mx) + (py-my)*(py-my)); // distance from center if (R > 10) continue; pix3 = PXMpix(E3pxm,px,py); // get pixel RGB values Rm += pix3[0]; // accumulate Gm += pix3[1]; Bm += pix3[2]; Npix++; } Rm = Rm / Npix; // mean RGB values Gm = Gm / Npix; // for pixels within mouse Bm = Bm / Npix; Npix = 0; Rn = Gn = Bn = 0; for (py = my-10; py <= my+10; py++) // loop pixels within mouise circle for (px = mx-10; px <= mx+10; px++) // (approx. 314 pixels) { R = sqrtf((px-mx)*(px-mx) + (py-my)*(py-my)); // distance from center if (R > 10) continue; Ro = Go = Bo = 0; for (qy = py-2; qy <= py+2; qy++) // for each pixel, get mean RGB for (qx = px-2; qx <= px+2; qx++) // for 5x5 surrounding pixels { pix3 = PXMpix(E3pxm,qx,qy); Ro += pix3[0]; Go += pix3[1]; Bo += pix3[2]; } Ro = Ro / 25; // mean RGB for surrounding pixels Go = Go / 25; Bo = Bo / 25; pix3 = PXMpix(E3pxm,px,py); // get pixel RGB noise levels Noise[Npix][0] = pix3[0] - Ro; // noise = pixel value - mean Noise[Npix][1] = pix3[1] - Go; Noise[Npix][2] = pix3[2] - Bo; Rn += fabsf(Noise[Npix][0]); // accumulate absolute values Gn += fabsf(Noise[Npix][1]); Bn += fabsf(Noise[Npix][2]); Npix++; } Rn = Rn / Npix; // mean RGB noise levels Gn = Gn / Npix; // for pixels within mouse Bn = Bn / Npix; snprintf(text,100,mformat,0.0,0.0,0.0); // clear dialog data zdialog_stuff(zd,"mlab",text); snprintf(text,100,nformat,0.0,0.0,0.0); zdialog_stuff(zd,"nlab",text); snprintf(text,100,mformat,Rm,Gm,Bm); // mean RGB: NNN NNN NNN zdialog_stuff(zd,"mlab",text); snprintf(text,100,nformat,Rn,Gn,Bn); // mean noise: N.NN N.NN N.NN zdialog_stuff(zd,"nlab",text); max = Rn; if (Gn > max) max = Gn; if (Bn > max) max = Bn; drawwin = denoise_measure_drawwin; gdkwin = gtk_widget_get_window(drawwin); // GDK drawing window dww = gtk_widget_get_allocated_width(drawwin); // drawing window size dhh = gtk_widget_get_allocated_height(drawwin); xscale = dww / 10.0; // x scale: 0 to max radius yscale = dhh / 20.0; // y scale: -10 to +10 noise level cr = draw_context_create(gdkwin,draw_context); cairo_set_source_rgb(cr,1,1,1); // white background cairo_paint(cr); cairo_set_source_rgb(cr,0,0,0); // paint black cairo_set_line_width(cr,2); // center line cairo_set_dash(cr,dashes,0,0); cairo_move_to(cr,0,0.5*dhh); cairo_line_to(cr,dww,0.5*dhh); cairo_stroke(cr); cairo_set_dash(cr,dashes,2,0); // dash lines at -5 and +5 cairo_move_to(cr,0,0.25*dhh); cairo_line_to(cr,dww,0.25*dhh); cairo_move_to(cr,0,0.75*dhh); cairo_line_to(cr,dww,0.75*dhh); cairo_stroke(cr); cairo_set_source_rgb(cr,1,0,0); Npix = 0; for (py = my-10; py <= my+10; py++) // loop pixels within mouise circle for (px = mx-10; px <= mx+10; px++) // (approx. 314 pixels) { R = sqrtf((px-mx)*(px-mx) + (py-my)*(py-my)); // distance from center if (R > 10) continue; Rn = Noise[Npix][0]; // RED noise rx = R * xscale; // px, 0 to dww ry = 0.5 * dhh - Rn * yscale; // red py, 0 to dhh cairo_move_to(cr,rx,ry); cairo_arc(cr,rx,ry,1,0,2*PI); Npix++; } cairo_stroke(cr); cairo_set_source_rgb(cr,0,1,0); Npix = 0; for (py = my-10; py <= my+10; py++) // same for GREEN noise for (px = mx-10; px <= mx+10; px++) { R = sqrtf((px-mx)*(px-mx) + (py-my)*(py-my)); if (R > 10) continue; Gn = Noise[Npix][1]; rx = R * xscale; ry = 0.5 * dhh - Gn * yscale; cairo_move_to(cr,rx,ry); cairo_arc(cr,rx,ry,1,0,2*PI); Npix++; } cairo_stroke(cr); cairo_set_source_rgb(cr,0,0,1); Npix = 0; for (py = my-10; py <= my+10; py++) // same for BLUE noise for (px = mx-10; px <= mx+10; px++) { R = (px-mx)*(px-mx) + (py-my)*(py-my); if (R > 100) continue; R = 0.1 * R; Bn = Noise[Npix][2]; rx = R * xscale; ry = 0.5 * dhh - Bn * yscale; cairo_move_to(cr,rx,ry); cairo_arc(cr,rx,ry,1,0,2*PI); Npix++; } cairo_stroke(cr); draw_context_destroy(draw_context); return; } /********************************************************************************/ // red eye removal function struct sredmem { // red-eye struct in memory char type, space[3]; int cx, cy, ww, hh, rad, clicks; float thresh, tstep; }; sredmem redmem[100]; // store up to 100 red-eyes int Nredmem = 0, maxredmem = 100; void redeye_mousefunc(); editfunc EFredeye; #define pixred(pix) (25*pix[0]/(pixbright(pix)+1)) // red brightness 0-100% // menu function void m_redeye(GtkWidget *, cchar *) { int redeye_dialog_event(zdialog *zd, cchar *event); cchar *redeye_message = ZTX( "Method 1:\n" " Left-click on red-eye to darken.\n" "Method 2:\n" " Drag down and right to enclose red-eye.\n" " Left-click on red-eye to darken.\n" "Undo red-eye:\n" " Right-click on red-eye."); F1_help_topic = "redeye_remove"; EFredeye.menufunc = m_redeye; EFredeye.funcname = "redeye"; EFredeye.Farea = 1; // select area ignored EFredeye.mousefunc = redeye_mousefunc; if (! edit_setup(EFredeye)) return; // setup edit zdialog *zd = zdialog_new(ZTX("Red Eye Reduction"),Mwin,Bdone,Bcancel,null); EFredeye.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",redeye_message); zdialog_run(zd,redeye_dialog_event,"save"); // run dialog - parallel Nredmem = 0; takeMouse(redeye_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int redeye_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (Nredmem > 0) { CEF->Fmods++; CEF->Fsaved = 0; } if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(redeye_mousefunc,dragcursor); // connect mouse function return 1; } // mouse functions to define, darken, and undo red-eyes int redeye_createF(int px, int py); // create 1-click red-eye (type F) int redeye_createR(int px, int py, int ww, int hh); // create robust red-eye (type R) void redeye_darken(int ii); // darken red-eye void redeye_distr(int ii); // build pixel redness distribution int redeye_find(int px, int py); // find red-eye at mouse position void redeye_remove(int ii); // remove red-eye at mouse position int redeye_radlim(int cx, int cy); // compute red-eye radius limit void redeye_mousefunc() { int ii, px, py, ww, hh; if (Nredmem == maxredmem) { zmessageACK(Mwin,"%d red-eye limit reached",maxredmem); // too many red-eyes return; } if (LMclick) // left mouse click { px = Mxclick; // click position py = Myclick; if (px < 0 || px > E3pxm->ww-1 || py < 0 || py > E3pxm->hh-1) // outside image area return; ii = redeye_find(px,py); // find existing red-eye if (ii < 0) ii = redeye_createF(px,py); // or create new type F redeye_darken(ii); // darken red-eye Fpaint2(); } if (RMclick) // right mouse click { px = Mxclick; // click position py = Myclick; ii = redeye_find(px,py); // find red-eye if (ii >= 0) redeye_remove(ii); // if found, remove Fpaint2(); } LMclick = RMclick = 0; if (Mxdrag || Mydrag) // mouse drag underway { px = Mxdown; // initial position py = Mydown; ww = Mxdrag - Mxdown; // increment hh = Mydrag - Mydown; Mxdrag = Mydrag = 0; if (ww < 2 && hh < 2) return; if (ww < 2) ww = 2; if (hh < 2) hh = 2; if (px < 1) px = 1; // keep within image area if (py < 1) py = 1; if (px + ww > E3pxm->ww-1) ww = E3pxm->ww-1 - px; if (py + hh > E3pxm->hh-1) hh = E3pxm->hh-1 - py; ii = redeye_find(px,py); // find existing red-eye if (ii >= 0) redeye_remove(ii); // remove it ii = redeye_createR(px,py,ww,hh); // create new red-eye type R } return; } // create type F redeye (1-click automatic) int redeye_createF(int cx, int cy) { int cx0, cy0, cx1, cy1, px, py, rad, radlim; int loops, ii; int Tnpix, Rnpix, R2npix; float rd, rcx, rcy, redpart; float Tsum, Rsum, R2sum, Tavg, Ravg, R2avg; float sumx, sumy, sumr; float *ppix; cx0 = cx; cy0 = cy; for (loops = 0; loops < 8; loops++) { cx1 = cx; cy1 = cy; radlim = redeye_radlim(cx,cy); // radius limit (image edge) Tsum = Tavg = Ravg = Tnpix = 0; for (rad = 0; rad < radlim-2; rad++) // find red-eye radius from (cx,cy) { Rsum = Rnpix = 0; R2sum = R2npix = 0; for (py = cy-rad-2; py <= cy+rad+2; py++) for (px = cx-rad-2; px <= cx+rad+2; px++) { rd = sqrt((px-cx)*(px-cx) + (py-cy)*(py-cy)); ppix = PXMpix(E3pxm,px,py); redpart = pixred(ppix); if (rd <= rad + 0.5 && rd > rad - 0.5) { // accum. redness at rad Rsum += redpart; Rnpix++; } else if (rd <= rad + 2.5 && rd > rad + 1.5) { // accum. redness at rad+2 R2sum += redpart; R2npix++; } } Tsum += Rsum; Tnpix += Rnpix; Tavg = Tsum / Tnpix; // avg. redness over 0-rad Ravg = Rsum / Rnpix; // avg. redness at rad R2avg = R2sum / R2npix; // avg. redness at rad+2 if (R2avg > Ravg || Ravg > Tavg) continue; if ((Ravg - R2avg) < 0.2 * (Tavg - Ravg)) break; // 0.1 --> 0.2 } sumx = sumy = sumr = 0; rad = int(1.2 * rad + 1); if (rad > radlim) rad = radlim; for (py = cy-rad; py <= cy+rad; py++) // compute center of gravity for for (px = cx-rad; px <= cx+rad; px++) // pixels within rad of (cx,cy) { rd = sqrt((px-cx)*(px-cx) + (py-cy)*(py-cy)); if (rd > rad + 0.5) continue; ppix = PXMpix(E3pxm,px,py); redpart = pixred(ppix); // weight by redness sumx += redpart * (px - cx); sumy += redpart * (py - cy); sumr += redpart; } rcx = cx + 1.0 * sumx / sumr; // new center of red-eye rcy = cy + 1.0 * sumy / sumr; if (fabsf(cx0 - rcx) > 0.6 * rad) break; // give up if big movement if (fabsf(cy0 - rcy) > 0.6 * rad) break; cx = int(rcx + 0.5); cy = int(rcy + 0.5); if (cx == cx1 && cy == cy1) break; // done if no change } radlim = redeye_radlim(cx,cy); if (rad > radlim) rad = radlim; ii = Nredmem++; // add red-eye to memory redmem[ii].type = 'F'; redmem[ii].cx = cx; redmem[ii].cy = cy; redmem[ii].rad = rad; redmem[ii].clicks = 0; redmem[ii].thresh = 0; return ii; } // create type R red-eye (drag an ellipse over red-eye area) int redeye_createR(int cx, int cy, int ww, int hh) { int rad, radlim; draw_mousearc(cx,cy,2*ww,2*hh,0,0); // draw ellipse around mouse pointer if (ww > hh) rad = ww; else rad = hh; radlim = redeye_radlim(cx,cy); if (rad > radlim) rad = radlim; int ii = Nredmem++; // add red-eye to memory redmem[ii].type = 'R'; redmem[ii].cx = cx; redmem[ii].cy = cy; redmem[ii].ww = 2 * ww; redmem[ii].hh = 2 * hh; redmem[ii].rad = rad; redmem[ii].clicks = 0; redmem[ii].thresh = 0; return ii; } // darken a red-eye and increase click count void redeye_darken(int ii) { int cx, cy, ww, hh, px, py, rad, clicks; float rd, thresh, tstep; char type; float *ppix; type = redmem[ii].type; cx = redmem[ii].cx; cy = redmem[ii].cy; ww = redmem[ii].ww; hh = redmem[ii].hh; rad = redmem[ii].rad; thresh = redmem[ii].thresh; tstep = redmem[ii].tstep; clicks = redmem[ii].clicks++; if (thresh == 0) // 1st click { redeye_distr(ii); // get pixel redness distribution thresh = redmem[ii].thresh; // initial redness threshhold tstep = redmem[ii].tstep; // redness step size draw_mousearc(0,0,0,0,1,0); // erase mouse ellipse } tstep = (thresh - tstep) / thresh; // convert to reduction factor thresh = thresh * pow(tstep,clicks); // reduce threshhold by total clicks for (py = cy-rad; py <= cy+rad; py++) // darken pixels over threshhold for (px = cx-rad; px <= cx+rad; px++) { if (type == 'R') { if (px < cx - ww/2) continue; if (px > cx + ww/2) continue; if (py < cy - hh/2) continue; if (py > cy + hh/2) continue; } rd = sqrt((px-cx)*(px-cx) + (py-cy)*(py-cy)); if (rd > rad + 0.5) continue; ppix = PXMpix(E3pxm,px,py); // set redness = threshhold if (pixred(ppix) > thresh) ppix[0] = int(thresh * (0.65 * ppix[1] + 0.10 * ppix[2] + 1) / (25 - 0.25 * thresh)); } return; } // Build a distribution of redness for a red-eye. Use this information // to set initial threshhold and step size for stepwise darkening. void redeye_distr(int ii) { int cx, cy, ww, hh, rad, px, py; int bin, npix, dbins[20], bsum, blim; float rd, maxred, minred, redpart, dbase, dstep; char type; float *ppix; type = redmem[ii].type; cx = redmem[ii].cx; cy = redmem[ii].cy; ww = redmem[ii].ww; hh = redmem[ii].hh; rad = redmem[ii].rad; maxred = 0; minred = 100; for (py = cy-rad; py <= cy+rad; py++) for (px = cx-rad; px <= cx+rad; px++) { if (type == 'R') { if (px < cx - ww/2) continue; if (px > cx + ww/2) continue; if (py < cy - hh/2) continue; if (py > cy + hh/2) continue; } rd = sqrt((px-cx)*(px-cx) + (py-cy)*(py-cy)); if (rd > rad + 0.5) continue; ppix = PXMpix(E3pxm,px,py); redpart = pixred(ppix); if (redpart > maxred) maxred = redpart; if (redpart < minred) minred = redpart; } dbase = minred; dstep = (maxred - minred) / 19.99; for (bin = 0; bin < 20; bin++) dbins[bin] = 0; npix = 0; for (py = cy-rad; py <= cy+rad; py++) for (px = cx-rad; px <= cx+rad; px++) { if (type == 'R') { if (px < cx - ww/2) continue; if (px > cx + ww/2) continue; if (py < cy - hh/2) continue; if (py > cy + hh/2) continue; } rd = sqrt((px-cx)*(px-cx) + (py-cy)*(py-cy)); if (rd > rad + 0.5) continue; ppix = PXMpix(E3pxm,px,py); redpart = pixred(ppix); bin = int((redpart - dbase) / dstep); ++dbins[bin]; ++npix; } bsum = 0; blim = int(0.5 * npix); for (bin = 0; bin < 20; bin++) // find redness level for 50% of { // pixels within red-eye radius bsum += dbins[bin]; if (bsum > blim) break; } redmem[ii].thresh = dbase + dstep * bin; // initial redness threshhold redmem[ii].tstep = dstep; // redness step (5% of range) return; } // find a red-eye (nearly) overlapping the mouse click position int redeye_find(int cx, int cy) { for (int ii = 0; ii < Nredmem; ii++) { if (cx > redmem[ii].cx - 2 * redmem[ii].rad && cx < redmem[ii].cx + 2 * redmem[ii].rad && cy > redmem[ii].cy - 2 * redmem[ii].rad && cy < redmem[ii].cy + 2 * redmem[ii].rad) return ii; // found } return -1; // not found } // remove a red-eye from memory void redeye_remove(int ii) { int cx, cy, rad, px, py; float *pix1, *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); cx = redmem[ii].cx; cy = redmem[ii].cy; rad = redmem[ii].rad; for (px = cx-rad; px <= cx+rad; px++) for (py = cy-rad; py <= cy+rad; py++) { pix1 = PXMpix(E1pxm,px,py); pix3 = PXMpix(E3pxm,px,py); memcpy(pix3,pix1,pcc); } for (ii++; ii < Nredmem; ii++) redmem[ii-1] = redmem[ii]; Nredmem--; draw_mousearc(0,0,0,0,1,0); // erase mouse ellipse return; } // compute red-eye radius limit: smaller of 100 and nearest image edge int redeye_radlim(int cx, int cy) { int radlim = 100; if (cx < 100) radlim = cx; if (E3pxm->ww-1 - cx < 100) radlim = E3pxm->ww-1 - cx; if (cy < 100) radlim = cy; if (E3pxm->hh-1 - cy < 100) radlim = E3pxm->hh-1 - cy; return radlim; } /********************************************************************************/ // make a black & white or color positive or negative, or sepia image namespace colormode_names { editfunc EFcolormode; int mode; float blend; } // menu function void m_color_mode(GtkWidget *, cchar *menu) { using namespace colormode_names; int colormode_dialog_event(zdialog *zd, cchar *event); void * colormode_thread(void *); F1_help_topic = "color_mode"; EFcolormode.menuname = menu; EFcolormode.menufunc = m_color_mode; EFcolormode.funcname = "color_mode"; EFcolormode.threadfunc = colormode_thread; EFcolormode.Farea = 2; // select area usable EFcolormode.Frestart = 1; // allow restart EFcolormode.Fscript = 1; // scripting supported if (! edit_setup(EFcolormode)) return; // setup edit: no preview /*** _____________________________ | Color Mode | | | | [reset] | // buttons instead of radio buttons 17.01 | [black/white positive] | | [black/white negative] | | [color positive] | | [color negative] | | [RGB -> GBR] | // added 17.01 | [sepia] | | | | 0% ===========[]===+ 100% | | | | [Done] [Cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(ZTX("Color Mode"),Mwin,Bdone,Bcancel,null); EFcolormode.zd = zd; zdialog_add_widget(zd,"zbutton","reset","dialog",ZTX("reset")); zdialog_add_widget(zd,"zbutton","b&wpos","dialog",ZTX("black/white positive")); zdialog_add_widget(zd,"zbutton","b&wneg","dialog",ZTX("black/white negative")); zdialog_add_widget(zd,"zbutton","colneg","dialog",ZTX("color negative")); zdialog_add_widget(zd,"zbutton","rotate","dialog",ZTX("RGB -> GBR")); zdialog_add_widget(zd,"zbutton","sepia","dialog",ZTX("sepia")); zdialog_add_widget(zd,"hbox","hbblend","dialog"); zdialog_add_widget(zd,"label","lab0","hbblend","0%","space=5"); zdialog_add_widget(zd,"hscale","blend","hbblend","0.0|1.0|0.01|1.0","expand"); zdialog_add_widget(zd,"label","lab100","hbblend","100%","space=5"); zdialog_resize(zd,200,0); zdialog_run(zd,colormode_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion callback function int colormode_dialog_event(zdialog *zd, cchar *event) { using namespace colormode_names; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) return 1; if (strmatch(event,"reset")) mode = 0; // 17.01 if (strmatch(event,"b&wpos")) mode = 1; if (strmatch(event,"b&wneg")) mode = 2; if (strmatch(event,"colneg")) mode = 3; if (strmatch(event,"rotate")) mode = 4; // 17.01 if (strmatch(event,"sepia")) mode = 5; zdialog_fetch(zd,"blend",blend); if (mode == 0) { edit_reset(); return 1; } signal_thread(); return 1; } // thread function void * colormode_thread(void *) { using namespace colormode_names; void * colormode_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(colormode_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved Fpaint2(); // update window } return 0; // not executed, stop warning } // worker thread functions void * colormode_wthread(void *arg) { using namespace colormode_names; int index = *((int *) (arg)); int ii, dist, px, py; float red1, green1, blue1; float red3, green3, blue3; float *pix1, *pix3; float brite, temp, ff1, ff2; dist = 0; // stop compiler warnings for (py = index; py < E3pxm->hh; py += NWT) for (px = 0; px < E3pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E1pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; red3 = pix3[0]; green3 = pix3[1]; blue3 = pix3[2]; switch (mode) { case 1: { // black and white positive red3 = green3 = blue3 = 0.333 * (red3 + green3 + blue3); break; } case 2: { // black and white negative red3 = green3 = blue3 = 255.9 - 0.333 * (red3 + green3 + blue3); break; } case 3: { // color negative red3 = 255.9 - red3; green3 = 255.9 - green3; blue3 = 255.9 - blue3; break; } case 4: { // rotate colors 17.01 temp = red3; red3 = green3; green3 = blue3; blue3 = temp; break; } case 5: { brite = red3; if (green3 > brite) brite = green3; // max. color level if (blue3 > brite) brite = blue3; brite = 0.2 * brite + 0.2666 * (red3 + green3 + blue3); // brightness, 0.0 ... 255.9 brite = brite * 0.003906; // 0.0 ... 1.0 ff1 = 1.0 - brite; // sepia part, 1.0 ... 0.0 ff2 = 1.0 - ff1; // B & W part, 0.0 ... 1.0 red3 = ff1 * 255.0 + ff2 * 256; // combine max. sepia with white green3 = ff1 * 150.0 + ff2 * 256; blue3 = ff1 * 46.0 + ff2 * 256; brite = 0.8333 * (brite + 0.2); // add brightness at low end red3 = red3 * brite; // output = combined color * brightness green3 = green3 * brite; blue3 = blue3 * brite; break; } } if (red3 < 0) red3 = 0; // prevent underflow/overlfow if (red3 > 255.9) red3 = 255.9; if (green3 < 0) green3 = 0; if (green3 > 255.9) green3 = 255.9; if (blue3 < 0) blue3 = 0; if (blue3 > 255.9) blue3 = 255.9; if (sa_stat == 3 && dist < sa_blend) { // blend changes over blendwidth ff1 = sa_blendfunc(dist); ff2 = 1.0 - ff1; red3 = ff1 * red3 + ff2 * red1; green3 = ff1 * green3 + ff2 * green1; blue3 = ff1 * blue3 + ff2 * blue1; } if (blend < 1.0) { ff1 = blend; ff2 = 1.0 - ff1; red3 = ff1 * red3 + ff2 * red1; green3 = ff1 * green3 + ff2 * green1; blue3 = ff1 * blue3 + ff2 * blue1; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } CEF->Fmods++; CEF->Fsaved = 0; exit_wthread(); return 0; } /********************************************************************************/ // image color-depth reduction int colordep_depth = 16; // bits per RGB color editfunc EFcolordep; void m_colordep(GtkWidget *, cchar *menu) { int colordep_dialog_event(zdialog *zd, cchar *event); void * colordep_thread(void *); cchar *colmess = ZTX("Set color depth to 1-16 bits"); F1_help_topic = "color_depth"; EFcolordep.menuname = menu; EFcolordep.menufunc = m_colordep; EFcolordep.funcname = "color_depth"; EFcolordep.FprevReq = 1; // use preview EFcolordep.Farea = 2; // select area usable EFcolordep.FusePL = 1; // use with paint/lever edits OK EFcolordep.Fscript = 1; // scripting supported 17.04 EFcolordep.threadfunc = colordep_thread; // thread function if (! edit_setup(EFcolordep)) return; // setup edit zdialog *zd = zdialog_new(ZTX("Set Color Depth"),Mwin,Bdone,Bcancel,null); EFcolordep.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",colmess,"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"zspin","colors","hb2","1|16|1|16","space=5"); colordep_depth = 16; zdialog_resize(zd,250,0); zdialog_run(zd,colordep_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int colordep_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) event = "colors"; // from script if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // done edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"undo")) edit_undo(); if (strmatch(event,"redo")) edit_redo(); if (strmatch(event,"blendwidth")) signal_thread(); if (strmatch(event,"colors")) { zdialog_fetch(zd,"colors",colordep_depth); signal_thread(); } return 0; } // image color depth thread function void * colordep_thread(void *) { int ii, px, py, rgb, dist = 0; uint16 m1, m2, val1, val3; float *pix1, *pix3; float fmag, f1, f2; while (true) { thread_idle_loop(); // wait for work or exit request m1 = 0xFFFF << (16 - colordep_depth); // 5 > 1111100000000000 m2 = 0x8000 >> colordep_depth; // 5 > 0000010000000000 fmag = 65535.0 / m1; for (py = 0; py < E3pxm->hh; py++) for (px = 0; px < E3pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel for (rgb = 0; rgb < 3; rgb++) { val1 = 256 * pix1[rgb]; // 16 bit integer <= 65535 if (val1 < m1) val3 = (val1 + m2) & m1; else val3 = m1; val3 = val3 * fmag; if (sa_stat == 3 && dist < sa_blend) { // select area is active, f2 = sa_blendfunc(dist); // blend changes over sa_blend f1 = 1.0 - f2; val3 = int(f1 * val1 + f2 * val3); } pix3[rgb] = val3 / 256; } } CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } /********************************************************************************/ // Shift Colors menu function // Gradually shift selected RGB colors into other colors. editfunc EFshiftcolr; // edit function data float shiftred = 0.5, shiftgreen = 0.5, shiftblue = 0.5; // menu function void m_shift_colors(GtkWidget *, cchar *menu) { int shiftcolr_dialog_event(zdialog* zd, const char *event); void * shiftcolr_thread(void *); F1_help_topic = "shift_colors"; EFshiftcolr.menuname = menu; EFshiftcolr.menufunc = m_shift_colors; EFshiftcolr.funcname = "shift_colors"; // function name EFshiftcolr.FprevReq = 1; // use preview EFshiftcolr.Farea = 2; // select area OK EFshiftcolr.Fscript = 1; // scripting supported EFshiftcolr.threadfunc = shiftcolr_thread; // thread function if (! edit_setup(EFshiftcolr)) return; // setup edit /*** Shift Colors red: green =======[]======= blue green: blue ==========[]==== red blue: red ======[]======== green all: all ========[]====== all [reset] [done] [cancel] ***/ zdialog *zd = zdialog_new(ZTX("Shift Colors"),Mwin,Breset,Bdone,Bcancel,null); EFshiftcolr.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"homog|space=3|expand"); zdialog_add_widget(zd,"vbox","vb4","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"label","labr","vb1",Bred); zdialog_add_widget(zd,"label","labr","vb1",Bgreen); zdialog_add_widget(zd,"label","labr","vb1",Bblue); zdialog_add_widget(zd,"label","labr","vb1",Ball); zdialog_add_widget(zd,"label","labg","vb2",Bgreen); zdialog_add_widget(zd,"label","labb","vb2",Bblue); zdialog_add_widget(zd,"label","labr","vb2",Bred); zdialog_add_widget(zd,"label","laba","vb2",Ball); zdialog_add_widget(zd,"hscale","red","vb3","0|1|0.001|0.5"); zdialog_add_widget(zd,"hscale","green","vb3","0|1|0.001|0.5"); zdialog_add_widget(zd,"hscale","blue","vb3","0|1|0.001|0.5"); zdialog_add_widget(zd,"hscale","all","vb3","0|1|0.001|0.5"); zdialog_add_widget(zd,"label","labb","vb4",Bblue); zdialog_add_widget(zd,"label","labr","vb4",Bred); zdialog_add_widget(zd,"label","labg","vb4",Bgreen); zdialog_add_widget(zd,"label","laba","vb4",Ball); zdialog_rescale(zd,"red",0,0.5,1); zdialog_rescale(zd,"green",0,0.5,1); zdialog_rescale(zd,"blue",0,0.5,1); zdialog_rescale(zd,"all",0,0.5,1); shiftred = shiftgreen = shiftblue = 0.5; // neutral values zdialog_resize(zd,350,0); zdialog_run(zd,shiftcolr_dialog_event,"save"); // run dialog - parallel return; } // shift color dialog event and completion function int shiftcolr_dialog_event(zdialog *zd, const char *event) { float shiftall; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"fullsize")) { // from select area if (! CEF->Fpreview) return 1; edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; // keep dialog active zdialog_stuff(zd,"red",0.5); zdialog_stuff(zd,"green",0.5); zdialog_stuff(zd,"blue",0.5); zdialog_stuff(zd,"all",0.5); edit_reset(); return 1; } if (zd->zstat == 2) { // done edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) return 1; if (strmatch(event,"all")) { zdialog_fetch(zd,"all",shiftall); shiftred = shiftgreen = shiftblue = shiftall; zdialog_stuff(zd,"red",shiftred); zdialog_stuff(zd,"green",shiftgreen); zdialog_stuff(zd,"blue",shiftblue); } zdialog_fetch(zd,"red",shiftred); zdialog_fetch(zd,"green",shiftgreen); zdialog_fetch(zd,"blue",shiftblue); if (strstr("red green blue all blendwidth apply",event)) // trigger update thread signal_thread(); return 1; } // thread function - launch multiple working threads to update image void * shiftcolr_thread(void *) { void * shiftcolr_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(shiftcolr_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image modified CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop warning } void * shiftcolr_wthread(void *arg) // worker thread function { int index = *((int *) (arg)); int px, py, ii, dist = 0; float *pix1, *pix3; float red1, green1, blue1, red3, green3, blue3; float lossR, lossG, lossB, gainR, gainG, gainB; float shift, move, maxrgb, f1, f2; for (py = index; py < E3pxm->hh; py += NWT) // loop all image pixels for (px = 0; px < E3pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // input pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; lossR = lossG = lossB = 0; gainR = gainG = gainB = 0; shift = shiftred; // red shift: green <-- red --> blue if (shift <= 0.5) { // 0.0 ... 0.5 move = red1 * 2.0 * (0.5 - shift); lossR += move; gainG += move; } if (shift > 0.5) { // 0.5 ... 1.0 move = red1 * 2.0 * (shift - 0.5); lossR += move; gainB += move; } shift = shiftgreen; // green shift: blue <-- green --> red if (shift <= 0.5) { // 0.0 ... 0.5 move = green1 * 2.0 * (0.5 - shift); lossG += move; gainB += move; } if (shift > 0.5) { // 0.5 ... 1.0 move = green1 * 2.0 * (shift - 0.5); lossG += move; gainR += move; } shift = shiftblue; // blue shift: red <-- blue --> green if (shift <= 0.5) { // 0.0 ... 0.5 move = blue1 * 2.0 * (0.5 - shift); lossB += move; gainR += move; } if (shift > 0.5) { // 0.5 ... 1.0 move = blue1 * 2.0 * (shift - 0.5); lossB += move; gainG += move; } pix3 = PXMpix(E3pxm,px,py); // output pixel red3 = red1 - lossR + gainR; green3 = green1 - lossG + gainG; blue3 = blue1 - lossB + gainB; maxrgb = red3; // find max. new RGB color if (green3 > maxrgb) maxrgb = green3; if (blue3 > maxrgb) maxrgb = blue3; if (maxrgb > 255.9) { // if too big, rescale all colors red3 = red3 * 255.9 / maxrgb; green3 = green3 * 255.9 / maxrgb; blue3 = blue3 * 255.9 / maxrgb; } if (sa_stat == 3 && dist < sa_blend) { // select area is active f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; red3 = f1 * red3 + f2 * red1; green3 = f1 * green3 + f2 * green1; blue3 = f1 * blue3 + f2 * blue1; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // image color saturation adjustment float colorsat; editfunc EFcolorsat; // menu function void m_colorsat(GtkWidget *, cchar *menu) { int colorsat_dialog_event(zdialog *zd, cchar *event); void * colorsat_thread(void *); cchar *colsatmess = ZTX("Color Saturation"); F1_help_topic = "color_saturation"; EFcolorsat.menuname = menu; EFcolorsat.menufunc = m_colorsat; EFcolorsat.funcname = "color_saturation"; EFcolorsat.FprevReq = 1; // use preview EFcolorsat.Farea = 2; // select area usable EFcolorsat.FusePL = 1; // use with paint/lever edits OK EFcolorsat.Fscript = 1; // scripting supported 17.04 EFcolorsat.threadfunc = colorsat_thread; // thread function if (! edit_setup(EFcolorsat)) return; // setup edit zdialog *zd = zdialog_new(colsatmess,Mwin,Bdone,Bcancel,null); EFcolorsat.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",colsatmess,"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"hscale","colorsat","hb2","-1.0|1.0|0.01|0","expand|space=5"); colorsat = 1.0; zdialog_resize(zd,250,0); zdialog_run(zd,colorsat_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int colorsat_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) event = "colorsat"; // from script if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // done edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"undo")) edit_undo(); if (strmatch(event,"redo")) edit_redo(); if (strmatch(event,"blendwidth")) signal_thread(); if (strmatch(event,"colorsat")) { zdialog_fetch(zd,"colorsat",colorsat); signal_thread(); } return 0; } // image color saturation thread function void * colorsat_thread(void *) { int ii, px, py, dist = 0; float *pix1, *pix3; float red1, green1, blue1; float red3, green3, blue3; float pixbrite, maxrgb, F1, F2; while (true) { thread_idle_loop(); // wait for work or exit request for (py = 0; py < E3pxm->hh; py++) for (px = 0; px < E3pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; pixbrite = 0.333 * (red1 + green1 + blue1); // pixel brightness, 0 to 255.9 red3 = red1 + colorsat * (red1 - pixbrite); // colorsat: -1 ... +1 green3 = green1 + colorsat * (green1 - pixbrite); blue3 = blue1 + colorsat * (blue1 - pixbrite); if (red3 < 0) red3 = 0; // stop underflow if (green3 < 0) green3 = 0; if (blue3 < 0) blue3 = 0; maxrgb = red3; // stop overflow if (green3 > maxrgb) maxrgb = green3; if (blue3 > maxrgb) maxrgb = blue3; if (maxrgb > 255.9) { red3 = red3 * 255.9 / maxrgb; green3 = green3 * 255.9 / maxrgb; blue3 = blue3 * 255.9 / maxrgb; } if (sa_stat == 3 && dist < sa_blend) { // select area edge blending F2 = sa_blendfunc(dist); F1 = 1.0 - F2; red3 = F2 * red3 + F1 * red1; green3 = F2 * green3 + F1 * green1; blue3 = F2 * blue3 + F1 * blue1; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } /********************************************************************************/ // Adjust RGB menu function // Adjust Brightness, contrast, and color levels using RGB or CMY colors editfunc EF_RGB; // edit function data float RGB_inputs[8]; // menu function void m_adjust_RGB(GtkWidget *, cchar *menu) { int RGB_dialog_event(zdialog *zd, cchar *event); void * RGB_thread(void *); F1_help_topic = "adjust_RGB_CMY"; EF_RGB.menuname = menu; EF_RGB.menufunc = m_adjust_RGB; EF_RGB.funcname = "adjust_RGB"; // function name EF_RGB.FprevReq = 1; // use preview EF_RGB.Farea = 2; // select area usable EF_RGB.Frestart = 1; // allow restart EF_RGB.Fscript = 1; // scripting supported EF_RGB.threadfunc = RGB_thread; // thread function if (! edit_setup(EF_RGB)) return; // setup edit /*** _______________________________________________________ | | | +Brightness =====[]===== Contrast =====[]===== | | +Red -Cyan =====[]===== Red =====[]===== | | +Green -Magenta =====[]===== Green =====[]===== | | +Blue -Yellow =====[]===== Blue =====[]===== | | | | [reset] [done] [cancel] | |_______________________________________________________| ***/ zdialog *zd = zdialog_new("Adjust RGB",Mwin,Breset,Bdone,Bcancel,null); EF_RGB.zd = zd; zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb2",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vb2","hb2",0,"homog|expand|space=5"); zdialog_add_widget(zd,"label","space","hb2",0,"space=8"); zdialog_add_widget(zd,"vbox","vb3","hb2",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vb4","hb2",0,"homog|expand|space=5"); zdialog_add_widget(zd,"label","labBriteDens","vb1",ZTX("+Brightness")); zdialog_add_widget(zd,"label","labRedDens","vb1",ZTX("+Red -Cyan")); zdialog_add_widget(zd,"label","labGreenDens","vb1",ZTX("+Green -Magenta")); zdialog_add_widget(zd,"label","labBlueDens","vb1",ZTX("+Blue -Yellow")); zdialog_add_widget(zd,"hscale","BriteDens","vb2","-1|+1|0.001|0","expand"); zdialog_add_widget(zd,"hscale","RedDens","vb2","-1|+1|0.001|0","expand"); zdialog_add_widget(zd,"hscale","GreenDens","vb2","-1|+1|0.001|0","expand"); zdialog_add_widget(zd,"hscale","BlueDens","vb2","-1|+1|0.001|0","expand"); zdialog_add_widget(zd,"label","labContrast","vb3",Bcontrast); zdialog_add_widget(zd,"label","labRedCon","vb3",ZTX("Red")); zdialog_add_widget(zd,"label","labGreenCon","vb3",ZTX("Green")); zdialog_add_widget(zd,"label","labBlueCon","vb3",ZTX("Blue")); zdialog_add_widget(zd,"hscale","Contrast","vb4","-1|+1|0.001|0","expand"); zdialog_add_widget(zd,"hscale","RedCon","vb4","-1|+1|0.001|0","expand"); zdialog_add_widget(zd,"hscale","GreenCon","vb4","-1|+1|0.001|0","expand"); zdialog_add_widget(zd,"hscale","BlueCon","vb4","-1|+1|0.001|0","expand"); zdialog_rescale(zd,"BriteDens",-1,0,+1); zdialog_rescale(zd,"RedDens",-1,0,+1); zdialog_rescale(zd,"GreenDens",-1,0,+1); zdialog_rescale(zd,"BlueDens",-1,0,+1); zdialog_rescale(zd,"Contrast",-1,0,+1); zdialog_rescale(zd,"RedCon",-1,0,+1); zdialog_rescale(zd,"GreenCon",-1,0,+1); zdialog_rescale(zd,"BlueCon",-1,0,+1); zdialog_resize(zd,500,0); zdialog_restore_inputs(zd); // restore prior inputs zdialog_run(zd,RGB_dialog_event,"save"); // run dialog - parallel zdialog_send_event(zd,"apply"); return; } // RGB dialog event and completion function int RGB_dialog_event(zdialog *zd, cchar *event) // RGB dialog event function { int mod = 0; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; // keep dialog active RGB_inputs[0] = 0; RGB_inputs[1] = 0; RGB_inputs[2] = 0; RGB_inputs[3] = 0; RGB_inputs[4] = 0; RGB_inputs[5] = 0; RGB_inputs[6] = 0; RGB_inputs[7] = 0; zdialog_stuff(zd,"BriteDens",0); zdialog_stuff(zd,"RedDens",0); zdialog_stuff(zd,"GreenDens",0); zdialog_stuff(zd,"BlueDens",0); zdialog_stuff(zd,"Contrast",0); zdialog_stuff(zd,"RedCon",0); zdialog_stuff(zd,"GreenCon",0); zdialog_stuff(zd,"BlueCon",0); mod++; } else if (zd->zstat == 2) { // done edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit return 1; } else { edit_cancel(0); // discard edit return 1; } } if (strmatch("focus",event)) return 1; zdialog_fetch(zd,"BriteDens",RGB_inputs[0]); // get all inputs zdialog_fetch(zd,"RedDens",RGB_inputs[1]); zdialog_fetch(zd,"GreenDens",RGB_inputs[2]); zdialog_fetch(zd,"BlueDens",RGB_inputs[3]); zdialog_fetch(zd,"Contrast",RGB_inputs[4]); zdialog_fetch(zd,"RedCon",RGB_inputs[5]); zdialog_fetch(zd,"GreenCon",RGB_inputs[6]); zdialog_fetch(zd,"BlueCon",RGB_inputs[7]); if (RGB_inputs[0]) mod++; if (RGB_inputs[1]) mod++; if (RGB_inputs[2]) mod++; if (RGB_inputs[3]) mod++; if (RGB_inputs[4]) mod++; if (RGB_inputs[5]) mod++; if (RGB_inputs[6]) mod++; if (RGB_inputs[7]) mod++; if (mod) signal_thread(); // trigger update thread return 1; } // thread function - multiple working threads to update image void * RGB_thread(void *) { void * RGB_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request if (sa_stat == 3) Fbusy_goal = sa_Npixel; // set up progress monitor else Fbusy_goal = E3pxm->ww * E3pxm->hh; Fbusy_done = 0; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(RGB_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = Fbusy_done = 0; CEF->Fmods++; // image modified CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop warning } // worker thread function void * RGB_wthread(void *arg) // overhauled { float R1, G1, B1, R3, G3, B3; float briA, briR, briG, briB; float conA, conR, conG, conB; float R, G, B; int index = *((int *) (arg)); int px, py, ii, dist = 0; float *pix1, *pix3; float cmax, F, f1, f2; briA = RGB_inputs[0]; // color brightness inputs, -1 to +1 briR = RGB_inputs[1]; briG = RGB_inputs[2]; briB = RGB_inputs[3]; R = briR - 0.5 * briG - 0.5 * briB + briA; // red = red - green - blue + all G = briG - 0.5 * briR - 0.5 * briB + briA; // etc. B = briB - 0.5 * briR - 0.5 * briG + briA; R += 1; // -1 ... 0 ... +1 >> 0 ... 1 ... 2 G += 1; // increase the range B += 1; briR = R; // final color brightness factors briG = G; briB = B; conA = RGB_inputs[4]; // contrast inputs, -1 to +1 conR = RGB_inputs[5]; conG = RGB_inputs[6]; conB = RGB_inputs[7]; if (conA < 0) conA = 0.5 * conA + 1; // -1 ... 0 >> 0.5 ... 1.0 else conA = conA + 1; // 0 ... 1 >> 1.0 ... 2.0 if (conR < 0) conR = 0.5 * conR + 1; else conR = conR + 1; if (conG < 0) conG = 0.5 * conG + 1; else conG = conG + 1; if (conB < 0) conB = 0.5 * conB + 1; else conB = conB + 1; conR = conR * conA; // apply overall contrast conG = conG * conA; conB = conB * conA; for (py = index; py < E3pxm->hh; py += NWT) // loop all image pixels for (px = 0; px < E3pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel R1 = pix1[0]; // input RGB values, 0-255 G1 = pix1[1]; B1 = pix1[2]; R3 = R1 * briR; // apply color brightness factors G3 = G1 * briG; B3 = B1 * briB; R3 = conR * (R3 - 128) + 128; // apply contrast factors G3 = conG * (G3 - 128) + 128; B3 = conB * (B3 - 128) + 128; if (R3 < 0) R3 = 0; // stop underflow if (G3 < 0) G3 = 0; if (B3 < 0) B3 = 0; if (R3 > 255.9 || G3 > 255.9 || B3 > 255.9) { // stop overflow cmax = R3; if (G3 > cmax) cmax = G3; if (B3 > cmax) cmax = B3; F = 255.9 / cmax; R3 *= F; G3 *= F; B3 *= F; } if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; R3 = f1 * R3 + f2 * R1; G3 = f1 * G3 + f2 * G1; B3 = f1 * B3 + f2 * B1; } pix3[0] = R3; // output RGB values pix3[1] = G3; pix3[2] = B3; } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // HSL color menu function // Adjust colors using the HSL (hue/saturation/lightness) color model namespace HSL_names { GtkWidget *RGBframe, *RGBcolor; GtkWidget *Hframe, *Hscale; editfunc EFHSL; // edit function data int Huse, Suse, Luse; // "match using" flags, 0 or 1 int Hout, Sout, Lout; // "output color" flags, 0 or 1 float Rm, Gm, Bm; // RGB image color to match float Hm, Sm, Lm; // corresp. HSL color float Mlev; // match level 0..1 = 100% float Hc, Sc, Lc; // new color to add float Rc, Gc, Bc; // corresp. RGB color float Adj; // color adjustment, 0..1 = 100% } void HSLtoRGB(float H, float S, float L, float &R, float &G, float &B); void RGBtoHSL(float R, float G, float B, float &H, float &S, float &L); // menu function void m_adjust_HSL(GtkWidget *, const char *) // overhauled { using namespace HSL_names; void HSL_RGBcolor(GtkWidget *drawarea, cairo_t *cr, int *); void HSL_Hscale(GtkWidget *drawarea, cairo_t *cr, int *); int HSL_dialog_event(zdialog *zd, cchar *event); void HSL_mousefunc(); void * HSL_thread(void *); F1_help_topic = "adjust_HSL"; EFHSL.menufunc = m_adjust_HSL; EFHSL.funcname = "adjust_HSL"; // function name EFHSL.FprevReq = 1; // use preview EFHSL.Farea = 2; // select area usable EFHSL.Frestart = 1; // allow restart EFHSL.FusePL = 1; // use with paint/lever edits OK EFHSL.mousefunc = HSL_mousefunc; // mouse function EFHSL.threadfunc = HSL_thread; // thread function if (! edit_setup(EFHSL)) return; // setup edit /*** ___________________________________________________ | | | Input color to match and adjust: [#####] | | Match using: [] Hue [] Saturation [] Lightness | | Match Level: ==================[]========== 100% | | - - - - - - - - - - - - - - - - - - - - - - - - | | Output Color | | [########] [#################################] | // new color and hue spectrum | [] Color Hue ================[]============== | | [] Saturation =====================[]========= | | [] Lightness ===========[]=================== | | Adjustment ===================[]========== 100% | | | | [reset] [done] [cancel] | |___________________________________________________| ***/ zdialog *zd = zdialog_new("Adjust HSL",Mwin,Breset,Bdone,Bcancel,null); EFHSL.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"label","labmatch","hb1",ZTX("Input color to match and adjust:"),"space=5"); zdialog_add_widget(zd,"colorbutt","matchRGB","hb1","0|0|0"); zdialog_add_ttip(zd,"matchRGB",ZTX("shift+click on image to select color")); zdialog_add_widget(zd,"hbox","hbmu","dialog"); zdialog_add_widget(zd,"label","labmu","hbmu",ZTX("Match using:"),"space=5"); zdialog_add_widget(zd,"check","Huse","hbmu",ZTX("Hue"),"space=3"); zdialog_add_widget(zd,"check","Suse","hbmu",ZTX("Saturation"),"space=3"); zdialog_add_widget(zd,"check","Luse","hbmu",ZTX("Lightness"),"space=3"); zdialog_add_widget(zd,"hbox","hbmatch","dialog"); zdialog_add_widget(zd,"label","labmatch","hbmatch",Bmatchlevel,"space=5"); zdialog_add_widget(zd,"hscale","Mlev","hbmatch","0|1|0.001|1.0","expand"); zdialog_add_widget(zd,"label","lab100%","hbmatch","100%","space=4"); zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"label","laboutput","hb1",ZTX("Output Color")); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb2",0,"homog"); zdialog_add_widget(zd,"vbox","vb2","hb2",0,"homog|expand"); zdialog_add_widget(zd,"frame","RGBframe","vb1",0,"space=1"); // drawing area for RGB color RGBframe = zdialog_widget(zd,"RGBframe"); RGBcolor = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(RGBframe),RGBcolor); gtk_widget_set_size_request(RGBcolor,0,16); G_SIGNAL(RGBcolor,"draw",HSL_RGBcolor,0); zdialog_add_widget(zd,"frame","Hframe","vb2",0,"space=1"); // drawing area for hue scale Hframe = zdialog_widget(zd,"Hframe"); Hscale = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(Hframe),Hscale); gtk_widget_set_size_request(Hscale,200,16); G_SIGNAL(Hscale,"draw",HSL_Hscale,0); zdialog_add_widget(zd,"check","Hout","vb1",ZTX("Color Hue")); zdialog_add_widget(zd,"check","Sout","vb1",ZTX("Saturation")); zdialog_add_widget(zd,"check","Lout","vb1",ZTX("Lightness")); zdialog_add_widget(zd,"label","labadjust","vb1",ZTX("Adjustment")); zdialog_add_widget(zd,"hscale","Hc","vb2","0|359.9|0.1|180","expand"); zdialog_add_widget(zd,"hscale","Sc","vb2","0|1|0.001|0.5","expand"); zdialog_add_widget(zd,"hscale","Lc","vb2","0|1|0.001|0.5","expand"); zdialog_add_widget(zd,"hbox","vb2hb","vb2"); zdialog_add_widget(zd,"hscale","Adj","vb2hb","0|1|0.001|0.0","expand"); zdialog_add_widget(zd,"label","lab100%","vb2hb","100%","space=4"); zdialog_stuff(zd,"Huse",1); // default: match on hue and saturation zdialog_stuff(zd,"Suse",1); zdialog_stuff(zd,"Luse",0); zdialog_stuff(zd,"Hout",1); // default: replace only hue zdialog_stuff(zd,"Sout",0); zdialog_stuff(zd,"Lout",0); Rm = Gm = Bm = 0; // color to match = black = not set Hm = Sm = Lm = 0; Huse = Suse = 1; Luse = 0; Hout = 1; Sout = Lout = 0; Mlev = 1.0; Hc = 180; // new HSL color to set / mix Sc = 0.5; Lc = 0.5; Adj = 0.0; zdialog_run(zd,HSL_dialog_event,"save"); // run dialog - parallel takeMouse(HSL_mousefunc,arrowcursor); // connect mouse function return; } // Paint RGBcolor drawing area with RGB color from new HSL color void HSL_RGBcolor(GtkWidget *drawarea, cairo_t *cr, int *) { using namespace HSL_names; int ww, hh; ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); HSLtoRGB(Hc,Sc,Lc,Rc,Gc,Bc); // new RGB color cairo_set_source_rgb(cr,Rc,Gc,Bc); cairo_rectangle(cr,0,0,ww-1,hh-1); cairo_fill(cr); return; } // Paint Hscale drawing area with all hue values in a horizontal scale void HSL_Hscale(GtkWidget *drawarea, cairo_t *cr, int *) { using namespace HSL_names; int px, ww, hh; float H, S, L, R, G, B; ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); S = L = 0.5; // 17.08 for (px = 0; px < ww; px++) // paint hue color scale { H = 360 * px / ww; HSLtoRGB(H,S,L,R,G,B); // 17.08 cairo_set_source_rgb(cr,R,G,B); cairo_move_to(cr,px,0); cairo_line_to(cr,px,hh-1); cairo_stroke(cr); } return; } // HSL dialog event and completion function int HSL_dialog_event(zdialog *zd, cchar *event) // HSL dialog event function { using namespace HSL_names; void HSL_mousefunc(); int mod = 0; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; // keep dialog active Mlev = 1.0; Hc = 180; // set defaults Sc = 0.5; Lc = 0.5; Adj = 0.0; zdialog_stuff(zd,"Mlev",Mlev); zdialog_stuff(zd,"Hc",Hc); zdialog_stuff(zd,"Sc",Sc); zdialog_stuff(zd,"Lc",Lc); zdialog_stuff(zd,"Adj",Adj); mod = 1; } else if (zd->zstat == 2) { // done edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit return 1; } else { edit_cancel(0); // discard edit return 1; } } if (strmatch("focus",event)) { takeMouse(HSL_mousefunc,arrowcursor); return 1; } if (strmatch(event,"Huse")) { // match on Hue, 0/1 zdialog_fetch(zd,"Huse",Huse); mod = 1; } if (strmatch(event,"Suse")) { // match on Saturation, 0/1 zdialog_fetch(zd,"Suse",Suse); mod = 1; } if (strmatch(event,"Luse")) { // match on Lightness, 0/1 zdialog_fetch(zd,"Luse",Luse); mod = 1; } if (strmatch(event,"Hout")) { // replace Hue, 0/1 zdialog_fetch(zd,"Hout",Hout); mod = 1; } if (strmatch(event,"Sout")) { // replace Saturation, 0/1 zdialog_fetch(zd,"Sout",Sout); mod = 1; } if (strmatch(event,"Lout")) { // replace Lightness, 0/1 zdialog_fetch(zd,"Lout",Lout); mod = 1; } if (strmatch("Mlev",event)) { // color match 0..1 = 100% zdialog_fetch(zd,"Mlev",Mlev); mod = 1; } if (strmatch("Hc",event)) { // new color hue 0-360 zdialog_fetch(zd,"Hc",Hc); mod = 1; } if (strmatch("Sc",event)) { // saturation 0-1 zdialog_fetch(zd,"Sc",Sc); mod = 1; } if (strmatch("Lc",event)) { // lightness 0-1 zdialog_fetch(zd,"Lc",Lc); mod = 1; } if (strmatch("Adj",event)) { // adjustment 0..1 = 100% zdialog_fetch(zd,"Adj",Adj); mod = 1; } if (strmatch("blendwidth",event)) mod = 1; // area blend width changed if (mod) { gtk_widget_queue_draw(RGBcolor); // draw current RGB color signal_thread(); // trigger update thread } return 1; } // mouse function // click on image to set the color to match and change void HSL_mousefunc() { using namespace HSL_names; int mx, my, px, py; char color[20]; float *pix1, R, G, B; float f256 = 1.0 / 256.0; zdialog *zd = EFHSL.zd; if (! KBshiftkey) return; // check shift + left or right click if (! LMclick && ! RMclick) return; mx = Mxclick; // clicked pixel on image my = Myclick; if (mx < 1) mx = 1; // pull back from image edge if (mx > E3pxm->ww - 2) mx = E3pxm->ww - 2; if (my < 1) my = 1; if (my > E3pxm->hh - 2) my = E3pxm->hh - 2; R = G = B = 0; for (py = my-1; py <= my+1; py++) // compute average RGB for 3x3 18.01 for (px = mx-1; px <= mx+1; px++) // block of pixels { pix1 = PXMpix(E1pxm,px,py); R += pix1[0]; G += pix1[1]; B += pix1[2]; } R = R / 9; G = G / 9; B = B / 9; if (LMclick) // left mouse click { // pick MATCH color from image LMclick = 0; snprintf(color,19,"%.0f|%.0f|%.0f",R,G,B); // draw new match color button if (zd) zdialog_stuff(zd,"matchRGB",color); Rm = R * f256; Gm = G * f256; Bm = B * f256; RGBtoHSL(Rm,Gm,Bm,Hm,Sm,Lm); // set HSL color to match } if (RMclick) // right mouse click { // pick OUTPUT color from image RMclick = 0; R = R * f256; G = G * f256; B = B * f256; RGBtoHSL(R,G,B,Hc,Sc,Lc); // output HSL zdialog_stuff(zd,"Hc",Hc); zdialog_stuff(zd,"Sc",Sc); zdialog_stuff(zd,"Lc",Lc); gtk_widget_queue_draw(RGBcolor); // draw current RGB color signal_thread(); // trigger update thread } return; } // thread function - multiple working threads to update image void * HSL_thread(void *) { using namespace HSL_names; void * HSL_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(HSL_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image modified CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop warning } // worker thread function void * HSL_wthread(void *arg) { using namespace HSL_names; int index = *((int *) (arg)); int px, py, ii, dist = 0; float *pix1, *pix3; float R1, G1, B1, R3, G3, B3; float H1, S1, L1, H3, S3, L3; float dH, dS, dL, match; float a1, a2, f1, f2; float f256 = 1.0 / 256.0; for (py = index; py < E3pxm->hh; py += NWT) // loop all image pixels for (px = 0; px < E3pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel R1 = f256 * pix1[0]; // input pixel RGB G1 = f256 * pix1[1]; B1 = f256 * pix1[2]; RGBtoHSL(R1,G1,B1,H1,S1,L1); // convert to HSL match = 1.0; // compare image pixel to match HSL if (Huse) { dH = fabsf(Hm - H1); if (360 - dH < dH) dH = 360 - dH; dH *= 0.002778; // H difference, normalized 0..1 match = 1.0 - dH; } if (Suse) { dS = fabsf(Sm - S1); // S difference, 0..1 match *= (1.0 - dS); } if (Luse) { dL = fabsf(Lm - L1); // L difference, 0..1 match *= (1.0 - dL); } a1 = pow(match, 10.0 * Mlev); // color selectivity, 0..1 = max a1 = Adj * a1; a2 = 1.0 - a1; if (Hout) H3 = a1 * Hc + a2 * H1; // output HSL = a1 * new HSL else H3 = H1; // + a2 * old HSL if (Sout) S3 = a1 * Sc + a2 * S1; else S3 = S1; if (Lout) L3 = a1 * Lc + a2 * L1; else L3 = L1; HSLtoRGB(H3,S3,L3,R3,G3,B3); if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; R3 = f1 * R3 + f2 * R1; G3 = f1 * G3 + f2 * G1; B3 = f1 * B3 + f2 * B1; } pix3[0] = 255.0 * R3; pix3[1] = 255.0 * G3; pix3[2] = 255.0 * B3; } exit_wthread(); return 0; // not executed, avoid gcc warning } // HSL to RGB converter (Wikipedia) // H = 0-360 deg. S = 0-1 L = 0-1 // output RGB values = 0-1 void HSLtoRGB(float H, float S, float L, float &R, float &G, float &B) { float C, X, M; float h1, h2; h1 = H / 60; h2 = h1 - 2 * int(h1/2); C = (1 - fabsf(2*L-1)) * S; X = C * (1 - fabsf(h2-1)); M = L - C/2; if (H < 60) { R = C; G = X; B = 0; } else if (H < 120) { R = X; G = C; B = 0; } else if (H < 180) { R = 0; G = C; B = X; } else if (H < 240) { R = 0; G = X; B = C; } else if (H < 300) { R = X; G = 0; B = C; } else { R = C; G = 0; B = X; } R = R + M; G = G + M; B = B + M; return; } // RGB to HSL converter // input RGB values 0-1 // outputs: H = 0-360 deg. S = 0-1 L = 0-1 void RGBtoHSL(float R, float G, float B, float &H, float &S, float &L) { float max, min, D; max = R; if (G > max) max = G; if (B > max) max = B; min = R; if (G < min) min = G; if (B < min) min = B; D = max - min; L = 0.5 * (max + min); if (D < 0.004) { H = S = 0; return; } if (L > 0.5) S = D / (2 - max - min); else S = D / (max + min); if (max == R) H = (G - B) / D; else if (max == G) H = 2 + (B - R) / D; else H = 4 + (R - G) / D; H = H * 60; if (H < 0) H += 360; return; } /********************************************************************************/ // Revise color in selected image areas. // Show RGB values for 1-9 pixels selected with mouse-clicks. // Revise RGB values for the selected pixels and then revise all other // pixels to match, using a distance-weighted average of the selected pixels. void zonal_colors_dialog(); void zonal_colors_mousefunc(); void zonal_colors_stuff(); void * zonal_colors_thread(void *); int zonal_colors_pixel[9][2]; // last 9 pixel locations clicked float zonal_colors_val1[9][3]; // original RGB values, 0 to 255.9 float zonal_colors_val3[9][3]; // revised RGB values, 0 to 255.9 int zonal_colors_metric = 1; // 1/2/3 = /RGB/EV/OD int zonal_colors_delta = 0; // flag, delta mode int zonal_colors_npix; // no. of selected pixels double zonal_colors_time; // last click time int zonal_colors_blend; // blend factor, 1 to 100 editfunc EFrgbrevise; // edit function parameters #define RGB2EV(rgb) (log2(rgb)-7) // RGB 0.1 to 255.9 >> EV -10 to +1 #define EV2RGB(ev) (pow(2,ev+7)) // inverse #define RGB2OD(rgb) (2-log10(100*rgb/256)) // RGB 0.1 to 255.9 >> OD 3.4 to 0.000017 #define OD2RGB(od) (pow(10,2.40824-od)) // inverse // menu function void m_zonal_colors(GtkWidget *, cchar *menu) { int zonal_colors_event(zdialog *zd, cchar *event); cchar *limits; zdialog *zd; cchar *mess = ZTX("Click image to select areas."); F1_help_topic = "zonal_colors"; EFrgbrevise.menufunc = m_zonal_colors; EFrgbrevise.funcname = "zonal_colors"; // setup edit function EFrgbrevise.threadfunc = zonal_colors_thread; EFrgbrevise.mousefunc = zonal_colors_mousefunc; EFrgbrevise.Farea = 1; if (! edit_setup(EFrgbrevise)) return; /*** Click image to select areas. [x] delta Metric: (o) RGB (o) EV (o) OD Pixel Red Green Blue A xxxx xxxx [ ] [ ] [ ] // spin buttons for RGB/EV/OD values B xxxx xxxx [ ] [ ] [ ] // spin buttons for RGB/EV/OD values C xxxx xxxx [ ] [ ] [ ] // spin buttons for RGB/EV/OD values D xxxx xxxx [ ] [ ] [ ] // spin buttons for RGB/EV/OD values E xxxx xxxx [ ] [ ] [ ] // spin buttons for RGB/EV/OD values F xxxx xxxx [ ] [ ] [ ] // spin buttons for RGB/EV/OD values G xxxx xxxx [ ] [ ] [ ] // spin buttons for RGB/EV/OD values H xxxx xxxx [ ] [ ] [ ] // spin buttons for RGB/EV/OD values I xxxx xxxx [ ] [ ] [ ] // spin buttons for RGB/EV/OD values Blend ==============[]================ [reset] [done] [cancel] ***/ zd = zdialog_new(ZTX("Local Color"),Mwin,Breset,Bdone,Bcancel,null); EFrgbrevise.zd = zd; zdialog_add_widget(zd,"hbox","hbmess","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labmess","hbmess",mess,"space=5"); zdialog_add_widget(zd,"hbox","hbmym","dialog"); zdialog_add_widget(zd,"check","delta","hbmym","delta","space=5"); zdialog_stuff(zd,"delta",zonal_colors_delta); zdialog_add_widget(zd,"hbox","hbmetr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labmetr","hbmetr",ZTX("Metric:"),"space=5"); zdialog_add_widget(zd,"radio","radRGB","hbmetr","RGB","space=3"); zdialog_add_widget(zd,"radio","radEV","hbmetr","EV","space=3"); zdialog_add_widget(zd,"radio","radOD","hbmetr","OD","space=3"); if (zonal_colors_metric == 1) zdialog_stuff(zd,"radRGB",1); // set current metric if (zonal_colors_metric == 2) zdialog_stuff(zd,"radEV",1); if (zonal_colors_metric == 3) zdialog_stuff(zd,"radOD",1); zdialog_add_widget(zd,"hbox","hbdata","dialog"); zdialog_add_widget(zd,"vbox","vbpix","hbdata",0,"space=3|homog"); // vbox for pixel locations zdialog_add_widget(zd,"hbox","hbpix","vbpix"); zdialog_add_widget(zd,"label","labpix","hbpix","Pixel"); // header char hbpixx[8] = "hbpixx", pixx[8] = "pixx"; for (int ii = 1; ii < 10; ii++) { // add labels for pixel locations hbpixx[5] = '0' + ii; pixx[3] = '0' + ii; zdialog_add_widget(zd,"hbox",hbpixx,"vbpix"); // add hbox "hbpix1" to "hbpix9" zdialog_add_widget(zd,"label",pixx,hbpixx); // add label "pix1" to "pix9" } if (zonal_colors_metric == 1) limits = "-255|255|1|0.0"; // metric = RGB 17.08 else if (zonal_colors_metric == 2) limits = "-8|8|0.01|0.0"; // EV else limits = "-3|3|0.01|0.0"; // OD zdialog_add_widget(zd,"vbox","vbdat","hbdata",0,"space=3|homog"); // vbox for pixel RGB values zdialog_add_widget(zd,"hbox","hbrgb","vbdat",0,"homog"); zdialog_add_widget(zd,"label","labr","hbrgb",Bred); zdialog_add_widget(zd,"label","labg","hbrgb",Bgreen); zdialog_add_widget(zd,"label","labb","hbrgb",Bblue); char hbdatx[8] = "hbdatx", redx[8] = "redx", greenx[8] = "greenx", bluex[8] = "bluex"; for (int ii = 1; ii < 10; ii++) { hbdatx[5] = '0' + ii; redx[3] = '0' + ii; greenx[5] = '0' + ii; bluex[4] = '0' + ii; zdialog_add_widget(zd,"hbox",hbdatx,"vbdat"); // add hbox "hbdat1" to "hbdat9" zdialog_add_widget(zd,"zspin",redx,hbdatx,limits,"space=3"); // add spin buttons "red1" to "red9" etc. zdialog_add_widget(zd,"zspin",greenx,hbdatx,limits,"space=3"); // zspin 17.08 zdialog_add_widget(zd,"zspin",bluex,hbdatx,limits,"space=3"); } zdialog_add_widget(zd,"hbox","hbsoft","dialog","space=5"); zdialog_add_widget(zd,"label","labsoft","hbsoft",ZTX("Blend"),"space=3"); zdialog_add_widget(zd,"hscale","blend","hbsoft","0|100|1|20","space=5|expand"); zdialog_add_widget(zd,"label","softval","hbsoft","20"); if (strmatch(menu,"restart")) // stuff current pixels if restart zonal_colors_stuff(); else { zonal_colors_blend = 20; // default slider value zonal_colors_npix = 0; // no pixels selected yet } zdialog_run(zd,zonal_colors_event,"save"); // run dialog takeMouse(zonal_colors_mousefunc,dragcursor); // connect mouse function return; } // dialog event function int zonal_colors_event(zdialog *zd, cchar *event) { int button; int ii, jj; char text[8]; float val1, val3; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // Reset zd->zstat = 0; // keep dialog active zonal_colors_npix = 0; // no pixels selected yet zonal_colors_stuff(); // clear dialog edit_reset(); // reset edits return 1; } if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit EFrgbrevise.zd = 0; erase_toptext(101); // erase pixel labels from image return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(zonal_colors_mousefunc,dragcursor); // connect mouse function if (strmatch(event,"blend")) { // new blend factor zdialog_fetch(zd,"blend",zonal_colors_blend); snprintf(text,8,"%d",zonal_colors_blend); // numeric feedback zdialog_stuff(zd,"softval",text); signal_thread(); return 1; } if (strmatchN(event,"rad",3)) // metric was changed { if (strmatch(event,"radRGB")) { // RGB zdialog_fetch(zd,event,button); if (button) zonal_colors_metric = 1; } if (strmatch(event,"radEV")) { // EV zdialog_fetch(zd,event,button); if (button) zonal_colors_metric = 2; } if (strmatch(event,"radOD")) { // OD zdialog_fetch(zd,event,button); if (button) zonal_colors_metric = 3; } if (button) { // restart with new limits edit_cancel(0); m_zonal_colors(0,"restart"); } return 1; } if (strmatch(event,"delta")) { // change absolute/delta mode zdialog_fetch(zd,"delta",zonal_colors_delta); edit_cancel(0); m_zonal_colors(0,"restart"); return 1; } ii = -1; // no RGB change yet if (strmatchN(event,"red",3)) { // red1 - red9 was changed ii = event[3] - '1'; // pixel index 0-8 jj = 0; // color = red } if (strmatchN(event,"green",5)) { // green1 - green9 ii = event[5] - '1'; jj = 1; } if (strmatchN(event,"blue",4)) { // blue1 - blue9 ii = event[4] - '1'; jj = 2; } if (ii >= 0 && ii < 9) // RGB value was revised { val1 = zonal_colors_val1[ii][jj]; // original pixel RGB value if (zonal_colors_metric == 2) val1 = RGB2EV(val1); // convert to EV or OD units if (zonal_colors_metric == 3) val1 = RGB2OD(val1); zdialog_fetch(zd,event,val3); // revised RGB/EV/OD value from dialog if (zonal_colors_delta) val3 += val1; // if delta mode, make absolute if (zonal_colors_metric == 2) val3 = EV2RGB(val3); // convert EV/OD to RGB value if (zonal_colors_metric == 3) val3 = OD2RGB(val3); if (fabsf(zonal_colors_val3[ii][jj] - val3) < 0.001) return 1; // ignore re-entry after change if (val3 < 0.1) val3 = 0.1; // limit RGB within 0.1 to 255.9 if (val3 > 255.9) val3 = 255.9; zonal_colors_val3[ii][jj] = val3; // new RGB value for pixel if (zonal_colors_metric == 2) val3 = RGB2EV(val3); // convert RGB to EV/OD units if (zonal_colors_metric == 3) val3 = RGB2OD(val3); if (zonal_colors_delta) val3 -= val1; // absolute back to relative zdialog_stuff(zd,event,val3); // limited value back to dialog signal_thread(); // signal thread to update image } return 1; } // mouse function void zonal_colors_mousefunc() // mouse function { int ii; float *pix3; if (! LMclick) return; LMclick = 0; zonal_colors_time = get_seconds(); // mark time of pixel click if (zonal_colors_npix == 9) { // if table is full (9 entries) for (ii = 1; ii < 9; ii++) { // move positions 1-8 up zonal_colors_pixel[ii-1][0] = zonal_colors_pixel[ii][0]; // to fill positions 0-7 zonal_colors_pixel[ii-1][1] = zonal_colors_pixel[ii][1]; } zonal_colors_npix = 8; // count is now 8 entries } ii = zonal_colors_npix; // next table position to fill zonal_colors_pixel[ii][0] = Mxclick; // newest pixel zonal_colors_pixel[ii][1] = Myclick; pix3 = PXMpix(E3pxm,Mxclick,Myclick); // get initial RGB values from zonal_colors_val1[ii][0] = zonal_colors_val3[ii][0] = pix3[0]; // modified image E3 zonal_colors_val1[ii][1] = zonal_colors_val3[ii][1] = pix3[1]; zonal_colors_val1[ii][2] = zonal_colors_val3[ii][2] = pix3[2]; zonal_colors_npix++; // up pixel count zonal_colors_stuff(); // stuff pixels and values into dialog return; } // stuff dialog with current pixels and their RGB/EV/OD values void zonal_colors_stuff() { static char lab1[9][4] = { "A", "B", "C", "D", "E", "F", "G", "H", "I" }; static char lab2[9][4] = { " A ", " B ", " C ", " D ", " E ", " F ", " G ", " H ", " I " }; int px, py; float red1, green1, blue1, red3, green3, blue3; char text[100], pixx[8] = "pixx"; char redx[8] = "redx", greenx[8] = "greenx", bluex[8] = "bluex"; zdialog *zd = EFrgbrevise.zd; erase_toptext(101); // erase prior labels from image for (int ii = 0; ii < 9; ii++) // loop slots 0-8 { pixx[3] = '1' + ii; // widget names "pix1" ... "pix9" redx[3] = '1' + ii; // widget names "red1" ... "red9" greenx[5] = '1' + ii; bluex[4] = '1' + ii; px = zonal_colors_pixel[ii][0]; // next pixel to report py = zonal_colors_pixel[ii][1]; if (ii >= zonal_colors_npix) { // > last pixel selected zdialog_stuff(zd,pixx,""); zdialog_stuff(zd,redx,0); // blank pixel and zero values zdialog_stuff(zd,greenx,0); zdialog_stuff(zd,bluex,0); continue; } snprintf(text,100,"%s %4d %4d",lab1[ii],px,py); // format pixel "A nnnn nnnn" zdialog_stuff(zd,pixx,text); // pixel >> widget add_toptext(101,px,py,lab2[ii],"Sans 8"); // paint label on image at pixel red1 = zonal_colors_val1[ii][0]; // original RGB values for pixel green1 = zonal_colors_val1[ii][1]; blue1 = zonal_colors_val1[ii][2]; red3 = zonal_colors_val3[ii][0]; // revised RGB values green3 = zonal_colors_val3[ii][1]; blue3 = zonal_colors_val3[ii][2]; if (zonal_colors_metric == 2) { // convert to EV units if needed red1 = RGB2EV(red1); green1 = RGB2EV(green1); blue1 = RGB2EV(blue1); red3 = RGB2EV(red3); green3 = RGB2EV(green3); blue3 = RGB2EV(blue3); } if (zonal_colors_metric == 3) { // or OD units red1 = RGB2OD(red1); green1 = RGB2OD(green1); blue1 = RGB2OD(blue1); red3 = RGB2OD(red3); green3 = RGB2OD(green3); blue3 = RGB2OD(blue3); } if (zonal_colors_delta) { // dialog is delta mode red3 -= red1; green3 -= green1; blue3 -= blue1; } zdialog_stuff(zd,redx,red3); zdialog_stuff(zd,greenx,green3); zdialog_stuff(zd,bluex,blue3); } zdialog_stuff(zd,"blend",zonal_colors_blend); signal_thread(); return; } // thread function - multiple working threads to update image void * zonal_colors_thread(void *) { void * zonal_colors_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request zsleep(0.3); // more time for dialog controls if (zonal_colors_npix < 1) continue; // must have 1+ pixels in table for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(zonal_colors_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image modified CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop warning } void * zonal_colors_wthread(void *arg) // worker thread function { int index = *((int *) (arg)); int px1, py1, px2, py2, ii; float *pix1, *pix3; float dist[9], weight[9], sumdist; float blend, delta, red, green, blue, max; blend = E1pxm->ww; if (E1pxm->hh > E1pxm->ww) blend = E1pxm->hh; blend = blend * blend; blend = 0.0002 * zonal_colors_blend * zonal_colors_blend * blend + 100; // 100 to 2 * E1pxm->ww**2 for (ii = 0; ii < 9; ii++) { // stop misleading gcc warnings dist[ii] = 1; weight[ii] = 1; // avoid gcc warning } for (py1 = index; py1 < E3pxm->hh; py1 += NWT) // loop all image pixels for (px1 = 0; px1 < E3pxm->ww; px1++) { for (sumdist = ii = 0; ii < zonal_colors_npix; ii++) // compute weight of each revision point { px2 = zonal_colors_pixel[ii][0]; py2 = zonal_colors_pixel[ii][1]; dist[ii] = (px1-px2)*(px1-px2) + (py1-py2)*(py1-py2); // distance (px1,py1) to (px2,py2) dist[ii] = 1.0 / (dist[ii] + blend); // blend reduces peaks at revision points sumdist += dist[ii]; // sum inverse distances } for (ii = 0; ii < zonal_colors_npix; ii++) // weight of each point weight[ii] = dist[ii] / sumdist; pix1 = PXMpix(E1pxm,px1,py1); // input pixel pix3 = PXMpix(E3pxm,px1,py1); // output pixel red = pix1[0]; green = pix1[1]; blue = pix1[2]; for (ii = 0; ii < zonal_colors_npix; ii++) { // apply weighted color changes delta = zonal_colors_val3[ii][0] - zonal_colors_val1[ii][0]; // to each color red += weight[ii] * delta; delta = zonal_colors_val3[ii][1] - zonal_colors_val1[ii][1]; green += weight[ii] * delta; delta = zonal_colors_val3[ii][2] - zonal_colors_val1[ii][2]; blue += weight[ii] * delta; } max = red; if (green > max) max = green; if (blue > max) max = blue; if (max > 255.9) { // stop overflow/underflow red = red * 255.9 / max; green = green * 255.9 / max; blue = blue * 255.9 / max; } if (red < 0) red = 0; if (green < 0) green = 0; if (blue < 0) blue = 0; pix3[0] = red; pix3[1] = green; pix3[2] = blue; } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // match_color edit function // Adjust colors of image 2 to match the colors of image 1 // using small selected areas in each image as the match standard. void * match_color_thread(void *); void match_color_mousefunc(); float match_color_RGB1[3]; // image 1 base colors to match float match_color_RGB2[3]; // image 2 target colors to match int match_color_radius = 10; // mouse radius int match_color_mode = 0; editfunc EFmatchcolor; // menu function void m_match_color(GtkWidget *, const char *) { int match_color_dialog_event(zdialog* zd, const char *event); cchar *title = ZTX("Color Match Images"); F1_help_topic = "match_colors"; if (checkpend("all")) return; // check, no block: edit_setup() follows /* Color Match Images 1 [ 10 ] mouse radius for color sample 2 [Open] image for source color 3 click on image to get source color 4 [Open] image for target color 5 click on image to set target color [done] [cancel] */ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); // match_color dialog zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=2"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"label","labn1","vb1","1"); zdialog_add_widget(zd,"label","labn2","vb1","2"); zdialog_add_widget(zd,"label","labn3","vb1","3"); zdialog_add_widget(zd,"label","labn4","vb1","4"); zdialog_add_widget(zd,"label","labn5","vb1","5"); zdialog_add_widget(zd,"hbox","hbrad","vb2"); zdialog_add_widget(zd,"zspin","radius","hbrad","1|20|1|10","space=5"); zdialog_add_widget(zd,"label","labrad","hbrad",ZTX("mouse radius for color sample")); zdialog_add_widget(zd,"hbox","hbop1","vb2"); zdialog_add_widget(zd,"button","open1","hbop1",ZTX("Open"),"space=5"); zdialog_add_widget(zd,"label","labop1","hbop1",ZTX("image for source color")); zdialog_add_widget(zd,"hbox","hbclik1","vb2"); zdialog_add_widget(zd,"label","labclik1","hbclik1",ZTX("click on image to get source color")); zdialog_add_widget(zd,"hbox","hbop2","vb2"); zdialog_add_widget(zd,"button","open2","hbop2",ZTX("Open"),"space=5"); zdialog_add_widget(zd,"label","labop2","hbop2",ZTX("image to set matching color")); zdialog_add_widget(zd,"hbox","hbclik2","vb2"); zdialog_add_widget(zd,"label","labclik2","hbclik2",ZTX("click on image to set matching color")); zdialog_stuff(zd,"radius",match_color_radius); // remember last radius EFmatchcolor.funcname = "match_color"; EFmatchcolor.Farea = 1; // select area ignored EFmatchcolor.zd = zd; EFmatchcolor.threadfunc = match_color_thread; EFmatchcolor.mousefunc = match_color_mousefunc; match_color_mode = 0; if (curr_file) { match_color_mode = 1; // image 1 ready to click takeMouse(match_color_mousefunc,0); // connect mouse function } zdialog_run(zd,match_color_dialog_event,"parent"); // run dialog - parallel return; } // match_color dialog event and completion function int match_color_dialog_event(zdialog *zd, const char *event) // match_color dialog event function { int err; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (match_color_mode == 4) { // edit was started if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit match_color_mode = 0; return 1; } freeMouse(); // abandoned zdialog_free(zd); match_color_mode = 0; return 1; } if (strmatch(event,"radius")) // set new mouse radius zdialog_fetch(zd,"radius",match_color_radius); if (strmatch(event,"open1")) // get image 1 for color source { if (match_color_mode == 4) edit_cancel(1); // cancel edit, keep dialog match_color_mode = 0; err = f_open(null); // open image 1 if (! err) match_color_mode = 1; // image 1 ready to click } if (strmatch(event,"open2")) // get image 2 to set matching color { if (match_color_mode < 2) { zmessageACK(Mwin,ZTX("select source image color first")); // check that RGB1 has been set return 1; } match_color_mode = 2; err = f_open(null); // open image 2 if (err) return 1; match_color_mode = 3; // image 2 ready to click } takeMouse(match_color_mousefunc,0); // reconnect mouse function return 1; } // mouse function - click on image and get colors to match void match_color_mousefunc() { void match_color_getRGB(int px, int py, float rgb[3]); int px, py; if (match_color_mode < 1) return; // no image available yet draw_mousecircle(Mxposn,Myposn,match_color_radius,0,0); // draw circle around pointer if (LMclick) { LMclick = 0; px = Mxclick; py = Myclick; if (match_color_mode == 1 || match_color_mode == 2) // image 1 ready to click { match_color_getRGB(px,py,match_color_RGB1); // get RGB1 color match_color_mode = 2; return; } if (match_color_mode == 3 || match_color_mode == 4) // image 2 ready to click { if (match_color_mode == 4) edit_reset(); else { if (! edit_setup(EFmatchcolor)) return; // setup edit - thread will launch match_color_mode = 4; // edit waiting for cancel or done } match_color_getRGB(px,py,match_color_RGB2); // get RGB2 color signal_thread(); // update the target image return; } } return; } // get the RGB averages for pixels within mouse radius void match_color_getRGB(int px, int py, float rgb[3]) { int radflat1 = match_color_radius; int radflat2 = radflat1 * radflat1; int rad, npix, qx, qy; float red, green, blue; float *pix1; PXM *pxm; pxm = PXM_load(curr_file,1); // popup ACK if error if (! pxm) return; npix = 0; red = green = blue = 0; for (qy = py-radflat1; qy <= py+radflat1; qy++) for (qx = px-radflat1; qx <= px+radflat1; qx++) { if (qx < 0 || qx > pxm->ww-1) continue; if (qy < 0 || qy > pxm->hh-1) continue; rad = (qx-px) * (qx-px) + (qy-py) * (qy-py); if (rad > radflat2) continue; pix1 = PXMpix(pxm,qx,qy); red += pix1[0]; green += pix1[1]; blue += pix1[2]; npix++; } rgb[0] = red / npix; rgb[1] = green / npix; rgb[2] = blue / npix; PXM_free(pxm); return; } // thread function - start multiple working threads void * match_color_thread(void *) { void * match_color_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(match_color_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, avoid warning } void * match_color_wthread(void *arg) // worker thread function { int index = *((int *) (arg)); int px, py; float *pix3; float Rred, Rgreen, Rblue; float red, green, blue, cmax; Rred = match_color_RGB1[0] / match_color_RGB2[0]; // color adjustment ratios Rgreen = match_color_RGB1[1] / match_color_RGB2[1]; Rblue = match_color_RGB1[2] / match_color_RGB2[2]; for (py = index; py < E3pxm->hh; py += NWT) // loop all image pixels for (px = 0; px < E3pxm->ww; px++) { pix3 = PXMpix(E3pxm,px,py); red = pix3[0] * Rred; // adjust colors green = pix3[1] * Rgreen; blue = pix3[2] * Rblue; cmax = red; // check for overflow if (green > cmax) cmax = green; if (blue > cmax) cmax = blue; if (cmax > 255.9) { // fix overflow red = red * 255.9 / cmax; green = green * 255.9 / cmax; blue = blue * 255.9 / cmax; } pix3[0] = red; pix3[1] = green; pix3[2] = blue; } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Correct chromatic abberation (1st order only) by stretching // or shrinking a selected RGB color plane. editfunc EFchromatic; float chromaticRed, chromaticBlue; int chromaticFarea; // menu function void m_color_fringes(GtkWidget *, cchar *) { int chromatic_dialog_event(zdialog *zd, cchar *event); void * chromatic_thread(void *); cchar *chromatic_message = ZTX(" Adjust each RGB color to minimize \n" " color fringes at the image extremes. "); F1_help_topic = "color_fringes"; EFchromatic.menufunc = m_color_fringes; EFchromatic.funcname = "color_fringes"; EFchromatic.Farea = 2; // select area usable EFchromatic.threadfunc = chromatic_thread; if (! edit_setup(EFchromatic)) return; // setup edit /*** Chromatic Abberation Adjust each RGB color to minimize color fringes at the image extremes. Red =======[]=========== [-2.1] Blue ============[]====== [+2.3] [done] [cancel] ***/ zdialog *zd = zdialog_new(ZTX("Color Fringes"),Mwin,Bdone,Bcancel,null); EFchromatic.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",chromatic_message,"space=3"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=5|homog|expand"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"label","labred","vb1",Bred); zdialog_add_widget(zd,"label","labblue","vb1",Bblue); zdialog_add_widget(zd,"hscale","red","vb2","-5.0|5.0|0.1|0.0"); zdialog_add_widget(zd,"hscale","blue","vb2","-5.0|5.0|0.1|0.10"); zdialog_add_widget(zd,"label","redval","vb3"," 0.0"); zdialog_add_widget(zd,"label","blueval","vb3"," 0.0"); chromaticRed = chromaticBlue = 0; chromaticFarea = 0; zdialog_run(zd,chromatic_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int chromatic_dialog_event(zdialog *zd, cchar *event) { char text[8]; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // done if (chromaticFarea && sa_stat != 3) // extend to whole image if signal_thread(); // former select area now deleted edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"red")) { zdialog_fetch(zd,"red",chromaticRed); snprintf(text,8,"%+.1f",chromaticRed); zdialog_stuff(zd,"redval",text); signal_thread(); return 1; } if (strmatch(event,"blue")) { zdialog_fetch(zd,"blue",chromaticBlue); snprintf(text,8,"%+.1f",chromaticBlue); zdialog_stuff(zd,"blueval",text); signal_thread(); return 1; } return 1; } // chromatic image and accumulate chromatic memory void * chromatic_thread(void *) { void * chromatic_wthread(void *); // worker thread process while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(chromatic_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * chromatic_wthread(void *arg) // worker thread { int index = *((int *) (arg)); int px3, py3, ii; int xlo, xhi, ylo, yhi; float px1, py1; float vpix1[4], *pix3; float cx, cy, fx, fy; cx = E3pxm->ww / 2.0; cy = E3pxm->hh / 2.0; if (sa_stat == 3) { chromaticFarea = 1; // remember if area active xlo = sa_minx + index; // set area limits xhi = sa_maxx; ylo = sa_miny + index; yhi = sa_maxy; } else { chromaticFarea = 0; xlo = index; // set whole image limits xhi = E3pxm->ww; ylo = index; yhi = E3pxm->hh; } for (py3 = ylo; py3 < yhi; py3 += NWT) // loop all output pixels for (px3 = xlo; px3 < xhi; px3++) { if (chromaticFarea) { // area active ii = py3 * E3pxm->ww + px3; if (! sa_pixmap[ii]) continue; // pixel not in area } pix3 = PXMpix(E3pxm,px3,py3); // output pixel fx = (px3 - cx) / cx; // -1 to 0 to +1 fy = (py3 - cy) / cy; px1 = px3 + fx * chromaticRed; // red shift py1 = py3 + fy * chromaticRed; vpixel(E1pxm,px1,py1,vpix1); pix3[0] = vpix1[0]; px1 = px3 + fx * chromaticBlue; // blue shift py1 = py3 + fy * chromaticBlue; vpixel(E1pxm,px1,py1,vpix1); pix3[2] = vpix1[2]; } exit_wthread(); return 0; } /********************************************************************************/ // Smart Erase menu function - Replace pixels inside a select area // with a reflection of pixels outside the area. editfunc EFerase; // menu function void m_smart_erase(GtkWidget *, const char *) { int smart_erase_dialog_event(zdialog* zd, const char *event); int cc; cchar *erase_message = ZTX("1. Drag mouse to select. \n" "2. Erase. 3. Repeat. "); F1_help_topic = "smart_erase"; EFerase.menufunc = m_smart_erase; EFerase.funcname = "smart_erase"; EFerase.Farea = 0; // select area deleted EFerase.mousefunc = sa_mouse_select; // mouse function (use select area) if (! edit_setup(EFerase)) return; // setup edit /** _________________________________________ | | | 1. Drag mouse to select. | | 2. Erase. 3. Repeat. | | | | Radius [10|v] Blur [0.5|v] | | [New Area] [Show] [Hide] [Erase] [Undo] | | | | [Done] | |_________________________________________| **/ zdialog *zd = zdialog_new(ZTX("Smart Erase"),Mwin,Bdone,null); EFerase.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",erase_message,"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labr","hb2",ZTX("Radius"),"space=5"); zdialog_add_widget(zd,"zspin","radius","hb2","1|20|1|10"); zdialog_add_widget(zd,"label","labb","hb2",ZTX("Blur"),"space=10"); zdialog_add_widget(zd,"zspin","blur","hb2","0|9|0.5|0.5"); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=5"); zdialog_add_widget(zd,"button","newarea","hb3",ZTX("New Area"),"space=3"); zdialog_add_widget(zd,"button","show","hb3",ZTX(Bshow),"space=3"); zdialog_add_widget(zd,"button","hide","hb3",Bhide,"space=3"); zdialog_add_widget(zd,"button","erase","hb3",Berase,"space=3"); zdialog_add_widget(zd,"button","undo1","hb3",Bundo,"space=3"); sa_unselect(); // unselect area if any cc = E1pxm->ww * E1pxm->hh * sizeof(uint16); // create new area sa_pixmap = (uint16 *) zmalloc(cc); memset(sa_pixmap,0,cc); sa_mode = mode_mouse; // mode = select by mouse sa_stat = 1; // status = active edit sa_fww = E1pxm->ww; sa_fhh = E1pxm->hh; sa_searchrange = 1; // search within mouse radius sa_mouseradius = 10; // initial mouse select radius takeMouse(sa_mouse_select,0); // use select area mouse function sa_show(1,0); zdialog_run(zd,smart_erase_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion function int smart_erase_dialog_event(zdialog *zd, const char *event) // overhauled { void smart_erase_func(int mode); void smart_erase_blur(float radius); float radius; int cc; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { sa_unselect(); // delete select area freeMouse(); // disconnect mouse if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"radius")) zdialog_fetch(zd,"radius",sa_mouseradius); if (strmatch(event,"newarea")) { sa_unselect(); cc = E1pxm->ww * E1pxm->hh * sizeof(uint16); // create new area sa_pixmap = (uint16 *) zmalloc(cc); memset(sa_pixmap,0,cc); sa_mode = mode_mouse; // mode = select by mouse sa_stat = 1; // status = active edit sa_fww = E1pxm->ww; sa_fhh = E1pxm->hh; sa_show(1,0); takeMouse(sa_mouse_select,0); } if (strmatch(event,"show")) { sa_stat = 1; // status = active edit sa_show(1,0); takeMouse(sa_mouse_select,0); } if (strmatch(event,"hide")) { sa_show(0,0); freeMouse(); } if (strmatch(event,"erase")) { // do smart erase sa_finish_auto(); // finish the area smart_erase_func(1); zdialog_fetch(zd,"blur",radius); // add optional blur if (radius > 0) smart_erase_blur(radius); sa_show(0,0); } if (strmatch(event,"undo1")) // dialog undo, undo last erase smart_erase_func(2); return 1; } // erase the area or restore last erased area // mode = 1 = erase, mode = 2 = restore void smart_erase_func(int mode) { int px, py, npx, npy; int qx, qy, sx, sy, tx, ty; int ww, hh, sx2, sy2; int ii, rad, inc, cc; int dist2, mindist2; float slope; char *pmap; float *pix1, *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (sa_stat != 3) return; // nothing selected if (! sa_validate()) return; // area invalid for curr. image file ww = E1pxm->ww; hh = E1pxm->hh; for (py = sa_miny; py < sa_maxy; py++) // undo all pixels in area for (px = sa_minx; px < sa_maxx; px++) { ii = py * ww + px; if (! sa_pixmap[ii]) continue; // pixel not selected pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel memcpy(pix3,pix1,pcc); } Fpaint2(); // update window if (mode == 2) return; // mode = undo, done cc = ww * hh; // allocate pixel done map pmap = (char *) zmalloc(cc); memset(pmap,0,cc); for (py = sa_miny; py < sa_maxy; py++) // loop all pixels in area for (px = sa_minx; px < sa_maxx; px++) { ii = py * ww + px; if (! sa_pixmap[ii]) continue; // pixel not selected if (pmap[ii]) continue; // pixel already done mindist2 = 999999; // find nearest edge npx = npy = 0; for (rad = 1; rad < 50; rad++) // 50 pixel limit { for (qx = px-rad; qx <= px+rad; qx++) // search within rad for (qy = py-rad; qy <= py+rad; qy++) { if (qx < 0 || qx >= ww) continue; // off image edge if (qy < 0 || qy >= hh) continue; ii = qy * ww + qx; if (sa_pixmap[ii]) continue; // within selected area dist2 = (px-qx) * (px-qx) + (py-qy) * (py-qy); // distance**2 to edge pixel if (dist2 < mindist2) { mindist2 = dist2; npx = qx; // save nearest edge pixel found npy = qy; } } if (rad * rad >= mindist2) break; // can quit now } if (! npx && ! npy) continue; // edge not found, should not happen qx = npx; // nearest edge pixel from px/py qy = npy; if (abs(qy - py) > abs(qx - px)) { slope = 1.0 * (qx - px) / (qy - py); if (qy > py) inc = 1; else inc = -1; for (sy = py; sy != qy; sy += inc) // line from px/py to qx/qy { sx = px + slope * (sy - py); ii = sy * ww + sx; if (pmap[ii]) continue; pmap[ii] = 1; tx = qx + (qx - sx); // tx/ty = parallel line from qx/qy ty = qy + (qy - sy); if (tx < 0) tx = 0; if (tx > ww-1) tx = ww-1; if (ty < 0) ty = 0; if (ty > hh-1) ty = hh-1; sx2 = sx; // skip-over selected pixels sy2 = sy; while (tx > 0 && tx < ww && ty > 0 && ty < hh) { ii = ty * ww + tx; if (! sa_pixmap[ii]) break; sy2 += inc; sx2 = px + slope * (sy2 - py); tx = qx + (qx - sx2); ty = qy + (qy - sy2); } pix1 = PXMpix(E3pxm,tx,ty); // copy pixel from tx/ty to sx/sy pix3 = PXMpix(E3pxm,sx,sy); memcpy(pix3,pix1,pcc); } } else { slope = 1.0 * (qy - py) / (qx - px); if (qx > px) inc = 1; else inc = -1; for (sx = px; sx != qx; sx += inc) { sy = py + slope * (sx - px); ii = sy * ww + sx; if (pmap[ii]) continue; pmap[ii] = 1; tx = qx + (qx - sx); ty = qy + (qy - sy); if (tx < 0) tx = 0; if (tx > ww-1) tx = ww-1; if (ty < 0) ty = 0; if (ty > hh-1) ty = hh-1; sx2 = sx; // skip-over selected pixels sy2 = sy; while (tx > 0 && tx < ww && ty > 0 && ty < hh) { ii = ty * ww + tx; if (! sa_pixmap[ii]) break; sy2 += inc; sx2 = px + slope * (sy2 - py); tx = qx + (qx - sx2); ty = qy + (qy - sy2); } pix1 = PXMpix(E3pxm,tx,ty); pix3 = PXMpix(E3pxm,sx,sy); memcpy(pix3,pix1,pcc); } } } zfree(pmap); // free memory CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window return; } // add blur to the erased area to help mask the side-effects int smart_erase_blur(float radius) { int ii, px, py, dx, dy, adx, ady; float blur_weight[12][12]; // up to blur radius = 10 float rad, radflat2; float m, d, w, sum, weight; float red, green, blue; float *pix9, *pix3, *pixN; int nc = E3pxm->nc; if (sa_stat != 3) return 0; rad = radius - 0.2; radflat2 = rad * rad; for (dx = 0; dx < 12; dx++) // clear weights array for (dy = 0; dy < 12; dy++) blur_weight[dx][dy] = 0; for (dx = -rad-1; dx <= rad+1; dx++) // blur_weight[dx][dy] = no. of pixels for (dy = -rad-1; dy <= rad+1; dy++) // at distance (dx,dy) from center ++blur_weight[abs(dx)][abs(dy)]; m = sqrt(radflat2 + radflat2); // corner pixel distance from center sum = 0; for (dx = 0; dx <= rad+1; dx++) // compute weight of pixel for (dy = 0; dy <= rad+1; dy++) // at distance dx, dy { d = sqrt(dx*dx + dy*dy); w = (m + 1.2 - d) / m; w = w * w; sum += blur_weight[dx][dy] * w; blur_weight[dx][dy] = w; } for (dx = 0; dx <= rad+1; dx++) // make weights add up to 1.0 for (dy = 0; dy <= rad+1; dy++) blur_weight[dx][dy] = blur_weight[dx][dy] / sum; E9pxm = PXM_copy(E3pxm); // copy edited image for (py = sa_miny; py < sa_maxy; py++) // loop all pixels in area for (px = sa_minx; px < sa_maxx; px++) { ii = py * E1pxm->ww + px; if (! sa_pixmap[ii]) continue; // pixel not in area pix9 = PXMpix(E9pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel rad = radius; red = green = blue = 0; for (dy = -rad-1; dy <= rad+1; dy++) // loop neighbor pixels within radius for (dx = -rad-1; dx <= rad+1; dx++) { if (px+dx < 0 || px+dx >= E3pxm->ww) continue; // omit pixels off edge if (py+dy < 0 || py+dy >= E3pxm->hh) continue; adx = abs(dx); ady = abs(dy); pixN = pix9 + (dy * E3pxm->ww + dx) * nc; weight = blur_weight[adx][ady]; // weight at distance (dx,dy) red += pixN[0] * weight; // accumulate contributions green += pixN[1] * weight; blue += pixN[2] * weight; } pix3[0] = red; pix3[1] = green; pix3[2] = blue; } PXM_free(E9pxm); CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window return 0; } /********************************************************************************/ // add a brightness/color curved ramp to an image, in a chosen direction namespace bright_grad_names { editfunc EFbright_grad; int Fline, linex1, liney1, linex2, liney2; float A, B, C; float ex1, ey1, ex2, ey2; } // menu function void m_bright_ramp(GtkWidget *, cchar *menu) // 17.01 { using namespace bright_grad_names; void bright_grad_curvedit(int spc); int bright_grad_dialog_event(zdialog* zd, cchar *event); void * bright_grad_thread(void *); void bright_grad_mousefunc(); cchar *mess = ZTX("Draw a line across the image in \n" "direction of brightness change."); F1_help_topic = "bright_ramp"; EFbright_grad.menuname = menu; EFbright_grad.menufunc = m_bright_ramp; EFbright_grad.funcname = "bright_ramp"; EFbright_grad.FprevReq = 1; // use preview EFbright_grad.Farea = 2; // select area usable EFbright_grad.threadfunc = bright_grad_thread; EFbright_grad.mousefunc = bright_grad_mousefunc; // bugfix 18.01 if (! edit_setup(EFbright_grad)) return; // setup edit Fline = 0; // no drawn line initially /*** _______________________________________________ | Brightness Ramp | | | | Draw a line across the image in | | direction of brightness change. | | ___________________________________________ | | | | | // 5 curves are maintained: | | | | // curve 0: current display curve | | | | // 1: curve for all colors | | curve edit area | | // 2,3,4: red, green, blue | | | | | | | | | | | | | |___________________________________________| | | (o) all (o) red (o) green (o) blue | // select curve to display | | | [reset] [Done] [Cancel] | |_______________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Brightness Ramp"),Mwin,Breset,Bdone,Bcancel,null); EFbright_grad.zd = zd; zdialog_add_widget(zd,"label","labmess","dialog",mess); zdialog_add_widget(zd,"frame","frameH","dialog",0,"expand"); // edit-curves zdialog_add_widget(zd,"hbox","hbrgb","dialog"); // radio buttons all/red/green/blue zdialog_add_widget(zd,"radio","all","hbrgb",Ball,"space=5"); zdialog_add_widget(zd,"radio","red","hbrgb",Bred,"space=3"); zdialog_add_widget(zd,"radio","green","hbrgb",Bgreen,"space=3"); zdialog_add_widget(zd,"radio","blue","hbrgb",Bblue,"space=3"); GtkWidget *frameH = zdialog_widget(zd,"frameH"); // setup edit curves spldat *sd = splcurve_init(frameH,bright_grad_curvedit); EFbright_grad.curves = sd; sd->Nscale = 1; // horizontal fixed line, neutral curve sd->xscale[0][0] = 0.01; sd->yscale[0][0] = 0.50; sd->xscale[1][0] = 0.99; sd->yscale[1][0] = 0.50; for (int ii = 0; ii < 4; ii++) // loop curves 0-3 { sd->nap[ii] = 3; // initial curves are neutral sd->vert[ii] = 0; sd->fact[ii] = 0; sd->apx[ii][0] = 0.01; sd->apx[ii][1] = 0.50; // curve 0 = overall brightness sd->apx[ii][2] = 0.99; // curve 1/2/3 = R/G/B adjustment sd->apy[ii][0] = 0.5; sd->apy[ii][1] = 0.5; sd->apy[ii][2] = 0.5; splcurve_generate(sd,ii); } sd->Nspc = 4; // 4 curves sd->fact[0] = 1; // curve 0 active zdialog_stuff(zd,"all",1); // stuff default selection, all zdialog_resize(zd,300,250); zdialog_run(zd,bright_grad_dialog_event,"save"); // run dialog - parallel takeMouse(bright_grad_mousefunc,dragcursor); // connect mouse return; } // dialog event and completion callback function int bright_grad_dialog_event(zdialog *zd, cchar *event) { using namespace bright_grad_names; void bright_grad_mousefunc(); int ii, Fupdate = 0; spldat *sd = EFbright_grad.curves; if (strmatch(event,"done")) zd->zstat = 2; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (strmatch(event,"apply")) Fupdate++; // from script if (strmatch(event,"focus")) takeMouse(bright_grad_mousefunc,dragcursor); // connect mouse if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) // reset { for (int ii = 0; ii < 4; ii++) { // loop curves 0-3 sd->nap[ii] = 3; // all curves are neutral sd->vert[ii] = 0; sd->fact[ii] = 0; sd->apx[ii][0] = 0.01; sd->apx[ii][1] = 0.50; sd->apx[ii][2] = 0.99; sd->apy[ii][0] = 0.5; sd->apy[ii][1] = 0.5; sd->apy[ii][2] = 0.5; splcurve_generate(sd,ii); sd->fact[ii] = 0; } sd->fact[0] = 1; gtk_widget_queue_draw(sd->drawarea); // draw curve 0 zdialog_stuff(zd,"all",1); zdialog_stuff(zd,"red",0); zdialog_stuff(zd,"green",0); zdialog_stuff(zd,"blue",0); edit_reset(); // restore initial image zd->zstat = 0; return 1; } if (zd->zstat == 2) { // done edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit Ntoplines = 0; Fpaint2(); return 1; } if (strstr("all red green blue",event)) // new choice of curve { zdialog_fetch(zd,event,ii); if (! ii) return 0; // button OFF event, wait for ON event for (ii = 0; ii < 4; ii++) sd->fact[ii] = 0; ii = strmatchV(event,"all","red","green","blue",null); ii = ii-1; // new active curve: 0, 1, 2, 3 sd->fact[ii] = 1; splcurve_generate(sd,ii); // regenerate curve gtk_widget_queue_draw(sd->drawarea); // draw curve Fupdate = 1; } if (strmatch(event,"blendwidth")) Fupdate = 1; if (Fupdate) signal_thread(); // trigger image update return 1; } // bright_grad mouse function void bright_grad_mousefunc() { using namespace bright_grad_names; int mx, my; float d1, d2; if (! (LMclick || RMclick || Mxdrag || Mydrag)) return; // ignore mouse movement if (LMclick || RMclick) { // left or right mouse click mx = Mxclick; my = Myclick; LMclick = RMclick = 0; } if (Mxdrag || Mydrag) { // mouse drag mx = Mxdrag; my = Mydrag; Mxdrag = Mydrag = 0; } if (! Fline) { Fline = 1; linex1 = mx; // draw arbitrary line to start with liney1 = my; linex2 = mx + 100; liney2 = my + 100; } else // move nearest line end point to mouse { d1 = (linex1 - mx) * (linex1 - mx) + (liney1 - my) * (liney1 - my); d2 = (linex2 - mx) * (linex2 - mx) + (liney2 - my) * (liney2 - my); if (d1 < d2) { linex1 = mx; liney1 = my; } else { linex2 = mx; liney2 = my; } } Ntoplines = 1; // update line data toplines[0].x1 = linex1; toplines[0].y1 = liney1; toplines[0].x2 = linex2; toplines[0].y2 = liney2; toplines[0].type = 1; signal_thread(); // update image return; } // this function is called when a curve is edited void bright_grad_curvedit(int spc) { signal_thread(); return; } // bright_grad thread function void * bright_grad_thread(void *arg) { using namespace bright_grad_names; void bright_grad_equation(); void bright_grad_rampline(); void * bright_grad_wthread(void *); while (true) { thread_idle_loop(); // wait for work or exit request ex1 = linex1; // ramp line end points ey1 = liney1; ex2 = linex2; ey2 = liney2; bright_grad_equation(); // compute line equation bright_grad_rampline(); // compute new end points for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(bright_grad_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image3 modified CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * bright_grad_wthread(void *arg) // worker thread function { using namespace bright_grad_names; void bright_grad_posn(int px, int py, float &rx, float &ry); int ww = E3pxm->ww, hh = E3pxm->hh; int index = *((int *) arg); int ii, dist = 0, px3, py3; float x3, y3; float d1, d2, rampval; float *pix1, *pix3; float red1, green1, blue1, maxrgb; float red3, green3, blue3; float Fall, Fred, Fgreen, Fblue; float dold, dnew; spldat *sd = EFbright_grad.curves; for (py3 = index; py3 < hh; py3 += NWT) // loop output pixels for (px3 = 0; px3 < ww; px3++) { if (sa_stat == 3) { // select area active ii = py3 * ww + px3; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } bright_grad_posn(px3,py3,x3,y3); // nearest point on ramp line d1 = sqrtf((x3-ex1) * (x3-ex1) + (y3-ey1) * (y3-ey1)); // compute ramp value d2 = sqrtf((x3-ex2) * (x3-ex2) + (y3-ey2) * (y3-ey2)); rampval = d1 / (d1 + d2); // 0.0 ... 1.0 ii = 999.0 * rampval; // corresp. curve index 0-999 Fall = sd->yval[0][ii] * 2.0; // curve values 0.0 - 1.0 Fred = sd->yval[1][ii] * 2.0; // (0.5 is neutral value) Fgreen = sd->yval[2][ii] * 2.0; Fblue = sd->yval[3][ii] * 2.0; pix1 = PXMpix(E1pxm,px3,py3); // input pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; red3 = red1 * Fall; // curve "all" adjustment green3 = green1 * Fall; // projected on each RGB color blue3 = blue1 * Fall; red3 = red3 * Fred; // add additional RGB adjustments green3 = green3 * Fgreen; blue3 = blue3 * Fblue; maxrgb = red3; // stop overflows if (green3 > maxrgb) maxrgb = green3; if (blue3 > maxrgb) maxrgb = blue3; if (maxrgb > 255.9) { red3 = red3 * 255.9 / maxrgb; green3 = green3 * 255.9 / maxrgb; blue3 = blue3 * 255.9 / maxrgb; } if (sa_stat == 3 && dist < sa_blend) { // blend changes over blendwidth dnew = sa_blendfunc(dist); dold = 1.0 - dnew; red3 = dnew * red3 + dold * red1; green3 = dnew * green3 + dold * green1; blue3 = dnew * blue3 + dold * blue1; } pix3 = PXMpix(E3pxm,px3,py3); // output pixel pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } exit_wthread(); return 0; // not executed, avoid gcc warning } // get equation of ramp line in the form Ax + By + C = 0 // end points are (ex1,ey1) and (ex2,ey2) void bright_grad_equation() { using namespace bright_grad_names; if (ex1 != ex2) { A = (ey2 - ey1) / (ex2 - ex1); B = -1; C = ey1 - A * ex1; } else { A = 1; B = 0; C = -ex1; } return; } // compute nearest point on ramp line for given image pixel position void bright_grad_posn(int px, int py, float &rx, float &ry) { using namespace bright_grad_names; float F1, F2; F1 = B * px - A * py; F2 = A * A + B * B; rx = (B * F1 - A * C) / F2; ry = (-A * F1 - B * C) / F2; return; } // extend ramp line end points long enough for entire image void bright_grad_rampline() { using namespace bright_grad_names; void bright_grad_posn(int px, int py, float &rx, float &ry); int ww, hh; float rx, ry, d1, d2; ww = E3pxm->ww; hh = E3pxm->hh; if (B == 0) { // vertical line ey1 = 0; ey2 = hh - 1; return; } if (A == 0) { // horizontal line ex1 = 0; ex2 = ww - 1; return; } bright_grad_posn(0,0,rx,ry); if (rx < 0 || ry < 0) { d1 = (rx - ex1) * (rx - ex1) + (ry - ey1) * (ry - ey1); d2 = (rx - ex2) * (rx - ex2) + (ry - ey2) * (ry - ey2); if (d1 < d2) { ex1 = rx; ey1 = ry; } else { ex2 = rx; ey2 = ry; } } bright_grad_posn(ww,0,rx,ry); if (rx > ww || ry < 0) { d1 = (rx - ex1) * (rx - ex1) + (ry - ey1) * (ry - ey1); d2 = (rx - ex2) * (rx - ex2) + (ry - ey2) * (ry - ey2); if (d1 < d2) { ex1 = rx; ey1 = ry; } else { ex2 = rx; ey2 = ry; } } bright_grad_posn(ww,hh,rx,ry); if (rx > ww || ry > hh) { d1 = (rx - ex1) * (rx - ex1) + (ry - ey1) * (ry - ey1); d2 = (rx - ex2) * (rx - ex2) + (ry - ey2) * (ry - ey2); if (d1 < d2) { ex1 = rx; ey1 = ry; } else { ex2 = rx; ey2 = ry; } } bright_grad_posn(0,hh,rx,ry); if (rx < 0 || ry > hh) { d1 = (rx - ex1) * (rx - ex1) + (ry - ey1) * (ry - ey1); d2 = (rx - ex2) * (rx - ex2) + (ry - ey2) * (ry - ey2); if (d1 < d2) { ex1 = rx; ey1 = ry; } else { ex2 = rx; ey2 = ry; } } return; } /********************************************************************************/ // find and remove "dust" from an image (e.g. from a scanned dusty slide) // dust is defined as small dark areas surrounded by brighter areas // image 1 original with prior edits // image 3 accumulated dust removals that have been comitted // image 9 comitted dust removals + pending removal (work in process) namespace dust_names { editfunc EFdust; int spotspan; // max. dustspot span, pixels int spotspan2; // spotspan **2 float brightness; // brightness limit, 0 to 1 = white float contrast; // min. contrast, 0 to 1 = black/white int *pixgroup; // maps (px,py) to pixel group no. int Fred; // red pixels are on int Nstack; struct spixstack { uint16 px, py; // pixel group search stack uint16 direc; } *pixstack; #define maxgroups 1000000 int Ngroups; int groupcount[maxgroups]; // count of pixels in each group float groupbright[maxgroups]; int edgecount[maxgroups]; // group edge pixel count float edgebright[maxgroups]; // group edge pixel brightness sum typedef struct { uint16 px1, py1, px2, py2; // pixel group extreme pixels int span2; // span from px1/py1 to px2/py2 } sgroupspan; sgroupspan groupspan[maxgroups]; } // menu function void m_remove_dust(GtkWidget *, const char *) { using namespace dust_names; int dust_dialog_event(zdialog *zd, cchar *event); void * dust_thread(void *); F1_help_topic = "remove_dust"; EFdust.menufunc = m_remove_dust; EFdust.funcname = "remove_dust"; EFdust.Farea = 2; // select area usable EFdust.Frestart = 1; // restart allowed EFdust.threadfunc = dust_thread; // thread function if (! edit_setup(EFdust)) return; // setup edit E9pxm = PXM_copy(E3pxm); // image 9 = copy of image3 Fred = 0; int cc = E1pxm->ww * E1pxm->hh * sizeof(int); pixgroup = (int *) zmalloc(cc); // maps pixels to assigned groups cc = E1pxm->ww * E1pxm->hh * sizeof(spixstack); pixstack = (spixstack *) zmalloc(cc); // pixel group search stack /*** Remove Dust spot size limit =========[]=========== max. brightness =============[]======= min. contrast ========[]============ [erase] [red] [undo last] [apply] [Done] [Cancel] ***/ zdialog *zd = zdialog_new(ZTX("Remove Dust"),Mwin,Bdone,Bcancel,null); EFdust.zd = zd; zdialog_add_widget(zd,"hbox","hbssl","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labssl","hbssl",ZTX("spot size limit"),"space=5"); zdialog_add_widget(zd,"hscale","spotspan","hbssl","1|50|1|20","space=5|expand"); zdialog_add_widget(zd,"hbox","hbmb","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labmb","hbmb",ZTX("max. brightness"),"space=5"); zdialog_add_widget(zd,"hscale","brightness","hbmb","1|999|1|700","space=5|expand"); zdialog_add_widget(zd,"hbox","hbmc","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labmb","hbmc",ZTX("min. contrast"),"space=5"); zdialog_add_widget(zd,"hscale","contrast","hbmc","1|500|1|40","space=5|expand"); zdialog_add_widget(zd,"hbox","hbbutts","dialog",0,"space=5"); zdialog_add_widget(zd,"button","erase","hbbutts",Berase,"space=5"); zdialog_add_widget(zd,"button","red","hbbutts",Bred,"space=5"); zdialog_add_widget(zd,"button","undo1","hbbutts",Bundolast,"space=5"); zdialog_add_widget(zd,"button","apply","hbbutts",Bapply,"space=5"); zdialog_resize(zd,300,0); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_fetch(zd,"spotspan",spotspan); // max. dustspot span (pixels) spotspan2 = spotspan * spotspan; zdialog_fetch(zd,"brightness",brightness); // max. dustspot brightness brightness = 0.001 * brightness; // scale 0 to 1 = white zdialog_fetch(zd,"contrast",contrast); // min. dustspot contrast contrast = 0.001 * contrast; // scale 0 to 1 = black/white zdialog_run(zd,dust_dialog_event,"save"); // run dialog - parallel signal_thread(); return; } // dialog event and completion callback function int dust_dialog_event(zdialog *zd, cchar *event) { using namespace dust_names; void dust_erase(); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // done wrapup_thread(8); // thread finish, exit PXM_free(E3pxm); E3pxm = E9pxm; // image 3 = image 9 E9pxm = 0; edit_done(0); // commit edit } else { // cancel PXM_free(E9pxm); edit_cancel(0); // discard edit } zfree(pixgroup); // free memory zfree(pixstack); return 1; } if (strstr("spotspan brightness contrast red",event)) { zdialog_fetch(zd,"spotspan",spotspan); // max. dustspot span (pixels) spotspan2 = spotspan * spotspan; zdialog_fetch(zd,"brightness",brightness); // max. dustspot brightness brightness = 0.001 * brightness; // scale 0 to 1 = white zdialog_fetch(zd,"contrast",contrast); // min. dustspot contrast contrast = 0.001 * contrast; // scale 0 to 1 = black/white signal_thread(); // do the work } if (strmatch(event,"erase")) dust_erase(); if (strmatch(event,"blendwidth")) dust_erase(); if (strmatch(event,"undo1")) { PXM_free(E3pxm); E3pxm = PXM_copy(E9pxm); Fred = 0; Fpaint2(); } if (strmatch(event,"apply")) { // button if (Fred) dust_erase(); PXM_free(E9pxm); // image 9 = copy of image 3 E9pxm = PXM_copy(E3pxm); CEF->Fmods++; CEF->Fsaved = 0; } return 1; } // dust find thread function - find the dust particles and mark them void * dust_thread(void *) { using namespace dust_names; int xspan, yspan, span2; int group, cc, ii, kk, Nremoved; int px, py, dx, dy, ppx, ppy, npx, npy; float gbright, pbright, pcontrast; float ff = 1.0 / 256.0; uint16 direc; float *pix3; while (true) { thread_idle_loop(); // wait for work or exit request paintlock(1); // block window updates PXM_free(E3pxm); E3pxm = PXM_copy(E9pxm); paintlock(0); // unblock window updates cc = E1pxm->ww * E1pxm->hh * sizeof(int); // clear group arrays memset(pixgroup,0,cc); cc = maxgroups * sizeof(int); memset(groupcount,0,cc); memset(edgecount,0,cc); cc = maxgroups * sizeof(float ); memset(groupbright,0,cc); memset(edgebright,0,cc); cc = maxgroups * sizeof(sgroupspan); memset(groupspan,0,cc); group = 0; for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; if (sa_stat == 3 && ! sa_pixmap[ii]) continue; // not in active area if (pixgroup[ii]) continue; // already assigned to a group pix3 = PXMpix(E3pxm,px,py); // get pixel brightness gbright = ff * pixbright(pix3); // 0 to 1.0 = white if (gbright > brightness) continue; // ignore bright pixel if (group == maxgroups-1) break; // too many groups, make no more pixgroup[ii] = ++group; // assign next group groupcount[group] = 1; groupbright[group] = gbright; pixstack[0].px = px; // put pixel into stack with pixstack[0].py = py; // direction = ahead pixstack[0].direc = 0; Nstack = 1; while (Nstack) { kk = Nstack - 1; // get last pixel in stack px = pixstack[kk].px; py = pixstack[kk].py; direc = pixstack[kk].direc; // next search direction if (direc == 'x') { Nstack--; // none left continue; } if (Nstack > 1) { ii = Nstack - 2; // get prior pixel in stack ppx = pixstack[ii].px; ppy = pixstack[ii].py; } else { ppx = px - 1; // if only one, assume prior = left ppy = py; } dx = px - ppx; // vector from prior to this pixel dy = py - ppy; switch (direc) { case 0: npx = px + dx; npy = py + dy; pixstack[kk].direc = 1; break; case 1: npx = px + dy; npy = py + dx; pixstack[kk].direc = 3; break; case 2: npx = px - dx; // back to prior pixel npy = py - dy; // (this path never taken) zappcrash("stack search bug"); break; case 3: npx = px - dy; npy = py - dx; pixstack[kk].direc = 4; break; case 4: npx = px - dx; npy = py + dy; pixstack[kk].direc = 5; break; case 5: npx = px - dy; npy = py + dx; pixstack[kk].direc = 6; break; case 6: npx = px + dx; npy = py - dy; pixstack[kk].direc = 7; break; case 7: npx = px + dy; npy = py - dx; pixstack[kk].direc = 'x'; break; default: npx = npy = 0; zappcrash("stack search bug"); } if (npx < 0 || npx > E1pxm->ww-1) continue; // pixel off the edge if (npy < 0 || npy > E1pxm->hh-1) continue; ii = npy * E1pxm->ww + npx; if (pixgroup[ii]) continue; // pixel already assigned if (sa_stat == 3 && ! sa_pixmap[ii]) continue; // pixel outside area pix3 = PXMpix(E3pxm,npx,npy); // pixel brightness pbright = ff * pixbright(pix3); if (pbright > brightness) continue; // brighter than limit pixgroup[ii] = group; // assign pixel to group ++groupcount[group]; // count pixels in group groupbright[group] += pbright; // sum brightness for group kk = Nstack++; // put pixel into stack pixstack[kk].px = npx; pixstack[kk].py = npy; pixstack[kk].direc = 0; // search direction } } Ngroups = group; // group numbers are 1-Ngroups Nremoved = 0; for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; group = pixgroup[ii]; if (! group) continue; if (groupspan[group].px1 == 0) { // first pixel found in this group groupspan[group].px1 = px; // group px1/py1 = this pixel groupspan[group].py1 = py; continue; } xspan = groupspan[group].px1 - px; // span from group px1/py1 to this pixel yspan = groupspan[group].py1 - py; span2 = xspan * xspan + yspan * yspan; if (span2 > groupspan[group].span2) { groupspan[group].span2 = span2; // if greater, group px2/py2 = this pixel groupspan[group].px2 = px; groupspan[group].py2 = py; } } for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; group = pixgroup[ii]; if (! group) continue; if (groupspan[group].span2 > spotspan2) continue; xspan = groupspan[group].px2 - px; // span from this pixel to group px2/py2 yspan = groupspan[group].py2 - py; span2 = xspan * xspan + yspan * yspan; if (span2 > groupspan[group].span2) { groupspan[group].span2 = span2; // if greater, group px1/py1 = this pixel groupspan[group].px1 = px; groupspan[group].py1 = py; } } for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; // eliminate group if span > limit group = pixgroup[ii]; if (! group) continue; if (! groupcount[group]) pixgroup[ii] = 0; else if (groupspan[group].span2 > spotspan2) { pixgroup[ii] = 0; groupcount[group] = 0; Nremoved++; } } for (py = 1; py < E1pxm->hh-1; py++) // loop all pixels except image edges for (px = 1; px < E1pxm->ww-1; px++) { ii = py * E1pxm->ww + px; group = pixgroup[ii]; if (group) continue; // find pixels bordering group pixels pix3 = PXMpix(E3pxm,px,py); pbright = ff * pixbright(pix3); group = pixgroup[ii-E1pxm->ww-1]; if (group) { ++edgecount[group]; // accumulate pixel count and edgebright[group] += pbright; // bordering the groups } group = pixgroup[ii-E1pxm->ww]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii-E1pxm->ww+1]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii-1]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii+1]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii+E1pxm->ww-1]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii+E1pxm->ww]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } group = pixgroup[ii+E1pxm->ww+1]; if (group) { ++edgecount[group]; edgebright[group] += pbright; } } for (group = 1; group <= Ngroups; group++) // compute group pixel and edge pixel { // mean brightness if (groupcount[group] && edgecount[group]) { edgebright[group] = edgebright[group] / edgecount[group]; groupbright[group] = groupbright[group] / groupcount[group]; pcontrast = edgebright[group] - groupbright[group]; // edge - group contrast if (pcontrast < contrast) { groupcount[group] = 0; Nremoved++; } } } for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; // eliminate group if low contrast group = pixgroup[ii]; if (! group) continue; if (! groupcount[group]) pixgroup[ii] = 0; } for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; if (! pixgroup[ii]) continue; // not a dust pixel pix3 = PXMpix(E3pxm,px,py); // paint it red pix3[0] = 255; pix3[1] = pix3[2] = 0; } Fred = 1; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } // erase the selected dust areas void dust_erase() { using namespace dust_names; int cc, ii, px, py, inc; int qx, qy, npx, npy; int sx, sy, tx, ty; int rad, dist, dist2, mindist2; float slope, f1, f2; float *pix1, *pix3; char *pmap; int nc = E1pxm->nc, pcc = nc * sizeof(float); Ffuncbusy = 1; PXM_free(E3pxm); E3pxm = PXM_copy(E9pxm); cc = E1pxm->ww * E1pxm->hh; // allocate pixel done map pmap = (char *) zmalloc(cc); memset(pmap,0,cc); for (py = 0; py < E1pxm->hh; py++) // loop all pixels for (px = 0; px < E1pxm->ww; px++) { ii = py * E1pxm->ww + px; if (! pixgroup[ii]) continue; // not a dust pixel if (pmap[ii]) continue; // skip pixels already done mindist2 = 999999; npx = npy = 0; for (rad = 1; rad < 10; rad++) // find nearest edge (10 pixel limit) { for (qx = px-rad; qx <= px+rad; qx++) // search within rad for (qy = py-rad; qy <= py+rad; qy++) { if (qx < 0 || qx >= E1pxm->ww) continue; // off image edge if (qy < 0 || qy >= E1pxm->hh) continue; ii = qy * E1pxm->ww + qx; if (pixgroup[ii]) continue; // within dust area dist2 = (px-qx) * (px-qx) + (py-qy) * (py-qy); // distance**2 to edge pixel if (dist2 < mindist2) { mindist2 = dist2; npx = qx; // save nearest pixel found npy = qy; } } if (rad * rad >= mindist2) break; // can quit now } if (! npx && ! npy) continue; // should not happen qx = npx; // nearest edge pixel qy = npy; if (abs(qy - py) > abs(qx - px)) { // qx/qy = near edge from px/py slope = 1.0 * (qx - px) / (qy - py); if (qy > py) inc = 1; else inc = -1; for (sy = py; sy != qy+inc; sy += inc) // line from px/py to qx/qy { sx = px + slope * (sy - py); ii = sy * E1pxm->ww + sx; if (pmap[ii]) continue; pmap[ii] = 1; tx = qx + (qx - sx); // tx/ty = parallel line from qx/qy ty = qy + (qy - sy); if (tx < 0) tx = 0; if (tx > E1pxm->ww-1) tx = E1pxm->ww-1; if (ty < 0) ty = 0; if (ty > E1pxm->hh-1) ty = E1pxm->hh-1; pix1 = PXMpix(E3pxm,tx,ty); // copy pixel from tx/ty to sx/sy pix3 = PXMpix(E3pxm,sx,sy); memcpy(pix3,pix1,pcc); } } else { slope = 1.0 * (qy - py) / (qx - px); if (qx > px) inc = 1; else inc = -1; for (sx = px; sx != qx+inc; sx += inc) { sy = py + slope * (sx - px); ii = sy * E1pxm->ww + sx; if (pmap[ii]) continue; pmap[ii] = 1; tx = qx + (qx - sx); ty = qy + (qy - sy); if (tx < 0) tx = 0; if (tx > E1pxm->ww-1) tx = E1pxm->ww-1; if (ty < 0) ty = 0; if (ty > E1pxm->hh-1) ty = E1pxm->hh-1; pix1 = PXMpix(E3pxm,tx,ty); pix3 = PXMpix(E3pxm,sx,sy); memcpy(pix3,pix1,pcc); } } } zfree(pmap); if (sa_stat == 3) // area edge blending { for (ii = 0; ii < E1pxm->ww * E1pxm->hh; ii++) // find pixels in select area { dist = sa_pixmap[ii]; if (! dist || dist >= sa_blend) continue; py = ii / E1pxm->ww; px = ii - py * E1pxm->ww; pix1 = PXMpix(E1pxm,px,py); // input pixel, unchanged image pix3 = PXMpix(E3pxm,px,py); // output pixel, changed image f2 = sa_blendfunc(dist); f1 = 1.0 - f2; pix3[0] = f1 * pix1[0] + f2 * pix3[0]; // blend the pixels pix3[1] = f1 * pix1[1] + f2 * pix3[1]; pix3[2] = f1 * pix1[2] + f2 * pix3[2]; } } Fred = 0; Ffuncbusy = 0; Fpaint2(); // update window return; } /********************************************************************************/ // anti-alias menu function editfunc EFantialias; // edit function data // menu function void m_anti_alias(GtkWidget *, const char *) { int antialias_dialog_event(zdialog* zd, const char *event); zdialog *zd; F1_help_topic = "anti_alias"; EFantialias.menufunc = m_anti_alias; EFantialias.funcname = "anti_alias"; // function name EFantialias.Farea = 2; // select area usable if (! edit_setup(EFantialias)) return; // setup edit zd = zdialog_new("Anti-Alias",Mwin,Bapply,Bcancel,null); // setup dialog EFantialias.zd = zd; zdialog_resize(zd,200,0); zdialog_run(zd,antialias_dialog_event,"save"); // run dialog - parallel return; } // antialias dialog event and completion function int antialias_dialog_event(zdialog *zd, const char *event) // antialias dialog event function { void antialias_func(); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // done antialias_func(); // apply edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } return 1; } // compare two pixels // 1.0 = perfect match, 0.0 = perfect mismatch (black/white) inline float antialias_match(float *pix1, float *pix2) { return PIXMATCH(pix1,pix2); // 0..1 = zero..perfect match } // antialias function /*** Algorithm is like scale2x but adapted for photos instead of computer generated pixel art. _________________ | | | | | A | B | C | |_____|_____|_____| _____________ | | | | | | | | D | E | F | | E0 | E1 | |_____|_____|_____| |______|______| | | | | | | | | G | H | I | | E2 | E3 | |_____|_____|_____| |______|______| 1. make output image = 2x input image dimensions 2. loop for each pixel E in input image: 3. for corresponding pixels E0 E1 E2 E3 in output image: 4. if D:B match more than E:D and E:B E0 = 0.333 * (D + B + E) else E0 = E 5. same for E1, E2, E3 6. add some annealing and blur ***/ void antialias_func() { int ww, hh, ww2, hh2; int px, py, px2, py2, qx, qy; int ii, dist, rgb; float *pixB, *pixD, *pixE, *pixF, *pixH; float *pixE0, *pixE1, *pixE2, *pixE3; float matchDB, matchBF, matchDH, matchHF; float matchEB, matchED, matchEF, matchEH; float red, green, blue, *pixel; float blurf1, blurf2; paintlock(1); // block window updates ww = E1pxm->ww; hh = E1pxm->hh; ww2 = 2 * ww; // create 2x output image hh2 = 2 * hh; E9pxm = PXM_rescale(E1pxm,ww2,hh2); // calculate each output pixel group E0 E1 E2 E3 // from input pixel E and its 8 neighbors for (py = 1; py < hh-1; py++) // loop all (inside) input pixels for (px = 1; px < ww-1; px++) { if (sa_stat == 3) { // select area active ii = py * E1pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pixB = PXMpix(E1pxm,px ,py-1); pixD = PXMpix(E1pxm,px-1,py); pixE = PXMpix(E1pxm,px, py); pixF = PXMpix(E1pxm,px+1,py); pixH = PXMpix(E1pxm,px ,py+1); px2 = px * 2; // 2x2 pixel block in output image py2 = py * 2; // corresponding to (px,py) pixE0 = PXMpix(E9pxm,px2,py2); pixE1 = PXMpix(E9pxm,px2+1,py2); pixE2 = PXMpix(E9pxm,px2,py2+1); pixE3 = PXMpix(E9pxm,px2+1,py2+1); matchDB = antialias_match(pixD,pixB); matchBF = antialias_match(pixB,pixF); matchDH = antialias_match(pixD,pixH); matchHF = antialias_match(pixH,pixF); matchEB = antialias_match(pixE,pixB); matchED = antialias_match(pixE,pixD); matchEF = antialias_match(pixE,pixF); matchEH = antialias_match(pixE,pixH); for (rgb = 0; rgb < 3; rgb++) // fill 2x2 pixel block { if (matchDB > matchED && matchDB > matchEB) pixE0[rgb] = 0.25 * (pixD[rgb] + pixB[rgb]) + 0.5 * pixE[rgb]; else pixE0[rgb] = pixE[rgb]; if (matchBF > matchEB && matchBF > matchEF) pixE1[rgb] = 0.25 * (pixB[rgb] + pixF[rgb]) + 0.5 * pixE[rgb]; else pixE1[rgb] = pixE[rgb]; if (matchDH > matchED && matchDH > matchEH) pixE2[rgb] = 0.25 * (pixD[rgb] + pixH[rgb]) + 0.5 * pixE[rgb]; else pixE2[rgb] = pixE[rgb]; if (matchHF > matchEH && matchHF > matchEF) pixE3[rgb] = 0.25 * (pixH[rgb] + pixF[rgb]) + 0.5 * pixE[rgb]; else pixE3[rgb] = pixE[rgb]; } } // apply a little bit of blur to each output pixel blurf1 = 0.6; // contribution of original pixel blurf2 = (1.0 - blurf1) / 9.0; // contribution of surrounding pixels for (py = 1; py < hh2-1; py++) for (px = 1; px < ww2-1; px++) { if (sa_stat == 3) { // select area active ii = py/2 * E1pxm->ww + px/2; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } red = green = blue = 0; for (qy = py-1; qy <= py+1; qy++) for (qx = px-1; qx <= px+1; qx++) { pixel = PXMpix(E9pxm,qx,qy); red += pixel[0]; green += pixel[1]; blue += pixel[2]; } pixel = PXMpix(E9pxm,px,py); pixel[0] = blurf1 * pixel[0] + blurf2 * red; pixel[1] = blurf1 * pixel[1] + blurf2 * green; pixel[2] = blurf1 * pixel[2] + blurf2 * blue; } PXM_free(E3pxm); // E3 = E9 resized 1/2 E3pxm = PXM_rescale(E9pxm,ww,hh); E9pxm = 0; paintlock(0); // unblock window updates if (sa_stat) sa_unselect(); // area is no longer valid CEF->Fmods = 1; // image modified CEF->Fsaved = 0; Fpaint2(); // update window return; } /********************************************************************************/ // Find and fix stuck pixels (always bright or dark) from camera sensor defects. namespace stuckpix_names { editfunc EFstuckpix; int stuckpix_1x1, stuckpix_2x2, stuckpix_3x3; // pixel blocks to search cchar *stuckpix_mode; // find or fix float stuckpix_threshcon; // min. contrast threshold char *stuckpix_file = 0; // file for saved stuck pixels struct stuckpix_t { // memory for stuck pixels int px, py; // location (NW corner of block) int size; // size: 1/2/3 = 1x1/2x2/3x3 block float pcon; // contrast with surrounding pixels int rgb[3]; // surrounding pixel mean RGB }; stuckpix_t stuckpix[1000]; int maxstuck = 1000, Nstuck; } // menu function void m_stuck_pixels(GtkWidget *, cchar *menu) { using namespace stuckpix_names; int stuckpix_dialog_event(zdialog *zd, cchar *event); void * stuckpix_thread(void *); zdialog *zd; F1_help_topic = "stuck_pixels"; EFstuckpix.menufunc = m_stuck_pixels; EFstuckpix.funcname = "stuck_pixels"; // function name EFstuckpix.threadfunc = stuckpix_thread; // thread function if (! edit_setup(EFstuckpix)) return; // setup edit /*** ________________________________________________ | Fix Stuck Pixels | | | | pixel group [x] 1x1 [x] 2x2 [x] 3x3 | | contrast =========[]================== | | stuck pixels 12 | | | | [open] [save] [apply] [done] [cancel] | |________________________________________________| ***/ zd = zdialog_new(ZTX("Stuck Pixels"),Mwin,Bopen,Bsave,Bapply,Bdone,Bcancel,null); EFstuckpix.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=5|expand|homog"); zdialog_add_widget(zd,"label","labgroup","vb1",ZTX("pixel group")); zdialog_add_widget(zd,"label","labcont","vb1",Bcontrast); zdialog_add_widget(zd,"hbox","hbgroup","vb2"); zdialog_add_widget(zd,"check","1x1","hbgroup","1x1","space=3"); zdialog_add_widget(zd,"check","2x2","hbgroup","2x2","space=3"); zdialog_add_widget(zd,"check","3x3","hbgroup","3x3","space=3"); zdialog_add_widget(zd,"hscale","contrast","vb2","0.1|1.0|0.001|0.5","expand"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labstuck","hb2",ZTX("stuck pixels:"),"space=5"); zdialog_stuff(zd,"1x1",1); // initz. dialog controls zdialog_stuff(zd,"2x2",1); zdialog_stuff(zd,"3x3",1); stuckpix_1x1 = stuckpix_2x2 = stuckpix_3x3 = 1; // corresp. data values stuckpix_threshcon = 0.5; if (! stuckpix_file) { // default file stuckpix_file = (char *) zmalloc(200); // /.../.fotoxx/stuck-pixels snprintf(stuckpix_file,200,"%s/stuck-pixels",get_zhomedir()); } zdialog_run(zd,stuckpix_dialog_event,"save"); // run dialog zd_thread = zd; // setup for thread event stuckpix_mode = "find"; signal_thread(); // find and show the stuck pixels return; } // dialog event function int stuckpix_dialog_event(zdialog *zd, cchar *event) { using namespace stuckpix_names; void stuckpix_open(); void stuckpix_save(); void stuckpix_show(); char stuck_pixels[50]; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // open defects file zd->zstat = 0; // keep dialog active stuckpix_open(); } else if (zd->zstat == 2) { // save defects file zd->zstat = 0; // keep dialog active stuckpix_save(); } else if (zd->zstat == 3) { // apply 18.01 zd->zstat = 0; // keep dialog active stuckpix_mode = "apply"; signal_thread(); // fix the stuck pixels erase_topcircles(); } else if (zd->zstat == 4) { // done zd_thread = 0; stuckpix_mode = "apply"; signal_thread(); // fix the stuck pixels edit_done(0); // commit edit erase_topcircles(); } else { // cancel zd_thread = 0; edit_cancel(0); // discard edit erase_topcircles(); } return 1; } if (strmatch(event,"stuck pixels")) { // update count in dialog snprintf(stuck_pixels,49,"%s %d",ZTX("stuck pixels:"),Nstuck); if (Nstuck >= maxstuck) strcat(stuck_pixels,"+"); zdialog_stuff(zd,"labstuck",stuck_pixels); } if (strstr("1x1 2x2 3x3 contrast",event)) { zdialog_fetch(zd,"1x1",stuckpix_1x1); // get dialog inputs zdialog_fetch(zd,"2x2",stuckpix_2x2); zdialog_fetch(zd,"3x3",stuckpix_3x3); zdialog_fetch(zd,"contrast",stuckpix_threshcon); stuckpix_mode = "find"; signal_thread(); // find and show stuck pixels } return 1; } // load stuck pixel list from a previously saved file void stuckpix_open() { using namespace stuckpix_names; int stuckpix_open_dialog_event(zdialog *zd, cchar *event); zdialog *zd; zd = zdialog_new(ZTX("Load Stuck Pixels"),Mwin,Bopen,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",ZTX("File:"),"space=3"); zdialog_add_widget(zd,"zentry","file","hb1",0,"expand|size=40"); zdialog_add_widget(zd,"button","browse","hb1",Bbrowse,"space=5"); zdialog_stuff(zd,"file",stuckpix_file); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,stuckpix_open_dialog_event,"save"); zdialog_wait(zd); zdialog_free(zd); return; } int stuckpix_open_dialog_event(zdialog *zd, cchar *event) { using namespace stuckpix_names; char *pp, file[200]; FILE *fid = 0; int zstat, nn, ii, px, py, size; if (strmatch(event,"browse")) { pp = zgetfile(ZTX("Stuck Pixels file"),MWIN,"file",stuckpix_file); if (! pp) return 1; zdialog_stuff(zd,"file",pp); zfree(pp); } zstat = zd->zstat; // completion button if (zstat == 1) // open { zdialog_fetch(zd,"file",file,200); // get file from dialog if (stuckpix_file) zfree(stuckpix_file); stuckpix_file = zstrdup(file); fid = fopen(stuckpix_file,"r"); // open file if (! fid) { zmessageACK(Mwin,Bfilenotfound); return 1; } nn = fscanf(fid,"stuck pixels px py size"); // read headers for (ii = 0; ii < maxstuck; ii++) { nn = fscanf(fid," %5d %5d %5d ",&px,&py,&size); // read stuck pixels data if (nn == EOF) break; if (nn != 3) break; stuckpix[ii].px = px; stuckpix[ii].py = py; stuckpix[ii].size = size; stuckpix[ii].pcon = 0; } Nstuck = ii; fclose(fid); if (! Nstuck || nn != EOF) zmessageACK(Mwin,ZTX("file format error")); stuckpix_mode = "file"; // process pixels signal_thread(); } return 1; } // save stuck pixel list to a file or add them to a previous file void stuckpix_save() // simplified { using namespace stuckpix_names; char *file; FILE *fid = 0; int ii, px, py, size; file = zgetfile(ZTX("Stuck Pixels file"),MWIN,"save",stuckpix_file); if (! file) return; if (stuckpix_file) zfree(stuckpix_file); stuckpix_file = file; fid = fopen(stuckpix_file,"w"); // open file if (! fid) { zmessageACK(Mwin,strerror(errno)); return; } fprintf(fid,"stuck pixels \n"); // write headers fprintf(fid," px py size \n"); for (ii = 0; ii < Nstuck; ii++) // write stuck pixel data { px = stuckpix[ii].px; py = stuckpix[ii].py; size = stuckpix[ii].size; fprintf(fid," %5d %5d %5d \n",px,py,size); } fclose(fid); return; } // compare two pixels and return contrast // 0 = no contrast, 1 = max. contrast (black:white) inline float stuckpix_getcon(float rgbA[3], float rgbB[3]) { float match; match = PIXMATCH(rgbA,rgbB); // 0..1 = zero..perfect match return (1.0 - match); } // perform the fix function // find pixel groups with contrast exceeding the limit // replace these pixels with surrounding ones void * stuckpix_thread(void *) { using namespace stuckpix_names; void stuckpix_pixelblock(int px, int py, int size, float rgb[3]); void stuckpix_surroundings(int px, int py, int size, float rgb[3]); void stuckpix_insert(int px, int py, int size, float pcon, float rgb[3]); void stuckpix_show(); int px, py, qx, qy, ii, size; float rgbA[3], rgbB[3]; float threshcon, pcon; float *ppix; while (true) { thread_idle_loop(); if (strmatch(stuckpix_mode,"find")) // find stuck pixels in image { threshcon = stuckpix_threshcon; // threshold contrast, 0.1 to 1.0 Nstuck = 0; // count stuck pixels found if (stuckpix_3x3) // find 3x3 pixel groups FIRST { for (py = 2; py < E3pxm->hh-5; py++) for (px = 2; px < E3pxm->ww-5; px++) { stuckpix_pixelblock(px,py,3,rgbA); // get mean RGB for 3x3 pixel block stuckpix_surroundings(px,py,3,rgbB); // get surrounding pixels mean RGB pcon = stuckpix_getcon(rgbA,rgbB); // contrast with surrounding if (pcon > threshcon) stuckpix_insert(px,py,3,pcon,rgbB); // if > threshold, add to table if (Nstuck == maxstuck) goto findquit; } } if (stuckpix_2x2) // find 2x2 pixel groups { for (py = 2; py < E3pxm->hh-4; py++) for (px = 2; px < E3pxm->ww-4; px++) { stuckpix_pixelblock(px,py,2,rgbA); // get mean RGB for 2x2 pixel block stuckpix_surroundings(px,py,2,rgbB); // get surrounding pixels mean RGB pcon = stuckpix_getcon(rgbA,rgbB); // contrast with surrounding if (pcon > threshcon) stuckpix_insert(px,py,2,pcon,rgbB); // if > threshold, add to table if (Nstuck == maxstuck) goto findquit; } } if (stuckpix_1x1) // find 1x1 pixel groups LAST { for (py = 2; py < E3pxm->hh-3; py++) for (px = 2; px < E3pxm->ww-3; px++) { stuckpix_pixelblock(px,py,1,rgbA); // get mean RGB for 1x1 pixel block stuckpix_surroundings(px,py,1,rgbB); // get surrounding pixels mean RGB pcon = stuckpix_getcon(rgbA,rgbB); // contrast with surrounding if (pcon > threshcon) stuckpix_insert(px,py,1,pcon,rgbB); // if > threshold, add to table if (Nstuck == maxstuck) goto findquit; } } findquit: stuckpix_show(); // show the stuck pixels found } if (strmatch(stuckpix_mode,"file")) // process stuck pixels read from a file { for (ii = 0; ii < Nstuck; ii++) { px = stuckpix[ii].px; py = stuckpix[ii].py; size = stuckpix[ii].size; stuckpix_pixelblock(px,py,size,rgbA); stuckpix_surroundings(px,py,size,rgbB); pcon = stuckpix_getcon(rgbA,rgbB); stuckpix[ii].pcon = pcon; stuckpix[ii].rgb[0] = rgbB[0]; stuckpix[ii].rgb[1] = rgbB[1]; stuckpix[ii].rgb[2] = rgbB[2]; } stuckpix_show(); // show the stuck pixels read } if (strmatch(stuckpix_mode,"apply")) // replace the stuck pixels { for (ii = 0; ii < Nstuck; ii++) // loop pixel groups found { px = stuckpix[ii].px; py = stuckpix[ii].py; size = stuckpix[ii].size; if (! size) continue; if (size == 1) // 1x1 pixel group { ppix = PXMpix(E3pxm,px,py); // replace pixel group ppix[0] = stuckpix[ii].rgb[0]; ppix[1] = stuckpix[ii].rgb[1]; ppix[2] = stuckpix[ii].rgb[2]; } if (size == 2) // 2x2 pixel group { for (qy = py; qy < py+2; qy++) // replace pixel group for (qx = px; qx < px+2; qx++) { ppix = PXMpix(E3pxm,qx,qy); ppix[0] = stuckpix[ii].rgb[0]; ppix[1] = stuckpix[ii].rgb[1]; ppix[2] = stuckpix[ii].rgb[2]; } } if (size == 3) // 3x3 pixel group { for (qy = py; qy < py+3; qy++) // replace pixel group for (qx = px; qx < px+3; qx++) { ppix = PXMpix(E3pxm,qx,qy); ppix[0] = stuckpix[ii].rgb[0]; ppix[1] = stuckpix[ii].rgb[1]; ppix[2] = stuckpix[ii].rgb[2]; } } } CEF->Fmods++; // image modified CEF->Fsaved = 0; Fpaint2(); // update window } } return 0; } // get the mean RGB values for a block of (defective) pixels void stuckpix_pixelblock(int px, int py, int size, float rgb[3]) { float *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9; if (size == 1) // 1x1 block, 1 pixel { pix1 = PXMpix(E3pxm,px,py); rgb[0] = pix1[0]; rgb[1] = pix1[1]; rgb[2] = pix1[2]; } if (size == 2) // 2x2 block, 4 pixels { pix1 = PXMpix(E3pxm,px,py); pix2 = PXMpix(E3pxm,px+1,py); pix3 = PXMpix(E3pxm,px,py+1); pix4 = PXMpix(E3pxm,px+1,py+1); rgb[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) / 4; rgb[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) / 4; rgb[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) / 4; } if (size == 3) // 3x3 block, 9 pixels { pix1 = PXMpix(E3pxm,px,py); pix2 = PXMpix(E3pxm,px+1,py); pix3 = PXMpix(E3pxm,px+2,py); pix4 = PXMpix(E3pxm,px,py+1); pix5 = PXMpix(E3pxm,px+1,py+1); pix6 = PXMpix(E3pxm,px+2,py+1); pix7 = PXMpix(E3pxm,px,py+2); pix8 = PXMpix(E3pxm,px+1,py+2); pix9 = PXMpix(E3pxm,px+2,py+2); rgb[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0] + pix5[0] + pix6[0] + pix7[0] + pix8[0] + pix9[0]) / 9; rgb[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1] + pix5[1] + pix1[1] + pix7[1] + pix8[1] + pix9[1]) / 9; rgb[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2] + pix5[2] + pix6[2] + pix7[2] + pix8[2] + pix9[2]) / 9; } return; } // get the mean RGB values for pixels surrounding a given pixel block void stuckpix_surroundings(int px, int py, int size, float rgb[3]) { int qx, qy, ii; float red, green, blue; float *ppix; int n8x[8] = { -1, 0, 1,-1, 1,-1, 0, 1 }; // 8 neighbors of 1x1 group at [0,0] int n8y[8] = { -1,-1,-1, 0, 0, 1, 1, 1 }; int n12x[12] = { -1, 0, 1, 2,-1, 2,-1, 2,-1, 0, 1, 2 }; // 12 neighbors of 2x2 group at [0,0] int n12y[12] = { -1,-1,-1,-1, 0, 0, 1, 1, 2, 2, 2, 2 }; int n16x[16] = { -1, 0, 1, 2, 3,-1, 3,-1, 3,-1, 3,-1, 0, 1, 2, 3 }; // 16 neighbors of 3x3 group at [0,0] int n16y[16] = { -1,-1,-1,-1,-1, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3 }; red = green = blue = 0; if (size == 1) { for (ii = 0; ii < 8; ii++) // surrounding 8 pixels { qx = px + n8x[ii]; qy = py + n8y[ii]; ppix = PXMpix(E3pxm,qx,qy); red += ppix[0]; green += ppix[1]; blue += ppix[2]; } red = red / 8; // average surrounding pixels green = green / 8; blue = blue / 8; } if (size == 2) { for (ii = 0; ii < 12; ii++) // surrounding 12 pixels { qx = px + n12x[ii]; qy = py + n12y[ii]; ppix = PXMpix(E3pxm,qx,qy); red += ppix[0]; green += ppix[1]; blue += ppix[2]; } red = red / 12; // average surrounding pixels green = green / 12; blue = blue / 12; } if (size == 3) { for (ii = 0; ii < 16; ii++) // surrounding 16 pixels { qx = px + n16x[ii]; qy = py + n16y[ii]; ppix = PXMpix(E3pxm,qx,qy); red += ppix[0]; green += ppix[1]; blue += ppix[2]; } red = red / 16; // average surrounding pixels green = green / 16; blue = blue / 16; } rgb[0] = red; rgb[1] = green; rgb[2] = blue; return; } // draw circles around the stuck pixels void stuckpix_show() { using namespace stuckpix_names; int ii, px, py, size, rad; erase_topcircles(); // erase prior circles for (ii = 0; ii < Nstuck; ii++) // write circles around stuck pixels { px = stuckpix[ii].px; py = stuckpix[ii].py; size = stuckpix[ii].size; if (! size) continue; rad = 8 + 2 * size; px += size / 2; py += size / 2; add_topcircle(px,py,rad); } zd_thread = EFstuckpix.zd; // send stuck pixel count to zdialog zd_thread_event = "stuck pixels"; Fpaint2(); // update window return; } // Insert a new stuck pixel block into the stuck pixel table. // If the new entry touches on a previous entry, // then remove the entry with lesser contrast. void stuckpix_insert(int px, int py, int size, float pcon, float rgb[3]) { using namespace stuckpix_names; int ii, px1, py1, px2, py2, px3, py3, px4, py4; px1 = px - 1; // "touch" periphery py1 = py - 1; px2 = px + size + 1; py2 = py + size + 1; for (ii = 0; ii < Nstuck; ii++) // loop stuckpix table { if (stuckpix[ii].size == 0) continue; // skip deleted entry px3 = stuckpix[ii].px; py3 = stuckpix[ii].py; px4 = px3 + stuckpix[ii].size - 1; py4 = py3 + stuckpix[ii].size - 1; if ((px1 >= px3 && px1 <= px4 && py1 >= py3 && py1 <= py4) || // test if old entry touches new (px2 >= px3 && px2 <= px4 && py2 >= py3 && py2 <= py4) || (px1 >= px3 && px1 <= px4 && py2 >= py3 && py2 <= py4) || (px2 >= px3 && px2 <= px4 && py1 >= py3 && py1 <= py4) || (px3 >= px1 && px3 <= px2 && py3 >= py1 && py3 <= py2) || (px4 >= px1 && px4 <= px2 && py4 >= py1 && py4 <= py2) || (px3 >= px1 && px3 <= px2 && py4 >= py1 && py4 <= py2) || (px4 >= px1 && px4 <= px2 && py3 >= py1 && py3 <= py2)) { if (size < stuckpix[ii].size && // if new touches a larger block, pcon < 2.0 * stuckpix[ii].pcon) return; // keep only if contrast much greater if (pcon < 1.0 * stuckpix[ii].pcon) return; // keep only if contrast is greater stuckpix[ii].size = 0; // delete old entry } } for (ii = 0; ii < Nstuck; ii++) // loop stuckpix table if (stuckpix[ii].size == 0) break; // find first empty slot or last + 1 if (ii == maxstuck) return; // table full stuckpix[ii].px = px; // replace overlapping entry stuckpix[ii].py = py; // or add to end of table stuckpix[ii].size = size; stuckpix[ii].pcon = pcon; stuckpix[ii].rgb[0] = rgb[0]; stuckpix[ii].rgb[1] = rgb[1]; stuckpix[ii].rgb[2] = rgb[2]; if (ii == Nstuck) Nstuck++; // incr. count if added to end return; } /********************************************************************************/ // Paint transparency function - paint pixel alpha channel with the mouse. namespace ptransp_names { int pt_dialog_event(zdialog* zd, cchar *event); void pt_mousefunc(); void pt_dopixels(int px, int py); int mode = 1; // 1/2 = more/less transparency int Fgrad = 1; // gradual or instant transparency int Mradius; // mouse radius float kernel[400][400]; // radius <= 199 editfunc EFpaintransp; } // menu function void m_paint_transp(GtkWidget *, cchar *) { using namespace ptransp_names; cchar *mess1 = ZTX("left drag: add transparency \n" "right drag: add opacity"); F1_help_topic = "paint_transp"; EFpaintransp.menufunc = m_paint_transp; EFpaintransp.funcname = "paint_transp"; EFpaintransp.Farea = 2; // select area OK EFpaintransp.mousefunc = pt_mousefunc; // mouse function if (! edit_setup(EFpaintransp)) return; // setup edit PXM_addalpha(E1pxm); // add an alpha channel if req. PXM_addalpha(E3pxm); Fpaintnow(); /*** __________________________________ | Paint transparency | | | | left drag: add transparency | | right drag: add opacity | | | | paintbrush radius [____] | | strength center [____] | | strength edge [____] | | [x] gradual paint | | | | [done] [cancel] | |__________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Paint Transparency"),Mwin,Bdone,Bcancel,null); EFpaintransp.zd = zd; zdialog_add_widget(zd,"hbox","hbr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labm","dialog",mess1,"space=5"); zdialog_add_widget(zd,"hbox","hbbri","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vbbr1","hbbri",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbbr2","hbbri",0,"homog|space=5"); zdialog_add_widget(zd,"label","labbr","vbbr1",ZTX("paintbrush radius")); zdialog_add_widget(zd,"label","labsc","vbbr1",ZTX("strength center")); zdialog_add_widget(zd,"label","labse","vbbr1",ZTX("strength edge")); zdialog_add_widget(zd,"zspin","radius","vbbr2","1|199|1|30"); zdialog_add_widget(zd,"zspin","stcent","vbbr2","0|100|1|95"); zdialog_add_widget(zd,"zspin","stedge","vbbr2","0|100|1|100"); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=3"); zdialog_add_widget(zd,"check","Fgrad","hb4",ZTX("gradual paint"),"space=5"); zdialog_stuff(zd,"Fgrad",1); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,pt_dialog_event,"save"); // run dialog, parallel zdialog_send_event(zd,"radius"); // get kernel initialized zdialog_fetch(zd,"Fgrad",Fgrad); // instant/gradual paint mode = 1; // start with paint mode takeMouse(pt_mousefunc,drawcursor); // connect mouse function return; } // dialog event and completion callback function int ptransp_names::pt_dialog_event(zdialog *zd, cchar *event) { using namespace ptransp_names; int radius, dx, dy; float rad, kern, stcent, stedge; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(pt_mousefunc,drawcursor); if (strstr("radius stcent stedge",event)) // get new brush attributes { zdialog_fetch(zd,"radius",Mradius); // mouse radius zdialog_fetch(zd,"stcent",stcent); // center transparency zdialog_fetch(zd,"stedge",stedge); // edge transparency stcent = 0.01 * stcent; // scale 0 ... 1 stedge = 0.01 * stedge; radius = Mradius; for (dy = -radius; dy <= radius; dy++) // build kernel for (dx = -radius; dx <= radius; dx++) { rad = sqrt(dx*dx + dy*dy); kern = (radius - rad) / radius; // center ... edge >> 1 ... 0 kern = kern * (stcent - stedge) + stedge; // strength center ... edge if (kern < 0) kern = 0; if (kern > 1) kern = 1; if (rad > radius) kern = 2; // beyond radius, within square kernel[dx+radius][dy+radius] = kern; } } if (strmatch(event,"Fgrad")) // flag, gradual overpaints zdialog_fetch(zd,"Fgrad",Fgrad); return 1; } // pixel paint mouse function void ptransp_names::pt_mousefunc() { using namespace ptransp_names; static int pmxdown = 0, pmydown = 0; int px, py; if (LMclick || RMclick) { if (LMclick) mode = 1; // left click, paint if (RMclick) mode = 2; // right click, erase px = Mxclick; py = Myclick; pt_dopixels(px,py); // do 1 block of pixels } else if (Mxdrag || Mydrag) // drag in progress { if (Mbutton == 1) mode = 1; // left drag, paint if (Mbutton == 3) mode = 2; // right drag, erase px = Mxdrag; py = Mydrag; if (Mxdown != pmxdown || Mydown != pmydown) { // new drag pmxdown = Mxdown; pmydown = Mydown; } pt_dopixels(px,py); // do 1 block of pixels LMclick = RMclick = Mxdrag = Mydrag = 0; return; } LMclick = RMclick = Mxdrag = Mydrag = 0; draw_mousecircle(Mxposn,Myposn,Mradius,0,0); // draw mouse circle return; } // paint or erase 1 block of pixels within mouse radius of px, py void ptransp_names::pt_dopixels(int px, int py) { using namespace ptransp_names; float *pix3; int radius, dx, dy, qx, qy; int ii, ww, hh, dist = 0; float kern; cairo_t *cr = draw_context_create(gdkwin,draw_context); // 17.04 ww = E3pxm->ww; hh = E3pxm->hh; radius = Mradius; for (dy = -radius; dy <= radius; dy++) // loop surrounding block of pixels for (dx = -radius; dx <= radius; dx++) { qx = px + dx; qy = py + dy; if (qx < 0 || qx > ww-1) continue; if (qy < 0 || qy > hh-1) continue; if (sa_stat == 3) { // select area active ii = qy * ww + qx; dist = sa_pixmap[ii]; if (! dist) continue; // pixel is outside area } kern = kernel[dx+radius][dy+radius]; // mouse transparencies if (kern > 1) continue; // outside mouse radius if (sa_stat == 3 && dist < sa_blend) // within blend distance, kern = kern * sa_blendfunc(dist); // kern = kern ... 0 at edge pix3 = PXMpix(E3pxm,qx,qy); // edited image pixel if (mode == 1) { // add transparency if (Fgrad) { pix3[3] -= 2 * kern; // accumulate transparency if (pix3[3] < 0) pix3[3] = 0; } else pix3[3] = 0; // instant } if (mode == 2) { // add opacity if (Fgrad) { pix3[3] += 2 * kern; // accumulate if (pix3[3] > 255.0) pix3[3] = 255.0; } else pix3[3] = 255.0; // instant } } CEF->Fmods++; CEF->Fsaved = 0; px = px - radius - 1; // repaint modified area py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww,cr); draw_mousecircle(Mxposn,Myposn,Mradius,0,cr); // draw mouse circle draw_context_destroy(draw_context); // 17.04 return; } /********************************************************************************/ // Add transparency function // Add transparency proportional to brightness or match with selected color. namespace add_transp_names { int dialog_event(zdialog* zd, cchar *event); void mousefunc(); void *thread(void *); void *wthread(void *); int ww, hh; int Hu, Su, Lu; // "match using" flags, 0 or 1 float Rm, Gm, Bm; // RGB image color to match float Hm, Sm, Lm; // corresp. HSL color int Fbrightness, Fcolor, Finvert; float matchlevel, transparency, threshold; float Rmatch, Gmatch, Bmatch; editfunc EFaddtransp; } void HSLtoRGB(float H, float S, float L, float &R, float &G, float &B); void RGBtoHSL(float R, float G, float B, float &H, float &S, float &L); // menu function void m_add_transp(GtkWidget *, cchar *) { using namespace add_transp_names; F1_help_topic = "add_transparency"; EFaddtransp.menufunc = m_add_transp; EFaddtransp.funcname = "add_transp"; EFaddtransp.Farea = 2; // select area OK EFaddtransp.mousefunc = mousefunc; EFaddtransp.threadfunc = thread; if (! edit_setup(EFaddtransp)) return; // setup edit PXM_addalpha(E1pxm); // add an alpha channel if req. PXM_addalpha(E3pxm); Fpaintnow(); /*** ___________________________________________________ | Add Transparency | | | | [x] Match Brightness | | | | [x] Match Color [######] | | Match using: [] Hue [] Saturation [] Lightness | | Match Level: =================[]========== 100% | | | | [x] Invert Match | | Transparency =================[]============== | transparency level 0-100% | Threshold ===========[]======================= | transparency threshold | | | [done] [cancel] | |___________________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Add Transparency"),Mwin,Bdone,Bcancel,null); EFaddtransp.zd = zd; zdialog_add_widget(zd,"hbox","hbmb","dialog"); zdialog_add_widget(zd,"check","Fbrightness","hbmb",ZTX("Match Brightness"),"space=3"); zdialog_add_widget(zd,"hsep","hsbc","dialog"); zdialog_add_widget(zd,"hbox","hbmc","dialog"); zdialog_add_widget(zd,"check","Fcolor","hbmc",ZTX("Match Color"),"space=3"); zdialog_add_widget(zd,"colorbutt","matchRGB","hbmc","0|0|0","space=3"); zdialog_add_ttip(zd,"matchRGB",ZTX("click on image to select color")); zdialog_add_widget(zd,"hbox","hbmu","dialog"); zdialog_add_widget(zd,"label","labmu","hbmu",ZTX("Match using:"),"space=5"); zdialog_add_widget(zd,"check","Hu","hbmu","Hue","space=3"); zdialog_add_widget(zd,"check","Su","hbmu","Saturation","space=3"); zdialog_add_widget(zd,"check","Lu","hbmu","Lightness","space=3"); zdialog_add_widget(zd,"hbox","hbml","dialog"); zdialog_add_widget(zd,"label","labml","hbml",Bmatchlevel,"space=5"); zdialog_add_widget(zd,"hscale","matchlevel","hbml","0|1|0.001|1.0","expand"); zdialog_add_widget(zd,"label","lab100%","hbml","100%","space=4"); zdialog_add_widget(zd,"hsep","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbinv","dialog"); zdialog_add_widget(zd,"check","Finvert","hbinv",ZTX("Invert Match"),"space=3"); zdialog_add_widget(zd,"hbox","hbtran","dialog"); zdialog_add_widget(zd,"label","labint","hbtran",ZTX("Transparency"),"space=5"); zdialog_add_widget(zd,"hscale","transparency","hbtran","0.0|1.0|0.001|0.0","space=5|expand"); zdialog_add_widget(zd,"hbox","hbthresh","dialog"); zdialog_add_widget(zd,"label","labthr","hbthresh",Bthresh,"space=5"); zdialog_add_widget(zd,"hscale","threshold","hbthresh","0.0|1.0|0.001|0.0","space=5|expand"); zdialog_stuff(zd,"Fbrightness",1); // defaults zdialog_stuff(zd,"Fcolor",0); zdialog_stuff(zd,"Finvert",0); zdialog_stuff(zd,"Hu",1); zdialog_stuff(zd,"Su",1); zdialog_stuff(zd,"Lu",1); Fbrightness = 1; Fcolor = Finvert = 0; matchlevel = 1.0; transparency = threshold = 0.0; Rm = Gm = Bm = 0; // color to match = black = not set Hm = Sm = Lm = 0; Hu = Su = Lu = 1; ww = E3pxm->ww; hh = E3pxm->hh; zdialog_run(zd,dialog_event,"save"); // run dialog, parallel takeMouse(mousefunc,arrowcursor); return; } // dialog event and completion callback function int add_transp_names::dialog_event(zdialog *zd, cchar *event) { using namespace add_transp_names; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // [done] else edit_cancel(0); // [cancel] or [x] return 1; } if (strmatch(event,"Fbrightness")) { zdialog_fetch(zd,"Fbrightness",Fbrightness); Fcolor = 1 - Fbrightness; zdialog_stuff(zd,"Fcolor",Fcolor); } if (strmatch(event,"Fcolor")) { zdialog_fetch(zd,"Fcolor",Fcolor); Fbrightness = 1 - Fcolor; zdialog_stuff(zd,"Fbrightness",Fbrightness); } zdialog_fetch(zd,"Fbrightness",Fbrightness); // get all dialog controls zdialog_fetch(zd,"Fcolor",Fcolor); zdialog_fetch(zd,"Finvert",Finvert); zdialog_fetch(zd,"matchlevel",matchlevel); zdialog_fetch(zd,"transparency",transparency); zdialog_fetch(zd,"threshold",threshold); zdialog_fetch(zd,"Hu",Hu); zdialog_fetch(zd,"Su",Su); zdialog_fetch(zd,"Lu",Lu); signal_thread(); // set pixel transparencies return 1; } // mouse function // click on image to set the color to match void add_transp_names::mousefunc() { using namespace add_transp_names; char mcolor[20]; float *pix1; float f256 = 1.0 / 256.0; zdialog *zd = EFaddtransp.zd; if (LMclick) // left mouse click { LMclick = 0; pix1 = PXMpix(E1pxm,Mxclick,Myclick); // pick match color from image Rm = pix1[0]; Gm = pix1[1]; Bm = pix1[2]; snprintf(mcolor,19,"%.0f|%.0f|%.0f",Rm,Gm,Bm); zdialog_stuff(zd,"matchRGB",mcolor); Rm *= f256; Gm *= f256; Bm *= f256; RGBtoHSL(Rm,Gm,Bm,Hm,Sm,Lm); // HSL color to match zdialog_stuff(zd,"Fcolor",1); zdialog_stuff(zd,"Fbrightness",0); Fbrightness = 0; Fcolor = 1; signal_thread(); } return; } // thread function - set pixel transparencies from user inputs void * add_transp_names::thread(void *) { using namespace add_transp_names; while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image modified CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop warning } void * add_transp_names::wthread(void *arg) // worker thread function { using namespace add_transp_names; int index = *((int *) (arg)); int ii, px, py, dist = 0; float f1, f2, alfa; float pixbrite, pixmatch; float *pix1, *pix3; float red, green, blue; float R1, G1, B1; float H1, S1, L1, dH, dS, dL; float f256 = 1.0 / 256.0; for (py = index; py < hh; py += NWT) for (px = 0; px < ww; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; if (! dist) continue; // pixel is outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red = pix1[0]; green = pix1[1]; blue = pix1[2]; pixmatch = 0; if (Fbrightness) { // match pixel to brightness pixbrite = red; if (green > pixbrite) pixbrite = green; if (blue > pixbrite) pixbrite = blue; pixmatch = 0.003906 * pixbrite; // 0 .. 1 for brightness 0 .. 256 pixmatch = pixmatch * pixmatch; // increase sensitivity pixmatch = pixmatch * pixmatch; } else // match pixel to HSL color { R1 = f256 * red; G1 = f256 * green; B1 = f256 * blue; RGBtoHSL(R1,G1,B1,H1,S1,L1); // convert to HSL pixmatch = 1.0; // compare pixel to match HSL if (Hu) { dH = fabsf(Hm - H1); if (360 - dH < dH) dH = 360 - dH; dH *= 0.002778; // H difference, normalized 0..1 pixmatch = 1.0 - dH; } if (Su) { dS = fabsf(Sm - S1); // S difference, 0..1 pixmatch *= (1.0 - dS); } if (Lu) { dL = fabsf(Lm - L1); // L difference, 0..1 pixmatch *= (1.0 - dL); } pixmatch = pixmatch * pixmatch; // increase sensitivity pixmatch = pixmatch * pixmatch; } if (Finvert) pixmatch = 1.0 - pixmatch; // invert match if required f1 = matchlevel; // reduce for lower match level f2 = 1.0 - f1; pixmatch = f1 * pixmatch + f2; pixmatch = pixmatch * transparency; // reduce for lower transparency if (pixmatch < threshold) pixmatch = 0; // eliminate below threshold alfa = 255.0 - 255.0 * pixmatch; // 255 .. 0 for pixmatch 0 .. 1 if (sa_stat == 3 && dist < sa_blend) { // within area blend distance f1 = sa_blendfunc(dist); // reduce transparency f2 = 1.0 - f1; alfa = f1 * alfa + f2 * 255.0; } pix3[3] = alfa; } exit_wthread(); return 0; // not executed, avoid gcc warning } fotoxx-18.01.1/f.edit.cc0000644000175000017500000142666513222767271013366 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit - Edit menu functions m_trim_rotate trim/crop and rotate combination m_upright upright a rotated image m_resize resize (rescale) image m_voodoo1 automatic image enhancement with limited smarts m_voodoo2 automatic image enhancement with limited smarts m_retouch_combo adjust brightness, contrast, color, white balance m_edit_britedist flatten and/or expand brightness distribution m_gradients magnify brightness gradients to enhance details m_flatten flatten brightness distribution m_retinex rescale pixel RGB brightness range m_mirror horizontal or vertical mirror image m_paint_image paint on the image with a color color_palette select color from private color palette file HSL_chooser select color from HSL chooser dialog m_clone_image paint image with pixels from elsewhere on the image m_blend_image blend image pixels via mouse painting m_add_text add text to an image gentext create text graphic with attributes and transparency m_add_lines add lines or arrows to an image genline create line/arrow graphic with attributes and transparency m_paint_edits paint edit function gradually with the mouse m_lever_edits apply edit function leveraged by RGB level or contrast m_plugins plugins menu function m_edit_plugins add/revise/delete plugin menu functions m_run_plugin run a plugin menu command and update image *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // trim (crop) and/or rotate an image // combine trim and rotate // preview mode and related code removed // fotoxx.h // 17.04 // int trimx1, trimy1, trimx2, trimy2; // trim rectangle limits // int trimww, trimhh; // trim rectangle width and height namespace trimrotate { int ptrimx1, ptrimy1, ptrimx2, ptrimy2; // prior trim rectangle (this image) int ptrimww, ptrimhh; // prior trim size (previous image) float trimR; // trim ratio, width/height float rotate_goal, rotate_angle; // target and actual rotation int E0ww, E0hh, E3ww, E3hh; // full size image and preview size int Fguidelines, guidelineX, guidelineY; // horz/vert guidelines for rotate int Fcorner, Fside, Frotate; int KBcorner, KBside; int Fmax = 0; editfunc EFtrimrotate; void dialog(); int dialog_event(zdialog *zd, cchar *event); void trim_customize(); void mousefunc(); void KBfunc(int key); void trim_limits(); void trim_darkmargins(); void trim_final(); void rotate_func(); void drawlines(cairo_t *cr); void autotrim(); } // menu function void m_trim_rotate(GtkWidget *, cchar *menu) { using namespace trimrotate; int ii, xmargin, ymargin; cchar *trim_message = ZTX("Trim: drag middle to move, drag corners to resize"); cchar *rotate_mess = ZTX("Minor rotate: drag right edge with mouse"); char text[20]; zdialog *zd; F1_help_topic = "trim_rotate"; EFtrimrotate.menufunc = m_trim_rotate; // menu function EFtrimrotate.funcname = "trim_rotate"; EFtrimrotate.Frestart = 1; // allow restart EFtrimrotate.mousefunc = mousefunc; if (! edit_setup(EFtrimrotate)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); E0ww = E0pxm->ww; // full-size image dimensions E0hh = E0pxm->hh; E3ww = E3pxm->ww; E3hh = E3pxm->hh; if (E9pxm) PXM_free(E9pxm); // E9 invalid 17.04 E9pxm = 0; if (menu && strmatch(menu,"keep")) { // keep preset trim rectangle trimww = trimx2 - trimx1; // (full image scale) trimhh = trimy2 - trimy1; trimR = 1.0 * trimww / trimhh; // trim ratio = width/height } else if (menu && strmatch(menu,"max")) { trimww = E0ww; // initial trim rectangle, 1x image size trimhh = E0hh; xmargin = ymargin = 0; trimx1 = 0; trimx2 = E0ww; trimy1 = 0; trimy2 = E0hh; trimR = 1.0 * E0ww / E0hh; // trim ratio = width/height } else { trimww = 0.8 * E0ww; // initial trim rectangle, 80% image size trimhh = 0.8 * E0hh; xmargin = 0.5 * (E0ww - trimww); // set balanced margins ymargin = 0.5 * (E0hh - trimhh); trimx1 = xmargin; trimx2 = E0ww - xmargin; trimy1 = ymargin; trimy2 = E0hh - ymargin; trimR = 1.0 * trimww / trimhh; // trim ratio = width/height } ptrimx1 = 0; // set prior trim rectangle ptrimy1 = 0; // = 100% of image ptrimx2 = E3ww; ptrimy2 = E3hh; Fcorner = Fside = KBcorner = KBside = Frotate = 0; // nothing underway rotate_goal = rotate_angle = 0; // initially no rotation /*** ___________________________________________________________ | | | Trim: drag middle to move, drag corners to resize | | | | width [____] height [____] ratio 1.5 | | trim size: [Max] [Prev] [Invert] [Auto] [x] Lock Ratio | | [1:1] [2:1] [3:2] [4:3] [16:9] [gold] [Customize] | | | | Minor rotate: drag right edge with mouse [level] | | Rotate: degrees [____] | // auto-trim removed | | | Rotate 90° [image] [image] 180° [image] | | | | [Grid] [Apply] [Done] [Cancel] | |___________________________________________________________| ***/ zd = zdialog_new(ZTX("Trim/Rotate"),Mwin,Bgrid,Bapply,Bdone,Bcancel,null); EFtrimrotate.zd = zd; zdialog_add_widget(zd,"label","labtrim","dialog",trim_message,"space=3"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labW","hb1",Bwidth,"space=5"); zdialog_add_widget(zd,"zspin","width","hb1","20|30000|1|1000"); // fotoxx.h 18.01 zdialog_add_widget(zd,"label","space","hb1",0,"space=5"); zdialog_add_widget(zd,"label","labH","hb1",Bheight,"space=5"); zdialog_add_widget(zd,"zspin","height","hb1","20|30000|1|600"); zdialog_add_widget(zd,"label","space","hb1",0,"space=5"); zdialog_add_widget(zd,"label","labR","hb1",ZTX("ratio"),"space=5"); zdialog_add_widget(zd,"label","ratio","hb1","1.67 "); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab2","hb2",ZTX("trim size:"),"space=5"); zdialog_add_widget(zd,"button","max","hb2",Bmax,"space=5"); zdialog_add_widget(zd,"button","prev","hb2",Bprev,"space=5"); zdialog_add_widget(zd,"button","invert","hb2",Binvert,"space=5"); zdialog_add_widget(zd,"button","auto","hb2",Bauto,"space=5"); zdialog_add_widget(zd,"check","lock","hb2",ZTX("Lock Ratio"),"space=15"); zdialog_add_ttip(zd,"max",ZTX("maximize trim box")); zdialog_add_ttip(zd,"prev",ZTX("use previous size")); zdialog_add_ttip(zd,"invert",ZTX("invert width/height")); zdialog_add_ttip(zd,"auto",ZTX("trim transparent edges")); zdialog_add_ttip(zd,"lock",ZTX("lock width/height ratio")); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=3"); for (ii = 0; ii < 6; ii++) zdialog_add_widget(zd,"button",trimbuttons[ii],"hb3",trimbuttons[ii],"space=5"); zdialog_add_widget(zd,"button","custom","hb3",ZTX("Customize"),"space=5"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hb4","dialog"); zdialog_add_widget(zd,"label","labrotmess","hb4",rotate_mess,"space=5"); zdialog_add_widget(zd,"button","level","hb4",ZTX("Level"),"space=10"); zdialog_add_ttip(zd,"level",ZTX("use EXIF data if available")); zdialog_add_widget(zd,"hbox","hb5","dialog"); zdialog_add_widget(zd,"label","labrotate","hb5",ZTX("Rotate: degrees"),"space=5"); zdialog_add_widget(zd,"zspin","degrees","hb5","-360|360|0.1|0"); zdialog_add_widget(zd,"hbox","hb90","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab90","hb90","Rotate 90°","space=5"); zdialog_add_widget(zd,"imagebutt","-90","hb90","rotate-left.png","size=32|space=8"); zdialog_add_widget(zd,"imagebutt","+90","hb90","rotate-right.png","size=32|space=8"); zdialog_add_widget(zd,"label","lab180","hb90","180°","space=8"); zdialog_add_widget(zd,"imagebutt","180","hb90","rotate-180.png","size=32"); zd = EFtrimrotate.zd; zdialog_restore_inputs(zd); // restore all prior inputs zdialog_fetch(zd,"width",ptrimww); // preserve prior trim size zdialog_fetch(zd,"height",ptrimhh); // for use by [prev] button zdialog_stuff(zd,"width",trimww); // stuff width, height, ratio as zdialog_stuff(zd,"height",trimhh); // pre-calculated for this image snprintf(text,20,"%.3f ",trimR); zdialog_stuff(zd,"ratio",text); zdialog_stuff(zd,"degrees",0); takeMouse(mousefunc,dragcursor); // connect mouse function currgrid = 1; // use trim/rotate grid Fzoom = 0; trim_darkmargins(); zdialog_run(zd,dialog_event,"save"); // run dialog - parallel if (menu && strmatch(menu,"auto")) zdialog_send_event(zd,"auto"); if (Fmax) { Fmax = 0; zdialog_send_event(zd,"max"); } return; } // dialog event and completion callback function int trimrotate::dialog_event(zdialog *zd, cchar *event) { using namespace trimrotate; static int flip = 0; int width, height, delta; int ii, rlock; float r1, r2, ratio = 0; char text[20]; cchar *pp; cchar *exifkey[1] = { exif_rollangle_key }; char *ppv[1]; if (strmatch(event,"done")) zd->zstat = 3; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 4; // cancel Fcorner = Fside = KBcorner = KBside = Frotate = 0; // nothing underway if (zd->zstat) // dialog complete { if (zd->zstat == 1) { // [grid] zd->zstat = 0; // keep dialog active if (! gridsettings[1][GON]) m_gridlines(0,"grid 1"); else toggle_grid(2); return 1; } if (zd->zstat == 2) { // [apply] trim_final(); CEF->Fmods++; edit_done(0); m_trim_rotate(0,"max"); // restart edit return 1; } if (zd->zstat == 3) { // [done] if (trimww == E0ww && trimhh == E0hh) goto cancel; // no change, cancel trim_final(); CEF->Fmods++; currgrid = 0; // restore normal grid settings edit_done(0); return 1; } cancel: draw_toplines(2,0); // [cancel] - erase trim rectangle currgrid = 0; // restore normal grid settings edit_cancel(0); return 1; } if (strmatch(event,"custom")) { // [customize] button draw_toplines(2,0); // erase trim rectangle edit_cancel(0); // cancel edit trim_customize(); // customize dialog m_trim_rotate(0,0); // restart edit return 1; } if (strmatch(event,"focus")) { takeMouse(mousefunc,dragcursor); // connect mouse function return 1; } if (strmatch(event,"prev")) // use width/height from prior { // image trim width = ptrimww; height = ptrimhh; if (width < 20 || height < 20) return 1; // no value established, ignore if (width > E0ww) width = E0ww; if (height > E0hh) height = E0hh; zdialog_stuff(zd,"width",width); zdialog_stuff(zd,"height",height); event = "width"; // process same as manual inputs } if (strstr("width height",event)) // get direct width/height inputs { zdialog_fetch(zd,"width",width); // full image scale zdialog_fetch(zd,"height",height); zdialog_fetch(zd,"lock",rlock); // lock ratio on/off if (strmatch(event,"width")) { if (width > E3ww) width = E3ww; if (rlock) { // ratio locked height = width / trimR + 0.5; // try to keep ratio if (height > E3hh) height = E3hh; } } if (strmatch(event,"height")) { if (height > E3hh) height = E3hh; if (rlock) { width = height * trimR + 0.5; if (width > E3ww) width = E3ww; } } flip = 1 - flip; // alternates 0, 1, 0, 1 ... delta = width - trimww; if (delta > 0) { // increased width trimx1 = trimx1 - delta / 2; // left and right sides equally trimx2 = trimx2 + delta / 2; if (delta % 2) { // if increase is odd trimx1 = trimx1 - flip; // add 1 alternatively to each side trimx2 = trimx2 + 1 - flip; } if (trimx1 < 0) { // add balance to upper limit trimx2 = trimx2 - trimx1; trimx1 = 0; } if (trimx2 > E3ww) { // add balance to lower limit trimx1 = trimx1 - trimx2 + E3ww; trimx2 = E3ww; } } if (delta < 0) { // decreased width trimx1 = trimx1 - delta / 2; trimx2 = trimx2 + delta / 2; if (delta % 2) { trimx1 = trimx1 + flip; trimx2 = trimx2 - 1 + flip; } } delta = height - trimhh; if (delta > 0) { // increased height trimy1 = trimy1 - delta / 2; // top and bottom sides equally trimy2 = trimy2 + delta / 2; if (delta % 2) { // if increase is odd trimy1 = trimy1 - flip; // add 1 alternatively to each side trimy2 = trimy2 + 1 - flip; } if (trimy1 < 0) { trimy2 = trimy2 - trimy1; trimy1 = 0; } if (trimy2 > E3hh) { trimy1 = trimy1 - trimy2 + E3hh; trimy2 = E3hh; } } if (delta < 0) { // decreased height trimy1 = trimy1 - delta / 2; trimy2 = trimy2 + delta / 2; if (delta % 2) { trimy1 = trimy1 + flip; trimy2 = trimy2 - 1 + flip; } } if (trimx1 < 0) trimx1 = 0; // keep within limits if (trimx2 > E3ww) trimx2 = E3ww; // use ww not ww-1 if (trimy1 < 0) trimy1 = 0; if (trimy2 > E3hh) trimy2 = E3hh; width = trimx2 - trimx1; // new width and height height = trimy2 - trimy1; if (width > E3ww) width = E3ww; // limit to actual size if (height > E3hh) height = E3hh; trimww = width; // new trim size trimhh = height; zdialog_stuff(zd,"width",width); // update dialog values zdialog_stuff(zd,"height",height); if (! rlock) // set new ratio if not locked trimR = 1.0 * trimww / trimhh; snprintf(text,20,"%.3f ",trimR); // stuff new ratio zdialog_stuff(zd,"ratio",text); trim_darkmargins(); // show trim area in image return 1; } if (strmatch(event,"max")) // maximize trim rectangle { trimx1 = 0; trimy1 = 0; trimx2 = E3ww; trimy2 = E3hh; width = E3ww; // full scale trim size height = E3hh; trimww = width; // new trim size trimhh = height; zdialog_stuff(zd,"width",width); // update dialog values zdialog_stuff(zd,"height",height); trimR = 1.0 * trimww / trimhh; snprintf(text,20,"%.3f ",trimR); // stuff new ratio zdialog_stuff(zd,"ratio",text); zdialog_stuff(zd,"lock",0); // set no ratio lock trim_darkmargins(); // show trim area in image Fpaintnow(); // 17.08 return 1; } if (strmatch(event,"invert")) // invert width/height dimensions ratio = 1.0 / trimR; // mark ratio changed for (ii = 0; ii < 6; ii++) // trim ratio buttons if (strmatch(event,trimbuttons[ii])) break; if (ii < 6) { r1 = r2 = ratio = 0; pp = strField(trimratios[ii],':',1); if (pp) r1 = atof(pp); pp = strField(trimratios[ii],':',2); if (pp) r2 = atof(pp); if (r1 > 0 && r2 > 0) ratio = r1/r2; if (ratio < 0.1 || ratio > 10) ratio = 1.0; if (! ratio) return 1; zdialog_stuff(zd,"lock",1); // assume lock is wanted trimR = ratio; } if (ratio) // ratio was changed { trimR = ratio; if (trimx2 - trimx1 > trimy2 - trimy1) trimy2 = trimy1 + (trimx2 - trimx1) / trimR; // adjust smaller dimension else trimx2 = trimx1 + (trimy2 - trimy1) * trimR; if (trimx2 > E3ww) { // if off the right edge, trimx2 = E3ww; // adjust height trimy2 = trimy1 + (trimx2 - trimx1) / trimR; } if (trimy2 > E3hh) { // if off the bottom edge, trimy2 = E3hh; // adjust width trimx2 = trimx1 + (trimy2 - trimy1) * trimR; } trimww = trimx2 - trimx1; // new rectangle dimensions trimhh = trimy2 - trimy1; zdialog_stuff(zd,"width",trimww); // stuff width, height, ratio zdialog_stuff(zd,"height",trimhh); snprintf(text,20,"%.3f ",trimR); zdialog_stuff(zd,"ratio",text); trim_darkmargins(); // update trim area in image return 1; } if (strmatch(event,"level")) // auto level using EXIF RollAngle { exif_get(curr_file,exifkey,ppv,1); if (ppv[0]) { rotate_goal = atof(ppv[0]); zfree(ppv[0]); rotate_func(); // E3 is rotated E1 } } if (strstr("+90 -90 180 degrees",event)) // rotate action { if (strmatch(event,"+90")) // 90 deg. CW rotate_goal += 90; if (strmatch(event,"-90")) // 90 deg. CCW rotate_goal -= 90; if (strmatch(event,"180")) // 180 deg. (upside down) rotate_goal += 180; if (strmatch(event,"degrees")) // degrees adjustment zdialog_fetch(zd,"degrees",rotate_goal); if (rotate_goal > 180) rotate_goal -= 360; // keep within -180 to +180 deg. if (rotate_goal < -180) rotate_goal += 360; if (fabsf(rotate_goal) < 0.01) rotate_goal = 0; // = 0 within my precision zdialog_stuff(zd,"degrees",rotate_goal); rotate_func(); // E3 is rotated E1 } if (strmatch(event,"auto")) // auto trim margins { if (CEF->Fmods) { // save pending edit dialog_event(zd,"max"); trim_final(); edit_done(0); } else edit_cancel(0); autotrim(); // do auto-trim m_trim_rotate(0,"keep"); // restart edit return 1; } return 1; } // trim/rotate mouse function void trimrotate::mousefunc() { using namespace trimrotate; int mpx, mpy, xdrag, ydrag; int moveall = 0; int dx, dy, dd, dc, ds; int d1, d2, d3, d4; zdialog *zd = EFtrimrotate.zd; if (! LMclick && ! RMclick && ! Mxdrag && ! Mydrag) { // no click or drag Fcorner = Fside = Frotate = 0; return; } if (LMclick) // add vertical and horizontal { // lines at click position LMclick = 0; Fcorner = Fside = KBcorner = KBside = Frotate = 0; Fguidelines = 1; guidelineX = Mxclick; guidelineY = Myclick; trim_darkmargins(); Fpaint2(); return; } if (RMclick) // remove the lines { RMclick = 0; Fcorner = Fside = KBcorner = KBside = Frotate = 0; Fguidelines = 0; trim_darkmargins(); Fpaint2(); return; } if (Mxdrag || Mydrag) // drag { mpx = Mxdrag; mpy = Mydrag; xdrag = Mxdrag - Mxdown; ydrag = Mydrag - Mydown; Mxdown = Mxdrag; // reset drag origin Mydown = Mydrag; Mxdrag = Mydrag = 0; // 17.04 } if (Frotate) goto mouse_rotate; // continue rotate in progress else if (Fcorner) { if (Fcorner == 1) { trimx1 = mpx; trimy1 = mpy; } // continue dragging corner with mouse if (Fcorner == 2) { trimx2 = mpx; trimy1 = mpy; } if (Fcorner == 3) { trimx2 = mpx; trimy2 = mpy; } if (Fcorner == 4) { trimx1 = mpx; trimy2 = mpy; } } else if (Fside) { if (Fside == 1) trimx1 = mpx; // continue dragging side with mouse 17.04 if (Fside == 2) trimy1 = mpy; if (Fside == 3) trimx2 = mpx; if (Fside == 4) trimy2 = mpy; } else { moveall = 1; dd = 0.2 * (trimx2 - trimx1); // test if mouse is in the broad if (mpx < trimx1 + dd) moveall = 0; // middle of the rectangle if (mpx > trimx2 - dd) moveall = 0; dd = 0.2 * (trimy2 - trimy1); if (mpy < trimy1 + dd) moveall = 0; if (mpy > trimy2 - dd) moveall = 0; } if (moveall) { // yes, move the whole rectangle trimx1 += xdrag; trimx2 += xdrag; trimy1 += ydrag; trimy2 += ydrag; Fcorner = Fside = KBcorner = KBside = Frotate = 0; } else if (! Fcorner && ! Fside) // no, find closest corner/side to mouse { dx = mpx - trimx1; dy = mpy - trimy1; d1 = sqrt(dx*dx + dy*dy); // distance from NW corner dx = mpx - trimx2; dy = mpy - trimy1; d2 = sqrt(dx*dx + dy*dy); // NE dx = mpx - trimx2; dy = mpy - trimy2; d3 = sqrt(dx*dx + dy*dy); // SE dx = mpx - trimx1; dy = mpy - trimy2; d4 = sqrt(dx*dx + dy*dy); // SW Fcorner = 1; // find closest corner dc = d1; // NW if (d2 < dc) { Fcorner = 2; dc = d2; } // NE if (d3 < dc) { Fcorner = 3; dc = d3; } // SE if (d4 < dc) { Fcorner = 4; dc = d4; } // SW dx = mpx - trimx1; dy = mpy - (trimy1 + trimy2) / 2; d1 = sqrt(dx*dx + dy*dy); // distance from left side middle dx = mpx - (trimx1 + trimx2) / 2; dy = mpy - trimy1; d2 = sqrt(dx*dx + dy*dy); // top middle dx = mpx - trimx2; dy = mpy - (trimy1 + trimy2) / 2; d3 = sqrt(dx*dx + dy*dy); // right side middle dx = mpx - (trimx1 + trimx2) / 2; dy = mpy - trimy2; d4 = sqrt(dx*dx + dy*dy); // bottom middle Fside = 1; // find closest side ds = d1; // left if (d2 < ds) { Fside = 2; ds = d2; } // top if (d3 < ds) { Fside = 3; ds = d3; } // right if (d4 < ds) { Fside = 4; ds = d4; } // bottom if (dc < ds) Fside = 0; // closer to corner else Fcorner = 0; // closer to side if (Fside == 3 && E3ww - mpx < ds) // closest to right side, goto mouse_rotate; // and even closer to right edge if (Fcorner == 1) { trimx1 = mpx; trimy1 = mpy; } // move this corner to mouse if (Fcorner == 2) { trimx2 = mpx; trimy1 = mpy; } if (Fcorner == 3) { trimx2 = mpx; trimy2 = mpy; } if (Fcorner == 4) { trimx1 = mpx; trimy2 = mpy; } if (Fside == 1) trimx1 = mpx; // move this side to mouse if (Fside == 2) trimy1 = mpy; if (Fside == 3) trimx2 = mpx; if (Fside == 4) trimy2 = mpy; KBcorner = Fcorner; // save last corner/side moved KBside = Fside; } trim_limits(); // check margin limits and adjust if req. trim_darkmargins(); // show trim area in image return; // ------------------------------------------------------------------------ mouse_rotate: Frotate = 1; Fcorner = Fside = KBcorner = KBside = 0; rotate_goal += 40.0 * ydrag / E3ww; // convert radians to degrees (reduced) if (rotate_goal > 180) rotate_goal -= 360; // keep within -180 to +180 deg. if (rotate_goal < -180) rotate_goal += 360; if (fabsf(rotate_goal) < 0.01) rotate_goal = 0; // = 0 within my precision zdialog_stuff(zd,"degrees",rotate_goal); // update dialog rotate_func(); // E3 is rotated E1 return; } // Keyboard function // KB arrow keys tweak the last selected corner or side void trimrotate::KBfunc(int key) { using namespace trimrotate; int xstep, ystep; xstep = ystep = 0; if (key == GDK_KEY_Left) xstep = -1; if (key == GDK_KEY_Right) xstep = +1; if (key == GDK_KEY_Up) ystep = -1; if (key == GDK_KEY_Down) ystep = +1; if (KBcorner == 1) { // NW trimx1 += xstep; trimy1 += ystep; } if (KBcorner == 2) { // NE trimx2 += xstep; trimy1 += ystep; } if (KBcorner == 3) { // SE trimx2 += xstep; trimy2 += ystep; } if (KBcorner == 4) { // SW trimx1 += xstep; trimy2 += ystep; } if (KBside == 1) trimx1 += xstep; // left if (KBside == 2) trimy1 += ystep; // top if (KBside == 3) trimx2 += xstep; // right if (KBside == 4) trimy2 += ystep; // bottom trim_limits(); // check margin limits and adjust if req. trim_darkmargins(); // show trim area in image return; } // check new margins for sanity and enforce locked aspect ratio void trimrotate::trim_limits() { using namespace trimrotate; int rlock, chop; int ww, hh, ww2, hh2; float drr; char text[20]; zdialog *zd = EFtrimrotate.zd; if (trimx1 > trimx2-10) trimx1 = trimx2-10; // sanity limits if (trimy1 > trimy2-10) trimy1 = trimy2-10; zdialog_fetch(zd,"lock",rlock); // w/h ratio locked if (rlock && Fcorner) { if (Fcorner < 3) trimy1 = trimy2 - 1.0 * (trimx2 - trimx1) / trimR; else trimy2 = trimy1 + 1.0 * (trimx2 - trimx1) / trimR; } if (rlock && Fside) { // 17.04 ww = trimx2 - trimx1; hh = trimy2 - trimy1; ww2 = ww; hh2 = hh; if (Fside == 1 || Fside == 3) hh2 = ww / trimR; else ww2 = hh * trimR; ww2 = (ww2 - ww) / 2; hh2 = (hh2 - hh) / 2; trimx1 -= ww2; trimx2 += ww2; trimy1 -= hh2; trimy2 += hh2; } chop = 0; if (trimx1 < 0) { // look for off the edge trimx1 = 0; // after corner move chop = 1; } if (trimx2 > E3ww) { trimx2 = E3ww; chop = 2; } if (trimy1 < 0) { trimy1 = 0; chop = 3; } if (trimy2 > E3hh) { trimy2 = E3hh; chop = 4; } if (rlock && chop) { // keep ratio if off edge if (chop < 3) trimy2 = trimy1 + 1.0 * (trimx2 - trimx1) / trimR; else trimx2 = trimx1 + 1.0 * (trimy2 - trimy1) * trimR; } if (trimx1 > trimx2-10) trimx1 = trimx2-10; // sanity limits if (trimy1 > trimy2-10) trimy1 = trimy2-10; if (trimx1 < 0) trimx1 = 0; // keep within visible area if (trimx2 > E3ww) trimx2 = E3ww; if (trimy1 < 0) trimy1 = 0; if (trimy2 > E3hh) trimy2 = E3hh; trimww = trimx2 - trimx1; // new rectangle dimensions trimhh = trimy2 - trimy1; drr = 1.0 * trimww / trimhh; // new w/h ratio if (! rlock) trimR = drr; zdialog_stuff(zd,"width",trimww); // stuff width, height, ratio zdialog_stuff(zd,"height",trimhh); snprintf(text,20,"%.3f ",trimR); zdialog_stuff(zd,"ratio",text); return; } // Darken image pixels outside of current trim margins. // messy logic: update pixmaps only for changed pixels to increase speed void trimrotate::trim_darkmargins() { using namespace trimrotate; int ox1, oy1, ox2, oy2; // outer trim rectangle int nx1, ny1, nx2, ny2; // inner trim rectangle int px, py; float *pix1, *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (trimx1 < 0) trimx1 = 0; // keep within visible area if (trimx2 > E3ww) trimx2 = E3ww; if (trimy1 < 0) trimy1 = 0; if (trimy2 > E3hh) trimy2 = E3hh; if (ptrimx1 < trimx1) ox1 = ptrimx1; // outer rectangle else ox1 = trimx1; if (ptrimx2 > trimx2) ox2 = ptrimx2; else ox2 = trimx2; if (ptrimy1 < trimy1) oy1 = ptrimy1; else oy1 = trimy1; if (ptrimy2 > trimy2) oy2 = ptrimy2; else oy2 = trimy2; if (ptrimx1 > trimx1) nx1 = ptrimx1; // inner rectangle else nx1 = trimx1; if (ptrimx2 < trimx2) nx2 = ptrimx2; else nx2 = trimx2; if (ptrimy1 > trimy1) ny1 = ptrimy1; else ny1 = trimy1; if (ptrimy2 < trimy2) ny2 = ptrimy2; else ny2 = trimy2; ox1 -= 2; // expand outer rectangle oy1 -= 2; ox2 += 2; oy2 += 2; nx1 += 2; // reduce inner rectangle ny1 += 2; nx2 -= 2; ny2 -= 2; if (ox1 < 0) ox1 = 0; if (oy1 < 0) oy1 = 0; if (ox2 > E3ww) ox2 = E3ww; if (oy2 > E3hh) oy2 = E3hh; if (nx1 < ox1) nx1 = ox1; if (ny1 < oy1) ny1 = oy1; if (nx2 > ox2) nx2 = ox2; if (ny2 > oy2) ny2 = oy2; if (nx2 < nx1) nx2 = nx1; if (ny2 < ny1) ny2 = ny1; if (! E9pxm) E9pxm = PXM_copy(E3pxm); // 17.04 for (py = oy1; py < ny1; py++) // top band of pixels for (px = ox1; px < ox2; px++) { pix1 = PXMpix(E9pxm,px,py); pix3 = PXMpix(E3pxm,px,py); if (px < trimx1 || px >= trimx2 || py < trimy1 || py >= trimy2) { pix3[0] = pix1[0] / 2; // outside trim margins pix3[1] = pix1[1] / 2; // 50% brightness pix3[2] = pix1[2] / 2; } else memcpy(pix3,pix1,pcc); // inside, full brightness } for (py = oy1; py < oy2; py++) // right band for (px = nx2; px < ox2; px++) { pix1 = PXMpix(E9pxm,px,py); pix3 = PXMpix(E3pxm,px,py); if (px < trimx1 || px >= trimx2 || py < trimy1 || py >= trimy2) { pix3[0] = pix1[0] / 2; pix3[1] = pix1[1] / 2; pix3[2] = pix1[2] / 2; } else memcpy(pix3,pix1,pcc); } for (py = ny2; py < oy2; py++) // bottom band for (px = ox1; px < ox2; px++) { pix1 = PXMpix(E9pxm,px,py); pix3 = PXMpix(E3pxm,px,py); if (px < trimx1 || px >= trimx2 || py < trimy1 || py >= trimy2) { pix3[0] = pix1[0] / 2; pix3[1] = pix1[1] / 2; pix3[2] = pix1[2] / 2; } else memcpy(pix3,pix1,pcc); } for (py = oy1; py < oy2; py++) // left band for (px = ox1; px < nx1; px++) { pix1 = PXMpix(E9pxm,px,py); pix3 = PXMpix(E3pxm,px,py); if (px < trimx1 || px >= trimx2 || py < trimy1 || py >= trimy2) { pix3[0] = pix1[0] / 2; pix3[1] = pix1[1] / 2; pix3[2] = pix1[2] / 2; } else memcpy(pix3,pix1,pcc); } cairo_t *cr = draw_context_create(gdkwin,draw_context); Fpaint3(ox1,oy1,ox2-ox1,ny1-oy1,cr); // 4 updated rectangles Fpaint3(nx2,oy1,ox2-nx2,oy2-oy1,cr); Fpaint3(ox1,ny2,ox2-ox1,oy2-ny2,cr); Fpaint3(ox1,oy1,nx1-ox1,oy2-oy1,cr); drawlines(cr); // draw trim margin lines draw_context_destroy(draw_context); ptrimx1 = trimx1; // set prior trim rectangle ptrimx2 = trimx2; // from current trim rectangle ptrimy1 = trimy1; ptrimy2 = trimy2; if (gridsettings[currgrid][GON]) Fpaint2(); // refresh gridlines, delayed return; } // final trim - cut margins off // E3 is the input image, rotated and with black edges cropped if chosen // E3 is the output image from user trim rectangle void trimrotate::trim_final() { using namespace trimrotate; int px1, py1, px2, py2; float *pix3, *pix9; int nc = E3pxm->nc, pcc = nc * sizeof(float); draw_toplines(2,0); // erase trim rectangle if (trimx2 > E3ww) trimx2 = E3ww; // final check if (trimy2 > E3hh) trimy2 = E3hh; trimww = trimx2 - trimx1; // new rectangle dimensions trimhh = trimy2 - trimy1; if (E9pxm) PXM_free(E9pxm); E9pxm = PXM_make(trimww,trimhh,nc); // new pixmap with requested size for (py1 = trimy1; py1 < trimy2; py1++) // copy E3 (rotated) to new size for (px1 = trimx1; px1 < trimx2; px1++) { px2 = px1 - trimx1; py2 = py1 - trimy1; pix3 = PXMpix(E3pxm,px1,py1); pix9 = PXMpix(E9pxm,px2,py2); memcpy(pix9,pix3,pcc); } PXM_free(E3pxm); E3pxm = E9pxm; E9pxm = 0; E3ww = E3pxm->ww; // update E3 dimensions E3hh = E3pxm->hh; CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); return; } // rotate function // E3 = E1 with rotation void trimrotate::rotate_func() { using namespace trimrotate; zdialog *zd = EFtrimrotate.zd; if (fabs(rotate_goal) < 0.01) { // no rotation rotate_goal = rotate_angle = 0.0; zdialog_stuff(zd,"degrees",0.0); PXM_free(E3pxm); // E1 >> E3 E3pxm = PXM_copy(E1pxm); } if (rotate_goal != rotate_angle) { // 17.04 paintlock(1); // block window updates (rotate is thread) PXM_free(E3pxm); E3pxm = PXM_rotate(E1pxm,rotate_goal,1); // E3 is rotated E1 faster 17.04 rotate_angle = rotate_goal; paintlock(0); // unblock window updates } if (E9pxm) PXM_free(E9pxm); // E9 invalid 17.04 E9pxm = 0; E3ww = E3pxm->ww; // update E3 dimensions E3hh = E3pxm->hh; if (trimx2 > E3ww) trimx2 = E3ww; // contract trim rectangle if needed if (trimy2 > E3hh) trimy2 = E3hh; trimww = trimx2 - trimx1; // new rectangle dimensions trimhh = trimy2 - trimy1; zdialog_stuff(zd,"width",trimww); // stuff width, height zdialog_stuff(zd,"height",trimhh); ptrimx1 = 0; // set prior trim rectangle ptrimy1 = 0; ptrimx2 = E3ww; // = 100% of image ptrimy2 = E3hh; CEF->Fmods++; Fpaintnow(); // bugfix 17.08 return; } // draw lines on image: trim rectangle, guidelines, gridlines void trimrotate::drawlines(cairo_t *cr) { using namespace trimrotate; Ntoplines = 4; // outline trim rectangle toplines[0].x1 = trimx1; toplines[0].y1 = trimy1; toplines[0].x2 = trimx2; toplines[0].y2 = trimy1; toplines[0].type = 1; toplines[1].x1 = trimx2; toplines[1].y1 = trimy1; toplines[1].x2 = trimx2; toplines[1].y2 = trimy2; toplines[1].type = 1; toplines[2].x1 = trimx2; toplines[2].y1 = trimy2; toplines[2].x2 = trimx1; toplines[2].y2 = trimy2; toplines[2].type = 1; toplines[3].x1 = trimx1; toplines[3].y1 = trimy2; toplines[3].x2 = trimx1; toplines[3].y2 = trimy1; toplines[3].type = 1; if (Fguidelines) { // vert/horz rotate guidelines Ntoplines = 6; toplines[4].x1 = guidelineX; toplines[4].y1 = 0; toplines[4].x2 = guidelineX; toplines[4].y2 = E3hh-1; toplines[4].type = 2; toplines[5].x1 = 0; toplines[5].y1 = guidelineY; toplines[5].x2 = E3ww-1; toplines[5].y2 = guidelineY; toplines[5].type = 2; } draw_toplines(1,cr); // draw trim rectangle and guidelines return; } // auto-trim image - set trim rectangle to exclude transparent regions void trimrotate::autotrim() { using namespace trimrotate; PXM *pxm = 0; int px1, py1, px2, py2; int qx1, qy1, qx2, qy2; int qx, qy, step1, step2; int area1, area2, Fgrow = 0; float *ppix; if (! E0pxm) return; // no edit image pxm = E0pxm; if (pxm->nc < 4) return; // no alpha channel px1 = 0.4 * pxm->ww; // select small rectangle in the middle py1 = 0.4 * pxm->hh; px2 = 0.6 * pxm->ww; py2 = 0.6 * pxm->hh; step1 = 0.02 * (pxm->ww + pxm->hh); // start with big search steps step2 = 0.2 * step1; if (step2 < 1) step2 = 1; while (true) { while (true) { Fgrow = 0; area1 = (px2 - px1) * (py2 - py1); // area of current selection rectangle area2 = area1; for (qx1 = px1-step1; qx1 <= px1+step1; qx1 += step2) // loop, vary NW and SE corners +/- for (qy1 = py1-step1; qy1 <= py1+step1; qy1 += step2) for (qx2 = px2-step1; qx2 <= px2+step1; qx2 += step2) for (qy2 = py2-step1; qy2 <= py2+step1; qy2 += step2) { if (qx1 < 0) continue; // check image limits if (qy1 < 0) continue; if (qx1 > 0.5 * pxm->ww) continue; if (qy1 > 0.5 * pxm->hh) continue; if (qx2 > pxm->ww-1) continue; if (qy2 > pxm->hh-1) continue; if (qx2 < 0.5 * pxm->ww) continue; if (qy2 < 0.5 * pxm->hh) continue; ppix = PXMpix(pxm,qx1,qy1); // check 4 corners are not if (ppix[3] < 1) continue; // in transparent zones ppix = PXMpix(pxm,qx2,qy1); if (ppix[3] < 1) continue; ppix = PXMpix(pxm,qx2,qy2); if (ppix[3] < 1) continue; ppix = PXMpix(pxm,qx1,qy2); if (ppix[3] < 1) continue; area2 = (qx2 - qx1) * (qy2 - qy1); // look for larger enclosed area if (area2 <= area1) continue; for (qx = qx1; qx < qx2; qx++) { // check top/bottom sides not intersect ppix = PXMpix(pxm,qx,qy1); // transparent zones if (ppix[3] < 1) break; ppix = PXMpix(pxm,qx,qy2); if (ppix[3] < 1) break; } if (qx < qx2) continue; for (qy = qy1; qy < qy2; qy++) { // also left/right sides ppix = PXMpix(pxm,qx1,qy); if (ppix[3] < 1) break; ppix = PXMpix(pxm,qx2,qy); if (ppix[3] < 1) break; } if (qy < qy2) continue; Fgrow = 1; // successfully grew the rectangle px1 = qx1; // new bigger rectangle coordinates py1 = qy1; // for the next search iteration px2 = qx2; py2 = qy2; goto breakout; // leave all 4 loops } breakout: if (! Fgrow) break; } if (step2 == 1) break; // done step1 = 0.6 * step1; // reduce search step size if (step1 < 2) step1 = 2; step2 = 0.2 * step1; if (step2 < 1) step2 = 1; } trimx1 = px1; // set parameters for trim function trimx2 = px2; trimy1 = py1; trimy2 = py2; return; } // dialog to get custom trim button names and corresponding ratios void trimrotate::trim_customize() { using namespace trimrotate; char text[20], blab[8], rlab[8]; float r1, r2, ratio; cchar *pp; int ii, zstat; /*** ___________________________________________________________ | Trim Buttons | | | | label [ 5:4 ] [ 4:3 ] [ 8:5 ] [ 16:9 ] [ 2:1 ] [ gold ] | | ratio [ 5:4 ] [ 4:3 ] [ 8:5 ] [ 16:9 ] [ 2:1 ] [1.62:1] | | | | [Done] [Cancel] | |___________________________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Trim Buttons"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbb","dialog",0,"homog|space=3"); zdialog_add_widget(zd,"hbox","hbr","dialog",0,"homog|space=3"); zdialog_add_widget(zd,"label","label","hbb",ZTX("label"),"space=3"); zdialog_add_widget(zd,"label","ratio","hbr",ZTX("ratio"),"space=3"); strcpy(blab,"butt-0"); strcpy(rlab,"ratio-0"); for (ii = 0; ii < 6; ii++) // add current trimbuttons to dialog { blab[5] = '0' + ii; rlab[6] = '0' + ii; zdialog_add_widget(zd,"zentry",blab,"hbb",trimbuttons[ii],"size=6"); zdialog_add_widget(zd,"zentry",rlab,"hbr",trimratios[ii],"size=6"); } zdialog_set_modal(zd); // 17.08 zdialog_run(zd,0,"mouse"); // run dialog zstat = zdialog_wait(zd); // wait for complete if (zstat == 1) // done { for (ii = 0; ii < 6; ii++) // get new button names { blab[5] = '0' + ii; zdialog_fetch(zd,blab,text,12); strTrim2(text); if (! *text) continue; zfree(trimbuttons[ii]); trimbuttons[ii] = zstrdup(text); } for (ii = 0; ii < 6; ii++) // get new ratios { rlab[6] = '0' + ii; zdialog_fetch(zd,rlab,text,12); strTrim2(text); r1 = r2 = ratio = 0; pp = strField(text,':',1); if (! pp) continue; r1 = atof(pp); pp = strField(text,':',2); if (! pp) continue; r2 = atof(pp); if (r1 > 0 && r2 > 0) ratio = r1/r2; if (ratio < 0.1 || ratio > 10) continue; zfree(trimratios[ii]); trimratios[ii] = zstrdup(text); } } zdialog_free(zd); // kill dialog save_params(); // save parameters return; } /********************************************************************************/ // Upright a rotated image: -90, +90, 180 degrees. // This is not an edit transaction: file is replaced and re-opened. void m_upright(GtkWidget *, cchar *menu) { int upright_dialog_event(zdialog *zd, cchar *event); zdialog *zd; F1_help_topic = "upright"; if (checkpend("all")) return; if (clicked_file) { // use clicked file if present if (! strmatch(clicked_file,curr_file)) f_open(clicked_file,0,0,1,0); // avoid f_open() re-entry 18.01 clicked_file = 0; } if (! curr_file) { if (zdupright) zdialog_free(zdupright); zdupright = 0; return; } /*** _____________________________________________________ | Upright Image | | | | Image File: xxxxxxxxxxxxxxx.jpg | | _______ _______ _______ _______ _______ | | | | | | | | | | | | | | |upright| | -90 | | +90 | | 180 | | X | | | |_______| |_______| |_______| |_______| |_______| | |_____________________________________________________| ***/ if (! zdupright) { zd = zdialog_new(ZTX("Upright Image"),Mwin,null); zdupright = zd; zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labf","hbf",ZTX("Image File:"),"space=3"); zdialog_add_widget(zd,"label","file","hbf","filename.jpg","space=5"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"button","upright","hb1",ZTX("Upright"),"space=5"); zdialog_add_widget(zd,"imagebutt","-90","hb1","rotate-left.png","size=32|space=5"); zdialog_add_widget(zd,"imagebutt","+90","hb1","rotate-right.png","size=32|space=5"); zdialog_add_widget(zd,"imagebutt","180","hb1","rotate-180.png","size=32|space=5"); zdialog_add_widget(zd,"imagebutt","cancel","hb1","cancel.png","size=32|space=5"); zdialog_run(zd,upright_dialog_event,"save"); } zd = zdupright; char *pp = strrchr(curr_file,'/'); // stuff file name in dialog if (pp) zdialog_stuff(zd,"file",pp+1); return; } // dialog event and completion function int upright_dialog_event(zdialog *zd, cchar *event) { int angle = 0; char orientation = 0, *ppv[1]; cchar *exifkey[1] = { exif_orientation_key }; cchar *exifdata[1]; if (! curr_file) return 1; if (zd->zstat) { // [x] button zdialog_free(zd); zdupright = 0; return 1; } if (strmatch(event,"cancel")) { // cancel button zdialog_free(zd); zdupright = 0; return 1; } if (! strstr("upright -90 +90 180",event)) return 1; // ignore other events if (strmatch(event,"upright")) // auto upright button { exif_get(curr_file,exifkey,ppv,1); // get EXIF: Orientation if (ppv[0]) { orientation = *ppv[0]; // single character zfree(ppv[0]); } if (orientation == '6') angle = +90; // rotate clockwise 90 deg. if (orientation == '8') angle = -90; // counterclockwise 90 deg. if (orientation == '3') angle = 180; // 180 deg. if (! angle) { zmessageACK(Mwin,ZTX("rotation unknown")); return 1; } } if (strmatch(event,"+90")) angle += 90; // other buttons if (strmatch(event,"-90")) angle -= 90; if (strmatch(event,"180")) angle += 180; if (! angle) return 1; // ignore other events if (checkpend("all")) return 1; paintlock(1); // block window updates Fblock = 1; E0pxm = PXM_load(curr_file,1); // load poss. 16-bit image if (E0pxm) { E3pxm = PXM_rotate(E0pxm,angle); // rotate (threaded) PXM_free(E0pxm); E0pxm = E3pxm; E3pxm = 0; } paintlock(0); // unblock window updates f_save(curr_file,curr_file_type,curr_file_bpc,1); PXM_free(E0pxm); exifdata[0] = (char *) ""; // remove EXIF orientation data exif_put(curr_file,exifkey,exifdata,1); // (f_save() omits if no edit made) update_image_index(curr_file); // update index data Fblock = 0; f_open(curr_file); gallery(curr_file,"paint",0); // repaint gallery return 1; } /********************************************************************************/ // Resize (rescale) image // // Output pixels are composites of input pixels, e.g. 2/3 size means // that 3x3 input pixels are mapped into 2x2 output pixels, and an // image size of 1000 x 600 becomes 667 x 400. int resize_ww0, resize_hh0; // original size int resize_ww1, resize_hh1; // new size float resize_lockratio; // lock w/h ratio editfunc EFresize; void m_resize(GtkWidget *, cchar *menu) { int resize_dialog_event(zdialog *zd, cchar *event); void * resize_thread(void *); F1_help_topic = "resize_image"; char rtext[12]; EFresize.menuname = menu; EFresize.menufunc = m_resize; EFresize.funcname = "resize"; EFresize.Frestart = 1; // allow restart EFresize.Fscript = 1; // scripting supported EFresize.threadfunc = resize_thread; // thread function if (! edit_setup(EFresize)) return; // setup edit /**** _________________________________________________________________ | | | pixels percent | | width [_____] [_____] | | height [_____] [_____] | | | | [x] 1/1 [_] 3/4 [_] 2/3 [_] 1/2 [_] 1/3 [_] 1/4 [_] Prev | | | | W/H Ratio: 1.333 Lock [_] | | [done] [cancel] | |_________________________________________________________________| ****/ zdialog *zd = zdialog_new(ZTX("Resize Image"),Mwin,Bdone,Bcancel,null); EFresize.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vb11","hb1",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vb12","hb1",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vb13","hb1",0,"homog|space=5"); zdialog_add_widget(zd,"label","placeholder","vb11",0); zdialog_add_widget(zd,"label","labw","vb11",Bwidth); zdialog_add_widget(zd,"label","labh","vb11",Bheight); zdialog_add_widget(zd,"label","labpix","vb12","pixels"); zdialog_add_widget(zd,"zspin","wpix","vb12","20|30000|1|20"); // fotoxx.h 18.01 zdialog_add_widget(zd,"zspin","hpix","vb12","20|30000|1|20"); zdialog_add_widget(zd,"label","labpct","vb13",Bpercent); zdialog_add_widget(zd,"zspin","wpct","vb13","1|400|1|100"); // step size = 1.0 % 17.04 zdialog_add_widget(zd,"zspin","hpct","vb13","1|400|1|100"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"label","space","hb2",0); zdialog_add_widget(zd,"check","1/1","hb2","1/1"); zdialog_add_widget(zd,"check","3/4","hb2","3/4"); zdialog_add_widget(zd,"check","2/3","hb2","2/3"); zdialog_add_widget(zd,"check","1/2","hb2","1/2"); zdialog_add_widget(zd,"check","1/3","hb2","1/3"); zdialog_add_widget(zd,"check","1/4","hb2","1/4"); zdialog_add_widget(zd,"check","prev","hb2",Bprev); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labratio","hb3",ZTX("W/H Ratio:"),"space=5"); zdialog_add_widget(zd,"label","ratio","hb3","1.333"); zdialog_add_widget(zd,"check","lockratio","hb3",ZTX("Lock"),"space=10"); zdialog_add_ttip(zd,"prev",ZTX("use previous settings")); resize_ww0 = E1pxm->ww; // original width, height resize_hh0 = E1pxm->hh; zdialog_stuff(zd,"wpix",resize_ww0); zdialog_stuff(zd,"hpix",resize_hh0); zdialog_stuff(zd,"lockratio",1); // default ratio locked zdialog_stuff(zd,"1/1",1); // begin with full size zdialog_stuff(zd,"3/4",0); zdialog_stuff(zd,"2/3",0); zdialog_stuff(zd,"1/2",0); zdialog_stuff(zd,"1/3",0); zdialog_stuff(zd,"1/4",0); zdialog_stuff(zd,"prev",0); resize_lockratio = 1.0 * resize_ww0 / resize_hh0; snprintf(rtext,12,"%.3f",resize_lockratio); zdialog_stuff(zd,"ratio",rtext); zdialog_run(zd,resize_dialog_event,"save"); // run dialog return; } // dialog event and completion callback function int resize_dialog_event(zdialog *zd, cchar *event) // overhauled for scripting { int lock, nn; double wwxhh; float wpct1, hpct1, ratio; char rtext[12]; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (zd->zstat) // dialog complete { if (zd->zstat == 1) { // done editresize[0] = resize_ww1; // remember size used editresize[1] = resize_hh1; edit_done(0); } else edit_cancel(0); // cancel or kill Fzoom = 0; return 1; } if (strmatch(event,"focus")) return 1; // ignore focus if (event[0] =='w' || event[0] == 'h') { // if width or height set directly, zdialog_stuff(zd,"1/1",0); // set all ratio buttons off zdialog_stuff(zd,"3/4",0); zdialog_stuff(zd,"2/3",0); zdialog_stuff(zd,"1/2",0); zdialog_stuff(zd,"1/3",0); zdialog_stuff(zd,"1/4",0); zdialog_stuff(zd,"prev",0); } if (strstr("1/1 3/4 2/3 1/2 1/3 1/4 prev",event)) { // a ratio button was selected zdialog_stuff(zd,"1/1",0); // set all ratio buttons off zdialog_stuff(zd,"3/4",0); zdialog_stuff(zd,"2/3",0); zdialog_stuff(zd,"1/2",0); zdialog_stuff(zd,"1/3",0); zdialog_stuff(zd,"1/4",0); zdialog_stuff(zd,"prev",0); zdialog_stuff(zd,event,1); // set selected button on } zdialog_fetch(zd,"wpix",resize_ww1); // get width and height values zdialog_fetch(zd,"hpix",resize_hh1); zdialog_fetch(zd,"wpct",wpct1); zdialog_fetch(zd,"hpct",hpct1); zdialog_fetch(zd,"lockratio",lock); // lock ratio on/off if (strmatch(event,"lockratio") && lock) // lock ratio enabled resize_lockratio = 1.0 * resize_ww1 / resize_hh1; // update lock ratio to hold if (strmatch(event,"wpct")) // width % - set pixel width resize_ww1 = int(wpct1 / 100.0 * resize_ww0 + 0.5); if (strmatch(event,"hpct")) // height % - set pixel height resize_hh1 = int(hpct1 / 100.0 * resize_hh0 + 0.5); if (lock && event[0] == 'w') // preserve width/height ratio resize_hh1 = int(resize_ww1 * 1.0 / resize_lockratio + 0.5); if (lock && event[0] == 'h') resize_ww1 = int(resize_hh1 * resize_lockratio + 0.5); zdialog_fetch(zd,"1/1",nn); // if a ratio was selected, if (nn) { // set image size accordingly resize_ww1 = resize_ww0; resize_hh1 = resize_hh0; } zdialog_fetch(zd,"3/4",nn); if (nn) { resize_ww1 = (3 * resize_ww0 + 3) / 4; resize_hh1 = (3 * resize_hh0 + 3) / 4; } zdialog_fetch(zd,"2/3",nn); if (nn) { resize_ww1 = (2 * resize_ww0 + 2) / 3; resize_hh1 = (2 * resize_hh0 + 2) / 3; } zdialog_fetch(zd,"1/2",nn); if (nn) { resize_ww1 = (resize_ww0 + 1) / 2; resize_hh1 = (resize_hh0 + 1) / 2; } zdialog_fetch(zd,"1/3",nn); if (nn) { resize_ww1 = (resize_ww0 + 2) / 3; resize_hh1 = (resize_hh0 + 2) / 3; } zdialog_fetch(zd,"1/4",nn); if (nn) { resize_ww1 = (resize_ww0 + 3) / 4; resize_hh1 = (resize_hh0 + 3) / 4; } zdialog_fetch(zd,"prev",nn); // set previously used size if (nn) { resize_ww1 = editresize[0]; resize_hh1 = editresize[1]; } if (resize_ww1 < 20 || resize_hh1 < 20) return 1; // refuse tiny size ratio = 1.0 * resize_ww1 / resize_hh1; // w/h ratio if (resize_ww1 > wwhh_limit1) { // enforce limits 18.01 resize_ww1 = wwhh_limit1; resize_hh1 = resize_ww1 / ratio; } if (resize_hh1 > wwhh_limit1) { resize_hh1 = wwhh_limit1; resize_ww1 = resize_hh1 * ratio; } wwxhh = 1.0 * resize_ww1 * resize_hh1; // enforce image size limit 18.01 if (wwxhh > wwhh_limit2) { resize_ww1 *= wwhh_limit2 / wwxhh; resize_hh1 *= wwhh_limit2 / wwxhh; } hpct1 = 100.0 * resize_hh1 / resize_hh0; // set percents to match pixels wpct1 = 100.0 * resize_ww1 / resize_ww0; zdialog_stuff(zd,"wpix",resize_ww1); // update all widget values zdialog_stuff(zd,"hpix",resize_hh1); zdialog_stuff(zd,"wpct",wpct1); zdialog_stuff(zd,"hpct",hpct1); ratio = 1.0 * resize_ww1 / resize_hh1; // update w/h ratio snprintf(rtext,12,"%.3f",ratio); zdialog_stuff(zd,"ratio",rtext); signal_thread(); // update image, no wait for idle return 1; } // do the resize job void * resize_thread(void *) { PXM *pxmtemp1, *pxmtemp2; while (true) { thread_idle_loop(); // wait for signal pxmtemp1 = PXM_rescale(E1pxm,resize_ww1,resize_hh1); // rescale the edit image paintlock(1); // block window updates pxmtemp2 = E3pxm; E3pxm = pxmtemp1; PXM_free(pxmtemp2); paintlock(0); // unblock window updates CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); } return 0; } /********************************************************************************/ // automatic image tuneup without user guidance float voodoo1_brdist[256]; // pixel count per brightness level int voodoo_01, voodoo_99; // 1% and 99% brightness levels (0 - 255) void voodoo1_distribution(); // compute brightness distribution void * voodoo1_thread(void *); editfunc EFvoodoo1; void m_voodoo1(GtkWidget *, cchar *menu) { F1_help_topic = "voodoo"; EFvoodoo1.menuname = menu; EFvoodoo1.menufunc = m_voodoo1; EFvoodoo1.funcname = "voodoo1"; EFvoodoo1.Farea = 1; // select area ignored EFvoodoo1.Fscript = 1; // scripting supported EFvoodoo1.threadfunc = voodoo1_thread; if (! edit_setup(EFvoodoo1)) return; // setup edit voodoo1_distribution(); // compute brightness distribution signal_thread(); // start update thread edit_done(0); // edit done return; } // compute brightness distribution for image void voodoo1_distribution() { int px, py, ii; int ww = E1pxm->ww, hh = E1pxm->hh; float bright1; float *pix1; for (ii = 0; ii < 256; ii++) // clear brightness distribution data voodoo1_brdist[ii] = 0; for (py = 0; py < hh; py++) // compute brightness distribution for (px = 0; px < ww; px++) { pix1 = PXMpix(E1pxm,px,py); bright1 = pixbright(pix1); voodoo1_brdist[int(bright1)]++; } for (ii = 1; ii < 256; ii++) // cumulative brightness distribution voodoo1_brdist[ii] += voodoo1_brdist[ii-1]; // 0 ... (ww * hh) voodoo_01 = 0; voodoo_99 = 255; for (ii = 0; ii < 256; ii++) // compute index values (0 - 255) { // for darkest 1% and brightest 1% if (voodoo1_brdist[ii] < 0.01 * ww * hh) voodoo_01 = ii; if (voodoo1_brdist[ii] < 0.99 * ww * hh) voodoo_99 = ii; } for (ii = 0; ii < 256; ii++) voodoo1_brdist[ii] = voodoo1_brdist[ii] // multiplier per brightness level / (ww * hh) * 256.0 / (ii + 1); // ( = 1 for a flat distribution) return; } // thread function - use multiple working threads void * voodoo1_thread(void *) { void * voodoo1_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(voodoo1_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * voodoo1_wthread(void *arg) // worker thread function { int index = *((int *) (arg)); int px, py; float *pix1, *pix3; float bright1, bright2, bright3, cmax; float red1, green1, blue1, red3, green3, blue3; float flat1 = 0.3; // strength of distribution flatten float flat2 = 1.0 - flat1; float sat1 = 0.3, sat2; // strength of saturation increase float f1, f2; float expand = 256.0 / (voodoo_99 - voodoo_01 + 1); // brightness range expander for (py = index; py < E1pxm->hh; py += NWT) // voodoo brightness distribution for (px = 0; px < E1pxm->ww; px++) { pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; bright2 = 0.25 * red1 + 0.65 * green1 + 0.10 * blue1; // input brightness, 0 - 256 bright2 = (bright2 - voodoo_01) * expand; // expand to clip low / high 1% if (bright2 < 0) bright2 = 0; if (bright2 > 255) bright2 = 255; bright1 = voodoo1_brdist[int(bright2)]; // factor for flat output brightness bright3 = flat1 * bright1 + flat2; // attenuate f1 = (256.0 - bright2) / 256.0; // bright2 = 0 - 256 >> f1 = 1 - 0 f2 = 1.0 - f1; // prevent banding in bright areas bright3 = f1 * bright3 + f2; // tends to 1.0 for brighter pixels red3 = red1 * bright3; // blend new and old brightness green3 = green1 * bright3; blue3 = blue1 * bright3; bright3 = 0.333 * (red3 + green3 + blue3); // mean color brightness sat2 = sat1 * (256.0 - bright3) / 256.0; // bright3 = 0 - 256 >> sat2 = sat1 - 0 red3 = red3 + sat2 * (red3 - bright3); // amplified color, max for dark pixels green3 = green3 + sat2 * (green3 - bright3); blue3 = blue3 + sat2 * (blue3 - bright3); if (red3 < 0) red3 = 0; // stop possible underflow if (green3 < 0) green3 = 0; if (blue3 < 0) blue3 = 0; cmax = red3; // stop overflow, keep color balance if (green3 > cmax) cmax = green3; if (blue3 > cmax) cmax = blue3; if (cmax > 255.9) { cmax = 255.9 / cmax; red3 = red3 * cmax; green3 = green3 * cmax; blue3 = blue3 * cmax; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // flatten brightness distribution based on the distribution of nearby zones namespace voodoo2_names { float flatten = 30; // flatten value, 30% float deband = 70; // deband value, 70% int Iww, Ihh; // image dimensions int NZ; // no. of image zones int Zsize, Zrows, Zcols; // zone parameters float *Br; // Br[py][px] pixel brightness int *Zxlo, *Zylo, *Zxhi, *Zyhi; // Zxlo[ii] etc. zone ii pixel range int *Zcen; // Zcen[ii][2] zone ii center (py,px) uint8 *Zn; // Zn[py][px][9] 9 nearest zones for pixel: 0-200 (255 = none) uint8 *Zw; // Zw[py][px][9] zone weights for pixel: 0-100 = 1.0 float *Zff; // Zff[ii][1000] zone ii flatten factors editfunc EFvoodoo2; // edit function void * wthread(void *); // working thread } void m_voodoo2(GtkWidget *, cchar *menu) { using namespace voodoo2_names; int gNZ, pNZ; int px, py, cx, cy; int rx, ii, jj, kk; int zww, zhh, row, col; float *pix1, bright; float weight[9], sumweight; float D, Dthresh; F1_help_topic = "voodoo"; EFvoodoo2.menuname = menu; EFvoodoo2.menufunc = m_voodoo2; EFvoodoo2.funcname = "voodoo2"; EFvoodoo2.Farea = 1; // select area ignored EFvoodoo2.Fscript = 1; // scripting supported if (! edit_setup(EFvoodoo2)) return; // setup edit Iww = E1pxm->ww; // image dimensions Ihh = E1pxm->hh; if (Iww * Ihh > 227 * MEGA) { zmessageACK(Mwin,"image too big"); edit_cancel(0); return; } Ffuncbusy = 1; NZ = 40; // zone count gNZ = pNZ = NZ; // zone count goal while (true) { Zsize = sqrtf(Iww * Ihh / gNZ); // approx. zone size Zrows = Ihh / Zsize + 0.5; // get appropriate rows and cols Zcols = Iww / Zsize + 0.5; NZ = Zrows * Zcols; // NZ matching rows x cols if (NZ >= pNZ) break; gNZ++; } Br = (float *) zmalloc(Iww * Ihh * sizeof(float)); // allocate memory Zn = (uint8 *) zmalloc(Iww * Ihh * 9 * sizeof(uint8)); // cannot exceed 2048 M Zw = (uint8 *) zmalloc(Iww * Ihh * 9 * sizeof(uint8)); Zxlo = (int *) zmalloc(NZ * sizeof(int)); Zylo = (int *) zmalloc(NZ * sizeof(int)); Zxhi = (int *) zmalloc(NZ * sizeof(int)); Zyhi = (int *) zmalloc(NZ * sizeof(int)); Zcen = (int *) zmalloc(NZ * 2 * sizeof(int)); Zff = (float *) zmalloc(NZ * 1000 * sizeof(float)); for (py = 0; py < Ihh; py++) // get initial pixel brightness levels for (px = 0; px < Iww; px++) { pix1 = PXMpix(E1pxm,px,py); bright = pixbright(pix1); ii = py * Iww + px; Br[ii] = bright; zmainloop(1000); // keep GTK alive } zww = Iww / Zcols; // actual zone size zhh = Ihh / Zrows; for (row = 0; row < Zrows; row++) for (col = 0; col < Zcols; col++) // set low/high bounds per zone { ii = row * Zcols + col; Zxlo[ii] = col * zww; Zylo[ii] = row * zhh; Zxhi[ii] = Zxlo[ii] + zww; Zyhi[ii] = Zylo[ii] + zhh; Zcen[2*ii] = Zylo[ii] + zhh/2; Zcen[2*ii+1] = Zxlo[ii] + zww/2; } for (ii = 0; ii < NZ; ii++) // compute brightness distributiion { // for each zone for (jj = 0; jj < 1000; jj++) Zff[1000*ii+jj] = 0; for (py = Zylo[ii]; py < Zyhi[ii]; py++) // brightness distribution for (px = Zxlo[ii]; px < Zxhi[ii]; px++) { pix1 = PXMpix(E1pxm,px,py); bright = 1000.0 / 256.0 * pixbright(pix1); Zff[1000*ii+int(bright)]++; } for (jj = 1; jj < 1000; jj++) // cumulative brightness distribution Zff[1000*ii+jj] += Zff[1000*ii+jj-1]; for (jj = 0; jj < 1000; jj++) // multiplier per brightness level Zff[1000*ii+jj] = Zff[1000*ii+jj] / (zww*zhh) * 1000 / (jj+1); // to make distribution flat zmainloop(1000); // keep GTK alive } for (py = 0; py < Ihh; py++) // set 9 nearest zones per pixel for (px = 0; px < Iww; px++) { rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py row = py / zhh; // zone containing pixel col = px / zww; ii = 0; for (jj = row-1; jj <= row+1; jj++) // loop 3x3 surrounding zones for (kk = col-1; kk <= col+1; kk++) { if (jj >= 0 && jj < Zrows && kk >= 0 && kk < Zcols) // zone is not off the edge Zn[rx+ii] = jj * Zcols + kk; else Zn[rx+ii] = 255; // edge row/col: missing neighbor ii++; } } if (zww < zhh) // pixel to zone distance threshold Dthresh = 1.5 * zww; // area influence = 0 beyond this distance else Dthresh = 1.5 * zhh; for (py = 0; py < Ihh; py++) // set zone weights per pixel for (px = 0; px < Iww; px++) { rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py for (ii = 0; ii < 9; ii++) // distance to each zone center { jj = Zn[rx+ii]; if (jj == 255) { weight[ii] = 0; // missing zone weight = 0 continue; } cy = Zcen[2*jj]; cx = Zcen[2*jj+1]; D = sqrtf((py-cy)*(py-cy) + (px-cx)*(px-cx)); // distance from pixel to zone center D = D / Dthresh; if (D > 1) D = 1; // zone influence reaches zero at Dthresh weight[ii] = 1.0 - D; } sumweight = 0; for (ii = 0; ii < 9; ii++) // zone weights based on distance sumweight += weight[ii]; for (ii = 0; ii < 9; ii++) // normalize weights, sum = 1.0 Zw[rx+ii] = 100 * weight[ii] / sumweight; // 0-100 = 1.0 zmainloop(1000); // keep GTK alive } for (int ii = 0; ii < NWT; ii++) // start worker thread per processor core start_wthread(wthread,&Nval[ii]); wait_wthreads(); // wait for completion zfree(Br); // free memory zfree(Zn); zfree(Zw); zfree(Zxlo); zfree(Zylo); zfree(Zxhi); zfree(Zyhi); zfree(Zcen); zfree(Zff); Ffuncbusy = 0; CEF->Fmods++; CEF->Fsaved = 0; edit_done(0); // edit done return; } // worker thread for each CPU processor core void * voodoo2_names::wthread(void *arg) { using namespace voodoo2_names; int index = *((int *) (arg)); int px, py, rx, ii, jj; float *pix1, *pix3; float fold, fnew, cmax; float red1, green1, blue1, red3, green3, blue3; float FF, weight, bright, debandx; for (py = index; py < Ihh; py += NWT) // loop all image pixels for (px = 0; px < Iww; px++) { pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; bright = 0.25 * red1 + 0.65 * green1 + 0.10 * blue1; // input pixel brightness 0-255.9 bright *= 1000.0 / 256.0; // 0-999 rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py FF = 0; for (ii = 0; ii < 9; ii++) { // loop 9 nearest zones weight = Zw[rx+ii]; if (weight > 0) { // 0-100 = 1.0 jj = Zn[rx+ii]; FF += 0.01 * weight * Zff[1000*jj+int(bright)]; // sum weight * flatten factor } } red3 = FF * red1; // fully flattened brightness green3 = FF * green1; blue3 = FF * blue1; debandx = (1000.0 - bright) / 1000.0; // bright = 0 to 1000 >> debandx = 1 to 0 debandx += (1.0 - debandx) * (1.0 - 0.01 * deband); // debandx = 1 to 1 >> 1 to 0 fnew = 0.01 * flatten; // how much to flatten, 0 to 1 fnew = debandx * fnew; // attenuate flatten of brighter pixels fold = 1.0 - fnew; // how much to retain, 1 to 0 red3 = fnew * red3 + fold * red1; // blend new and old brightness green3 = fnew * green3 + fold * green1; blue3 = fnew * blue3 + fold * blue1; cmax = red3; // stop overflow, keep color balance if (green3 > cmax) cmax = green3; if (blue3 > cmax) cmax = blue3; if (cmax > 255.9) { cmax = 255.9 / cmax; red3 = red3 * cmax; green3 = green3 * cmax; blue3 = blue3 * cmax; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Retouch Combo // Adjust all aspects of brightness, contrast, color. // Brightness curves are used, overall and per RGB color. // Color saturation and white balance adjustments are also available. namespace combo_names { editfunc EFcombo; float brightness; // brightness input, -1 ... +1 float contrast; // contrast input, -1 ... +1 float wbalR, wbalG, wbalB; // white balance, white basis standard float blackR, blackG, blackB; // RGB black point if not 0/0/0 int whitebal, blacklev; // white balance or black level active int Fblack; // flag, black level was set int wbtemp; // illumination temp. input, 1K ... 10K float tempR, tempG, tempB; // RGB of illumination temp. float combR, combG, combB; // combined RGB factors float satlevel; // saturation input, -1 ... +1 = saturated float areaemph; // emphasis input, -1 ... +1 = bright areas float emphcurve[256]; // emphasis per brightness level, 0 ... 1 int Fapply = 0; // flag, apply dialog controls to image int Fdist = 0; // flag, show brightness distribution float amplify = 1.0; // curve amplifier, 0 ... 2 GtkWidget *drawwin_dist, *drawwin_scale; // brightness distribution graph widgets int RGBW[4] = { 0, 0, 0, 0 }; // " colors: red/green/blue/white (all) } void blackbodyRGB(int K, float &R, float &G, float &B); // menu function void m_retouch_combo(GtkWidget *, cchar *menu) { using namespace combo_names; int combo_dialog_event(zdialog* zd, cchar *event); void combo_mousefunc(); void combo_curvedit(int spc); void * combo_thread(void *); F1_help_topic = "retouch_combo"; EFcombo.menuname = menu; EFcombo.menufunc = m_retouch_combo; EFcombo.funcname = "retouch_combo"; EFcombo.FprevReq = 1; // use preview EFcombo.Farea = 2; // select area usable EFcombo.Frestart = 1; // restart allowed EFcombo.FusePL = 1; // use with paint/lever edits OK EFcombo.Fscript = 1; // scripting supported EFcombo.threadfunc = combo_thread; EFcombo.mousefunc = combo_mousefunc; // 18.01 if (! edit_setup(EFcombo)) return; // setup edit /*** _____________________________________________________ | Retouch Combo | | _________________________________________________ | | | | | // 5 curves are maintained: | | | | // curve 0: current display curve | | | | // 1: curve for all colors | | | | // curve 0: current display curve | | | | // 1: curve for all colors | | curve edit area | | // 2,3,4: red, green, blue | | | | | | | | | | | | // curve 0: current display curve | | | | // 1: curve for all colors | |_________________________________________________| | | |_________________________________________________| | // brightness scale: black to white stripe | (o) all (o) red (o) green (o) blue | // select curve to display | | | Amplifier ================[]============= Max. | // curve amplifier | Brightness ================[]============= High | // brightness | Contrast ================[]============= High | // contrast | Low Color ====================[]========= High | // color saturation | Warmer ====================[]========= Cooler | // color temperature | Dark Areas ==========[]=================== Bright | // color emphasis | | | [x] Show Brightness Distribution | | [x] Click for white balance [x] or black level | // click gray/white spot or dark spot | | | Settings File [Load] [Save] | | | | [Reset] [Prev] [Done] [Cancel] | |_____________________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Retouch Combo"),Mwin,Breset,Bprev,Bdone,Bcancel,null); EFcombo.zd = zd; zdialog_add_widget(zd,"frame","frameH","dialog",0,"expand"); // edit-curve and distribution graph zdialog_add_widget(zd,"frame","frameB","dialog"); // black to white brightness scale zdialog_add_widget(zd,"hbox","hbrgb","dialog"); zdialog_add_widget(zd,"radio","all","hbrgb",Ball,"space=5"); zdialog_add_widget(zd,"radio","red","hbrgb",Bred,"space=3"); zdialog_add_widget(zd,"radio","green","hbrgb",Bgreen,"space=3"); zdialog_add_widget(zd,"radio","blue","hbrgb",Bblue,"space=3"); zdialog_add_widget(zd,"hbox","hbcolor","dialog"); zdialog_add_widget(zd,"label","space","hbcolor",0,"space=1"); zdialog_add_widget(zd,"vbox","vbcolor1","hbcolor",0,"homog"); zdialog_add_widget(zd,"label","space","hbcolor",0,"space=1"); zdialog_add_widget(zd,"vbox","vbcolor2","hbcolor",0,"homog|expand"); zdialog_add_widget(zd,"label","space","hbcolor",0,"space=1"); zdialog_add_widget(zd,"vbox","vbcolor3","hbcolor",0,"homog"); zdialog_add_widget(zd,"label","space","hbcolor",0,"space=1"); zdialog_add_widget(zd,"label","labamp","vbcolor1",ZTX("Amplifier")); zdialog_add_widget(zd,"label","labrite","vbcolor1",ZTX("Brightness")); zdialog_add_widget(zd,"label","labcont","vbcolor1",ZTX("Contrast")); zdialog_add_widget(zd,"label","labsat1","vbcolor1",ZTX("Low Color")); zdialog_add_widget(zd,"label","labtemp1","vbcolor1",ZTX("Warmer")); zdialog_add_widget(zd,"label","labarea1","vbcolor1",ZTX("Dark Areas")); zdialog_add_widget(zd,"hscale","amplify","vbcolor2","0.0|2.0|0.01|1.0"); zdialog_add_widget(zd,"hscale","brightness","vbcolor2","-1.0|1.0|0.01|0"); zdialog_add_widget(zd,"hscale","contrast","vbcolor2","-1.0|1.0|0.01|0"); zdialog_add_widget(zd,"hscale","satlevel","vbcolor2","-1.0|1.0|0.01|0"); zdialog_add_widget(zd,"hscale","wbtemp","vbcolor2","2000|8000|1|5000"); zdialog_add_widget(zd,"hscale","areaemph","vbcolor2","0|1.0|0.01|0.5"); zdialog_add_widget(zd,"label","labrite2","vbcolor3",ZTX("Max.")); zdialog_add_widget(zd,"label","labrite2","vbcolor3",ZTX("High")); zdialog_add_widget(zd,"label","labcont2","vbcolor3",ZTX("High")); zdialog_add_widget(zd,"label","labsat2","vbcolor3",ZTX("High")); zdialog_add_widget(zd,"label","labtemp2","vbcolor3",ZTX("Cooler")); zdialog_add_widget(zd,"label","labarea2","vbcolor3",ZTX("Bright")); zdialog_add_widget(zd,"hbox","hbdist","dialog"); zdialog_add_widget(zd,"check","dist","hbdist",ZTX("Brightness Distribution"),"space=3"); zdialog_add_widget(zd,"hbox","hbclick","dialog"); zdialog_add_widget(zd,"check","whitebal","hbclick",ZTX("Click for white balance"),"space=3"); zdialog_add_widget(zd,"check","blacklev","hbclick",ZTX("Click for black level"),"space=3"); zdialog_add_widget(zd,"hbox","hbset","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labset","hbset",ZTX("Settings File"),"space=5"); zdialog_add_widget(zd,"button","load","hbset",Bload,"space=3"); zdialog_add_widget(zd,"button","save","hbset",Bsave,"space=3"); zdialog_add_ttip(zd,Bprev,ZTX("recall previous settings used")); zdialog_rescale(zd,"brightness",-1,0,1); zdialog_rescale(zd,"contrast",-1,0,1); zdialog_rescale(zd,"satlevel",-1,0,1); zdialog_rescale(zd,"wbtemp",2000,5000,8000); GtkWidget *frameH = zdialog_widget(zd,"frameH"); // setup edit curves spldat *sd = splcurve_init(frameH,combo_curvedit); EFcombo.curves = sd; sd->Nscale = 1; // horizontal line, neutral value 17.08 sd->xscale[0][0] = 0.00; sd->yscale[0][0] = 0.50; sd->xscale[1][0] = 1.00; sd->yscale[1][0] = 0.50; for (int ii = 0; ii < 4; ii++) // loop curves 0-3 { sd->nap[ii] = 3; // initial curves are neutral sd->vert[ii] = 0; sd->fact[ii] = 0; sd->apx[ii][0] = 0.01; // horizontal lines 17.08 sd->apy[ii][0] = 0.50; sd->apx[ii][1] = 0.50; sd->apy[ii][1] = 0.50; // curve 0 = overall brightness sd->apx[ii][2] = 0.99; sd->apy[ii][2] = 0.50; // curve 1/2/3 = R/G/B adjustment splcurve_generate(sd,ii); } sd->Nspc = 4; // 4 curves sd->fact[0] = 1; // curve 0 active zdialog_stuff(zd,"all",1); // stuff default selection, all drawwin_dist = sd->drawarea; // setup brightness distr. drawing area G_SIGNAL(drawwin_dist,"draw",brdist_drawgraph,RGBW); GtkWidget *frameB = zdialog_widget(zd,"frameB"); // setup brightness scale drawing area drawwin_scale = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(frameB),drawwin_scale); gtk_widget_set_size_request(drawwin_scale,300,12); G_SIGNAL(drawwin_scale,"draw",brdist_drawscale,0); brightness = 0; // neutral brightness contrast = 0; // neutral contrast amplify = 1.0; // neutral amplifier 17.04.1 whitebal = blacklev = 0; wbalR = wbalG = wbalB = 1.0; // neutral white balance wbtemp = 5000; // neutral illumination temp. blackbodyRGB(wbtemp,tempR,tempG,tempB); // all are 1.0 combR = combG = combB = 1.0; // neutral RGB factors blackR = blackG = blackB = 0.0; // zero black point 17.08 Fblack = 0; satlevel = 0; // neutral saturation Fapply = Fdist = 0; // 17.04.2 areaemph = 0.5; // even dark/bright area emphasis for (int ii = 0; ii < 256; ii++) emphcurve[ii] = 1; zdialog_stuff(zd,"whitebal",0); // reset mouse click status 18.01 zdialog_stuff(zd,"blacklev",0); zdialog_resize(zd,350,450); zdialog_run(zd,combo_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion callback function int combo_dialog_event(zdialog *zd, cchar *event) { using namespace combo_names; void combo_mousefunc(); spldat *sd = EFcombo.curves; float c1, c2, xval, yval; float bright0, dbrite, cont0, dcont; float dx, dy; int ii; Fapply = 0; if (strmatch(event,"done")) zd->zstat = 3; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 4; if (strmatch(event,"apply")) Fapply = 1; // from script if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); // get full size image signal_thread(); return 1; } if (zd->zstat == 1) // [reset] { zd->zstat = 0; // keep dialog active zdialog_stuff(zd,"amplify",1); // neutral amplifier zdialog_stuff(zd,"brightness",0); // neutral brightness zdialog_stuff(zd,"contrast",0); // neutral contrast brightness = contrast = 0; amplify = 1.0; // 17.04.1 wbalR = wbalG = wbalB = 1.0; // neutral white balance wbtemp = 5000; // neutral illumination temp. zdialog_stuff(zd,"wbtemp",wbtemp); blackbodyRGB(wbtemp,tempR,tempG,tempB); // all are 1.0 combR = combG = combB = 1.0; // neutral RGB factors blackR = blackG = blackB = 0.0; // zero black point 17.08 Fblack = 0; satlevel = 0; // neutral saturation zdialog_stuff(zd,"satlevel",0); areaemph = 0.5; // even area emphasis zdialog_stuff(zd,"areaemph",0.5); for (int ii = 0; ii < 256; ii++) // flat curve emphcurve[ii] = 1; for (int ii = 0; ii < 4; ii++) // loop brightness curves 0-3 { sd->nap[ii] = 3; // all curves are neutral sd->vert[ii] = 0; sd->apx[ii][0] = 0.01; // 17.08 sd->apy[ii][0] = 0.50; sd->apx[ii][1] = 0.50; sd->apy[ii][1] = 0.50; sd->apx[ii][2] = 0.99; sd->apy[ii][2] = 0.50; splcurve_generate(sd,ii); sd->fact[ii] = 0; } sd->fact[0] = 1; zdialog_stuff(zd,"all",1); zdialog_stuff(zd,"red",0); zdialog_stuff(zd,"green",0); zdialog_stuff(zd,"blue",0); zdialog_stuff(zd,"whitebal",0); // 18.01 zdialog_stuff(zd,"blacklev",0); edit_reset(); // restore initial image event = "dograph"; // trigger graph update } if (zd->zstat == 2) // [prev] restore previous settings { zd->zstat = 0; // keep dialog active edit_load_prev_widgets(&EFcombo); Fapply = 1; // trigger apply event } if (zd->zstat) // [done] or [cancel] { freeMouse(); if (zd->zstat == 3 && CEF->Fmods) { // [done] edit_fullsize(); // get full size image signal_thread(); edit_save_last_widgets(&EFcombo); edit_done(0); } else edit_cancel(0); // [cancel] or [x] return 1; } if (strmatch(event,"load")) // load all settings from a file edit_load_widgets(&EFcombo); if (strmatch(event,"save")) // save all settings to a file edit_save_widgets(&EFcombo); if (strmatch(event,"whitebal")) { // white balance checkbox 18.01 zdialog_fetch(zd,"whitebal",whitebal); if (whitebal) { zdialog_stuff(zd,"blacklev",0); blacklev = 0; } } if (strmatch(event,"blacklev")) { // black level checkbox 18.01 zdialog_fetch(zd,"blacklev",blacklev); if (blacklev) { zdialog_stuff(zd,"whitebal",0); whitebal = 0; } } if (strstr("whitebal blacklev",event)) { // white balance or black level 18.01 if (whitebal || blacklev) // one is on: takeMouse(combo_mousefunc,dragcursor); // connect mouse function else freeMouse(); // both off: free mouse return 1; } if (strmatch(event,"dist")) { // distribution checkbox zdialog_fetch(zd,"dist",Fdist); // distribution graph on/off event = "dograph"; // trigger graph update } if (strstr("all red green blue",event)) // new choice of curve { zdialog_fetch(zd,event,ii); if (! ii) return 0; // button OFF event, wait for ON event for (ii = 0; ii < 4; ii++) sd->fact[ii] = 0; ii = strmatchV(event,"all","red","green","blue",null); ii = ii-1; // new active curve: 0, 1, 2, 3 sd->fact[ii] = 1; splcurve_generate(sd,ii); // regenerate curve gtk_widget_queue_draw(sd->drawarea); // draw curve } if (strmatch(event,"brightness")) // brightness slider, 0 ... 1 { bright0 = brightness; zdialog_fetch(zd,"brightness",brightness); dbrite = brightness - bright0; // change in brightness, + - zdialog_stuff(zd,"all",1); // active curve is "all" sd->fact[0] = 1; for (ii = 1; ii < 4; ii++) sd->fact[ii] = 0; for (ii = 0; ii < sd->nap[0]; ii++) // update curve 0 nodes from slider { dy = sd->apy[0][ii] + 0.5 * dbrite; // increment brightness if (dy < 0) dy = 0; if (dy > 1) dy = 1; sd->apy[0][ii] = dy; } splcurve_generate(sd,0); // regenerate curve 0 gtk_widget_queue_draw(sd->drawarea); // draw curve 0 } if (strmatch(event,"contrast")) // contrast slider, 0 ... 1 { cont0 = contrast; zdialog_fetch(zd,"contrast",contrast); dcont = contrast - cont0; // change in contrast, + - zdialog_stuff(zd,"all",1); // active curve is "all" sd->fact[0] = 1; for (ii = 1; ii < 4; ii++) sd->fact[ii] = 0; for (ii = 0; ii < sd->nap[0]; ii++) // update curve 0 nodes from slider { dx = sd->apx[0][ii]; // 0 ... 0.5 ... 1 if (dx < 0.0 || dx >= 1.0) continue; dy = dcont * (dx - 0.5); // -0.5 ... 0 ... +0.5 * dcont dy += sd->apy[0][ii]; if (dy < 0) dy = 0; if (dy > 1) dy = 1; sd->apy[0][ii] = dy; } splcurve_generate(sd,0); // regenerate curve 0 gtk_widget_queue_draw(sd->drawarea); // draw curve 0 } if (Fdist) { // distribution enabled zdialog_fetch(zd,"red",RGBW[0]); // get graph color choice zdialog_fetch(zd,"green",RGBW[1]); zdialog_fetch(zd,"blue",RGBW[2]); zdialog_fetch(zd,"all",RGBW[3]); if (RGBW[3]) RGBW[0] = RGBW[1] = RGBW[2] = 1; RGBW[3] = 0; } else RGBW[0] = RGBW[1] = RGBW[2] = RGBW[3] = 0; if (strmatch(event,"dograph")) // thread done or new color choice gtk_widget_queue_draw(drawwin_dist); // update distribution graph if (strmatch(event,"blendwidth")) Fapply = 1; // trigger apply event if (strstr("amplify brightness contrast",event)) Fapply = 1; if (strstr("satlevel areaemph wbtemp",event)) Fapply = 1; if (strmatch(event,"load")) Fapply = 1; if (! Fapply) return 1; // wait for change if (Fdist) gtk_widget_queue_draw(drawwin_dist); // update distribution graph 17.04.2 zdialog_fetch(zd,"amplify",amplify); // get curve amplifier setting zdialog_fetch(zd,"brightness",brightness); // get brightness setting zdialog_fetch(zd,"contrast",contrast); // get contrast setting zdialog_fetch(zd,"satlevel",satlevel); // get saturation setting zdialog_fetch(zd,"wbtemp",wbtemp); // get illumination temp. setting blackbodyRGB(wbtemp,tempR,tempG,tempB); // generate new temp. adjustments zdialog_fetch(zd,"areaemph",areaemph); // get dark/bright area emphasis if (areaemph < 0.5) { // areaemph = 0 ... 0.5 c1 = 1.2; // c1 = 1.2 c2 = 2 * areaemph; // c2 = 0 ... 1 } else { // areaemph = 0.5 ... 1 c1 = 2 * (1 - areaemph); // c1 = 1 ... 0 c2 = 1.2; // c2 = 1.2 } for (ii = 0; ii < 256; ii++) { xval = ii / 256.0; // xval = 0 ... 1 yval = c1 + xval * (c2 - c1); // yval = c1 ... c2 if (yval > 1.0) yval = 1.0; // limit to 1.0 emphcurve[ii] = yval; } signal_thread(); // update the image return 1; } // this function is called when a curve is edited void combo_curvedit(int spc) { using namespace combo_names; signal_thread(); return; } // get nominal white color or black point from mouse click position void combo_mousefunc() // mouse function { using namespace combo_names; int px, py, dx, dy; float red, green, blue, rgbmean; float *ppix; char mousetext[60]; if (! LMclick) return; LMclick = 0; px = Mxclick; // mouse click position py = Myclick; if (px < 2) px = 2; // pull back from edge if (px > E3pxm->ww-3) px = E3pxm->ww-3; if (py < 2) py = 2; if (py > E3pxm->hh-3) py = E3pxm->hh-3; red = green = blue = 0; for (dy = -1; dy <= 1; dy++) // 3x3 block around mouse position for (dx = -1; dx <= 1; dx++) { ppix = PXMpix(E1pxm,px+dx,py+dy); // input image red += ppix[0]; green += ppix[1]; blue += ppix[2]; } red = red / 9.0; // mean RGB levels green = green / 9.0; blue = blue / 9.0; snprintf(mousetext,60,"3x3 pixels RGB: %.0f %.0f %.0f \n",red,green,blue); poptext_mouse(Mwin,mousetext,10,10,0,3); if (whitebal) { // click pixel is new gray/white 18.01 rgbmean = (red + green + blue) / 3.0; wbalR = rgbmean / red; // = 1.0 if all RGB are equal wbalG = rgbmean / green; // <1/>1 if RGB should be less/more wbalB = rgbmean / blue; signal_thread(); // trigger image update } if (blacklev) { // click pixel is new black point 18.01 blackR = red; blackG = green; blackB = blue; Fblack = 1; signal_thread(); // update image } return; } // combo thread function void * combo_thread(void *arg) { using namespace combo_names; void * combo_wthread(void *); while (true) { thread_idle_loop(); // wait for work or exit request combR = wbalR * tempR / 256.0; // gray standard based on clicked pixel combG = wbalG * tempG / 256.0; // colors and illumination temp. combB = wbalB * tempB / 256.0; // <1/>1 if RGB should be less/more for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(combo_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image3 modified CEF->Fsaved = 0; Fpaint2(); // update window if (CEF->thread_status > 1) continue; // continue working Mint Lockup if (CEF->zd) { zd_thread = CEF->zd; // signal dialog to update graph zd_thread_event = "dograph"; } else zd_thread = 0; } return 0; // not executed, stop g++ warning } void * combo_wthread(void *arg) // worker thread function { using namespace combo_names; int index = *((int *) arg); int ii, dist = 0, px, py; float *pix1, *pix3; float red1, green1, blue1, maxrgb; float red3, green3, blue3; float Rbp, Gbp, Bbp; float pixbrite, F1, F2; float coeff = 1000.0 / 256.0; float dold, dnew, ff; spldat *sd = EFcombo.curves; Rbp = Gbp = Bbp = 1.0; if (Fblack) { // if RGB black points defined, 17.08 Rbp = 256.0 / (256.0 - blackR); // rescale for full brightness Gbp = 256.0 / (256.0 - blackG); Bbp = 256.0 / (256.0 - blackB); } for (py = index; py < E3pxm->hh; py += NWT) // loop all pixels for (px = 0; px < E3pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red1 = pix1[0]; // input RGB values green1 = pix1[1]; blue1 = pix1[2]; // apply RGB black points to input RGB values // 17.08 if (Fblack) { red1 = red1 - blackR; // clip black value at low end red1 = red1 * Rbp; // rescale so that high end = 256 if (red1 < 0) red1 = 0; green1 = green1 - blackG; green1 = green1 * Gbp; if (green1 < 0) green1 = 0; blue1 = blue1 - blackB; blue1 = blue1 * Bbp; if (blue1 < 0) blue1 = 0; } // apply white balance and temperature color shift red3 = combR * red1; // <1/>1 if RGB should be less/more green3 = combG * green1; blue3 = combB * blue1; // apply saturation color shift if (satlevel != 0) { pixbrite = 0.333 * (red3 + green3 + blue3); // pixel brightness, 0 to 255.9 red3 = red3 + satlevel * (red3 - pixbrite); // satlevel is -1 ... +1 green3 = green3 + satlevel * (green3 - pixbrite); blue3 = blue3 + satlevel * (blue3 - pixbrite); } if (red3 < 0) red3 = 0; // stop underflow if (green3 < 0) green3 = 0; if (blue3 < 0) blue3 = 0; maxrgb = red3; // stop overflow if (green3 > maxrgb) maxrgb = green3; if (blue3 > maxrgb) maxrgb = blue3; if (maxrgb > 255.9) { red3 = red3 * 255.9 / maxrgb; green3 = green3 * 255.9 / maxrgb; blue3 = blue3 * 255.9 / maxrgb; } // apply dark/bright area emphasis curve for color changes if (areaemph != 0.5) { maxrgb = red1; // use original colors if (green1 > maxrgb) maxrgb = green1; if (blue1 > maxrgb) maxrgb = blue1; F1 = emphcurve[int(maxrgb)]; // 0 ... 1 neutral is flat curve = 1 F2 = 1.0 - F1; red3 = F1 * red3 + F2 * red1; green3 = F1 * green3 + F2 * green1; blue3 = F1 * blue3 + F2 * blue1; } // apply brightness/contrast curves pixbrite = red3; // use max. RGB value if (green3 > pixbrite) pixbrite = green3; if (blue3 > pixbrite) pixbrite = blue3; ii = coeff * pixbrite; // "all" curve index 0-999 if (ii < 0) ii = 0; if (ii > 999) ii = 999; ff = 1.0 + amplify * (sd->yval[0][ii] - 0.5); red3 = ff * red3; green3 = ff * green3; blue3 = ff * blue3; ii = coeff * red3; // additional RGB curve adjustments if (ii < 0) ii = 0; if (ii > 999) ii = 999; red3 = red3 * (1.0 + sd->yval[1][ii] - 0.5); // 17.08 ii = coeff * green3; if (ii < 0) ii = 0; if (ii > 999) ii = 999; green3 = green3 * (1.0 + sd->yval[2][ii] - 0.5); ii = coeff * blue3; if (ii < 0) ii = 0; if (ii > 999) ii = 999; blue3 = blue3 * (1.0 + sd->yval[3][ii] - 0.5); if (red3 < 0) red3 = 0; // stop underflow if (green3 < 0) green3 = 0; if (blue3 < 0) blue3 = 0; maxrgb = red3; // stop overflow if (green3 > maxrgb) maxrgb = green3; if (blue3 > maxrgb) maxrgb = blue3; if (maxrgb > 255.9) { red3 = red3 * 255.9 / maxrgb; green3 = green3 * 255.9 / maxrgb; blue3 = blue3 * 255.9 / maxrgb; } // select area edge blending if (sa_stat == 3 && dist < sa_blend) { dnew = sa_blendfunc(dist); dold = 1.0 - dnew; red3 = dnew * red3 + dold * red1; green3 = dnew * green3 + dold * green1; blue3 = dnew * blue3 + dold * blue1; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Return relative RGB illumination values for a light source // having a given input temperature of 1000-10000 deg. K // 5000 K is neutral: all returned factors relative to 1.0 void blackbodyRGB(int K, float &R, float &G, float &B) { float kk[19] = { 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0 }; float r1[19] = { 255, 255, 255, 255, 255, 255, 255, 255, 254, 250, 242, 231, 220, 211, 204, 197, 192, 188, 184 }; float g1[19] = { 060, 148, 193, 216, 232, 242, 249, 252, 254, 254, 251, 245, 239, 233, 228, 224, 220, 217, 215 }; float b1[19] = { 000, 010, 056, 112, 158, 192, 219, 241, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; static int ftf = 1; static float r2[10000], g2[10000], b2[10000]; if (ftf) { // initialize spline1(19,kk,r1); for (int T = 1000; T < 10000; T++) r2[T] = spline2(0.001 * T); spline1(19,kk,g1); for (int T = 1000; T < 10000; T++) g2[T] = spline2(0.001 * T); spline1(19,kk,b1); for (int T = 1000; T < 10000; T++) b2[T] = spline2(0.001 * T); ftf = 0; } if (K < 1000 || K > 9999) zappcrash("blackbody bad K: %dK",K); R = r2[K]; G = g2[K]; B = b2[K]; return; } /********************************************************************************/ // adjust brightness distribution by flattening and/or expanding range namespace britedist_names { int ww, hh; float LC, HC; // low, high cutoff levels float LF, MF, HF; // low, mid, high flatten parms float LS, MS, HS; // low, mid, high stretch parms float BB[1000]; // adjusted B for input B 0-999 int dialog_event(zdialog* zd, cchar *event); void compute_BB(); void * thread(void *); void * wthread(void *); editfunc EFbrightdist; } // menu function void m_edit_britedist(GtkWidget *, cchar *menu) { using namespace britedist_names; cchar *title = ZTX("Adjust Brightness Distribution"); F1_help_topic = "edit_brightness"; EFbrightdist.menuname = menu; EFbrightdist.menufunc = m_edit_britedist; EFbrightdist.funcname = "brightdist"; EFbrightdist.FprevReq = 1; // preview EFbrightdist.Farea = 2; // select area usable EFbrightdist.Frestart = 1; // restart allowed EFbrightdist.Fscript = 1; // scripting supported EFbrightdist.threadfunc = thread; if (! edit_setup(EFbrightdist)) return; // setup edit /*** __________________________________________ | Adjust Brightness Distribution | | | | Low Cutoff ==========[]============== | | High Cutoff ==============[]========== | | Low Flatten =========[]=============== | | Mid Flatten ============[]============ | | High Flatten ==============[]========== | | Low Stretch ================[]======== | | Mid Stretch =============[]=========== | | High Stretch =========[]=============== | | | | [Reset] [Done] [Cancel] | |__________________________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Breset,Bdone,Bcancel,null); EFbrightdist.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog|expand"); zdialog_add_widget(zd,"label","labLC","vb1",ZTX("Low Cutoff")); zdialog_add_widget(zd,"label","labHC","vb1",ZTX("High Cutoff")); zdialog_add_widget(zd,"label","labLF","vb1",ZTX("Low Flatten")); zdialog_add_widget(zd,"label","labMF","vb1",ZTX("Mid Flatten")); zdialog_add_widget(zd,"label","labHF","vb1",ZTX("High Flatten")); zdialog_add_widget(zd,"label","labLS","vb1",ZTX("Low Stretch")); zdialog_add_widget(zd,"label","labMS","vb1",ZTX("Mid Stretch")); zdialog_add_widget(zd,"label","labHS","vb1",ZTX("High Stretch")); zdialog_add_widget(zd,"hscale","LC","vb2","0|1.0|0.002|0","expand"); zdialog_add_widget(zd,"hscale","HC","vb2","0|1.0|0.002|0","expand"); zdialog_add_widget(zd,"hscale","LF","vb2","0|1.0|0.002|0","expand"); zdialog_add_widget(zd,"hscale","MF","vb2","0|1.0|0.002|0","expand"); zdialog_add_widget(zd,"hscale","HF","vb2","0|1.0|0.002|0","expand"); zdialog_add_widget(zd,"hscale","LS","vb2","0|1.0|0.002|0","expand"); zdialog_add_widget(zd,"hscale","MS","vb2","0|1.0|0.002|0","expand"); zdialog_add_widget(zd,"hscale","HS","vb2","0|1.0|0.002|0","expand"); zdialog_resize(zd,300,0); zdialog_run(zd,dialog_event,"save"); // run dialog - parallel m_show_brdist(0,0); // popup brightness histogram LC = HC = LF = MF = HF = LS = MS = HS = 0.0; compute_BB(); return; } // dialog event and completion function int britedist_names::dialog_event(zdialog *zd, cchar *event) { using namespace britedist_names; if (strmatch(event,"done")) zd->zstat = 2; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); // get full size image compute_BB(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; // keep dialog active LC = HC = LF = MF = HF = LS = MS = HS = 0.0; zdialog_stuff(zd,"LC",LC); zdialog_stuff(zd,"HC",HC); zdialog_stuff(zd,"LF",LF); zdialog_stuff(zd,"MF",MF); zdialog_stuff(zd,"HF",HF); zdialog_stuff(zd,"LS",LS); zdialog_stuff(zd,"MS",MS); zdialog_stuff(zd,"HS",HS); compute_BB(); signal_thread(); } else if (zd->zstat == 2 && CEF->Fmods) { // done edit_fullsize(); // get full size image compute_BB(); signal_thread(); m_show_brdist(0,"kill"); // kill distribution graph edit_done(0); // commit edit return 1; } else { edit_cancel(0); // cancel - discard edit m_show_brdist(0,"kill"); // kill distribution graph return 1; } } if (strmatch(event,"blendwidth")) // select area blendwidth change signal_thread(); if (strstr("LC HC LF MF HF LS MS HS apply",event)) { zdialog_fetch(zd,"LC",LC); zdialog_fetch(zd,"HC",HC); zdialog_fetch(zd,"LF",LF); zdialog_fetch(zd,"MF",MF); zdialog_fetch(zd,"HF",HF); zdialog_fetch(zd,"LS",LS); zdialog_fetch(zd,"MS",MS); zdialog_fetch(zd,"HS",HS); compute_BB(); signal_thread(); } wait_thread_idle(); // no overlap window update and threads Fpaintnow(); return 1; } // compute flattened brightness levels for preview size or full size image // FB[B] = flattened brightness level for brightness B, scaled 0-1000 void britedist_names::compute_BB() { using namespace britedist_names; int ii, npix, py, px, iB; float *pix1; float B, LC2, HC2; float FB[1000]; float LF2, MF2, HF2, LS2, MS2, HS2; float LFB, MFB, HFB, LSB, MSB, HSB; float LFW, MFW, HFW, LSW, MSW, HSW, TWB; ww = E1pxm->ww; hh = E1pxm->hh; for (ii = 0; ii < 1000; ii++) // clear brightness distribution data FB[ii] = 0; if (sa_stat == 3) // process selected area { for (ii = npix = 0; ii < ww * hh; ii++) { if (! sa_pixmap[ii]) continue; py = ii / ww; px = ii - py * ww; pix1 = PXMpix(E1pxm,px,py); B = 1000.0 * (pix1[0] + pix1[1] + pix1[2]) / 768.0; FB[int(B)]++; npix++; } for (ii = 1; ii < 1000; ii++) // cumulative brightness distribution FB[ii] += FB[ii-1]; // 0 ... npix for (ii = 0; ii < 1000; ii++) FB[ii] = FB[ii] / npix * 999.0; // flattened brightness level } else // process whole image { for (py = 0; py < hh; py++) // compute brightness distribution for (px = 0; px < ww; px++) { pix1 = PXMpix(E1pxm,px,py); B = 1000.0 * (pix1[0] + pix1[1] + pix1[2]) / 768.0; FB[int(B)]++; } for (ii = 1; ii < 1000; ii++) // cumulative brightness distribution FB[ii] += FB[ii-1]; // 0 ... (ww * hh) for (ii = 0; ii < 1000; ii++) FB[ii] = FB[ii] / (ww * hh) * 999.0; // flattened brightness level } LC2 = 500 * LC; // low cutoff, 0 ... 500 HC2 = 1000 - 500 * HC; // high cutoff, 1000 ... 500 for (iB = 0; iB < 1000; iB++) // loop brightness 0 - 1000 { B = iB; if (LC2 > 0 || HC2 < 1000) { // stretch to cutoff limits if (B < LC2) B = 0; else if (B > HC2) B = 999; else B = 1000.0 * (B - LC2) / (HC2 - LC2); } LF2 = LF * (1000 - B) / 1000; // low flatten LF ... 0 LF2 = LF2 * LF2; LFB = LF2 * FB[iB] + (1.0 - LF2) * B; LS2 = LS * (1000 - B) / 1000; // low stretch LS ... 0 LS2 = LS2 * LS2; LSB = B * (1 - LS2); MF2 = MF * (500 - fabsf(B - 500)) / 500; // mid flatten 0 ... MF ... 0 MF2 = sqrtf(MF2); MFB = MF2 * FB[iB] + (1.0 - MF2) * B; MS2 = MS * (B - 500) / 500; // mid stretch -MS ... 0 ... MS MSB = B * (1 + 0.5 * MS2); HF2 = HF * B / 1000; // high flatten 0 ... HF HF2 = HF2 * HF2; HFB = HF2 * FB[iB] + (1.0 - HF2) * B; HS2 = HS * B / 1000; // high stretch 0 ... HS HS2 = HS2 * HS2; HSB = B * (1 + HS2); LFW = fabsf(B - LFB) / (B + 1); // weight of each component LSW = fabsf(B - LSB) / (B + 1); MFW = fabsf(B - MFB) / (B + 1); MSW = fabsf(B - MSB) / (B + 1); HFW = fabsf(B - HFB) / (B + 1); HSW = fabsf(B - HSB) / (B + 1); TWB = LFW + LSW + MFW + MSW + HFW + HSW; // add weighted components if (TWB == 0) BB[iB] = B; else BB[iB] = (LFW * LFB + LSW * LSB + MFW * MFB + MSW * MSB + HFW * HFB + HSW * HSB) / TWB; } return; } // adjust brightness distribution thread function void * britedist_names::thread(void *) { using namespace britedist_names; while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker thread per processor core start_wthread(wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image modified, not saved CEF->Fsaved = 0; } return 0; // not executed, stop g++ warning } // worker thread for each CPU processor core void * britedist_names::wthread(void *arg) { using namespace britedist_names; int index = *((int *) (arg)); int iB, px, py, ii, dist = 0; float B, *pix1, *pix3; float dold, dnew, cmax; float red1, green1, blue1, red3, green3, blue3; for (py = index; py < E1pxm->hh; py += NWT) // flatten brightness distribution for (px = 0; px < E1pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E1pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel B = (pix1[0] + pix1[1] + pix1[2]) / 768.0 * 1000.0; // pixel brightness scaled 0-1000 iB = int(B); if (B > 0) B = BB[iB] / B; red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; red3 = B * red1; green3 = B * green1; blue3 = B * blue1; if (sa_stat == 3 && dist < sa_blend) { // select area is active, dnew = sa_blendfunc(dist); // blend edge dold = 1.0 - dnew; red3 = dnew * red3 + dold * red1; green3 = dnew * green3 + dold * green1; blue3 = dnew * blue3 + dold * blue1; } cmax = red3; // stop overflow, keep color balance if (green3 > cmax) cmax = green3; if (blue3 > cmax) cmax = blue3; if (cmax > 255.9) { cmax = 255.9 / cmax; red3 = red3 * cmax; green3 = green3 * cmax; blue3 = blue3 * cmax; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } exit_wthread(); return 0; // not executed, avoid gcc warning } /******************************************************************************** Magnify Gradients function enhance detail by magnifying brightness gradients methodology: get brightness gradients for each pixel in 4 directions: SE SW NE NW amplify gradients using the edit curve: new gradient = F(old gradient) integrate 4 new brightness surfaces from the amplified gradients: - pixel brightness = prior pixel brightness + amplified gradient - the Amplify control varies amplification from zero to maximum new pixel brightness = average from 4 calculated brightness surfaces *********************************************************************************/ namespace gradients_names { float *brmap1, *brmap2; float *brmap4[4]; int contrast99; float amplify; int ww, hh; editfunc EFgradients; void gradients_initz(zdialog *zd); int gradients_dialog_event(zdialog *zd, cchar *event); void gradients_curvedit(int); void * gradients_thread(void *); void * gradients_wthread1(void *arg); void * gradients_wthread2(void *arg); void * gradients_wthread3(void *arg); } // menu function void m_gradients(GtkWidget *, cchar *menu) { using namespace gradients_names; F1_help_topic = "gradients"; cchar *title = ZTX("Magnify Gradients"); EFgradients.menuname = menu; EFgradients.menufunc = m_gradients; EFgradients.funcname = "gradients"; EFgradients.Farea = 2; // select area usable EFgradients.Frestart = 1; // restart allowed EFgradients.FusePL = 1; // use with paint/lever edits OK EFgradients.Fscript = 1; // scripting supported EFgradients.threadfunc = gradients_thread; if (! edit_setup(EFgradients)) return; // setup: no preview, select area OK /*** _____________________________ | | | | | curve drawing area | | | |_____________________________| low contrast high Amplify ========[]========= Curve File: [ Open ] [ Save ] ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); EFgradients.zd = zd; zdialog_add_widget(zd,"frame","frame","dialog",0,"expand"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labcL","hb1",ZTX("low"),"space=4"); zdialog_add_widget(zd,"label","labcM","hb1",Bcontrast,"expand"); zdialog_add_widget(zd,"label","labcH","hb1",ZTX("high"),"space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labcon","hb2",ZTX("Amplify"),"space=5"); zdialog_add_widget(zd,"hscale","amplify","hb2","0|1|0.005|0","expand"); zdialog_add_widget(zd,"label","ampval","hb2",0,"space=5"); zdialog_add_widget(zd,"hbox","hbcf","dialog",0,"space=8"); zdialog_add_widget(zd,"label","labcf","hbcf",Bcurvefile,"space=5"); zdialog_add_widget(zd,"button","loadcurve","hbcf",Bopen,"space=5"); zdialog_add_widget(zd,"button","savecurve","hbcf",Bsave,"space=5"); GtkWidget *frame = zdialog_widget(zd,"frame"); // set up curve edit spldat *sd = splcurve_init(frame,gradients_curvedit); EFgradients.curves = sd; sd->Nspc = 1; sd->fact[0] = 1; sd->vert[0] = 0; sd->nap[0] = 3; // initial curve anchor points sd->apx[0][0] = 0.01; sd->apy[0][0] = 0.10; sd->apx[0][1] = 0.40; sd->apy[0][1] = 0.30; sd->apx[0][2] = 0.99; sd->apy[0][2] = 0.01; splcurve_generate(sd,0); // generate curve data gradients_initz(zd); // initialize return; } // initialization for new image file void gradients_names::gradients_initz(zdialog *zd) { using namespace gradients_names; int ii, cc, px, py; float b1, *pix1; int jj, sum, limit, condist[100]; ww = E1pxm->ww; // image dimensions hh = E1pxm->hh; cc = ww * hh * sizeof(float); // allocate brightness map memory brmap1 = (float *) zmalloc(cc); brmap2 = (float *) zmalloc(cc); for (ii = 0; ii < 4; ii++) brmap4[ii] = (float *) zmalloc(cc); for (py = 0; py < hh; py++) // map initial image brightness for (px = 0; px < ww; px++) { ii = py * ww + px; pix1 = PXMpix(E1pxm,px,py); b1 = 0.333 * (pix1[0] + pix1[1] + pix1[2]); // pixel brightness 0-255.9 if (b1 < 1) b1 = 1; brmap1[ii] = b1; } for (ii = 0; ii < 100; ii++) condist[ii] = 0; for (py = 1; py < hh; py++) // map contrast distribution for (px = 1; px < ww; px++) { ii = py * ww + px; jj = 0.388 * fabsf(brmap1[ii] - brmap1[ii-1]); // horiz. contrast, ranged 0 - 99 condist[jj]++; jj = 0.388 * fabsf(brmap1[ii] - brmap1[ii-ww]); // vertical condist[jj]++; } sum = 0; limit = 0.99 * 2 * (ww-1) * (hh-1); // find 99th percentile contrast for (ii = 0; ii < 100; ii++) { sum += condist[ii]; if (sum > limit) break; } contrast99 = 255.0 * ii / 100.0; // 0 to 255 if (contrast99 < 4) contrast99 = 4; // rescale low-contrast image zdialog_resize(zd,300,300); zdialog_run(zd,gradients_dialog_event,"save"); // run dialog - parallel amplify = 0; return; } // dialog event and completion callback function int gradients_names::gradients_dialog_event(zdialog *zd, cchar *event) { using namespace gradients_names; spldat *sd = EFgradients.curves; char text[8]; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (zd->zstat) { // dialog complete if (zd->zstat == 1) edit_done(0); else edit_cancel(0); zfree(brmap1); // free memory zfree(brmap2); brmap1 = brmap2 = 0; for (int ii = 0; ii < 4; ii++) { zfree(brmap4[ii]); brmap4[ii] = 0; } return 1; } if (strmatch(event,"apply")) // from script gradients_dialog_event(zd,"amplify"); if (strmatch(event,"blendwidth")) signal_thread(); if (strmatch(event,"amplify")) { // slider value zdialog_fetch(zd,"amplify",amplify); snprintf(text,8,"%.2f",amplify); // numeric feedback zdialog_stuff(zd,"ampval",text); signal_thread(); // trigger update thread } if (strmatch(event,"loadcurve")) { // load saved curve splcurve_load(sd); signal_thread(); return 0; } if (strmatch(event,"savecurve")) { // save curve to file splcurve_save(sd); return 0; } return 0; } // this function is called when the curve is edited void gradients_names::gradients_curvedit(int) { signal_thread(); return; } // thread function void * gradients_names::gradients_thread(void *) { using namespace gradients_names; int ii; while (true) { thread_idle_loop(); // wait for work or exit request for (ii = 0; ii < 4; ii++) // start working threads 1 (4) start_wthread(gradients_wthread1,&Nval[ii]); wait_wthreads(); // wait for completion for (ii = 0; ii < 4; ii++) // start working threads 2 (4) start_wthread(gradients_wthread2,&Nval[ii]); wait_wthreads(); // wait for completion for (ii = 0; ii < NWT; ii++) // start working threads 3 (NWT) start_wthread(gradients_wthread3,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; if (amplify == 0) CEF->Fmods = 0; Fpaint2(); } return 0; // not executed, stop g++ warning } // working threads void * gradients_names::gradients_wthread1(void *arg) { using namespace gradients_names; int ii, kk, bii, pii, dist = 0; int px, px1, px2, pxinc; int py, py1, py2, pyinc; float b1, b4, xval, yval, grad; float amp, con99; spldat *sd = EFgradients.curves; con99 = contrast99; // 99th percentile contrast con99 = 1.0 / con99; // inverted amp = pow(amplify,0.5); // get amplification, 0 to 1 bii = *((int *) arg); // thread 0...3 if (bii == 0) { // direction SE px1 = 1; px2 = ww; pxinc = 1; py1 = 1; py2 = hh; pyinc = 1; pii = - 1 - ww; } else if (bii == 1) { // direction SW px1 = ww-2; px2 = 0; pxinc = -1; py1 = 1; py2 = hh; pyinc = 1; pii = + 1 - ww; } else if (bii == 2) { // direction NE px1 = 1; px2 = ww; pxinc = 1; py1 = hh-2; py2 = 0; pyinc = -1; pii = - 1 + ww; } else { /* bii == 3 */ // direction NW px1 = ww-2; px2 = 0; pxinc = -1; py1 = hh-2; py2 = 0; pyinc = -1; pii = + 1 + ww; } for (ii = 0; ii < ww * hh; ii++) // initial brightness map brmap4[bii][ii] = brmap1[ii]; for (py = py1; py != py2; py += pyinc) // loop all image pixels for (px = px1; px != px2; px += pxinc) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } b1 = brmap1[ii]; // this pixel brightness grad = b1 - brmap1[ii+pii]; // - prior pixel --> gradient xval = fabsf(grad) * con99; // gradient scaled 0 to 1+ kk = 1000.0 * xval; if (kk > 999) kk = 999; yval = 1.0 + 5.0 * sd->yval[0][kk]; // amplifier = 1...6 grad = grad * yval; // magnified gradient b4 = brmap4[bii][ii+pii] + grad; // pixel brightness = prior + gradient b4 = (1.0 - amp) * b1 + amp * b4; // apply amplifier: b4 range = b1 --> b4 brmap4[bii][ii] = b4; // new pixel brightness } exit_wthread(); return 0; // not executed, avoid gcc warning } void * gradients_names::gradients_wthread2(void *arg) { using namespace gradients_names; int index, ii, px, py, dist = 0; float b4; index = *((int *) arg); // thread 0...3 for (py = index; py < hh; py += 4) // loop all image pixels for (px = 0; px < ww; px++) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } b4 = brmap4[0][ii] + brmap4[1][ii] // new brightness = average of four + brmap4[2][ii] + brmap4[3][ii]; // calculated brightness surfaces b4 = 0.25 * b4; if (b4 < 1) b4 = 1; brmap2[ii] = b4; } exit_wthread(); return 0; // not executed, avoid gcc warning } void * gradients_names::gradients_wthread3(void *arg) { using namespace gradients_names; float *pix1, *pix3; int index, ii, px, py, dist = 0; float b1, b2, bf, f1, f2; float red1, green1, blue1, red3, green3, blue3; index = *((int *) arg); for (py = index; py < hh; py += NWT) // loop all image pixels for (px = 0; px < ww; px++) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel b1 = brmap1[ii]; // initial pixel brightness b2 = brmap2[ii]; // calculated new brightness bf = b2 / b1; // brightness ratio red1 = pix1[0]; // input RGB green1 = pix1[1]; blue1 = pix1[2]; red3 = bf * red1; // output RGB if (red3 > 255) red3 = 255; green3 = bf * green1; if (green3 > 255) green3 = 255; blue3 = bf * blue1; if (blue3 > 255) blue3 = 255; if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = sa_blendfunc(dist); // blend changes f2 = 1.0 - f1; red3 = f1 * red3 + f2 * red1; green3 = f1 * green3 + f2 * green1; blue3 = f1 * blue3 + f2 * blue1; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } exit_wthread(); return 0; // not executed, stop g++ warning } /********************************************************************************/ // flatten brightness distribution based on the distribution of nearby zones namespace flatten_names { int Zinit; // zone initialization needed float flatten; // flatten amount, 0 - 100 float deband1, deband2; // deband dark, bright, 0 - 100 int Iww, Ihh; // image dimensions int NZ; // no. of image zones int pNZ; // prior image zones int Zsize, Zrows, Zcols; // zone parameters float *Br; // Br[py][px] pixel brightness int *Zxlo, *Zylo, *Zxhi, *Zyhi; // Zxlo[ii] etc. zone ii pixel range int *Zcen; // Zcen[ii][2] zone ii center (py,px) int16 *Zn; // Zn[py][px][9] 9 nearest zones for pixel: 0-200 (-1 = none) uint8 *Zw; // Zw[py][px][9] zone weights for pixel: 0-100 = 1.0 float *Zff; // Zff[ii][1000] zone ii flatten factors editfunc EFflatten; int dialog_event(zdialog* zd, cchar *event); void doflatten(); void calczones(); void initzones(); void * thread(void *); void * wthread(void *); } // menu function void m_flatten(GtkWidget *, cchar *menu) { using namespace flatten_names; cchar *title = ZTX("Flatten Brightness"); F1_help_topic = "flatten"; Iww = Fpxb->ww; Ihh = Fpxb->hh; if (Iww * Ihh > 227 * MEGA) { zmessageACK(Mwin,"image too big"); edit_cancel(0); return; } EFflatten.menuname = menu; EFflatten.menufunc = m_flatten; EFflatten.funcname = "flatten"; EFflatten.FprevReq = 1; // use preview image EFflatten.Farea = 2; // select area usable EFflatten.Frestart = 1; // restartable EFflatten.FusePL = 1; // use with paint/lever edits OK EFflatten.Fscript = 1; // scripting supported EFflatten.threadfunc = thread; if (! edit_setup(EFflatten)) return; // setup edit Iww = E0pxm->ww; Ihh = E0pxm->hh; if (Iww * Ihh > 227 * MEGA) { zmessageACK(Mwin,"image too big"); edit_cancel(0); return; } /*** ______________________________________ | Flatten Brightness | | | | Zones [ 123 ] [Apply] | | Flatten =========[]============ NN | | Deband Dark =========[]========= NN | | Deband Bright ==========[]====== NN | | | | [Done] [Cancel] | |______________________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); EFflatten.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labreg","hb1",ZTX("Zones"),"space=5"); zdialog_add_widget(zd,"zspin","zones","hb1","1|999|1|100"); // zone range 1-999 18.01 zdialog_add_widget(zd,"button","apply","hb1",Bapply,"space=10"); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"label","labflatten","hb2",Bflatten,"space=5"); zdialog_add_widget(zd,"hscale","flatten","hb2","0|100|1|0","expand"); zdialog_add_widget(zd,"hbox","hb3","dialog"); zdialog_add_widget(zd,"label","labdeband1","hb3",ZTX("Deband Dark"),"space=5"); zdialog_add_widget(zd,"hscale","deband1","hb3","0|100|1|0","expand"); zdialog_add_widget(zd,"hbox","hb4","dialog"); zdialog_add_widget(zd,"label","labdeband2","hb4",ZTX("Deband Bright"),"space=5"); zdialog_add_widget(zd,"hscale","deband2","hb4","0|100|1|0","expand"); zdialog_resize(zd,300,0); zdialog_run(zd,dialog_event,"save"); // run dialog - parallel NZ = pNZ = 40; // default zone count calczones(); // adjust to fit image flatten = deband1 = deband2 = 0; // dialog controls = neutral Zinit = 1; // zone initialization needed Br = 0; // no memory allocated return; } // dialog event and completion function int flatten_names::dialog_event(zdialog *zd, cchar *event) { using namespace flatten_names; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); Zinit = 1; doflatten(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // done edit_fullsize(); // get full size image Zinit = 1; // recalculate zones doflatten(); // flatten full image edit_done(0); // commit edit } else edit_cancel(0); // discard edit if (Br) { zfree(Br); // free memory zfree(Zn); zfree(Zw); zfree(Zxlo); zfree(Zylo); zfree(Zxhi); zfree(Zyhi); zfree(Zcen); zfree(Zff); Br = 0; } return 1; } if (strmatch(event,"apply")) { // [apply] (also from script) dialog_event(zd,"zones"); dialog_event(zd,"flatten"); dialog_event(zd,"deband1"); dialog_event(zd,"deband2"); doflatten(); } if (strmatch(event,"blendwidth")) // select area blendwidth change doflatten(); if (strmatch(event,"zones")) { // get zone count input zdialog_fetch(zd,"zones",NZ); if (NZ == pNZ) return 1; calczones(); // adjust to fit image zdialog_stuff(zd,"zones",NZ); // update dialog Zinit = 1; // zone initialization needed } if (strmatch(event,"flatten")) { zdialog_fetch(zd,"flatten",flatten); // get slider values doflatten(); } if (strmatch(event,"deband1")) { zdialog_fetch(zd,"deband1",deband1); doflatten(); } if (strmatch(event,"deband2")) { zdialog_fetch(zd,"deband2",deband2); doflatten(); } return 1; } // perform the flatten calculations and update the image void flatten_names::doflatten() { if (flatten > 0) { signal_thread(); wait_thread_idle(); // no overlap window update and threads Fpaintnow(); } else edit_reset(); return; } // recalculate zone count based on what fits the image dimensions // done only when the user zone count input changes // outputs: NZ, Zrows, Zcols void flatten_names::calczones() { int gNZ, dNZ; Iww = E1pxm->ww; // image dimensions Ihh = E1pxm->hh; gNZ = NZ; // new zone count goal dNZ = NZ - pNZ; // direction of change while (true) { Zsize = sqrtf(Iww * Ihh / gNZ); // approx. zone size Zrows = Ihh / Zsize + 0.5; // get appropriate rows and cols Zcols = Iww / Zsize + 0.5; if (Zrows < 1) Zrows = 1; // bugfix 18.01 if (Zcols < 1) Zcols = 1; NZ = Zrows * Zcols; // NZ matching rows x cols if (dNZ > 0 && NZ <= pNZ) { // no increase, try again if (NZ >= 999) break; gNZ++; continue; } if (dNZ < 0 && NZ >= pNZ) { // no decrease, try again if (NZ <= 20) break; gNZ--; continue; } if (dNZ == 0) break; if (dNZ > 0 && NZ > pNZ) break; if (dNZ < 0 && NZ < pNZ) break; } pNZ = NZ; // final zone count dNZ = 0; return; } // build up the zones data when NZ or the image size changes // (preview or full size image) void flatten_names::initzones() { int px, py, cx, cy; int rx, ii, jj, kk; int zww, zhh, row, col; float *pix1, bright; float weight[9], sumweight; float D, Dthresh; if (Br) { zfree(Br); // free memory zfree(Zn); zfree(Zw); zfree(Zxlo); zfree(Zylo); zfree(Zxhi); zfree(Zyhi); zfree(Zcen); zfree(Zff); Br = 0; } Iww = E1pxm->ww; // image dimensions Ihh = E1pxm->hh; Br = (float *) zmalloc(Iww * Ihh * sizeof(float)); // allocate memory Zn = (int16 *) zmalloc(Iww * Ihh * 9 * sizeof(int16)); Zw = (uint8 *) zmalloc(Iww * Ihh * 9 * sizeof(uint8)); Zxlo = (int *) zmalloc(NZ * sizeof(int)); Zylo = (int *) zmalloc(NZ * sizeof(int)); Zxhi = (int *) zmalloc(NZ * sizeof(int)); Zyhi = (int *) zmalloc(NZ * sizeof(int)); Zcen = (int *) zmalloc(NZ * 2 * sizeof(int)); Zff = (float *) zmalloc(NZ * 1000 * sizeof(float)); for (py = 0; py < Ihh; py++) // get initial pixel brightness levels for (px = 0; px < Iww; px++) { pix1 = PXMpix(E1pxm,px,py); bright = pixbright(pix1); ii = py * Iww + px; Br[ii] = bright; } zww = Iww / Zcols; // actual zone size zhh = Ihh / Zrows; for (row = 0; row < Zrows; row++) for (col = 0; col < Zcols; col++) // set low/high bounds per zone { ii = row * Zcols + col; Zxlo[ii] = col * zww; Zylo[ii] = row * zhh; Zxhi[ii] = Zxlo[ii] + zww; Zyhi[ii] = Zylo[ii] + zhh; Zcen[2*ii] = Zylo[ii] + zhh/2; Zcen[2*ii+1] = Zxlo[ii] + zww/2; } for (ii = 0; ii < NZ; ii++) // compute brightness distributiion { // for each zone for (jj = 0; jj < 1000; jj++) Zff[1000*ii+jj] = 0; for (py = Zylo[ii]; py < Zyhi[ii]; py++) // brightness distribution for (px = Zxlo[ii]; px < Zxhi[ii]; px++) { pix1 = PXMpix(E1pxm,px,py); kk = pixbright(pix1); if (kk > 255) kk = 255; bright = 3.906 * kk; // 1000 / 256 * kk Zff[1000*ii+int(bright)]++; } for (jj = 1; jj < 1000; jj++) // cumulative brightness distribution Zff[1000*ii+jj] += Zff[1000*ii+jj-1]; for (jj = 0; jj < 1000; jj++) // multiplier per brightness level Zff[1000*ii+jj] = Zff[1000*ii+jj] / (zww*zhh) * 1000 / (jj+1); // to make distribution flat } for (py = 0; py < Ihh; py++) // set 9 nearest zones per pixel for (px = 0; px < Iww; px++) { rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py row = py / zhh; // zone containing pixel col = px / zww; ii = 0; for (jj = row-1; jj <= row+1; jj++) // loop 3x3 surrounding zones for (kk = col-1; kk <= col+1; kk++) { if (jj >= 0 && jj < Zrows && kk >= 0 && kk < Zcols) // zone is not off the edge Zn[rx+ii] = jj * Zcols + kk; else Zn[rx+ii] = -1; // edge row/col: missing neighbor ii++; } } if (zww < zhh) // pixel to zone distance threshold Dthresh = 1.5 * zww; // area influence = 0 beyond this distance else Dthresh = 1.5 * zhh; for (py = 0; py < Ihh; py++) // set zone weights per pixel for (px = 0; px < Iww; px++) { rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py for (ii = 0; ii < 9; ii++) // distance to each zone center { jj = Zn[rx+ii]; if (jj == -1) { // missign zone weight[ii] = 0; // weight = 0 continue; } cy = Zcen[2*jj]; cx = Zcen[2*jj+1]; D = sqrtf((py-cy)*(py-cy) + (px-cx)*(px-cx)); // distance from pixel to zone center D = D / Dthresh; if (D > 1) D = 1; // zone influence reaches zero at Dthresh weight[ii] = 1.0 - D; } sumweight = 0; for (ii = 0; ii < 9; ii++) // zone weights based on distance sumweight += weight[ii]; for (ii = 0; ii < 9; ii++) // normalize weights, sum = 1.0 Zw[rx+ii] = 100 * weight[ii] / sumweight; // 0-100 = 1.0 } return; } // adjust brightness distribution thread function void * flatten_names::thread(void *) { using namespace flatten_names; while (true) { thread_idle_loop(); // wait for work or exit request if (Zinit) initzones(); // reinitialize zones Zinit = 0; for (int ii = 0; ii < NWT; ii++) // start worker thread per processor core start_wthread(wthread,&Nval[ii]); wait_wthreads(); // wait for completion if (! flatten) CEF->Fmods = 0; // no modification else { CEF->Fmods++; // image modified, not saved CEF->Fsaved = 0; } } return 0; // not executed, stop g++ warning } // worker thread for each CPU processor core void * flatten_names::wthread(void *arg) { using namespace flatten_names; int index = *((int *) (arg)); int px, py, rx, ii, jj, dist = 0; float *pix1, *pix3; float fnew1, fnew2, fnew, fold; float dold, dnew, cmax; float red1, green1, blue1, red3, green3, blue3; float FF, weight, bright; for (py = index; py < Ihh; py += NWT) // loop all image pixels for (px = 0; px < Iww; px++) { if (sa_stat == 3) { // select area active ii = py * E1pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; bright = 0.976 * red1 + 2.539 * green1 + 0.39 * blue1; // brightness scaled 0-999.9 18.01 rx = (py * Iww + px) * 9; // index for 9 nearest zones to px/py FF = 0; for (ii = 0; ii < 9; ii++) { // loop 9 nearest zones weight = Zw[rx+ii]; if (weight > 0) { // 0-100 = 1.0 jj = Zn[rx+ii]; FF += 0.01 * weight * Zff[1000*jj+int(bright)]; // sum weight * flatten factor } } red3 = FF * red1; // fully flattened brightness green3 = FF * green1; blue3 = FF * blue1; fnew1 = 1 - 0.01 * deband1 * 0.001 * (1000 - bright); // attenuate dark pixels fnew2 = 1 - 0.01 * deband2 * 0.001 * bright; // attenuate bright pixels fnew = fnew1 * fnew2; fnew = fnew * 0.01 * flatten; // how much to flatten, 0 to 1 fold = 1.0 - fnew; // how much to retain, 1 to 0 red3 = fnew * red3 + fold * red1; // blend new and old brightness green3 = fnew * green3 + fold * green1; blue3 = fnew * blue3 + fold * blue1; if (sa_stat == 3 && dist < sa_blend) { // select area is active, dnew = sa_blendfunc(dist); // blend changes over sa_blend dold = 1.0 - dnew; red3 = dnew * red3 + dold * red1; green3 = dnew * green3 + dold * green1; blue3 = dnew * blue3 + dold * blue1; } cmax = red3; // stop overflow, keep color balance if (green3 > cmax) cmax = green3; if (blue3 > cmax) cmax = blue3; if (cmax > 255.9) { cmax = 255.9 / cmax; red3 = red3 * cmax; green3 = green3 * cmax; blue3 = blue3 * cmax; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Retinex function // Global: rescale RGB values based on entire image - eliminate color caste and reduce fog/haze. // Zonal: rescale RGB values based on surrounding zones: increase local brightness range. // Transfer color (RGB ratios) from global to zonal result. namespace retinex_names { editfunc EFretinex; // edit function data int e3ww, e3hh; cchar *thread_command; // "global" or "zonal" float Rdark, Gdark, Bdark; // global parameters float Rbrite, Gbrite, Bbrite; float Rmpy, Gmpy, Bmpy; float pRdark, pGdark, pBdark; // prior values float pRbrite, pGbrite, pBbrite; float pRmpy, pGmpy, pBmpy; int Fbrightpoint, Fdarkpoint; int zonesize, pzonesize; // zonal parameters float blend, reducebright; float *LminR, *LminG, *LminB, *LmaxR, *LmaxG, *LmaxB; // RGB range float *Lmin2R, *Lmin2G, *Lmin2B, *Lmax2R, *Lmax2G, *Lmax2B; } // menu function void m_retinex(GtkWidget *, cchar *menu) // new 18.01 { using namespace retinex_names; int retinex_dialog_event(zdialog *zd, cchar *event); void retinex_mousefunc(); void * retinex_thread(void *); F1_help_topic = "retinex"; EFretinex.menuname = menu; EFretinex.menufunc = m_retinex; EFretinex.funcname = "retinex"; // function name EFretinex.FprevReq = 1; // use preview EFretinex.Farea = 2; // select area usable EFretinex.Frestart = 1; // allow restart EFretinex.Fscript = 1; // scripting supported EFretinex.threadfunc = retinex_thread; // thread function EFretinex.mousefunc = retinex_mousefunc; // mouse function if (! edit_setup(EFretinex)) return; // setup edit e3ww = E3pxm->ww; // image size e3hh = E3pxm->hh; E8pxm = E9pxm = 0; int cc = e3ww * e3hh; LminR = (float *) zmalloc(cc * sizeof(float)); // allocate zone RGB range LminG = (float *) zmalloc(cc * sizeof(float)); LminB = (float *) zmalloc(cc * sizeof(float)); LmaxR = (float *) zmalloc(cc * sizeof(float)); LmaxG = (float *) zmalloc(cc * sizeof(float)); LmaxB = (float *) zmalloc(cc * sizeof(float)); /*** ______________________________________ | Retinex | | | | Red Green Blue +/- | | Dark Point: [__] [__] [__] [__] | 0 ... 255 neutral point is 0 | Bright Point: [__] [__] [__] [__] | 0 ... 255 neutral point is 255 | Multiplyer: [__] [__] [__] [__] | 0 ... 5 neutral point is 1 | | | [auto] brightness rescale | | [x] bright point [x] dark point | mouse click spots | - - - - - - - - - - - - - - - - - - | | zone size: [___] [apply] | | - - - - - - - - - - - - - - - - - - | | blend: =======[]=================== | | reduce bright: ==========[]======== | | | | [reset] [done] [cancel] | |______________________________________| ***/ zdialog *zd = zdialog_new("Retinex",Mwin,Breset,Bdone,Bcancel,null); EFretinex.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","space","hb1","","space=5"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog"); zdialog_add_widget(zd,"label","space","hb1","","space=5"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog"); zdialog_add_widget(zd,"label","space","hb1","","space=5"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"homog"); zdialog_add_widget(zd,"label","space","hb1","","space=5"); zdialog_add_widget(zd,"vbox","vb4","hb1",0,"homog"); zdialog_add_widget(zd,"label","space","hb1","","space=5"); zdialog_add_widget(zd,"vbox","vb5","hb1",0,"homog"); zdialog_add_widget(zd,"label","labglobal","vb1","Global Retinex"); zdialog_add_widget(zd,"label","labdark","vb1",ZTX("Dark Point")); zdialog_add_widget(zd,"label","labbrite","vb1",ZTX("Bright Point")); zdialog_add_widget(zd,"label","labmpy","vb1",ZTX("Multiplyer")); zdialog_add_widget(zd,"label","labred","vb2",Bred); zdialog_add_widget(zd,"zspin","Rdark","vb2","0|255|1|0"); zdialog_add_widget(zd,"zspin","Rbrite","vb2","0|255|1|255"); zdialog_add_widget(zd,"zspin","Rmpy","vb2","0.1|5|0.01|1"); zdialog_add_widget(zd,"label","labgreen","vb3",Bgreen); zdialog_add_widget(zd,"zspin","Gdark","vb3","0|255|1|0"); zdialog_add_widget(zd,"zspin","Gbrite","vb3","0|255|1|255"); zdialog_add_widget(zd,"zspin","Gmpy","vb3","0.1|5|0.01|1"); zdialog_add_widget(zd,"label","labred","vb4",Bblue); zdialog_add_widget(zd,"zspin","Bdark","vb4","0|255|1|0"); zdialog_add_widget(zd,"zspin","Bbrite","vb4","0|255|1|255"); zdialog_add_widget(zd,"zspin","Bmpy","vb4","0.1|5|0.01|1"); zdialog_add_widget(zd,"label","laball","vb5",Ball); zdialog_add_widget(zd,"zspin","dark+-","vb5","-1|+1|1|0"); zdialog_add_widget(zd,"zspin","brite+-","vb5","-1|+1|1|0"); zdialog_add_widget(zd,"zspin","mpy+-","vb5","-1|+1|1|0"); zdialog_add_widget(zd,"hbox","hbauto","dialog"); zdialog_add_widget(zd,"button","autoscale","hbauto",Bauto,"space=3"); zdialog_add_widget(zd,"label","labauto","hbauto",ZTX("brightness rescale"),"space=5"); zdialog_add_widget(zd,"hbox","hbclicks","dialog"); zdialog_add_widget(zd,"check","brightpoint","hbclicks",ZTX("click bright point"),"space=3"); zdialog_add_widget(zd,"check","darkpoint","hbclicks",ZTX("click dark point"),"space=5"); zdialog_add_widget(zd,"hsep","sep1","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbz","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labzr","hbz","Zonal Retinex","space=5"); zdialog_add_widget(zd,"label","labzs","hbz","zone size:","space=5"); zdialog_add_widget(zd,"zspin","zone size","hbz","10|500|1|200"); zdialog_add_widget(zd,"button","apply","hbz",Bapply,"space=3"); zdialog_add_widget(zd,"hsep","sep2","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbb","dialog"); zdialog_add_widget(zd,"label","labb","hbb",ZTX("blend"),"space=5"); zdialog_add_widget(zd,"hscale","blend","hbb","0|1.0|0.01|1.0","expand"); zdialog_add_widget(zd,"hbox","hbrd","dialog"); zdialog_add_widget(zd,"label","labrd","hbrd",ZTX("reduce bright"),"space=5"); zdialog_add_widget(zd,"hscale","reduce bright","hbrd","0|1.0|0.01|0.0","expand"); zdialog_run(zd,retinex_dialog_event,"save"); // run dialog - parallel zdialog_send_event(zd,"reset"); return; } // dialog event and completion function int retinex_dialog_event(zdialog *zd, cchar *event) { using namespace retinex_names; void retinex_mousefunc(); void retinex_fullsize(); int adddark, addbrite, addmpy; int Fchange = 0; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"reset")) zd->zstat = 1; // initz. if (strmatch(event,"fullsize")) { // from select area retinex_fullsize(); // apply to full size image return 1; } if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; // keep dialog active Rdark = Gdark = Bdark = 0; // set neutral values Rbrite = Gbrite = Bbrite = 255; pRdark = pGdark = pBdark = 0; // prior values = same pRbrite = pGbrite = pBbrite = 255; // (no change) Rmpy = Gmpy = Bmpy = 1.0; Fbrightpoint = Fdarkpoint = 0; zonesize = pzonesize = 0; blend = 1.0; reducebright = 0.0; zdialog_stuff(zd,"Rdark",0); // stuff neutral values into dialog zdialog_stuff(zd,"Gdark",0); zdialog_stuff(zd,"Bdark",0); zdialog_stuff(zd,"Rbrite",255); zdialog_stuff(zd,"Gbrite",255); zdialog_stuff(zd,"Bbrite",255); zdialog_stuff(zd,"Rmpy",1.0); zdialog_stuff(zd,"Gmpy",1.0); zdialog_stuff(zd,"Bmpy",1.0); zdialog_stuff(zd,"brightpoint",0); zdialog_stuff(zd,"darkpoint",0); edit_reset(); if (E8pxm) PXM_free(E8pxm); // free memory if (E9pxm) PXM_free(E9pxm); E8pxm = E9pxm = 0; return 1; } else if (zd->zstat == 2) { // done retinex_fullsize(); // apply to full size image edit_done(0); // commit edit if (E8pxm) PXM_free(E8pxm); // free memory if (E9pxm) PXM_free(E9pxm); E8pxm = E9pxm = 0; zfree(LminR); zfree(LminG); zfree(LminB); zfree(LmaxR); zfree(LmaxG); zfree(LmaxB); return 1; } else { edit_cancel(0); // discard edit if (E8pxm) PXM_free(E8pxm); // free memory if (E9pxm) PXM_free(E9pxm); E8pxm = E9pxm = 0; zfree(LminR); zfree(LminG); zfree(LminB); zfree(LmaxR); zfree(LmaxG); zfree(LmaxB); return 1; } } Fbrightpoint = Fdarkpoint = 0; // disable mouse if (strmatch(event,"autoscale")) // auto set dark and bright points { edit_reset(); thread_command = "autoscale"; // get dark/bright limits from signal_thread(); // darkest/brightest pixels wait_thread_idle(); zdialog_stuff(zd,"Rdark",Rdark); // update dialog widgets zdialog_stuff(zd,"Gdark",Gdark); zdialog_stuff(zd,"Bdark",Bdark); zdialog_stuff(zd,"Rbrite",Rbrite); zdialog_stuff(zd,"Gbrite",Gbrite); zdialog_stuff(zd,"Bbrite",Bbrite); zdialog_stuff(zd,"Rmpy",1.0); zdialog_stuff(zd,"Gmpy",1.0); zdialog_stuff(zd,"Bmpy",1.0); zdialog_stuff(zd,"brightpoint",0); zdialog_stuff(zd,"darkpoint",0); pRdark = Rdark; // prior values = same pGdark = Gdark; pBdark = Bdark; pRbrite = Rbrite; pGbrite = Gbrite; pBbrite = Bbrite; pRmpy = Rmpy; pGmpy = Gmpy; pBmpy = Bmpy; return 1; } if (strmatch(event,"brightpoint")) { zdialog_fetch(zd,"brightpoint",Fbrightpoint); if (Fbrightpoint) { zdialog_stuff(zd,"darkpoint",0); Fdarkpoint = 0; } } if (strmatch(event,"darkpoint")) { zdialog_fetch(zd,"darkpoint",Fdarkpoint); if (Fdarkpoint) { zdialog_stuff(zd,"brightpoint",0); Fbrightpoint = 0; } } if (strstr("brightpoint darkpoint",event)) { // brightpoint or darkpoint takeMouse(retinex_mousefunc,dragcursor); // connect mouse function return 1; } else { zdialog_stuff(zd,"brightpoint",0); // reset zdialog checkboxes zdialog_stuff(zd,"darkpoint",0); } adddark = addbrite = addmpy = 0; if (strmatch(event,"dark+-")) zdialog_fetch(zd,"dark+-",adddark); // [+/-] button if (strmatch(event,"brite+-")) zdialog_fetch(zd,"brite+-",addbrite); if (strmatch(event,"mpy+-")) zdialog_fetch(zd,"mpy+-",addmpy); if (adddark) { zdialog_fetch(zd,"Rdark",Rdark); // increment dark levels zdialog_fetch(zd,"Gdark",Gdark); zdialog_fetch(zd,"Bdark",Bdark); Rdark += adddark; Gdark += adddark; Bdark += adddark; if (Rdark < 0) Rdark = 0; if (Gdark < 0) Gdark = 0; if (Bdark < 0) Bdark = 0; if (Rdark > 255) Rdark = 255; if (Gdark > 255) Gdark = 255; if (Bdark > 255) Bdark = 255; zdialog_stuff(zd,"Rdark",Rdark); zdialog_stuff(zd,"Gdark",Gdark); zdialog_stuff(zd,"Bdark",Bdark); } if (addbrite) { // increment bright levels zdialog_fetch(zd,"Rbrite",Rbrite); zdialog_fetch(zd,"Gbrite",Gbrite); zdialog_fetch(zd,"Bbrite",Bbrite); Rbrite += addbrite; Gbrite += addbrite; Bbrite += addbrite; if (Rbrite < 0) Rbrite = 0; if (Gbrite < 0) Gbrite = 0; if (Bbrite < 0) Bbrite = 0; if (Rbrite > 255) Rbrite = 255; if (Gbrite > 255) Gbrite = 255; if (Bbrite > 255) Bbrite = 255; zdialog_stuff(zd,"Rbrite",Rbrite); zdialog_stuff(zd,"Gbrite",Gbrite); zdialog_stuff(zd,"Bbrite",Bbrite); } if (addmpy) { // increment mpy factors zdialog_fetch(zd,"Rmpy",Rmpy); zdialog_fetch(zd,"Gmpy",Gmpy); zdialog_fetch(zd,"Bmpy",Bmpy); Rmpy += 0.01 * addmpy; Gmpy += 0.01 * addmpy; Bmpy += 0.01 * addmpy; if (Rmpy < 0.1) Rmpy = 0.1; if (Gmpy < 0.1) Gmpy = 0.1; if (Bmpy < 0.1) Bmpy = 0.1; if (Rmpy > 5) Rmpy = 5; if (Gmpy > 5) Gmpy = 5; if (Bmpy > 5) Bmpy = 5; zdialog_stuff(zd,"Rmpy",Rmpy); zdialog_stuff(zd,"Gmpy",Gmpy); zdialog_stuff(zd,"Bmpy",Bmpy); } zdialog_fetch(zd,"Rdark",Rdark); // get all params zdialog_fetch(zd,"Gdark",Gdark); zdialog_fetch(zd,"Bdark",Bdark); zdialog_fetch(zd,"Rbrite",Rbrite); zdialog_fetch(zd,"Gbrite",Gbrite); zdialog_fetch(zd,"Bbrite",Bbrite); zdialog_fetch(zd,"Rmpy",Rmpy); zdialog_fetch(zd,"Gmpy",Gmpy); zdialog_fetch(zd,"Bmpy",Bmpy); Fchange = 0; if (Rdark != pRdark) Fchange = 1; // detect changed params if (Gdark != pGdark) Fchange = 1; if (Bdark != pBdark) Fchange = 1; if (Rbrite != pRbrite) Fchange = 1; if (Gbrite != pGbrite) Fchange = 1; if (Bbrite != pBbrite) Fchange = 1; if (Rmpy != pRmpy) Fchange = 1; if (Gmpy != pGmpy) Fchange = 1; if (Bmpy != pBmpy) Fchange = 1; pRdark = Rdark; // remember values for change detection pGdark = Gdark; pBdark = Bdark; pRbrite = Rbrite; pGbrite = Gbrite; pBbrite = Bbrite; pRmpy = Rmpy; pGmpy = Gmpy; pBmpy = Bmpy; if (Fchange) { // global params changed thread_command = "global"; signal_thread(); // update image wait_thread_idle(); Fchange = 0; } if (strmatch(event,"apply")) { // new zone size zdialog_fetch(zd,"zone size",zonesize); thread_command = "zonal"; signal_thread(); // update image wait_thread_idle(); } if (strstr("blend, reduce bright",event)) { // blend params changed zdialog_fetch(zd,"blend",blend); zdialog_fetch(zd,"reduce bright",reducebright); thread_command = "blend"; signal_thread(); // update image wait_thread_idle(); } return 1; } // get dark point or bright point from mouse click position void retinex_mousefunc() { using namespace retinex_names; int px, py, dx, dy; float red, green, blue; float *ppix; char mousetext[60]; zdialog *zd = EFretinex.zd; if (! zd) { freeMouse(); return; } if (! Fbrightpoint && ! Fdarkpoint) { freeMouse(); return; } if (! LMclick) return; LMclick = 0; px = Mxclick; // mouse click position py = Myclick; if (px < 2) px = 2; // pull back from edge if (px > E3pxm->ww-3) px = E3pxm->ww-3; if (py < 2) py = 2; if (py > E3pxm->hh-3) py = E3pxm->hh-3; red = green = blue = 0; for (dy = -1; dy <= 1; dy++) // 3x3 block around mouse position for (dx = -1; dx <= 1; dx++) { ppix = PXMpix(E1pxm,px+dx,py+dy); // input image red += ppix[0]; green += ppix[1]; blue += ppix[2]; } red = red / 9.0; // mean RGB levels green = green / 9.0; blue = blue / 9.0; snprintf(mousetext,60,"3x3 pixels RGB: %.0f %.0f %.0f \n",red,green,blue); poptext_mouse(Mwin,mousetext,10,10,0,3); if (Fbrightpoint) { // click pixel is new bright point Rbrite= red; Gbrite = green; Bbrite = blue; zdialog_stuff(zd,"Rbrite",Rbrite); // stuff values into dialog zdialog_stuff(zd,"Gbrite",Gbrite); zdialog_stuff(zd,"Bbrite",Bbrite); } if (Fdarkpoint) { // click pixel is new dark point Rdark = red; Gdark = green; Bdark = blue; zdialog_stuff(zd,"Rdark",Rdark); // stuff values into dialog zdialog_stuff(zd,"Gdark",Gdark); zdialog_stuff(zd,"Bdark",Bdark); } if (Fbrightpoint || Fdarkpoint) { thread_command = "global"; signal_thread(); // trigger image update wait_thread_idle(); } return; } // edit fullsize - scale zones from preview to full size image void retinex_fullsize() { using namespace retinex_names; int pww, phh, cc, ii, kk; int px, py, qx, qy; int zonesize2; if (! CEF->Fpreview) return; // already full-size pww = e3ww; // save preview image size phh = e3hh; zonesize2 = zonesize; // preserve zone size edit_fullsize(); // get full size image e3ww = E3pxm->ww; // new image size e3hh = E3pxm->hh; thread_command = "global"; signal_thread(); // update image wait_thread_idle(); Lmin2R = LminR; // preview zone limits Lmin2G = LminG; Lmin2B = LminB; Lmax2R = LmaxR; Lmax2G = LmaxG; Lmax2B = LmaxB; cc = e3ww * e3hh; LminR = (float *) zmalloc(cc * sizeof(float)); // allocate full-size zone limits LminG = (float *) zmalloc(cc * sizeof(float)); LminB = (float *) zmalloc(cc * sizeof(float)); LmaxR = (float *) zmalloc(cc * sizeof(float)); LmaxG = (float *) zmalloc(cc * sizeof(float)); LmaxB = (float *) zmalloc(cc * sizeof(float)); for (py = 0; py < e3hh; py++) // loop full size image pixels for (px = 0; px < e3ww; px++) { qy = py * 1.0 * phh / e3hh; // corresp. preview image pixels qx = px * 1.0 * pww / e3ww; ii = py * e3ww + px; // copy the preview zone limits to kk = qy * pww + qx; // corresp. full-size zone limits LminR[ii] = Lmin2R[kk]; LminG[ii] = Lmin2G[kk]; LminB[ii] = Lmin2B[kk]; LmaxR[ii] = Lmax2R[kk]; LmaxG[ii] = Lmax2G[kk]; LmaxB[ii] = Lmax2B[kk]; } zfree(Lmin2R); // free preview zone limits zfree(Lmin2G); zfree(Lmin2B); zfree(Lmax2R); zfree(Lmax2G); zfree(Lmax2B); zonesize = pzonesize = zonesize2; // restore zone size thread_command = "zonal"; signal_thread(); // update image wait_thread_idle(); return; } // thread function - multiple working threads to update image void * retinex_thread(void *) { using namespace retinex_names; void * Aretinex_wthread(void *arg); // worker threads void * Gretinex_wthread(void *arg); void * Zretinex_wthread(void *arg); void * Bretinex_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request if (strmatch(thread_command,"autoscale")) { Aretinex_wthread(0); // worker thread for autoscale retinex thread_command = "global"; } if (strmatch(thread_command,"global")) { for (int ii = 0; ii < NWT; ii++) // worker thread for global retinex start_wthread(Gretinex_wthread,&Nval[ii]); wait_wthreads(); // wait for completion if (E8pxm) PXM_free(E8pxm); // E8 image = global retinex output E8pxm = PXM_copy(E3pxm); zonesize = pzonesize = 0; // invalidate zonal retinex if (E9pxm) PXM_free(E9pxm); E9pxm = 0; thread_command = "blend"; // auto blend after global retinex } if (strmatch(thread_command,"zonal")) // worker thread for zonal retinex { Zretinex_wthread(0); if (E9pxm) PXM_free(E9pxm); // E9 image = zonal retinex output E9pxm = PXM_copy(E3pxm); thread_command = "blend"; // auto blend after zonal retinex } if (strmatch(thread_command,"blend")) { for (int ii = 0; ii < NWT; ii++) // worker thread for blend retinex start_wthread(Bretinex_wthread,&Nval[ii]); wait_wthreads(); // wait for completion } CEF->Fmods++; // image modified CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop warning } // worker thread function - autoscale retinex void * Aretinex_wthread(void *) { using namespace retinex_names; int ii, dist = 0; int px, py, dx, dy; int red, green, blue; float *pix1; Rdark = Gdark = Bdark = 255; Rbrite = Gbrite = Bbrite = 0; Rmpy = Gmpy = Bmpy = 1.0; Fbrightpoint = Fdarkpoint = 0; for (py = 1; py < E1pxm->hh-1; py++) for (px = 1; px < E1pxm->ww-1; px++) { if (sa_stat == 3) { // select area active ii = py * E1pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } red = green = blue = 0; for (dy = -1; dy <= 1; dy++) // 3x3 block around mouse position for (dx = -1; dx <= 1; dx++) { pix1 = PXMpix(E1pxm,px+dx,py+dy); // input image red += pix1[0]; green += pix1[1]; blue += pix1[2]; } red = red / 9.0; // mean RGB levels green = green / 9.0; blue = blue / 9.0; if (red < Rdark) Rdark = red; // update limits if (green < Gdark) Gdark = green; if (blue < Bdark) Bdark = blue; if (red > Rbrite) Rbrite = red; if (green > Gbrite) Gbrite = green; if (blue > Bbrite) Bbrite = blue; } return 0; // not executed, avoid gcc warning } // worker thread function - global retinex void * Gretinex_wthread(void *arg) { using namespace retinex_names; int ii, index, dist = 0; int px, py; float R1, G1, B1, R3, G3, B3; float f1, f2, F, cmax; float *pix1, *pix3; index = *((int *) arg); for (py = index; py < E3pxm->hh; py += NWT) // loop all image pixels for (px = 0; px < E3pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel R1 = pix1[0]; // input RGB values G1 = pix1[1]; B1 = pix1[2]; R1 = 255 * (R1 - Rdark) / (Rbrite - Rdark); // rescale for full 0-255 range G1 = 255 * (G1 - Gdark) / (Gbrite - Gdark); B1 = 255 * (B1 - Bdark) / (Bbrite - Bdark); if (R1 < 0) R1 = 0; if (G1 < 0) G1 = 0; if (B1 < 0) B1 = 0; R3 = R1 * Rmpy; G3 = G1 * Gmpy; B3 = B1 * Bmpy; if (R3 > 255 || G3 > 255 || B3 > 255) { // stop overflow cmax = R3; if (G3 > cmax) cmax = G3; if (B3 > cmax) cmax = B3; F = 255 / cmax; R3 *= F; G3 *= F; B3 *= F; } if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; R3 = f1 * R3 + f2 * R1; G3 = f1 * G3 + f2 * G1; B3 = f1 * B3 + f2 * B1; } pix3[0] = R3; // output RGB values pix3[1] = G3; pix3[2] = B3; } exit_wthread(); return 0; // not executed, avoid gcc warning } // worker thread function - zonal retinex void * Zretinex_wthread(void *) { using namespace retinex_names; void * Zretinex_wthread1(void *arg); // worker threads void * Zretinex_wthread2(void *arg); void * Zretinex_wthread3(void *arg); void * Zretinex_wthread4(void *arg); int ii, cc; if (zonesize == 0) return 0; // no zone size set if (zonesize == pzonesize) goto rescale; // no change, can skip pzonesize = zonesize; // compute luminance range for zone surrounding each pixel Fbusy_goal = e3ww * e3hh; // track progress Fbusy_done = 0; for (ii = 0; ii < NWT; ii++) // start worker threads start_wthread(Zretinex_wthread1,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = 0; // "anneal" the zone luminance values with neighboring zones cc = e3ww * e3hh; Lmin2R = (float *) zmalloc(cc * sizeof(float)); Lmin2G = (float *) zmalloc(cc * sizeof(float)); Lmin2B = (float *) zmalloc(cc * sizeof(float)); Lmax2R = (float *) zmalloc(cc * sizeof(float)); Lmax2G = (float *) zmalloc(cc * sizeof(float)); Lmax2B = (float *) zmalloc(cc * sizeof(float)); Fbusy_goal = e3ww * e3hh; // track progress Fbusy_done = 0; for (ii = 0; ii < NWT; ii++) // start worker threads start_wthread(Zretinex_wthread2,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = 0; zfree(LminR); // replace zone luminance values zfree(LminG); zfree(LminB); zfree(LmaxR); zfree(LmaxG); zfree(LmaxB); LminR = Lmin2R; LminG = Lmin2G; LminB = Lmin2B; LmaxR = Lmax2R; LmaxG = Lmax2G; LmaxB = Lmax2B; rescale: // rescale the luminance of each pixel so that the full 0-255 range is used for (ii = 0; ii < NWT; ii++) // start worker threads start_wthread(Zretinex_wthread3,&Nval[ii]); wait_wthreads(); // wait for completion return 0; } // zonal worker thread 1 - compute RGB range for each zone void * Zretinex_wthread1(void *arg) { using namespace retinex_names; int index = *((int *) arg); int px, py, qx, qy; int xlo, xhi, ylo, yhi; int ii, rad, dist = 0; float *pix1; float lminR, lminG, lminB, lmaxR, lmaxG, lmaxB; rad = zonesize / 2; for (py = index; py < e3hh; py += NWT) // loop each image pixel for (px = 0; px < e3ww; px++) { if (sa_stat == 3) { // select area active ii = py * e3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } lminR = lminG = lminB = 255; lmaxR = lmaxG = lmaxB = 0; ylo = py - rad; if (ylo < 0) ylo = 0; yhi = py + rad; if (yhi > e3hh) yhi = e3hh; xlo = px - rad; if (xlo < 0) xlo = 0; xhi = px + rad; if (xhi > e3ww) xhi = e3ww; for (qy = ylo; qy < yhi; qy += 2) // find min and max RGB for (qx = xlo; qx < xhi; qx += 2) // for zone surrounding pixel { if (sa_stat == 3) { // select area active ii = qy * e3ww + qx; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } if (E8pxm) pix1 = PXMpix(E8pxm,qx,qy); // input image from global retinex else pix1 = PXMpix(E1pxm,qx,qy); // else original input image if (pix1[0] < lminR) lminR = pix1[0]; if (pix1[0] > lmaxR) lmaxR = pix1[0]; if (pix1[1] < lminG) lminG = pix1[1]; if (pix1[1] > lmaxG) lmaxG = pix1[1]; if (pix1[2] < lminB) lminB = pix1[2]; if (pix1[2] > lmaxB) lmaxB = pix1[2]; } ii = py * e3ww + px; LminR[ii] = lminR; LmaxR[ii] = lmaxR; LminG[ii] = lminG; LmaxG[ii] = lmaxG; LminB[ii] = lminB; LmaxB[ii] = lmaxB; Fbusy_done++; } exit_wthread(); return 0; // not executed, avoid gcc warning } // zonal worker thread 2 - anneal zone RGB values void * Zretinex_wthread2(void *arg) { using namespace retinex_names; int index = *((int *) arg); int px, py, qx, qy; int ylo, yhi; int rad, ii, lsum; double lminR, lminG, lminB, lmaxR, lmaxG, lmaxB; rad = zonesize; for (py = index; py < e3hh; py += NWT) // loop all image pixels for (px = 0; px < e3ww; px++) { if (px == 0) // start new row { lminR = lminG = lminB = 0; lmaxR = lmaxG = lmaxB = 0; lsum = 0; ylo = py - rad; if (ylo < 0) ylo = 0; yhi = py + rad; if (yhi > e3hh) yhi = e3hh; for (qy = ylo; qy < yhi; qy++) // comute min/max RGB sums for (qx = 0; qx < px+rad; qx++) // (sum all columns for row) { ii = qy * e3ww + qx; lminR += LminR[ii]; lminG += LminG[ii]; lminB += LminB[ii]; lmaxR += LmaxR[ii]; lmaxG += LmaxG[ii]; lmaxB += LmaxB[ii]; lsum++; } } else // continue row { qx = px-rad-1; // subtract column lost if (qx >= 0) { for (qy = ylo; qy < yhi; qy++) { ii = qy * e3ww + qx; lminR -= LminR[ii]; lminG -= LminG[ii]; lminB -= LminB[ii]; lmaxR -= LmaxR[ii]; lmaxG -= LmaxG[ii]; lmaxB -= LmaxB[ii]; lsum--; } } qx = px+rad-1; // add column gained if (qx < e3ww) { for (qy = ylo; qy < yhi; qy++) { ii = qy * e3ww + qx; lminR += LminR[ii]; lminG += LminG[ii]; lminB += LminB[ii]; lmaxR += LmaxR[ii]; lmaxG += LmaxG[ii]; lmaxB += LmaxB[ii]; lsum++; } } } ii = py * e3ww + px; Lmin2R[ii] = lminR / lsum; // mean RGB Lmin2G[ii] = lminG / lsum; Lmin2B[ii] = lminB / lsum; Lmax2R[ii] = lmaxR / lsum; Lmax2G[ii] = lmaxG / lsum; Lmax2B[ii] = lmaxB / lsum; Fbusy_done++; } exit_wthread(); return 0; // not executed, avoid gcc warning } // zonal worker thread 3 // rescale each pixel RGB value so that the full 0-255 range is used void * Zretinex_wthread3(void *arg) { using namespace retinex_names; int index = *((int *) arg); int px, py; int ii, dist = 0; float *pix1, *pix3; float L1R, L1G, L1B, L2R, L2G, L2B; float lminR, lminG, lminB, lmaxR, lmaxG, lmaxB; float R1, G1, B1, R3, G3, B3; float range, F1; for (py = index; py < e3hh; py += NWT) // loop all image pixels for (px = 0; px < e3ww; px++) { if (sa_stat == 3) { // select area active ii = py * e3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } if (E8pxm) pix1 = PXMpix(E8pxm,px,py); // input image from global retinex else pix1 = PXMpix(E1pxm,px,py); // else original input image pix3 = PXMpix(E3pxm,px,py); // output image pixel ii = py * e3ww + px; lminR = LminR[ii]; // RGB range of pixel zone lminG = LminG[ii]; lminB = LminB[ii]; lmaxR = LmaxR[ii]; lmaxG = LmaxG[ii]; lmaxB = LmaxB[ii]; L1R = pix1[0]; // rescale RGB for full range if (L1R < lminR) L1R = lminR; range = (lmaxR - lminR); if (range > 1) L2R = 255.0 * (L1R - lminR) / range; else L2R = L1R; L1G = pix1[1]; if (L1G < lminG) L1G = lminG; range = (lmaxG - lminG); if (range > 1) L2G = 255.0 * (L1G - lminG) / range; else L2G = L1G; L1B = pix1[2]; if (L1B < lminB) L1B = lminB; range = (lmaxB - lminB); if (range > 1) L2B = 255.0 * (L1B - lminB) / range; else L2B = L1B; if (L1R < 1) L1R = 1; if (L1G < 1) L1G = 1; if (L1B < 1) L1B = 1; R1 = pix1[0]; // input RGB values G1 = pix1[1]; B1 = pix1[2]; R3 = L2R/L1R * R1; // output RGB values G3 = L2G/L1G * G1; B3 = L2B/L1B * B1; F1 = R3; // prevent overflow if (G3 > F1) F1 = G3; if (B3 > F1) F1 = B3; if (F1 > 255) { F1 = 255.0 / F1; R3 = F1 * R3; G3 = F1 * G3; B3 = F1 * B3; } pix3[0] = R3; // output RGB values pix3[1] = G3; pix3[2] = B3; } exit_wthread(); return 0; // not executed, avoid gcc warning } // retinex blend thread // Transfer global retinex color to zonal retinex image. // Blend input and output images, attenuate bright pixels. void * Bretinex_wthread(void *arg) { using namespace retinex_names; int index = *((int *) arg); int px, py; int ii, dist = 0; float *pix1, *pix2, *pix3; float R1, G1, B1, R2, G2, B2, R3, G3, B3; float F1, F2, Lmax; for (py = index; py < e3hh; py += NWT) // loop all image pixels for (px = 0; px < e3ww; px++) { if (sa_stat == 3) { // select area active ii = py * e3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside area } pix1 = PXMpix(E1pxm,px,py); // input image pixel if (E9pxm) pix2 = PXMpix(E9pxm,px,py); // use zonal retinex image if available else if (E8pxm) pix2 = PXMpix(E8pxm,px,py); // else use global retinex image else pix2 = PXMpix(E3pxm,px,py); // else use output image pixel pix3 = PXMpix(E3pxm,px,py); // output image pixel R1 = pix1[0]; // input color - original image G1 = pix1[1]; B1 = pix1[2]; R2 = pix2[0]; // input color - retinex image G2 = pix2[1]; B2 = pix2[2]; if (sa_stat == 3 && dist < sa_blend) { // select area is active, F1 = sa_blendfunc(dist); // blend changes over sa_blend F2 = 1.0 - F1; R2 = F1 * R2 + F2 * R1; G2 = F1 * G2 + F2 * G1; B2 = F1 * B2 + F2 * B1; } Lmax = R1; // max. RGB input if (G1 > Lmax) Lmax = G1; if (B1 > Lmax) Lmax = B1; F1 = blend; // 0 ... 1 >> E1 ... E3 F2 = reducebright; // 0 ... 1 F1 = F1 * (1.0 - F2 * Lmax / 255.0); // reduce F1 for high F2 * Lmax R3 = F1 * R2 + (1.0 - F1) * R1; // output RGB = blended inputs G3 = F1 * G2 + (1.0 - F1) * G1; B3 = F1 * B2 + (1.0 - F1) * B1; pix3[0] = R3; // output RGB values pix3[1] = G3; pix3[2] = B3; } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // mirror an image horizontally or vertically editfunc EFmirror; void m_mirror(GtkWidget *, cchar *menu) { int mirror_dialog_event(zdialog *zd, cchar *event); F1_help_topic = "mirror_image"; EFmirror.menuname = menu; EFmirror.menufunc = m_mirror; EFmirror.funcname = "mirror"; EFmirror.Fscript = 1; // allow scripting 17.04 EFmirror.Frestart = 1; // allow restart if (! edit_setup(EFmirror)) return; // setup edit zdialog *zd = zdialog_new(ZTX("Mirror Image"),Mwin,Bdone,Bcancel,null); EFmirror.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"check","horz","hb1",ZTX("horizontal"),"space=5"); zdialog_add_widget(zd,"check","vert","hb1",ZTX("vertical"),"space=5"); zdialog_run(zd,mirror_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int mirror_dialog_event(zdialog *zd, cchar *event) { int mirror_horz(); int mirror_vert(); int nn; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 2; // cancel if (zd->zstat) // dialog complete { if (zd->zstat == 1) edit_done(0); else edit_cancel(0); return 1; } if (strmatch(event,"apply")) { // from script 17.04 zdialog_fetch(zd,"horz",nn); if (nn) mirror_horz(); zdialog_fetch(zd,"vert",nn); if (nn) mirror_vert(); } if (strmatch(event,"horz")) mirror_horz(); if (strmatch(event,"vert")) mirror_vert(); return 1; } int mirror_horz() { int px, py; float *pix3, *pix9; int nc = E3pxm->nc, pcc = nc * sizeof(float); E9pxm = PXM_copy(E3pxm); for (py = 0; py < E3pxm->hh; py++) for (px = 0; px < E3pxm->ww; px++) { pix3 = PXMpix(E3pxm,px,py); // image9 = mirrored image3 pix9 = PXMpix(E9pxm,E3pxm->ww-1-px,py); memcpy(pix9,pix3,pcc); } PXM_free(E3pxm); // image9 >> image3 E3pxm = E9pxm; E9pxm = 0; CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); return 0; } int mirror_vert() { int px, py; float *pix3, *pix9; int nc = E3pxm->nc, pcc = nc * sizeof(float); E9pxm = PXM_copy(E3pxm); for (py = 0; py < E3pxm->hh; py++) for (px = 0; px < E3pxm->ww; px++) { pix3 = PXMpix(E3pxm,px,py); // image9 = mirrored image3 pix9 = PXMpix(E9pxm,px,E3pxm->hh-1-py); memcpy(pix9,pix3,pcc); } PXM_free(E3pxm); // image9 >> image3 E3pxm = E9pxm; E9pxm = 0; CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); return 0; } /********************************************************************************/ // Pixel paint function - paint individual pixels with the mouse. // The mouse circle paints a selected color. namespace paint_names { int paint_dialog_event(zdialog* zd, cchar *event); void paint_mousefunc(); void paint_dopixels(int px, int py); // update pixel block void paint_savepixB(int px, int py); // save pixel block for poss. undo void paint_undolastB(); // undo last pixel block, free memory void paint_freefirstB(); // free memory for first pixel block void paint_freeallB(); // free memory for all pixel blocks uint8 RGB[3] = { 255, 0, 0 }; // color to paint int mode; // 1/2 = paint / erase int Mradius; // mouse radius int Fptran = 0; // flag, paint over transparent areas int Fdrag = 0; // flag, mouse drags image int nc, ac; // no. channels, alpha channel float kernel[202][202]; // Mradius <= 100 int64 maxmem = 2000 * MEGA; // max. pixel block memory (2 GB) int64 totmem; // pixB memory allocated int maxpixB = 10000; // max. pixel blocks int totpixB = 0; // total pixel blocks int pixBseq = 0; // last pixel block sequence no. typedef struct { // pixel block before edit int seq; // block sequence no. uint16 px, py; // center pixel (radius org.) uint16 radius; // radius of pixel block float pixel[][4]; // array of pixel[npix][4] } pixBmem_t; pixBmem_t **pixBmem = 0; // *pixBmem_t[] int pixBmem_cc = 12; // all except pixel array + pad int pcc4 = 4 * sizeof(float); // pixel cc: RGBA = 4 channels editfunc EFpaint; } // menu function void m_paint_image(GtkWidget *, cchar *) // separate paint and clone 17.04 { using namespace paint_names; cchar *mess1 = ZTX("shift + left click: pick color from image \n" "left drag: paint color on image \n" // remove click actions 17.04 "right drag: restore original image"); cchar *dash = " \xcc\xb6 "; F1_help_topic = "paint_image"; EFpaint.menufunc = m_paint_image; EFpaint.funcname = "paint"; EFpaint.Farea = 2; // select area OK EFpaint.mousefunc = paint_mousefunc; // mouse function if (! edit_setup(EFpaint)) return; // setup edit /**** __________________________________________________ | Paint on Image | | | | shift + left click: pick color from image | | left drag: paint color on image | | right drag: restore original image | | | | paint color [######] [palette] [HSL] | | | | brush size NN ============[]============= | | opacity center NN ==================[]======= | | opacity edge NN ====[]===================== | | | | [x] paint transparent areas | | (o) paint (o) erase [undo last] [undo all] | | [x] drag image zoom image [+] [-] | | | | [done] [cancel] | |__________________________________________________| ****/ zdialog *zd = zdialog_new(ZTX("Paint on Image"),Mwin,Bdone,Bcancel,null); EFpaint.zd = zd; zdialog_add_widget(zd,"label","labm","dialog",mess1,"space=5"); zdialog_add_widget(zd,"hbox","hbp","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labp","hbp",ZTX("paint color"),"space=5"); zdialog_add_widget(zd,"colorbutt","colorbutt","hbp","255|0|0"); zdialog_add_widget(zd,"label","space","hbp",0,"space=10"); zdialog_add_widget(zd,"button","palette","hbp","palette","space=10"); zdialog_add_widget(zd,"button","HSL","hbp","HSL"); zdialog_add_widget(zd,"hbox","hbbru","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vbbru1","hbbru",0,"homog|space=1"); zdialog_add_widget(zd,"vbox","vbbru2","hbbru",0,"homog|space=1"); zdialog_add_widget(zd,"vbox","vbbru3","hbbru",0,"homog|expand|space=1"); zdialog_add_widget(zd,"label","labbr","vbbru1",ZTX("brush size")); zdialog_add_widget(zd,"label","laboc","vbbru1",ZTX("opacity center")); zdialog_add_widget(zd,"label","laboe","vbbru1",ZTX("opacity edge")); zdialog_add_widget(zd,"label","labbrNN","vbbru2","NN"); zdialog_add_widget(zd,"label","labocNN","vbbru2","NNN"); zdialog_add_widget(zd,"label","laboeNN","vbbru2","NNN"); zdialog_add_widget(zd,"hscale","Mradius","vbbru3","1|100|1|20","expand"); zdialog_add_widget(zd,"hscale","opccent","vbbru3","1|100|1|50","expand"); zdialog_add_widget(zd,"hscale","opcedge","vbbru3","0|100|1|100","expand"); zdialog_add_widget(zd,"hbox","hb5","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","paint","hb5",ZTX("paint"),"space=5"); zdialog_add_widget(zd,"radio","erase","hb5",ZTX("erase")); zdialog_add_widget(zd,"check","Fptran","hb5",ZTX("include transparent areas"),"space=8"); zdialog_add_widget(zd,"hbox","hbdo","dialog",0,"space=3"); zdialog_add_widget(zd,"button","undlast","hbdo",Bundolast,"space=5"); zdialog_add_widget(zd,"button","undall","hbdo",Bundoall,"space=5"); zdialog_add_widget(zd,"hbox","hb6","dialog"); zdialog_add_widget(zd,"check","Fdrag","hb6",ZTX("drag image"),"space=5"); zdialog_add_widget(zd,"label","space","hb6",0,"space=10"); zdialog_add_widget(zd,"label","labzoom","hb6",ZTX("zoom image"),"space=3"); zdialog_add_widget(zd,"button","zoom+","hb6"," + ","space=3"); zdialog_add_widget(zd,"button","zoom-","hb6",dash,"space=3"); zdialog_rescale(zd,"Mradius",1,2,100); // stretch scales at sensitive end 17.04 zdialog_rescale(zd,"opccent",1,2,100); zdialog_rescale(zd,"opcedge",0,1,100); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_stuff(zd,"Fptran",0); // initialize zdialog_stuff(zd,"paint",1); zdialog_stuff(zd,"erase",0); zdialog_stuff(zd,"Fdrag",0); mode = 1; Fdrag = 0; zdialog_run(zd,paint_dialog_event,"save"); // run dialog, parallel zdialog_send_event(zd,"colorbutt"); // initialize paint color zdialog_send_event(zd,"Mradius"); // get kernel initialized zdialog_fetch(zd,"Fptran",Fptran); // paint over transparent areas totmem = 0; // memory used pixBmem = 0; // pixel block memory totpixB = 0; // pixel blocks pixBseq = 0; ac = 0; nc = E1pxm->nc; // channels, RGBA if (nc > 3) ac = 1; // alpha channel present takeMouse(paint_mousefunc,drawcursor); // connect mouse function return; } // dialog event and completion callback function int paint_names::paint_dialog_event(zdialog *zd, cchar *event) { using namespace paint_names; cchar *pp; char color[20], text[20]; float opccent, opcedge; // center and edge opacities int paint, radius, dx, dy, err; float rad, kern; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit paint_freeallB(); // free pixel block memory return 1; } draw_mousecircle(0,0,0,1,0); // erase mouse circle if (strmatch(event,"focus")) // toggle mouse capture takeMouse(paint_mousefunc,drawcursor); if (strmatch(event,"colorbutt")) { zdialog_fetch(zd,"colorbutt",color,19); // get paint color from color wheel pp = strField(color,"|",1); if (pp) RGB[0] = atoi(pp); pp = strField(color,"|",2); if (pp) RGB[1] = atoi(pp); pp = strField(color,"|",3); if (pp) RGB[2] = atoi(pp); } if (strmatch(event,"palette")) { err = color_palette(RGB); // select color from palette 17.04 if (err) return 1; snprintf(color,20,"%d|%d|%d",RGB[0],RGB[1],RGB[2]); zdialog_stuff(zd,"colorbutt",color); } if (strmatch(event,"HSL")) { err = HSL_chooser(RGB); // select color from palette 17.08 if (err) return 1; snprintf(color,20,"%d|%d|%d",RGB[0],RGB[1],RGB[2]); zdialog_stuff(zd,"colorbutt",color); } if (strstr("Mradius opccent opcedge",event)) { zdialog_fetch(zd,"Mradius",Mradius); // get new brush attributes zdialog_fetch(zd,"opccent",opccent); zdialog_fetch(zd,"opcedge",opcedge); sprintf(text,"%3d",Mradius); // stuff corresp. number values zdialog_stuff(zd,"labbrNN",text); sprintf(text,"%3.0f",opccent); zdialog_stuff(zd,"labocNN",text); sprintf(text,"%3.0f",opcedge); zdialog_stuff(zd,"laboeNN",text); opccent = 0.01 * opccent; // opacity 0 ... 1 opcedge = 0.01 * opcedge; opccent = pow(opccent,2.0); // change response curve opcedge = opccent * opcedge; // edge relative to center radius = Mradius; for (dy = -radius; dy <= radius; dy++) // build kernel for (dx = -radius; dx <= radius; dx++) { rad = sqrt(dx*dx + dy*dy); kern = (radius - rad) / radius; // center ... edge >> 1 ... 0 kern = kern * (opccent - opcedge) + opcedge; // opacity center ... edge if (rad > radius) kern = 0; // beyond radius, within square if (kern < 0) kern = 0; if (kern > 1) kern = 1; kernel[dx+radius][dy+radius] = kern; } } if (strmatch(event,"undlast")) // undo last edit (click or drag) paint_undolastB(); if (strmatch(event,"undall")) { // undo all edits edit_reset(); paint_freeallB(); } if (strmatch(event,"Fptran")) // flag, paint over transparency zdialog_fetch(zd,"Fptran",Fptran); if (strstr("paint erase",event)) { // set paint or erase mode zdialog_fetch(zd,"paint",paint); if (paint) mode = 1; else mode = 2; } if (strmatch(event,"Fdrag")) // mouse drags image zdialog_fetch(zd,"Fdrag",Fdrag); if (strmatch(event,"zoom+")) m_zoom(0,"in"); // zoom image in or out if (strmatch(event,"zoom-")) m_zoom(0,"out"); return 1; } // mouse function void paint_names::paint_mousefunc() // no action on clicks 17.04 { using namespace paint_names; static int pmxdown = 0, pmydown = 0; static int pmxdrag = 0, pmydrag = 0; char color[20]; float *pix3; zdialog *zd = EFpaint.zd; if (Fdrag) return; // pass through to main() if (LMclick && KBshiftkey) // shift + left mouse click { LMclick = 0; pix3 = PXMpix(E3pxm,Mxclick,Myclick); // pick new color from image RGB[0] = pix3[0]; RGB[1] = pix3[1]; RGB[2] = pix3[2]; snprintf(color,19,"%d|%d|%d",RGB[0],RGB[1],RGB[2]); if (zd) zdialog_stuff(zd,"colorbutt",color); return; } if (Mxdrag || Mydrag) // drag in progress { if (Mxdown != pmxdown || Mydown != pmydown) { // new drag pixBseq++; // new undo seq. no. pmxdown = Mxdown; pmydown = Mydown; } if (Mxdrag == pmxdrag && Mydrag == pmydrag) { // no movement Mxdrag = Mydrag = 0; return; } pmxdrag = Mxdrag; pmydrag = Mydrag; paint_dopixels(Mxdrag,Mydrag); // do 1 block of pixels } draw_mousecircle(Mxposn,Myposn,Mradius,0,0); // draw mouse circle Mxdrag = Mydrag = LMclick = 0; return; } // paint or erase 1 block of pixels within mouse radius of px, py void paint_names::paint_dopixels(int px, int py) { using namespace paint_names; float *pix1, *pix3; int radius, dx, dy, qx, qy; int ii, ww, hh, dist = 0; int pot = ac * Fptran; // paint over transparent areas float red, green, blue; float kern; cairo_t *cr = draw_context_create(gdkwin,draw_context); // 17.04 draw_mousecircle(0,0,0,1,cr); // erase mouse circle ww = E3pxm->ww; hh = E3pxm->hh; paint_savepixB(px,py); // save pixels for poss. undo red = RGB[0]; // paint color green = RGB[1]; blue = RGB[2]; radius = Mradius; if (mode == 1) { // paint CEF->Fmods++; CEF->Fsaved = 0; } for (dy = -radius; dy <= radius; dy++) // loop surrounding block of pixels for (dx = -radius; dx <= radius; dx++) { qx = px + dx; qy = py + dy; if (qx < 0 || qx > ww-1) continue; if (qy < 0 || qy > hh-1) continue; if (sa_stat == 3) { // select area active ii = qy * ww + qx; dist = sa_pixmap[ii]; if (! dist) continue; // pixel is outside area } kern = kernel[dx+radius][dy+radius]; // mouse opacities if (kern == 0) continue; // outside mouse radius if (sa_stat == 3 && dist < sa_blend) // select area edge blend, kern = kern * sa_blendfunc(dist); // reduce opacity pix1 = PXMpix(E1pxm,qx,qy); // source image pixel pix3 = PXMpix(E3pxm,qx,qy); // edited image pixel if (mode == 1 && Mbutton < 2) // paint { kern = kern * (1.0 + 50.0 * wacom_pressure); // wacom 17.04 if (kern > 1.0) kern = 1.0; pix3[0] = kern * red + (1.0 - kern) * pix3[0]; // overpaints accumulate pix3[1] = kern * green + (1.0 - kern) * pix3[1]; pix3[2] = kern * blue + (1.0 - kern) * pix3[2]; if (pot) pix3[3] = kern * 255.0 + (1.0 - kern) * pix3[3]; // overpaint transparent area } if (mode == 2 || Mbutton >= 2) // erase { kern = kern * (2.0 + 50.0 * wacom_pressure); // wacom 17.04 if (kern > 1.0) kern = 1.0; if (kern > 1) kern = 1; pix3[0] = kern * pix1[0] + (1.0 - kern) * pix3[0]; // gradual erase pix3[1] = kern * pix1[1] + (1.0 - kern) * pix3[1]; pix3[2] = kern * pix1[2] + (1.0 - kern) * pix3[2]; if (pot) pix3[3] = kern * pix1[3] + (1.0 - kern) * pix3[3]; } } px = px - radius - 1; // repaint modified area py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww,cr); draw_context_destroy(draw_context); // 17.04 return; } // save 1 block of pixels for possible undo void paint_names::paint_savepixB(int px, int py) { using namespace paint_names; int cc, npix, radius, dx, dy; float *pix3; pixBmem_t *paintsave1; if (! pixBmem) { // first time pixBmem = (pixBmem_t **) zmalloc(maxpixB * sizeof(void *)); totpixB = 0; totmem = 0; } if (totmem > maxmem || totpixB == maxpixB) // free memory for oldest updates while (totmem > 0.7 * maxmem || totpixB > 0.7 * maxpixB) paint_freefirstB(); radius = Mradius; npix = 0; for (dy = -radius; dy <= radius; dy++) // count pixels in block for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; npix++; } cc = npix * pcc4 + pixBmem_cc; paintsave1 = (pixBmem_t *) zmalloc(cc); // allocate memory for block pixBmem[totpixB] = paintsave1; totpixB += 1; totmem += cc; paintsave1->seq = pixBseq; // save pixel block poop paintsave1->px = px; paintsave1->py = py; paintsave1->radius = radius; npix = 0; for (dy = -radius; dy <= radius; dy++) // save pixels in block for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,(px+dx),(py+dy)); // edited image pixel paintsave1->pixel[npix][0] = pix3[0]; paintsave1->pixel[npix][1] = pix3[1]; paintsave1->pixel[npix][2] = pix3[2]; if (ac) paintsave1->pixel[npix][3] = pix3[3]; npix++; } return; } // undo last pixel block (newest edit) and free memory void paint_names::paint_undolastB() { using namespace paint_names; int ii, cc, npix, radius; int ww, px, py, dx, dy; float *pix3; pixBmem_t *paintsave1; for (ii = totpixB-1; ii >= 0; ii--) { paintsave1 = pixBmem[ii]; if (paintsave1->seq != pixBseq) break; px = paintsave1->px; py = paintsave1->py; radius = paintsave1->radius; npix = 0; for (dy = -radius; dy <= radius; dy++) for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,(px+dx),(py+dy)); pix3[0] = paintsave1->pixel[npix][0]; pix3[1] = paintsave1->pixel[npix][1]; pix3[2] = paintsave1->pixel[npix][2]; if (ac) pix3[3] = paintsave1->pixel[npix][3]; npix++; } px = px - radius - 1; py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww,0); zfree(paintsave1); pixBmem[ii] = 0; cc = npix * pcc4 + pixBmem_cc; totmem -= cc; totpixB--; } if (pixBseq > 0) --pixBseq; return; } // free memory for first pixel block (oldest edit) void paint_names::paint_freefirstB() { using namespace paint_names; int firstseq; int ii, jj, cc, npix, radius; int px, py, dx, dy; pixBmem_t *paintsave1; if (! totpixB) return; firstseq = pixBmem[0]->seq; for (ii = 0; ii < totpixB; ii++) { paintsave1 = pixBmem[ii]; if (paintsave1->seq != firstseq) break; px = paintsave1->px; py = paintsave1->py; radius = paintsave1->radius; npix = 0; for (dy = -radius; dy <= radius; dy++) for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; npix++; } zfree(paintsave1); pixBmem[ii] = 0; cc = npix * pcc4 + pixBmem_cc; totmem -= cc; } for (jj = 0; ii < totpixB; jj++, ii++) pixBmem[jj] = pixBmem[ii]; totpixB = jj; return; } // free all pixel block memory void paint_names::paint_freeallB() { using namespace paint_names; int ii; pixBmem_t *paintsave1; for (ii = totpixB-1; ii >= 0; ii--) { paintsave1 = pixBmem[ii]; zfree(paintsave1); } if (pixBmem) zfree(pixBmem); pixBmem = 0; pixBseq = 0; totpixB = 0; totmem = 0; return; } /********************************************************************************/ // Select a color from a color palette image file. // Returns status = 0 if OK, = 1 if error or user cancel. // Returns selected RGB color in rgb[3] argument. namespace color_palette_names { // char *color_palette_file; // defined in fotoxx.h uint8 RGB[3] = { 255, 255, 255 }; // last color selection char color[20]; zdialog *zd; PIXBUF *pixbuf = 0; GtkWidget *frame, *drawarea; } int color_palette(uint8 rgb[3]) // 17.04 { using namespace color_palette_names; int color_palette_dialog_event(zdialog *zd, cchar *event); // dialog events function int color_palette_draw(GtkWidget *window, cairo_t *cr); // window draw function int color_palette_mouse(GtkWidget *window, GdkEventButton *); // window mouse button event int zstat; /*** ____________________________________________ | Color Palette | | | | ##### | | ##### click on image to choose color | | ##### | | ________________________________________ | | | | | | | | | | | | | | | image | | | | | | | | | | | | | | | | | | | |________________________________________| | | | | [_______________________________] [browse] | | | | [done] [cancel] | |____________________________________________| ***/ zd = zdialog_new(ZTX("Color Palette"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbcolor","dialog",0,"space=3"); zdialog_add_widget(zd,"colorbutt","color","hbcolor","255|255|255","space=5"); zdialog_add_widget(zd,"label","labcolor","hbcolor",ZTX("click on image to choose color")); zdialog_add_widget(zd,"frame","frame","dialog"); frame = zdialog_widget(zd,"frame"); drawarea = gtk_drawing_area_new(); gtk_widget_set_size_request(drawarea,-1,200); gtk_container_add(GTK_CONTAINER(frame),drawarea); zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=3"); zdialog_add_widget(zd,"zentry","file","hbfile",0,"space=3|expand"); zdialog_add_widget(zd,"button","browse","hbfile",Bbrowse,"space=3"); zdialog_stuff(zd,"file",color_palette_file); G_SIGNAL(drawarea,"draw",color_palette_draw,0); G_SIGNAL(drawarea,"button-press-event",color_palette_mouse,0); gtk_widget_add_events(drawarea,GDK_BUTTON_PRESS_MASK); snprintf(color,20,"%d|%d|%d",RGB[0],RGB[1],RGB[2]); zdialog_stuff(zd,"color",color); zdialog_resize(zd,300,0); zdialog_run(zd,color_palette_dialog_event,"save"); zstat = zdialog_wait(zd); if (zstat != 1) { // [cancel] or [x] zdialog_free(zd); return 1; } rgb[0] = RGB[0]; // [done] rgb[1] = RGB[1]; rgb[2] = RGB[2]; zdialog_free(zd); return 0; } // dialog event and completion function int color_palette_dialog_event(zdialog *zd, cchar *event) { using namespace color_palette_names; char *pp; if (strmatch(event,"browse")) { pp = gallery_select1(color_palette_file); // 17.08 if (! pp) return 1; zdialog_stuff(zd,"file",pp); if (color_palette_file) zfree(color_palette_file); color_palette_file = pp; gtk_widget_queue_draw(drawarea); } return 1; } // color palette window draw function int color_palette_draw(GtkWidget *widget, cairo_t *cr) { using namespace color_palette_names; PIXBUF *pixbuf1; GError *gerror = 0; GdkWindow *gdkwin; int ww1, hh1, ww2, hh2; if (*color_palette_file != '/') return 1; // load last color palette file pixbuf1 = gdk_pixbuf_new_from_file_at_size(color_palette_file,500,500,&gerror); if (! pixbuf1) { printz("pixbuf error: %s \n",gerror->message); // popup message >> draw event loop 17.04.2 return 1; // GTK 3.22.11 } ww1 = gdk_pixbuf_get_width(pixbuf1); // image dimensions hh1 = gdk_pixbuf_get_height(pixbuf1); gdkwin = gtk_widget_get_window(widget); // set drawing area to match ww2 = gdk_window_get_width(gdkwin); // aspect ratio hh2 = ww2 * hh1 / ww1; gtk_widget_set_size_request(widget,-1,hh2); if (pixbuf) g_object_unref(pixbuf); pixbuf = gdk_pixbuf_scale_simple(pixbuf1,ww2,hh2,BILINEAR); g_object_unref(pixbuf1); if (! pixbuf) return 1; gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); // draw image cairo_paint(cr); return 1; } // color palette mouse click function int color_palette_mouse(GtkWidget *widget, GdkEventButton *event) { using namespace color_palette_names; int mx, my, rs, nc; uint8 *pixels, *pix1; if (! pixbuf) return 1; rs = gdk_pixbuf_get_rowstride(pixbuf); nc = gdk_pixbuf_get_n_channels(pixbuf); pixels = gdk_pixbuf_get_pixels(pixbuf); mx = event->x; my = event->y; pix1 = pixels + my * rs + mx * nc; RGB[0] = pix1[0]; RGB[1] = pix1[1]; RGB[2] = pix1[2]; snprintf(color,20,"%d|%d|%d",RGB[0],RGB[1],RGB[2]); zdialog_stuff(zd,"color",color); return 1; } /********************************************************************************/ // HSL color chooser function // Returns status = 0 if OK, = 1 if error or user cancel. // Returns selected RGB color in rgb[3] argument. namespace HSL_chooser_names // 17.08 { zdialog *HSLzdialog; GtkWidget *RGBframe, *RGBcolor; GtkWidget *Hframe, *Hscale; float H, S, L; // chosen HSL color float R, G, B; // corresp. RGB color } int HSL_chooser(uint8 rgb[3]) { using namespace HSL_chooser_names; void HSL_chooser_RGB(GtkWidget *drawarea, cairo_t *cr, int *); void HSL_chooser_Hscale(GtkWidget *drawarea, cairo_t *cr, int *); int HSL_chooser_dialog_event(zdialog *zd, cchar *event); void HSL_chooser_mousefunc(); zdialog *zd; int zstat; /*** ________________________________________________ | | | [#######] [##############################] | | Color Hue ================[]============== | | Saturation =====================[]========= | | Lightness ===========[]=================== | | | | [select] [cancel] | |________________________________________________| ***/ zd = zdialog_new("Adjust HSL",Mwin,Bselect,Bcancel,null); HSLzdialog = zd; zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb2",0,"homog|space=0"); zdialog_add_widget(zd,"vbox","vb2","hb2",0,"homog|expand|space=0"); zdialog_add_widget(zd,"frame","RGBframe","vb1",0,"space=1"); // drawing area for RGB color RGBframe = zdialog_widget(zd,"RGBframe"); RGBcolor = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(RGBframe),RGBcolor); gtk_widget_set_size_request(RGBcolor,0,16); G_SIGNAL(RGBcolor,"draw",HSL_chooser_RGB,0); zdialog_add_widget(zd,"frame","Hframe","vb2",0,"space=1"); // drawing area for hue scale Hframe = zdialog_widget(zd,"Hframe"); Hscale = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(Hframe),Hscale); gtk_widget_set_size_request(Hscale,200,16); G_SIGNAL(Hscale,"draw",HSL_chooser_Hscale,0); zdialog_add_widget(zd,"label","labhue","vb1",ZTX("Color Hue")); zdialog_add_widget(zd,"label","labsat","vb1",ZTX("Saturation")); zdialog_add_widget(zd,"label","lablgt","vb1",ZTX("Lightness")); zdialog_add_widget(zd,"hscale","H","vb2","0|359.9|0.1|180","expand"); zdialog_add_widget(zd,"hscale","S","vb2","0|1|0.001|0.5","expand"); zdialog_add_widget(zd,"hscale","L","vb2","0|1|0.001|0.5","expand"); H = 180; // chosen HSL color = not set S = 0.5; L = 0.5; zdialog_run(zd,HSL_chooser_dialog_event,"save"); // run dialog - parallel takeMouse(HSL_chooser_mousefunc,arrowcursor); // connect mouse function zstat = zdialog_wait(zd); // wait for dialog complete if (zstat == 1) return 1; // cancel rgb[0] = 255 * R; // return chosen RGB color rgb[1] = 255 * G; rgb[2] = 255 * B; return 0; } // Paint RGBcolor drawing area with RGB color from chosen HSL color void HSL_chooser_RGB(GtkWidget *drawarea, cairo_t *cr, int *) { using namespace HSL_chooser_names; int ww, hh; ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); HSLtoRGB(H,S,L,R,G,B); // RGB color from chosen HSL cairo_set_source_rgb(cr,R,G,B); cairo_rectangle(cr,0,0,ww-1,hh-1); cairo_fill(cr); return; } // Paint Hscale drawing area with all hue values in a horizontal scale void HSL_chooser_Hscale(GtkWidget *drawarea, cairo_t *cr, int *) { using namespace HSL_chooser_names; int px, ww, hh; float H, S, L, R, G, B; ww = gtk_widget_get_allocated_width(drawarea); // drawing area size hh = gtk_widget_get_allocated_height(drawarea); S = L = 0.5; for (px = 0; px < ww; px++) // paint hue color scale { H = 360 * px / ww; HSLtoRGB(H,S,L,R,G,B); cairo_set_source_rgb(cr,R,G,B); cairo_move_to(cr,px,0); cairo_line_to(cr,px,hh-1); cairo_stroke(cr); } return; } // HSL dialog event and completion function int HSL_chooser_dialog_event(zdialog *zd, cchar *event) // HSL dialog event function { using namespace HSL_chooser_names; void HSL_chooser_mousefunc(); if (zd->zstat) { // zdialog complete zdialog_free(zd); freeMouse(); // 18.01 HSLzdialog = 0; return 1; } if (strstr("H S L",event)) { // HSL inputs changed zdialog_fetch(zd,"H",H); zdialog_fetch(zd,"S",S); zdialog_fetch(zd,"L",L); gtk_widget_queue_draw(RGBcolor); // draw corresp. RGB color } if (strmatch("focus",event)) takeMouse(HSL_chooser_mousefunc,arrowcursor); return 1; } // mouse function // click on image to set the color void HSL_chooser_mousefunc() { using namespace HSL_chooser_names; float *pix1; float f256 = 1.0 / 256.0; zdialog *zd = HSLzdialog; if (! zd) return; if (LMclick && KBshiftkey) // shift + left mouse click { LMclick = 0; pix1 = PXMpix(E1pxm,Mxclick,Myclick); // pick output color from image R = pix1[0] * f256; G = pix1[1] * f256; B = pix1[2] * f256; RGBtoHSL(R,G,B,H,S,L); // set corresp. HSL zdialog_stuff(zd,"H",H); zdialog_stuff(zd,"S",S); zdialog_stuff(zd,"L",L); gtk_widget_queue_draw(RGBcolor); // draw current RGB color } return; } /********************************************************************************/ // Clone Image function. // Copy from one image area to another with variable opacity. namespace clone_names { int clone_dialog_event(zdialog* zd, cchar *event); void clone_mousefunc(); void clone_dopixels(int px, int py); // update pixel block void clone_savepixB(int px, int py); // save pixel block for poss. undo void clone_undolastB(); // undo last pixel block, free memory void clone_freefirstB(); // free memory for first pixel block void clone_freeallB(); // free memory for all pixel blocks int mode; // 1/2 = paint / erase int Mradius; // mouse radius int imagex, imagey; // source image location float kernel[402][402]; // radius <= 200 int Fptran = 0; // flag, paint over transparent areas int nc, ac; // no. channels, alpha channel int64 maxmem = 2000 * MEGA; // max. pixel block memory int64 totmem; // pixB memory allocated int maxpixB = 10000; // max. pixel blocks int totpixB = 0; // total pixel blocks int pixBseq = 0; // last pixel block sequence no. typedef struct { // pixel block before edit int seq; // block sequence no. uint16 px, py; // center pixel (radius org.) uint16 radius; // radius of pixel block float pixel[][4]; // array of pixel[npix][4] } pixBmem_t; pixBmem_t **pixBmem = 0; // *pixBmem_t[] int pixBmem_cc = 12; // all except pixel array + pad int pcc4 = 4 * sizeof(float); // pixel cc: RGBA = 4 channels editfunc EFclone; } // menu function void m_clone_image(GtkWidget *, cchar *) // separate paint and clone 17.04 { using namespace clone_names; cchar *mess1 = ZTX("shift + left click: pick image position to copy \n" "left drag: copy image to mouse position \n" "right drag: restore image"); F1_help_topic = "clone_image"; EFclone.menufunc = m_clone_image; EFclone.funcname = "clone"; EFclone.Farea = 2; // select area OK EFclone.mousefunc = clone_mousefunc; // mouse function if (! edit_setup(EFclone)) return; // setup edit /******** ____________________________________________________ | Clone Image | | | | shift + left click: pick image position to copy | | left click or drag: copy image to mouse position | | right click or drag: restore image | | | | brush size [____] [undo last] | | opacity center [____] [undo all] | | opacity edge [____] | | | | [x] paint transparent areas | | | | [done] [cancel] | |____________________________________________________| ********/ zdialog *zd = zdialog_new(ZTX("Clone Image"),Mwin,Bdone,Bcancel,null); EFclone.zd = zd; zdialog_add_widget(zd,"hbox","hbr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labm","dialog",mess1,"space=5"); zdialog_add_widget(zd,"hbox","hbbri","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vbbr1","hbbri",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbbr2","hbbri",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","space","hbbri",0,"space=10"); zdialog_add_widget(zd,"vbox","vbbr3","hbbri",0,"space=10"); zdialog_add_widget(zd,"label","labbr","vbbr1",ZTX("brush size")); zdialog_add_widget(zd,"label","labtc","vbbr1",ZTX("opacity center")); zdialog_add_widget(zd,"label","labte","vbbr1",ZTX("opacity edge")); zdialog_add_widget(zd,"zspin","Mradius","vbbr2","1|200|1|30"); zdialog_add_widget(zd,"zspin","opccent","vbbr2","1|100|1|10"); zdialog_add_widget(zd,"zspin","opcedge","vbbr2","0|100|1|0"); zdialog_add_widget(zd,"button","undlast","vbbr3",Bundolast); zdialog_add_widget(zd,"button","undall","vbbr3",Bundoall); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=3"); zdialog_add_widget(zd,"check","Fptran","hb4",ZTX("paint over transparent areas"),"space=5"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,clone_dialog_event,"save"); // run dialog, parallel zdialog_fetch(zd,"Fptran",Fptran); // paint over transparent areas zdialog_send_event(zd,"Mradius"); // get kernel initialized totmem = 0; // memory used pixBmem = 0; // pixel block memory totpixB = 0; // pixel blocks pixBseq = 0; imagex = imagey = 0; // no clone source pixels ac = 0; nc = E1pxm->nc; // channels, RGBA if (nc > 3) ac = 1; // alpha channel present takeMouse(clone_mousefunc,drawcursor); // connect mouse function return; } // dialog event and completion callback function int clone_names::clone_dialog_event(zdialog *zd, cchar *event) { using namespace clone_names; int radius, dx, dy; float rad, kern, opccent, opcedge; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit clone_freeallB(); // free pixel block memory return 1; } draw_mousecircle(0,0,0,1,0); // erase mouse circle draw_mousecircle2(0,0,0,1,0); // erase source tracking circle if (strmatch(event,"focus")) // toggle mouse capture takeMouse(clone_mousefunc,drawcursor); if (strstr("Mradius opccent opcedge",event)) // get new brush attributes { zdialog_fetch(zd,"Mradius",Mradius); zdialog_fetch(zd,"opccent",opccent); zdialog_fetch(zd,"opcedge",opcedge); opccent = 0.01 * opccent; // scale 0 ... 1 opcedge = 0.01 * opcedge; opccent = pow(opccent,2); // change response curve opcedge = opccent * opcedge; // edge relative to center radius = Mradius; for (dy = -radius; dy <= radius; dy++) // build kernel for (dx = -radius; dx <= radius; dx++) { rad = sqrt(dx*dx + dy*dy); kern = (radius - rad) / radius; // center ... edge >> 1 ... 0 kern = kern * (opccent - opcedge) + opcedge; // opacity center ... edge if (rad > radius) kern = 0; // beyond radius, within square if (kern < 0) kern = 0; if (kern > 1) kern = 1; kernel[dx+radius][dy+radius] = kern; } } if (strmatch(event,"undlast")) // undo last edit (click or drag) clone_undolastB(); if (strmatch(event,"undall")) { // undo all edits edit_reset(); clone_freeallB(); } if (strmatch(event,"Fptran")) // flag, paint over transparency zdialog_fetch(zd,"Fptran",Fptran); return 1; } // pixel paint mouse function void clone_names::clone_mousefunc() { using namespace clone_names; static int pmxdown = 0, pmydown = 0; int px, py; if (LMclick && KBshiftkey) // shift + left mouse click { imagex = Mxclick; // new source image location imagey = Myclick; } else if (LMclick || RMclick) { if (LMclick) mode = 1; // left click, paint if (RMclick) mode = 2; // right click, erase px = Mxdown = Mxclick; // bugfix 17.04 py = Mydown = Myclick; pixBseq++; // new undo seq. no. clone_dopixels(px,py); // do 1 block of pixels } else if (Mxdrag || Mydrag) // drag in progress { if (Mbutton == 1) mode = 1; // left drag, paint if (Mbutton == 3) mode = 2; // right drag, erase px = Mxdrag; py = Mydrag; if (Mxdown != pmxdown || Mydown != pmydown) { // new drag pixBseq++; // new undo seq. no. pmxdown = Mxdown; pmydown = Mydown; } clone_dopixels(px,py); // do 1 block of pixels } draw_mousecircle(Mxposn,Myposn,Mradius,0,0); // draw mouse circle if (mode == 1 && (Mxdown || Mydown)) { // 2nd circle tracks source pixels px = imagex + Mxposn - Mxdown; py = imagey + Myposn - Mydown; if (px > 0 && px < E3pxm->ww-1 && py > 0 && py < E3pxm->hh-1) draw_mousecircle2(px,py,Mradius,0,0); } else draw_mousecircle2(0,0,0,1,0); // no 2nd circle LMclick = RMclick = Mxdrag = Mydrag = 0; return; } // paint or erase 1 block of pixels within mouse radius of px, py void clone_names::clone_dopixels(int px, int py) { using namespace clone_names; float *pix1, *pix3, *pix9; int radius, dx, dy, qx, qy, sx, sy; int ii, ww, hh, dist = 0; int pot = ac * Fptran; // paint over transparent areas float kern; if (! imagex && ! imagey) return; // no source area defined cairo_t *cr = draw_context_create(gdkwin,draw_context); // 17.04 draw_mousecircle(0,0,0,1,cr); // erase mouse circle draw_mousecircle2(0,0,0,1,cr); // erase source tracking circle ww = E3pxm->ww; hh = E3pxm->hh; clone_savepixB(px,py); // save pixels for poss. undo radius = Mradius; if (mode == 1) { CEF->Fmods++; CEF->Fsaved = 0; } for (dy = -radius; dy <= radius; dy++) // loop surrounding block of pixels for (dx = -radius; dx <= radius; dx++) { qx = px + dx; qy = py + dy; if (qx < 0 || qx > ww-1) continue; if (qy < 0 || qy > hh-1) continue; if (sa_stat == 3) { // select area active ii = qy * ww + qx; dist = sa_pixmap[ii]; if (! dist) continue; // pixel is outside area } kern = kernel[dx+radius][dy+radius]; // mouse opacities if (kern == 0) continue; // outside mouse radius if (sa_stat == 3 && dist < sa_blend) // select area edge blend, kern = kern * sa_blendfunc(dist); // reduce opacity pix1 = PXMpix(E1pxm,qx,qy); // source image pixel pix3 = PXMpix(E3pxm,qx,qy); // edited image pixel if (mode == 1) // paint (clone) { sx = imagex + qx - Mxdown; // image location + mouse drag shift sy = imagey + qy - Mydown; if (sx < 0) sx = 0; if (sx > ww-1) sx = ww-1; if (sy < 0) sy = 0; if (sy > hh-1) sy = hh-1; pix9 = PXMpix(E1pxm,sx,sy); // source image pixel at location kern = 0.3 * kern; pix3[0] = kern * pix9[0] + (1.0 - kern) * pix3[0]; // overpaints accumulate pix3[1] = kern * pix9[1] + (1.0 - kern) * pix3[1]; pix3[2] = kern * pix9[2] + (1.0 - kern) * pix3[2]; if (pot) pix3[3] = kern * pix9[3] + (1.0 - kern) * pix3[3]; // overpaint transparent area } if (mode == 2) // erase { pix3[0] = kern * pix1[0] + (1.0 - kern) * pix3[0]; // gradual erase pix3[1] = kern * pix1[1] + (1.0 - kern) * pix3[1]; pix3[2] = kern * pix1[2] + (1.0 - kern) * pix3[2]; if (pot) pix3[3] = kern * pix1[3] + (1.0 - kern) * pix3[3]; } } px = px - radius - 1; // repaint modified area py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww,cr); draw_context_destroy(draw_context); // 17.04 return; } // save 1 block of pixels for possible undo void clone_names::clone_savepixB(int px, int py) { using namespace clone_names; int cc, npix, radius, dx, dy; float *pix3; pixBmem_t *clonesave1; if (! pixBmem) { // first time pixBmem = (pixBmem_t **) zmalloc(maxpixB * sizeof(void *)); totpixB = 0; totmem = 0; } if (totmem > maxmem || totpixB == maxpixB) // free memory for oldest updates while (totmem > 0.7 * maxmem || totpixB > 0.7 * maxpixB) clone_freefirstB(); radius = Mradius; npix = 0; for (dy = -radius; dy <= radius; dy++) // count pixels in block for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; npix++; } cc = npix * pcc4 + pixBmem_cc; clonesave1 = (pixBmem_t *) zmalloc(cc); // allocate memory for block pixBmem[totpixB] = clonesave1; totpixB += 1; totmem += cc; clonesave1->seq = pixBseq; // save pixel block poop clonesave1->px = px; clonesave1->py = py; clonesave1->radius = radius; npix = 0; for (dy = -radius; dy <= radius; dy++) // save pixels in block for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,(px+dx),(py+dy)); // edited image pixel clonesave1->pixel[npix][0] = pix3[0]; clonesave1->pixel[npix][1] = pix3[1]; clonesave1->pixel[npix][2] = pix3[2]; if (ac) clonesave1->pixel[npix][3] = pix3[3]; npix++; } return; } // undo last pixel block (newest edit) and free memory void clone_names::clone_undolastB() { using namespace clone_names; int ii, cc, npix, radius; int ww, px, py, dx, dy; float *pix3; pixBmem_t *clonesave1; for (ii = totpixB-1; ii >= 0; ii--) { clonesave1 = pixBmem[ii]; if (clonesave1->seq != pixBseq) break; px = clonesave1->px; py = clonesave1->py; radius = clonesave1->radius; npix = 0; for (dy = -radius; dy <= radius; dy++) for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,(px+dx),(py+dy)); pix3[0] = clonesave1->pixel[npix][0]; pix3[1] = clonesave1->pixel[npix][1]; pix3[2] = clonesave1->pixel[npix][2]; if (ac) pix3[3] = clonesave1->pixel[npix][3]; npix++; } px = px - radius - 1; py = py - radius - 1; ww = 2 * radius + 3; Fpaint3(px,py,ww,ww,0); zfree(clonesave1); pixBmem[ii] = 0; cc = npix * pcc4 + pixBmem_cc; totmem -= cc; totpixB--; } if (pixBseq > 0) --pixBseq; return; } // free memory for first pixel (oldest edit) void clone_names::clone_freefirstB() { using namespace clone_names; int firstseq; int ii, jj, cc, npix, radius; int px, py, dx, dy; pixBmem_t *clonesave1; if (! totpixB) return; firstseq = pixBmem[0]->seq; for (ii = 0; ii < totpixB; ii++) { clonesave1 = pixBmem[ii]; if (clonesave1->seq != firstseq) break; px = clonesave1->px; py = clonesave1->py; radius = clonesave1->radius; npix = 0; for (dy = -radius; dy <= radius; dy++) for (dx = -radius; dx <= radius; dx++) { if (px + dx < 0 || px + dx > E3pxm->ww-1) continue; if (py + dy < 0 || py + dy > E3pxm->hh-1) continue; npix++; } zfree(clonesave1); pixBmem[ii] = 0; cc = npix * pcc4 + pixBmem_cc; totmem -= cc; } for (jj = 0; ii < totpixB; jj++, ii++) pixBmem[jj] = pixBmem[ii]; totpixB = jj; return; } // free all pixel block memory void clone_names::clone_freeallB() { using namespace clone_names; int ii; pixBmem_t *clonesave1; for (ii = totpixB-1; ii >= 0; ii--) { clonesave1 = pixBmem[ii]; zfree(clonesave1); } if (pixBmem) zfree(pixBmem); pixBmem = 0; pixBseq = 0; totpixB = 0; totmem = 0; return; } /********************************************************************************/ // Blend Image function - blend image in areas painted with the mouse namespace bi_names { int bi_dialog_event(zdialog* zd, cchar *event); void bi_mousefunc(); void * bi_thread(void *); void * bi_wthread(void *); int mode = 1; // 1/2 = blend/restore int Mradius = 20; // mouse radius float kernel[400][400]; // radius <= 199 int mousex, mousey; // mouse click/drag position editfunc EFblendimage; } // menu function void m_blend_image(GtkWidget *, cchar *) // 17.01 { using namespace bi_names; cchar *mess1 = ZTX("left drag: blend image \n" "right drag: restore image"); F1_help_topic = "blend_image"; EFblendimage.menufunc = m_blend_image; EFblendimage.funcname = "blend_image"; EFblendimage.Farea = 2; // select area OK EFblendimage.mousefunc = bi_mousefunc; // mouse function EFblendimage.threadfunc = bi_thread; // thread function if (! edit_setup(EFblendimage)) return; // setup edit /*** ____________________________________ | Blend Image | | | | left drag: blend image | | right drag: restore image | | | | paint radius [____] | | strength center [____] | | strength edge [____] | | | | [done] [cancel] | |____________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Blend Image"),Mwin,Bdone,Bcancel,null); EFblendimage.zd = zd; zdialog_add_widget(zd,"label","labm","dialog",mess1,"space=5"); zdialog_add_widget(zd,"hbox","hbbr","dialog",0,"space=5"); zdialog_add_widget(zd,"vbox","vbbr1","hbbr",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbbr2","hbbr",0,"homog|space=5"); zdialog_add_widget(zd,"label","labbr","vbbr1",Bpaintradius); zdialog_add_widget(zd,"label","labsc","vbbr1",ZTX("strength center")); zdialog_add_widget(zd,"label","labse","vbbr1",ZTX("strength edge")); zdialog_add_widget(zd,"zspin","radius","vbbr2","2|199|1|20"); zdialog_add_widget(zd,"zspin","stcent","vbbr2","0|100|1|50"); zdialog_add_widget(zd,"zspin","stedge","vbbr2","0|100|1|10"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,bi_dialog_event,"save"); // run dialog, parallel zdialog_send_event(zd,"radius"); // get kernel initialized mode = 1; // start with paint mode takeMouse(bi_mousefunc,drawcursor); // connect mouse function return; } // dialog event and completion callback function int bi_names::bi_dialog_event(zdialog *zd, cchar *event) { using namespace bi_names; int radius, dx, dy; float rad, kern, stcent, stedge; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(bi_mousefunc,drawcursor); if (strstr("radius stcent stedge",event)) // get new brush attributes { zdialog_fetch(zd,"radius",Mradius); // mouse radius zdialog_fetch(zd,"stcent",stcent); // center transparency zdialog_fetch(zd,"stedge",stedge); // edge transparency stcent = 0.01 * stcent; // scale 0 ... 1 stedge = 0.01 * stedge; radius = Mradius; for (dy = -radius; dy <= radius; dy++) // build kernel for (dx = -radius; dx <= radius; dx++) { rad = sqrt(dx*dx + dy*dy); kern = (radius - rad) / radius; // center ... edge >> 1 ... 0 kern = kern * (stcent - stedge) + stedge; // strength center ... edge if (kern < 0) kern = 0; if (kern > 1) kern = 1; if (rad > radius) kern = 2; // beyond radius, within square kernel[dx+radius][dy+radius] = kern; } } return 1; } // blend image mouse function void bi_names::bi_mousefunc() { using namespace bi_names; int px, py, ww; if (LMclick || RMclick) { if (LMclick) mode = 1; // left click, paint if (RMclick) mode = 2; // right click, erase mousex = Mxclick; mousey = Myclick; signal_thread(); } else if (Mxdrag || Mydrag) // drag in progress { if (Mbutton == 1) mode = 1; // left drag, paint if (Mbutton == 3) mode = 2; // right drag, erase mousex = Mxdrag; mousey = Mydrag; signal_thread(); } cairo_t *cr = draw_context_create(gdkwin,draw_context); // 17.04 px = mousex - Mradius - 1; // repaint modified area py = mousey - Mradius - 1; ww = 2 * Mradius + 3; Fpaint3_thread(px,py,ww,ww); // 17.01 draw_mousecircle(Mxposn,Myposn,Mradius,0,cr); // draw mouse circle draw_context_destroy(draw_context); // 17.04 LMclick = RMclick = Mxdrag = Mydrag = 0; return; } // blend image thread fuction void * bi_names::bi_thread(void *) { using namespace bi_names; while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(bi_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved } return 0; // not executed, stop warning } void * bi_names::bi_wthread(void *arg) // worker thread function { using namespace bi_names; int index = *((int *) arg); float *pix1, *pix3, *pixm; int radius, radius2, npix; int px, py, dx, dy, qx, qy, rx, ry, sx, sy; int ii, ww, hh, dist = 0; float kern, kern2, meanR, meanG, meanB; ww = E3pxm->ww; hh = E3pxm->hh; px = mousex; py = mousey; radius = Mradius; for (dy = -radius+index; dy <= radius; dy += NWT) // loop within mouse radius for (dx = -radius; dx <= radius; dx++) { qx = px + dx; qy = py + dy; if (qx < 0 || qx > ww-1) continue; // off image if (qy < 0 || qy > hh-1) continue; if (sa_stat == 3) { // select area active ii = qy * ww + qx; dist = sa_pixmap[ii]; if (! dist) continue; // pixel is outside area } kern = kernel[dx+radius][dy+radius]; // mouse transparencies if (kern > 1) continue; // outside mouse radius if (sa_stat == 3 && dist < sa_blend) // within blend distance kern = kern * sa_blendfunc(dist); pix1 = PXMpix(E1pxm,qx,qy); // original pixel pix3 = PXMpix(E3pxm,qx,qy); // edited pixel meanR = meanG = meanB = npix = 0; radius2 = sqrtf(radius); // radius = 2..99 >> radius2 = 1..9 for (ry = -radius2; ry <= radius2; ry++) for (rx = -radius2; rx <= radius2; rx++) { sx = qx + rx; sy = qy + ry; if (px - sx < -radius || px - sx > radius) continue; // outside mouse radius if (py - sy < -radius || py - sy > radius) continue; if (sx < 0 || sx > ww-1) continue; // off image if (sy < 0 || sy > hh-1) continue; pixm = PXMpix(E3pxm,sx,sy); meanR += pixm[0]; meanG += pixm[1]; meanB += pixm[2]; npix++; } if (npix == 0) continue; meanR = meanR / npix; meanG = meanG / npix; meanB = meanB / npix; if (mode == 1) { // blend kern2 = 0.5 * kern; pix3[0] = kern2 * meanR + (1.0 - kern2) * pix3[0]; // pix3 tends to regional mean pix3[1] = kern2 * meanG + (1.0 - kern2) * pix3[1]; pix3[2] = kern2 * meanB + (1.0 - kern2) * pix3[2]; } if (mode == 2) { // restore kern2 = 0.1 * kern; pix3[0] = kern2 * pix1[0] + (1.0 - kern2) * pix3[0]; // pix3 tends to pix1 pix3[1] = kern2 * pix1[1] + (1.0 - kern2) * pix3[1]; pix3[2] = kern2 * pix1[2] + (1.0 - kern2) * pix3[2]; } } exit_wthread(); return 0; } /********************************************************************************/ // add text on top of the image namespace addtext { #define Bversion ZTX("+Version") textattr_t attr; // text attributes and image char file[1000] = ""; // file for write_text data char metakey[60] = ""; int px, py; // text position on image int textpresent; // flag, text present on image int dialog_event(zdialog *zd, cchar *event); // dialog event function void mousefunc(); // mouse event function void write(int mode); // write text on image editfunc EFaddtext; } // menu function void m_add_text(GtkWidget *, cchar *menu) { using namespace addtext; cchar *title = ZTX("Add Text to Image"); cchar *tip = ZTX("Enter text, click/drag on image, right click to remove"); F1_help_topic = "add_text"; // user guide topic EFaddtext.menufunc = m_add_text; EFaddtext.funcname = "add_text"; EFaddtext.Farea = 1; // select area ignored EFaddtext.Frestart = 1; // allow restart EFaddtext.mousefunc = mousefunc; // 18.01 if (! edit_setup(EFaddtext)) return; // setup edit /*** ____________________________________________________________________ | Add Text to Image | | | | Enter text, click/drag on image, right click to remove. | | | | Use settings file [Open] [Save] | Bopen Bsave | Text [____________________________________________________] | text | Use metadata key [________________________________] [Fetch] | metakey Bfetch | [Font] [FreeSans_________] Size [ 44|v] | Bfont fontname fontsize | | | color transp. width angle | | text [#####] [_______] [______] | fgcolor fgtransp fgangle | backing [#####] [_______] | bgcolor bgtransp | outline [#####] [_______] [_______] | tocolor totransp towidth | shadow [#####] [_______] [_______] [______] | shcolor shtransp shwidth shangle | | | [Clear] [Replace] [+Version] [Next] [Apply] [Done] [Cancel] | |____________________________________________________________________| [Clear] clear text and metadata key [Replace] edit_done(), replace current file, restart dialog [+Version] edit_done(), create new file version, restart dialog [Next] edit_done(), replace current file, move to next file, restart dialog, write same text [Apply] edit_done, restart dialog [Done] edit_done() [Cancel] edit_cancel() ***/ zdialog *zd = zdialog_new(title,Mwin,Bclear,Breplace,Bversion,Bnext,Bapply,Bdone,Bcancel,null); EFaddtext.zd = zd; EFaddtext.mousefunc = mousefunc; EFaddtext.menufunc = m_add_text; // allow restart zdialog_add_widget(zd,"label","tip","dialog",tip,"space=5"); zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labfile","hbfile",ZTX("Use settings file"),"space=3"); zdialog_add_widget(zd,"button",Bopen,"hbfile",Bopen); zdialog_add_widget(zd,"button",Bsave,"hbfile",Bsave); zdialog_add_widget(zd,"hbox","hbtext","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labtext","hbtext",ZTX("Text"),"space=5"); zdialog_add_widget(zd,"frame","frtext","hbtext",0,"expand"); zdialog_add_widget(zd,"edit","text","frtext","text","expand|wrap"); zdialog_add_widget(zd,"hbox","hbmeta","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labmeta","hbmeta",ZTX("Use metadata key"),"space=5"); zdialog_add_widget(zd,"zentry","metakey","hbmeta",0,"space=2|expand"); zdialog_add_widget(zd,"button",Bfetch,"hbmeta",Bfetch); zdialog_add_widget(zd,"hbox","hbfont","dialog",0,"space=2"); zdialog_add_widget(zd,"button",Bfont,"hbfont",Bfont); zdialog_add_widget(zd,"zentry","fontname","hbfont","FreeSans","space=2|size=20"); zdialog_add_widget(zd,"label","space","hbfont",0,"space=10"); zdialog_add_widget(zd,"label","labfsize","hbfont",Bsize); zdialog_add_widget(zd,"zspin","fontsize","hbfont","8|500|1|40","space=3"); zdialog_add_widget(zd,"hbox","hbattr","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vbattr1","hbattr",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbattr2","hbattr",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbattr3","hbattr",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbattr4","hbattr",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbattr5","hbattr",0,"homog|space=2"); zdialog_add_widget(zd,"label","space","vbattr1"); zdialog_add_widget(zd,"label","labtext","vbattr1",ZTX("text")); zdialog_add_widget(zd,"label","labback","vbattr1",ZTX("backing")); zdialog_add_widget(zd,"label","laboutln","vbattr1",ZTX("outline")); zdialog_add_widget(zd,"label","labshadow","vbattr1",ZTX("shadow")); zdialog_add_widget(zd,"label","labcol","vbattr2",Bcolor); zdialog_add_widget(zd,"colorbutt","fgcolor","vbattr2","0|0|0"); zdialog_add_widget(zd,"colorbutt","bgcolor","vbattr2","255|255|255"); zdialog_add_widget(zd,"colorbutt","tocolor","vbattr2","255|0|0"); zdialog_add_widget(zd,"colorbutt","shcolor","vbattr2","255|0|0"); zdialog_add_widget(zd,"label","labtran","vbattr3","transp."); zdialog_add_widget(zd,"zspin","fgtransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"zspin","bgtransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"zspin","totransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"zspin","shtransp","vbattr3","0|100|1|0"); zdialog_add_widget(zd,"label","labw","vbattr4",Bwidth); zdialog_add_widget(zd,"label","space","vbattr4"); zdialog_add_widget(zd,"label","space","vbattr4"); zdialog_add_widget(zd,"zspin","towidth","vbattr4","0|30|1|0"); zdialog_add_widget(zd,"zspin","shwidth","vbattr4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbattr5",Bangle); zdialog_add_widget(zd,"zspin","fgangle","vbattr5","-180|180|0.5|0"); zdialog_add_widget(zd,"label","space","vbattr5"); zdialog_add_widget(zd,"label","space","vbattr5"); zdialog_add_widget(zd,"zspin","shangle","vbattr5","-180|180|1|0"); zdialog_add_ttip(zd,Breplace,ZTX("save to current file")); zdialog_add_ttip(zd,Bversion,ZTX("save as new file version")); zdialog_add_ttip(zd,Bnext,ZTX("save to current file \n" "open next file with same text")); zdialog_restore_inputs(zd); // restore prior inputs memset(&attr,0,sizeof(attr)); zdialog_fetch(zd,"text",attr.text,1000); // get defaults or prior inputs zdialog_fetch(zd,"fontname",attr.font,80); zdialog_fetch(zd,"fontsize",attr.size); zdialog_fetch(zd,"fgcolor",attr.color[0],20); zdialog_fetch(zd,"fgtransp",attr.transp[0]); zdialog_fetch(zd,"fgangle",attr.angle); zdialog_fetch(zd,"bgcolor",attr.color[1],20); zdialog_fetch(zd,"bgtransp",attr.transp[1]); zdialog_fetch(zd,"tocolor",attr.color[2],20); zdialog_fetch(zd,"totransp",attr.transp[2]); zdialog_fetch(zd,"towidth",attr.towidth); zdialog_fetch(zd,"shcolor",attr.color[3],20); zdialog_fetch(zd,"shtransp",attr.transp[3]); zdialog_fetch(zd,"shwidth",attr.shwidth); zdialog_fetch(zd,"shangle",attr.shangle); zdialog_fetch(zd,"metakey",metakey,60); gentext(&attr); // initial text takeMouse(mousefunc,dragcursor); // connect mouse function textpresent = 0; // no text on image yet zdialog_run(zd,dialog_event,"save"); // run dialog, parallel if (*metakey) zdialog_send_event(zd,Bfetch); // metadata key active, get text return; } // dialog event and completion callback function int addtext::dialog_event(zdialog *zd, cchar *event) { using namespace addtext; GtkWidget *font_dialog; char font[100]; // font name and size int size, err; char *newfilename, *pp; cchar *keyname[1]; char *keyvals[1]; if (strmatch(event,"done")) zd->zstat = 6; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 7; // cancel if (zd->zstat) { if (zd->zstat < 0 || zd->zstat > 7) zd->zstat = 7; // cancel if (zd->zstat == 1) { // clear all inputs *attr.text = 0; *metakey = 0; zdialog_stuff(zd,"text",""); zdialog_stuff(zd,"metakey",""); zd->zstat = 0; // keep dialog active } if (zd->zstat == 2) { // replace current file if (textpresent) edit_done(0); // finish else edit_cancel(0); f_save(curr_file,curr_file_type,curr_file_bpc); // replace curr. file curr_file_size = f_save_size; m_add_text(0,0); // start again return 1; } if (zd->zstat == 3) { // make a new file version if (textpresent) edit_done(0); // finish else edit_cancel(0); newfilename = file_new_version(curr_file); // get next avail. file version name if (! newfilename) return 1; err = f_save(newfilename,curr_file_type,curr_file_bpc); // save file if (! err) f_open_saved(); // open saved file with edit hist zfree(newfilename); m_add_text(0,0); // start again return 1; } if (zd->zstat == 4) { // finish and go to next image file if (textpresent) edit_done(0); // save mods else edit_cancel(0); f_save(curr_file,curr_file_type,curr_file_bpc); // replace curr. file curr_file_size = f_save_size; m_next(0,0); // open next file m_add_text(0,0); // start again write(1); // put same text etc. onto image return 1; } if (zd->zstat == 5) { // apply if (! textpresent) return 1; edit_done(0); // save mods m_add_text(0,0); // restart return 1; } if (zd->zstat == 6) { // done if (textpresent) edit_done(0); // save mods else edit_cancel(0); return 1; } if (zd->zstat == 7) { edit_cancel(0); // cancel or [x] return 1; } } if (strmatch(event,"focus")) { // toggle mouse capture takeMouse(mousefunc,dragcursor); // connect mouse function return 1; } if (strmatch(event,Bopen)) // load zdialog fields from a file { load_text(zd); zdialog_fetch(zd,"text",attr.text,1000); // get all zdialog fields zdialog_fetch(zd,"fontname",attr.font,80); zdialog_fetch(zd,"fontsize",attr.size); zdialog_fetch(zd,"fgcolor",attr.color[0],20); zdialog_fetch(zd,"fgtransp",attr.transp[0]); zdialog_fetch(zd,"fgangle",attr.angle); zdialog_fetch(zd,"bgcolor",attr.color[1],20); zdialog_fetch(zd,"bgtransp",attr.transp[1]); zdialog_fetch(zd,"tocolor",attr.color[2],20); zdialog_fetch(zd,"totransp",attr.transp[2]); zdialog_fetch(zd,"towidth",attr.towidth); zdialog_fetch(zd,"shcolor",attr.color[3],20); zdialog_fetch(zd,"shtransp",attr.transp[3]); zdialog_fetch(zd,"shwidth",attr.shwidth); zdialog_fetch(zd,"shangle",attr.shangle); } if (strmatch(event,Bsave)) { // save zdialog fields to file save_text(zd); return 1; } if (strmatch(event,Bfetch)) { // load text from metadata keyname zdialog_fetch(zd,"metakey",metakey,60); if (*metakey < ' ') return 1; keyname[0] = metakey; exif_get(curr_file,keyname,keyvals,1); if (! keyvals[0]) return 1; if (strlen(keyvals[0]) > 999) keyvals[0][999] = 0; repl_1str(keyvals[0],attr.text,"\\n","\n"); // replace "\n" with newlines zfree(keyvals[0]); zdialog_stuff(zd,"text",attr.text); // stuff dialog with metadata } if (strmatch(event,"text")) // get text from dialog zdialog_fetch(zd,"text",attr.text,1000); if (strmatch(event,Bfont)) { // select new font snprintf(font,100,"%s %d",attr.font,attr.size); font_dialog = gtk_font_chooser_dialog_new(ZTX("select font"),MWIN); gtk_font_chooser_set_font(GTK_FONT_CHOOSER(font_dialog),font); gtk_dialog_run(GTK_DIALOG(font_dialog)); pp = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(font_dialog)); gtk_widget_destroy(font_dialog); if (pp) { // should have "fontname nn" strncpy0(font,pp,100); g_free(pp); pp = font + strlen(font); while (*pp != ' ') pp--; if (pp > font) { size = atoi(pp); if (size < 8) size = 8; zdialog_stuff(zd,"fontsize",size); attr.size = size; *pp = 0; strncpy0(attr.font,font,80); // get fontname = new font name zdialog_stuff(zd,"fontname",font); } } } if (strmatch(event,"fontsize")) // new font size zdialog_fetch(zd,"fontsize",attr.size); if (strmatch(event,"fgangle")) zdialog_fetch(zd,"fgangle",attr.angle); if (strmatch(event,"fgcolor")) // foreground (text) color zdialog_fetch(zd,"fgcolor",attr.color[0],20); if (strmatch(event,"bgcolor")) // background color zdialog_fetch(zd,"bgcolor",attr.color[1],20); if (strmatch(event,"tocolor")) // text outline color zdialog_fetch(zd,"tocolor",attr.color[2],20); if (strmatch(event,"shcolor")) // text shadow color zdialog_fetch(zd,"shcolor",attr.color[3],20); if (strmatch(event,"fgtransp")) // foreground transparency zdialog_fetch(zd,"fgtransp",attr.transp[0]); if (strmatch(event,"bgtransp")) // background transparency zdialog_fetch(zd,"bgtransp",attr.transp[1]); if (strmatch(event,"totransp")) // text outline transparency zdialog_fetch(zd,"totransp",attr.transp[2]); if (strmatch(event,"shtransp")) // text shadow transparency zdialog_fetch(zd,"shtransp",attr.transp[3]); if (strmatch(event,"towidth")) // text outline width zdialog_fetch(zd,"towidth",attr.towidth); if (strmatch(event,"shwidth")) // text shadow width zdialog_fetch(zd,"shwidth",attr.shwidth); if (strmatch(event,"shangle")) // text shadow angle zdialog_fetch(zd,"shangle",attr.shangle); gentext(&attr); // build text image from text and attributes if (textpresent) write(1); // update text on image return 1; } // mouse function, set position for text on image void addtext::mousefunc() { using namespace addtext; if (LMclick) { // left mouse click px = Mxclick; // new text position on image py = Myclick; write(1); // erase old, write new text on image } if (RMclick) { // right mouse click write(2); // erase old text, if any px = py = -1; } if (Mxdrag || Mydrag) // mouse dragged { px = Mxdrag; // new text position on image py = Mydrag; write(1); // erase old, write new text on image } LMclick = RMclick = Mxdrag = Mydrag = 0; return; } // write text on image at designated location // mode: 1 erase old and write to new position // 2 erase old and write nothing void addtext::write(int mode) { using namespace addtext; float *pix1, *pix3; uint8 *pixT; int px1, py1, px3, py3, done; float e3part, Ot, Om, Ob; static int orgx1, orgy1, ww1, hh1; // old text image overlap rectangle int orgx2, orgy2, ww2, hh2; // new overlap rectangle int nc = E1pxm->nc, pcc = nc * sizeof(float); cairo_t *cr = draw_context_create(gdkwin,draw_context); if (textpresent) { for (py3 = orgy1; py3 < orgy1 + hh1; py3++) // erase prior text image for (px3 = orgx1; px3 < orgx1 + ww1; px3++) // replace E3 pixels with E1 pixels { // in prior overlap rectangle if (px3 < 0 || px3 >= E3pxm->ww) continue; if (py3 < 0 || py3 >= E3pxm->hh) continue; pix1 = PXMpix(E1pxm,px3,py3); pix3 = PXMpix(E3pxm,px3,py3); memcpy(pix3,pix1,pcc); } } done = 0; if (mode == 2) done = 1; // erase only if (! *attr.text) done = 2; // no text defined if (px < 0 && py < 0) done = 3; // no position defined if (done) { if (textpresent) { Fpaint3(orgx1,orgy1,ww1,hh1,cr); // update window to erase old text textpresent = 0; // mark no text present CEF->Fmods--; } draw_context_destroy(draw_context); return; } ww2 = attr.pxb_text->ww; // text image size hh2 = attr.pxb_text->hh; if (px > E3pxm->ww) px = E3pxm->ww; // if off screen, pull back in sight if (py > E3pxm->hh) py = E3pxm->hh; orgx2 = px - ww2/2; // copy-to image3 location orgy2 = py - hh2/2; for (py1 = 0; py1 < hh2; py1++) // loop all pixels in text image for (px1 = 0; px1 < ww2; px1++) { px3 = orgx2 + px1; // copy-to image3 pixel py3 = orgy2 + py1; if (px3 < 0 || px3 >= E3pxm->ww) continue; // omit parts beyond edges if (py3 < 0 || py3 >= E3pxm->hh) continue; pixT = PXBpix(attr.pxb_text,px1,py1); // copy-from text pixel pix3 = PXMpix(E3pxm,px3,py3); // copy-to image pixel e3part = pixT[3] / 256.0; // text image transparency pix3[0] = pixT[0] + e3part * pix3[0]; // combine text part + image part pix3[1] = pixT[1] + e3part * pix3[1]; pix3[2] = pixT[2] + e3part * pix3[2]; if (nc > 3) { Ot = (1.0 - e3part); // text opacity Om = pix3[3] / 256.0; // image opacity Ob = 1.0 - (1.0 - Ot) * (1.0 - Om); // combined opacity pix3[3] = 255.0 * Ob; } } if (textpresent) { Fpaint3(orgx1,orgy1,ww1,hh1,cr); // update window to erase old text textpresent = 0; CEF->Fmods--; } // (updates together to reduce flicker) Fpaint3(orgx2,orgy2,ww2,hh2,cr); // update window for new text draw_context_destroy(draw_context); textpresent = 1; // mark text is present CEF->Fmods++; // increase mod count CEF->Fsaved = 0; orgx1 = orgx2; // remember overlap rectangle orgy1 = orgy2; // for next call ww1 = ww2; hh1 = hh2; return; } // load text and attributes from a file void load_text(zdialog *zd) { FILE *fid; int err, nn; char *pp, *pp2, *file, buff[1200]; cchar *dialogtitle = "load text data from a file"; textattr_t attr; file = zgetfile(dialogtitle,MWIN,"file",addtext_dirk); // get input file from user if (! file) return; fid = fopen(file,"r"); // open for read if (! fid) { zmessageACK(Mwin,strerror(errno)); zfree(file); return; } pp = fgets_trim(buff,1200,fid); // read text string if (! pp) goto badfile; if (! strmatchN(pp,"text string: ",13)) goto badfile; pp += 13; if (strlen(pp) < 2) goto badfile; repl_Nstrs(pp,attr.text,"\\n","\n",null); // replace "\n" with newline char. pp = fgets_trim(buff,1200,fid); // read font and size if (! pp) goto badfile; if (! strmatchN(pp,"font: ",6)) goto badfile; pp += 6; strTrim(pp); pp2 = strstr(pp,"size: "); if (! pp2) goto badfile; *pp2 = 0; pp2 += 6; strncpy0(attr.font,pp,80); attr.size = atoi(pp2); if (attr.size < 8) goto badfile; pp = fgets_trim(buff,1200,fid); // read text attributes if (! pp) goto badfile; nn = sscanf(pp,"attributes: %f %s %s %s %s %d %d %d %d %d %d %d", &attr.angle, attr.color[0], attr.color[1], attr.color[2], attr.color[3], &attr.transp[0], &attr.transp[1], &attr.transp[2], &attr.transp[3], &attr.towidth, &attr.shwidth, &attr.shangle); if (nn != 12) goto badfile; err = fclose(fid); if (err) { zmessageACK(Mwin,strerror(errno)); zfree(file); return; } zdialog_stuff(zd,"text",attr.text); // stuff zdialog fields zdialog_stuff(zd,"fontname",attr.font); zdialog_stuff(zd,"fontsize",attr.size); zdialog_stuff(zd,"fgangle",attr.angle); zdialog_stuff(zd,"fgcolor",attr.color[0]); zdialog_stuff(zd,"bgcolor",attr.color[1]); zdialog_stuff(zd,"tocolor",attr.color[2]); zdialog_stuff(zd,"shcolor",attr.color[3]); zdialog_stuff(zd,"fgtransp",attr.transp[0]); zdialog_stuff(zd,"bgtransp",attr.transp[1]); zdialog_stuff(zd,"totransp",attr.transp[2]); zdialog_stuff(zd,"shtransp",attr.transp[3]); zdialog_stuff(zd,"towidth",attr.towidth); zdialog_stuff(zd,"shwidth",attr.shwidth); zdialog_stuff(zd,"shangle",attr.shangle); return; badfile: // project file had a problem fclose(fid); zmessageACK(Mwin,ZTX("text file is defective")); printz("buff: %s\n",buff); } // save text to a file void save_text(zdialog *zd) { cchar *dialogtitle = "save text data to a file"; FILE *fid; char *file, text2[1200]; textattr_t attr; file = zgetfile(dialogtitle,MWIN,"save",addtext_dirk); // get output file from user if (! file) return; fid = fopen(file,"w"); // open for write if (! fid) { zmessageACK(Mwin,strerror(errno)); zfree(file); return; } zdialog_fetch(zd,"text",attr.text,1000); // get text and attributes from zdialog zdialog_fetch(zd,"fontname",attr.font,80); zdialog_fetch(zd,"fontsize",attr.size); zdialog_fetch(zd,"fgangle",attr.angle); zdialog_fetch(zd,"fgcolor",attr.color[0],20); zdialog_fetch(zd,"bgcolor",attr.color[1],20); zdialog_fetch(zd,"tocolor",attr.color[2],20); zdialog_fetch(zd,"shcolor",attr.color[3],20); zdialog_fetch(zd,"fgtransp",attr.transp[0]); zdialog_fetch(zd,"bgtransp",attr.transp[1]); zdialog_fetch(zd,"totransp",attr.transp[2]); zdialog_fetch(zd,"shtransp",attr.transp[3]); zdialog_fetch(zd,"towidth",attr.towidth); zdialog_fetch(zd,"shwidth",attr.shwidth); zdialog_fetch(zd,"shangle",attr.shangle); repl_Nstrs(attr.text,text2,"\n","\\n",null); // replace newlines with "\n" fprintf(fid,"text string: %s\n",text2); fprintf(fid,"font: %s size: %d \n",attr.font, attr.size); fprintf(fid,"attributes: %.4f %s %s %s %s %d %d %d %d %d %d %d \n", attr.angle, attr.color[0], attr.color[1], attr.color[2], attr.color[3], attr.transp[0], attr.transp[1], attr.transp[2], attr.transp[3], attr.towidth, attr.shwidth, attr.shangle); fclose(fid); return; } /********************************************************************************/ /*** Create a graphic image with text, any color, any font, any angle. Add outline and shadow colors. Apply transparencies. struct textattr_t // attributes for gentext() function char text[1000]; // text to generate image from char font[80]; // font name int size; // font size int tww, thh; // generated image size, unrotated float angle; // text angle, degrees float sinT, cosT; // trig funcs for text angle char color[4][20]; // text, backing, outline, shadow "R|G|B" int transp[4]; // corresponding transparencies 0-255 int towidth; // outline width, pixels int shwidth; // shadow width int shangle; // shadow angle -180...+180 PXB *pxb_text; // image with text/outline/shadow ***/ int gentext(textattr_t *attr) { PXB * gentext_outline(textattr_t *, PXB *); PXB * gentext_shadow(textattr_t *, PXB *); PangoFontDescription *pfont; PangoLayout *playout; cairo_surface_t *surface; cairo_t *cr; float angle; int fgred, fggreen, fgblue; int bgred, bggreen, bgblue; int tored, togreen, toblue; int shred, shgreen, shblue; float fgtransp, bgtransp, totransp, shtransp; PXB *pxb_temp1, *pxb_temp2; uint8 *cairo_data, *cpix; uint8 *pix1, *pix2; int px, py, ww, hh; char font[100]; // font name and size int red, green, blue; float fgpart, topart, shpart, bgpart; if (! *attr->text) strcpy(attr->text,"null"); if (attr->pxb_text) PXB_free(attr->pxb_text); snprintf(font,100,"%s %d",attr->font,attr->size); // font name and size together angle = attr->angle; // text angle, degrees attr->sinT = sin(angle/57.296); // trig funcs for text angle attr->cosT = cos(angle/57.296); fgred = atoi(strField(attr->color[0],'|',1)); // get text foreground color fggreen = atoi(strField(attr->color[0],'|',2)); fgblue = atoi(strField(attr->color[0],'|',3)); bgred = atoi(strField(attr->color[1],'|',1)); // get text background color bggreen = atoi(strField(attr->color[1],'|',2)); bgblue = atoi(strField(attr->color[1],'|',3)); tored = atoi(strField(attr->color[2],'|',1)); // get text outline color togreen = atoi(strField(attr->color[2],'|',2)); toblue = atoi(strField(attr->color[2],'|',3)); shred = atoi(strField(attr->color[3],'|',1)); // get text shadow color shgreen = atoi(strField(attr->color[3],'|',2)); shblue = atoi(strField(attr->color[3],'|',3)); fgtransp = 0.01 * attr->transp[0]; // get transparencies bgtransp = 0.01 * attr->transp[1]; // text, background, outline, shadow totransp = 0.01 * attr->transp[2]; shtransp = 0.01 * attr->transp[3]; pfont = pango_font_description_from_string(font); // make layout with text playout = gtk_widget_create_pango_layout(Fdrawin,null); // Fdrawin instead of Cdrawin 17.08 if (! playout) zappcrash("gentext(): cannot create pango layout"); pango_layout_set_font_description(playout,pfont); pango_layout_set_text(playout,attr->text,-1); pango_layout_get_pixel_size(playout,&ww,&hh); ww += 2 + 0.2 * attr->size; // compensate bad font metrics hh += 2 + 0.1 * attr->size; attr->tww = ww; // save text image size before rotate attr->thh = hh; surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,ww,hh); // cairo output image cr = cairo_create(surface); pango_cairo_show_layout(cr,playout); // write text layout to image cairo_data = cairo_image_surface_get_data(surface); // get text image pixels pxb_temp1 = PXB_make(ww+5,hh,0); // create PXB 18.01 for (py = 0; py < hh; py++) // copy text image to PXB for (px = 0; px < ww; px++) { cpix = cairo_data + 4 * (ww * py + px); // pango output is monocolor pix2 = PXBpix(pxb_temp1,px+4,py); // small pad before text 18.01 pix2[0] = cpix[3]; // use red [0] for text intensity pix2[1] = pix2[2] = 0; } pango_font_description_free(pfont); // free resources g_object_unref(playout); cairo_destroy(cr); cairo_surface_destroy(surface); pxb_temp2 = gentext_outline(attr,pxb_temp1); // add text outline if any if (pxb_temp2) { // using green [1] for outline intensity PXB_free(pxb_temp1); pxb_temp1 = pxb_temp2; } pxb_temp2 = gentext_shadow(attr,pxb_temp1); // add text shadow color if any if (pxb_temp2) { // using blue [2] for shadow intensity PXB_free(pxb_temp1); pxb_temp1 = pxb_temp2; } if (fabsf(angle) > 0.1) { // rotate text if wanted pxb_temp2 = PXB_rotate(pxb_temp1,angle); PXB_free(pxb_temp1); pxb_temp1 = pxb_temp2; } ww = pxb_temp1->ww; // text image input PXB hh = pxb_temp1->hh; pxb_temp2 = PXB_make(ww,hh,1); // text image output PXB for (py = 0; py < hh; py++) // loop all pixels in text image for (px = 0; px < ww; px++) { pix1 = PXBpix(pxb_temp1,px,py); // copy-from pixel (text + outline + shadow) pix2 = PXBpix(pxb_temp2,px,py); // copy-to pixel fgpart = pix1[0] / 256.0; // text opacity 0-1 topart = pix1[1] / 256.0; // outline shpart = pix1[2] / 256.0; // shadow bgpart = (1.0 - fgpart - topart - shpart); // background fgpart = fgpart * (1.0 - fgtransp); // reduce for transparencies topart = topart * (1.0 - totransp); shpart = shpart * (1.0 - shtransp); bgpart = bgpart * (1.0 - bgtransp); red = fgpart * fgred + topart * tored + shpart * shred + bgpart * bgred; green = fgpart * fggreen + topart * togreen + shpart * shgreen + bgpart * bggreen; blue = fgpart * fgblue + topart * toblue + shpart * shblue + bgpart * bgblue; pix2[0] = red; // output total red, green, blue pix2[1] = green; pix2[2] = blue; pix2[3] = 255 * (1.0 - fgpart - topart - shpart - bgpart); // image part visible through text } PXB_free(pxb_temp1); attr->pxb_text = pxb_temp2; return 0; } // add an outline color to the text character edges // red color [0] is original monocolor text // use green color [1] for added outline PXB * gentext_outline(textattr_t *attr, PXB *pxb1) { PXB *pxb2; int toww, ww1, hh1, ww2, hh2; int px, py, dx, dy; uint8 *pix1, *pix2; float theta; toww = attr->towidth; // text outline color width if (toww == 0) return 0; // zero ww1 = pxb1->ww; // input PXB dimensions hh1 = pxb1->hh; ww2 = ww1 + toww * 2; // add margins for outline width hh2 = hh1 + toww * 2; pxb2 = PXB_make(ww2,hh2,0); // output PXB for (py = 0; py < hh1; py++) // copy text pixels to outline pixels for (px = 0; px < ww1; px++) // displaced by outline width { pix1 = PXBpix(pxb1,px,py); pix2 = PXBpix(pxb2,px+toww,py+toww); pix2[0] = pix1[0]; } theta = 0.7 / toww; for (theta = 0; theta < 6.3; theta += 0.7/toww) // displace outline pixels in all directions { dx = roundf(toww * sinf(theta)); dy = roundf(toww * cosf(theta)); for (py = 0; py < hh1; py++) for (px = 0; px < ww1; px++) { pix1 = PXBpix(pxb1,px,py); pix2 = PXBpix(pxb2,px+toww+dx,py+toww+dy); if (pix2[1] < pix1[0] - pix2[0]) // compare text to outline brightness pix2[1] = pix1[0] - pix2[0]; // brighter part is outline pixel } } return pxb2; // pix2[0] / pix2[1] = text / outline } // add a shadow to the text character edges // red color [0] is original monocolor text intensity // green color [1] is added outline if any // use blue color [2] for added shadow PXB * gentext_shadow(textattr_t *attr, PXB *pxb1) { PXB *pxb2; int shww, ww1, hh1, ww2, hh2; int px, py, dx, dy; uint8 *pix1, *pix2; float theta; shww = attr->shwidth; // text shadow width if (shww == 0) return 0; // zero ww1 = pxb1->ww; // input PXB dimensions hh1 = pxb1->hh; ww2 = ww1 + shww * 2; // add margins for shadow width hh2 = hh1 + shww * 2; pxb2 = PXB_make(ww2,hh2,0); // output PXB for (py = 0; py < hh1; py++) // copy text pixels to shadow pixels for (px = 0; px < ww1; px++) // displaced by shadow width { pix1 = PXBpix(pxb1,px,py); pix2 = PXBpix(pxb2,px+shww,py+shww); pix2[0] = pix1[0]; pix2[1] = pix1[1]; } theta = (90 - attr->shangle) / 57.3; // degrees to radians, 0 = to the right dx = roundf(shww * sinf(theta)); dy = roundf(shww * cosf(theta)); for (py = 0; py < hh1; py++) // displace text by shadow width for (px = 0; px < ww1; px++) { pix1 = PXBpix(pxb1,px,py); pix2 = PXBpix(pxb2,px+shww+dx,py+shww+dy); if (pix2[2] < pix1[0] + pix1[1] - pix2[0] - pix2[1]) // compare text+outline to shadow pixels pix2[2] = pix1[0] + pix1[1] - pix2[0] - pix2[1]; // brighter part is shadow pixel } return pxb2; // pix2[0] / pix2[1] / pix2[2] } // = text / outline / shadow brightness /********************************************************************************/ // write a line or arrow on top of the image namespace addline { lineattr_t attr; // line/arrow attributes and image int mpx, mpy; // mouse position on image int linepresent; // flag, line present on image int orgx1, orgy1, ww1, hh1; // old line image overlap rectangle int orgx2, orgy2, ww2, hh2; // new overlap rectangle zdialog *zd; int dialog_event(zdialog *zd, cchar *event); // dialog event function void mousefunc(); // mouse event function void write(int mode); // write line on image editfunc EFaddline; } void m_add_lines(GtkWidget *, cchar *menu) { using namespace addline; cchar *intro = ZTX("Enter line or arrow properties in dialog, \n" "click/drag on image, right click to remove"); F1_help_topic = "add_lines"; // user guide topic EFaddline.menufunc = m_add_lines; EFaddline.funcname = "add_lines"; EFaddline.Farea = 1; // select area ignored EFaddline.mousefunc = mousefunc; // 18.01 if (! edit_setup(EFaddline)) return; // setup edit /*** ___________________________________________________ | Add lines or arrows to an image | | | | Enter line or arrow properties in dialog, | | click/drag on image, right click to remove. | | | | Use settings file [Open] [Save] | | | | Line length [____] width [____] | | Arrow head [x] left [x] right | | | | color transp. width angle | | line [#####] [_______] [______] | fgcolor fgtransp fgangle | backing [#####] [_______] | bgcolor bgtransp | outline [#####] [_______] [_______] | tocolor totransp towidth | shadow [#####] [_______] [_______] [______] | shcolor shtransp shwidth shangle | | | [Apply] [Done] [Cancel] | |___________________________________________________| ***/ zd = zdialog_new(ZTX("Add lines or arrows to an image"),Mwin,Bapply,Bdone,Bcancel,null); EFaddline.zd = zd; EFaddline.mousefunc = mousefunc; EFaddline.menufunc = m_add_lines; // allow restart zdialog_add_widget(zd,"label","intro","dialog",intro,"space=3"); zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labfile","hbfile",ZTX("Use settings file"),"space=3"); zdialog_add_widget(zd,"button",Bopen,"hbfile",Bopen); zdialog_add_widget(zd,"button",Bsave,"hbfile",Bsave); zdialog_add_widget(zd,"hbox","hbline","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lablength","hbline",ZTX("Line length"),"space=5"); zdialog_add_widget(zd,"zspin","length","hbline","2|9999|1|20"); zdialog_add_widget(zd,"label","space","hbline",0,"space=10"); zdialog_add_widget(zd,"label","labwidth","hbline",Bwidth,"space=5"); zdialog_add_widget(zd,"zspin","width","hbline","1|99|1|2"); zdialog_add_widget(zd,"hbox","hbarrow","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labarrow","hbarrow",ZTX("Arrow head"),"space=5"); zdialog_add_widget(zd,"check","larrow","hbarrow",Bleft); zdialog_add_widget(zd,"label","space","hbarrow",0,"space=10"); zdialog_add_widget(zd,"check","rarrow","hbarrow",Bright); zdialog_add_widget(zd,"hbox","hbcol","dialog"); zdialog_add_widget(zd,"vbox","vbcol1","hbcol",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbcol2","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol3","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol4","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol5","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"label","space","vbcol1"); zdialog_add_widget(zd,"label","labline","vbcol1",ZTX("line")); zdialog_add_widget(zd,"label","labback","vbcol1",ZTX("backing")); zdialog_add_widget(zd,"label","laboutln","vbcol1",ZTX("outline")); zdialog_add_widget(zd,"label","labshadow","vbcol1",ZTX("shadow")); zdialog_add_widget(zd,"label","labcol","vbcol2",Bcolor); zdialog_add_widget(zd,"colorbutt","fgcolor","vbcol2","0|0|0"); zdialog_add_widget(zd,"colorbutt","bgcolor","vbcol2","255|255|255"); zdialog_add_widget(zd,"colorbutt","tocolor","vbcol2","255|0|0"); zdialog_add_widget(zd,"colorbutt","shcolor","vbcol2","255|0|0"); zdialog_add_widget(zd,"label","labcol","vbcol3","Transp."); zdialog_add_widget(zd,"zspin","fgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","bgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","totransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","shtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"label","labw","vbcol4",Bwidth); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"zspin","towidth","vbcol4","0|30|1|0"); zdialog_add_widget(zd,"zspin","shwidth","vbcol4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbcol5",Bangle); zdialog_add_widget(zd,"zspin","fgangle","vbcol5","-180|180|0.1|0"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"zspin","shangle","vbcol5","-180|180|1|0"); zdialog_add_ttip(zd,Bapply,ZTX("fix line/arrow in layout \n start new line/arrow")); zdialog_restore_inputs(zd); // restore prior inputs memset(&attr,0,sizeof(attr)); zdialog_fetch(zd,"length",attr.length); // get defaults or prior inputs zdialog_fetch(zd,"width",attr.width); zdialog_fetch(zd,"larrow",attr.larrow); zdialog_fetch(zd,"rarrow",attr.rarrow); zdialog_fetch(zd,"fgangle",attr.angle); zdialog_fetch(zd,"fgcolor",attr.color[0],20); zdialog_fetch(zd,"bgcolor",attr.color[1],20); zdialog_fetch(zd,"tocolor",attr.color[2],20); zdialog_fetch(zd,"shcolor",attr.color[3],20); zdialog_fetch(zd,"fgtransp",attr.transp[0]); zdialog_fetch(zd,"bgtransp",attr.transp[1]); zdialog_fetch(zd,"totransp",attr.transp[2]); zdialog_fetch(zd,"shtransp",attr.transp[3]); zdialog_fetch(zd,"towidth",attr.towidth); zdialog_fetch(zd,"shwidth",attr.shwidth); zdialog_fetch(zd,"shangle",attr.shangle); genline(&attr); // generate initial line takeMouse(mousefunc,dragcursor); // connect mouse function linepresent = 0; // no line on image yet mpx = mpy = -1; // no position defined yet zdialog_run(zd,dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int addline::dialog_event(zdialog *zd, cchar *event) { using namespace addline; if (strmatch(event,"done")) zd->zstat = 2; // apply and quit if (strmatch(event,"cancel")) zd->zstat = 3; // cancel if (zd->zstat) { if (zd->zstat == 1) { // Apply, commit present line to image write(1); linepresent = 0; // (no old line to erase) mpx = mpy = -1; zd->zstat = 0; edit_done(0); // finish m_add_lines(0,0); // start again return 1; } if (zd->zstat == 2 && CEF->Fmods) edit_done(0); // Done, complete pending edit else edit_cancel(0); // Cancel or kill return 1; } if (strmatch(event,"focus")) { // toggle mouse capture takeMouse(mousefunc,dragcursor); // connect mouse function return 1; } if (strmatch(event,Bopen)) // load zdialog fields from a file { load_line(zd); zdialog_fetch(zd,"length",attr.length); zdialog_fetch(zd,"width",attr.width); zdialog_fetch(zd,"larrow",attr.larrow); zdialog_fetch(zd,"rarrow",attr.rarrow); zdialog_fetch(zd,"fgcolor",attr.color[0],20); zdialog_fetch(zd,"fgtransp",attr.transp[0]); zdialog_fetch(zd,"fgangle",attr.angle); zdialog_fetch(zd,"bgcolor",attr.color[1],20); zdialog_fetch(zd,"bgtransp",attr.transp[1]); zdialog_fetch(zd,"tocolor",attr.color[2],20); zdialog_fetch(zd,"totransp",attr.transp[2]); zdialog_fetch(zd,"towidth",attr.towidth); zdialog_fetch(zd,"shcolor",attr.color[3],20); zdialog_fetch(zd,"shtransp",attr.transp[3]); zdialog_fetch(zd,"shwidth",attr.shwidth); zdialog_fetch(zd,"shangle",attr.shangle); } if (strmatch(event,Bsave)) { // save zdialog fields to file save_line(zd); return 1; } if (strmatch(event,"length")) // line length zdialog_fetch(zd,"length",attr.length); if (strmatch(event,"width")) // line width zdialog_fetch(zd,"width",attr.width); if (strmatch(event,"larrow")) // left arrow head zdialog_fetch(zd,"larrow",attr.larrow); if (strmatch(event,"rarrow")) // right arrow head zdialog_fetch(zd,"rarrow",attr.rarrow); if (strmatch(event,"fgangle")) // line angle zdialog_fetch(zd,"fgangle",attr.angle); if (strmatch(event,"fgcolor")) // foreground (line) color zdialog_fetch(zd,"fgcolor",attr.color[0],20); if (strmatch(event,"bgcolor")) // background color zdialog_fetch(zd,"bgcolor",attr.color[1],20); if (strmatch(event,"tocolor")) // line outline color zdialog_fetch(zd,"tocolor",attr.color[2],20); if (strmatch(event,"shcolor")) // line shadow color zdialog_fetch(zd,"shcolor",attr.color[3],20); if (strmatch(event,"fgtransp")) // foreground transparency zdialog_fetch(zd,"fgtransp",attr.transp[0]); if (strmatch(event,"bgtransp")) // background transparency zdialog_fetch(zd,"bgtransp",attr.transp[1]); if (strmatch(event,"totransp")) // line outline transparency zdialog_fetch(zd,"totransp",attr.transp[2]); if (strmatch(event,"shtransp")) // line shadow transparency zdialog_fetch(zd,"shtransp",attr.transp[3]); if (strmatch(event,"towidth")) // line outline width zdialog_fetch(zd,"towidth",attr.towidth); if (strmatch(event,"shwidth")) // line shadow width zdialog_fetch(zd,"shwidth",attr.shwidth); if (strmatch(event,"shangle")) // line shadow angle zdialog_fetch(zd,"shangle",attr.shangle); genline(&attr); // build line image from attributes write(1); // write on image return 1; } // mouse function, set new position for line on image void addline::mousefunc() { using namespace addline; float ax1, ay1, ax2, ay2; // line/arrow end points float angle, rad, l2; float amx, amy; float d1, d2, sinv; if (RMclick) { // right mouse click write(2); // erase old line, if any mpx = mpy = -1; LMclick = RMclick = Mxdrag = Mydrag = 0; return; } if (LMclick + Mxdrag + Mydrag == 0) return; if (LMclick) { mpx = Mxclick; // new line position on image mpy = Myclick; } else { mpx = Mxdrag; mpy = Mydrag; } LMclick = RMclick = Mxdrag = Mydrag = 0; if (! linepresent) { orgx2 = mpx; orgy2 = mpy; write(1); return; } // move the closest line endpoint to the mouse position and leave the other endpoint fixed angle = attr.angle; rad = -angle / 57.296; l2 = attr.length / 2.0; ww2 = attr.pxb_line->ww; // line image buffer hh2 = attr.pxb_line->hh; amx = ww2 / 2.0; // line midpoint within line image amy = hh2 / 2.0; ax1 = amx - l2 * cosf(rad) + 0.5; // line end points ay1 = amy + l2 * sinf(rad) + 0.5; ax2 = amx + l2 * cosf(rad) + 0.5; ay2 = amy - l2 * sinf(rad) + 0.5; d1 = (mpx-ax1-orgx1) * (mpx-ax1-orgx1) + (mpy-ay1-orgy1) * (mpy-ay1-orgy1); d2 = (mpx-ax2-orgx1) * (mpx-ax2-orgx1) + (mpy-ay2-orgy1) * (mpy-ay2-orgy1); d1 = sqrtf(d1); // mouse - end point distance d2 = sqrtf(d2); if (d1 < d2) { // move ax1/ay1 end to mouse ax2 += orgx1; ay2 += orgy1; ax1 = mpx; ay1 = mpy; attr.length = d2 + 0.5; sinv = (ay1-ay2) / d2; if (sinv > 1.0) sinv = 1.0; if (sinv < -1.0) sinv = -1.0; rad = asinf(sinv); angle = -57.296 * rad; if (mpx > ax2) angle = -180 - angle; } else { // move ax2/ay2 end to mouse ax1 += orgx1; ay1 += orgy1; ax2 = mpx; ay2 = mpy; attr.length = d1 + 0.5; sinv = (ay1-ay2) / d1; if (sinv > 1.0) sinv = 1.0; if (sinv < -1.0) sinv = -1.0; rad = asinf(sinv); angle = -57.296 * rad; if (mpx < ax1) angle = -180 - angle; } if (angle < -180) angle += 360; if (angle > 180) angle -= 360; attr.angle = angle; genline(&attr); ww2 = attr.pxb_line->ww; hh2 = attr.pxb_line->hh; amx = (ax1 + ax2) / 2.0; amy = (ay1 + ay2) / 2.0; orgx2 = amx - ww2 / 2.0; orgy2 = amy - hh2 / 2.0; write(1); zdialog_stuff(zd,"fgangle",attr.angle); zdialog_stuff(zd,"length",attr.length); return; } // write line on image at designated location // mode: 1 erase old and write to new position // 2 erase old and write nothing void addline::write(int mode) { using namespace addline; float *pix1, *pix3; uint8 *pixL; int px1, py1, px3, py3, done; float e3part, Ot, Om, Ob; int nc = E1pxm->nc, pcc = nc * sizeof(float); cairo_t *cr = draw_context_create(gdkwin,draw_context); // 17.04 if (linepresent) { for (py3 = orgy1; py3 < orgy1 + hh1; py3++) // erase prior line image for (px3 = orgx1; px3 < orgx1 + ww1; px3++) // replace E3 pixels with E1 pixels { // in prior overlap rectangle if (px3 < 0 || px3 >= E3pxm->ww) continue; if (py3 < 0 || py3 >= E3pxm->hh) continue; pix1 = PXMpix(E1pxm,px3,py3); pix3 = PXMpix(E3pxm,px3,py3); memcpy(pix3,pix1,pcc); } } done = 0; if (mode == 2) done = 1; // erase only if (mpx < 0 && mpy < 0) done = 1; // no position defined if (done) { if (linepresent) { Fpaint3(orgx1,orgy1,ww1,hh1,cr); // update window to erase old line linepresent = 0; // mark no line present } draw_context_destroy(draw_context); // 17.04 return; } ww2 = attr.pxb_line->ww; // line image buffer hh2 = attr.pxb_line->hh; for (py1 = 0; py1 < hh2; py1++) // loop all pixels in line image for (px1 = 0; px1 < ww2; px1++) { px3 = orgx2 + px1; // copy-to image3 pixel py3 = orgy2 + py1; if (px3 < 0 || px3 >= E3pxm->ww) continue; // omit parts beyond edges if (py3 < 0 || py3 >= E3pxm->hh) continue; pixL = PXBpix(attr.pxb_line,px1,py1); // copy-from line pixel pix3 = PXMpix(E3pxm,px3,py3); // copy-to image pixel e3part = pixL[3] / 256.0; // line image transparency pix3[0] = pixL[0] + e3part * pix3[0]; // combine line part + image part pix3[1] = pixL[1] + e3part * pix3[1]; pix3[2] = pixL[2] + e3part * pix3[2]; if (nc > 3) { Ot = (1.0 - e3part); // line opacity Om = pix3[3] / 256.0; // image opacity Ob = 1.0 - (1.0 - Ot) * (1.0 - Om); // combined opacity pix3[3] = 255.0 * Ob; } } if (linepresent) { Fpaint3(orgx1,orgy1,ww1,hh1,cr); // update window to erase old line linepresent = 0; } // (updates together to reduce flicker) Fpaint3(orgx2,orgy2,ww2,hh2,cr); // update window for new line draw_context_destroy(draw_context); // 17.04 CEF->Fmods++; CEF->Fsaved = 0; linepresent = 1; // mark line is present orgx1 = orgx2; // remember overlap rectangle orgy1 = orgy2; // for next call ww1 = ww2; hh1 = hh2; return; } // load line attributes from a file void load_line(zdialog *zd) { FILE *fid; int err, nn; char *pp, *file, buff[200]; cchar *dialogtitle = "load text data from a file"; lineattr_t attr; file = zgetfile(dialogtitle,MWIN,"file",addline_dirk); // get input file from user if (! file) return; fid = fopen(file,"r"); // open for read if (! fid) { zmessageACK(Mwin,strerror(errno)); zfree(file); return; } pp = fgets_trim(buff,200,fid); // read line attributes if (! pp) goto badfile; nn = sscanf(pp,"line attributes: %d %d %d %d %f %s %s %s %s %d %d %d %d %d %d %d", &attr.length, &attr.width, &attr.larrow, &attr.rarrow, &attr.angle, attr.color[0], attr.color[1], attr.color[2], attr.color[3], &attr.transp[0], &attr.transp[1], &attr.transp[2], &attr.transp[3], &attr.towidth, &attr.shwidth, &attr.shangle); if (nn != 16) goto badfile; err = fclose(fid); if (err) { zmessageACK(Mwin,strerror(errno)); zfree(file); return; } zdialog_stuff(zd,"length",attr.length); // stuff line attributes into zdialog zdialog_stuff(zd,"width",attr.width); zdialog_stuff(zd,"larrow",attr.larrow); zdialog_stuff(zd,"rarrow",attr.rarrow); zdialog_stuff(zd,"fgangle",attr.angle); zdialog_stuff(zd,"fgcolor",attr.color[0]); zdialog_stuff(zd,"bgcolor",attr.color[1]); zdialog_stuff(zd,"tocolor",attr.color[2]); zdialog_stuff(zd,"shcolor",attr.color[3]); zdialog_stuff(zd,"fgtransp",attr.transp[0]); zdialog_stuff(zd,"bgtransp",attr.transp[1]); zdialog_stuff(zd,"totransp",attr.transp[2]); zdialog_stuff(zd,"shtransp",attr.transp[3]); zdialog_stuff(zd,"towidth",attr.towidth); zdialog_stuff(zd,"shwidth",attr.shwidth); zdialog_stuff(zd,"shangle",attr.shangle); return; badfile: fclose(fid); zmessageACK(Mwin,ZTX("text file is defective")); printz("buff: %s\n",buff); } // save line attributes to a file void save_line(zdialog *zd) { cchar *dialogtitle = "save text data to a file"; FILE *fid; char *file; lineattr_t attr; file = zgetfile(dialogtitle,MWIN,"save",addline_dirk); // get output file from user if (! file) return; fid = fopen(file,"w"); // open for write if (! fid) { zmessageACK(Mwin,strerror(errno)); zfree(file); return; } zdialog_fetch(zd,"length",attr.length); // get line attributes from zdialog zdialog_fetch(zd,"width",attr.width); zdialog_fetch(zd,"larrow",attr.larrow); zdialog_fetch(zd,"rarrow",attr.rarrow); zdialog_fetch(zd,"fgangle",attr.angle); zdialog_fetch(zd,"fgcolor",attr.color[0],20); zdialog_fetch(zd,"bgcolor",attr.color[1],20); zdialog_fetch(zd,"tocolor",attr.color[2],20); zdialog_fetch(zd,"shcolor",attr.color[3],20); zdialog_fetch(zd,"fgtransp",attr.transp[0]); zdialog_fetch(zd,"bgtransp",attr.transp[1]); zdialog_fetch(zd,"totransp",attr.transp[2]); zdialog_fetch(zd,"shtransp",attr.transp[3]); zdialog_fetch(zd,"towidth",attr.towidth); zdialog_fetch(zd,"shwidth",attr.shwidth); zdialog_fetch(zd,"shangle",attr.shangle); fprintf(fid,"line attributes: %d %d %d %d %.4f %s %s %s %s %d %d %d %d %d %d %d \n", attr.length, attr.width, attr.larrow, attr.rarrow, attr.angle, attr.color[0], attr.color[1], attr.color[2], attr.color[3], attr.transp[0], attr.transp[1], attr.transp[2], attr.transp[3], attr.towidth, attr.shwidth, attr.shangle); fclose(fid); return; } /********************************************************************************/ // Create a graphic image of a line or arrow, any color, // any size, any angle. Add outline and shadow colors. int genline(lineattr_t *attr) { PXB * genline_outline(lineattr_t *, PXB *); PXB * genline_shadow(lineattr_t *, PXB *); cairo_surface_t *surface; cairo_t *cr; float angle; int fgred, fggreen, fgblue; int bgred, bggreen, bgblue; int tored, togreen, toblue; int shred, shgreen, shblue; float fgtransp, bgtransp, totransp, shtransp; PXB *pxb_temp1, *pxb_temp2; uint8 *cairo_data, *cpix; uint8 *pix1, *pix2; float length, width; int px, py, ww, hh; int red, green, blue; float fgpart, topart, shpart, bgpart; if (attr->pxb_line) PXB_free(attr->pxb_line); angle = attr->angle; // line angle, degrees attr->sinT = sin(angle/57.296); // trig funcs for line angle attr->cosT = cos(angle/57.296); fgred = atoi(strField(attr->color[0],'|',1)); // get line foreground color fggreen = atoi(strField(attr->color[0],'|',2)); fgblue = atoi(strField(attr->color[0],'|',3)); bgred = atoi(strField(attr->color[1],'|',1)); // get line background color bggreen = atoi(strField(attr->color[1],'|',2)); bgblue = atoi(strField(attr->color[1],'|',3)); tored = atoi(strField(attr->color[2],'|',1)); // get line outline color togreen = atoi(strField(attr->color[2],'|',2)); toblue = atoi(strField(attr->color[2],'|',3)); shred = atoi(strField(attr->color[3],'|',1)); // get line shadow color shgreen = atoi(strField(attr->color[3],'|',2)); shblue = atoi(strField(attr->color[3],'|',3)); fgtransp = 0.01 * attr->transp[0]; // get transparencies bgtransp = 0.01 * attr->transp[1]; // line, background, outline, shadow totransp = 0.01 * attr->transp[2]; shtransp = 0.01 * attr->transp[3]; length = attr->length; // line dimensions width = attr->width; ww = length + 20; // create cairo surface hh = width + 20; // with margins all around if (attr->larrow || attr->rarrow) // wider if arrow head used hh += width * 4; attr->lww = ww; attr->lhh = hh; surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,ww,hh); cr = cairo_create(surface); cairo_set_antialias(cr,CAIRO_ANTIALIAS_BEST); cairo_set_line_width(cr,width); cairo_set_line_cap(cr,CAIRO_LINE_CAP_ROUND); cairo_move_to(cr, 10, hh/2.0); // draw line in middle of surface cairo_line_to(cr, length+10, hh/2.0); if (attr->larrow) { // add arrow heads if req. cairo_move_to(cr, 10, hh/2.0); cairo_line_to(cr, 10 + 2 * width, hh/2.0 - 2 * width); cairo_move_to(cr, 10, hh/2.0); cairo_line_to(cr, 10 + 2 * width, hh/2.0 + 2 * width); } if (attr->rarrow) { cairo_move_to(cr, length+10, hh/2.0); cairo_line_to(cr, length+10 - 2 * width, hh/2.0 - 2 * width); cairo_move_to(cr, length+10, hh/2.0); cairo_line_to(cr, length+10 - 2 * width, hh/2.0 + 2 * width); } cairo_stroke(cr); cairo_data = cairo_image_surface_get_data(surface); // cairo image pixels pxb_temp1 = PXB_make(ww,hh,0); // create PXB for (py = 0; py < hh; py++) // copy image to PXB for (px = 0; px < ww; px++) { cpix = cairo_data + 4 * (ww * py + px); // pango output is monocolor pix2 = PXBpix(pxb_temp1,px,py); pix2[0] = cpix[3]; // use red [0] for line intensity pix2[1] = pix2[2] = 0; } cairo_destroy(cr); // free resources cairo_surface_destroy(surface); pxb_temp2 = genline_outline(attr,pxb_temp1); // add line outline if any if (pxb_temp2) { // using green [1] for outline intensity PXB_free(pxb_temp1); pxb_temp1 = pxb_temp2; } pxb_temp2 = genline_shadow(attr,pxb_temp1); // add line shadow color if any if (pxb_temp2) { // using blue [2] for shadow intensity PXB_free(pxb_temp1); pxb_temp1 = pxb_temp2; } if (fabsf(angle) > 0.1) { // rotate line if wanted pxb_temp2 = PXB_rotate(pxb_temp1,angle); PXB_free(pxb_temp1); pxb_temp1 = pxb_temp2; } ww = pxb_temp1->ww; // line image input PXB hh = pxb_temp1->hh; pxb_temp2 = PXB_make(ww,hh,1); // line image output PXB for (py = 0; py < hh; py++) // loop all pixels in line image for (px = 0; px < ww; px++) { pix1 = PXBpix(pxb_temp1,px,py); // copy-from pixel (line + outline + shadow) pix2 = PXBpix(pxb_temp2,px,py); // copy-to pixel fgpart = pix1[0] / 256.0; topart = pix1[1] / 256.0; shpart = pix1[2] / 256.0; bgpart = (1.0 - fgpart - topart - shpart); fgpart = fgpart * (1.0 - fgtransp); topart = topart * (1.0 - totransp); shpart = shpart * (1.0 - shtransp); bgpart = bgpart * (1.0 - bgtransp); red = fgpart * fgred + topart * tored + shpart * shred + bgpart * bgred; green = fgpart * fggreen + topart * togreen + shpart * shgreen + bgpart * bggreen; blue = fgpart * fgblue + topart * toblue + shpart * shblue + bgpart * bgblue; pix2[0] = red; // output total red, green blue pix2[1] = green; pix2[2] = blue; pix2[3] = 255 * (1.0 - fgpart - topart - shpart - bgpart); // image part visible through line } PXB_free(pxb_temp1); attr->pxb_line = pxb_temp2; return 0; } // add an outline color to the line edges // red color [0] is original monocolor line // use green color [1] for added outline PXB * genline_outline(lineattr_t *attr, PXB *pxb1) { PXB *pxb2; int toww, ww1, hh1, ww2, hh2; int px, py, dx, dy; uint8 *pix1, *pix2; float theta; toww = attr->towidth; // line outline color width if (toww == 0) return 0; // zero ww1 = pxb1->ww; // input PXB dimensions hh1 = pxb1->hh; ww2 = ww1 + toww * 2; // add margins for outline width hh2 = hh1 + toww * 2; pxb2 = PXB_make(ww2,hh2,0); // output PXB for (py = 0; py < hh1; py++) // copy line pixels to outline pixels for (px = 0; px < ww1; px++) // displaced by outline width { pix1 = PXBpix(pxb1,px,py); pix2 = PXBpix(pxb2,px+toww,py+toww); pix2[0] = pix1[0]; } theta = 0.7 / toww; for (theta = 0; theta < 6.3; theta += 0.7/toww) // displace outline pixels in all directions { dx = roundf(toww * sinf(theta)); dy = roundf(toww * cosf(theta)); for (py = 0; py < hh1; py++) for (px = 0; px < ww1; px++) { pix1 = PXBpix(pxb1,px,py); pix2 = PXBpix(pxb2,px+toww+dx,py+toww+dy); if (pix2[1] < pix1[0] - pix2[0]) // compare line to outline brightness pix2[1] = pix1[0] - pix2[0]; // brighter part is outline pixel } } return pxb2; // pix2[0] / pix2[1] = line / outline } // add a shadow to the line edges // red color [0] is original monocolor line intensity // green color [1] is added outline if any // use blue color [2] for added shadow PXB * genline_shadow(lineattr_t *attr, PXB *pxb1) { PXB *pxb2; int shww, ww1, hh1, ww2, hh2; int px, py, dx, dy; uint8 *pix1, *pix2; float theta; shww = attr->shwidth; // line shadow width if (shww == 0) return 0; // zero ww1 = pxb1->ww; // input PXB dimensions hh1 = pxb1->hh; ww2 = ww1 + shww * 2; // add margins for shadow width hh2 = hh1 + shww * 2; pxb2 = PXB_make(ww2,hh2,0); // output PXB for (py = 0; py < hh1; py++) // copy line pixels to shadow pixels for (px = 0; px < ww1; px++) // displaced by shadow width { pix1 = PXBpix(pxb1,px,py); pix2 = PXBpix(pxb2,px+shww,py+shww); pix2[0] = pix1[0]; pix2[1] = pix1[1]; } theta = (90 - attr->shangle) / 57.3; // degrees to radians, 0 = to the right dx = roundf(shww * sinf(theta)); dy = roundf(shww * cosf(theta)); for (py = 0; py < hh1; py++) // displace line by shadow width for (px = 0; px < ww1; px++) { pix1 = PXBpix(pxb1,px,py); pix2 = PXBpix(pxb2,px+shww+dx,py+shww+dy); if (pix2[2] < pix1[0] + pix1[1] - pix2[0] - pix2[1]) // compare line+outline to shadow pixels pix2[2] = pix1[0] + pix1[1] - pix2[0] - pix2[1]; // brighter part is shadow pixel } return pxb2; // pix2[0] / pix2[1] / pix2[2] } // = line / outline / shadow brightness /********************************************************************************/ // Select area and edit in parallel // Current edit function is applied to areas painted with the mouse. // Mouse can be weak or strong, and edits are applied incrementally. // // method: // entire image is a select area with all pixel edge distance = 0 (outside area) // blendwidth = 10000 (infinite) // pixels painted with mouse have increasing edge distance to amplify edits int paint_edits_radius; int paint_edits_cpower; int paint_edits_epower; void m_paint_edits(GtkWidget *, cchar *) // menu function { int paint_edits_dialog_event(zdialog *, cchar *event); // dialog event function void paint_edits_mousefunc(); // mouse function cchar *title = ZTX("Paint Edits"); cchar *helptext = ZTX("Press F1 for help"); int yn, cc; F1_help_topic = "paint_edits"; if (sa_stat || zdsela) { // warn select area will be lost yn = zmessageYN(Mwin,ZTX("Select area cannot be kept.\n" "Continue?")); if (! yn) return; sa_unselect(); // unselect area if (zdsela) zdialog_free(zdsela); } if (! CEF) { // edit func must be active zmessageACK(Mwin,ZTX("Edit function must be active")); return; } if (! CEF->FusePL) { zmessageACK(Mwin,ZTX("Cannot use Paint Edits")); return; } if (CEF->Fpreview) zdialog_send_event(CEF->zd,"fullsize"); // use full-size image /*** ______________________________________ | Press F1 for help | | Edit Function must be active | | | | mouse radius [____] | | power: center [____] edge [____] | | [reset area] | | [done] | |______________________________________| ***/ zdsela = zdialog_new(title,Mwin,Bdone,null); zdialog_add_widget(zdsela,"label","labhelp1","dialog",helptext,"space=5"); zdialog_add_widget(zdsela,"label","labspace","dialog"); zdialog_add_widget(zdsela,"hbox","hbr","dialog",0,"space=3"); zdialog_add_widget(zdsela,"label","labr","hbr",Bmouseradius,"space=5"); zdialog_add_widget(zdsela,"zspin","radius","hbr","2|500|1|50"); zdialog_add_widget(zdsela,"hbox","hbt","dialog",0,"space=3"); zdialog_add_widget(zdsela,"label","labtc","hbt",ZTX("power: center"),"space=5"); zdialog_add_widget(zdsela,"zspin","center","hbt","0|100|1|50"); zdialog_add_widget(zdsela,"label","labte","hbt",Bedge,"space=5"); zdialog_add_widget(zdsela,"zspin","edge","hbt","0|100|1|0"); zdialog_add_widget(zdsela,"hbox","hbra","dialog",0,"space=5"); zdialog_add_widget(zdsela,"button","reset","hbra",ZTX("reset area"),"space=5"); paint_edits_radius = 50; paint_edits_cpower = 50; paint_edits_epower = 0; cc = Fpxb->ww * Fpxb->hh * sizeof(uint16); // allocate sa_pixmap[] for area sa_pixmap = (uint16 *) zmalloc(cc); memset(sa_pixmap,0,cc); // edge distance = 0 for all pixels sa_minx = 0; // enclosing rectangle sa_maxx = Fpxb->ww; sa_miny = 0; sa_maxy = Fpxb->hh; sa_Npixel = Fpxb->ww * Fpxb->hh; sa_stat = 3; // area status = complete sa_mode = mode_image; // area mode = whole image sa_calced = 1; // edge calculation complete sa_blend = 10000; // "blend width" sa_fww = Fpxb->ww; // valid image dimensions sa_fhh = Fpxb->hh; areanumber++; // next sequential number zdialog_run(zdsela,paint_edits_dialog_event,"save"); // run dialog - parallel return; } // Adjust whole image area to increase edit power for pixels within the mouse radius // sa_pixmap[*] = 0 = never touched by mouse // = 1 = minimum edit power (barely painted) // = sa_blend = maximum edit power (edit fully applied) int paint_edits_dialog_event(zdialog *zd, cchar *event) { void paint_edits_mousefunc(); // mouse function int cc; if (zd->zstat) // done or cancel { freeMouse(); // disconnect mouse function if (CEF) zdialog_send_event(CEF->zd,"done"); // complete edit zdialog_free(zdsela); // kill dialog sa_unselect(); // unselect area return 0; } if (sa_stat != 3) return 1; // area gone if (! sa_validate()) return 1; // area invalid for curr. image file if (strmatch(event,"focus")) { // toggle mouse capture if (CEF) takeMouse(paint_edits_mousefunc,0); else freeMouse(); // disconnect mouse } if (strmatch(event,"radius")) zdialog_fetch(zd,"radius",paint_edits_radius); // set mouse radius if (strmatch(event,"center")) zdialog_fetch(zd,"center",paint_edits_cpower); // set mouse center power if (strmatch(event,"edge")) zdialog_fetch(zd,"edge",paint_edits_epower); // set mouse edge power if (strmatch(event,"reset")) { sa_unselect(); // unselect current area if any cc = Fpxb->ww * Fpxb->hh * sizeof(uint16); // allocate sa_pixmap[] for new area sa_pixmap = (uint16 *) zmalloc(cc); memset(sa_pixmap,0,cc); // edge distance = 0 for all pixels sa_minx = 0; // enclosing rectangle sa_maxx = Fpxb->ww; sa_miny = 0; sa_maxy = Fpxb->hh; sa_Npixel = Fpxb->ww * Fpxb->hh; sa_stat = 3; // area status = complete sa_mode = mode_image; // area mode = whole image sa_calced = 1; // edge calculation complete sa_blend = 10000; // "blend width" sa_fww = Fpxb->ww; // valid image dimensions sa_fhh = Fpxb->hh; areanumber++; // next sequential number } return 1; } // mouse function - adjust edit strength for areas within mouse radius // "edge distance" is increased for more strength, decreased for less void paint_edits_mousefunc() { int ii, px, py, rx, ry; int radius, radius2, cpower, epower; float rad, rad2, power; if (! CEF) return; // no active edit if (sa_stat != 3) return; // area gone? radius = paint_edits_radius; // pixel selection radius radius2 = radius * radius; cpower = paint_edits_cpower; epower = paint_edits_epower; draw_mousecircle(Mxposn,Myposn,radius,0,0); // show mouse selection circle if (LMclick || RMclick) // mouse click, process normally return; if (Mbutton != 1 && Mbutton != 3) // button released return; Mxdrag = Mydrag = 0; // neutralize drag for (rx = -radius; rx <= radius; rx++) // loop every pixel in radius for (ry = -radius; ry <= radius; ry++) { rad2 = rx * rx + ry * ry; if (rad2 > radius2) continue; // outside radius px = Mxposn + rx; py = Myposn + ry; if (px < 0 || px > Fpxb->ww-1) continue; // off the image edge if (py < 0 || py > Fpxb->hh-1) continue; ii = Fpxb->ww * py + px; rad = sqrt(rad2); power = cpower + rad / radius * (epower - cpower); // power at pixel radius if (Mbutton == 1) { // left mouse button sa_pixmap[ii] += 5.0 * power; // increase edit power if (sa_pixmap[ii] > sa_blend) sa_pixmap[ii] = sa_blend; } if (Mbutton == 3) { // right mouse button if (sa_pixmap[ii] <= 5.0 * power) sa_pixmap[ii] = 0; // weaken edit power else sa_pixmap[ii] -= 5.0 * power; } } zdialog_send_event(CEF->zd,"blendwidth"); // notify edit dialog draw_mousecircle(Mxposn,Myposn,radius,0,0); // show mouse selection circle return; } /********************************************************************************/ // Use the image brightness or color values to leverage subsequent edits. // Method: // Select the whole image as an area. // Set "edge distance" 1 to 999 from pixel brightness or RGB color. // Set "blend width" to 999. // Edit function coefficient = edge distance / blend width. spldat *leveds_curve; int leveds_type, leveds_color; int leveds_ptype, leveds_pcolor; float *leveds_lever = 0; // menu function void m_lever_edits(GtkWidget *, cchar *) { int leveds_event(zdialog *, cchar *event); // dialog event and completion func void leveds_curve_update(int spc); // curve update callback function cchar *title = ZTX("Leverage Edits"); cchar *legend = ZTX("Edit Function Amplifier"); int yn; F1_help_topic = "leverage_edits"; if (sa_stat || zdsela) { // warn select area will be lost yn = zmessageYN(Mwin,ZTX("Select area cannot be kept.\n" "Continue?")); if (! yn) return; sa_unselect(); // unselect area if (zdsela) zdialog_free(zdsela); } if (! CEF) { // edit func must be active zmessageACK(Mwin,ZTX("Edit function must be active")); return; } if (! CEF->FusePL) { zmessageACK(Mwin,ZTX("Cannot use Leverage Edits")); return; } if (CEF->Fpreview) zdialog_send_event(CEF->zd,"fullsize"); // use full-size image /*** Edit Function Amplifier ------------------------------------------ | | | | | curve drawing area | | | | | ------------------------------------------ minimum maximum (o) Brightness (o) Contrast (o) All (o) Red (o) Green (o) Blue Curve File: [ Open ] [ Save ] [ Done ] ***/ zdsela = zdialog_new(title,Mwin,Bdone,null); zdialog_add_widget(zdsela,"label","labt","dialog",legend); zdialog_add_widget(zdsela,"frame","fr1","dialog",0,"expand"); zdialog_add_widget(zdsela,"hbox","hba","dialog"); zdialog_add_widget(zdsela,"label","labda","hba",ZTX("minimum"),"space=5"); zdialog_add_widget(zdsela,"label","space","hba",0,"expand"); zdialog_add_widget(zdsela,"label","labba","hba",ZTX("maximum"),"space=5"); zdialog_add_widget(zdsela,"hbox","hbbr1","dialog"); zdialog_add_widget(zdsela,"radio","bright","hbbr1",Bbrightness,"space=5"); zdialog_add_widget(zdsela,"radio","contrast","hbbr1",Bcontrast,"space=5"); zdialog_add_widget(zdsela,"hbox","hbbr2","dialog"); zdialog_add_widget(zdsela,"radio","all","hbbr2",Ball,"space=5"); zdialog_add_widget(zdsela,"radio","red","hbbr2",Bred,"space=5"); zdialog_add_widget(zdsela,"radio","green","hbbr2",Bgreen,"space=5"); zdialog_add_widget(zdsela,"radio","blue","hbbr2",Bblue,"space=5"); zdialog_add_widget(zdsela,"hbox","hbcf","dialog",0,"space=5"); zdialog_add_widget(zdsela,"label","labcf","hbcf",Bcurvefile,"space=5"); zdialog_add_widget(zdsela,"button","loadcurve","hbcf",Bopen,"space=5"); zdialog_add_widget(zdsela,"button","savecurve","hbcf",Bsave,"space=5"); GtkWidget *frame = zdialog_widget(zdsela,"fr1"); // setup for curve editing spldat *sd = splcurve_init(frame,leveds_curve_update); leveds_curve = sd; sd->Nspc = 1; sd->vert[0] = 0; sd->nap[0] = 3; // initial curve anchor points sd->fact[0] = 1; sd->apx[0][0] = 0.01; sd->apy[0][0] = 0.5; sd->apx[0][1] = 0.50; sd->apy[0][1] = 0.5; sd->apx[0][2] = 0.99; sd->apy[0][2] = 0.5; splcurve_generate(sd,0); // generate curve data zdialog_stuff(zdsela,"bright",1); // type leverage = brightness zdialog_stuff(zdsela,"contrast",0); zdialog_stuff(zdsela,"all",1); // color used = all zdialog_stuff(zdsela,"red",0); zdialog_stuff(zdsela,"green",0); zdialog_stuff(zdsela,"blue",0); leveds_type = 1; leveds_color = 1; leveds_ptype = 0; leveds_pcolor = 0; int cc = Fpxb->ww * Fpxb->hh * sizeof(uint16); // allocate sa_pixmap[] for area sa_pixmap = (uint16 *) zmalloc(cc); memset(sa_pixmap,0,cc); // edge distance = 0 for all pixels sa_minx = 0; // enclosing rectangle sa_maxx = Fpxb->ww; sa_miny = 0; sa_maxy = Fpxb->hh; sa_Npixel = Fpxb->ww * Fpxb->hh; sa_stat = 3; // area status = complete sa_mode = mode_image; // area mode = whole image sa_calced = 1; // edge calculation complete sa_blend = 999; // "blend width" = 999 sa_fww = Fpxb->ww; // valid image dimensions sa_fhh = Fpxb->hh; areanumber++; // next sequential number if (leveds_lever) zfree(leveds_lever); cc = E1pxm->ww * E1pxm->hh * sizeof(float); // allocate memory for lever leveds_lever = (float *) zmalloc(cc); zdialog_resize(zdsela,0,360); zdialog_run(zdsela,leveds_event,"save"); // run dialog - parallel leveds_event(zdsela,"init"); // initialize default params return; } // dialog event and completion function int leveds_event(zdialog *zd, cchar *event) { int ii, kk, pixdist; float xval, yval, lever; float *pixel0, *pixel1, *pixel2, *pixel3, *pixel4; spldat *sd = leveds_curve; if (zd->zstat) { // done or cancel if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"done"); // notify edit dialog sa_unselect(); // delete area zdialog_free(zdsela); zfree(sd); // free curve edit memory zfree(leveds_lever); leveds_lever = 0; return 1; } if (! sa_validate() || sa_stat != 3) { // select area gone zdialog_free(zdsela); zfree(sd); // free curve edit memory zfree(leveds_lever); leveds_lever = 0; return 1; } if (strmatch(event,"loadcurve")) { // load saved curve splcurve_load(sd); if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"blendwidth"); // notify edit dialog return 1; } if (strmatch(event,"savecurve")) { // save curve to file splcurve_save(sd); return 1; } ii = strmatchV(event,"bright","contrast",null); // new lever type if (ii >= 1 && ii <= 2) leveds_type = ii; ii = strmatchV(event,"all","red","green","blue",null); // new lever color if (ii >= 1 && ii <= 4) leveds_color = ii; if (leveds_type != leveds_ptype || leveds_color != leveds_pcolor) // test for change { if (! CEF) return 1; // edit canceled leveds_ptype = leveds_type; leveds_pcolor = leveds_color; for (int ipy = 1; ipy < E1pxm->hh-1; ipy++) for (int ipx = 1; ipx < E1pxm->ww-1; ipx++) { pixel0 = PXMpix(E1pxm,ipx,ipy); // target pixel to measure lever = 0; if (leveds_type == 1) // lever type = brightness { if (leveds_color == 1) lever = 0.333 * (pixel0[0] + pixel0[1] + pixel0[2]); // use all colors else { ii = leveds_color - 2; // use single color lever = pixel0[ii]; } } else if (leveds_type == 2) // lever type = contrast { pixel1 = PXMpix(E1pxm,ipx-1,ipy); pixel2 = PXMpix(E1pxm,ipx+1,ipy); pixel3 = PXMpix(E1pxm,ipx,ipy-1); pixel4 = PXMpix(E1pxm,ipx,ipy+1); if (leveds_color == 1) // use all colors { lever = fabsf(pixel0[0] - pixel1[0]) + fabsf(pixel0[0] - pixel2[0]) + fabsf(pixel0[0] - pixel3[0]) + fabsf(pixel0[0] - pixel4[0]); lever += fabsf(pixel0[1] - pixel1[1]) + fabsf(pixel0[1] - pixel2[1]) + fabsf(pixel0[1] - pixel3[1]) + fabsf(pixel0[1] - pixel4[1]); lever += fabsf(pixel0[2] - pixel1[2]) + fabsf(pixel0[2] - pixel2[2]) + fabsf(pixel0[2] - pixel3[2]) + fabsf(pixel0[2] - pixel4[2]); lever = lever / 12.0; } else // use single color { ii = leveds_color - 2; lever = fabsf(pixel0[ii] - pixel1[ii]) + fabsf(pixel0[ii] - pixel2[ii]) + fabsf(pixel0[ii] - pixel3[ii]) + fabsf(pixel0[ii] - pixel4[ii]); lever = lever / 4.0; } } lever = lever / 1000.0; // scale 0.0 to 0.999 lever = pow(lever,0.3); // log scale: 0.1 >> 0.5, 0.8 >> 0.94 ii = ipy * E1pxm->ww + ipx; // save lever for each pixel leveds_lever[ii] = lever; } } if (strmatch(event,"edit")) { splcurve_generate(sd,0); // regenerate the curve gtk_widget_queue_draw(sd->drawarea); } if (! CEF) return 1; // edit canceled for (int ipy = 1; ipy < E1pxm->hh-1; ipy++) for (int ipx = 1; ipx < E1pxm->ww-1; ipx++) { pixel0 = PXMpix(E1pxm,ipx,ipy); // target pixel to measure ii = ipy * E1pxm->ww + ipx; lever = leveds_lever[ii]; // leverage to apply, 0.0 to 0.999 xval = lever; // curve x-value, 0.0 to 0.999 kk = 1000 * xval; if (kk > 999) kk = 999; yval = sd->yval[0][kk]; // y-value, 0 to 0.999 pixdist = 1 + 998 * yval; // pixel edge distance, 1 to 999 sa_pixmap[ii] = pixdist; } if (CEF->zd) zdialog_send_event(CEF->zd,"blendwidth"); // notify edit dialog return 1; } // this function is called when curve is edited using mouse void leveds_curve_update(int) { if (! zdsela) return; leveds_event(zdsela,"edit"); return; } /********************************************************************************/ // Plugin menu functions namespace plugins_names { #define maxplugins 100 int Nplugins; // plugin menu items char *plugins[100]; GtkWidget *popup_plugmenu = 0; editfunc EFplugin; } // edit plugins menu or choose and run a plugin function void m_plugins(GtkWidget *, cchar *) { using namespace plugins_names; void m_edit_plugins(GtkWidget *, cchar *); void m_run_plugin(GtkWidget *, cchar *); char plugfile[200], buff[200], *pp; FILE *fid; STATB stbuff; int ii, err; if (popup_plugmenu) { popup_menu(Mwin,popup_plugmenu); // popup the plugins menu return; } snprintf(plugfile,200,"%s/plugins",get_zhomedir()); // plugins file err = stat(plugfile,&stbuff); // exists? if (err) { fid = fopen(plugfile,"w"); // no, create default fprintf(fid,"Gimp = gimp %%s \n"); fprintf(fid,"auto-gamma = mogrify -auto-gamma %%s \n"); fprintf(fid,"whiteboard cleanup = mogrify " // ImageMagick white board cleanup "-morphology Convolve DoG:15,100,0 " "-negate -normalize -blur 0x1 -channel RBG " "-level 60%%,91%%,0.1 %%s \n"); fclose(fid); } fid = fopen(plugfile,"r"); // open plugins file if (! fid) { zmessageACK(Mwin,"plugins file: %s",strerror(errno)); return; } for (ii = 0; ii < 99; ii++) // read list of plugins { pp = fgets_trim(buff,200,fid,1); if (! pp) break; plugins[ii] = zstrdup(buff); } fclose(fid); Nplugins = ii; popup_plugmenu = create_popmenu(); // create popup menu for plugins add_popmenu_item(popup_plugmenu, ZTX("Edit Plugins"), // 1st entry is Edit Plugins m_edit_plugins, 0, ZTX("Edit plugins menu")); for (ii = 0; ii < Nplugins; ii++) // add the plugin menu functions { char *pp = strstr(plugins[ii]," = "); if (! pp) continue; *pp = 0; add_popmenu_item(popup_plugmenu, plugins[ii], m_run_plugin, 0, ZTX("Run as Fotoxx edit function")); *pp = ' '; } popup_menu(Mwin,popup_plugmenu); // popup the menu return; } // edit plugins menu void m_edit_plugins(GtkWidget *, cchar *) // overhauled { using namespace plugins_names; int edit_plugins_event(zdialog *zd, cchar *event); int ii; char *pp; zdialog *zd; F1_help_topic = "plugins"; /*** ___________________________________ | Edit Plugins | | | | menu name [_____________|v] | e.g. edit with gimp | command [___________________] | e.g. gimp %s | | | [Add] [Remove] [Done] | |___________________________________| ***/ zd = zdialog_new(ZTX("Edit Plugins"),Mwin,Badd,Bremove,Bdone,null); zdialog_add_widget(zd,"hbox","hbm","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labm","hbm",ZTX("menu name"),"space=5"); zdialog_add_widget(zd,"comboE","menuname","hbm",0,"space=5"); zdialog_add_widget(zd,"hbox","hbc","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labc","hbc",ZTX("command"),"space=5"); zdialog_add_widget(zd,"zentry","command","hbc",0,"space=5|expand"); for (ii = 0; ii < Nplugins; ii++) // stuff combo box with available menus { pp = strstr(plugins[ii]," = "); // menu name = command line if (! pp) continue; *pp = 0; zdialog_cb_app(zd,"menuname",plugins[ii]); *pp = ' '; } zdialog_set_modal(zd); // 17.08 zdialog_run(zd,edit_plugins_event,"mouse"); return; } // dialog event function int edit_plugins_event(zdialog *zd, cchar *event) { using namespace plugins_names; int ii, jj, cc, zstat; char *pp, menuname[100], command[200]; char buff[200]; FILE *fid; if (strmatch(event,"menuname")) { zdialog_fetch(zd,"menuname",menuname,100); for (ii = 0; ii < Nplugins; ii++) // find selected menu name { pp = strstr(plugins[ii]," = "); if (! pp) continue; *pp = 0; jj = strmatch(menuname,plugins[ii]); *pp = ' '; if (jj) { zdialog_stuff(zd,"command",pp+3); // stuff corresp. command in dialog break; } } return 1; } zstat = zd->zstat; // wait for dialog completion if (! zstat) return 1; if (zstat < 1 || zstat > 3) { // cancel zdialog_free(zd); return 1; } if (zstat == 1) // add plugin or replace same menu name { zd->zstat = 0; // keep dialog active if (Nplugins == maxplugins) { zmessageACK(Mwin,"too many plugins"); return 1; } zdialog_fetch(zd,"menuname",menuname,100); zdialog_fetch(zd,"command",command,200); pp = strstr(command," %s"); if (! pp) zmessageACK(Mwin,"Warning: command without \"%%s\" "); for (ii = 0; ii < Nplugins; ii++) // find existing plugin record { pp = strstr(plugins[ii]," = "); if (! pp) continue; *pp = 0; jj = strmatch(menuname,plugins[ii]); *pp = ' '; if (jj) break; } if (ii == Nplugins) { // new plugin record plugins[ii] = 0; Nplugins++; zdialog_cb_app(zd,"menuname",menuname); pp = zdialog_cb_get(zd,"menuname",ii); } if (plugins[ii]) zfree(plugins[ii]); cc = strlen(menuname) + strlen(command) + 4; plugins[ii] = (char *) zmalloc(cc); *plugins[ii] = 0; strncatv(plugins[ii],cc,menuname," = ",command,0); } if (zstat == 2) // remove current plugin { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"menuname",menuname,100); for (ii = 0; ii < Nplugins; ii++) // find existing plugin record { pp = strstr(plugins[ii]," = "); if (! pp) continue; *pp = 0; jj = strmatch(menuname,plugins[ii]); *pp = ' '; if (jj) break; } if (ii == Nplugins) return 1; // not found zfree(plugins[ii]); // remove plugin record Nplugins--; for (jj = ii; jj < Nplugins; jj++) plugins[jj] = plugins[jj+1]; zdialog_cb_delete(zd,"menuname",menuname); // delete entry from combo box zdialog_stuff(zd,"menuname",""); zdialog_stuff(zd,"command",""); } if (zstat == 3) // done { snprintf(buff,199,"%s/plugins",get_zhomedir()); // open file for plugins fid = fopen(buff,"w"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return 1; } for (int ii = 0; ii < Nplugins; ii++) // save plugins if (plugins[ii]) fprintf(fid,"%s \n",plugins[ii]); fclose(fid); zdialog_free(zd); popup_plugmenu = 0; // rebuild popup menu } return 1; } // process plugin menu selection // execute correspinding command using current image file void m_run_plugin(GtkWidget *, cchar *menu) { using namespace plugins_names; int ii, jj, err; char *pp = 0, plugincommand[200], pluginfile[100]; PXM *pxmtemp; zdialog *zd = 0; F1_help_topic = "plugins"; for (ii = 0; ii < Nplugins; ii++) // search plugins for menu name { pp = strchr(plugins[ii],'='); // match menu name to plugin command if (! pp) continue; // menu name = ... *pp = 0; jj = strmatch(plugins[ii],menu); *pp = '='; if (jj) break; } if (ii == Nplugins) { zmessageACK(Mwin,"plugin menu not found %s",menu); return; } strncpy0(plugincommand,pp+1,200); // corresp. command strTrim2(plugincommand); pp = strstr(plugincommand,"%s"); // no file placeholder in command if (! pp) { err = shell_ack(plugincommand); // execute non-edit plugin command goto RETURN; } EFplugin.menufunc = m_run_plugin; EFplugin.funcname = menu; if (! edit_setup(EFplugin)) return; // start edit function snprintf(pluginfile,100,"%s/plugfile.tif",tempdir); // /tmp/fotoxx-nnnnnn/plugfile.tif err = PXM_TIFF_save(E1pxm,pluginfile,8); // E1 >> plugin_file if (err) { if (*file_errmess) // pass error to user zmessageACK(Mwin,file_errmess); goto FAIL; } zd = zmessage_post(Mwin,0,ZTX("Plugin working ...")); repl_1str(plugincommand,command,"%s",pluginfile); // command filename err = shell_ack(command); // execute plugin command if (err) goto FAIL; pxmtemp = TIFF_PXM_load(pluginfile); // read command output file if (! pxmtemp) { zmessageACK(Mwin,ZTX("plugin failed")); goto FAIL; } PXM_free(E3pxm); // plugin_file >> E3 E3pxm = pxmtemp; CEF->Fmods = 1; // assume image was modified CEF->Fsaved = 0; edit_done(0); goto RETURN; FAIL: edit_cancel(0); RETURN: if (zd) zdialog_free(zd); return; } fotoxx-18.01.1/debian-control0000644000175000017500000000270513222767271014511 0ustar micomicoPackage: fotoxx Version: 18.01.1 Architecture: amd64 Section: graphics Installed-Size: 8948 Keywords: image, photo, edit, retouch Maintainer: Mike Cornelison Priority: extra Homepage: https://kornelix.net Depends: libc6, xdg-utils, binutils, libimage-exiftool-perl, libchamplain-gtk-0.12-0, libclutter-gtk-1.0-0 Suggests: rawtherapee, growisofs, hugin, ffmpeg, totem Description: Edit photos and manage a large collection. Survey an image collection with a thumbnail browser and navigator. View and edit image files (RAW, jpeg, png, tiff ...). Editing is done in 24 bits/color, output is 8 or 16 bits/color. Fotoxx has a large set of functions for edit, repair, and art effects. Fotoxx is fast and interactive: view full or zoomed image as it changes. Undo and redo within and across edit functions. File versioning: save and recall multiple edit stages. Select image objects or areas to edit separately from background. Copy and paste area selections within and across images. Composite functions: HDR, HDF, panorama, stack, image/text montage. Metadata edit and report: tags, captions, geocodes ... any metadata. Search images based on any metadata and folder/file names or parts. Albums: select images and arranged order with drag and drop. Slide Show with animated transitions and pan/zoom. Scalable world map with image markers - click marker to view gallery. Batch functions: file convert, resize, upright, move, revise metatata. fotoxx-18.01.1/f.albums.cc0000644000175000017500000065001713222767271013711 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit - album and slide show functions m_manage_albums manage image albums album_new create a new album album_cuttocache remove file position from album, add to cache album_pastecache add files from cache to album at position album_pastefile add file to album at position album_movefile move file in album (drag/drop same file) album_show set and show current album album_clean purge missing files from album and notify user conv_albums convert albums when files are moved select_addtocache select files, add to cache file_addtocache add image file to end of file cache clear_cache clear image file cache m_copyto_cache popup menu - add clicked or current file to cache m_album_removefile popup menu - remove clicked file from album m_album_cutfile popup menu - remove file and add to cache m_album_pastecache popup menu - paste file cache at clicked position m_update_albums update album files to last version m_replace_album_file replace album file with another file m_slideshow display album images in sequence with arty transitions *********************************************************************************/ #define EX extern // disable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // Manage Albums - create, view, edit named albums of images #define ANCC 100 // max. album name #define AFCC 200 // max. album file name (path) #define maxcache 1000 // file cache capacity namespace albums { char *albumfile = 0; // current or last open album file char *filez; zdialog *zdmanagealbums = 0; GdkDisplay *display; PIXBUF *pixbuf; GdkCursor *cursor; GdkWindow *Ggdkwin; int dragNth; char *cachefiles[maxcache]; // cache for moving image files around int Ncache = 0; // count of image files in cache char albumbuff[XFCC]; // album IO buffer }; void album_new(); // start a new album void album_cuttocache(int posn, int Faddcache); // remove from album, add to cache void album_pastecache(int posn, int Fclear); // paste image cache into album void album_clean(); // purge missing files and notify user void select_addtocache(); // select files, add to cache int file_addtocache(char *file, int posn); // add image file to file cache void clear_cache(); // clear image file cache // menu function void m_manage_albums(GtkWidget *, cchar *) { using namespace albums; int manage_albums_dialog_event(zdialog *zd, cchar *event); // manage albumd dialog event func cchar *helptext1 = ZTX("Right-click album thumbnail to cut/copy \n" "to cache, insert from cache, or remove."); cchar *helptext2 = ZTX("Right-click left/right side of album \n" "thumbnail to insert cached images \n" "before/after the thumbnail."); cchar *helptext3 = ZTX("Drag album thumbnail to new position."); F1_help_topic = "manage_albums"; if (zdmanagealbums) return; // already active if (checkpend("all")) return; // check nothing pending Fblock = 1; /*** ____________________________________________ | | | Manage Albums | | | | [ New ] Create or replace an album | | [Choose] Album to view or edit | | [ Add ] Select images, add to cache | | [Clear ] Clear image cache | | [Delete] Delete an album | | | | Image cache has NN images | | | | Right-click album thumbnail to cut/copy | | to cache, insert from cache, or remove. | | | | Right-click left/right side of album | | thumbnail to insert cached images | | before/after the thumbnail. | | | | Drag album thumbnail to new position. | | | | [done] | |____________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Manage Albums"),Mwin,Bdone,null); zdmanagealbums = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog|space=5"); zdialog_add_widget(zd,"button","new","vb1",Bnew); zdialog_add_widget(zd,"button","choose","vb1",Bchoose); zdialog_add_widget(zd,"button","add","vb1",Badd); zdialog_add_widget(zd,"button","clear","vb1",Bclear); zdialog_add_widget(zd,"button","delete","vb1",Bdelete); zdialog_add_widget(zd,"hbox","hbnew","vb2"); zdialog_add_widget(zd,"label","labnew","hbnew",ZTX("Create or replace an album")); zdialog_add_widget(zd,"hbox","hbchoose","vb2"); zdialog_add_widget(zd,"label","labchoose","hbchoose",ZTX("Album to view or edit")); zdialog_add_widget(zd,"hbox","hbadd","vb2"); zdialog_add_widget(zd,"label","labadd","hbadd",ZTX("Select images, add to cache")); zdialog_add_widget(zd,"hbox","hbclear","vb2"); zdialog_add_widget(zd,"label","labclear","hbclear",ZTX("Clear image cache")); zdialog_add_widget(zd,"hbox","hbdelete","vb2"); zdialog_add_widget(zd,"label","labdelete","hbdelete",ZTX("Delete an album")); zdialog_add_widget(zd,"hbox","hbNcache","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labNcache","hbNcache",0,"space=5"); zdialog_add_widget(zd,"hbox","hbhelp1","dialog",0,"space=8"); zdialog_add_widget(zd,"label","labhelp1","hbhelp1",helptext1,"space=5"); zdialog_add_widget(zd,"hbox","hbhelp2","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labhelp2","hbhelp2",helptext2,"space=5"); zdialog_add_widget(zd,"hbox","hbhelp3","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labhelp3","hbhelp3",helptext3,"space=5"); zdialog_run(zd,manage_albums_dialog_event,"save"); // run dialog return; } // manage albums dialog event and completion function int manage_albums_dialog_event(zdialog *zd, cchar *event) { using namespace albums; cchar *choosealbum = ZTX("Choose Album"); cchar *delalbum = ZTX("Delete an album"); cchar *ncacheFormat = ZTX("Image cache has %d images"); cchar *dumpmess = ZTX("cache added to empty album"); char *cfile, ncachetext[60]; if (zd->zstat) { // [done] or [x] zdialog_free(zd); zdmanagealbums = 0; Fblock = 0; return 1; } if (strmatch(event,"new")) // start a new album for editing { zdialog_show(zd,0); // hide manage albums dialog album_new(); // select files, add to image cache zdialog_show(zd,1); // restore manage albums dialog } if (strmatch(event,"choose")) // choose an album to view or edit { zdialog_show(zd,0); // hide manage albums dialog cfile = zgetfile(choosealbum,MWIN,"file",albums_dirk); // choose album file if (! cfile) { zdialog_show(zd,1); // bugfix 17.04.3 return 1; } if (albumfile) zfree(albumfile); // set current album for editing albumfile = cfile; album_show(); // show album zdialog_show(zd,1); // restore manage albums dialog } if (strmatch(event,"add")) // select images and add to cache { zdialog_show(zd,0); // hide manage albums dialog select_addtocache(); // select files, add to image cache if (albumfile) { album_show(); // refresh album if (navi::Nfiles == 0) { // album is empty album_pastecache(0,1); // insert cache into album zmessageACK(Mwin,dumpmess); album_show(); } } zdialog_show(zd,1); // restore manage albums dialog } if (strmatch(event,"clear")) // clear image cache clear_cache(); if (strmatch(event,"delete")) // choose album to delete { cfile = zgetfile(delalbum,MWIN,"file",albums_dirk); // choose album file if (! cfile) return 1; if (! zmessageYN(Mwin,ZTX("delete %s ?"),cfile)) return 1; if (albumfile && strmatch(cfile,albumfile)) { // 17.04 zfree(navi::galleryname); navi::galleryname = 0; navi::gallerytype = TNONE; m_viewmode(0,"F"); zfree(albumfile); albumfile = 0; } remove(cfile); zfree(cfile); } snprintf(ncachetext,60,ncacheFormat,Ncache); // update cache count zdialog_stuff(zd,"labNcache",ncachetext); return 1; } // create a new album void album_new() { using namespace albums; int album_new_dialog_event(zdialog *zd, cchar *event); char cachelab[100]; snprintf(cachelab,100,ZTX("fill from image cache (%d images)"),Ncache); /*** __________________________________________________ | Create or replace an album | | | | Album Name [____________________] [Browse] | | (o) make an initially empty album | | (o) fill from the image cache (N images) | | (o) fill from the current gallery | | | | [done] [cancel] | |__________________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Create or replace an album"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbname","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labname","hbname",ZTX("Album Name"),"space=3"); zdialog_add_widget(zd,"zentry","albumname","hbname",0,"space=3|size=20"); zdialog_add_widget(zd,"button","browse","hbname",Bbrowse,"space=3"); zdialog_add_widget(zd,"hbox","hbopt","dialog",0,"space=5"); zdialog_add_widget(zd,"label","space","hbopt",0,"space=10"); zdialog_add_widget(zd,"vbox","vbopt","hbopt"); zdialog_add_widget(zd,"radio","empty","vbopt",ZTX("make an initially empty album")); zdialog_add_widget(zd,"radio","cache","vbopt",cachelab); zdialog_add_widget(zd,"radio","gallery","vbopt",ZTX("fill from current gallery")); zdialog_stuff(zd,"empty",1); zdialog_stuff(zd,"cache",0); zdialog_stuff(zd,"gallery",0); zdialog_run(zd,album_new_dialog_event,"parent"); zdialog_wait(zd); zdialog_free(zd); return; } // dialog event and completion function int album_new_dialog_event(zdialog *zd, cchar *event) { using namespace albums; int Fempty, Fcache, Fgallery; char *cfile, *pp, albumname[ANCC], newalbumfile[AFCC]; FILE *fid; if (strmatch(event,"browse")) { cfile = zgetfile(ZTX("Album Name"),MWIN,"save",albums_dirk); // choose file name if (! cfile) return 1; pp = strrchr(cfile,'/'); if (! pp) return 1; zdialog_stuff(zd,"albumname",pp+1); zfree(cfile); return 1; } if (! zd->zstat) return 1; // wait for completion if (zd->zstat != 1) return 1; // cancel or [x] zdialog_fetch(zd,"albumname",albumname,ANCC); // get album name if (*albumname <= ' ') { zmessageACK(Mwin,ZTX("enter an album name")); zd->zstat = 0; // keep dialog active return 1; } snprintf(newalbumfile,AFCC,"%s/%s",albums_dirk,albumname); // make filespec fid = fopen(newalbumfile,"w"); // open/write empty album file if (! fid) { zmessageACK(Mwin,strerror(errno)); return 1; } fclose(fid); if (albumfile) zfree(albumfile); // set current album for editing albumfile = zstrdup(newalbumfile); zdialog_fetch(zd,"empty",Fempty); // get option zdialog_fetch(zd,"cache",Fcache); zdialog_fetch(zd,"gallery",Fgallery); if (Fcache) album_pastecache(0,1); // fill album from image cache if (Fgallery) // fill album from gallery album_from_gallery(albumfile); album_show(); // make gallery = album zmessage_post(Mwin,3,ZTX("new album created")); return 1; } // make an album from the current gallery // return 0 = OK, +N = error int album_from_gallery(cchar *newalbumfile) // 17.01 { char *pp; int Nth; FILE *fid; FTYPE ftype; if (navi::Nimages == 0) { zmessageACK(Mwin,ZTX("gallery is empty")); return 1; } fid = fopen(newalbumfile,"w"); // open/write album file if (! fid) { zmessageACK(Mwin,strerror(errno)); return 1; } for (Nth = 0; Nth < navi::Nfiles; Nth++) // add gallery images to album file { pp = gallery(0,"get",Nth); if (! pp) break; ftype = image_file_type(pp); // must be image or RAW file if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { // 17.08 zfree(pp); continue; } fprintf(fid,"%s\n",pp); zfree(pp); } fclose(fid); return 0; } // remove file at position from album, optionally add to image cache void album_cuttocache(int posn, int addcache) { using namespace albums; int ii; char *pp; if (! albumfile) return; linedit_open(albumfile); // 17.08 for (ii = 0; ; ii++) // loop album member files { pp = linedit_get(); if (! pp) break; if (ii == posn) { // position to remove if (addcache) file_addtocache(pp,posn); // add to image cache if wanted continue; // omit from copy file } linedit_put(pp); } linedit_close(); return; } // insert image cache files into album at designated position void album_pastecache(int posn, int clear) { using namespace albums; int ii, jj; char *pp; if (! albumfile) return; if (Ncache == 0) return; linedit_open(albumfile); // 17.08 for (ii = 0; ; ii++) // loop album member files { if (ii == posn) for (jj = 0; jj < Ncache; jj++) // add cached files here linedit_put(cachefiles[jj]); pp = linedit_get(); // copy rest if (! pp) break; // EOF linedit_put(pp); } linedit_close(); if (clear) clear_cache(); return; } // insert image file into album at designated position void album_pastefile(char *file, int posn) { using namespace albums; int ii; char *pp; if (! albumfile) return; if (! file) return; linedit_open(albumfile); // 17.08 for (ii = 0; ; ii++) // loop album member files { if (ii == posn) linedit_put(file); // insert current file here pp = linedit_get(); // copy album member file if (! pp) break; // EOF linedit_put(pp); } linedit_close(); return; } // move an album file from pos1 to pos2 (for drag and drop) void album_movefile(int pos1, int pos2) { using namespace albums; int ii; char *pp; if (! albumfile) return; if (pos1 == pos2) return; filez = gallery(0,"get",pos1); // file to move if (! filez) return; linedit_open(albumfile); // 17.08 for (ii = 0; ; ii++) // loop album member files { pp = linedit_get(); // album member file if (! pp) break; if (ii == pos1) continue; // skip file position to move from if (ii == pos2) { // file position to move to linedit_put(filez); // insert the moved file here pos2 = -1; } linedit_put(pp); // copy to output file } if (pos2 >= 0) linedit_put(filez); // add at the end linedit_close(); return; } // initz. gallery = current album, show gallery void album_show(char *file) { using namespace albums; int err; STATB statb; if (file) { if (albumfile) zfree(albumfile); albumfile = 0; err = stat(file,&statb); if (err) { zmessageACK(Mwin,strerror(errno)); return; } albumfile = zstrdup(file); } if (! albumfile) { // 17.04 m_viewmode(0,"F"); return; } album_clean(); // purge missing files, notify user navi::gallerytype = ALBUM; gallery(albumfile,"initF",0); // gallery = album gallery(0,"sort",-2); // recall sort and position gallery(0,"paint",-1); // paint m_viewmode(0,"G"); return; } // purge missing files from current album and notify user void album_clean() { using namespace albums; GtkWidget *popwin = 0; FTYPE ftype; char *pp, *albumname, poptitle[100]; FILE *fidr; if (! albumfile) return; fidr = fopen(albumfile,"r"); // open/read album file if (! fidr) { zmessageACK(Mwin,strerror(errno)); return; } while (true) // read album member files { pp = fgets_trim(albumbuff,XFCC,fidr); if (! pp) break; ftype = image_file_type(pp); // look for missing files if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) continue; // 17.08 if (! popwin) { // notify user albumname = strrchr(albumfile,'/'); if (albumname) albumname++; snprintf(poptitle,100,"album %s - missing files",albumname); popwin = popup_report_open(Mwin,poptitle,600,300); } popup_report_write(0,"%s \n",pp); } fclose(fidr); if (! popwin) return; // nothing missing linedit_open(albumfile); // 17.08 while (true) // copy album member files { pp = linedit_get(); if (! pp) break; ftype = image_file_type(pp); // remove deleted files if (ftype == FNF) continue; // 18.01 linedit_put(pp); } linedit_close(); return; } // Fix albums when image files have been renamed or moved. // inputs: a list of old filenames and corresponding new filenames. // used by batch_convert(). void conv_albums(char **oldfiles, char **newfiles, int nfiles) { char *pp, *albumnames[999]; int ii, jj, err; int Nalbum, contx = 0; cchar *findcomm = "find -L \"%s\" -type f"; for (contx = ii = 0; ii < 999; ii++) { pp = command_output(contx,findcomm,albums_dirk); // find all album files if (! pp) break; albumnames[ii] = pp; } if (contx) command_kill(contx); Nalbum = ii; if (! Nalbum) return; if (Nalbum == 999) zmessageACK(Mwin,"999 albums limit reached"); for (ii = 0; ii < Nalbum; ii++) // loop all albums { linedit_open(albumnames[ii]); // 17.08 while ((pp = linedit_get())) // read all album recs = image files { for (jj = 0; jj < nfiles; jj++) // list of renamed/moved files if (strmatch(pp,oldfiles[jj])) break; // includes this file? if (jj == nfiles) linedit_put(pp); // no, copy old filespec else linedit_put(newfiles[jj]); // yes, copy corresp. new filespec } err = linedit_close(); if (err) goto error; } goto cleanup; error: zmessageACK(Mwin,"%s \n %s",albumnames[ii],strerror(errno)); cleanup: for (ii = 0; ii < Nalbum; ii++) zfree(albumnames[ii]); Nalbum = 0; return; } /********************************************************************************/ // select files from gallery thumbnails, add to image cache void select_addtocache() { using namespace albums; int ii; gallery_select_clear(); // clear gallery_select() files for (ii = 0; ii < Ncache; ii++) // pre-select existing cache GSfiles[ii] = cachefiles[ii]; GScount = Ncache; Ncache = 0; gallery_select(); // select files for (ii = 0; ii < GScount; ii++) // cache = selected files cachefiles[ii] = GSfiles[ii]; Ncache = GScount; // new cache count GScount = 0; // no selected files if (zdmanagealbums) zdialog_send_event(zdmanagealbums,"cache"); return; } // add image file to image cache // returns current cache count int file_addtocache(char *file, int posn) { using namespace albums; if (Ncache == maxcache) { zmessageACK(Mwin,"max. cache exceeded: %d",maxcache); return Ncache; } cachefiles[Ncache] = zstrdup(file); ++Ncache; if (zdmanagealbums) zdialog_send_event(zdmanagealbums,"cache"); return Ncache; } // clear the image file cache void clear_cache() { using namespace albums; for (int ii = 0; ii < Ncache; ii++) zfree(cachefiles[ii]); Ncache = 0; if (zdmanagealbums) zdialog_send_event(zdmanagealbums,"cache"); return; } /********************************************************************************/ // popup menu function - add clicked file or current file to file cache void m_copyto_cache(GtkWidget *, cchar *) { if (clicked_file) { file_addtocache(clicked_file,clicked_posn); zfree(clicked_file); clicked_file = 0; } else if (curr_file) file_addtocache(curr_file,curr_file_posn); return; } // popup menu function - remove clicked file from album void m_album_removefile(GtkWidget *, cchar *menu) { using namespace albums; if (! clicked_file) return; if (navi::gallerytype != ALBUM) { zfree(clicked_file); // reset clicked file clicked_file = 0; return; } if (albumfile) zfree(albumfile); // album being edited albumfile = zstrdup(navi::galleryname); album_cuttocache(clicked_posn,0); // remove clicked file zfree(clicked_file); // reset clicked file clicked_file = 0; album_show(); return; } // popup menu function - remove clicked file from album, add to cache void m_album_cutfile(GtkWidget *, cchar *menu) { using namespace albums; if (! clicked_file) return; if (navi::gallerytype != ALBUM) { zfree(clicked_file); // reset clicked file clicked_file = 0; return; } if (albumfile) zfree(albumfile); // album being edited albumfile = zstrdup(navi::galleryname); album_cuttocache(clicked_posn,1); // remove clicked file, add to cache zfree(clicked_file); // reset clicked file clicked_file = 0; album_show(); // show revised album return; } // popup menu function - paste image cache at clicked position // optionally clear the cache void m_album_pastecache(GtkWidget *, cchar *menu) { using namespace albums; int posn, clear; if (! clicked_file) return; if (navi::gallerytype != ALBUM) { // clicked file not an album zfree(clicked_file); // reset clicked file clicked_file = 0; return; } if (albumfile) zfree(albumfile); // album being edited albumfile = zstrdup(navi::galleryname); posn = clicked_posn; if (clicked_width > 50) ++posn; // thumbnail right side clicked, bump posn if (strmatch(menu,"clear")) clear = 1; // set clear cache option else clear = 0; album_pastecache(posn,clear); // paste cached images at position zfree(clicked_file); // reset clicked file clicked_file = 0; album_show(); // show revised album return; } /********************************************************************************/ // update album files to latest file version namespace update_albums_names { char *albumfiles[1000]; int Nalbums; }; // menu function void m_update_albums(GtkWidget *, cchar *menu) { using namespace update_albums_names; using namespace albums; int update_albums_dialog_event(zdialog *zd, cchar *event); zdialog *zd; char *pp, *pp2; int ii, zstat; F1_help_topic = "update_albums"; Nalbums = 0; /*** ______________________________________ | Update Album Files | | | | [Choose Albums] N albums chosen | | | | [Proceed] [Cancel] | |______________________________________| ***/ zd = zdialog_new(ZTX("Update Album Files"),Mwin,Bproceed,Bcancel,0); zdialog_add_widget(zd,"hbox","hbchoose","dialog",0,"space=5"); zdialog_add_widget(zd,"button","choose","hbchoose",ZTX("Choose Albums"),"space=5"); zdialog_add_widget(zd,"label","labcount","hbchoose","0 albums chosen"); zdialog_run(zd,update_albums_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); if (zstat != 1) return; if (! Nalbums) return; popup_report_open(Mwin,"Album Changes",600,400); // open report window for (ii = 0; ii < Nalbums; ii++) { pp = strrchr(albumfiles[ii],'/'); // album: album-name if (pp) pp++; else pp = albumfiles[ii]; popup_report_write(0,"album: %s \n",pp); linedit_open(albumfiles[ii]); // 17.08 while ((pp = linedit_get())) // read all album recs = image files { pp2 = file_last_version(pp); // get last file version if (! pp2) continue; if (! strmatch(pp,pp2)) { // last version not same file popup_report_write(0," old file: %s \n",pp); popup_report_write(0," new file: %s \n",pp2); } linedit_put(pp2); // replace file with last version zfree(pp2); } linedit_close(); } popup_report_write(0,"COMPLETED \n"); for (ii = 0; ii < Nalbums; ii++) // free memory zfree(albumfiles[ii]); if (albumfile) album_show(); // 17.04 return; } // dialog event and completion callback function int update_albums_dialog_event(zdialog *zd, cchar *event) { using namespace update_albums_names; char **pp, countmess[40]; int ii; if (strmatch(event,"choose")) // [choose] button { for (ii = 0; ii < Nalbums; ii++) // free prior album names, if any zfree(albumfiles[ii]); Nalbums = 0; pp = zgetfiles(ZTX("Choose Albums"),MWIN,"files",albums_dirk); // choose album files if (pp) { for (ii = 0; ii < 1000 && pp[ii]; ii++) albumfiles[ii] = pp[ii]; Nalbums = ii; // album count selected zfree(pp); } snprintf(countmess,40,"%d albums chosen",Nalbums); // update dialog album count zdialog_stuff(zd,"labcount",countmess); } return 1; } /********************************************************************************/ // Replace an old file with a designated new file in selected albums, // or add the new file after the old file. namespace replace_album_file_names { char *albumfiles[1000]; char oldfile[XFCC], newfile[XFCC]; char text[200]; int Nalbums; int Fchooseall = 0; }; // menu function void m_replace_album_file(GtkWidget *, cchar *menu) // overhauled 18.01 { using namespace albums; using namespace replace_album_file_names; int replace_album_file_dialog_event(zdialog *zd, cchar *event); zdialog *zd; STATB statb; char *pp; int ii, contx, zstat, Freplace, Fchanged; cchar *findcomm = "find -L \"%s\" -type f"; F1_help_topic = "replace_album_file"; if (! clicked_file && ! curr_file) return; /*** __________________________________________________ | Replace Album File | | | | [Choose Albums] N albums chosen [All Albums] | | | | old file [__________________________] [Browse] | | new file [__________________________] [Browse] | | | | (o) replace old (o) add after old | | | | [Proceed] [Cancel] | |__________________________________________________| ***/ zd = zdialog_new(ZTX("Replace Album File"),Mwin,Bproceed,Bcancel,0); zdialog_add_widget(zd,"hbox","hbchoose","dialog",0,"space=5"); zdialog_add_widget(zd,"button","choose","hbchoose",ZTX("Choose Albums"),"space=5"); zdialog_add_widget(zd,"label","labcount","hbchoose","0 albums chosen"); zdialog_add_widget(zd,"button","choose all","hbchoose",ZTX("All Albums"),"space=20"); zdialog_add_widget(zd,"vbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbold","dialog"); zdialog_add_widget(zd,"label","labold","hbold",ZTX("old file"),"space=5"); zdialog_add_widget(zd,"zentry","oldfile","hbold",0,"expand"); zdialog_add_widget(zd,"button","browseold","hbold",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbnew","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labnew","hbnew",ZTX("new file"),"space=5"); zdialog_add_widget(zd,"zentry","newfile","hbnew",0,"expand"); zdialog_add_widget(zd,"button","browsenew","hbnew",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbrep","dialog",0,"space=10"); zdialog_add_widget(zd,"radio","replace","hbrep",ZTX("replace old"),"space=5"); zdialog_add_widget(zd,"radio","addafter","hbrep",ZTX("add after old"),"space=10"); if (clicked_file) pp = clicked_file; else if (curr_file) pp = curr_file; else return; zdialog_stuff(zd,"newfile",pp); pp = file_prior_version(pp); if (pp) { zdialog_stuff(zd,"oldfile",pp); zfree(pp); } if (clicked_file) { zfree(clicked_file); clicked_file = 0; } Nalbums = 0; zdialog_stuff(zd,"replace",1); zdialog_stuff(zd,"addafter",0); zdialog_resize(zd,500,0); zdialog_run(zd,replace_album_file_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) { zdialog_free(zd); // [cancel] or [x] return; } zdialog_fetch(zd,"oldfile",oldfile,XFCC); zdialog_fetch(zd,"newfile",newfile,XFCC); zdialog_fetch(zd,"replace",Freplace); // 17.01 zdialog_free(zd); if (stat(oldfile,&statb) || ! S_ISREG(statb.st_mode)) { zmessageACK(Mwin,Bfilenotfound2,oldfile); return; } if (stat(newfile,&statb) || ! S_ISREG(statb.st_mode)) { zmessageACK(Mwin,Bfilenotfound2,newfile); return; } if (Fchooseall) { for (contx = ii = 0; ii < 999; ii++) { pp = command_output(contx,findcomm,albums_dirk); // find all album files if (! pp) break; albumfiles[ii] = pp; } if (contx) command_kill(contx); Nalbums = ii; if (Nalbums == 999) zmessageACK(Mwin,"999 albums limit reached"); } if (! Nalbums) { zmessageACK(Mwin,ZTX("no albums chosen")); return; } popup_report_open(Mwin,"Album Changes",600,400); for (ii = 0; ii < Nalbums; ii++) { pp = strrchr(albumfiles[ii],'/'); // album: album-name if (pp) pp++; else pp = albumfiles[ii]; linedit_open(albumfiles[ii]); // 17.08 Fchanged = 0; while ((pp = linedit_get())) // read all album recs = image files { if (strmatch(pp,oldfile)) // found old file { if (Freplace) linedit_put(newfile); // replace: write new file only else { linedit_put(oldfile); // add after: write both files linedit_put(newfile); } Fchanged = 1; } else linedit_put(pp); } linedit_close(); if (Fchanged) popup_report_write(0,"album updated: %s \n",albumfiles[ii]); } popup_report_write(0,"COMPLETED \n"); for (ii = 0; ii < Nalbums; ii++) // free memory zfree(albumfiles[ii]); if (albumfile) album_show(); // 17.04 return; } // dialog event and completion callback function int replace_album_file_dialog_event(zdialog *zd, cchar *event) { using namespace replace_album_file_names; char **pp, *pp1, countmess[40]; int ii; if (strstr(event,"choose")) // "choose" or "choose all" { for (ii = 0; ii < Nalbums; ii++) // free prior album names, if any zfree(albumfiles[ii]); Nalbums = 0; Fchooseall = 0; } if (strmatch(event,"choose")) // [choose] button { pp = zgetfiles(ZTX("Choose Albums"),MWIN,"files",albums_dirk); // choose album files if (pp) { for (ii = 0; ii < 1000 && pp[ii]; ii++) albumfiles[ii] = pp[ii]; Nalbums = ii; // album count selected zfree(pp); } snprintf(countmess,40,"%d albums chosen",Nalbums); // update dialog album count zdialog_stuff(zd,"labcount",countmess); } if (strmatch(event,"choose all")) // choose all albums { zdialog_stuff(zd,"labcount",ZTX("ALL albums chosen")); Fchooseall = 1; } if (strmatch(event,"browseold")) { zdialog_fetch(zd,"oldfile",oldfile,XFCC); if (*oldfile <= ' ' && topdirks[0]) strncpy0(oldfile,topdirks[0],XFCC); pp1 = gallery_select1(oldfile); // 17.08 if (! pp1) return 1; zdialog_stuff(zd,"oldfile",pp1); zfree(pp1); } if (strmatch(event,"browsenew")) { zdialog_fetch(zd,"newfile",newfile,XFCC); if (*newfile <= ' ' && *oldfile > ' ') strncpy0(newfile,oldfile,XFCC); pp1 = gallery_select1(newfile); // 17.08 if (! pp1) return 1; zdialog_stuff(zd,"newfile",pp1); zfree(pp1); } return 1; } /********************************************************************************/ // slide show function int slideshow_dialog_event(zdialog *zd, cchar *event); // user dialogs void ss_KBprefs_dialog(); // edit KB control key preferences void ss_transprefs_dialog(); // edit transition preferences void ss_imageprefs_dialog(); // edit image preferences void ss_loadprefs(); // load preferences from file void ss_saveprefs(); // write preferences to file int ss_timerfunc(void *); // timer function int ss_nextrans(); // select next transition to use void ss_blankwindow(); // blank the window void ss_showcapcom(int mode); // show captions and comments PIXBUF *ss_loadpxb(char *file); // load image as pixbuf PIXBUF *ss_zoom_posn(char *file, int mode, float zoom, int fast); // position zoomed image in pixbuf void ss_instant(); // transition functions void ss_fadein(); void ss_rollright(); void ss_rolldown(); void ss_venetian(); void ss_grate(); void ss_rectangle(); void ss_implode(); void ss_explode(); void ss_radar(); void ss_spiral(); void ss_japfan(); void ss_jaws(); void ss_ellipse(); void ss_raindrops(); void ss_doubledoor(); void ss_rotate(); void ss_fallover(); void ss_spheroid(); void ss_turnpage(); void ss_frenchdoor(); void ss_turncube(); void ss_windmill(); void ss_pixelize(); void ss_twist(); void ss_Xopen(); void ss_squish(); void ss_zoomin(); void ss_zoomout(); char ss_albumfile[AFCC] = ""; // slide show album file char *ss_albumname = 0; // album name (ss_albumfile tail) int ss_Nfiles = 0; // album file count int ss_seconds = 0; // standard display time from user int ss_cliplimit = 10; // image clipping limit from user char ss_musicfile[500] = "none"; // /directory.../musicfile.ogg int ss_random = 0; // use random transitions option from user int ss_fullscreen = 0; // flag, full screen mode int ss_replay = 0; // flag, start over when done int ss_slowdown = 0; // transition slowdown factor 0-99 float ss_zoomsize = 1.0; // zoom-in size, 1-3.0 = 3x int ss_zoomsteps; // zoom-in steps int ss_zoomlocx, ss_zoomlocy; // zoom-in target (50/50 = image midpoint) int ss_setzloc; // 1-shot flag for image prefs dialog int ss_ww, ss_hh, ss_rs; // slide show image size, rowstride char *ss_oldfile, *ss_newfile; // image files for transition PIXBUF *ss_pxbold, *ss_pxbnew; // pixbuf images: old, new double ss_timer = 0; // slide show timer cchar *ss_state = 0; // slide show state int ss_Fcurrent = 0; // next file to show int ss_Flast = -1; // last file shown int ss_isblank = 0; // screen is blank int ss_nwt; // threads for transition funcs 18.01 int ss_block = 0; // timer function blocker 17.08 int ss_previous; // previous image >> slide show process int ss_next; // next image >> slide show process int ss_tran_next; // transition to next >> slide show process int ss_pause; // pause >> slide show process int ss_blank; // blank screen >> slide show process int ss_magnify; // magnify >> slide show process char ss_KBkeyB; // KB key: blank screen 18.01 char ss_KBkeyN; // KB key: transition to next image char ss_KBkeyP; // KB key: pause /resume char ss_KBkeyX; // KB key: magnify image #define SSNF 27 // slide show transition types #define SSMAXI 10000 // max. slide show images struct ss_trantab_t { // transition table char tranname[32]; // transition name int enabled; // enabled or not int slowdown; // slowdown factor int preference; // relative preference, 0-99 void (*func)(); // function to perform transition }; ss_trantab_t ss_trantab[SSNF]; // specific slide show transition prefs int ss_Tused[SSNF]; // list of transition types enabled int ss_Tlast[SSNF]; // last transitions used, in order int ss_Nused; // count of enabled transitions, 0-SSNF int ss_Tnext; // next transition to use >> last one used ss_trantab_t ss_trantab_default[SSNF] = // transition defaults { // name enab slow pref function // (enabled, slowdown, preference) { "instant", 1, 0, 10, ss_instant }, { "fade-in", 1, 15, 10, ss_fadein }, { "roll-right", 1, 7, 10, ss_rollright }, // NO BLANKS IN TRANSITION NAMES { "roll-down", 1, 7, 10, ss_rolldown }, { "venetian", 1, 7, 10, ss_venetian }, // transitions about 2 seconds for { "grate", 1, 5, 10, ss_grate }, // CPU = 3 GHz x 4 cores { "rectangle", 1, 6, 10, ss_rectangle }, // window = 1800 x 1200 { "implode", 1, 30, 10, ss_implode }, { "explode", 1, 30, 10, ss_explode }, { "radar", 1, 6, 10, ss_radar }, { "spiral", 1, 6, 10, ss_spiral }, { "Japan-fan", 1, 10, 10, ss_japfan }, { "jaws", 1, 8, 10, ss_jaws }, { "ellipse", 1, 16, 10, ss_ellipse }, { "raindrops", 1, 6, 10, ss_raindrops }, { "doubledoor", 1, 14, 10, ss_doubledoor }, { "rotate", 1, 5, 10, ss_rotate }, { "fallover", 1, 4, 10, ss_fallover }, { "spheroid", 1, 24, 10, ss_spheroid }, { "turn-page", 1, 18, 10, ss_turnpage }, { "french-door", 1, 20, 10, ss_frenchdoor }, { "turn-cube", 1, 21, 10, ss_turncube }, { "windmill", 1, 20, 10, ss_windmill }, { "pixelize", 1, 22, 10, ss_pixelize }, { "twist", 1, 15, 10, ss_twist }, { "Xopen", 1, 28, 10, ss_Xopen }, { "squish", 1, 16, 10, ss_squish } }; struct ss_imagetab_t { // image table char *imagefile; // image file int tone; // flag, play tone when shown int capsecs, commsecs; // seconds to show caption, comments int wait1; // seconds to wait int zoomtype; // 0/1/2 = none/zoomin/zoomout float zoomsize; // zoom-in size, 1-3.0 = 3x int zoomsteps; // zoom-in steps int zoomlocx, zoomlocy; // zoom-in target (50/50 = image midpoint) int wait2; // seconds to wait char tranname[32]; // transition type to use }; ss_imagetab_t ss_imagetab[SSMAXI]; // specific slide show image table // menu function - start or stop a slide show void m_slideshow(GtkWidget *, cchar *) // overhauled { zdialog *zd; int zstat, ii; char *pp; ZTX("instant"); // add translations to .po file ZTX("fade-in"); ZTX("roll-right"); ZTX("roll-down"); ZTX("venetian"); ZTX("grate"); ZTX("rectangle"); ZTX("implode"); ZTX("explode"); ZTX("radar"); ZTX("spiral"); ZTX("Japan-fan"); ZTX("jaws"); ZTX("ellipse"); ZTX("raindrops"); ZTX("doubledoor"); ZTX("rotate"); ZTX("fallover"); ZTX("spheroid"); ZTX("turn-page"); ZTX("french-door"); ZTX("turn-cube"); ZTX("windmill"); ZTX("pixelize"); ZTX("twist"); // ZTX("Xopen"); not translated ZTX("squish"); ss_nwt = NWT - 1; // leave one thread for x11/wayland 18.01 F1_help_topic = "slide_show"; if (checkpend("all")) return; // check nothing pending ss_KBkeyB = ss_KBkeys[0]; // unpack KB control keys 18.01 ss_KBkeyN = ss_KBkeys[1]; ss_KBkeyP = ss_KBkeys[2]; ss_KBkeyX = ss_KBkeys[3]; /*** ________________________________________________________ | [x] [-] [ ] Slide Show | | | | [Select] album-name 123 images [use gallery] | | Seconds [___] Clip Limit (%) [___] | | Music File: [_______________________________] [Browse] | | [x] Full Screen [x] Auto-replay | | Customize: [transitions] [image files] [KB controls] | // KB functions 18.01 | | | [Proceed] [Cancel] | |________________________________________________________| ***/ zd = zdialog_new(ZTX("Slide Show"),Mwin,Bproceed,Bcancel,null); // user dialog zdialog_add_widget(zd,"hbox","hbss","dialog",0,"space=3"); zdialog_add_widget(zd,"button","selectalbum","hbss",Bselect,"space=5"); zdialog_add_widget(zd,"label","albumname","hbss",Bnoselection,"space=5"); zdialog_add_widget(zd,"label","nfiles","hbss",Bnoimages,"space=5"); zdialog_add_widget(zd,"button","use gallery","hbss",ZTX("use gallery"),"space=5"); zdialog_add_widget(zd,"hbox","hbprefs","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labsecs","hbprefs",Bseconds,"space=5"); zdialog_add_widget(zd,"zspin","seconds","hbprefs","1|99|1|3"); zdialog_add_widget(zd,"label","space","hbprefs",0,"space=5"); zdialog_add_widget(zd,"label","labclip","hbprefs",ZTX("Clip Limit"),"space=5"); zdialog_add_widget(zd,"zspin","cliplim","hbprefs","0|50|1|0"); zdialog_add_widget(zd,"hbox","hbmuf","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labmf","hbmuf",ZTX("Music File"),"space=3"); zdialog_add_widget(zd,"zentry","musicfile","hbmuf","none","size=30|space=5"); zdialog_add_widget(zd,"button","browse","hbmuf",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbscreen","dialog",0,"space=2"); zdialog_add_widget(zd,"check","fullscreen","hbscreen",ZTX("Full Screen"),"space=3"); zdialog_add_widget(zd,"check","replay","hbscreen",ZTX("Auto-replay"),"space=5"); zdialog_add_widget(zd,"hbox","hbcust","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labprefs","hbcust",ZTX("Customize:"),"space=5"); zdialog_add_widget(zd,"button","transprefs","hbcust",ZTX("transitions"),"space=5"); zdialog_add_widget(zd,"button","imageprefs","hbcust",ZTX("image files"),"space=5"); zdialog_add_widget(zd,"button","KBprefs","hbcust",ZTX("KB controls"),"space=5"); zdialog_run(zd,slideshow_dialog_event,"save"); // run dialog zdialog_send_event(zd,"previous"); // use prior album if available 17.01 zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) { // cancel zdialog_free(zd); Fblock = 0; return; } if (! ss_Nfiles) { // no selection zmessageACK(Mwin,ZTX("invalid album")); zdialog_free(zd); Fblock = 0; return; } zdialog_fetch(zd,"seconds",ss_seconds); // timer interval, seconds if (ss_Nused == 0) ss_seconds = 9999; // if only arrow-keys used, huge interval zdialog_fetch(zd,"cliplim",ss_cliplimit); // image clipping limit zdialog_fetch(zd,"musicfile",ss_musicfile,500); // music file zdialog_fetch(zd,"fullscreen",ss_fullscreen); // full screen option zdialog_fetch(zd,"replay",ss_replay); // replay option (last --> first image) zdialog_free(zd); // kill dialog ss_saveprefs(); // save preference changes if (curr_file) { // start at curr. file for (ii = 0; ii < ss_Nfiles; ii++) // if member of file list if (strmatch(curr_file,ss_imagetab[ii].imagefile)) break; if (ii == ss_Nfiles) ii = 0; } else ii = 0; // else first image ss_Fcurrent = ii; // next file in list to show f_open(ss_imagetab[ii].imagefile); m_viewmode(0,"F"); // insure tab F Fblock = 1; // stop edits etc. 17.04 Fslideshow = 1; // slideshow active for KB events ss_isblank = 0; // not blank window ss_pause = 0; ss_state = "first"; Fblowup = 1; // expand small images Fzoom = 0; // fit window pp = ss_musicfile; // start music if any if (*pp == '/') shell_ack("paplay \"%s\" &",pp); ss_newfile = 0; // no new image ss_pxbnew = 0; ss_oldfile = 0; // no old (prior) image ss_pxbold = 0; if (ss_fullscreen) win_fullscreen(1); // full screen, hide menu and panel zmainsleep(0.5); ss_ww = gdk_window_get_width(gdkwin); // window size ss_hh = gdk_window_get_height(gdkwin); g_timeout_add(100,ss_timerfunc,0); // start timer for image changes return; } // dialog event function - file chooser for images to show and music file int slideshow_dialog_event(zdialog *zd, cchar *event) { char *file, *pp; char countmess[50]; int err; if (strmatch(event,"focus")) F1_help_topic = "slide_show"; if (zd->zstat == 1) { // [proceed] if (ss_Nfiles) return 1; zmessageACK(Mwin,ZTX("invalid album")); // diagnose and keep dialog open zd->zstat = 0; return 1; } if (zd->zstat) return 1; // cancel or [x] if (strmatch(event,"previous")) { if (! ss_albumname) return 1; if (strmatch(ss_albumname,"gallery")) event = "gallery"; else goto initz_album; } if (strmatch(event,"use gallery")) { snprintf(ss_albumfile,AFCC,"%s/gallery",albums_dirk); // make album named "gallery" err = album_from_gallery(ss_albumfile); if (err) return 1; pp = strrchr(ss_albumfile,'/'); // get album name ss_albumname = pp + 1; goto initz_album; } if (strmatch(event,"selectalbum")) // select a slide show album { ss_Nfiles = 0; // reset album data ss_albumfile[0] = 0; zdialog_stuff(zd,"albumname",Bnoselection); zdialog_stuff(zd,"nfiles",Bnoimages); file = zgetfile(ZTX("open album"),MWIN,"file",albums_dirk); if (! file) return 1; if (strlen(file) > 299) { zmessageACK(Mwin,"file name too long"); return 1; } strncpy0(ss_albumfile,file,AFCC); pp = strrchr(ss_albumfile,'/'); // get album name ss_albumname = pp + 1; zfree(file); goto initz_album; } if (strmatch(event,"browse")) { // browse for music file pp = ss_musicfile; pp = zgetfile(ZTX("Select music file"),MWIN,"file",pp); if (! pp) pp = zstrdup("none"); zdialog_stuff(zd,"musicfile",pp); zfree(pp); } if (strmatch(event,"KBprefs")) // 18.01 ss_KBprefs_dialog(); if (! ss_Nfiles) return 1; if (strmatch(event,"transprefs")) { // edit transition preferences zdialog_show(zd,0); ss_transprefs_dialog(); zdialog_show(zd,1); } if (strmatch(event,"imageprefs")) { // edit image preferences zdialog_show(zd,0); ss_imageprefs_dialog(); zdialog_show(zd,1); } return 1; initz_album: ss_loadprefs(); // get slide show prefs or defaults if (! ss_Nfiles) return 1; zdialog_stuff(zd,"albumname",ss_albumname); // update dialog album data snprintf(countmess,50,ZTX("%d images"),ss_Nfiles); zdialog_stuff(zd,"nfiles",countmess); zdialog_stuff(zd,"seconds",ss_seconds); zdialog_stuff(zd,"cliplim",ss_cliplimit); zdialog_stuff(zd,"musicfile",ss_musicfile); zdialog_stuff(zd,"fullscreen",ss_fullscreen); zdialog_stuff(zd,"replay",ss_replay); navi::gallerytype = ALBUM; // open gallery with slide show album gallery(ss_albumfile,"initF",0); gallery(0,"paint",0); m_viewmode(0,"G"); return 1; } // ------------------------------------------------------------------------------ // set preferences for keyboard control keys (blank screen, next image, pause/resume, magnify) void ss_KBprefs_dialog() { int KBprefs_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int zstat; char keyx[4] = "X"; cchar *arrowmess = ZTX("arrow keys show previous or next image instantly"); /*** ___________________________________________________ | Keyboard Preferences | | | | [ B ] blank or unblank window | | [ N ] show next image, with transition | | [ P ] pause or resume slide show | | [ X ] magnify image (loupe tool) | | | | arrow keys show previous or next image instantly | | | | [done] [camcel] | |___________________________________________________| ***/ zd = zdialog_new(ZTX("Keyboard Preferences"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbB","dialog"); zdialog_add_widget(zd,"edit","B","hbB","B","space=5|size=2"); zdialog_add_widget(zd,"label","labB","hbB",ZTX("blank or unblank window")); zdialog_add_widget(zd,"hbox","hbN","dialog"); zdialog_add_widget(zd,"zentry","N","hbN","N","space=5|size=2"); zdialog_add_widget(zd,"label","labN","hbN",ZTX("show next image, with transition")); zdialog_add_widget(zd,"hbox","hbP","dialog"); zdialog_add_widget(zd,"zentry","P","hbP","P","space=5|size=2"); zdialog_add_widget(zd,"label","labP","hbP",ZTX("pause or resume slide show")); zdialog_add_widget(zd,"hbox","hbX","dialog"); zdialog_add_widget(zd,"zentry","X","hbX","X","space=5|size=2"); zdialog_add_widget(zd,"label","labX","hbX",ZTX("magnify image (loupe tool)")); zdialog_add_widget(zd,"label","labak","dialog",arrowmess,"space=5"); keyx[0] = ss_KBkeyB; zdialog_stuff(zd,"B",keyx); keyx[0] = ss_KBkeyN; zdialog_stuff(zd,"N",keyx); keyx[0] = ss_KBkeyP; zdialog_stuff(zd,"P",keyx); keyx[0] = ss_KBkeyX; zdialog_stuff(zd,"X",keyx); zdialog_run(zd,KBprefs_dialog_event,"parent"); zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"B",keyx,4); ss_KBkeyB = ss_KBkeys[0] = keyx[0]; zdialog_fetch(zd,"N",keyx,4); ss_KBkeyN = ss_KBkeys[1] = keyx[0]; zdialog_fetch(zd,"P",keyx,4); ss_KBkeyP = ss_KBkeys[2] = keyx[0]; zdialog_fetch(zd,"X",keyx,4); ss_KBkeyX = ss_KBkeys[3] = keyx[0]; return; } // dialog event and completion function int KBprefs_dialog_event(zdialog *zd, cchar *event) { char keyx[4]; if (zd->zstat) { zdialog_destroy(zd); return 1; } if (strstr("B N P X",event)) { zdialog_fetch(zd,event,keyx,4); strTrim2(keyx); keyx[0] = toupper(keyx[0]); zdialog_stuff(zd,event,keyx); } return 1; } // ------------------------------------------------------------------------------ // set transitions preferences for specific slide show void ss_transprefs_dialog() { int transprefs_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int ii, jj, zstat; char nameii[SSNF], enabii[SSNF], slowii[SSNF], prefii[SSNF]; cchar *randmess = ZTX("random sequence"); /*** ______________________________________________________________________ | | | Transitions File [load] [save] | | | | enable [all] [none] [x] random sequence | | | | transition use slow pref | transition use slow pref | | instant [x] [ 1 ] [ 10 ] | raindrops [x] [ 5 ] [ 10 ] | | fade-in [x] [ 2 ] [ 20 ] | doubledoor [x] [ 9 ] [ 20 ] | | roll-right [x] [ 3 ] [ 0 ] | rotate [ ] [ 6 ] [ 50 ] | | .... ... ... ... | .... ... ... ... | | | | [done] [cancel] | |______________________________________________________________________| ***/ if (! ss_Nfiles) { zmessageACK(Mwin,ZTX("invalid album")); return; } zd = zdialog_new(ZTX("Transition Preferences"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labfile","hbfile",ZTX("Transitions File"),"space=3"); zdialog_add_widget(zd,"button","load","hbfile",Bload,"space=3"); zdialog_add_widget(zd,"button","save","hbfile",Bsave,"space=3"); zdialog_add_widget(zd,"hbox","hbenab","dialog",0,"space=3"); // 17.01 zdialog_add_widget(zd,"label","labenab","hbenab",Bselect,"space=3"); zdialog_add_widget(zd,"button","all","hbenab",Ball,"space=3"); zdialog_add_widget(zd,"button","none","hbenab",Bnone,"space=3"); zdialog_add_widget(zd,"check","rand","hbenab",randmess,"space=10"); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb3","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb4","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vsep","vs1","hb1",0,"space=5"); // two columns of transition poop 17.08 zdialog_add_widget(zd,"vbox","vb5","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb6","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb7","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"vbox","vb8","hb1",0,"space=5|homog"); zdialog_add_widget(zd,"label","labname","vb1",ZTX("transition")); zdialog_add_widget(zd,"label","labenab","vb2",ZTX("use")); zdialog_add_widget(zd,"label","labslow","vb3",ZTX("slow")); zdialog_add_widget(zd,"label","labpref","vb4",ZTX("pref")); zdialog_add_widget(zd,"label","labname","vb5",ZTX("transition")); zdialog_add_widget(zd,"label","labenab","vb6",ZTX("use")); zdialog_add_widget(zd,"label","labslow","vb7",ZTX("slow")); zdialog_add_widget(zd,"label","labpref","vb8",ZTX("pref")); zdialog_stuff(zd,"rand",ss_random); // stuff random checkbox for (ii = 0; ii < SSNF; ii++) { // build input dialog for transition prefs snprintf(nameii,SSNF,"name_%d",ii); snprintf(enabii,SSNF,"enab_%d",ii); snprintf(slowii,SSNF,"slow_%d",ii); snprintf(prefii,SSNF,"pref_%d",ii); if (ii <= SSNF / 2) { // 17.08 zdialog_add_widget(zd,"label",nameii,"vb1","transition"); zdialog_add_widget(zd,"check",enabii,"vb2"); zdialog_add_widget(zd,"zspin",slowii,"vb3","0|99|1|1","size=3"); zdialog_add_widget(zd,"zspin",prefii,"vb4","0|99|1|10","size=3"); } else { zdialog_add_widget(zd,"label",nameii,"vb5","transition"); zdialog_add_widget(zd,"check",enabii,"vb6"); zdialog_add_widget(zd,"zspin",slowii,"vb7","0|99|1|1","size=3"); zdialog_add_widget(zd,"zspin",prefii,"vb8","0|99|1|10","size=3"); } zdialog_stuff(zd,nameii,ZTX(ss_trantab[ii].tranname)); // stuff current transition prefs zdialog_stuff(zd,enabii,ss_trantab[ii].enabled); zdialog_stuff(zd,slowii,ss_trantab[ii].slowdown); zdialog_stuff(zd,prefii,ss_trantab[ii].preference); } zdialog_run(zd,transprefs_dialog_event,"parent"); // run dialog, wait for completion zstat = zdialog_wait(zd); if (zstat != 1) { zdialog_free(zd); return; } zdialog_fetch(zd,"rand",ss_random); // get mode, 0/1 = sequential/random for (ii = 0; ii < SSNF; ii++) { // get all preferences snprintf(enabii,SSNF,"enab_%d",ii); snprintf(slowii,SSNF,"slow_%d",ii); snprintf(prefii,SSNF,"pref_%d",ii); zdialog_fetch(zd,enabii,ss_trantab[ii].enabled); zdialog_fetch(zd,slowii,ss_trantab[ii].slowdown); zdialog_fetch(zd,prefii,ss_trantab[ii].preference); } zdialog_free(zd); ss_saveprefs(); // update preferences file for (ii = jj = 0; ii < SSNF; ii++) { // initialize list of enabled if (ss_trantab[ii].enabled) { // and last used transition types ss_Tused[jj] = ii; jj++; } ss_Tlast[ii] = 0; } ss_Nused = jj; // no. enabled transition types ss_Tnext = 0; // next one to use (first) return; } // transition prefs dialog event and completion function int transprefs_dialog_event(zdialog *zd, cchar *event) { int transprefs_load(FILE *fid); int transprefs_save(FILE *fid); FILE *fid; int err, ii; char *file; char nameii[SSNF], enabii[SSNF], slowii[SSNF], prefii[SSNF]; if (strmatch(event,"all")) // enable all transitions 17.01 { for (ii = 0; ii < SSNF; ii++) { snprintf(enabii,SSNF,"enab_%d",ii); zdialog_stuff(zd,enabii,1); } } if (strmatch(event,"none")) // disable all transitions 17.01 { for (ii = 0; ii < SSNF; ii++) { snprintf(enabii,SSNF,"enab_%d",ii); zdialog_stuff(zd,enabii,0); } } if (strmatch(event,"load")) // load trans prefs from a file { file = zgetfile("load",MWIN,"file",slideshow_trans_dirk,0); // open trans prefs file if (! file) return 1; fid = fopen(file,"r"); if (! fid) { zmessageACK(Mwin,ZTX("invalid file")); return 1; } err = transprefs_load(fid); // load file into dialog fclose(fid); if (err) return 1; zdialog_stuff(zd,"rand",ss_random); // stuff random/sequential mode for (ii = 0; ii < SSNF; ii++) { // build input dialog for transition prefs snprintf(nameii,SSNF,"name_%d",ii); snprintf(enabii,SSNF,"enab_%d",ii); snprintf(slowii,SSNF,"slow_%d",ii); snprintf(prefii,SSNF,"pref_%d",ii); zdialog_stuff(zd,nameii,ZTX(ss_trantab[ii].tranname)); // stuff current transition prefs zdialog_stuff(zd,enabii,ss_trantab[ii].enabled); zdialog_stuff(zd,slowii,ss_trantab[ii].slowdown); zdialog_stuff(zd,prefii,ss_trantab[ii].preference); } } if (strmatch(event,"save")) // save trans prefs to a file { zdialog_fetch(zd,"rand",ss_random); // get random/sequential mode for (ii = 0; ii < SSNF; ii++) { // get all preferences snprintf(enabii,SSNF,"enab_%d",ii); snprintf(slowii,SSNF,"slow_%d",ii); snprintf(prefii,SSNF,"pref_%d",ii); zdialog_fetch(zd,enabii,ss_trantab[ii].enabled); zdialog_fetch(zd,slowii,ss_trantab[ii].slowdown); zdialog_fetch(zd,prefii,ss_trantab[ii].preference); } file = zgetfile("save",MWIN,"save",slideshow_trans_dirk,0); // save trans prefs file if (! file) return 1; fid = fopen(file,"w"); if (! fid) { zmessageACK(Mwin,ZTX("invalid file")); return 1; } transprefs_save(fid); // save dialog to file fclose(fid); } return 1; } // load transition prefs from a file // returns 0 = OK, +N = error int transprefs_load(FILE *fid) { char *pp, buff[XFCC]; int ii, jj, nn; char tranname[32]; int n1, n2, n3; pp = fgets_trim(buff,XFCC,fid,1); if (! pp) goto format_error; nn = sscanf(buff,"random %d ",&ss_random); if (nn != 1) goto format_error; while (true) { pp = fgets_trim(buff,XFCC,fid,1); if (! pp) break; nn = sscanf(buff,"%s %d %d %d ",tranname,&n1,&n2,&n3); // tranname N NN NN if (nn != 4) goto format_error; // (enabled 0-1 slowdown 0-99 pref. 0-99) for (ii = 0; ii < SSNF; ii++) if (strmatch(tranname,ss_trantab[ii].tranname)) break; if (ii == SSNF) goto format_error; ss_trantab[ii].enabled = n1; ss_trantab[ii].slowdown = n2; ss_trantab[ii].preference = n3; } for (ii = jj = 0; ii < SSNF; ii++) { // initialize list of enabled if (ss_trantab[ii].enabled) { // transition types ss_Tused[jj] = ii; jj++; } } ss_Nused = jj; // no. enabled transition types return 0; format_error: zmessageACK(Mwin,ZTX("file format error: \n %s"),buff); return 1; } // save transition prefs to a file int transprefs_save(FILE *fid) { fprintf(fid,"random %d \n",ss_random); for (int ii = 0; ii < SSNF; ii++) fprintf(fid,"%s %d %d %d \n", ss_trantab[ii].tranname, ss_trantab[ii].enabled, ss_trantab[ii].slowdown, ss_trantab[ii].preference); return 0; } // ------------------------------------------------------------------------------ // set image preferences for specific slide show void ss_imageprefs_dialog() { int ss_imageprefs_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int ii; char *pp, ztypN[8] = "ztypN"; char zoomloc[40]; /*** _______________________________________________ | Image Preferences | | | | Image File: /path.../filename.jpg | | Play Tone [x] | | Show Caption [ 0 ] Seconds | | Show Comments [ 12 ] Seconds | | Wait [ 3 ] Seconds | | Zoom (o) none (o) zoomin (o) zoomout | | Zoom size (x) [ 2.5 ] Steps [ 200 ] | | [Zoom Center] position: 0 0 | | Wait [ 6 ] Seconds | | Transition [ rotate |v] | | | | [Done] | // no [cancel] - widgets apply instantly |_______________________________________________| ***/ if (! ss_Nfiles) { zmessageACK(Mwin,ZTX("invalid album")); return; } m_viewmode(0,"G"); // gallery view zd = zdialog_new(ZTX("Image Preferences"),Mwin,Bdone,null); zd_ss_imageprefs = zd; zdialog_add_widget(zd,"hbox","hbimf","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labimf","hbimf",ZTX("Image File:"),"space=3"); zdialog_add_widget(zd,"label","imagefile","hbimf",0,"space=3"); zdialog_add_widget(zd,"hbox","hbpt","dialog"); zdialog_add_widget(zd,"check","tone","hbpt",ZTX("Play tone when image shows"),"space=3"); zdialog_add_widget(zd,"hbox","hbcap","dialog"); zdialog_add_widget(zd,"label","labcap","hbcap",ZTX("Show image caption"),"space=3"); zdialog_add_widget(zd,"zspin","caption","hbcap","0|99|1|0","space=3"); zdialog_add_widget(zd,"label","labsecs","hbcap",Bseconds,"space=5"); zdialog_add_widget(zd,"hbox","hbcom","dialog"); zdialog_add_widget(zd,"label","labcom","hbcom",ZTX("Show image comments"),"space=3"); zdialog_add_widget(zd,"zspin","comments","hbcom","0|99|1|0","space=3"); zdialog_add_widget(zd,"label","labsecs","hbcom",Bseconds,"space=5"); zdialog_add_widget(zd,"hbox","hbbz","dialog"); zdialog_add_widget(zd,"label","labwait","hbbz",ZTX("Wait before zoom"),"space=3"); zdialog_add_widget(zd,"zspin","wait1","hbbz","0|99|1|0","space=3"); zdialog_add_widget(zd,"label","labbzs2","hbbz",Bseconds,"space=5"); zdialog_add_widget(zd,"hbox","hbztyp","dialog"); zdialog_add_widget(zd,"label","labztyp","hbztyp",ZTX("Zoom type:"),"space=3"); zdialog_add_widget(zd,"radio","ztyp0","hbztyp",Bnone,"space=3"); zdialog_add_widget(zd,"radio","ztyp1","hbztyp",ZTX("zoom-in"),"space=3"); zdialog_add_widget(zd,"radio","ztyp2","hbztyp",ZTX("zoom-out"),"space=3"); zdialog_add_widget(zd,"hbox","hbz","dialog"); zdialog_add_widget(zd,"label","labzsz","hbz",ZTX("Zoom size (x)"),"space=3"); zdialog_add_widget(zd,"zspin","zoomsize","hbz","1.0|3.0|0.1|1.0","space=3"); zdialog_add_widget(zd,"label","space","hbz",0,"space=5"); zdialog_add_widget(zd,"label","labzst","hbz",ZTX("Steps"),"space=3"); zdialog_add_widget(zd,"zspin","zoomsteps","hbz","50|999|1|200"); zdialog_add_widget(zd,"hbox","hbzloc","dialog"); zdialog_add_widget(zd,"button","zloc","hbzloc",ZTX("Zoom Center"),"space=3"); zdialog_add_widget(zd,"label","labzloc","hbzloc","position: 50 50","space=3"); zdialog_add_widget(zd,"hbox","hbaz","dialog"); zdialog_add_widget(zd,"label","labwait","hbaz",ZTX("Wait after zoom"),"space=3"); zdialog_add_widget(zd,"zspin","wait2","hbaz","0|99|1|0","space=3"); zdialog_add_widget(zd,"label","labbzs2","hbaz",Bseconds,"space=5"); zdialog_add_widget(zd,"hbox","hbtrn","dialog"); zdialog_add_widget(zd,"label","labtr","hbtrn",ZTX("Transition to next image"),"space=3"); zdialog_add_widget(zd,"combo","tranname","hbtrn"); zdialog_cb_app(zd,"tranname",ZTX("next")); // default transition for (ii = 0; ii < SSNF; ii++) // add all transitions to dropdown list zdialog_cb_app(zd,"tranname",ZTX(ss_trantab[ii].tranname)); if (curr_file) { // set to curr. file for (ii = 0; ii < ss_Nfiles; ii++) // if member of album if (strmatch(curr_file,ss_imagetab[ii].imagefile)) break; if (ii == ss_Nfiles) ii = 0; } else ii = 0; // else first file in album ss_Fcurrent = ii; ss_setzloc = 0; // 1-shot flag is off pp = strrchr(ss_imagetab[ii].imagefile,'/'); // stuff curr. image file data if (pp) zdialog_stuff(zd,"imagefile",pp+1); // into dialog zdialog_stuff(zd,"tone",ss_imagetab[ii].tone); zdialog_stuff(zd,"caption",ss_imagetab[ii].capsecs); zdialog_stuff(zd,"comments",ss_imagetab[ii].commsecs); zdialog_stuff(zd,"wait1",ss_imagetab[ii].wait1); ztypN[4] = ss_imagetab[ii].zoomtype + '0'; zdialog_stuff(zd,ztypN,1); zdialog_stuff(zd,"zoomsize",ss_imagetab[ii].zoomsize); zdialog_stuff(zd,"zoomsteps",ss_imagetab[ii].zoomsteps); snprintf(zoomloc,40,"position: x=%02d y=%02d", // stuff zoom location if defined ss_imagetab[ii].zoomlocx,ss_imagetab[ii].zoomlocy); // (else x=00 y=00) zdialog_stuff(zd,"labzloc",zoomloc); zdialog_stuff(zd,"wait2",ss_imagetab[ii].wait2); zdialog_stuff(zd,"tranname",ZTX(ss_imagetab[ii].tranname)); zdialog_run(zd,ss_imageprefs_dialog_event,"parent"); // run dialog zdialog_wait(zd); // wait for completion zdialog_free(zd); zd_ss_imageprefs = 0; ss_saveprefs(); // save updated preferences file return; } // image prefs dialog event and completion function int ss_imageprefs_dialog_event(zdialog *zd, cchar *event) { int ii, jj; float ff; char tranname[32]; GdkWindow *gdkwin; ii = ss_Fcurrent; // from mouse click on thumbnail if (ii >= ss_Nfiles) return 1; if (strmatch(event,"tone")) { zdialog_fetch(zd,"tone",jj); ss_imagetab[ii].tone = jj; } if (strmatch(event,"caption")) { zdialog_fetch(zd,"caption",jj); ss_imagetab[ii].capsecs = jj; } if (strmatch(event,"comments")) { zdialog_fetch(zd,"comments",jj); ss_imagetab[ii].commsecs = jj; } if (strmatch(event,"wait1")) { zdialog_fetch(zd,"wait1",jj); ss_imagetab[ii].wait1 = jj; } if (strstr("ztyp0 ztyp1 ztyp2",event)) { // zoom type zdialog_fetch(zd,event,jj); jj = event[4] - '0'; ss_imagetab[ii].zoomtype = jj; } if (strmatch(event,"zoomsize")) { zdialog_fetch(zd,"zoomsize",ff); ss_imagetab[ii].zoomsize = ff; } if (strmatch(event,"zoomsteps")) { zdialog_fetch(zd,"zoomsteps",jj); ss_imagetab[ii].zoomsteps = jj; } if (strmatch(event,"zloc")) { ss_setzloc = 1; // set 1-shot flag gdkwin = gtk_widget_get_window(Gdrawin); gdk_window_set_cursor(gdkwin,dragcursor); poptext_mouse(Mwin,ZTX("click on thumbnail to set zoom center"),20,20,0,2); } if (strmatch(event,"wait2")) { zdialog_fetch(zd,"wait2",jj); ss_imagetab[ii].wait2 = jj; } if (strmatch(event,"tranname")) { zdialog_fetch(zd,"tranname",tranname,32); if (! strmatch(tranname,ZTX("next"))) { for (jj = 0; jj < SSNF; jj++) if (strmatch(tranname,ZTX(ss_trantab[jj].tranname))) break; if (jj == SSNF) return 1; strncpy0(ss_imagetab[ii].tranname,ss_trantab[jj].tranname,32); } else strncpy0(ss_imagetab[ii].tranname,"next",32); } return 1; } // response function for gallery thumbnail left-click // stuff image prefs dialog with data for clicked image void ss_imageprefs_Lclick_func(int Nth) { zdialog *zd; int ii; char *pp, ztypN[8] = "ztypN"; char zoomloc[40]; GdkWindow *gdkwin; if (! clicked_file) return; zd = zd_ss_imageprefs; // should not happen if (! zd) { zfree(clicked_file); clicked_file = 0; return; } for (ii = 0; ii < ss_Nfiles; ii++) // find clicked file in image prefs if (strmatch(clicked_file,ss_imagetab[ii].imagefile)) break; zfree(clicked_file); clicked_file = 0; if (ii == ss_Nfiles) return; // not found, album file removed ss_Fcurrent = ii; if (ss_setzloc) { // 1-shot flag is set ss_setzloc = 0; ss_imagetab[ii].zoomlocx = clicked_width; // set zoom-in location from ss_imagetab[ii].zoomlocy = clicked_height; // thumbnail click position gdkwin = gtk_widget_get_window(Gdrawin); gdk_window_set_cursor(gdkwin,0); } pp = strrchr(ss_imagetab[ii].imagefile,'/'); // stuff image data into dialog if (pp) zdialog_stuff(zd,"imagefile",pp+1); zdialog_stuff(zd,"tone",ss_imagetab[ii].tone); zdialog_stuff(zd,"caption",ss_imagetab[ii].capsecs); zdialog_stuff(zd,"comments",ss_imagetab[ii].commsecs); zdialog_stuff(zd,"wait1",ss_imagetab[ii].wait1); ztypN[4] = '0' + ss_imagetab[ii].zoomtype; zdialog_stuff(zd,ztypN,1); zdialog_stuff(zd,"zoomsize",ss_imagetab[ii].zoomsize); zdialog_stuff(zd,"zoomsteps",ss_imagetab[ii].zoomsteps); snprintf(zoomloc,40,"position: x=%02d y=%02d", // stuff zoom location ss_imagetab[ii].zoomlocx,ss_imagetab[ii].zoomlocy); zdialog_stuff(zd,"labzloc",zoomloc); zdialog_stuff(zd,"wait2",ss_imagetab[ii].wait2); zdialog_stuff(zd,"tranname",ZTX(ss_imagetab[ii].tranname)); return; } /*********************** preferences file format ****************************** global data: seconds: NN 0-999 cliplimit: NN 0-50 random: N 0-1 musicfile: /path.../file.ogg music file or "none" fullscreen: 0-1 replay: 0-1 transitions data: tranname N NN NN tranname enabled slowdown preference tranname N NN NN 0-1 0-99 0-99 ... images data: imagefile: /path.../file.jpg tone: N 0-1 caption: NN seconds to show caption comments: NN seconds to show comments wait1: NN seconds to wait 0-99 zoomtype: N 0/1/2 = none/zoomin/zoomout // added zoomsize: N.N 1.0 - 3.0 zoomsteps: NNN 100-999 zoomloc: NN NN 20-80 20-80 wait2: NN seconds to wait 0-99 tranname: aaaaaa transition name or "next" ... *********************************************************************************/ // Load all data for a specific slide show from a slide show preferences file. // Set defaults if no data previously defined. void ss_loadprefs() { FILE *fid; char buff[XFCC]; char prefsfile[200], *pp; int ii, jj, nn, format; FTYPE ftype; char tranname[32]; int n1, n2, n3; float ff; for (ii = 0; ii < ss_Nfiles; ii++) { // free prior image data if any pp = ss_imagetab[ii].imagefile; if (pp) zfree(pp); ss_imagetab[ii].imagefile = 0; } ss_Nfiles = 0; fid = fopen(ss_albumfile,"r"); // open album file if (! fid) { zmessageACK(Mwin,ZTX("invalid album")); return; } for (ii = 0; ii < SSMAXI; ) { // read all image file names pp = fgets_trim(buff,XFCC,fid); if (! pp) break; ftype = image_file_type(pp); // screen out deleted image files if (ftype != IMAGE && ftype != VIDEO) continue; // 17.08 ss_imagetab[ii].imagefile = zstrdup(pp); // add to image table ii++; } fclose(fid); ss_Nfiles = ii; if (! ss_Nfiles) { zmessageACK(Mwin,ZTX("invalid album")); return; } navi::gallerytype = ALBUM; // open gallery with slide show album gallery(ss_albumfile,"initF",0); gallery(0,"paint",0); m_viewmode(0,"G"); ss_seconds = 3; // defaults: image display time ss_cliplimit = 0; // image clip limit = no clipping strcpy(ss_musicfile,"none"); // no music file for (ii = 0; ii < SSNF; ii++) // initialize transitions table ss_trantab[ii] = ss_trantab_default[ii]; // with default preferences for (ii = 0; ii < SSNF; ii++) { ss_Tused[ii] = ii; // all transition types are used ss_Tlast[ii] = 0; // last used list is empty } ss_random = 0; // random transitions = NO ss_Nused = SSNF; // used transitions = all ss_Tnext = 0; // next = first for (ii = 0; ii < ss_Nfiles; ii++) { // initialize image table with defaults ss_imagetab[ii].tone = 0; ss_imagetab[ii].capsecs = 0; ss_imagetab[ii].commsecs = 0; ss_imagetab[ii].wait1 = 0; ss_imagetab[ii].zoomtype = 0; // no zoom-in ss_imagetab[ii].zoomsize = 1.0; ss_imagetab[ii].zoomsteps = 200; ss_imagetab[ii].zoomlocx = 50; ss_imagetab[ii].zoomlocy = 50; ss_imagetab[ii].wait2 = 0; strcpy(ss_imagetab[ii].tranname,"next"); } snprintf(prefsfile,200,"%s/%s",slideshow_dirk,ss_albumname); fid = fopen(prefsfile,"r"); // open slide show prefs file if (! fid) return; format = 0; while (true) { pp = fgets_trim(buff,XFCC,fid,1); if (! pp) break; if (strmatchN(pp,"global data:",12)) { format = 1; continue; } if (strmatchN(pp,"transitions data:",17)) { format = 2; continue; } if (strmatchN(pp,"images data:",12)) { format = 3; continue; } if (format == 1) // overall preferences { if (strmatchN(pp,"seconds: ",9)) { // seconds: NN seconds to show each image ss_seconds = atoi(pp+9); continue; } if (strmatchN(pp,"cliplimit: ",11)) { // cliplimit: NN margin clip limit 0-50% ss_cliplimit = atoi(pp+11); continue; } if (strmatchN(pp,"random: ",8)) { // random: N 0-1 = seq./random transactions ss_random = atoi(pp+8); continue; } if (strmatchN(pp,"musicfile: ",11)) { // musicfile: /directory/.../musicfile.ogg strncpy0(ss_musicfile,pp+11,500); continue; } if (strmatchN(pp,"fullscreen: ",12)) { // fullscreen: N 0-1 = no / full screen ss_fullscreen = atoi(pp+12); continue; } if (strmatchN(pp,"replay: ",8)) { // random: N 0-1 = no / replay after end ss_replay = atoi(pp+8); continue; } } if (format == 2) // transition preferences { nn = sscanf(buff,"%s %d %d %d ",tranname,&n1,&n2,&n3); // tranname N NN NN if (nn != 4) goto format_error; // (enabled 0-1 slowdown 0-99 pref. 0-99) for (ii = 0; ii < SSNF; ii++) if (strmatch(tranname,ss_trantab[ii].tranname)) break; if (ii == SSNF) { printz("unknown transition: %s \n",tranname); // ignore and continue 17.01 continue; } ss_trantab[ii].enabled = n1; ss_trantab[ii].slowdown = n2; ss_trantab[ii].preference = n3; } if (format == 3) // image file preferences { if (strmatchN(pp,"imagefile: ",11)) { // set image file for subsequent recs pp += 11; if (*pp != '/') goto format_error; for (ii = 0; ii < ss_Nfiles; ii++) // search album for matching image if (strmatch(pp,ss_imagetab[ii].imagefile)) break; if (ii == ss_Nfiles) ii = -1; // if not found, set no curr. image continue; } if (ii < 0) continue; // ignore recs following invalid image if (strmatchN(pp,"tone: ",6)) { // tone: N 0 or 1 = play tone nn = atoi(pp+6); ss_imagetab[ii].tone = nn; continue; } if (strmatchN(pp,"caption: ",9)) { // caption: NN seconds to show caption nn = atoi(pp+9); ss_imagetab[ii].capsecs = nn; continue; } if (strmatchN(pp,"comments: ",10)) { // comments: NN seconds to show comments nn = atoi(pp+10); ss_imagetab[ii].commsecs = nn; continue; } if (strmatchN(pp,"wait1: ",7)) { // wait1: NN seconds before zoom-in nn = atoi(pp+7); ss_imagetab[ii].wait1 = nn; continue; } if (strmatchN(pp,"zoomtype: ",10)) { // zoomtype: N zoom type nn = atoi(pp+10); ss_imagetab[ii].zoomtype = nn; continue; } if (strmatchN(pp,"zoomsize: ",10)) { // zoomsize: N.N 1.0 - 3.0 = 3x ff = atof(pp+10); // ff float ss_imagetab[ii].zoomsize = ff; continue; } if (strmatchN(pp,"zoomsteps: ",11)) { // zoomsteps: NNN zoom-in steps 100-999 nn = atoi(pp+11); ss_imagetab[ii].zoomsteps = nn; continue; } if (strmatchN(pp,"zoomloc: ",9)) { // zoomloc: NN NN zoom-in location nn = strtol(pp+9,&pp,10); // (20-80% of image width and height) ss_imagetab[ii].zoomlocx = nn; nn = atoi(pp); ss_imagetab[ii].zoomlocy = nn; continue; } if (strmatchN(pp,"wait2: ",7)) { // wait2: NN seconds after zoom-in nn = atoi(pp+7); ss_imagetab[ii].wait2 = nn; continue; } if (strmatchN(pp,"tranname: ",10)) { // transaction to next image strncpy0(ss_imagetab[ii].tranname,pp+10,32); continue; } } } fclose(fid); for (ii = jj = 0; ii < SSNF; ii++) { // initialize list of enabled if (ss_trantab[ii].enabled) { // transition types ss_Tused[jj] = ii; jj++; } } ss_Nused = jj; // no. enabled transition types return; format_error: zmessageACK(Mwin,ZTX("file format error: \n %s"),buff); fclose(fid); return; } // Save all data for a specific slide show to a slide show preferences file. void ss_saveprefs() { FILE *fid; char prefsfile[200]; int ii; if (! ss_Nfiles) { zmessageACK(Mwin,ZTX("invalid album")); return; } snprintf(prefsfile,200,"%s/%s",slideshow_dirk,ss_albumname); fid = fopen(prefsfile,"w"); // open slide show prefs file if (! fid) { zmessageACK(Mwin,strerror(errno)); return; } fprintf(fid,"global data: \n"); fprintf(fid,"seconds: %d \n",ss_seconds); fprintf(fid,"cliplimit: %d \n",ss_cliplimit); fprintf(fid,"random: %d \n",ss_random); fprintf(fid,"musicfile: %s \n",ss_musicfile); fprintf(fid,"fullscreen: %d \n",ss_fullscreen); fprintf(fid,"replay: %d \n",ss_replay); fprintf(fid,"transitions data: \n"); for (ii = 0; ii < SSNF; ii++) fprintf(fid,"%s %d %d %d \n", ss_trantab[ii].tranname, ss_trantab[ii].enabled, ss_trantab[ii].slowdown, ss_trantab[ii].preference); fprintf(fid,"images data: \n"); for (ii = 0; ii < ss_Nfiles; ii++) { fprintf(fid,"imagefile: %s \n",ss_imagetab[ii].imagefile); fprintf(fid,"tone: %d \n",ss_imagetab[ii].tone); fprintf(fid,"caption: %d \n",ss_imagetab[ii].capsecs); fprintf(fid,"comments: %d \n",ss_imagetab[ii].commsecs); fprintf(fid,"wait1: %d \n",ss_imagetab[ii].wait1); fprintf(fid,"zoomtype: %d \n",ss_imagetab[ii].zoomtype); fprintf(fid,"zoomsize: %.1f \n",ss_imagetab[ii].zoomsize); fprintf(fid,"zoomsteps: %d \n",ss_imagetab[ii].zoomsteps); fprintf(fid,"zoomloc: %d %d \n",ss_imagetab[ii].zoomlocx,ss_imagetab[ii].zoomlocy); fprintf(fid,"wait2: %d \n",ss_imagetab[ii].wait2); fprintf(fid,"tranname: %s \n",ss_imagetab[ii].tranname); } fclose(fid); ss_loadprefs(); // reload to sync poss. album edits return; } // ------------------------------------------------------------------------------ // Show next slide when time is up or user navigates with arrow keys. // Cycles every 0.1 seconds when slide show is active. int ss_timerfunc(void *) { int ii, jj; int capsecs, commsecs, mode; double cctime; static int pmode; static double starttime; float zoomsize; int zoomtype; if (ss_block) return 1; // transition function busy 17.08 ii = ss_Fcurrent; // current image file in album if (strmatch(ss_state,"end")) { // end of image list if (ss_replay) { ii = ss_Fcurrent = 0; // if auto replay, back to first image ss_state = "show"; } else { f_open(ss_imagetab[ii].imagefile); // last file shown --> current file zmessage_post_bold(Mwin,3,"END (Escape to exit)"); ss_state = "end2"; return 1; // wait for escape } } if (ss_escape) { // end slide show, return to dialog ss_escape = 0; if (ss_pxbold) g_object_unref(ss_pxbold); // free memory if (ss_pxbnew) g_object_unref(ss_pxbnew); ss_pxbold = ss_pxbnew = 0; if (*ss_musicfile == '/') shell_quiet("pulseaudio --kill"); // kill music if any win_unfullscreen(); // restore old window size, menu etc. ss_fullscreen = 0; Fslideshow = 0; // reset flags Fblowup = 0; Fblock = 0; // 17.04 f_open(ss_imagetab[ii].imagefile); // open current image 17.08 m_slideshow(0,0); // return to slide show dialog return 0; // stop the timer } if (strmatch(ss_state,"end2")) return 1; // waiting for escape if (ss_pause) { // pause/resume ss_pause = 0; ss_isblank = 0; if (! strmatch(ss_state,"pause")) { // pause ss_state = "pause"; f_open(ss_imagetab[ii].imagefile); // open current image } else { // resume if (zdmagnify) zdialog_send_event(zdmagnify,"kill"); // if magnify active, terminate ss_Flast = ss_Fcurrent; ss_Fcurrent++; if (ss_Fcurrent == ss_Nfiles) ss_Fcurrent = 0; ss_state = "instant"; } return 1; } if (ss_magnify) { // magnify image ss_magnify = 0; if (! strmatch(ss_state,"pause")) return 1; // ignore if not paused f_open(ss_imagetab[ii].imagefile); // 18.01 m_magnify(0,0); return 1; } if (ss_blank) { // blank/unblank window ss_blank = 0; ss_isblank = 1 - ss_isblank; if (ss_isblank) { ss_blankwindow(); ss_state = "pause"; } else ss_state = "instant"; return 1; } if (ss_previous) { // previous image ss_previous = 0; ss_isblank = 0; ss_Flast = ss_Fcurrent; ss_Fcurrent--; if (ss_Fcurrent < 0) ss_Fcurrent = ss_Nfiles-1; ss_state = "arrow"; return 1; } if (ss_next) { // next image ss_next = 0; ss_isblank = 0; ss_Flast = ss_Fcurrent; ss_Fcurrent++; if (ss_Fcurrent == ss_Nfiles) ss_Fcurrent = 0; ss_state = "arrow"; return 1; } if (ss_tran_next) { // transition + next image ss_tran_next = 0; ss_isblank = 0; ss_Flast = ss_Fcurrent; ss_Fcurrent++; if (ss_Fcurrent == ss_Nfiles) ss_Fcurrent = 0; ss_state = "shownow"; return 1; } if (strmatch(ss_state,"pause")) return 1; // do nothing if (strmatch(ss_state,"first")) // first image ss_state = "instant"; if (strstr("show shownow instant arrow",ss_state)) // show or instant show { ss_oldfile = ss_newfile; // old file = new if (ss_pxbold) g_object_unref(ss_pxbold); ss_pxbold = ss_pxbnew; // new pixbuf --> old ss_newfile = ss_imagetab[ii].imagefile; // new current file ss_pxbnew = ss_loadpxb(ss_newfile); // new pixbuf if (! ss_pxbnew) { ss_escape = 1; // failure, quit slide show return 1; } zoomsize = ss_imagetab[ii].zoomsize; // zoom size zoomtype = ss_imagetab[ii].zoomtype; // zoom type, none or in or out ss_zoomlocx = ss_imagetab[ii].zoomlocx; // target location for final center ss_zoomlocy = ss_imagetab[ii].zoomlocy; if (zoomsize > 1 && zoomtype == 2) { // next image will be zoomed out g_object_unref(ss_pxbnew); ss_block = 1; // block timer function 17.08 ss_zoom_posn(ss_newfile,1,zoomsize,0); // initial image = zoomed image ss_pxbnew = ss_zoom_posn(0,2,zoomsize,0); ss_block = 0; // unblock } if (strstr("instant arrow",ss_state)) // skip transition, show now ss_instant(); else { jj = ss_nextrans(); // select next transition type ss_slowdown = ss_trantab[jj].slowdown; // set slowdown factor for transition ss_block = 1; // block timer function 17.08 ss_trantab[jj].func(); // call transition function ss_block = 0; // unblock 17.08 } if (! ss_fullscreen) // if not full screen mode, gtk_window_set_title(MWIN,ss_newfile); // put filename in window title bar if (strmatch("arrow",ss_state)) { // show immediately ss_instant(); ss_state = "pause"; return 1; } if (strmatch("shownow",ss_state)) { // show immediately ss_state = "instant"; return 1; } if (ss_imagetab[ii].tone) // play tone if specified shell_quiet("paplay %s/slideshow-tone.oga &",slideshow_dirk); ss_state = "wait1"; // wait before zoom starttime = get_seconds(); if (ss_imagetab[ii].capsecs > ss_imagetab[ii].commsecs) cctime = ss_imagetab[ii].capsecs; // add larger of caption/comments time else cctime = ss_imagetab[ii].commsecs; ss_timer = starttime + cctime + ss_imagetab[ii].wait1; // time to wait return 1; } if (strmatch(ss_state,"wait1")) { // show caption/comments cctime = get_seconds() - starttime; // for specified intervals capsecs = ss_imagetab[ii].capsecs; commsecs = ss_imagetab[ii].commsecs; mode = 0; if (capsecs && capsecs > cctime) mode = 1; // show caption if (commsecs && commsecs > cctime) mode += 2; // show comments or both if ((capsecs || commsecs) && ! mode) mode = 4; // erase them if (mode != pmode) ss_showcapcom(mode); pmode = mode; } if (strmatch(ss_state,"wait1")) { if (get_seconds() < ss_timer) return 1; // wait for zoom time ss_zoomsize = ss_imagetab[ii].zoomsize; // 1-3x = no zoom to 3x zoom ss_zoomsteps = ss_imagetab[ii].zoomsteps; // zoom steps ss_zoomlocx = ss_imagetab[ii].zoomlocx; // target location for final center ss_zoomlocy = ss_imagetab[ii].zoomlocy; // (0-100% of image, 50/50 = middle) if (ss_zoomsize > 1.0) { ss_block = 1; // block timer function 17.08 if (ss_imagetab[ii].zoomtype == 1) ss_zoomin(); // zoomin or zoomout if (ss_imagetab[ii].zoomtype == 2) ss_zoomout(); ss_block = 0; // unblock } ss_state = "wait2"; // wait for next image ss_timer = get_seconds() + ss_imagetab[ii].wait2; return 1; } if (strmatch(ss_state,"wait2")) { if (image_file_type(ss_newfile) == VIDEO) { // if VIDEO file, play now 17.08 shell_ack("ffplay -loglevel -8 -autoexit \"%s\" ",ss_newfile); ss_state = "sswait"; // when done, next image immediately ss_timer = 0; return 1; } } if (strmatch(ss_state,"wait2")) { if (get_seconds() < ss_timer) return 1; // wait until next image time ss_state = "sswait"; ss_timer = get_seconds() + ss_seconds; return 1; } if (strmatch(ss_state,"sswait")) { // global time interval if (get_seconds() < ss_timer) return 1; // wait for my time ss_Flast = ss_Fcurrent; if (ss_Fcurrent == ss_Nfiles - 1) // was last image file ss_state = "end"; else { ss_Fcurrent++; // show next ss_state = "show"; } return 1; } return 1; } // select next transition type to use // mode = sequential: use each enabled transition type in sequence // mode = random: exclude recently used, choose random from remaining int ss_nextrans() { int ii, jj, maxii, maxjj, next; float maxrank, rank; ii = ss_Flast; // transition type from last image if (! strmatch(ss_imagetab[ii].tranname,"next")) { // image transition not "next" for (jj = 0; jj < SSNF; jj++) if (strmatch(ss_trantab[jj].tranname,ss_imagetab[ii].tranname)) break; if (jj < SSNF) { next = jj; // assigned transition type for (ii = ss_Nused - 1; ii > 0; ii--) // >> most recently used ss_Tlast[ii] = ss_Tlast[ii-1]; ss_Tlast[0] = next; return next; } } if (ss_Nused < 2 || ss_random == 0) // few enabled transition types { // or sequential mode ss_Tnext++; if (ss_Tnext == ss_Nused) ss_Tnext = 0; // select transition types sequentially next = ss_Tused[ss_Tnext]; } else // select transition types randomly { maxrank = 0; maxii = 0; maxjj = ss_Nused / 2; // most recently used to exclude if (maxjj > 4) maxjj = 4; // max. 4 for (ii = 0; ii < ss_Nused; ii++) // search enabled transitions { for (jj = 0; jj < maxjj; jj++) // exclude most recently used 50% if (ss_Tused[ii] == ss_Tlast[jj]) break; if (jj < maxjj) continue; jj = ss_Tused[ii]; rank = ss_trantab[jj].preference * drandz(); // rank = preference * random value if (rank > maxrank) { maxrank = rank; // remember highest rank maxii = ii; } } next = ss_Tused[maxii]; // transition to use for (ii = ss_Nused - 1; ii > 0; ii--) // make it most recent ss_Tlast[ii] = ss_Tlast[ii-1]; ss_Tlast[0] = next; } return next; } // ------------------------------------------------------------------------------ // write captions and comments at the top of the image // mode: 1 write caption only // 2 write comments only // 3 write both // 4 clear both void ss_showcapcom(int mode) { cchar *keynames[2] = { iptc_caption_key, exif_comment_key }; char *keyvals[2]; char caption[200], comments[200]; static char text[402]; PIXBUF *pxbclear; static PangoFontDescription *pangofont = null; static PangoLayout *pangolayout = null; static int plww, plhh; if (plww) { // clear previous text pxbclear = gdk_pixbuf_new_subpixbuf(ss_pxbnew,0,0,plww+10,plhh+10); cairo_t *cr = draw_context_create(gdkwin,draw_context); gdk_cairo_set_source_pixbuf(cr,pxbclear,0,0); cairo_paint(cr); draw_context_destroy(draw_context); g_object_unref(pxbclear); plww = 0; zmainloop(); } if (mode == 4) return; *caption = *comments = 0; exif_get(ss_newfile,keynames,keyvals,2); // get captions and comments metadata if (keyvals[0]) { strncpy0(caption,keyvals[0],200); zfree(keyvals[0]); } if (keyvals[1]) { strncpy0(comments,keyvals[1],200); zfree(keyvals[1]); } *text = 0; if (mode == 1 && *caption) strcpy(text,caption); // show caption only if (mode == 2 && *comments) strcpy(text,comments); // show comments only if (mode == 3) { if (*caption) strcpy(text,caption); // show both on two lines if (*comments && *caption) strcat(text,"\n"); if (*comments) strcat(text,comments); } if (! *text) return; for (int ii = 0; text[ii]; ii++) // replace "\n" with real newlines if (text[ii] == '\\' && text[ii+1] == 'n') memmove(text+ii,"\n ",2); pangofont = pango_font_description_from_string("Sans 12"); // make pango layout for font pangolayout = gtk_widget_create_pango_layout(Fdrawin,0); // Fdrawin instead of Cdrawin 17.08 pango_layout_set_font_description(pangolayout,pangofont); pango_layout_set_text(pangolayout,text,-1); // add text to layout pango_layout_get_pixel_size(pangolayout,&plww,&plhh); cairo_t *cr = draw_context_create(gdkwin,draw_context); cairo_set_line_width(cr,1); cairo_set_source_rgb(cr,1,1,1); // draw white background cairo_rectangle(cr,10,10,plww,plhh); cairo_fill(cr); cairo_move_to(cr,10,10); // draw layout with text cairo_set_source_rgb(cr,0,0,0); pango_cairo_show_layout(cr,pangolayout); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // process keyboard input key void ss_KBfunc(int kbkey) { kbkey = toupper(kbkey); if (kbkey == ss_KBkeyB) ss_blank = 1; // blank window if (kbkey == ss_KBkeyN) ss_tran_next = 1; // transition to next image if (kbkey == ss_KBkeyP) ss_pause = 1; // pause / resume if (kbkey == ss_KBkeyX) ss_magnify = 1; // magnify image tool if (kbkey == GDK_KEY_Left) ss_previous = 1; // previous image if (kbkey == GDK_KEY_Right) ss_next = 1; // next image return; } // ------------------------------------------------------------------------------ // Load image and rescale to fit in window size. // If image aspect ratio is close enough to window ratio, // truncate to avoid having margins around around the image. PIXBUF * ss_loadpxb(char *file) { PIXBUF *pxbin, *pxbtemp, *pxbout; GError *gerror = 0; FTYPE ftype; char *framefile; int err; int ww1, hh1, ww2, hh2; int Iorgx, Iorgy, Worgx, Worgy; float Rm, Rw, dR; Dww = gdk_window_get_width(gdkwin); // refresh drawing window size Dhh = gdk_window_get_height(gdkwin); ftype = image_file_type(file); if (ftype == IMAGE) { pxbin = gdk_pixbuf_new_from_file(file,&gerror); // load image file into pixbuf if (! pxbin) { zmessageACK(Mwin,"%s",gerror->message); return 0; } } else if (ftype == VIDEO) { // 17.08 framefile = zstrdup(tempdir,20); strcat(framefile,"/frame1.jpg"); err = shell_quiet("ffmpeg -i \"%s\" -v 8 -frames 1 -y %s",file,framefile); if (err) { zmessageACK(Mwin,strerror(err)); return 0; } pxbin = gdk_pixbuf_new_from_file(framefile,&gerror); // load image file into pixbuf if (! pxbin) { zmessageACK(Mwin,"%s",gerror->message); return 0; } } else return 0; pxbtemp = gdk_pixbuf_stripalpha(pxbin); // stip alpha channel if present if (pxbtemp) { g_object_unref(pxbin); pxbin = pxbtemp; } ww1 = gdk_pixbuf_get_width(pxbin); // image dimensions hh1 = gdk_pixbuf_get_height(pxbin); ww2 = ss_ww; // window dimensions hh2 = ss_hh; Rm = 1.0 * ww1 / hh1; // image width/height ratio Rw = 1.0 * ww2 / hh2; // window width/height ratio dR = fabsf(Rm - Rw) / Rw; // discrepancy ratio if (dR <= 0.01 * ss_cliplimit) { // discrepancy within user limit if (Rw >= Rm) { ww1 = ww2; // height will be clipped hh1 = ww1 / Rm; } else { hh1 = hh2; // width will be clipped ww1 = hh1 * Rm; } } else { // discrepancy too great if (Rw >= Rm) { hh1 = hh2; // ratio image to fit in window ww1 = hh1 * Rm; } else { ww1 = ww2; hh1 = ww1 / Rm; } } pxbtemp = pixbuf_rescale_fast(pxbin,ww1,hh1); // avoid pixbuf scaling bug 17.08 g_object_unref(pxbin); Iorgx = (ww1 - ww2) / 2.0; // top left corner of image to copy from if (Iorgx < 0) Iorgx = 0; Iorgy = (hh1 - hh2) / 2.0; if (Iorgy < 0) Iorgy = 0; Worgx = (ww2 - ww1) / 2.0; // top left corner of window to copy to if (Worgx < 0) Worgx = 0; Worgy = (hh2 - hh1) / 2.0; if (Worgy < 0) Worgy = 0; if (ww2 < ww1) ww1 = ww2; // copy width if (hh2 < hh1) hh1 = hh2; // copy height pxbout = gdk_pixbuf_new(GDKRGB,0,8,ww2,hh2); // output pixbuf = window size gdk_pixbuf_fill(pxbout,0); // black margins gdk_pixbuf_copy_area(pxbtemp,Iorgx,Iorgy,ww1,hh1,pxbout,Worgx,Worgy); // insert image g_object_unref(pxbtemp); ss_rs = gdk_pixbuf_get_rowstride(pxbout); // set image row stride return pxbout; } // ------------------------------------------------------------------------------ // write black to entire window void ss_blankwindow() { GdkRGBA GDKdark; cairo_t *cr = draw_context_create(gdkwin,draw_context); GDKdark.red = GDKdark.green = GDKdark.blue = 0.2; GDKdark.alpha = 1; gdk_cairo_set_source_rgba(cr,&GDKdark); cairo_paint(cr); draw_context_destroy(draw_context); zmainloop(); return; } // ------------------------------------------------------------------------------ // instant transition (also used for keyboard arrow keys) void ss_instant() { cairo_t *cr = draw_context_create(gdkwin,draw_context); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); cairo_paint(cr); draw_context_destroy(draw_context); zmainloop(); return; } // ------------------------------------------------------------------------------ // fade-out / fade-in transition void ss_fadein() { PIXBUF *pxbmix; int ii, jj, kk, px, py, rs, iinc; float newpart, oldpart; uint8 *pixels1, *pixels2, *pixels3; uint8 *pix1, *pix2, *pix3; pxbmix = gdk_pixbuf_copy(ss_pxbold); rs = gdk_pixbuf_get_rowstride(pxbmix); pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); pixels2 = gdk_pixbuf_get_pixels(ss_pxbnew); pixels3 = gdk_pixbuf_get_pixels(pxbmix); iinc = 100.0 / (1 + ss_slowdown / 2.0); // slowdown factor if (iinc < 1) iinc = 1; cairo_t *cr = draw_context_create(gdkwin,draw_context); for (ii = 0; ii <= 1000; ii += iinc) { newpart = 0.001 * ii; oldpart = 1.0 - newpart; for (jj = 0; jj < 2; jj++) // four passes, each modifies 25% for (kk = 0; kk < 2; kk++) // of the pixels (visually smoother) { for (py = jj; py < ss_hh; py += 2) for (px = kk; px < ss_ww; px += 2) { pix1 = pixels1 + py * ss_rs + px * 3; pix2 = pixels2 + py * ss_rs + px * 3; pix3 = pixels3 + py * rs + px * 3; pix3[0] = newpart * pix2[0] + oldpart * pix1[0]; pix3[1] = newpart * pix2[1] + oldpart * pix1[1]; pix3[2] = newpart * pix2[2] + oldpart * pix1[2]; } gdk_cairo_set_source_pixbuf(cr,pxbmix,0,0); cairo_paint(cr); zmainloop(); } } g_object_unref(pxbmix); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image rolls over prior image from left to right void ss_rollright() { PIXBUF *pixbuf; int px; float delay = 1.0 * ss_slowdown / ss_ww; uint8 *pixels, *pix3; pixels = gdk_pixbuf_get_pixels(ss_pxbnew); cairo_t *cr = draw_context_create(gdkwin,draw_context); for (px = 0; px < ss_ww-4; px += 4) // 4-wide { pix3 = pixels + px * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,4,ss_hh,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px,0); cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); zsleep(delay); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image rolls over prior image from top down void ss_rolldown() { PIXBUF *pixbuf; int py; float delay = 1.0 * ss_slowdown / ss_hh; uint8 *pixels, *pix3; pixels = gdk_pixbuf_get_pixels(ss_pxbnew); cairo_t *cr = draw_context_create(gdkwin,draw_context); for (py = 0; py < ss_hh-2; py += 4) // 4-deep { pix3 = pixels + py * ss_rs; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ss_ww,4,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,py); cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); zsleep(delay); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image opens up in horizontal rows like venetian blinds void ss_venetian() { PIXBUF *pixbuf; int py1, py2; uint8 *pixels, *pix3; int louver, Nlouvers = 20; int louversize = ss_hh / Nlouvers; float delay = 1.0 / louversize * (1 + ss_slowdown / 8.0); pixels = gdk_pixbuf_get_pixels(ss_pxbnew); cairo_t *cr = draw_context_create(gdkwin,draw_context); for (py1 = 0; py1 < louversize; py1++) // y-row within each louver { for (louver = 0; louver < Nlouvers; louver++) // louver, first to last { py2 = py1 + louver * louversize; if (py2 >= ss_hh) break; pix3 = pixels + py2 * ss_rs; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ss_ww,1,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,py2); cairo_paint(cr); g_object_unref(pixbuf); } zmainloop(); zsleep(delay); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // a grate opens up to show new image void ss_grate() { PIXBUF *pixbuf; int px1, px2, py1, py2; uint8 *pixels, *pix3; int row, col, Nrow, Ncol; // rows and columns int boxww, boxhh; float delay; pixels = gdk_pixbuf_get_pixels(ss_pxbnew); Ncol = 20; // 20 columns boxww = boxhh = ss_ww / Ncol; // square boxes Nrow = ss_hh / boxhh; // corresp. rows Ncol++; // round up Nrow++; delay = 1.0 / boxhh * (1 + ss_slowdown / 8.0); cairo_t *cr = draw_context_create(gdkwin,draw_context); for (py1 = 0; py1 < boxhh; py1++) { for (row = 0; row < Nrow; row++) { py2 = py1 + row * boxhh; if (py2 >= ss_hh) break; pix3 = pixels + py2 * ss_rs; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ss_ww,1,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,0,py2); cairo_paint(cr); g_object_unref(pixbuf); } px1 = py1; for (col = 0; col < Ncol; col++) { px2 = px1 + col * boxww; if (px2 >= ss_ww) break; pix3 = pixels + px2 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,1,ss_hh,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px2,0); cairo_paint(cr); g_object_unref(pixbuf); } zmainloop(); zsleep(delay); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // A rectangular hole opens up from the center and expands outward to reveal new image. void ss_rectangle() { PIXBUF *pixbuf; int px1, py1, px2, py2, px3, py3; int ww1, hh1, ww2, hh2; uint8 *pixels, *pix3; int step, Nsteps = 200; float delay = 1.0 / Nsteps * (1 + ss_slowdown / 8.0); pixels = gdk_pixbuf_get_pixels(ss_pxbnew); cairo_t *cr = draw_context_create(gdkwin,draw_context); for (step = 1; step < Nsteps; step++) { ww1 = ss_ww * step / Nsteps; hh1 = ww1 * ss_hh / ss_ww; ww2 = ss_ww / Nsteps / 2 + 1; hh2 = ss_hh / Nsteps / 2 + 1; px1 = (ss_ww - ww1) / 2; py1 = (ss_hh - hh1) / 2; px2 = px1 + ww1 - ww2; py2 = py1; px3 = px1; py3 = py1 + hh1 - hh2; pix3 = pixels + py1 * ss_rs + px1 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww1+1,hh2+1,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px1,py1); cairo_paint(cr); g_object_unref(pixbuf); pix3 = pixels + py2 * ss_rs + px2 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww2+1,hh1+1,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px2,py2); cairo_paint(cr); g_object_unref(pixbuf); pix3 = pixels + py3 * ss_rs + px3 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww1+1,hh2+1,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px3,py3); cairo_paint(cr); g_object_unref(pixbuf); pix3 = pixels + py1 * ss_rs + px1 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww2+1,hh1+1,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px1,py1); cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); zsleep(delay); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // old image shrinks to the center, revealing new image void ss_implode() { int Nsteps = 100 + 20 * ss_slowdown; int ww, hh, px, py; float size = 1.0, F; PIXBUF *pxbnew, *pxbold; F = 1.0 * (Nsteps-3) / Nsteps; cairo_t *cr = draw_context_create(gdkwin,draw_context); while (true) { pxbnew = gdk_pixbuf_copy(ss_pxbnew); // new image at full size size = F * size - 0.002; // 17.08 if (size < 0.03) break; ww = ss_ww * size; hh = ss_hh * size; pxbold = pixbuf_rescale_fast(ss_pxbold,ww,hh); // old image at reduced size px = ss_ww * 0.5 * (1.0 - size); // new image position, NW corner --> center py = ss_hh * 0.5 * (1.0 - size); gdk_pixbuf_copy_area(pxbold,0,0,ww,hh,pxbnew,px,py); // copy shrinking old image into new image gdk_cairo_set_source_pixbuf(cr,pxbnew,0,0); // paint new image cairo_paint(cr); g_object_unref(pxbnew); g_object_unref(pxbold); zmainloop(); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image grows from the center, covering old image void ss_explode() { int Nsteps = 100 + 20 * ss_slowdown; int ww, hh, px, py; float size = 0.05, F; PIXBUF *pxbnew, *pxbold; F = 1.0 * (Nsteps+3) / Nsteps; cairo_t *cr = draw_context_create(gdkwin,draw_context); while (true) { pxbold = gdk_pixbuf_copy(ss_pxbold); // old image at full size size = F * size + 0.002; // 17.08 if (size > 1.0) break; ww = ss_ww * size; hh = ss_hh * size; pxbnew = pixbuf_rescale_fast(ss_pxbnew,ww,hh); // new image at reduced size px = ss_ww * 0.5 * (1.0 - size); // new image position, center --> NW corner py = ss_hh * 0.5 * (1.0 - size); gdk_pixbuf_copy_area(pxbnew,0,0,ww,hh,pxbold,px,py); // copy shrinking old image into new image gdk_cairo_set_source_pixbuf(cr,pxbold,0,0); // paint new image cairo_paint(cr); g_object_unref(pxbnew); g_object_unref(pxbold); zmainloop(); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // New image sweeps into view like a circular radar image void ss_radar() { int px, py, ww, hh; int px1, py1, px2, py2; int ww2 = ss_ww/2, hh2 = ss_hh/2; float R, Rmax, dR, T, dT; float r, r1, r2; float cosT, sinT; uint8 *pixels1, *pixels3, *pix1, *pix3; GdkPixbuf *pixbuf; px = py = 0; // suppress compiler warnings pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); pixels3 = gdk_pixbuf_get_pixels(ss_pxbnew); Rmax = sqrt(ww2*ww2 + hh2*hh2); // max. line length, center to corner dR = 150; // line segment length dT = 1.2 / Rmax; // angle step cairo_t *cr = draw_context_create(gdkwin,draw_context); for (T = 0; T < 2*PI; T += dT) // angle from 0 to 360 deg. { cosT = cosf(T); sinT = sinf(T); for (R = 0; R < Rmax; R += dR) // R from center to edge { r1 = R; // R segment r2 = R + dR; px1 = ww2 + r1 * cosT; // R segment px2 = ww2 + r2 * cosT; py1 = hh2 - r1 * sinT; py2 = hh2 - r2 * sinT; for (r = r1; r <= r2; r++) // loop R segment pixels { px = ww2 + r * cosT; py = hh2 - r * sinT; if (px < 0) px = 0; if (px > ss_ww-2) px = ss_ww-2; if (py < 0) py = 0; if (py > ss_hh-1) py = ss_hh-1; pix1 = pixels1 + py * ss_rs + px * 3; // copy new image pixel to old image pix3 = pixels3 + py * ss_rs + px * 3; // right side memcpy(pix1,pix3,6); if (px == 0 || px == ss_ww-2) break; // reached edge of image if (py == 0 || py == ss_hh-1) break; } px2 = px; // actual end of R segment py2 = py; if (px1 < px2) px = px1; // get rectangle enclosing R segment else px = px2; if (py1 < py2) py = py1; else py = py2; ww = abs(px2-px1) + 1; hh = abs(py2-py1) + 1; if (ww <= 0) break; if (hh <= 0) break; if (px < 0) px = 0; if (px > ss_ww-1) px = ss_ww-1; if (px + ww > ss_ww) ww = ss_ww - px; if (py < 0) py = 0; if (py > ss_hh-1) py = ss_hh-1; if (py + hh > ss_hh) hh = ss_hh - py; pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbold,px,py,ww,hh); // paint window rectangle gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py); cairo_paint(cr); g_object_unref(pixbuf); } zmainloop(); zsleep(0.00001 * ss_slowdown); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // New image spirals outwards from the center void ss_spiral() { int px, py, ww, hh; int px1, py1, px2, py2; int ww2 = ss_ww/2, hh2 = ss_hh/2; float R, Rmax, dR, T, dT; float r, r1, r2; float cosT, sinT; uint8 *pixels1, *pixels3, *pix1, *pix3; GdkPixbuf *pixbuf; px = py = 0; // suppress compiler warnings pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); pixels3 = gdk_pixbuf_get_pixels(ss_pxbnew); Rmax = sqrt(ww2*ww2 + hh2*hh2); // max. line length, center to corner dR = 140; // radius step dT = 2.0 - 0.1 * ss_slowdown; // angle step if (dT < 0.2) dT = 0.2; dT = dT / Rmax; cairo_t *cr = draw_context_create(gdkwin,draw_context); for (R = 0; R < Rmax; R += dR) // R from center to edge { for (T = 0; T < 2*PI; T += dT) // angle from 0 to 360 deg. { cosT = cosf(T); sinT = sinf(T); r1 = R; // R segment r2 = R + dR; px1 = ww2 + r1 * cosT; // R segment px2 = ww2 + r2 * cosT; py1 = hh2 - r1 * sinT; py2 = hh2 - r2 * sinT; for (r = r1; r <= r2; r++) // loop R segment pixels { px = ww2 + r * cosT; py = hh2 - r * sinT; if (px < 0) px = 0; if (px > ss_ww-2) px = ss_ww-2; if (py < 0) py = 0; if (py > ss_hh-1) py = ss_hh-1; pix1 = pixels1 + py * ss_rs + px * 3; // copy new image pixel to old image pix3 = pixels3 + py * ss_rs + px * 3; // right side memcpy(pix1,pix3,6); if (px == 0 || px == ss_ww-2) break; // reached edge of image if (py == 0 || py == ss_hh-1) break; } px2 = px; // actual end of R segment py2 = py; if (px1 < px2) px = px1; // get rectangle enclosing R segment else px = px2; if (py1 < py2) py = py1; else py = py2; ww = abs(px2-px1) + 1; hh = abs(py2-py1) + 1; if (ww <= 0) break; if (hh <= 0) break; if (px < 0) px = 0; if (px > ss_ww-1) px = ss_ww-1; if (px + ww > ss_ww) ww = ss_ww - px; if (py < 0) py = 0; if (py > ss_hh-1) py = ss_hh-1; if (py + hh > ss_hh) hh = ss_hh - py; pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbold,px,py,ww,hh); // paint window rectangle gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py); cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); } } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image opens up like a Japanese fan void ss_japfan() { int px, py, pxL, ww, hh; int px1, py1, px2, py2; int ww2 = ss_ww/2, hh2 = ss_hh/2; float R, Rmax, dR, T, dT; float r, r1, r2; float cosT, sinT; uint8 *pixels1, *pixels3, *pix1, *pix3; GdkPixbuf *pixbuf; px = py = 0; // suppress compiler warnings pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); pixels3 = gdk_pixbuf_get_pixels(ss_pxbnew); Rmax = sqrt(ww2*ww2 + hh2*hh2); // max. line length, center to corner dR = 150; // line segment length dT = 1.2 / Rmax; // angle step cairo_t *cr = draw_context_create(gdkwin,draw_context); for (T = PI/2; T > -PI/2; T -= dT) // angle from +90 to -90 deg. { cosT = cosf(T); sinT = sinf(T); for (R = 0; R < Rmax; R += dR) // R from center to edge { r1 = R; // R segment r2 = R + dR; px1 = ww2 + r1 * cosT; // R segment px2 = ww2 + r2 * cosT; py1 = hh2 - r1 * sinT; py2 = hh2 - r2 * sinT; for (r = r1; r <= r2; r++) // loop R segment pixels { px = ww2 + r * cosT; py = hh2 - r * sinT; if (px > ss_ww-2) px = ss_ww-2; if (py < 0) py = 0; if (py > ss_hh-1) py = ss_hh-1; pix1 = pixels1 + py * ss_rs + px * 3; // copy new image pixel to old image pix3 = pixels3 + py * ss_rs + px * 3; // right side memcpy(pix1,pix3,6); pxL = ss_ww-1 - px; pix1 = pixels1 + py * ss_rs + pxL * 3; // left side pixel pix3 = pixels3 + py * ss_rs + pxL * 3; memcpy(pix1,pix3,6); if (pxL == 0) break; // reached edge of image if (py == 0 || py == ss_hh-1) break; } px2 = px; // actual end of R segment py2 = py; px = px1; if (py1 < py2) py = py1; // get rectangle enclosing R segment else py = py2; ww = px2 - px1 + 1; hh = abs(py2-py1) + 1; if (ww <= 0) break; if (hh <= 0) break; if (px < 0) px = 0; if (px > ss_ww-1) px = ss_ww-1; if (px + ww > ss_ww) ww = ss_ww - px; if (py < 0) py = 0; if (py > ss_hh-1) py = ss_hh-1; if (py + hh > ss_hh) hh = ss_hh - py; pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbold,px,py,ww,hh); // paint window rectangle gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py); cairo_paint(cr); g_object_unref(pixbuf); pxL = ss_ww-1 - px2; pixbuf = gdk_pixbuf_new_subpixbuf(ss_pxbold,pxL,py,ww,hh); // left side rectangle gdk_cairo_set_source_pixbuf(cr,pixbuf,pxL,py); cairo_paint(cr); g_object_unref(pixbuf); } zmainloop(); zsleep(0.00001 * ss_slowdown); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // New image closes in from top and bottom with jagged teeth void ss_jaws() { PIXBUF *pixbuf; int nteeth = 20, Np = 10; int tbase1, tbase2, twidth, tlength, tooth, tpos; int ii, px, py, ww, ww2; float delay = 0.0005 * ss_slowdown; uint8 *pixels, *pix3; pixels = gdk_pixbuf_get_pixels(ss_pxbnew); twidth = ss_ww / nteeth; tlength = twidth; Np = Np / (1.0 + ss_slowdown / 4.0); if (Np < 1) Np = 1; cairo_t *cr = draw_context_create(gdkwin,draw_context); for (ii = 0; ii <= ss_hh/2 - tlength/2 + 1; ii += Np) { tbase1 = ii; // tooth base from window top to middle tbase2 = ss_hh - tbase1 - 1; // tooth base from window bottom to middle for (tooth = 0; tooth <= nteeth; tooth++) // tooth first to last + 1 { for (tpos = 0; tpos < tlength; tpos += Np) // tooth position from base to point { ww = twidth * (tlength - tpos) / tlength; // tooth width at scan line if (ww < 2) break; py = tbase1 + tpos; // top teeth scan line y px = twidth / 2 + tooth * twidth - ww / 2; // scan line x to x + ww if (px < ss_ww) { ww2 = ww; if (px + ww2 > ss_ww) ww2 = ss_ww - px; pix3 = pixels + py * ss_rs + px * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww2,Np,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py); cairo_paint(cr); g_object_unref(pixbuf); } py = tbase2 - tpos; // bottom teeth scan line y py = py - Np; px = tooth * twidth - ww / 2; // scan line x to x + ww if (tooth == 0) { px = 0; // leftmost tooth is half ww = ww / 2; } if (px < ss_ww) { ww2 = ww; if (px + ww2 > ss_ww) ww2 = ss_ww - px; pix3 = pixels + py * ss_rs + px * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww2,Np,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px,py); cairo_paint(cr); g_object_unref(pixbuf); } } } zmainloop(); zsleep(delay); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // An ellipse opens up from the center and expands outward void ss_ellipse() { PIXBUF *pixbuf; uint8 *pixels, *pix3; int step, Nsteps = 100; int px1, py1, ww; float delay = 0.1 / Nsteps; float a, b, a2, b2, px, py, px2, py2; float ww2 = ss_ww / 2, hh2 = ss_hh / 2; delay = delay * (1 + ss_slowdown / 2.0); pixels = gdk_pixbuf_get_pixels(ss_pxbnew); cairo_t *cr = draw_context_create(gdkwin,draw_context); for (step = 1; step < 1.4 * Nsteps; step++) { a = ww2 * step / Nsteps; // ellipse a and b constants b = a * ss_hh / ss_ww; // from tiny to >> image size a2 = a * a; b2 = b * b; for (py = -b; py <= +b; py += 3) // py from top of ellipse to bottom { while (py < -(hh2-2)) py += 3; if (py > hh2-2) break; py2 = py * py; px2 = a2 * (1.0 - py2 / b2); // corresponding px value, px = sqrt(px2); // (+/- from center of ellipse) if (px > ww2) px = ww2; ww = 2 * px; // length of line thru ellipse if (ww < 2) continue; px1 = ww2 - px; // relocate origin py1 = py + hh2; if (px1 + ww > ss_ww) px1 = ss_ww - ww; // insurance if (py1 + 3 > ss_hh) py1 = ss_hh - 3; pix3 = pixels + py1 * ss_rs + px1 * 3; pixbuf = gdk_pixbuf_new_from_data(pix3,GDKRGB,0,8,ww,3,ss_rs,0,0); gdk_cairo_set_source_pixbuf(cr,pixbuf,px1,py1); cairo_paint(cr); g_object_unref(pixbuf); } zmainloop(); zsleep(delay); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image splats onto old image one drop at a time void ss_raindrops() { PIXBUF *pxbmix, *pxbdrop; int rsmix; int px, py, px1, py1, px2, py2, cx, cy; int Rmin, Rmax, R, R2, dist2, Ndrops; uint8 *pixels2, *pixels3; uint8 *pix2, *pix3 = 0; float dtime; pixels2 = gdk_pixbuf_get_pixels(ss_pxbnew); // source image pxbmix = gdk_pixbuf_copy(ss_pxbold); // destination image 17.04 pixels3 = gdk_pixbuf_get_pixels(pxbmix); rsmix = gdk_pixbuf_get_rowstride(pxbmix); Rmin = ss_ww * 0.01; // drop size range Rmax = ss_ww * 0.02; Ndrops = 3000; cairo_t *cr = draw_context_create(gdkwin,draw_context); for (int ii = 0; ii < Ndrops; ii++) { cx = drandz() * ss_ww; // drop location on image cy = drandz() * ss_hh; R = drandz() * Rmax + Rmin; // drop size R2 = R * R; px1 = cx - R; if (px1 < 0) px1 = 0; py1 = cy - R; if (py1 < 0) py1 = 0; px2 = cx + R; if (px2 >= ss_ww) px2 = ss_ww; py2 = cy + R; if (py2 > ss_hh) py2 = ss_hh; for (py = py1; py < py2; py++) // copy drop area from new image for (px = px1; px < px2; px++) // to old image { dist2 = (px-cx) * (px-cx) + (py-cy) * (py-cy); if (dist2 > R2) continue; pix2 = pixels2 + py * ss_rs + px * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix2,3); } pxbdrop = gdk_pixbuf_new_subpixbuf(pxbmix,px1,py1,px2-px1,py2-py1); gdk_cairo_set_source_pixbuf(cr,pxbdrop,px1,py1); cairo_paint(cr); g_object_unref(pxbdrop); dtime = 0.001 * (1.0 - pow(1.0*ii/Ndrops,0.1)); dtime = dtime * (1.0 + 0.5 * ss_slowdown); zmainloop(); zsleep(dtime); } g_object_unref(pxbmix); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image spreads from the middle to left and right edges // like a double-door swinging open void ss_doubledoor() { #define GPNFD(pix,ww,hh) gdk_pixbuf_new_from_data(pix,GDKRGB,0,8,ww,hh,ss_rs,0,0) PIXBUF *pixbuf; int bx, px; uint8 *pixels, *pix3; float delay = 0.2 / ss_ww; delay = delay * (1 + ss_slowdown); pixels = gdk_pixbuf_get_pixels(ss_pxbnew); cairo_t *cr = draw_context_create(gdkwin,draw_context); for (bx = 0; bx < ss_ww/2; bx++) // bx = 0 ... ww/2 { px = ss_ww / 2 - bx; pix3 = pixels + 3 * px; // line from (ww/2-bx,0) to (ww/2-bx,hh-1) pixbuf = GPNFD(pix3,1,ss_hh); gdk_cairo_set_source_pixbuf(cr,pixbuf,px,0); cairo_paint(cr); g_object_unref(pixbuf); px = ss_ww / 2 + bx; pix3 = pixels + 3 * px; // line from (ww/2+bx,0) to (ww/2+bx,hh-1) pixbuf = GPNFD(pix3,1,ss_hh); gdk_cairo_set_source_pixbuf(cr,pixbuf,px,0); cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); zsleep(delay); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // Rotate from old image to new image. namespace ss_rotate_names { int cx1, cy1, cx2, cy2, cy3, cy4; uint8 *pixels1, *pixels2, *pixels3; int rsmix, tbusy[max_threads]; } void ss_rotate() { using namespace ss_rotate_names; void * ss_rotate_thread1(void *arg); void * ss_rotate_thread2(void *arg); PIXBUF *pxbmix; float R, step, stepx = 1, Nsteps = 80; int ii; pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); // source images pixels2 = gdk_pixbuf_get_pixels(ss_pxbnew); pxbmix = gdk_pixbuf_new(GDKRGB,0,8,ss_ww,ss_hh); // destination image rsmix = gdk_pixbuf_get_rowstride(pxbmix); pixels3 = gdk_pixbuf_get_pixels(pxbmix); Nsteps = Nsteps * (1.0 + ss_slowdown / 10.0); cairo_t *cr = draw_context_create(gdkwin,draw_context); for (step = 0; step < Nsteps; step += stepx) { R = 1.0 * step / Nsteps; stepx = 2 - R; // 2 ... 1 if (step + stepx >= Nsteps) { step = Nsteps; R = 1.0; } cx1 = R * ss_ww / 2.0; // corners of shrinking trapezoid cy1 = 0.3 * R * ss_hh; cx2 = ss_ww - cx1; cy2 = 0; cy3 = ss_hh; cy4 = ss_hh - cy1; memset(pixels3,0,ss_hh * rsmix); for (ii = 0; ii < ss_nwt; ii++) { // start worker threads tbusy[ii] = 1; start_detached_thread(ss_rotate_thread1,&Nval[ii]); } for (ii = 0; ii < ss_nwt; ii++) while(tbusy[ii]) zsleep(0.001); gdk_cairo_set_source_pixbuf(cr,pxbmix,0,0); cairo_paint(cr); zmainloop(); } for (step = 0; step < Nsteps; step += stepx) { R = 1.0 * step / Nsteps; stepx = 1 + R; // 1 ... 2 if (step + stepx >= Nsteps) { step = Nsteps; R = 1.0; } cx1 = ss_ww * (0.5 + 0.5 * R); // corners of expanding trapezoid cy1 = ss_hh * (0.3 - 0.3 * R); cx2 = ss_ww * (0.5 - 0.5 * R); cy2 = 0; cy3 = ss_hh; cy4 = ss_hh - cy1; memset(pixels3,0,ss_hh * rsmix); for (ii = 0; ii < ss_nwt; ii++) { // start worker threads tbusy[ii] = 1; start_detached_thread(ss_rotate_thread2,&Nval[ii]); } for (ii = 0; ii < ss_nwt; ii++) while(tbusy[ii]) zsleep(0.001); gdk_cairo_set_source_pixbuf(cr,pxbmix,0,0); cairo_paint(cr); zmainloop(); } g_object_unref(pxbmix); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_rotate_thread1(void *arg) { using namespace ss_rotate_names; int index = *((int *) (arg)); int px, py, ylo, yhi, vpx, vpy; uint8 *pix1, *pix3; float Rx, Ry; for (px = cx1 + 2 * index; px < cx2-1; px += 2 * ss_nwt) // speedup 18.01 { Rx = 1.0 * (px - cx1) / (cx2 - cx1); ylo = cy1 + Rx * (cy2 - cy1); yhi = cy4 + Rx * (cy3 - cy4); for (py = ylo; py < yhi; py++) { Ry = 1.0 * (py - ylo) / (yhi - ylo); vpx = Rx * (ss_ww - 1); vpy = Ry * (ss_hh - 1); pix1 = pixels1 + vpy * ss_rs + vpx * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix1,6); // 18.01 } } tbusy[index] = 0; pthread_exit(0); return 0; } void * ss_rotate_thread2(void *arg) { using namespace ss_rotate_names; int index = *((int *) (arg)); int px, py, ylo, yhi, vpx, vpy; uint8 *pix2, *pix3; float Rx, Ry; for (px = cx2 + 2 * index; px < cx1-1; px += 2 * ss_nwt) // 18.01 { Rx = 1.0 * (px - cx2) / (cx1 - cx2); ylo = cy2 + Rx * (cy1 - cy2); yhi = cy3 + Rx * (cy4 - cy3); for (py = ylo; py < yhi; py++) { Ry = 1.0 * (py - ylo) / (yhi - ylo); vpx = Rx * (ss_ww - 1); vpy = Ry * (ss_hh - 1); pix2 = pixels2 + vpy * ss_rs + vpx * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix2,6); // 18.01 } } tbusy[index] = 0; pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // Old image falls over to reveal new image. namespace ss_fallover_names { int cx1, cy1, cx2, cy2; uint8 *pixels1, *pixels2, *pixels3; int rsmix, tbusy[max_threads]; } void ss_fallover() { using namespace ss_fallover_names; void * ss_fallover_thread(void *arg); PIXBUF *pxbmix; float R, step, stepx = 1, Nsteps = 300; int ii; pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); // old image pixels2 = gdk_pixbuf_get_pixels(ss_pxbnew); // new image pxbmix = gdk_pixbuf_new(GDKRGB,0,8,ss_ww,ss_hh); // output image - mixture rsmix = gdk_pixbuf_get_rowstride(pxbmix); pixels3 = gdk_pixbuf_get_pixels(pxbmix); Nsteps = Nsteps + 30 * ss_slowdown; cairo_t *cr = draw_context_create(gdkwin,draw_context); for (step = 0; step < Nsteps; step += stepx) { stepx += 30.0 / Nsteps; // acceleration R = 1.0 * step / Nsteps; // 0 ... 1 if (step + stepx >= Nsteps) R = 1; // last iteration cx1 = 0.2 * ss_ww * R; // top corners of falling old image cy1 = ss_hh * R; cx2 = ss_ww - cx1; cy2 = cy1; for (ii = 0; ii < ss_nwt; ii++) { // start worker threads tbusy[ii] = 1; // make combined image start_detached_thread(ss_fallover_thread,&Nval[ii]); } for (ii = 0; ii < ss_nwt; ii++) // wait for completion while(tbusy[ii]) zsleep(0.01); gdk_cairo_set_source_pixbuf(cr,pxbmix,0,0); cairo_paint(cr); zmainloop(); } g_object_unref(pxbmix); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_fallover_thread(void *arg) // construct combined image { using namespace ss_fallover_names; int index = *((int *) (arg)); int px, py, npix, vpx, vpy; int cx1a, cx2a; float R; uint8 *pix1, *pix3; for (py = index; py < ss_hh; py += ss_nwt) // py = 0 ... ss_hh { px = 0; // px = 0 ... ss_ww, new image npix = ss_ww; pix1 = pixels2 + py * ss_rs + px * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix1,npix*3); if (py < cy1) continue; R = 1.0 * (py - cy1) / (ss_hh - cy1); // 0 ... 1 cx1a = cx1 * (1.0 - R); // cx1a = cx1 ... 0 cx2a = cx2 + R * (ss_ww - cx2); // cx2a = cx2 ... ss_ww for (px = cx1a; px < cx2a; px++) // px = cx1a ... cx2a { vpx = ss_ww * (px - cx1a) / (cx2a - cx1a); // vpx = 0 ... ss_ww vpy = ss_hh * R; // vpy = 0 ... ss_hh pix1 = pixels1 + vpy * ss_rs + vpx * 3; pix3 = pixels3 + py * rsmix + px * 3; memcpy(pix3,pix1,3); } } tbusy[index] = 0; pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // old image deforms from flat to sphere and then shrinks to reveal new image namespace ss_spheroid_names { float Cx, Cy, D, R, F; PIXBUF *pxbold, *pxbnew; uint8 *pixels1, *pixels3; int tbusy[max_threads]; float *s1mem, *s2mem, Rmax; } void ss_spheroid() { using namespace ss_spheroid_names; void * ss_spheroid_thread(void *arg); int ii, cc, px, py, dx, dy; float Ftab1[14] = { 0.10, 0.15, 0.23, 0.34, 0.51, 0.76, 1.14, // D/ss_ww ratio 1.71, 2.56, 3.84, 5.77, 8.65, 13.0, 19.5 }; float Ftab2[14] = { 0.90, 0.93, 0.95, 0.97, 0.97, 0.97, 0.97, // D reduction factor 0.95, 0.93, 0.90, 0.84, 0.76, 0.66, 0.50 }; cc = ss_ww * ss_hh * sizeof(float); s1mem = (float *) zmalloc(cc); Rmax = 1.0 + 0.5 * sqrtf(ss_ww * ss_ww + ss_hh * ss_hh); cc = 10.1 * Rmax * sizeof(float); // float rounding s2mem = (float *) zmalloc(cc); pxbold = gdk_pixbuf_copy(ss_pxbold); // old image at full size pixels1 = gdk_pixbuf_get_pixels(pxbold); Cx = ss_ww / 2; // center of image Cy = ss_hh / 2; for (py = 0; py < ss_hh; py++) // pre-calculate for (px = 0; px < ss_ww; px++) { dx = px - Cx; dy = py - Cy; ii = py * ss_ww + px; s1mem[ii] = sqrtf(dx*dx + dy*dy); // dist. from center to pixel } F = 1.0; cairo_t *cr = draw_context_create(gdkwin,draw_context); for (D = 20 * ss_ww; D > 10; D *= F) // loop from flat to sphere projection { R = D / ss_ww; for (ii = 1; ii < 13; ii++) // get F value for D / ss_ww ratio if (R < Ftab1[ii]) break; R = (R - Ftab1[ii-1]) / (Ftab1[ii] - Ftab1[ii-1]); F = Ftab2[ii-1] + R * (Ftab2[ii] - Ftab2[ii-1]); R = 0.025 * ss_slowdown; // 0.0 ... 0.99 if (R > 1.0) R = 1.0; R = sqrtf(R); // steepen curve at low end F = R * 0.99 + (1.0 - R) * F; // add slowdown factor pxbnew = gdk_pixbuf_copy(ss_pxbnew); // new image at full size pixels3 = gdk_pixbuf_get_pixels(pxbnew); for (int ii = 0; ii < ss_nwt; ii++) { // start worker threads tbusy[ii] = 1; // make combined image start_detached_thread(ss_spheroid_thread,&Nval[ii]); } for (int ii = 0; ii < ss_nwt; ii++) // wait for completion while(tbusy[ii]) zsleep(0.001); gdk_cairo_set_source_pixbuf(cr,pxbnew,0,0); // paint new image cairo_paint(cr); g_object_unref(pxbnew); zmainloop(); } zfree(s1mem); zfree(s2mem); g_object_unref(pxbold); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_spheroid_thread(void *arg) { using namespace ss_spheroid_names; int index = *((int *) arg); int ii, px0, py0, px3, py3, dx, dy; float px1, py1, s1, s2, T; uint8 *pix0, *pix1, *pix2, *pix3, *pixx, *pixx2; float f0, f1, f2, f3; for (ii = 0; ii < 10 * Rmax; ii++) // pre-calculate { s1 = 0.1 * ii; T = s1 * PI / D; if (s1 == 0) s2mem[ii] = 0; else if (T > 1.0) s2mem[ii] = -1; else s2mem[ii] = D / PI * asinf(T) / s1; } for (py3 = 2 * index; py3 < ss_hh-1; py3 += 2 * ss_nwt) // loop all output pixels for (px3 = 0; px3 < ss_ww-1; px3 += 2) { /*** dx = px3 - Cx; // code without pre-calculations dy = py3 - Cy; s1 = sqrtf(dx*dx + dy*dy); // dist. from center to output pixel if (s1 == 0) continue; T = s1 * PI / D; // sine of subtended angle if (T > 1.0) continue; s2 = D / PI * asinf(T); // corresp. dist. on sphere px1 = Cx + dx * s2 / s1; // input v.pixel py1 = Cy + dy * s2 / s1; ***/ dx = px3 - Cx; dy = py3 - Cy; ii = py3 * ss_ww + px3; s1 = s1mem[ii]; // dist. from center to output pixel ii = 10 * s1; s2 = s2mem[ii]; // corresp. dist. on sphere / s1 if (s2 < 0) continue; px1 = Cx + dx * s2; // input v.pixel py1 = Cy + dy * s2; // inline vpixel() for speed px0 = px1; // px0/py0: integer px1/py1 py0 = py1; if (px0 < 0 || py0 < 0) continue; if (px0 > ss_ww-3 || py0 > ss_hh-3) continue; f0 = (px0+1 - px1) * (py0+1 - py1); // overlap of (px,py) f1 = (px0+1 - px1) * (py1 - py0); // in each of the 4 pixels f2 = (px1 - px0) * (py0+1 - py1); f3 = (px1 - px0) * (py1 - py0); pix0 = pixels1 + py0 * ss_rs + px0 * 3; // pixel (px0,py0) pix1 = pix0 + ss_rs; // (px0,py0+1) pix2 = pix0 + 3; // (px0+1,py0) pix3 = pix1 + 3; // (px0+1,py0+1) pixx = pixels3 + py3 * ss_rs + px3 * 3; // input v.pixel >> output pixel pixx[0] = f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0]; pixx[1] = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1]; pixx[2] = f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2]; pixx[3] = f0 * pix0[3] + f1 * pix1[3] + f2 * pix2[3] + f3 * pix3[3]; pixx[4] = f0 * pix0[4] + f1 * pix1[4] + f2 * pix2[4] + f3 * pix3[4]; pixx[5] = f0 * pix0[5] + f1 * pix1[5] + f2 * pix2[5] + f3 * pix3[5]; pixx2 = pixx + ss_rs; memcpy(pixx2,pixx,6); // 17.08 } tbusy[index] = 0; pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // new image turns up from lower right corner, like a book page void ss_turnpage() { PIXBUF *pixbuf3; uint8 *pixels1, *pixels2, *pixels3; uint8 *pix1, *pix2, *pix3; int pxA, pxS; int px1, py1, px3, py3; int cc, np1, np2, f1; float C; np1 = 40 - ss_slowdown; if (np1 < 1) np1 = 1; np2 = 2 + np1 / 5; pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); // old image pixels2 = gdk_pixbuf_get_pixels(ss_pxbnew); // new image pixbuf3 = gdk_pixbuf_copy(ss_pxbold); // output image - mixture pixels3 = gdk_pixbuf_get_pixels(pixbuf3); // (initially = old image) cairo_t *cr = draw_context_create(gdkwin,draw_context); for (pxA = ss_ww-1; ; pxA -= np1) // point A moves from lower right corner { // to the left for (px1 = pxA, py1 = ss_hh-1; px1 < ss_ww && py1 >= 0; px1++, py1--) { pix2 = pixels2 + py1 * ss_rs + px1 * 3; // new image row pix3 = pixels3 + py1 * ss_rs + px1 * 3; // output image row cc = 3 * (ss_ww - px1); if (px1 < 0) { pix2 -= 3 * px1; pix3 -= 3* px1; cc += 3 * px1; } if (cc < 1) continue; if (cc > 3 * ss_ww) cc = cc / 2; memcpy(pix3,pix2,cc); // paint new image from px1 to right edge } f1 = 1; for (pxS = pxA; pxS < ss_ww-np2; pxS += np2) // point S moves from point A to the right { C = 0.53 * (pxS - pxA) / (ss_ww - pxA); C = C * (pxS - pxA); for (px1 = pxS, py1 = ss_hh-1; px1 < ss_ww && py1 >= 0; px1++, py1--) { px3 = px1 - C; // dest pixel = source pixel py3 = py1 - C; // offset in NW direction if (px3 < 0 || px3 > ss_ww-1) continue; if (py3 < 0 || py3 > ss_hh-1) continue; pix1 = pixels1 + py1 * ss_rs + px1 * 3; // source pixel --> dest pixel pix3 = pixels3 + py3 * ss_rs + px3 * 3; memcpy(pix3,pix1,3*np2); f1 = 0; } } gdk_cairo_set_source_pixbuf(cr,pixbuf3,0,0); // paint image cairo_paint(cr); zmainloop(); if (pxA < 0 && f1) break; } g_object_unref(pixbuf3); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // French door: old image swings away left and right to reveal new image. namespace ss_frenchdoor_names { PIXBUF *pxbmix, *pxbmod; // output image float step, Nsteps, R, ww2; uint8 *pixels1, *pixels3; int tbusy[max_threads]; int ww3, ww4, sww; } void ss_frenchdoor() { using namespace ss_frenchdoor_names; #define GPNFD(pix,ww,hh) \ gdk_pixbuf_new_from_data(pix,GDKRGB,0,8,ww,hh,ss_rs,0,0) void * ss_frenchdoor_thread(void *arg); Nsteps = 70 + 5 * ss_slowdown; ww2 = 0.5 * ss_ww; cairo_t *cr = draw_context_create(gdkwin,draw_context); for (step = 0; step < Nsteps; step++) { R = step / Nsteps; pxbmix = gdk_pixbuf_copy(ss_pxbnew); // mixed image pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); // old image pixels3 = gdk_pixbuf_get_pixels(pxbmix); for (int ii = 0; ii < ss_nwt; ii++) { // start worker threads tbusy[ii] = 1; // make combined image start_detached_thread(ss_frenchdoor_thread,&Nval[ii]); } for (int ii = 0; ii < ss_nwt; ii++) // wait for completion while(tbusy[ii]) zsleep(0.001); sww = ww2 / Nsteps + 6; // left side ww3 = (1 - R) * ww2 + sww; pxbmod = GPNFD(pixels3,ww3,ss_hh); gdk_cairo_set_source_pixbuf(cr,pxbmod,0,0); cairo_paint(cr); g_object_unref(pxbmod); ww4 = (1 + R) * ww2 - sww; // right side pxbmod = GPNFD(pixels3+ww4*3,ww3+sww,ss_hh); gdk_cairo_set_source_pixbuf(cr,pxbmod,ww4,0); cairo_paint(cr); g_object_unref(pxbmod); g_object_unref(pxbmix); zmainloop(); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_frenchdoor_thread(void *arg) { using namespace ss_frenchdoor_names; int index = *((int *) arg); uint8 *pix1, *pix3; int px1, py1, px3, py3, px3L = -1; float F; for (px1 = index; px1 < ww2; px1 += ss_nwt) // 0 >> ww2 { px3 = (1 - R) * px1; // 0 >> ww2 - X if (px3 == px3L) continue; px3L = px3; for (py1 = 0; py1 < ss_hh-1; py1 += 2) // 0 >> ss_hh { F = 0.2 * ss_hh * R * px1 / ww2; // 0 >> max py3 = F + (1.0 * py1 / ss_hh) * (ss_hh - 2 * F); // F >> ss_hh - F pix1 = pixels1 + py1 * ss_rs + px1 * 3; pix3 = pixels3 + py3 * ss_rs + px3 * 3; memcpy(pix3,pix1,3); memcpy(pix3 + ss_rs, pix1 + ss_rs, 3); } } for (px1 = ww2 + index; px1 < ss_ww; px1 += ss_nwt) // ww2 >> ss_ww { px3 = R * ss_ww + (1 - R) * px1; // ww2 + X >> ss_ww if (px3 == px3L) continue; px3L = px3; for (py1 = 0; py1 < ss_hh-1; py1 += 2) // 0 >> ss_hh { F = 0.2 * ss_hh * R * (ss_ww - px1) / ww2; // max >> 0 py3 = F + (1.0 * py1 / ss_hh) * (ss_hh - 2 * F); // F >> ss_hh - F pix1 = pixels1 + py1 * ss_rs + px1 * 3; pix3 = pixels3 + py3 * ss_rs + px3 * 3; memcpy(pix3,pix1,3); memcpy(pix3 + ss_rs, pix1 + ss_rs, 3); } } tbusy[index] = 0; pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // turn a cube to a new face with new image namespace turncube_names { PIXBUF *pxbmix; int increment; float edge; uint8 *pixels1, *pixels2, *pixels3; int tbusy[max_threads]; int tc_nwt = 2; // 2 threads } void ss_turncube() // 17.01 { using namespace turncube_names; void * ss_turncube_thread(void *arg); pxbmix = gdk_pixbuf_copy(ss_pxbold); pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); pixels2 = gdk_pixbuf_get_pixels(ss_pxbnew); pixels3 = gdk_pixbuf_get_pixels(pxbmix); increment = 30 - ss_slowdown; if (increment < 2) increment = 2; cairo_t *cr = draw_context_create(gdkwin,draw_context); for (edge = ss_ww-3; edge > 1; edge -= increment) // ss_ww ... 0 (almost) { for (int ii = 0; ii < tc_nwt; ii++) { // start worker threads tbusy[ii] = 1; // make combined image start_detached_thread(ss_turncube_thread,&Nval[ii]); } for (int ii = 0; ii < tc_nwt; ii++) // wait for completion while(tbusy[ii]) zsleep(0.0001); gdk_cairo_set_source_pixbuf(cr,pxbmix,0,0); cairo_paint(cr); zmainloop(); } g_object_unref(pxbmix); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_turncube_thread(void *arg) { /** NOTE - reversing order of x/y loops is slower **/ using namespace turncube_names; int index = *((int *) arg); float M1, M2, R; int px1, py1, px2, py2, px3, py3; uint8 *pix1, *pix2, *pix3; uint8 pixblack[3] = { 0, 0, 0 }; M1 = 0.2 * (1.0 - edge / ss_ww); // 0 ... 0.2 for (px3 = 4 * index; px3 < edge-3; px3 += 4 * tc_nwt) // 0 ... edge, old image { R = px3 / edge; // 0 ... 1.0 M2 = M1 * (1.0 - R); // M1 ... 0 for (py3 = 0; py3 < ss_hh; py3++) // 0 ... ss_hh { px1 = R * ss_ww; // 0 ... ss_ww py1 = ss_hh * (-M2 + (1.0 + 2.0 * M2) * py3 / ss_hh); // (-M2 ... 1 + M2) * ss_hh if (py1 < 0 || py1 >= ss_hh) pix1 = pixblack; else pix1 = pixels1 + py1 * ss_rs + px1 * 3; pix3 = pixels3 + py3 * ss_rs + px3 * 3; memcpy(pix3,pix1,12); } } M1 = 0.2 * edge / ss_ww; // 0.2 ... 0 for (px3 = edge + 4 * index; px3 < ss_ww-3; px3 += 4 * tc_nwt) // edge ... ss_ww, new image { R = (px3 - edge) / (ss_ww - edge); // 0 ... 1.0 M2 = R * M1; // 0 ... M1 for (py3 = 0; py3 < ss_hh; py3++) // 0 ... ss_hh { px2 = R * ss_ww; // 0 ... ss_ww py2 = ss_hh * (-M2 + (1.0 + 2.0 * M2) * py3 / ss_hh); // (-M2 ... 1 + M2) * ss_hh if (py2 < 0 || py2 >= ss_hh) pix2 = pixblack; else pix2 = pixels2 + py2 * ss_rs + px2 * 3; pix3 = pixels3 + py3 * ss_rs + px3 * 3; memcpy(pix3,pix2,12); } } tbusy[index] = 0; pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // new image rotates over old image in many radial segments void ss_windmill() // 17.01 { int px, py; int skip = 0; int ww2 = ss_ww/2, hh2 = ss_hh/2; float R, Rmax, T1, T2, T, dT; float cosT, sinT; uint8 *pixels1, *pixels3, *pix1, *pix3; pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); pixels3 = gdk_pixbuf_get_pixels(ss_pxbnew); Rmax = sqrt(ww2*ww2 + hh2*hh2); // max. line length, center to corner dT = 0.7 / Rmax; // angle step cairo_t *cr = draw_context_create(gdkwin,draw_context); for (T1 = 0; T1 < PI/9; T1 += dT) // steps within a segment { for (T2 = 0; T2 < 2*PI; T2 += PI/9) // segments { T = T1 + T2; // segment and step cosT = cosf(T); sinT = sinf(T); for (R = 0; R < Rmax; R++) // radial line from center to edge { px = ww2 + R * cosT; py = hh2 + R * sinT; if (px < 0 || px >= ss_ww) break; if (py < 0 || py >= ss_hh) break; pix1 = pixels1 + py * ss_rs + px * 3; // copy new image pixel to old image pix3 = pixels3 + py * ss_rs + px * 3; memcpy(pix1,pix3,3); } } if (--skip > 0) continue; skip = 5; gdk_cairo_set_source_pixbuf(cr,ss_pxbold,0,0); cairo_paint(cr); zmainloop(); zsleep(0.0003 * ss_slowdown); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // Old image is increasingly / decreasingly pixelated to reveal new image void ss_pixelize() // 17.01 { PIXBUF *pxbmix; int px, py, px1, py1, px2, py2; int ii, cc, step, Nsteps, Npix, blocksize; int Rsum, Gsum, Bsum, Ravg, Gavg, Bavg; uint8 *pixels1, *pixels2, *pixels3, *pix3; float F1, F2; pxbmix = gdk_pixbuf_copy(ss_pxbnew); // 17.08 pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); pixels2 = gdk_pixbuf_get_pixels(ss_pxbnew); pixels3 = gdk_pixbuf_get_pixels(pxbmix); Nsteps = 30 + 2 * ss_slowdown; // 17.08 cairo_t *cr = draw_context_create(gdkwin,draw_context); for (step = 1; step < Nsteps; step++) { F1 = 1.0 * step / Nsteps; // new image part, 0 ... 1 F2 = 1 - F1; // old image part, 1 ... 0 cc = ss_hh * ss_rs; // pxbmix = mix of old and new images for (ii = 0; ii < cc; ii++) pixels3[ii] = F1 * pixels2[ii] + F2 * pixels1[ii]; // pxbold + pxbnew >> pxbmix blocksize = 0.05 * ss_ww; // 17.08 if (F1 < 0.5) blocksize = 2 + blocksize * F1; // pixel block size, 2 ... max ... 2 else blocksize = 2 + blocksize * F2; for (py1 = 0; py1 < ss_hh; py1 += blocksize) // loop pixel blocks for (px1 = 0; px1 < ss_ww; px1 += blocksize) { py2 = py1 + blocksize; // block region in image if (py2 > ss_hh) py2 = ss_hh; px2 = px1 + blocksize; if (px2 > ss_ww) px2 = ss_ww; Rsum = Gsum = Bsum = Npix = 0; for (py = py1; py < py2; py++) // loop pixels in pixel block for (px = px1; px < px2; px++) { pix3 = pixels3 + py * ss_rs + px * 3; Rsum += pix3[0]; // sum pixel RGB values Gsum += pix3[1]; Bsum += pix3[2]; Npix++; } Ravg = Rsum / Npix; // mean pixel RGB values for block Gavg = Gsum / Npix; Bavg = Bsum / Npix; for (py = py1; py < py2; py++) // loop pixels in pixel block for (px = px1; px < px2; px++) { pix3 = pixels3 + py * ss_rs + px * 3; pix3[0] = Ravg; // set all pixel RGB values to mean pix3[1] = Gavg; pix3[2] = Bavg; } } gdk_cairo_set_source_pixbuf(cr,pxbmix,0,0); cairo_paint(cr); zmainloop(); } g_object_unref(pxbmix); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // Old image twists 0 >> 360 deg. as new image untwists -360 >> 0 deg. // The old image fades-out as the new image fades-in. namespace twist_names { PXB *PXBold, *PXBnew, *PXBmix; int cx, cy; float Told, Tnew; float F1, F2; float *D, *Tp; float Dmax; int tbusy[max_threads]; } void ss_twist() // 17.08 { using namespace twist_names; void * ss_twist_thread(void *arg); int step, Nsteps = 100; int ii, px, py, Dx, Dy; cx = ss_ww / 2; // image center cy = ss_hh / 2; Dmax = sqrtf(cx * cx + cy * cy); // distance to corner ii = ss_ww * ss_hh; D = (float *) zmalloc(ii * sizeof(float)); // precalculated factors Tp = (float *) zmalloc(ii * sizeof(float)); // depending only on px, py for (py = 0; py < ss_hh; py++) // loop all pixels for (px = 0; px < ss_ww; px++) { ii = py * ss_ww + px; Dx = px - cx; // px/py relative to cx/cy Dy = py - cy; D[ii] = Dx * Dx + Dy * Dy; // distance pixel to center if (D[ii] == 0) continue; D[ii] = sqrtf(D[ii]); Tp[ii] = asinf(Dy/D[ii]); // angle of pixel line to center if (Dx < 0) { if (Dy > 0) Tp[ii] = PI - Tp[ii]; else Tp[ii] = - PI - Tp[ii]; } } PXBold = PXB_make(ss_pxbold); // do not free PXBnew = PXB_make(ss_pxbnew); // do not free PXBmix = PXB_copy(PXBold); // free Nsteps = 40 + 5 * ss_slowdown; cairo_t *cr = draw_context_create(gdkwin,draw_context); for (step = 1; step <= Nsteps; step++) { F1 = 1.0 * step / Nsteps; // new image part, 0 ... 1 F2 = 1 - F1; // old image part, 1 ... 0 Told = 2 * PI * F1; // old image twist 0 ... 360 deg. Tnew = - 2 * PI + Told; // new image twist -360 ... deg. /* Tnew = -Tnew; */ // opposite directions for (int ii = 0; ii < ss_nwt; ii++) { // start worker threads tbusy[ii] = 1; // make combined image start_detached_thread(ss_twist_thread,&Nval[ii]); } for (int ii = 0; ii < ss_nwt; ii++) // wait for completion while(tbusy[ii]) zsleep(0.001); gdk_cairo_set_source_pixbuf(cr,PXBmix->pixbuf,0,0); cairo_paint(cr); zmainloop(); } PXB_free(PXBmix); zfree(D); zfree(Tp); gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } void * ss_twist_thread(void *arg) { using namespace twist_names; int index = *((int *) arg); int ii, px, py, qx, qy; float DN, Tq, T; uint8 *pixmix, *vpix, pix1[3], pix2[3], pix3[3]; for (py = 2 * index; py < ss_hh-1; py += 2 * ss_nwt) // loop all pixels for (px = 0; px < ss_ww-1; px += 2) { ii = py * ss_ww + px; DN = D[ii]/Dmax; // distance from center, 0.0 ... 1.0 T = Told * DN; // old image rotation at distance Tq = Tp[ii] + T; // rotated pixel angle qx = D[ii] * cosf(Tq) + cx; // rotated pixel position qy = D[ii] * sinf(Tq) + cy; if (qx > 0 && qx < ss_ww && qy > 0 && qy < ss_hh) { vpix = PXBpix(PXBold,qx,qy); memcpy(pix1,vpix,3); } else memset(pix1,0,3); T = Tnew * DN; // same for new image Tq = Tp[ii] + T; qx = D[ii] * cosf(Tq) + cx; qy = D[ii] * sinf(Tq) + cy; if (qx > 0 && qx < ss_ww && qy > 0 && qy < ss_hh) { vpix = PXBpix(PXBnew,qx,qy); memcpy(pix2,vpix,3); } else memset(pix2,0,3); pixmix = PXBpix(PXBmix,px,py); // blend the twisted image pix3[0] = F2 * pix1[0] + F1 * pix2[0]; pix3[1] = F2 * pix1[1] + F1 * pix2[1]; pix3[2] = F2 * pix1[2] + F1 * pix2[2]; pixmix[0] = pix3[0]; pixmix[1] = pix3[1]; pixmix[2] = pix3[2]; pixmix[3] = pix3[0]; pixmix[4] = pix3[1]; pixmix[5] = pix3[2]; pixmix += ss_rs; pixmix[0] = pix3[0]; pixmix[1] = pix3[1]; pixmix[2] = pix3[2]; pixmix[3] = pix3[0]; pixmix[4] = pix3[1]; pixmix[5] = pix3[2]; } tbusy[index] = 0; pthread_exit(0); return 0; } // ------------------------------------------------------------------------------ // X-shaped region opens up from the center to reveal new image void ss_Xopen() // 17.08 { GdkPixbuf *pxbmix; float step, speed; int xdisp, ydisp; int px1, py1, px2, py2; int pxL, pxH; int ww = ss_ww, hh = ss_hh, rs = ss_rs; int ww2 = ww/2, hh2 = hh/2; uint8 *pixels1, *pixels2; uint8 *pix1, *pix2; float M = 1.0 * hh / ww; speed = 5 - 0.7 * sqrtf(ss_slowdown); if (speed < 0.3) speed = 0.3; cairo_t *cr = draw_context_create(gdkwin,draw_context); for (step = 0; step < hh2; step += speed) { pxbmix = gdk_pixbuf_copy(ss_pxbnew); pixels1 = gdk_pixbuf_get_pixels(ss_pxbold); // old image pixels2 = gdk_pixbuf_get_pixels(pxbmix); // new image + old image overlay ydisp = step; xdisp = step / M; for (py1 = ydisp; py1 < hh2; py1++) // top triangle { pxL = py1/M; pxH = ww - pxL; for (px1 = pxL; px1 < pxH; px1++) { py2 = py1 - ydisp; px2 = px1; pix1 = pixels1 + py1 * rs + px1 * 3; pix2 = pixels2 + py2 * rs + px2 * 3; memcpy(pix2,pix1,3); } } for (py1 = ydisp; py1 < hh - ydisp; py1++) // right triangle { pxL = ww2 + abs(py1 - hh2)/M; pxH = ww - xdisp; for (px1 = pxL; px1 < pxH; px1++) { py2 = py1; px2 = px1 + xdisp; pix1 = pixels1 + py1 * rs + px1 * 3; pix2 = pixels2 + py2 * rs + px2 * 3; memcpy(pix2,pix1,3); } } for (py1 = hh2; py1 < hh - ydisp; py1++) // bottom triangle { pxL = ww2 - (py1 - hh2)/M; pxH = ww - pxL; for (px1 = pxL; px1 < pxH; px1++) { py2 = py1 + ydisp; px2 = px1; pix1 = pixels1 + py1 * rs + px1 * 3; pix2 = pixels2 + py2 * rs + px2 * 3; memcpy(pix2,pix1,3); } } for (py1 = ydisp; py1 < hh - ydisp; py1++) // left triangle { pxL = xdisp; pxH = ww2 - abs(py1 - hh2)/M; for (px1 = pxL; px1 < pxH; px1++) { py2 = py1; px2 = px1 - xdisp; pix1 = pixels1 + py1 * rs + px1 * 3; pix2 = pixels2 + py2 * rs + px2 * 3; memcpy(pix2,pix1,3); } } gdk_cairo_set_source_pixbuf(cr,pxbmix,0,0); cairo_paint(cr); g_object_unref(pxbmix); zmainloop(); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image 17.08 cairo_paint(cr); draw_context_destroy(draw_context); return; } // ------------------------------------------------------------------------------ // new image presses out from center and squishes old image to the edges void ss_squish() // 18.01 { GdkPixbuf *pxbout; uint8 *pixold, *pixnew, *pixout; uint8 *pix1, *pix2; int ww, hh, rs, cx, cy, cmin; int cc, ii, Q; int px, py, qx, qy, vx, vy, ex, ey; float R, Rmax, Rstep; float *De, *Dp; // edge and pixel distance from center float *Ex, *Ey; // edge coordinates float M, V, Ra; Rstep = 20 - ss_slowdown; if (Rstep < 1) Rstep = 1; ww = ss_ww; hh = ss_hh; rs = ss_rs; cx = ww/2; cy = hh/2; Ra = 1.0 * hh/ww; Rmax = sqrtf(cx*cx + cy*cy); // distance (0,0) to corner cmin = cy; if (cx < cy) cmin = cx; pxbout = gdk_pixbuf_copy(ss_pxbold); // output image pixold = gdk_pixbuf_get_pixels(ss_pxbold); // old image pixnew = gdk_pixbuf_get_pixels(ss_pxbnew); // new image pixout = gdk_pixbuf_get_pixels(pxbout); cc = ww * hh * sizeof(float); // allocate distance arrays De = (float *) zmalloc(cc); // distance (0,0) to edge Dp = (float *) zmalloc(cc); // distance (0,0) to (px,py) Ex = (float *) zmalloc(cc); // line (0,0) to (px,py) Ey = (float *) zmalloc(cc); // extended to edge (Ex,Ey) for (py = 0; py < hh; py++) // compute distances to center for (px = 0; px < ww; px++) // and to edge for each pixel { qx = px - cx; // convert to center origin qy = py - cy; if (qx == 0) { M = 1000; // qx = 0 >> slope = huge if (qy > 0) Q = 4; else Q = 2; } else M = 1.0 * qy / qx; // slope of line (0,0) to (px,py) Q = 0; ex = ey = 0; if (qx > 0) { // extended line (0,0) to (px,py) if (qy > 0) { // intersects which edge? if (M > Ra) Q = 4; // bottom edge else Q = 1; // right edge } else if (-M > Ra) Q = 2; // top edge else Q = 1; // right edge } else if (qx < 0) { if (qy > 0) { if (-M > Ra) Q = 4; // bottom edge else Q = 3; // left edge } else { if (M > Ra) Q = 2; // top edge else Q = 3; // left edge } } if (Q == 1) { // get edge intersect coordinates ex = ww; ey = cy + M * (ex-cx); } else if (Q == 2) { ey = 0; ex = cx - cy / M; } else if (Q == 3) { ex = 0; ey = cy - cx * M; } else if (Q == 4) { ey = hh; ex = cx + (ey-cy) / M; } ii = ww * py + px; De[ii] = sqrtf((ex-cx)*(ex-cx) + (ey-cy)*(ey-cy)); // distance (0,0) to edge (ex,ey) Dp[ii] = sqrtf(qx*qx + qy*qy); // distance (0,0) to (px,py) Ex[ii] = ex - cx; // line (0.0) to (px,py) Ey[ii] = ey - cy; // extended to edge (ex,ey) } cairo_t *cr = draw_context_create(gdkwin,draw_context); for (R = Rstep; R < Rmax; R += Rstep) // loop R from center to edge { for (py = 0; py < hh-1; py += 2) // loop all image pixels for (px = 0; px < ww-1; px += 2) { ii = ww * py + px; qx = px - cx; // convert to center origin qy = py - cy; if (Dp[ii] < R) // new image from center to R { V = (1.0 - R/Rmax) * (cmin/R) + (R/Rmax); vx = cx + V * qx; vy = cy + V * qy; if (vx < 0 || vx > ww-1) continue; if (vy < 0 || vy > hh-1) continue; pix1 = pixnew + vy * rs + vx * 3; // new image pixel >> output image pix2 = pixout + py * rs + px * 3; memcpy(pix2,pix1,6); memcpy(pix2+rs,pix1+rs,6); } else if (R < De[ii]) // old image from R to edge { V = (Dp[ii] - R) / (De[ii] - R); // source pixel position vx = cx + V * Ex[ii]; vy = cy + V * Ey[ii]; if (vx < 0 || vx > ww-1) continue; if (vy < 0 || vy > hh-1) continue; pix1 = pixold + vy * rs + vx * 3; // old image pixel >> output image pix2 = pixout + py * rs + px * 3; memcpy(pix2,pix1,6); memcpy(pix2+rs,pix1+rs,6); } } gdk_cairo_set_source_pixbuf(cr,pxbout,0,0); cairo_paint(cr); zmainloop(); } gdk_cairo_set_source_pixbuf(cr,ss_pxbnew,0,0); // final image cairo_paint(cr); draw_context_destroy(draw_context); zfree(De); zfree(Dp); zfree(Ex); zfree(Ey); g_object_unref(pxbout); return; } // ------------------------------------------------------------------------------ // position zoomed image in pixbuf for display in window // file: input image file // zoom: image zoom size, e.g. 2.0 = 2x window size (1x = fit window) // mode: 1 = initialize for new image: zoom = full zoom factor // 2 = get zoomed image: 1.0 <= zoom <= full zoom factor // fast: 1 = fast zoom, 0 = slow zoom (higher quality) PIXBUF * ss_zoom_posn(char *file, int mode, float zoom, int fast) { static PIXBUF *pxb1 = 0, *pxb2 = 0, *pxb3 = 0; static int ww1, hh1, ww2, hh2, ww3, hh3; static float cx, cy; static float zoomax; static int ox1, oy1, ox2, oy2, ox3, oy3, ox4, oy4, ox5, oy5; GError *gerror = 0; float Rm, Rw, dR, R; if (mode == 1) // initialize { if (pxb1) g_object_unref(pxb1); // free prior memory bugfix 17.04.3 if (pxb2) g_object_unref(pxb2); pxb1 = gdk_pixbuf_new_from_file(file,&gerror); // load 1x image file if (! pxb1) { zmessageACK(Mwin,gerror->message); return 0; } pxb2 = gdk_pixbuf_stripalpha(pxb1); // stip alpha channel if present if (pxb2) { g_object_unref(pxb1); pxb1 = pxb2; } ww1 = gdk_pixbuf_get_width(pxb1); // image dimensions, 1x hh1 = gdk_pixbuf_get_height(pxb1); zoomax = zoom; // full zoom factor ww2 = ss_ww * zoomax; // image dimensions for full zoom hh2 = ss_hh * zoomax; // = zoomax * window size Rm = 1.0 * ww1 / hh1; // image width/height ratio Rw = 1.0 * ww2 / hh2; // window width/height ratio dR = fabsf(Rm - Rw) / Rw; // difference ratio if (dR <= 0.01 * ss_cliplimit) { // difference is within user limit if (Rw >= Rm) { ww1 = ww2; // width fits window hh1 = ww1 / Rm; // height will be cut } else { hh1 = hh2; // height fits window ww1 = hh1 * Rm; // width will be cut } } else { // difference is too great if (Rw >= Rm) { hh1 = hh2; // height fits window ww1 = hh1 * Rm; // width will get margins } else { ww1 = ww2; // width fits window hh1 = ww1 / Rm; // height will get margins } } pxb2 = pixbuf_rescale_fast(pxb1,ww1,hh1); // avoid pixbuf scaling bug 17.08 g_object_unref(pxb1); pxb1 = pxb2; pxb2 = 0; cx = 0.01 * ss_zoomlocx; // zoom center, 0.5/0.5 = image midpoint cy = 0.01 * ss_zoomlocy; cx = cx * ww1; // zoom center image position cy = cy * hh1; ox1 = (ww1 - ww2) / 2; // origin of image to copy from if (ox1 < 0) ox1 = 0; oy1 = (hh1 - hh2) / 2; if (oy1 < 0) oy1 = 0; ox2 = (ww2 - ww1) / 2; // origin of image to copy to if (ox2 < 0) ox2 = 0; oy2 = (hh2 - hh1) / 2; if (oy2 < 0) oy2 = 0; cx = cx - ox1 + ox2; // zoom target center, adjusted cy = cy - oy1 + oy2; // for clipping and margins if (ww2 < ww1) ww1 = ww2; // copy width if (hh2 < hh1) hh1 = hh2; // copy height pxb2 = gdk_pixbuf_new(GDKRGB,0,8,ww2,hh2); // pixbuf matching window w/h ratio gdk_pixbuf_fill(pxb2,0); // for black margins gdk_pixbuf_copy_area(pxb1,ox1,oy1,ww1,hh1,pxb2,ox2,oy2); // insert image g_object_unref(pxb1); // image at zoomax size, clipped pxb1 = pxb2; // or with added margins, pxb2 = 0; // matching window w/h ratio ww1 = ww2; // base input image, zoomax size hh1 = hh2; // (pxb1, ww1, hh1) ww2 = ww1 / zoomax; // final zoomed image size hh2 = hh1 / zoomax; // (will be scaled to window size) ox2 = cx - ww2 / 2; // upper left corner of oy2 = cy - hh2 / 2; // final zoomed image if (ox2 + ww2 > ww1) ox2 = ww1 - ww2; // keep within image bounds if (ox2 < 0) ox2 = 0; if (oy2 + hh2 > hh1) oy2 = hh1 - hh2; if (oy2 < 0) oy2 = 0; ox3 = ox2 + ww2; // lower right corner of oy3 = oy2 + hh2; // final zoomed image return 0; } if (mode == 2) // return zoomed image { R = (zoom - 1) / (zoomax - 1); // R = 0 ... 1 ox4 = ox2 * R; // upper left corner oy4 = oy2 * R; // = 0/0 ... ox2/oy2 ox5 = ww1 - (ww1 - ox3) * R; // lower right corner oy5 = hh1 - (hh1 - oy3) * R; // = ww1/hh1 ... ox3/oy3 ww3 = ox5 - ox4; // size = ww1/hh1 ... ww2/hh2 hh3 = oy5 - oy4; pxb2 = gdk_pixbuf_new_subpixbuf(pxb1,ox4,oy4,ww3,hh3); // image segment to display if (! pxb2) zappcrash("ss_zoomposn() %d %d %d %d",ox4,oy4,ww3,hh3); pxb3 = pixbuf_rescale_fast(pxb2,ss_ww,ss_hh); // avoid pixbuf scaling bug 17.08 g_object_unref(pxb2); pxb2 = 0; return pxb3; } return 0; } // slowly zoom-in on the image (Ken Burns effect) void ss_zoomin() { PIXBUF *pixbuf; float zoom; float zoominc = (ss_zoomsize - 1.0) / ss_zoomsteps; // zoom increment ss_zoom_posn(ss_newfile,1,ss_zoomsize,0); // initialize for new image cairo_t *cr = draw_context_create(gdkwin,draw_context); for (zoom = 1.0; zoom < ss_zoomsize; zoom += zoominc) // loop fit window ... zoom size { pixbuf = ss_zoom_posn(0,2,zoom,1); // get zoomed image at zoom size gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); // paint cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); if (ss_escape) break; } zoom = ss_zoomsize; pixbuf = ss_zoom_posn(0,2,zoom,0); // last image, high quality rescale gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); // paint cairo_paint(cr); zmainloop(); draw_context_destroy(draw_context); g_object_unref(ss_pxbnew); // retain final zoomed image ss_pxbnew = pixbuf; // for next transition return; } // slowly zoom-out from initial image center point void ss_zoomout() { PIXBUF *pixbuf; float zoom; float zoominc = (ss_zoomsize - 1.0) / ss_zoomsteps; // zoom increment ss_zoom_posn(ss_newfile,1,ss_zoomsize,0); // initialize for new image cairo_t *cr = draw_context_create(gdkwin,draw_context); for (zoom = ss_zoomsize; zoom > 1.0; zoom -= zoominc) // loop zoom size ... fit window { pixbuf = ss_zoom_posn(0,2,zoom,1); // get zoomed image at zoom size gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); // paint cairo_paint(cr); g_object_unref(pixbuf); zmainloop(); if (ss_escape) break; } zoom = 1.0; pixbuf = ss_zoom_posn(0,2,zoom,0); // last image, high quality rescale gdk_cairo_set_source_pixbuf(cr,pixbuf,0,0); // paint cairo_paint(cr); zmainloop(); draw_context_destroy(draw_context); g_object_unref(ss_pxbnew); // retain final zoomed image ss_pxbnew = pixbuf; // for next transition return; } fotoxx-18.01.1/zfuncs.cc0000644000175000017500000165524313222767271013521 0ustar micomico/******************************************************************************** zfuncs.cpp collection of Linux and GDK/GTK utility functions Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. *********************************************************************************/ // zfuncs.cpp version v.6.8 #include "zfuncs.h" /******************************************************************************** Table of Contents ================= System Utility Functions ------------------------ zmalloc zfree zstrdup replace malloc() etc. to add checks and statistics printz printf() with immediate fflush() zpopup_message popup message window, thread safe (no GTK) zbacktrace callable backtrace dump zappcrash abort with traceback dump to popup window and stdout catch_signals trap segfault, crash with zappcrash() TRACE trace() and tracedump() implement the TRACE macro beroot restart image as root, if password is OK timer functions elapsed time, CPU time, process time functions compact_time convert time_t type to yyyymmddhhmmss format pretty_datetime convert time_t type to yyyy-mm-dd hh:mm:ss format proc file functions parse data from various /proc files coretemp get current processor core temperature disktemp get temperature for given disk drive zsleep sleep for any amount of time (e.g. 0.1 seconds) global_lock lock/unlock a global resource (all processes/threads) resource_lock lock/unlock a resource within a process + threads zget_locked, etc. safely access parameters from multiple threads start_detached_thread simplified method to start a detached thread synch_threads make threads pause and resume together zsystem system() + return child process status shell_quiet format and run a shell command, return status shell_ack "" + popup error message if error shell_asynch "" + return immediately and query status later command_output start shell command and read the output as records signalProc pause, resume, or kill a child process runroot run a command or program as root user fgets_trim fgets() with trim of trailing \r \n and optionally blanks samedirk test if two files/directories have the same directory path parsefile parse filespec into directory, file, extension renamez like rename() but works across file systems check_create_dir check if directory exists, ask to create if not copyFile copy file to file or file to directory combine_argvs catenate argv[ii] elements from Nth to last cpu_profile measure CPU time spent per function or code block pagefaultrate monitor and report own process hard page fault rate String Functions ---------------- strField get delimited substrings from input string strParms parse a string in the form "parm1=1.23, parm2=22 ..." strHash hash string to random number in a range strncpy0 strcpy() with insured null delimiter strnPad add blank padding to specified length strTrim remove trailing blanks strTrim2 remove leading and trailing blanks strCompress remove imbedded blanks strncatv catenate multiple strings with length limit strmatchV compare 1 string to N strings strToUpper convert string to upper case strToLower convert string to lower case repl_1str replace substring within string repl_Nstrs replace multiple substrings within string breakup_text insert newline chars to limit text line lengths strncpyx convert string to hex format StripZeros remove trailing zeros (1.23000E+8 >> 1.23E+8) blank_null test string for null pointer, zero length, and all blanks clean_escapes replace 2-character escapes ("\n") with the escaped characters UTF-8 functions deal with UTF-8 multibyte character strings zsed substitute multiple strings in a file Number Conversion and Formatting -------------------------------- convSI string to integer with optional limits check convSD string to double with optional limits check convSF string to float with optional limits check convIS integer to string with returned length convDS double to string with specified digits of precision formatKBMB format a byte count with specified precision and B/KB/MB/GB units Wildcard Functions ------------------ MatchWild match string to wildcard string (multiple * and ?) MatchWildIgnoreCase works like MatchWild() but ignores case SearchWild wildcard file search (multiple * and ? in path or file name) SearchWildCase works like SearchWild() but ignores case in file name zglob file search with glob-style pattern matching Search and Sort Functions ------------------------- bsearch binary search of sorted list HeapSort sort list of integer / float / double / records / pointers to records MemSort sort records with multiple keys (data types and sequence) zmember test if a value is a member of a set of values Misc Functions -------------- pvlist_create, etc. functions to manage a list of variable strings random numbers int and double random numbers with improved distributions spline1/2 cubic spline curve fitting function Qtext FIFO queue for text strings, dual thread access Application Admin Functions --------------------------- zinitapp etc. initialize application directory and data files zsetfont set new application font locale_filespec find a locale-dependent installation file (translate-xx.po etc.) showz_userguide display user guide (optional help topic) showz_logfile display application log file showz_textfile show application text file (README, changelog, etc.) showz_html show a local or remote HTML file ZTX(), etc. translate GUI and message text strings for non-English locales GTK Utility Functions --------------------- zmainloop do main loop to process menu events, etc. zmainsleep loop zmainloop and zsleep for designated time draw_context_create get cairo drawing context for GDK window textwidget funcs text reports, navigation, line editing menus / toolbars simplified GTK menus, toolbars and status bars Gmenuz customizable graphic popup menu create_popmenu implement popup menus Vmenu vertical menu/toolbar in vertical packing box zdialog_new create new zdialog zdialog_set_title change a zdialog title zdialog_set_modal set a zdialog to be modal zdialog_set_decorated set a zdialog to be decorated or not zdialog_present present a zdialog (visible and on top) zdialog_add_widget add widget to existing zdialog zdialog_valid return 1/0 if zdialog is valid/invalid zdialog_widget get GTK widget from zdialog and widget name zdialog_add_ttip add a popup tool tip to a zdialog widget zdialog_set_group set a common group for a set of radio buttons zdialog_resize resize zdialog greater than initial size zdialog_set_limits set new limits for numeric data entry widget zdialog_rescale expande scale around neutral value zdialog_run run the zdialog and send events to event function zdialog_widget_event respond to zdialog widget events zdialog_KB_press respond to zdialog keyboard inputs zdialog_zspin_event response function for "zspin" widget zdialog_send_event send an event to an active zdialog zdialog_send_response complete a zdialog and assign status zdialog_show show or hide a zdialog window zdialog_destroy destroy a zdialog (data remains available) zdialog_free free zdialog memory (data is gone) zdialog_wait wait for zdialog completion, get status zdialog_goto put cursor at named widget zdialog_set_cursor set zdialog cursor (e.g. busy) zdialog_stuff stuff data into zdialog widget zdialog_labelfont set label text with font zdialog_fetch fetch data from zdialog widget zdialog_cb_app append item to combo box list widget zdialog_cb_prep prepend item to combo box list widget zdialog_cb_get get data from combo box entry widget zdialog_cb_delete delete combo box entry from list zdialog_cb_clear clear combo box list zdialog_cb_popup make combo box show list of all entries zdialog_cb_save save combo box list to a file zdialog_cb_load load combo box list from a file zdialog_positions load/save zdialog positions at app start/exit zdialog_inputs load/save zdialog inputs at app start/exit zdialog_restore_inputs retrieve prior zdialog input fields window_to_mouse move a GtkWindow to the mouse position popup_report_open etc. popup window and scrolling text report popup_command run a shell command with output in a popup window zmessageACK popup message, printf format, wait for user ACK zmessageYN popup message, printf format, wait for user Yes / No zmessage_post popup message, printf format, show until killed zmessage_post_bold popup message, same as above, but big bold red font zdialog_text popup zdialog to get 1-2 lines of text input from user zdialog_choose popup zdialog to show a message, select a button, return choice poptext_mouse popup message at current mouse position + offset poptext_window popup message at given window position poptext_killnow kill popup message popup_image show an image in a small popup window zgetfile simplified file chooser zdialog print_image_file zdialog to print an image file using GTK functions drag_drop_source connect window as drag-drop source drag_drop_dest connect window as drag-drop destination get_thumbnail get thumbnail image for given image file zmakecursor make a cursor from an image file (.png .jpg) gdk_pixbuf_rotate rotate a pixbuf through any angle gdk_pixbuf_stripalpha remove an alpha channel from a pixbuf text_pixbuf create pixbuf containing text move_pointer move the mouse pointer within a widget/window C++ Classes ----------- xstring string manipulation (= / + / insert / overlay) Vxstring array of xstrings with auto growth HashTab hash table: add, delete, find, step through Queue queue of xstrings: push, pop first or last Tree store / retrieve data by node names or numbers, any depth *********************************************************************************/ namespace zfuncs { struct timeb startime; // app startup time GdkDisplay *display; // workstation (KB, mouse, screen) GdkScreen *screen; // screen, N monitors GdkDevice *mouse; // pointer device GtkSettings *gtksettings = 0; // screen settings 6.2 int monitor_ww, monitor_hh; // monitor dimensions 6.6 cchar *appfont = "sans 10"; // application font and size 6.5 cchar *appboldfont = "sans bold 10"; // defaults cchar *appmonofont = "mono 10"; cchar *appmonoboldfont = "mono bold 10"; int appfontsize = 10; char zappname[20]; // app name/version char zprefix[200], zdatadir[200], zdocdir[200]; // app directories 6.8 char zicondir[200], zimagedir[200]; char zlocalesdir[200], zhomedir[200]; char zlang[8] = "en"; // "lc" or "lc_RC" int open_popup_windows = 0; // open popup window count pthread_t tid_main = 0; // main() thread ID int Nmalloc, Nstrdup, Nfree; // zmalloc() zstrdup() and zfree() calls int vmenuclickposn; // Vmenu image click posn. 0-100 int vmenuclickbutton; // button: 1/2/3 = L/M/R mouse zdialog *zdialog_list[zdialog_max]; // active zdialog list 6.0 int zdialog_count = 0; // total zdialogs (new - free) int zdialog_busy = 0; // open zdialogs (run - destroy) } using namespace zfuncs; /******************************************************************************** system-level utility functions *********************************************************************************/ /******************************************************************************** These are wrappers for malloc() and free() with extra diagnostics. zmalloc() crashes with a message to standard output if the allocation fails, hence the caller need not check. zmalloc() allocates extra memory for sentinels placed before and after the returned memory space, and zfree() checks the sentinels for validity and crashes with a message if they don't match. Benchmark zmalloc + zfree: 1 million random 50-300K bytes, 7.4 GB total: 2.6 secs. (3 GHz Core i5) *********************************************************************************/ namespace zmalloc_names { int xcc1 = 12; // int cc, "sen1", cc bytes, "sen2" int xcc2 = 20; // 20 more bytes 6.8 int64 zmalloc_total = 0; uint allocated = 0, fwarn = 0; } // allocate memory and set sentinels before and after user data void * zmalloc(uint cc) { using namespace zmalloc_names; double mfree, mcache; if (cc < 1) zappcrash("zmalloc() cc: %d",cc); // 6.5 cc = (cc + 16) & 0xFFFFFFF0; // cc is 16 multiple 6.8 void *maddr = malloc(cc + xcc1 + xcc2); // 0 allocate memory + extra 6.5 uint *pcc = (uint *) maddr; // 0 caller byte count char *psen1 = (char *) maddr + 4; // 4 sentinel "sen1" char *puser = (char *) maddr + 8; // 8 user data, cc bytes char *psen2 = (char *) puser + cc; // cc+8 sentinel "sen2" if (! maddr) { printz("OUT OF MEMORY \n"); exit(12); } allocated += cc; // check memory each 1 MB allocated 6.5 if (allocated > 1000000) { allocated = 0; parseprocfile("/proc/meminfo","MemFree:",&mfree,"Cached:",&mcache,0); if (((mfree + mcache) * 0.001 < 500) && ! fwarn) { // warn if < 500 MB fwarn = 1; zmessageACK(0,ZTX("LOW MEMORY - performance may be poor")); // xmessage() fails, why?? } } *pcc = cc; strncpy(psen1,"sen1",4); // set sentinel 1 memset(puser,0,cc); // clear user space to zeros strncpy(psen2,"sen2",4); // set sentinel 2 memset(psen2+4,0,xcc2); // clear extra space beyond zmalloc_total += cc; return puser; } // free memory and test for buffer overflow void zfree(void *puser) // 6.5 { using namespace zmalloc_names; if (! puser) zappcrash("zfree: null address"); void *maddr = (char *) puser - 8; uint *pcc = (uint *) maddr; uint cc = *pcc; char *psen1 = (char *) maddr + 4; char *psen2 = (char *) puser + cc; if (strncmp("sen1",psen1,4)) // check sentinels zappcrash("zfree invalid address: %p",puser); if (strncmp("sen2",psen2,4)) zappcrash("zfree overflow1: %p",puser); for (int ii = 0; ii < xcc2; ii++) // check for clobber after sen2 if (*(psen2+4+ii)) zappcrash("zfree overflow2: %p",puser); memset(maddr,0,cc+xcc1); // clobber all, trap "use after free" zmalloc_total -= cc; free(maddr); // free memory return; } // report total memory allocated void zmalloc_report() { using namespace zmalloc_names; printz("\n zmalloc total memory: %u \n",zmalloc_total); return; } // replacement for strdup() char *zstrdup(cchar *string, int addcc) { if (! string) zappcrash("zstrdup() null arg"); zfuncs::Nstrdup++; char *pp = (char *) zmalloc(strlen(string) + 1 + addcc); strcpy(pp,string); return pp; } /********************************************************************************/ // printf() and flush every output immediately even if stdout is a file void printz(cchar *format, ...) { va_list arglist; va_start(arglist,format); vprintf(format,arglist); va_end(arglist); fflush(stdout); return; } // output a message to a popup window // works like printf() // separate shell process, no GTK // returns immediately // popup message disappears after 'secs' seconds if secs > 0 // popup message disappears if user presses the OK button void zpopup_message(int secs, cchar *format, ... ) // 6.0 { va_list arglist; char message[400], command[500], timeout[20]; va_start(arglist,format); // format user message vsnprintf(message,400,format,arglist); va_end(arglist); printz("popup message: \n %s \n",message); // message to log file stdout strcpy(command,"xmessage -buttons OK:0 -center "); // create popup with message if (secs > 0) { // 6.0 snprintf(timeout,20,"-timeout %d ",secs); strcat(command,timeout); } strcat(command,message); strcat(command," &"); // return immediately zsystem(command); return; } /********************************************************************************/ // produce a backtrace dump to stdout void zbacktrace() { int nstack = 100; void *stacklist[100]; nstack = backtrace(stacklist,nstack); // get backtrace data if (nstack > 100) nstack = 100; backtrace_symbols_fd(stacklist,nstack,STDOUT_FILENO); // backtrace records to STDOUT return; } /********************************************************************************/ // Write an error message and backtrace dump to a file and to a popup window. // Error message works like printf(). // Depends on library program addr2line(). void zappcrash(cchar *format, ... ) // rev. 6.0 { static int crash = 0; struct utsname unbuff; va_list arglist; FILE *fid1, *fid2, *fid3; int fd, ii, err, cc, nstack = 100; int Flinenos = 1; void *stacklist[100]; char OS1[60] = "", OS2[60] = "", OS3[60] = ""; char message[300], progexe[300]; char buff1[300], buff2[300], hexaddr[20]; char *arch, *pp1, *pp2, dlim, *pfunc; if (crash++) return; // re-entry or multiple threads crash va_start(arglist,format); vsnprintf(message,300,format,arglist); va_end(arglist); uname(&unbuff); // get cpu arch. 32/64 bit arch = unbuff.machine; fid1 = popen("lsb_release -d","r"); // get Linux flavor and release if (fid1) { ii = fscanf(fid1,"%s %s %s",OS1,OS2,OS3); pclose(fid1); } printz("\n*** zappcrash: %s %s %s %s %s \n",arch,OS2,OS3,zappname,message); nstack = backtrace(stacklist,nstack); // get backtrace data if (nstack <= 0) { printz("*** zappcrash backtrace() failure \n"); exit(1); } if (nstack > 100) nstack = 100; fid1 = fopen("zbacktrace","w"); // open backtrace data output file if (! fid1) { printz("*** zappcrash fopen() failure \n"); exit(1); } fd = fileno(fid1); backtrace_symbols_fd(stacklist,nstack,fd); // write backtrace data fclose(fid1); // (use of malloc() is avoided) fid1 = fopen("zbacktrace","r"); // open backtrace data file if (! fid1) { printz("*** zappcrash fopen() failure \n"); exit(1); } fid2 = fopen("zappcrash","w"); // open zappcrash output file if (! fid2) { printz("*** zappcrash fopen() failure \n"); exit(1); } fprintf(fid2,"\n*** zappcrash: %s %s %s %s %s \n",arch,OS2,OS3,zappname,message); fprintf(fid2,"*** please send to kornelix@posteo.de *** \n"); cc = readlink("/proc/self/exe",progexe,300); // get own program path if (cc > 0) progexe[cc] = 0; // readlink() quirk else { fprintf(fid2,"progexe not available \n"); Flinenos = 0; } err = zsystem("which addr2line >> /dev/null"); // check if addr2line() available if (err) { fprintf(fid2,"addr2line not available \n"); Flinenos = 0; } for (ii = 0; ii < nstack; ii++) // loop backtrace records { pp1 = pp2 = 0; fgets_trim(buff1,300,fid1); // read backtrace line if (! Flinenos) goto output; pfunc = 0; pp1 = strstr(buff1,"+0x"); // new format (+0x12345...) 6.5 if (pp1) pp2 = strchr(pp1,')'); else { pp1 = strstr(buff1,"[0x"); // old format [0x12345...] if (pp1) pp2 = strchr(pp1,']'); } if (! pp1 || ! pp2) goto output; // cannot parse dlim = *pp2; *pp2 = 0; strncpy0(hexaddr,pp1+1,20); *pp2 = dlim; snprintf(buff2,300,"addr2line -i -e %s %s",progexe,hexaddr); // convert to source program fid3 = popen(buff2,"r"); // and line number if (! fid3) goto output; pfunc = fgets(buff2,300,fid3); pclose(fid3); if (! pfunc) goto output; cc = strlen(pfunc); if (cc < 10) goto output; if (pfunc[cc-1] < ' ') pfunc[cc-1] = 0; // remove tailing \n if present strncatv(buff1,300,"\n--- ",pfunc,null); output: fprintf(fid2,"%s \n",buff1); // output } fclose(fid1); fclose(fid2); err = zsystem("xdg-open zappcrash"); if (err) printz("*** xdg-open failure \n"); else remove("zbacktrace"); // if zappcrash.txt OK, delete 6.7 exit(1); } /********************************************************************************/ // application initialization function to catch some bad news signals // the signal handler calls zappcrash() to output a backtrace dump and exit void catch_signals() { void sighandler(int signal); struct sigaction sigact; sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGTERM,&sigact,0); sigaction(SIGSEGV,&sigact,0); sigaction(SIGILL,&sigact,0); sigaction(SIGFPE,&sigact,0); sigaction(SIGBUS,&sigact,0); sigaction(SIGABRT,&sigact,0); // heap or stack corruption return; } // catch fatal signals and produce backtrace dumps on-screen void sighandler(int signal) { const char *signame = "unknown"; if (signal == SIGTERM) { printz("process terminated by system \n"); exit(1); } if (signal == SIGSEGV) signame = "segment fault"; if (signal == SIGILL) signame = "illegal operation"; if (signal == SIGFPE) signame = "arithmetic exception"; if (signal == SIGBUS) signame = "bus error"; if (signal == SIGABRT) signame = "bug detected"; zappcrash("fatal signal: %s",signame); return; } /********************************************************************************/ // Implement the TRACE macro. // Trace program execution by function and source code line number. // tracedump() dumps last 50 uses of TRACE macro, latest first. namespace tracenames { char filebuff[50][100]; // last 50 TRACE calls char funcbuff[50][60]; int linebuff[50]; void *addrbuff[50]; int ii, ftf = 1; }; // Args are source file, source function name, source code line number, // caller address. These all come from the GCC compiler and TRACE macro. void trace(cchar *file, cchar *func, int line, void *addr) { using namespace tracenames; if (ftf) { ftf = 0; for (ii = 0; ii < 50; ii++) { filebuff[ii][99] = 0; funcbuff[ii][39] = 0; linebuff[ii] = 0; addrbuff[ii] = 0; } ii = 0; } if (line == linebuff[ii] && strmatch(func,funcbuff[ii])) return; // same as last call, don't duplicate if (++ii > 49) ii = 0; // add data to list strncpy(&filebuff[ii][0],file,99); strncpy(&funcbuff[ii][0],func,39); linebuff[ii] = line; addrbuff[ii] = addr; return; } // dump trace records to STDOUT void tracedump() { using namespace tracenames; FILE *fid; int kk; printz(" *** tracedump *** \n"); kk = ii; while (linebuff[kk]) { printz("TRACE %s %s %d %p \n",&filebuff[kk][0], &funcbuff[kk][0],linebuff[kk],addrbuff[kk]); if (--kk == ii) break; } fid = fopen("tracedump","w"); if (! fid) { perror("tracedump fopen() failure \n"); return; } fprintf(fid, " *** tracedump *** \n"); kk = ii; while (linebuff[kk]) { fprintf(fid, "TRACE %s %s %d %p \n",&filebuff[kk][0], &funcbuff[kk][0],linebuff[kk],addrbuff[kk]); if (--kk == ii) break; } fclose(fid); return; } /********************************************************************************/ // Execute a command and parameters as root user (after sudo password). // If argc and argv are the caller's original parameters, // the current program is restarted as root user. void beroot(int argc, char *argv[]) { int cc1, cc2, ii, err; char command[1000]; if (getuid() == 0) return; // already root strcpy(command,"gksudo "); cc1 = strlen(command); for (ii = 0; ii < argc; ii++) // construct command line { cc2 = strlen(argv[ii]); // gksudo program arg1 arg2 ... & if (cc1 + cc2 > 994) { printz("beroot() command line > 1000 bytes \n"); exit(1); } strcpy(command+cc1,argv[ii]); command[cc1+cc2] = ' '; cc1 = cc1 + cc2 + 1; } strcpy(command+cc1," &"); printz("beroot() command: %s \n",command); err = system(command); exit(err); return; } /********************************************************************************/ // get time in real seconds (since 2000.01.01 00:00:00) // (microsecond resolution until at least 2030) double get_seconds() { timeval time1; double time2; gettimeofday(&time1,0); time2 = time1.tv_sec + 0.000001 * time1.tv_usec - 946684800.0; return time2; } /********************************************************************************/ // start a timer or get elapsed time with millisecond resolution. void start_timer(double &time0) { timeval timev; gettimeofday(&timev,0); time0 = timev.tv_sec + 0.000001 * timev.tv_usec; return; } double get_timer(double &time0) { timeval timev; double time; gettimeofday(&timev,0); time = timev.tv_sec + 0.000001 * timev.tv_usec; return time - time0; } /********************************************************************************/ // start a process CPU timer or get elapsed process CPU time // returns seconds with millisecond resolution void start_CPUtimer(double &time0) { time0 = CPUtime(); return; } double get_CPUtimer(double &time0) { return CPUtime() - time0; } /********************************************************************************/ // get elapsed CPU time used by current process // returns seconds with millisecond resolution double CPUtime() { clock_t ctime = clock(); double dtime = ctime / 1000000.0; return dtime; } /********************************************************************************/ // Get elapsed CPU time used by current process, including all threads. // Returns seconds with millisecond resolution. double CPUtime2() { struct rusage usage; double utime, stime; int err; err = getrusage(RUSAGE_SELF,&usage); if (err) return 0.0; utime = usage.ru_utime.tv_sec + 0.000001 * usage.ru_utime.tv_usec; stime = usage.ru_stime.tv_sec + 0.000001 * usage.ru_stime.tv_usec; return utime + stime; } /********************************************************************************/ // get elapsed process time for my process, including threads and child processes. double jobtime() { double jiffy = 1.0 / sysconf(_SC_CLK_TCK); // "jiffy" time slice = 1.0 / HZ char buff[200]; double cpu1, cpu2, cpu3, cpu4; FILE *fid; char *pp; fid = fopen("/proc/self/stat","r"); if (! fid) return 0; pp = fgets(buff,200,fid); fclose(fid); if (! pp) return 0; parseprocrec(pp,14,&cpu1,15,&cpu2,16,&cpu3,17,&cpu4,null); return (cpu1 + cpu2 + cpu3 + cpu4) * jiffy; } /********************************************************************************/ // convert a time_t date/time (e.g. st_mtime from stat() call) // into a compact date/time format "yyyymmddhhmmss" void compact_time(const time_t DT, char *compactDT) { struct tm *fdt; int year, mon, day, hour, min, sec; fdt = localtime(&DT); year = fdt->tm_year + 1900; mon = fdt->tm_mon + 1; day = fdt->tm_mday; hour = fdt->tm_hour; min = fdt->tm_min; sec = fdt->tm_sec; compactDT[0] = year / 1000 + '0'; compactDT[1] = (year % 1000) / 100 + '0'; compactDT[2] = (year % 100) / 10 + '0'; compactDT[3] = year % 10 + '0'; compactDT[4] = mon / 10 + '0'; compactDT[5] = mon % 10 + '0'; compactDT[6] = day / 10 + '0'; compactDT[7] = day % 10 + '0'; compactDT[8] = hour / 10 + '0'; compactDT[9] = hour % 10 + '0'; compactDT[10] = min / 10 + '0'; compactDT[11] = min % 10 + '0'; compactDT[12] = sec / 10 + '0'; compactDT[13] = sec % 10 + '0'; compactDT[14] = 0; return; } /********************************************************************************/ // convert a time_t date/time (e.g. st_mtime from stat() call) // into a pretty date/time format "yyyy-mm-dd hh:mm:ss" void pretty_datetime(const time_t DT, char *prettyDT) // 6.2 { struct tm *fdt; int year, mon, day, hour, min, sec; fdt = localtime(&DT); year = fdt->tm_year + 1900; mon = fdt->tm_mon + 1; day = fdt->tm_mday; hour = fdt->tm_hour; min = fdt->tm_min; sec = fdt->tm_sec; prettyDT[0] = year / 1000 + '0'; prettyDT[1] = (year % 1000) / 100 + '0'; prettyDT[2] = (year % 100) / 10 + '0'; prettyDT[3] = year % 10 + '0'; prettyDT[4] = '-'; prettyDT[5] = mon / 10 + '0'; prettyDT[6] = mon % 10 + '0'; prettyDT[7] = '-'; prettyDT[8] = day / 10 + '0'; prettyDT[9] = day % 10 + '0'; prettyDT[10] = ' '; prettyDT[11] = hour / 10 + '0'; prettyDT[12] = hour % 10 + '0'; prettyDT[13] = ':'; prettyDT[14] = min / 10 + '0'; prettyDT[15] = min % 10 + '0'; prettyDT[16] = ':'; prettyDT[17] = sec / 10 + '0'; prettyDT[18] = sec % 10 + '0'; prettyDT[19] = 0; return; } /********************************************************************************/ // Read and parse /proc file with records formatted "parmname xxxxxxx" // Find all requested parameters and return their numeric values int parseprocfile(cchar *pfile, cchar *pname, double *value, ...) // EOL = 0 { FILE *fid; va_list arglist; char buff[1000]; const char *pnames[20]; double *values[20]; int ii, fcc, wanted, found; pnames[0] = pname; // 1st parameter values[0] = value; *value = 0; va_start(arglist,value); for (ii = 1; ii < 20; ii++) // get all parameters { pnames[ii] = va_arg(arglist,char *); if (! pnames[ii]) break; values[ii] = va_arg(arglist,double *); *values[ii] = 0; // initialize to zero } va_end(arglist); if (ii == 20) zappcrash("parseProcFile, too many fields"); wanted = ii; found = 0; fid = fopen(pfile,"r"); // open /proc/xxx file if (! fid) return 0; while ((fgets(buff,999,fid))) // read record, "parmname nnnnn" { for (ii = 0; ii < wanted; ii++) { // look for my fields fcc = strlen(pnames[ii]); if (strmatchN(buff,pnames[ii],fcc)) { *values[ii] = atof(buff+fcc); // return value found++; break; } } if (found == wanted) break; // stop when all found } fclose(fid); return found; } // Parse /proc record of the type "xxx xxxxx xxxxx xxxxxxxx xxx" // Return numeric values for requested fields (starting with 1) int parseprocrec(char *prec, int field, double *value, ...) // EOL = 0 { va_list arglist; int xfield = 1, found = 0; va_start(arglist,value); while (*prec == ' ') prec++; // skip leading blanks while (field > 0) { while (xfield < field) // skip to next wanted field { prec = strchr(prec,' '); // find next blank if (! prec) break; while (*prec == ' ') prec++; // skip multiple blanks xfield++; } if (! prec) break; *value = atof(prec); // convert, return double found++; field = va_arg(arglist,int); // next field number value = va_arg(arglist,double *); // next output double * } while (field > 0) { *value = 0; // zero values not found field = va_arg(arglist,int); value = va_arg(arglist,double *); } va_end(arglist); return found; } /********************************************************************************/ // get current processor core temperature // depends on file "temp1_input" somewhere under /sys/devices/* int coretemp() { FILE *fid; static int ftf = 1; static char *Tfile = 0; static char buff1[200], buff2[200], *ptemp; int temp; if (ftf) { // find file "temp1_input" ftf = 0; fid = popen("find /sys/devices/platform -name temp1_input","r"); if (! fid) fid = popen("find /sys/devices -name temp1_input","r"); // 6.6 if (! fid) return 0; Tfile = fgets(buff1,200,fid); pclose(fid); printz("coretemp file: %s \n",Tfile); } if (! Tfile) return 0; snprintf(buff2,200,"cat %s",Tfile); fid = popen(buff2,"r"); if (! fid) return 0; ptemp = fgets(buff2,200,fid); pclose(fid); if (! ptemp) return 0; temp = atoi(ptemp); while (temp > 100) temp = temp / 10; // may be deg. C x 10 or x 100 ... if (temp < 10) return 0; return temp; } /********************************************************************************/ // get current temperature for given disk, e.g. "/dev/sda" // depends on "smartctl" command from package smartmontools int disktemp(char *disk) { int id, temp; char *pp, *pp2; char buff[200], command[100]; FILE *ffid; temp = 0; pp2 = 0; snprintf(command,100,"smartctl -A %s",disk); ffid = popen(command,"r"); if (! ffid) return 0; while (true) { pp = fgets(buff,200,ffid); // revised for smartctl report if (! pp) break; // format changes if (strmatchN(pp,"ID#",3)) pp2 = strstr(pp,"RAW_VALUE"); id = atoi(pp); if (id != 190 && id != 194) continue; // Airflow Temp. or Temp. if (! pp2) continue; temp = atoi(pp2); if (temp < 10 || temp > 99) temp = 0; break; } pclose(ffid); return temp; } /********************************************************************************/ // sleep for specified time in seconds (double) // signals can cause early return void zsleep(double dsecs) { unsigned isecs, nsecs; timespec tsecs; if (dsecs == 0.0) return; isecs = unsigned(dsecs); nsecs = unsigned(1000000000.0 * (dsecs - isecs)); tsecs.tv_sec = isecs; tsecs.tv_nsec = nsecs; nanosleep(&tsecs,null); return; } /********************************************************************************/ // Lock or unlock a multi-process multi-thread resource. // Only one process/thread may possess a given lock. // A reboot or process exit or crash releases the lock. // fd = global_lock(lockfile) returns fd > 0 if success, -1 otherwise. int global_lock(cchar *lockfile) { int err, fd; fd = open(lockfile,O_RDWR|O_CREAT,0666); // open or create the lock file if (fd < 0) { printz("*** global_lock(), %s \n",strerror(errno)); return -1; } err = flock(fd,LOCK_EX|LOCK_NB); // request exclusive non-blocking lock if (err) { close(fd); return -1; } return fd + 1; // return value is >= 1 } int global_unlock(int fd, cchar *lockfile) { int err = close(fd-1); remove(lockfile); if (err < 0) return -1; else return 1; } /********************************************************************************/ // lock or unlock a resource // does not spin or wait, usable within or across threads // (mutex_lock() cannot be used within one thread, e.g. GTK main loop) // return 0 if already locked, otherwise lock and return 1 mutex_t resource_lock_lock = PTHREAD_MUTEX_INITIALIZER; int resource_lock(int &resource) // 6.0 { mutex_lock(&resource_lock_lock); if (resource) { mutex_unlock(&resource_lock_lock); return 0; // failed, locked } resource = 1; mutex_unlock(&resource_lock_lock); return 1; // locked OK } // unlock a locked resource void resource_unlock(int &resource) { mutex_lock(&resource_lock_lock); if (resource != 1) zappcrash("resource not locked"); // bug, not locked resource = 0; // unlock mutex_unlock(&resource_lock_lock); return; } /********************************************************************************/ // Safely access and update parameters from multiple threads. // A mutex lock is used to insure one thread at a time has access to the parameter. // Many parameters can be used but there is only one mutex lock. mutex_t zget_lock = PTHREAD_MUTEX_INITIALIZER; int zget_locked(int ¶m) // lock and return parameter { mutex_lock(&zget_lock); return param; } void zput_locked(int ¶m, int value) // set and unlock parameter { param = value; mutex_unlock(&zget_lock); return; } int zadd_locked(int ¶m, int incr) // lock, increment, unlock, return { int retval; mutex_lock(&zget_lock); retval = param + incr; param = retval; mutex_unlock(&zget_lock); return retval; } /********************************************************************************/ // Start a detached thread using a simplified protocol. // Will not make a zombie out of the calling thread if it exits // without checking the status of the created thread. // Thread should exit with pthread_exit(0); void start_detached_thread(void * threadfunc(void *), void * arg) { pthread_attr_t pthattr; pthread_t pthtid; int ptherr = 1; pthread_attr_init(&pthattr); pthread_attr_setdetachstate(&pthattr,PTHREAD_CREATE_DETACHED); while (ptherr) ptherr = pthread_create(&pthtid,&pthattr,threadfunc,arg); return; } /********************************************************************************/ // Synchronize execution of multiple threads. // Simultaneously resume NT calling threads. // from main(): synch_threads(NT) /* setup to synch NT threads */ // from each thread: synch_threads() /* suspend, resume simultaneously */ // // Each calling thread will suspend execution until all threads have suspended, // then they will all resume execution at the same time. If NT is greater than // the number of calling threads, the threads will never resume. void synch_threads(int NT) { static pthread_barrier_t barrier; static int bflag = 0; if (NT) { // main(), initialize if (bflag) pthread_barrier_destroy(&barrier); pthread_barrier_init(&barrier,null,NT); bflag = 1; return; } pthread_barrier_wait(&barrier); // thread(), wait for NT threads return; // unblock } /********************************************************************************/ // same as system() but status of child process is returned. int zsystem(cchar *command) // 6.7 { int err; err = system(command); if (! err) return 0; err = WEXITSTATUS(err); return err; } /********************************************************************************/ // Format and run a shell command. // Print a status message to stdout if there is an error. // returns 0 if OK, +N if error int shell_quiet(cchar *command, ...) { char *cbuff; va_list arglist; int cc, err; cc = strlen(command) + 1000; cbuff = (char *) zmalloc(cc+1); va_start(arglist,command); // format command vsnprintf(cbuff,cc,command,arglist); va_end(arglist); err = zsystem(cbuff); if (! err) { zfree(cbuff); return 0; } if (strmatchN(command,"diff",4)) { zfree(cbuff); return err; } // no diagnostic for these if (strmatchN(command,"cmp",3)) { zfree(cbuff); return err; } if (strmatchN(command,"which",5)) { zfree(cbuff); return err; } if (cc > 200) cbuff[200] = 0; if (err == 127) printz("%s *** %s \n",cbuff,"command not found"); // special case 6.3 else printz("%s *** %s \n",cbuff,strerror(err)); // log error message zfree(cbuff); return err; } // Format and run a shell command. // Print a status message to stdout if there is an error. // Also pop-up an error message window if error. // Thread safe: GTK is not used. // returns 0 if OK, +N if error int shell_ack(cchar *command, ...) { char *cbuff; va_list arglist; int cc, err; cc = strlen(command) + 1000; cbuff = (char *) zmalloc(cc+1); va_start(arglist,command); // format command vsnprintf(cbuff,cc,command,arglist); va_end(arglist); printz("shell: %s \n",cbuff); err = zsystem(cbuff); if (! err) { zfree(cbuff); return 0; } if (strmatchN(command,"diff",4)) { zfree(cbuff); return err; } // no diagnostic for these if (strmatchN(command,"cmp",3)) { zfree(cbuff); return err; } if (strmatchN(command,"which",5)) { zfree(cbuff); return err; } if (cc > 200) cbuff[200] = 0; if (err == 127) zmessageACK(0,"%s \n %s",cbuff,"command not found"); // special case 6.3 else zmessageACK(0,"%s \n %s",cbuff,strerror(err)); // popup error message zfree(cbuff); return err; } /********************************************************************************/ // Start a shell command from a new thread and return immediately. // The thread waits for the shell command and gets its status. // If there is an error, it is logged to STDOUT. // The calling process can get the status asynchronously. // The command format works like printf(). // // handle = shell_asynch(command, ...) // Start the command and return a reference handle. // // err = shell_asynch_status(handle) // Return the command status for the given handle: // -1 = busy, 0 = complete OK, +N = error (use wstrerror(err)). // // The status MUST be queried for allocated space to be freed. namespace shell_asynch_names { char *command[10]; int status[10]; mutex_t mlock = PTHREAD_MUTEX_INITIALIZER; } int shell_asynch(cchar *Fcommand, ...) { using namespace shell_asynch_names; void * shell_asynch_thread(void *); va_list arglist; static int ii; mutex_lock(&mlock); // block other callers for (ii = 0; ii < 10; ii++) if (command[ii] == 0) break; if (ii == 10) zappcrash("shell_asynch > 10 calls"); command[ii] = (char *) zmalloc(2000); // allocate memory va_start(arglist,Fcommand); // format command vsnprintf(command[ii],2000,Fcommand,arglist); va_end(arglist); start_detached_thread(shell_asynch_thread,&ii); // pass command to thread status[ii] = -1; // status = busy return ii; // return handle } void * shell_asynch_thread(void *arg) // thread function { using namespace shell_asynch_names; int err; int ii = *((int *) arg); // capture handle mutex_unlock(&mlock); // unblock other callers err = zsystem(command[ii]); // start command, wait until done if (! err) { status[ii] = 0; return 0; } if (err == 127) printz("%s \n *** %s \n",command[ii],"command not found"); // special case else printz("%s \n *** %s \n",command[ii],strerror(err)); // log error message status[ii] = err; // set status for caller return 0; } int shell_asynch_status(int handle) // get command status { using namespace shell_asynch_names; int ii = handle; if (status[ii] == -1) return -1; // return busy status zfree(command[ii]); // free memory command[ii] = 0; return status[ii]; // return completion status } /******************************************************************************** Run a shell command and get its outputs one record at a time. The outputs are returned one record at a time, until a NULL is returned, indicating the command has finished and has exited. The new line character is removed from the returned output records. Use contx = 0 to start a new command. Do not change the returned value. Up to 9 commands can run in parallel, with contx values 1-9. To get the command exit status: status = command_status(contx). If the command is still busy, -1 is returned. To kill a command before output is complete: command_kill(contx); ***/ FILE * CO_contx[10] = { 0,0,0,0,0,0,0,0,0,0 }; int CO_status[10]; char * command_output(int &contx, cchar *command, ...) // simplify, allow parallel usage { FILE *fid; va_list arglist; char buff[10000], *prec; if (contx == 0) // start new command { for (contx = 1; contx < 10; contx++) if (CO_contx[contx] == 0) break; if (contx == 10) { printz("*** command_output(), parallel usage > 9 \n"); return 0; } va_start(arglist,command); // format command vsnprintf(buff,9999,command,arglist); va_end(arglist); fid = popen(buff,"r"); // execute command, output to FID if (fid == 0) { CO_status[contx] = errno; // failed to start printz("*** command_output: %s\n %s\n",buff,strerror(errno)); return 0; } CO_contx[contx] = fid + 1000; CO_status[contx] = -1; // mark context busy } fid = CO_contx[contx] - 1000; prec = fgets_trim(buff,9999,fid,1); // next output, less trailing \n if (prec) return zstrdup(prec); CO_status[contx] = pclose(fid); // EOF, set status CO_contx[contx] = 0; // mark context free return 0; } int command_status(int contx) // get command exit status { int err = CO_status[contx]; return WEXITSTATUS(err); // special BS for subprocess } int command_kill(int contx) // kill output before completion { FILE *fid; if (! CO_contx[contx]) return 0; // context already closed fid = CO_contx[contx] - 1000; CO_status[contx] = pclose(fid); // close context and set status CO_contx[contx] = 0; // mark context free return 0; } /********************************************************************************/ // Signal a running subprocess by name (name of executable or shell command). // Signal is "pause", "resume" or "kill". If process is paused, kill may not work, // so issue resume first if process is paused. int signalProc(cchar *pname, cchar *signal) { pid_t pid; FILE *fid; char buff[100], *pp; int err, nsignal = 0; snprintf(buff,100,"ps -C %s h o pid",pname); fid = popen(buff,"r"); // popen() instead of system() if (! fid) return 2; pp = fgets(buff,100,fid); pclose(fid); if (! pp) return 4; pid = atoi(buff); if (! pid) return 5; if (strmatch(signal,"pause")) nsignal = SIGSTOP; if (strmatch(signal,"resume")) nsignal = SIGCONT; if (strmatch(signal,"kill")) nsignal = SIGKILL; err = kill(pid,nsignal); return err; } /********************************************************************************/ // run a command or program as root user // sucomm: root user access command, "su" or "sudo" // command: shell command or filespec of the program to start // returns 0 if successfully started, else returns an error code int runroot(cchar *sucomm, cchar *command) { char xtcommand[500]; int err; if (strmatch(sucomm,"sudo")) { snprintf(xtcommand,499,"xterm -geometry 40x3 -e sudo -S %s",command); err = zsystem(xtcommand); return err; } if (strmatch(sucomm,"su")) { snprintf(xtcommand,499,"xterm -geometry 40x3 -e su -c %s",command); err = zsystem(xtcommand); return err; } return -1; } /********************************************************************************/ // fgets() with additional feature: trailing \n \r are removed. // optional bf flag: true if trailing blanks are to be removed. // trailing null character is assured. char * fgets_trim(char *buff, int maxcc, FILE *fid, int bf) { int cc; char *pp; pp = fgets(buff,maxcc,fid); if (! pp) return pp; cc = strlen(buff); if (bf) while (cc && buff[cc-1] > 0 && buff[cc-1] <= ' ') --cc; else while (cc && buff[cc-1] > 0 && buff[cc-1] < ' ') --cc; buff[cc] = 0; return pp; } /********************************************************************************/ // Return 1 if both filespecs have the same directory, else return 0. // Both directories must be specified, at least one with ending '/' // (true if a file name is present) int samedirk(cchar *file1, cchar *file2) { cchar *p1, *p2; int cc1, cc2, cc; p1 = strrchr(file1,'/'); // /dir1/dir2 p2 = strrchr(file2,'/'); // /dir1/dir2/file cc1 = cc2 = 0; if (p1) cc1 = p1 - file1; // /dir1/dir2/file if (p2) cc2 = p2 - file2; // | | if (cc2 > cc1) cc = cc2; // 0 cc else cc = cc1; if (cc == 0) return 0; if (strmatchN(file1,file2,cc)) return 1; return 0; } /******************************************************************************** Parse a pathname (filespec) and return its components. Returned strings are allocated in static memory (no zfree needed). Missing components are returned as null pointers. input ppath outputs /name1/name2/ directory /name1/name2/ with no file /name1/name2 directory /name1/name2/ if name2 a directory, otherwise directory /name1/ and file name2 /name1/name2.xxx if .xxx < 8 chars, returns file name2 and ext .xxx, otherwise returns file name2.xxx and no ext returns 0 if no error, else 1 *********************************************************************************/ int parsefile(cchar *ppath, char **pdirk, char **pfile, char **pext) { STATB statb; static char dirk[1000], file[200], ext[8]; char *pp; int err, cc1, cc2; *pdirk = *pfile = *pext = null; cc1 = strlen(ppath); if (cc1 > 999) return 1; // ppath too long strcpy(dirk,ppath); *pdirk = dirk; err = stat(dirk,&statb); // have directory only if (! err && S_ISDIR(statb.st_mode)) return 0; pp = (char *) strrchr(dirk,'/'); if (! pp) return 1; // illegal pp++; cc2 = pp - dirk; if (cc2 < 2 || cc2 == cc1) return 0; // have /xxxx or /xxxx/ if (strlen(pp) > 199) return 1; // filename too long strcpy(file,pp); // file part *pfile = file; *pp = 0; // remove from dirk part pp = (char *) strrchr(file,'.'); if (! pp || strlen(pp) > 7) return 0; // file part, no .ext strcpy(ext,pp); // .ext part *pext = ext; *pp = 0; // remove from file part return 0; } /********************************************************************************/ // Move a source file to a destination file and delete the source file. // Equivalent to rename(), but the two files MAY be on different file systems. // Pathnames must be absolute (start with '/'). // Returns 0 if OK, +N if not. int renamez(cchar *file1, cchar *file2) { char *pp1, *pp2; int err, Frename = 0; if (*file1 != '/' || *file2 != '/') return 1; // not absolute pathnames pp1 = strchr((char *) file1+1,'/'); pp2 = strchr((char *) file2+1,'/'); if (! pp1 || ! pp2) return 2; *pp1 = *pp2 = 0; if (strmatch(file1,file2)) Frename = 1; *pp1 = *pp2 = '/'; if (Frename) { // same top directory err = rename(file1,file2); if (err) return errno; else return 0; } err = shell_quiet("mv -f %s %s",file1,file2); // not return err; } /********************************************************************************/ // Check if a directory exists. If not, ask user if it should be created. // Returns 0 if OK or +N if error or user refused to create. // The user is notified of failure, no other message needed. int check_create_dir(char *path) { int err, yn; STATB statbuf; err = stat(path,&statbuf); // check status if (! err) { if (S_ISDIR(statbuf.st_mode)) return 0; // exists, directory, OK else { zmessageACK(0,"%s \n %s",path,strerror(ENOTDIR)); // exists, not a directory return ENOTDIR; } } if (errno != ENOENT) { zmessageACK(0,"%s \n %s",path,strerror(errno)); // error other than missing return errno; } yn = zmessageYN(0,ZTX("create directory? \n %s"),path); // ask to create if (! yn) return ENOENT; err = shell_ack("mkdir -p -m 0750 %s",path); // create. rwx, rx, no access if (! err) return 0; zmessageACK(0,"%s \n %s",path,strerror(errno)); // failed to create return errno; } /********************************************************************************/ // Copy file to file or file to an existing directory. // Missing output directories will be created. // If inut file is a symlink, copy the symlink, not the file. int copyFile(cchar *sfile, cchar *dfile) // 6.7 { #define BIOCC (1024*1024) // read/write block size int fid1, fid2, err, cc, dlevs; char *pp1, *pp2, buff[BIOCC]; STATB statB; static char *dfile2 = 0; if (dfile2) zfree(dfile2); // stop memory leak dfile2 = 0; err = stat(dfile,&statB); if (! err && S_ISDIR(statB.st_mode)) { // output is an existing directory pp1 = (char *) strrchr(sfile,'/'); // get source file base name if (pp1) pp1++; else pp1 = (char *) sfile; cc = strlen(pp1); // construct output file path: dfile2 = zstrdup(dfile,cc+4); // output directory + base name pp2 = dfile2 + strlen(dfile2); if (pp2[-1] != '/') *pp2++ = '/'; // insure '/' after directory strcpy(pp2,pp1); dfile = dfile2; // output file full path } else { // output is a file path pp2 = (char *) dfile; dlevs = 0; while (true) { pp2 = strchr(pp2+1,'/'); // create missing directory levels if (! pp2) break; // (check and create from top down) *pp2 = 0; err = stat(dfile,&statB); if (err) { err = mkdir(dfile,0731); if (err) { printz("%s \n %s \n",strerror(errno),dfile); return errno; } dlevs++; } *pp2 = '/'; } } err = lstat(sfile,&statB); // get input file attributes if (err) { printz("%s \n %s \n",strerror(errno),sfile); return errno; } if (S_ISLNK(statB.st_mode)) { // input file is symlink cc = readlink(sfile,buff,XFCC); if (cc < 0 || cc > XFCC-2) return errno; buff[cc] = 0; err = symlink(buff,dfile); // create output symlink if (err) { printz("%s \n %s \n %s \n",strerror(errno),buff,dfile); return errno; } return 0; } if (strmatch(sfile,dfile)) return 0; // source and dest files are same fid1 = open(sfile,O_RDONLY); // open input file if (fid1 == -1) return errno; fid2 = creat(dfile,0700); // open output file if (fid2 == -1) { err = errno; close(fid1); if (err) printz("%s \n %s \n",strerror(err),dfile); return err; } while (true) { cc = read(fid1,buff,BIOCC); // read huge blocks if (cc == 0) break; if (cc == -1) { err = errno; close(fid1); close(fid2); if (err) printz("%s \n %s \n",strerror(err),sfile); return err; } cc = write(fid2,buff,cc); // write blocks if (cc == -1) { err = errno; close(fid1); close(fid2); if (err) printz("%s \n %s \n",strerror(err),dfile); return err; } } close(fid1); // close input file err = close(fid2); // close output file if (err) { printz("%s \n %s \n",strerror(errno),dfile); return errno; } return 0; } /********************************************************************************/ // appimage problem: // command line args are separated by blanks even for strings enclosed in // quotes: "aaaa bbbb" becomes two argv[] elements, "aaaa" and "bbbb" // this makes it impossible to get file path args with embedded spaces // // char * catenate_argvs(int argc, char *argv[], Nth) // combine argv[ii] elements from Nth to last // a single space is inserted between each argv[ii] element // command ... aaaaa bbbbb ccccc produces "aaaaa bbbbb ccccc" char * combine_argvs(int argc, char *argv[], int Nth) // 6.7 { int ii, ccv, outcc = 0; static char output[XFCC]; for (ii = Nth; ii < argc; ii++) { ccv = strlen(argv[ii]); if (outcc + ccv > XFCC - 2) return 0; strcpy(output+outcc,argv[ii]); outcc += ccv; output[outcc] = ' '; outcc++; } outcc--; output[outcc] = 0; return output; } /******************************************************************************** utility to measure CPU time spent in various functions or code blocks cpu_profile_init() initialize at start of test cpu_profile_enter(fnum) at entry to a function cpu_profile_exit(fnum) at exit from a function cpu_profile_report() report CPU time per function Methodology: cpu_profile_init() starts a thread that suspends and runs every 1 millisecond and updates a timer. cpu_profile_enter() and cpu_profile_exit() accumulate the time difference between entry and exit of code being measured. This may be zero because of the long interval between timer updates. Accuracy comes from statistical sampling over many seconds, so that if the time spent in a monitored function is significant, it will be accounted for. The accuracy is better than 1% as long as the measured function or code block consumes a second or more of CPU time during the measurement period. The "fnum" argument (1-99) designates the function or code block being measured. cpu_profile_report() stops the timer thread and reports time consumed per function, using the "fnum" tags in the report. The functions cpu_profile_enter() and cpu_profile_exit() subtract the timer difference and add to a counter per fnum, so the added overhead is insignificant. They are inline functions defined as follows: enter: cpu_profile_timer = cpu_profile_elapsed; exit: cpu_profile_table[fnum] += cpu_profile_elapsed - cpu_profile_timer; *********************************************************************************/ VOL double cpu_profile_table[100]; VOL double cpu_profile_timer; VOL double cpu_profile_elapsed; VOL int cpu_profile_kill = 0; void cpu_profile_init() { void * cpu_profile_timekeeper(void *); for (int ii = 0; ii < 99; ii++) cpu_profile_table[ii] = 0; cpu_profile_elapsed = 0; start_detached_thread(cpu_profile_timekeeper,null); } void cpu_profile_report() { cpu_profile_kill++; printz("elapsed: %.2f \n",cpu_profile_elapsed); for (int ii = 0; ii < 100; ii++) { double dtime = cpu_profile_table[ii]; if (dtime) printz("cpu profile func: %d time: %.2f \n",ii,dtime); } } void * cpu_profile_timekeeper(void *) { timeval time0, time1; gettimeofday(&time0,0); while (true) { gettimeofday(&time1,0); cpu_profile_elapsed = time1.tv_sec - time0.tv_sec + 0.000001 * (time1.tv_usec - time0.tv_usec); zsleep(0.001); if (cpu_profile_kill) break; } cpu_profile_kill = 0; pthread_exit(0); } /********************************************************************************/ // Returns hard page fault rate in faults/second. // First call starts a thread that runs every 2 seconds and keeps a // weighted average of hard fault rate for the last few intervals. namespace pagefaultrate_names { int ftf = 1; int samples = 0; int faultrate = 0; double time1, time2; void * threadfunc(void *); } int pagefaultrate() { using namespace pagefaultrate_names; if (ftf) { ftf = 0; start_detached_thread(threadfunc,0); time1 = get_seconds(); } return faultrate; } void * pagefaultrate_names::threadfunc(void *) { using namespace pagefaultrate_names; FILE *fid; char *pp, buff[200]; double pfs1, pfs2, fps, elaps; while (true) { sleep(2); time2 = get_seconds(); elaps = time2 - time1; time1 = time2; fid = fopen("/proc/self/stat","r"); if (! fid) break; pp = fgets(buff,200,fid); fclose(fid); if (! pp) break; pp = strchr(pp,')'); // closing ')' after (short) filename if (pp) parseprocrec(pp+1,10,&pfs1,11,&pfs2,null); fps = (pfs1 + pfs2) / elaps; faultrate = 0.7 * faultrate + 0.3 * fps; } printz("pagefaultrate() failure \n"); pthread_exit(0); } /******************************************************************************** strField() cchar * strField(cchar *string, cchar *delim, int Nth) Get the Nth field in input string, which contains at least N fields delimited by the character(s) in delim (e.g. blank, comma). Returns a pointer to the found field (actually a pointer to a copy of the found field, with a null terminator appended). If a delimiter is immediately followed by another delimiter, it is considered a field with zero length, and the string "" is returned. Leading blanks in a field are omitted from the returned field. A field with only blanks is returned as a single blank. The last field may be terminated by null or a delimiter. Characters within quotes (") are treated as data within a field, i.e. blanks and delimiters are not processed as such. The quotes are removed from the returned field. If there are less than Nth fields, a null pointer is returned. The last 100 fields are saved and recycled in a circular manner. The caller does not have to free memory. If more memory depth is needed, caller must copy the returned data elsewhere. The output string may be modified if the length is not increased. Fields within the input string must not exceed 2000 characters. Example: input string: ,a,bb, cc, ,dd"ee,ff"ggg, (first and last characters are comma) delimiter: comma Nth returned string 1: (null string) 2: a 3: bb 4: cc 5: (one blank) 6: ddee,ffggg 7: (null pointer >> no more fields) *********************************************************************************/ cchar * strField(cchar *string, cchar *delim, int Nth) { static int ftf = 1, nret = 0; static char *retf[100]; char *pf1, pf2[2000]; // 2000 limit cchar quote = '"'; int ii, nf, fcc = 0; static char blankstring[2], nullstring[1]; if (! string) return 0; // bad call if (Nth < 1) return 0; if (ftf) // overall first call { ftf = 0; for (ii = 0; ii < 100; ii++) retf[ii] = 0; strcpy(blankstring," "); *nullstring = 0; } pf1 = (char *) string - 1; // start parse nf = 0; while (nf < Nth) { pf1++; // start field nf++; fcc = 0; while (*pf1 == ' ') pf1++; // skip leading blanks while (true) { if (*pf1 == quote) { // pass chars between single quotes pf1++; // (but without the quotes) while (*pf1 && *pf1 != quote) pf2[fcc++] = *pf1++; if (*pf1 == quote) pf1++; } else if (strchr(delim,*pf1) || *pf1 == 0) break; // found delimiter or null else pf2[fcc++] = *pf1++; // pass normal character if (fcc > 1999) zappcrash("strField() too long"); } if (*pf1 == 0) break; } if (nf < Nth) return 0; // no Nth field if (fcc == 0) { // empty field if (*string && pf1[-1] == ' ' && ! strchr(delim,' ')) // all blanks and non-blank delimeter, return blankstring; // return one blank if (*pf1 == 0) return 0; // no field return nullstring; // return null string } if (++nret == 100) nret = 0; // use next return slot if (retf[nret]) zfree(retf[nret]); retf[nret] = (char *) zmalloc(fcc+2); strncpy0(retf[nret],pf2,fcc+1); return retf[nret]; } cchar * strField(cchar *string, cchar delim, int Nth) // alternative with one delimiter { char delims[2] = "x"; *delims = delim; return strField(string,delims,Nth); } /******************************************************************************** stat = strParms(begin, input, pname, maxcc, pval) Parse an input string with parameter names and values: "pname1=pval1 | pname2 | pname3=pval3 | pname4 ..." begin int & must be 1 to start new string, is modified input cchar * input string pname char * output parameter name maxcc int max. length for pname, including null pval double & output parameter value stat int status: 0=OK, -1=EOL, 1=parse error Each call returns the next pname and pval. A pname with no pval is assigned a value of 1 (present). Input format: pname1 | pname2=pval2 | pname3 ... null Leading blanks are ignored, and pnames may have imbedded blanks. pvals must convert to double using convSD (accepts decimal point or comma) ***/ int strParms(int &begin, cchar *input, char *pname, int maxcc, double &pval) { static int ii, beginx = 3579246; cchar *pnamex, *delim; int cc, err; if (begin == 1) { // start new string begin = ++beginx; ii = 0; } if (begin != beginx) zappcrash("strParms call error"); // thread safe, not reentrant *pname = 0; // initz. outputs to nothing pval = 0; while (input[ii] == ' ') ii++; // skip leading blanks if (input[ii] == 0) return -1; // no more data pnamex = input + ii; // next pname for (cc = 0; ; cc++) { // look for delimiter if (pnamex[cc] == '=') break; if (pnamex[cc] == '|') break; if (pnamex[cc] == 0) break; } if (cc == 0) return 1; // err: 2 delimiters if (cc >= maxcc) return 1; // err: pname too big strncpy0(pname,pnamex,cc+1); // pname >> caller strTrim(pname); // remove trailing blanks if (pnamex[cc] == 0) { // pname + null ii += cc; // position for next call pval = 1.0; // pval = 1 >> caller return 0; } if (pnamex[cc] == '|') { // pname + | ii += cc + 1; // position for next call pval = 1.0; // pval = 1 >> caller return 0; } ii += cc + 1; // pname = pval err = convSD(input + ii, pval, &delim); // parse pval (was strtod() if (err > 1) return 1; while (*delim == ' ') delim++; // skip poss. trailing blanks if (*delim && *delim != '|') return 1; // err: delimiter not | or null ii = delim - input; if (*delim) ii++; // position for next call return 0; } /********************************************************************************/ // Produce random value from hashed input string. // Output range is 0 to max-1. // Benchmark: 0.8 usec for 99 char. string 3 GHz Core i5 int strHash(cchar *string, int max) { uint hash = 1; uchar byte; while ((byte = *string++)) { hash *= byte; hash = hash ^ (hash >> 7); hash = hash * 7; // 6.5 hash = hash & 0x00FFFFFF; } hash = hash % max; return hash; } /********************************************************************************/ // Copy string with specified max. length (including null terminator). // truncate if needed. null terminator is always supplied. // Returns 0 if no truncation, 1 if input string was truncated to fit. int strncpy0(char *dest, cchar *source, uint cc) { strncpy(dest,source,cc); dest[cc-1] = 0; if (strlen(source) >= cc) return 1; // truncated else return 0; } /********************************************************************************/ // Copy string with blank pad to specified length. No null is added. void strnPad(char *dest, cchar *source, int cc) { strncpy(dest,source,cc); int ii = strlen(source); for (int jj = ii; jj < cc; jj++) dest[jj] = ' '; } /********************************************************************************/ // Remove trailing blanks from a string. Returns remaining length. int strTrim(char *dest, cchar *source) { if (dest != source) strcpy(dest,source); return strTrim(dest); } int strTrim(char *dest) { int ii = strlen(dest); while (ii && (dest[ii-1] == ' ')) dest[--ii] = 0; return ii; } /********************************************************************************/ // Remove leading and trailing blanks from a string. // Returns remaining length, possibly zero. int strTrim2(char *dest, cchar *source) { cchar *pp1, *pp2; int cc; pp1 = source; pp2 = source + strlen(source) - 1; while (*pp1 == ' ') pp1++; while (*pp2 == ' ' && pp2 > pp1) pp2--; cc = pp2 - pp1 + 1; memmove(dest,pp1,cc); dest[cc] = 0; return cc; } int strTrim2(char *string) { return strTrim2(string,(cchar *) string); } /********************************************************************************/ // Remove all blanks from a string. Returns remaining length. int strCompress(char *dest, cchar *source) { if (dest != source) strcpy(dest,source); return strCompress(dest); } int strCompress(char *string) { int ii, jj; for (ii = jj = 0; string[ii]; ii++) { if (string[ii] != ' ') { string[jj] = string[ii]; jj++; } } string[jj] = 0; return jj; } /********************************************************************************/ // Concatenate multiple strings, staying within a specified overall length. // The destination string is also the first source string. // Null marks the end of the source strings (omission --> crash). // Output is truncated to fit within the specified length. // A final null is assured and is included in the length. // Returns 0 if OK, 1 if truncation was needed. int strncatv(char *dest, int maxcc, cchar *source, ...) { cchar *ps; va_list arglist; maxcc = maxcc - strlen(dest) - 1; if (maxcc < 0) return 1; va_start(arglist,source); ps = source; while (ps) { strncat(dest,ps,maxcc); maxcc = maxcc - strlen(ps); if (maxcc < 0) break; ps = va_arg(arglist,cchar *); } va_end(arglist); if (maxcc < 0) return 1; return 0; } /********************************************************************************/ // Match 1st string to N additional strings. // Return matching string number 1 to N or 0 if no match. // Supply a null argument for end of list. int strmatchV(cchar *string, ...) { int match = 0; char *stringN; va_list arglist; va_start(arglist,string); while (1) { stringN = va_arg(arglist, char *); if (stringN == null) { va_end(arglist); return 0; } match++; if (strmatch(string,stringN)) { va_end(arglist); return match; } } } /********************************************************************************/ // convert string to upper case void strToUpper(char *string) { int ii; char jj; const int delta = 'A' - 'a'; for (ii = 0; (jj = string[ii]); ii++) if ((jj >= 'a') && (jj <= 'z')) string[ii] += delta; } void strToUpper(char *dest, cchar *source) { strcpy(dest,source); strToUpper(dest); } /********************************************************************************/ // convert string to lower case void strToLower(char *string) { int ii; char jj; const int delta = 'a' - 'A'; for (ii = 0; (jj = string[ii]); ii++) if ((jj >= 'A') && (jj <= 'Z')) string[ii] += delta; } void strToLower(char *dest, cchar *source) { strcpy(dest,source); strToLower(dest); } /********************************************************************************/ // Copy string strin to strout, replacing every occurrence // of the substring ssin with the substring ssout. // Returns the count of replacements, if any. // Replacement strings may be longer or shorter or have zero length. int repl_1str(cchar *strin, char *strout, cchar *ssin, cchar *ssout) { int ccc, cc1, cc2, nfound; cchar *ppp; cc1 = strlen(ssin); cc2 = strlen(ssout); nfound = 0; while ((ppp = strstr(strin,ssin))) { nfound++; ccc = ppp - strin; strncpy(strout,strin,ccc); strout += ccc; strin += ccc; strncpy(strout,ssout,cc2); strin += cc1; strout += cc2; } strcpy(strout,strin); return nfound; } /********************************************************************************/ // Copy string strin to strout, replacing multiple substrings with replacement strings. // Multiple pairs of string arguments follow strout, a substring and a replacement string. // Last pair of string arguments must be followed by a null argument. // Returns the count of replacements, if any. // Replacement strings may be longer or shorter or have zero length. int repl_Nstrs(cchar *strin, char *strout, ...) { va_list arglist; cchar *ssin, *ssout; char ftemp[XFCC]; int ftf, nfound; ftf = 1; nfound = 0; va_start(arglist,strout); while (true) { ssin = va_arg(arglist, char *); if (! ssin) break; ssout = va_arg(arglist, char *); if (ftf) { ftf = 0; nfound += repl_1str(strin,strout,ssin,ssout); } else { strcpy(ftemp,strout); nfound += repl_1str(ftemp,strout,ssin,ssout); } } va_end(arglist); return nfound; } /********************************************************************************/ // Break up a long text string into lines no longer than cc2 chars. // 6.0 // If fake newlines ("\n") are found, replace them with real newlines. // Break unconditionally where newlines are found and remove them. // Break at last char = blank between cc1 and cc2 if present. // Break at last delimiter char between cc1 and cc2 if present. // Break unconditionally at cc2 if none of the above. // Returns text lines in txout[*] with count as returned function value. // txout and txout[*] are subjects for zfree(). int breakup_text(cchar *txin0, char **&txout, cchar *delims, int cc1, int cc2) { char *txin; uchar ch; int p1, p2, cc3, Nout; int Np, Bp, Sp; txin = zstrdup(txin0); txout = (char **) zmalloc(100 * sizeof(char *)); if (strstr(txin0,"\\n")) // replace "\n" with real newline chars repl_1str(txin0,txin,"\\n","\n"); Nout = p1 = 0; while (true) { p2 = p1; // input line position cc3 = 0; // output line cc Np = Bp = Sp = 0; while (txin[p2]) { // scan further up to cc2 chars ch = txin[p2]; if (ch == '\n') { Np = p2; break; } // break out if newline found if (cc3 >= cc1) { if (ch == ' ') Bp = p2; // remember last ' ' found after cc1 chars if (strchr(delims,ch)) Sp = p2; // remember last delimiter found after cc1 } if (ch < 0) while ((ch = txin[p2+1]) < 0xC0) p2++; p2++; cc3++; if (cc3 == cc2) break; } if (! cc3 && ! Np) break; if (Np) cc3 = Np - p1; else if (Bp) cc3 = Bp - p1 + 1; else if (Sp) cc3 = Sp - p1 + 1; else cc3 = p2 - p1; txout[Nout] = (char *) zmalloc(cc3+1); strncpy0(txout[Nout],txin+p1,cc3+1); Nout++; p2 = p1 + cc3; if (Np) p2++; p1 = p2; if (Nout == 100) break; } zfree(txin); return Nout; } /********************************************************************************/ // Copy and convert string to hex string. // Each input character 'A' >> 3 output characters "41 " void strncpyx(char *out, cchar *in, int ccin) { int ii, jj, c1, c2; char cx[] = "0123456789ABCDEF"; if (! ccin) ccin = strlen(in); for (ii = 0, jj = 0; ii < ccin; ii++, jj += 3) { c1 = (uchar) in[ii] >> 4; c2 = in[ii] & 15; out[jj] = cx[c1]; out[jj+1] = cx[c2]; out[jj+2] = ' '; } out[jj] = 0; return; } /********************************************************************************/ // Strip trailing zeros from ascii floating numbers // (e.g. 1.230000e+02 --> 1.23e+02) void StripZeros(char *pNum) { int ii, cc; int pp, k1, k2; char work[20]; cc = strlen(pNum); if (cc >= 20) return; for (ii = 0; ii < cc; ii++) { if (pNum[ii] == '.') { pp = ii; k1 = k2 = 0; for (++ii; ii < cc; ii++) { if (pNum[ii] == '0') { if (! k1) k1 = k2 = ii; else k2 = ii; continue; } if ((pNum[ii] >= '1') && (pNum[ii] <= '9')) { k1 = 0; continue; } break; } if (! k1) return; if (k1 == pp + 1) k1++; if (k2 < k1) return; strcpy(work,pNum); strcpy(work+k1,pNum+k2+1); strcpy(pNum,work); return; } } } /********************************************************************************/ // test for blank/null string // Returns status depending on input string: // 0 not a blank or null string // 1 argument string is NULL // 2 string has zero length (*string == 0) // 3 string is all blanks int blank_null(cchar *string) { if (! string) return 1; // null string if (! *string) return 2; // zero length string int cc = strlen(string); for (int ii = 0; ii < cc; ii++) if (string[ii] != ' ') return 0; // non-blank string return 3; // blank string } /********************************************************************************/ // clean \x escape sequences and replace them with the escaped character // \n >> newline \" >> doublequote \\ >> backslash etc. // see $ man ascii for the complete list int clean_escapes(char *string) { char *pp1 = string, *pp2 = string, *pp; char char1; char escapes[] = "abtnvfr"; int count = 0; while (true) { char1 = *pp1++; if (char1 == 0) { *pp2 = 0; return count; } else if (char1 == '\\') { char1 = *pp1++; pp = strchr(escapes,char1); if (pp) char1 = pp - escapes + 7; count++; } *pp2++ = char1; } } /********************************************************************************/ // Compute the graphic character count for a UTF8 character string. // Depends on UTF8 rules: // - ascii characters are positive (0x00 to 0x7F) // - 1st char of multichar sequence is negative (0xC0 to 0xFD) // - subsequent multichars are in the range 0x80 to 0xBF int utf8len(cchar *utf8string) { int ii, cc; char xlimit = 0xC0; for (ii = cc = 0; utf8string[ii]; ii++) { if (utf8string[ii] < 0) // multibyte character while (utf8string[ii+1] < xlimit) ii++; // skip extra bytes cc++; } return cc; } /********************************************************************************/ // Extract a UTF8 substring with a specified count of graphic characters. // utf8in input UTF8 string // utf8out output UTF8 string, which must be long enough // pos initial graphic character position to get (0 = first) // cc max. count of graphic characters to get // returns number of graphic characters extracted, <= cc // Output string is null terminated after last extracted character. int utf8substring(char *utf8out, cchar *utf8in, int pos, int cc) { int ii, jj, kk, posx, ccx; char xlimit = 0xC0; for (ii = posx = 0; posx < pos && utf8in[ii]; ii++) { if (utf8in[ii] < 0) while (utf8in[ii+1] < xlimit) ii++; posx++; } jj = ii; for (ccx = 0; ccx < cc && utf8in[jj]; jj++) { if (utf8in[jj] < 0) while (utf8in[jj+1] < xlimit) jj++; ccx++; } kk = jj - ii; strncpy(utf8out,utf8in+ii,kk); utf8out[kk] = 0; return ccx; } /********************************************************************************/ // check a string for valid utf8 encoding // returns: 0 = OK, 1 = bad string int utf8_check(cchar *string) { cchar *pp; unsigned char ch1, ch2, nch; for (pp = string; *pp; pp++) { ch1 = *pp; if (ch1 < 0x7F) continue; if (ch1 > 0xBF && ch1 < 0xE0) nch = 1; else if (ch1 < 0xF0) nch = 2; else if (ch1 < 0xF8) nch = 3; else if (ch1 < 0xFC) nch = 4; else if (ch1 < 0xFE) nch = 5; else return 1; while (nch) { pp++; ch2 = *pp; if (ch2 < 0x80 || ch2 > 0xBF) return 1; nch--; } } return 0; } /********************************************************************************/ // Find the Nth graphic character position within a UTF8 string // utf8in input UTF8 string // Nth graphic character position, zero based // returns starting character (byte) position of Nth graphic character // returns -1 if Nth is beyond the string length int utf8_position(cchar *utf8in, int Nth) { int ii, posx; char xlimit = 0xC0; for (ii = posx = 0; posx < Nth && utf8in[ii]; ii++) { if (utf8in[ii] < 0) // multi-byte character while (utf8in[ii+1] && utf8in[ii+1] < xlimit) ii++; // traverse member bytes posx++; } if (utf8in[ii]) return ii; return -1; } /********************************************************************************/ // err = zsed(file, string1, string2 ... null) // // replace string1/3/5... with string2/4/6... in designated file // returns 0 OK // 1 file not found // 2 other error (with message) int zsed(cchar *infile ...) // 6.7 { int err, ftf; FILE *fid1, *fid2; char *outfile, *pp; char buffin[1000], buffout[1000], buffxx[1000]; cchar *stringin, *stringout; va_list arglist; fid1 = fopen(infile,"r"); if (! fid1) return 1; outfile = zstrdup(infile,8); strcat(outfile,"-temp"); fid2 = fopen(outfile,"w"); if (! fid2) { printz("%d \n",strerror(errno)); zfree(outfile); return 2; } while (true) { pp = fgets(buffin,500,fid1); if (! pp) break; va_start(arglist,infile); ftf = 1; while (true) { stringin = va_arg(arglist, char *); if (! stringin) break; stringout = va_arg(arglist, char *); if (! stringout) break; if (ftf) { ftf = 0; repl_1str(buffin,buffout,stringin,stringout); } else { strcpy(buffxx,buffout); repl_1str(buffxx,buffout,stringin,stringout); } } va_end(arglist); fputs(buffout,fid2); } fclose(fid1); err = fclose(fid2); if (err) { printz("%s \n",strerror(errno)); zfree(outfile); return 2; } rename(outfile,infile); zfree(outfile); return 0; } /******************************************************************************** Conversion Utilities convSI(string, inum, delim) string to int convSI(string, inum, low, high, delim) string to int with range check convSD(string, dnum, delim) string to double convSD(string, dnum, low, high, delim) string to double with range check convSF(string, fnum, delim) string to float convSF(string, fnum, low, high, delim) string to float with range check convIS(inum, string, cc) int to string with returned cc convDS(fnum, digits, string, cc) double to string with specified digits of precision and returned cc string input (cchar *) or output (char *) inum input (int) or output (int &) dnum input (double) or output (double &) delim optional returned delimiter (null or cchar **) low, high input range check (int or double) cc output string length (int &) digits input digits of precision (int) to be used for output string NOTE: decimal point may be comma or period. 1000's separators must NOT be present. convIS and convDS also return the length cc of the string output. convDS accepts same formats as atof. Decimal point can be comma or period. convDS will use whatever format (f/e) gives the shortest result. Outputs like "e03" or "e+03" will be shortened to "e3". function status returned: 0 normal conversion, no invalid digits, blank/null termination 1 successful converstion, but trailing non-numeric found 2 conversion OK, but outside specified limits 3 null or blank string, converted to zero 4 conversion error, invalid data in string overlapping statuses have following precedence: 4 3 2 1 0 *********************************************************************************/ #define max10 (0x7fffffff / 10) // Convert string to integer int convSI(cchar *string, int &inum, cchar **delim) { char ch; int sign = 0, digits = 0, tnb = 0; cchar *pch = string; inum = 0; while ((ch = *pch) == ' ') pch++; // skip leading blanks if (ch == '-') sign = -1; // process leading +/- sign if (ch == '+') sign = 1; // (at most one sign character) if (sign) pch++; while ((*pch >= '0') && (*pch <= '9')) // process digits 0 - 9 { if (inum > max10) goto conv_err; // value too big inum = 10 * inum + *pch - '0'; digits++; pch++; } if (delim) *delim = pch; // terminating delimiter if (*pch && (*pch != ' ')) tnb++; // not null or blank if (! digits) // no digits found { if (tnb) return 4; // non-numeric (invalid) string else return 3; // null or blank string } if (sign == -1) inum = -inum; // negate if - sign if (! tnb) return 0; // no trailing non-numerics else return 1; // trailing non-numerics conv_err: inum = 0; return 4; } int convSI(cchar *string, int & inum, int lolim, int hilim, cchar **delim) { int stat = convSI(string,inum,delim); if (stat > 2) return stat; // invalid or null/blank if (inum < lolim) return 2; // return 2 if out of limits if (inum > hilim) return 2; // (has precedence over status 1) return stat; // limits OK, return 0 or 1 } // Convert string to double. int convSD(cchar *string, double &dnum, cchar **delim) { char ch; int ii, sign = 0, digits = 0, ndec = 0; int exp = 0, esign = 0, edigits = 0, tnb = 0; cchar *pch = string; static int first = 1; static double decimals[21], exponents[74]; if (first) // first-time called { first = 0; // pre-calculate constants for (ii = 1; ii <= 20; ii++) decimals[ii] = pow(10.0,-ii); for (ii = -36; ii <= 36; ii++) exponents[ii+37] = pow(10.0,ii); } dnum = 0.0; if (! pch) return 3; // null arg 6.5 while (*pch == ' ') pch++; // skip leading blanks ch = *pch; if (! ch) return 3; // blank or null string 6.5 if (ch == '-') sign = -1; // process leading +/- sign if (ch == '+') sign = 1; // (at most one sign character) if (sign) pch++; get_digits: while ((*pch >= '0') && (*pch <= '9')) // process digits 0 - 9 { dnum = 10.0 * dnum + (*pch - '0'); pch++; digits++; if (ndec) ndec++; } if ((*pch == '.') || (*pch == ',')) // process decimal point { // (allow comma or period) if (ndec) goto conv_err; ndec++; pch++; goto get_digits; } if ((*pch == 'e') || (*pch == 'E')) // process optional exponent { pch++; if (*pch == '+') esign = 1; // optional +/- sign if (*pch == '-') esign = -1; if (esign) pch++; if ((*pch < '0') || (*pch > '9')) goto conv_err; // 1st digit exp = *pch - '0'; edigits++; pch++; if ((*pch >= '0') && (*pch <= '9')) // optional 2nd digit { exp = 10 * exp + (*pch - '0'); edigits++; pch++; } if ((exp < -36) || (exp > 36)) goto conv_err; // exponent too big } if (delim) *delim = pch; // terminating delimiter if (*pch && (*pch != ' ')) tnb++; // not null or blank if (!(digits + edigits)) // no digits found { if (tnb) return 4; // non-numeric (invalid) string else return 3; // null or blank string } if (ndec > 1) dnum = dnum * decimals[ndec-1]; // compensate for decimal places if (sign == -1) dnum = - dnum; // negate if negative if (exp) { if (esign == -1) exp = -exp; // process exponent dnum = dnum * exponents[exp+37]; } if (! tnb) return 0; // no trailing non-numerics else return 1; // trailing non-numerics conv_err: dnum = 0.0; return 4; } int convSD(cchar *string, double &dnum, double lolim, double hilim, cchar **delim) { int stat = convSD(string,dnum,delim); if (stat > 2) return stat; // invalid or null/blank if (dnum < lolim) return 2; // return 2 if out of limits if (dnum > hilim) return 2; // (has precedence over status 1) return stat; // limits OK, return 0 or 1 } int convSF(cchar *string, float &fnum, cchar **delim) { double dnum; int err; err = convSD(string,dnum,delim); fnum = dnum; return err; } int convSF(cchar *string, float &fnum, float lolim, float hilim, cchar **delim) { double dnum, dlolim = lolim, dhilim = hilim; int err; err = convSD(string,dnum,dlolim,dhilim,delim); fnum = dnum; return err; } // Convert int to string with returned length. // (will never exceed 12 characters) int convIS(int inum, char *string, int *cc) { int ccc; ccc = snprintf(string,12,"%d",inum); if (cc) *cc = ccc; return 0; } // Convert double to string with specified digits of precision. // Shortest length format (f/e) will be used. // Output length is returned in optional argument cc. // (will never exceed 20 characters) int convDS(double dnum, int digits, char *string, int *cc) { char *pstr; snprintf(string,20,"%.*g",digits,dnum); pstr = strstr(string,"e+"); // 1.23e+12 > 1.23e12 if (pstr) strcpy(pstr+1,pstr+2); pstr = strstr(string,"e0"); // 1.23e02 > 1.23e2 if (pstr) strcpy(pstr+1,pstr+2); pstr = strstr(string,"e0"); if (pstr) strcpy(pstr+1,pstr+2); pstr = strstr(string,"e-0"); // 1.23e-02 > 1.23e-2 if (pstr) strcpy(pstr+2,pstr+3); pstr = strstr(string,"e-0"); if (pstr) strcpy(pstr+2,pstr+3); if (cc) *cc = strlen(string); return 0; } // format a number as "123 B" or "12.3 KB" or "1.23 MB" etc. // prec is the desired digits of precision to output. // WARNING: only the last 100 conversions remain available in memory. // Example formats for 3 digits of precision: // 123 B, 999 B, 1.23 KB, 98.7 KB, 456 KB, 2.34 MB, 45.6 GB, 1.23 GB char * formatKBMB(double fnum, int prec) { #define kilo 1000 #define bmega (kilo*kilo) #define bgiga (kilo*kilo*kilo) cchar *units; static char *output[100]; static int ftf = 1, ii; double gnum; if (ftf) { // keep last 100 conversions ftf = 0; for (ii = 0; ii < 100; ii++) output[ii] = (char *) zmalloc(20); } gnum = fabs(fnum); if (gnum > bgiga) { fnum = fnum / bgiga; units = "GB"; } else if (gnum > bmega) { fnum = fnum / bmega; units = "MB"; } else if (gnum > kilo) { fnum = fnum / kilo; units = "KB"; } else units = "B "; gnum = fabs(fnum); if (prec == 2 && gnum >= 99.5) prec++; // avoid e+nn formats if (prec == 3 && gnum >= 999.5) prec++; if (prec == 4 && gnum >= 9999.5) prec++; if (prec == 5 && gnum >= 99999.5) prec++; if (prec == 6 && gnum >= 999999.5) prec++; if (++ii > 99) ii = 0; snprintf(output[ii],20,"%.*g %s",prec,fnum,units); return output[ii]; } /******************************************************************************** Wildcard string match Match candidate string to wildcard string containing any number of '*' or '?' wildcard characters. '*' matches any number of characters, including zero characters. '?' matches any one character. Returns 0 if match, 1 if no match. Benchmark: 0.032 usec. wild = *asdf*qwer?yxc 3.3 GHz Core i5 match = XXXasdfXXXXqwerXyxc *********************************************************************************/ int MatchWild(cchar *pWild, cchar *pString) { int ii, star; new_segment: star = 0; while (pWild[0] == '*') { star = 1; pWild++; } test_match: for (ii = 0; pWild[ii] && (pWild[ii] != '*'); ii++) { if (pWild[ii] != pString[ii]) { if (! pString[ii]) return 1; if (pWild[ii] == '?') continue; if (! star) return 1; pString++; goto test_match; } } if (pWild[ii] == '*') { pString += ii; pWild += ii; goto new_segment; } if (! pString[ii]) return 0; if (ii && pWild[ii-1] == '*') return 0; if (! star) return 1; pString++; goto test_match; } /******************************************************************************** Wildcard string match - ignoring case Works like MatchWild() above, but case is ignored. ***/ int MatchWildIgnoreCase(cchar *pWild, cchar *pString) { int ii, star; new_segment: star = 0; while (pWild[0] == '*') { star = 1; pWild++; } test_match: for (ii = 0; pWild[ii] && (pWild[ii] != '*'); ii++) { if (! strmatchcaseN(pWild+ii,pString+ii,1)) // the only difference { if (! pString[ii]) return 1; if (pWild[ii] == '?') continue; if (! star) return 1; pString++; goto test_match; } } if (pWild[ii] == '*') { pString += ii; pWild += ii; goto new_segment; } if (! pString[ii]) return 0; if (ii && pWild[ii-1] == '*') return 0; if (! star) return 1; pString++; goto test_match; } /******************************************************************************** SearchWild - wildcard file search Find all files with total /pathname/filename matching a pattern, which may have any number of the wildcard characters '*' and '?' in either or both the pathname and filename. cchar * SearchWild(cchar *wfilespec, int &flag) inputs: flag = 1 to start a new search flag = 2 abort a running search *** do not modify flag within a search *** wfilespec = filespec to search with optional wildcards e.g. "/name1/na*me2/nam??e3/name4*.ext?" return: a pointer to one matching file is returned per call, or null when there are no more matching files. The search may be aborted before completion, but make a final call with flag = 2 to clean up temp file. A new search with flag = 1 will also finish the cleanup. NOT THREAD SAFE - do not use in parallel threads (#) is used in place of (*) in comments below to prevent the compiler from interpreting (#/) as end of comments GNU find peculiarities: find /path/# omits "." files find /path/ includes "." files find /path/# recurses directories under /path/ find /path/#.txt does not recurse directories find /path/#/ finds all files under /path/ find /path/#/# finds files >= 1 directory level under /path/ find /path/xxx# never finds anything SearchWild uses simpler rules: '/' and '.' are treated like all other characters and match '#' and '?' no files are excluded except pure directories /path/#.txt finds all xxx.txt files under /path/ at all levels (because #.txt matches aaa.txt, /aaa/bbb.txt, etc.) Benchmark: search for /usr/share/#/README, find 365 from 119K files first time: 3.13 secs. (3.5 GHz Core i5 SSD disk) second time: 0.25 secs. ***/ cchar * SearchWild(cchar *wpath, int &uflag) { static FILE *fid = 0; static char matchfile[XFCC]; char searchpath[XFCC]; char command[XFCC]; int cc, err; char *pp; if ((uflag == 1) || (uflag == 2)) { // first call or stop flag if (fid) pclose(fid); // if file open, close it fid = 0; if (uflag == 2) return 0; // stop flag, done } if (uflag == 1) // first call flag { cc = strlen(wpath); if (cc == 0) return 0; if (cc > XFCC-20) zappcrash("SearchWild: wpath > XFCC"); repl_Nstrs(wpath,searchpath,"\"","\\\"","$","\\$",0); // escape " and $ chars. 6.7 pp = strchr(searchpath,'*'); if (pp) { // not efficient but foolproof while ((*pp != '/') && (pp > searchpath)) pp--; // /aaa/bbb/cc*cc... >>> /aaa/bbb/ if (pp > searchpath) *(pp+1) = 0; } snprintf(command,XFCC,"find -L \"%s\" -type f",searchpath); // find files (ordinary, symlink) 6.3 fid = popen(command,"r"); if (! fid) zappcrash(strerror(errno)); uflag = 763568954; // begin search } if (uflag != 763568954) zappcrash("SearchWild, uflag invalid"); while (true) { pp = fgets(matchfile,XFCC-2,fid); // next matching file if (! pp) { pclose(fid); // no more fid = 0; return 0; } cc = strlen(matchfile); // get rid of trailing \n matchfile[cc-1] = 0; err = MatchWild(wpath,matchfile); // wildcard match? if (err) continue; // no return matchfile; // return file } } /******************************************************************************** SearchWildCase - wildcard file search - ignoring case Works like SearchWild() above, but case of file name is ignored. Actually, the trailing part of the path name is also case-insensitive, meaning that it is possible to get more matches than technically correct if directories like this are present: /AAA/BBB/.../filename /AAA/bbb/.../filename ***/ cchar * SearchWildCase(cchar *wpath, int &uflag) { static FILE *fid = 0; static char matchfile[XFCC]; char searchpath[XFCC]; char command[XFCC]; int cc, err; char *pp; if ((uflag == 1) || (uflag == 2)) { // first call or stop flag if (fid) pclose(fid); // if file open, close it fid = 0; if (uflag == 2) return 0; // stop flag, done } if (uflag == 1) // first call flag { cc = strlen(wpath); if (cc == 0) return 0; if (cc > XFCC-20) zappcrash("SearchWild: wpath > XFCC"); repl_Nstrs(wpath,searchpath,"\"","\\\"","$","\\$",0); // escape " and $ chars. 6.7 pp = strchr(searchpath,'*'); if (pp) { // not efficient but foolproof while ((*pp != '/') && (pp > searchpath)) pp--; // /aaa/bbb/cc*cc... >>> /aaa/bbb/ if (pp > searchpath) *(pp+1) = 0; } snprintf(command,XFCC,"find -L \"%s\" -type f",searchpath); // find files (ordinary, symlink) 6.3 fid = popen(command,"r"); if (! fid) zappcrash(strerror(errno)); uflag = 763568954; // begin search } if (uflag != 763568954) zappcrash("SearchWild, uflag invalid"); while (true) { pp = fgets(matchfile,XFCC-2,fid); // next matching file if (! pp) { pclose(fid); // no more fid = 0; return 0; } cc = strlen(matchfile); // get rid of trailing \n matchfile[cc-1] = 0; err = MatchWildIgnoreCase(wpath,matchfile); // wildcard match? if (err) continue; // no return matchfile; // return file } } /********************************************************************************/ // perform a binary search on sorted list of integers // return matching element or -1 if not found // Benchmark: search a list of 10 million sorted integers // 0.35 usecs. 3.3 GHz Core i5 int bsearch(int seekint, int nn, int list[]) { int ii, jj, kk, rkk; ii = nn / 2; // next element to search jj = (ii + 1) / 2; // next increment nn--; // last element rkk = 0; while (true) { kk = list[ii] - seekint; // check element if (kk > 0) { ii -= jj; // too high, go down if (ii < 0) return -1; } else if (kk < 0) { ii += jj; // too low, go up if (ii > nn) return -1; } else if (kk == 0) return ii; // matched jj = jj / 2; // reduce increment if (jj == 0) { jj = 1; // step by 1 element if (! rkk) rkk = kk; // save direction else { if (rkk > 0) { if (kk < 0) return -1; } // if change direction, fail else if (kk > 0) return -1; } } } } // Perform a binary search on sorted set of records in memory. // Return matching record number (0 based) or -1 if not found. // Benchmark: search 10 million sorted records of 20 chars. // 0.61 usecs. 3.3 GHz Core i5 int bsearch(cchar *seekrec, cchar *allrecs, int recl, int nrecs) { int ii, jj, kk, rkk; ii = nrecs / 2; // next element to search jj = (ii + 1) / 2; // next increment nrecs--; // last element rkk = 0; while (true) { kk = strcmp(allrecs+ii*recl,seekrec); // compare member rec to seek rec if (kk > 0) { ii -= jj; // too high, go down in set if (ii < 0) return -1; } else if (kk < 0) { ii += jj; // too low, go up in set if (ii > nrecs) return -1; } else if (kk == 0) return ii; // matched jj = jj / 2; // reduce increment if (jj == 0) { jj = 1; // step by 1 element if (! rkk) rkk = kk; // save direction else { if (rkk > 0) { if (kk < 0) return -1; } // if change direction, fail else if (kk > 0) return -1; } } } } // Perform a binary search on sorted set of pointers to records in memory. // Return matching record number (0 based) or -1 if not found. // The pointers are sorted in the order of the records starting at char N. // The records need not be sorted. // The string length of seekrec is compared. int bsearch(cchar *seekrec, cchar **allrecs, int N, int nrecs) { int ii, jj, kk, rkk; ii = nrecs / 2; // next element to search jj = (ii + 1) / 2; // next increment nrecs--; // last element rkk = 0; while (true) { kk = strcmp(allrecs[ii]+N,seekrec); // compare member rec to seek rec 6.0 if (kk > 0) { ii -= jj; // too high, go down in set if (ii < 0) return -1; } else if (kk < 0) { ii += jj; // too low, go up in set if (ii > nrecs) return -1; } else if (kk == 0) return ii; // matched jj = jj / 2; // reduce increment if (jj == 0) { jj = 1; // step by 1 element if (! rkk) rkk = kk; // save direction else { if (rkk > 0) { if (kk < 0) return -1; } // if change direction, fail else if (kk > 0) return -1; } } } } /******************************************************************************** heap sort functions void HeapSort(int list[], int nn) void HeapSort(float flist[], int nn) void HeapSort(double dlist[], int nn) ------------------------------------- Sort list of nn integers, floats, or doubles. Numbers are sorted in ascending order. void HeapSort(char *plist[], int nn) ------------------------------------ Pointers are sorted in order of the strings they point to. The strings are not changed. void HeapSort(char *plist1[], char *plist2[], int nn) ----------------------------------------------------- Sort two lists of pointers to two sets of strings. Both lists are sorted in order of the first set of strings. void HeapSort(char *plist[], int nn, compfunc) ---------------------------------------------- Sort list of pointers to strings in user-defined order. Pointers are sorted, strings are not changed. void HeapSort(char *recs, int RL, int NR, compfunc) --------------------------------------------------- Sort an array of records in memory using a caller-supplied compare function. recs pointer to 1st record in array RL record length NR no. of records int compfunc(cchar *rec1, cchar *rec2) -------------------------------------- compare rec1 to rec2, return -1 0 +1 if rec1 < = > rec2 in sort order. Benchmarks: (3.3 GHz Core i5) 10 million integers: 1.5 secs 10 million doubles: 2.4 secs 2 million pointers to 100 character recs: 1.8 secs *********************************************************************************/ #define SWAP(x,y) (temp = (x), (x) = (y), (y) = temp) // heapsort for array of integers static void adjust(int vv[], int n1, int n2) { int *bb, jj, kk, temp; bb = vv - 1; jj = n1; kk = n1 * 2; while (kk <= n2) { if (kk < n2 && bb[kk] < bb[kk+1]) kk++; if (bb[jj] < bb[kk]) SWAP(bb[jj],bb[kk]); jj = kk; kk *= 2; } } void HeapSort(int vv[], int nn) { int *bb, jj, temp; for (jj = nn/2; jj > 0; jj--) adjust(vv,jj,nn); bb = vv - 1; for (jj = nn-1; jj > 0; jj--) { SWAP(bb[1], bb[jj+1]); adjust(vv,1,jj); } } // heapsort for array of floats static void adjust(float vv[], int n1, int n2) { float *bb, temp; int jj, kk; bb = vv - 1; jj = n1; kk = n1 * 2; while (kk <= n2) { if (kk < n2 && bb[kk] < bb[kk+1]) kk++; if (bb[jj] < bb[kk]) SWAP(bb[jj],bb[kk]); jj = kk; kk *= 2; } } void HeapSort(float vv[], int nn) { float *bb, temp; int jj; for (jj = nn/2; jj > 0; jj--) adjust(vv,jj,nn); bb = vv - 1; for (jj = nn-1; jj > 0; jj--) { SWAP(bb[1], bb[jj+1]); adjust(vv,1,jj); } } // heapsort for array of doubles static void adjust(double vv[], int n1, int n2) { double *bb, temp; int jj, kk; bb = vv - 1; jj = n1; kk = n1 * 2; while (kk <= n2) { if (kk < n2 && bb[kk] < bb[kk+1]) kk++; if (bb[jj] < bb[kk]) SWAP(bb[jj],bb[kk]); jj = kk; kk *= 2; } } void HeapSort(double vv[], int nn) { double *bb, temp; int jj; for (jj = nn/2; jj > 0; jj--) adjust(vv,jj,nn); bb = vv - 1; for (jj = nn-1; jj > 0; jj--) { SWAP(bb[1], bb[jj+1]); adjust(vv,1,jj); } } // heapsort array of pointers to strings in ascending order of strings // pointers are sorted, strings are not changed. static void adjust(char *vv[], int n1, int n2) { char **bb, *temp; int jj, kk; bb = vv - 1; jj = n1; kk = n1 * 2; while (kk <= n2) { if (kk < n2 && strcmp(bb[kk],bb[kk+1]) < 0) kk++; if (strcmp(bb[jj],bb[kk]) < 0) SWAP(bb[jj],bb[kk]); jj = kk; kk *= 2; } } void HeapSort(char *vv[], int nn) { char **bb, *temp; int jj; for (jj = nn/2; jj > 0; jj--) adjust(vv,jj,nn); bb = vv; for (jj = nn-1; jj > 0; jj--) { SWAP(bb[0], bb[jj]); adjust(vv,1,jj); } } // Heapsort 2 lists of pointers to 2 parallel sets of strings // in ascending order of the first set of strings. // Both lists of pointers are sorted together in tandem. // Pointers are sorted, strings are not changed. static void adjust(char *vv1[], char *vv2[], int n1, int n2) // 6.0 { char **bb1, **bb2, *temp; int jj, kk; bb1 = vv1 - 1; bb2 = vv2 - 1; jj = n1; kk = n1 * 2; while (kk <= n2) { if (kk < n2 && strcmp(bb1[kk],bb1[kk+1]) < 0) kk++; if (strcmp(bb1[jj],bb1[kk]) < 0) { SWAP(bb1[jj],bb1[kk]); SWAP(bb2[jj],bb2[kk]); } jj = kk; kk *= 2; } } void HeapSort(char *vv1[], char *vv2[], int nn) { char **bb1, **bb2, *temp; int jj; for (jj = nn/2; jj > 0; jj--) adjust(vv1,vv2,jj,nn); bb1 = vv1; bb2 = vv2; for (jj = nn-1; jj > 0; jj--) { SWAP(bb1[0], bb1[jj]); SWAP(bb2[0], bb2[jj]); adjust(vv1,vv2,1,jj); } } // heapsort array of pointers to strings in user-defined order. // pointers are sorted, strings are not changed. static void adjust(char *vv[], int n1, int n2, HeapSortUcomp fcomp) { char **bb, *temp; int jj, kk; bb = vv - 1; jj = n1; kk = n1 * 2; while (kk <= n2) { if (kk < n2 && fcomp(bb[kk],bb[kk+1]) < 0) kk++; if (fcomp(bb[jj],bb[kk]) < 0) SWAP(bb[jj],bb[kk]); jj = kk; kk *= 2; } } void HeapSort(char *vv[], int nn, HeapSortUcomp fcomp) { char **bb, *temp; int jj; for (jj = nn/2; jj > 0; jj--) adjust(vv,jj,nn,fcomp); bb = vv; for (jj = nn-1; jj > 0; jj--) { SWAP(bb[0], bb[jj]); adjust(vv,1,jj,fcomp); } } // heapsort for array of records, // using caller-supplied record compare function. // HeapSortUcomp returns [ -1 0 +1 ] for rec1 [ < = > ] rec2 // method: build array of pointers and sort these, then // use this sorted array to re-order the records at the end. static int *vv1, *vv2; static void adjust(char *recs, int RL, int n1, int n2, HeapSortUcomp fcomp) { int *bb, jj, kk, temp; char *rec1, *rec2; bb = vv1 - 1; jj = n1; kk = n1 * 2; while (kk <= n2) { rec1 = recs + RL * bb[kk]; rec2 = recs + RL * bb[kk+1]; if (kk < n2 && fcomp(rec1,rec2) < 0) kk++; rec1 = recs + RL * bb[jj]; rec2 = recs + RL * bb[kk]; if (fcomp(rec1,rec2) < 0) SWAP(bb[jj],bb[kk]); jj = kk; kk *= 2; } } void HeapSort(char *recs, int RL, int NR, HeapSortUcomp fcomp) { int *bb, jj, kk, temp, flag; char *vvrec; vv1 = new int[NR]; for (jj = 0; jj < NR; jj++) vv1[jj] = jj; for (jj = NR/2; jj > 0; jj--) adjust(recs,RL,jj,NR,fcomp); bb = vv1 - 1; for (jj = NR-1; jj > 0; jj--) { SWAP(bb[1], bb[jj+1]); adjust(recs,RL,1,jj,fcomp); } vv2 = new int[NR]; for (jj = 0; jj < NR; jj++) vv2[vv1[jj]] = jj; vvrec = new char[RL]; flag = 1; while (flag) { flag = 0; for (jj = 0; jj < NR; jj++) { kk = vv2[jj]; if (kk == jj) continue; memmove(vvrec,recs+jj*RL,RL); memmove(recs+jj*RL,recs+kk*RL,RL); memmove(recs+kk*RL,vvrec,RL); SWAP(vv2[jj],vv2[kk]); flag = 1; } } delete vv1; delete vv2; delete vvrec; } /******************************************************************************** int MemSort (char *RECS, int RL, int NR, int KEYS[][3], int NK) RECS is an array of records, to be sorted in-place. (record length = RL, record count = NR) KEYS[NK][3] is an integer array defined as follows: [N][0] starting position of Nth key field in RECS [N][1] length of Nth key field in RECS [N][2] type of sort for Nth key: 1 = char ascending 2 = char descending 3 = int*4 ascending 4 = int*4 descending 5 = float*4 ascending 6 = float*4 descending 7 = float*8 ascending (double) 8 = float*8 descending Benchmark: 2 million recs of 40 bytes with 4 sort keys: 2.5 secs (3.3 GHz Core i5). ***/ int MemSortComp(cchar *rec1, cchar *rec2); int MemSortKeys[10][3], MemSortNK; int MemSort(char *RECS, int RL, int NR, int KEYS[][3], int NK) { int ii; if (NR < 2) return 1; if (NK > 10) zappcrash("MemSort, bad NK"); if (NK < 1) zappcrash("MemSort, bad NK"); MemSortNK = NK; for (ii = 0; ii < NK; ii++) { MemSortKeys[ii][0] = KEYS[ii][0]; MemSortKeys[ii][1] = KEYS[ii][1]; MemSortKeys[ii][2] = KEYS[ii][2]; } HeapSort(RECS,RL,NR,MemSortComp); return 1; } int MemSortComp(cchar *rec1, cchar *rec2) { int ii, stat, kpos, ktype, kleng; int inum1, inum2; float rnum1, rnum2; double dnum1, dnum2; cchar *p1, *p2; for (ii = 0; ii < MemSortNK; ii++) // loop each key { kpos = MemSortKeys[ii][0]; // relative position kleng = MemSortKeys[ii][1]; // length ktype = MemSortKeys[ii][2]; // type p1 = rec1 + kpos; // absolute position p2 = rec2 + kpos; switch (ktype) { case 1: // char ascending stat = strncmp(p1,p2,kleng); // compare 2 key values if (stat) return stat; // + if rec1 > rec2, - if < break; // 2 keys are equal, check next key case 2: // char descending stat = strncmp(p1,p2,kleng); if (stat) return -stat; break; case 3: // int ascending memmove(&inum1,p1,4); memmove(&inum2,p2,4); if (inum1 > inum2) return 1; if (inum1 < inum2) return -1; break; case 4: // int descending memmove(&inum1,p1,4); memmove(&inum2,p2,4); if (inum1 > inum2) return -1; if (inum1 < inum2) return 1; break; case 5: // float ascending memmove(&rnum1,p1,4); memmove(&rnum2,p2,4); if (rnum1 > rnum2) return 1; if (rnum1 < rnum2) return -1; break; case 6: // float descending memmove(&rnum1,p1,4); memmove(&rnum2,p2,4); if (rnum1 > rnum2) return -1; if (rnum1 < rnum2) return 1; break; case 7: // double ascending memmove(&dnum1,p1,8); memmove(&dnum2,p2,8); if (dnum1 > dnum2) return 1; if (dnum1 < dnum2) return -1; break; case 8: // double descending memmove(&dnum1,p1,8); memmove(&dnum2,p2,8); if (dnum1 > dnum2) return -1; if (dnum1 < dnum2) return 1; break; default: // key type not 1-8 zappcrash("MemSort, bad KEYS sort type"); } } return 0; // records match on all keys } /********************************************************************************/ // test if an integer value matches any in a list of values // returns the matching value or zero if nothing matches // list of values must end with zero // zero cannot be one of the values to match int zmember(int testval, int matchval1, ...) // 6.7 { va_list arglist; int matchval; va_start(arglist,matchval1); matchval = matchval1; while (matchval) { if (testval == matchval) break; matchval = va_arg(arglist,int); } va_end(arglist); return matchval; } /******************************************************************************** variable string list functions - array / list of strings pvlist * pvlist_create(int max) void pvlist_free(pvlist *pv) int pvlist_append(pvlist *pv, cchar *entry, int unique) int pvlist_prepend(pvlist *pv, cchar *entry, int unique) int pvlist_find(pvlist *pv, cchar *entry) int pvlist_remove(pvlist *pv, cchar *entry) int pvlist_remove(pvlist *pv, int Nth) int pvlist_count(pvlist *pv) int pvlist_replace(pvlist *pv, int Nth, char *entry) cchar * pvlist_get(pvlist *pv, int Nth) int pvlist_sort(pvlist *pv) These functions manage a variable length list of variable length strings. Declare such a list as: pvlist *pv; *********************************************************************************/ // Creates a pvlist with a capacity of max strings and returns a pointer. // String lengths are unlimited, but the count of strings is limited to max. // Memory is allocated for max pointers at first. Memory for the strings is // allocated and freed as the strings are added or removed. pvlist * pvlist_create(int max) { pvlist *pv; pv = (pvlist *) zmalloc(sizeof(pvlist)); pv->max = max; pv->act = 0; pv->list = (char **) zmalloc(max * sizeof(char *)); return pv; } // free memory for variable list and contained strings void pvlist_free(pvlist *pv) { int ii; for (ii = 0; ii < pv->act; ii++) zfree(pv->list[ii]); zfree(pv->list); zfree(pv); } // append new entry to end of list (optional if unique) // if list if full, first entry is removed and rest are packed down // return: N >= 0: new entry added at position N // N = -1: not unique, not added int pvlist_append(pvlist *pv, cchar *entry, int unique) { int ii; if (unique && pvlist_find(pv,entry) >= 0) return -1; // not unique if (pv->act == pv->max) pvlist_remove(pv,0); // if list full, remove 1st entry ii = pv->act; pv->list[ii] = zstrdup(entry); // add to end of list pv->act++; return ii; } // prepend new entry to list (optional if unique) // prior list entries are pushed down to make room // if list is full, last entry is removed first // return: N = 0: new entry added at position 0 // N = -1: not unique, not added int pvlist_prepend(pvlist *pv, cchar *entry, int unique) { int ii; if (unique && pvlist_find(pv,entry) >= 0) return -1; // not unique if (pv->act == pv->max) pvlist_remove(pv,pv->act-1); // if list full, remove last entry for (ii = pv->act; ii > 0; ii--) // push all list entries down pv->list[ii] = pv->list[ii-1]; pv->list[0] = zstrdup(entry); // add to start of list pv->act++; return 0; } // find list entry by name, return entry (0 based) // return -1 if not found int pvlist_find(pvlist *pv, cchar *entry) { int ii; for (ii = 0; ii < pv->act; ii++) if (strmatch(entry,pv->list[ii])) break; if (ii < pv->act) return ii; return -1; } // remove an entry by name and repack list // return (former) entry or -1 if not found int pvlist_remove(pvlist *pv, cchar *entry) { int ii; ii = pvlist_find(pv,entry); if (ii < 0) return -1; pvlist_remove(pv,ii); return ii; } // remove an entry by number and repack list // returns -1 if entry is beyond list end int pvlist_remove(pvlist *pv, int ii) { if (ii < 0 || ii >= pv->act) return -1; zfree(pv->list[ii]); for (++ii; ii < pv->act; ii++) // pre-increment pv->list[ii-1] = pv->list[ii]; pv->act--; return 0; } // return entry count int pvlist_count(pvlist *pv) { return pv->act; } // replace Nth entry with new one int pvlist_replace(pvlist * pv, int ii, cchar *entry) { if (ii < 0 || ii >= pv->act) return -1; zfree(pv->list[ii]); pv->list[ii] = zstrdup(entry); return 0; } // return Nth entry or null char * pvlist_get(pvlist *pv, int Nth) { if (Nth >= pv->act) return 0; return pv->list[Nth]; } // sort list in ascending order int pvlist_sort(pvlist *pv) { HeapSort(pv->list,pv->act); return 0; } /********************************************************************************/ // Random number generators with explicit context // and improved randomness over a small series. // Benchmark: lrandz 0.012 usec drandz 0.014 usec 3.3 GHz Core i5 int lrandz(int64 *seed) // returns 0 to 0x7fffffff { *seed = *seed ^ (*seed << 17); *seed = *seed ^ (*seed << 20); return nrand48((unsigned int16 *) seed); } int lrandz() // implicit seed, repeatable sequence { static int64 seed = 12345678; return lrandz(&seed); } double drandz(int64 *seed) // returns 0.0 to 0.99999... { *seed = *seed ^ (*seed << 17); *seed = *seed ^ (*seed << 20); return erand48((unsigned int16 *) seed); } double drandz() // automatic seed, volatile { static int64 seed = get_seconds(); // 6.6 return drandz(&seed); } /******************************************************************************** spline1: define a curve using a set of data points (x and y values) spline2: for a given x-value, return a y-value fitting the curve For spline1, the no. of curve-defining points must be < 100. For spline2, the given x-value must be within the range defined in spline1. The algorithm was taken from the book "Numerical Recipes" (Cambridge University Press) and converted from Fortran to C++. ***/ namespace splinedata { int nn; float px1[100], py1[100], py2[100]; } void spline1(int dnn, float *dx1, float *dy1) { using namespace splinedata; float sig, p, u[100]; int ii; nn = dnn; if (nn > 100) zappcrash("spline1(), > 100 data points"); for (ii = 0; ii < nn; ii++) { px1[ii] = dx1[ii]; py1[ii] = dy1[ii]; if (ii && px1[ii] <= px1[ii-1]) zappcrash("spline1(), x-value not increasing"); } py2[0] = u[0] = 0; for (ii = 1; ii < nn-1; ii++) { sig = (px1[ii] - px1[ii-1]) / (px1[ii+1] - px1[ii-1]); p = sig * py2[ii-1] + 2; py2[ii] = (sig - 1) / p; u[ii] = (6 * ((py1[ii+1] - py1[ii]) / (px1[ii+1] - px1[ii]) - (py1[ii] - py1[ii-1]) / (px1[ii] - px1[ii-1])) / (px1[ii+1] - px1[ii-1]) - sig * u[ii-1]) / p; } py2[nn-1] = 0; for (ii = nn-2; ii >= 0; ii--) py2[ii] = py2[ii] * py2[ii+1] + u[ii]; return; } float spline2(float x) { using namespace splinedata; int kk, klo = 0, khi = nn-1; float h, a, b, y; while (khi - klo > 1) { kk = (khi + klo) / 2; if (px1[kk] > x) khi = kk; else klo = kk; } h = px1[khi] - px1[klo]; a = (px1[khi] - x) / h; b = (x - px1[klo]) / h; y = a * py1[klo] + b * py1[khi] + ((a*a*a - a) * py2[klo] + (b*b*b - b) * py2[khi]) * (h*h) / 6; return y; } /********************************************************************************/ // Add text strings to a FIFO queue, retrieve text strings. // Can be used by one or two threads. // thread 1: open queue, get strings, close queue. // thread 2: put strings into queue. // create and initialize Qtext queue, empty status void Qtext_open(Qtext *qtext, int cap) { int cc; qtext->qcap = cap; qtext->qnewest = -1; qtext->qoldest = -1; qtext->qdone = 0; cc = cap * sizeof(char *); qtext->qtext = (char **) zmalloc(cc); memset(qtext->qtext,0,cc); return; } // add new text string to Qtext queue // if queue full, sleep until space is available void Qtext_put(Qtext *qtext, cchar *format, ...) { int qnext; va_list arglist; char message[200]; va_start(arglist,format); vsnprintf(message,199,format,arglist); va_end(arglist); qnext = qtext->qnewest + 1; if (qnext == qtext->qcap) qnext = 0; while (qtext->qtext[qnext]) zsleep(0.01); qtext->qtext[qnext] = zstrdup(message); qtext->qnewest = qnext; return; } // remove oldest text string from Qtext queue // if queue empty, return a null string // returned string is subject for zfree() char * Qtext_get(Qtext *qtext) { int qnext; char *text; if (qtext->qcap == 0) return 0; qnext = qtext->qoldest + 1; if (qnext == qtext->qcap) qnext = 0; text = qtext->qtext[qnext]; if (! text) return 0; qtext->qtext[qnext] = 0; qtext->qoldest = qnext; return text; } // close Qtext, zfree() any leftover strings void Qtext_close(Qtext *qtext) { for (int ii = 0; ii < qtext->qcap; ii++) if (qtext->qtext[ii]) zfree(qtext->qtext[ii]); zfree(qtext->qtext); // 6.0 qtext->qcap = 0; return; } /******************************************************************************** Initialize application files according to following conventions: // new version + binary executable is at: /prefix/bin/appname // = PREFIX/bin/appname + other application directories are derived as follows: /prefix/share/appname/data/ desktop, parameters ... /prefix/share/doc/appname/ README, changelog, userguide-xx.html ... /prefix/share/appname/icons/ application icon files, filename.png /prefix/share/appname/images/ application image files 6.8 /prefix/share/appname/locales/ translate-xx.po ... (original) /home/user/.appname/ some installation files are copied here /home/user/.appname/logfile log file with error messages zprefix install location normally /usr, subdirs /bin /share /doc zdatadir installed data files /prefix/share/appname/data/ zdocdir documentation files /prefix/share/doc/appname/ zicondir icons /prefix/share/appname/icons/ zimagedir images /prefix/share/appname/images 6.8 zlocalesdir translation files /prefix/share/appname/locales/ zhomedir local app files /home//.appname If it does not already exist, an application directory for the current user is created at /home/username/.appname (following common Linux convention). If this directory was created for the first time, copy specified files (following the 1st argument) from the install directory into the newly created user-specific directory. The assumption is that all initial data files for the application (e.g. parameters) will be in the install data directory, and these are copied to the user directory where the user or application can modify them. If the running program is not connected to a terminal device, stdout and stderr are redirected to the log file at /home/user/.appname/logfile ***/ cchar * get_zprefix() { return zfuncs::zprefix; } // /usr or /home/ cchar * get_zhomedir() { return zfuncs::zhomedir; } // /home//.appname or /root/.appname cchar * get_zdatadir() { return zfuncs::zdatadir; } // data files, user guide cchar * get_zdocdir() { return zfuncs::zdocdir; } // documentation files cchar * get_zicondir() { return zfuncs::zicondir; } // icon files cchar * get_zimagedir() { return zfuncs::zimagedir; } // image files 6.8 cchar * get_zlocalesdir() { return zfuncs::zlocalesdir; } // translation files int zinitapp(cchar *appname, cchar *homedir) { char logfile[200], oldlog[200]; char Phomedir[200]; char *pp, *chTnow; int cc, err; time_t Tnow; STATB statb; FILE *fid; ftime(&startime); // app startup time Tnow = startime.time; catch_signals(); // catch segfault, do backtrace strcpy(zappname,appname); // appname from caller if (homedir && *homedir == '/') // homedir from caller 6.5 strncpy0(zhomedir,homedir,199); else { snprintf(zhomedir,199,"%s/.%s",getenv("HOME"),zappname); // use /home//.appname snprintf(Phomedir,200,"%s-home",zhomedir); // check /home//.appname-home 6.7 fid = fopen(Phomedir,"r"); if (fid) { pp = fgets_trim(Phomedir,200,fid); // if found, read pointer to homedir if (pp) strncpy0(zhomedir,pp,200); fclose(fid); } } printz("%s home: %s \n",zappname,zhomedir); cc = strlen(zhomedir); // stop humongous username if (cc > 160) { printz("home directory name too big: %s \n",zhomedir); exit(1); } err = stat(zhomedir,&statb); // does app home exist already? if (err) { err = mkdir(zhomedir,0750); // no, create and initialize if (err) { printz("cannot create %s: %s \n",zhomedir,strerror(errno)); exit(1); } } chTnow = ctime(&Tnow); chTnow[19] = 0; if (! isatty(fileno(stdin))) { // not attached to a terminal snprintf(logfile,199,"%s/logfile",zhomedir); // /home//logfile snprintf(oldlog,199,"%s/logfile.old",zhomedir); err = stat(logfile,&statb); if (! err) rename(logfile,oldlog); // rename old log file 6.6 fid = freopen(logfile,"a",stdout); // redirect output to log file fid = freopen(logfile,"a",stderr); if (! fid) printz("*** cannot redirect stdout and stderr \n"); } printz("\n""start %s %s \n",zappname,chTnow); fflush(0); printz("%s home directory: %s \n",zappname,zhomedir); cc = readlink("/proc/self/exe",zprefix,200); // get my executable path 6.4 if (cc <= 0) { printz("readlink() /proc/self/exe) failed \n"); // if failed, assume /usr/bin/ strcpy(zprefix,"/usr/bin/"); } else zprefix[cc] = 0; // readlink() quirk pp = strstr(zprefix,"/bin/"); // get install prefix (e.g. /usr) if (pp) *pp = 0; else (strcpy(zprefix,"/usr")); // if /xxxxx/bin --> /xxxxx 6.4 strncatv(zdatadir,199,zprefix,"/share/",zappname,"/data",null); // /prefix/share/appname/data strncatv(zicondir,199,zprefix,"/share/",zappname,"/icons",null); // /prefix/share/appname/icons strncatv(zimagedir,199,zprefix,"/share/",zappname,"/images",null); // /prefix/share/appname/images 6.8 strncatv(zlocalesdir,199,zprefix,"/share/",zappname,"/locales",null); // /prefix/share/appname/locales strncatv(zdocdir,199,zprefix,"/share/doc/",zappname,null); // /prefix/share/doc/appname #ifdef DOCDIR strncpy0(zdocdir,DOCDIR,199); // flexible DOCDIR location (SUSE) 6.5 #endif shell_quiet("cp -R -n %s/* %s",zdatadir,zhomedir); // copy MISSING files > user home 6.8 tid_main = pthread_self(); // thread ID of main() process display = gdk_display_get_default(); // get hardware info 6.8 screen = gdk_screen_get_default(); #if GTK_CHECK_VERSION(3,22,0) GdkRectangle rect; GdkMonitor *monitor; monitor = gdk_display_get_primary_monitor(display); if (! monitor) { printz("*** GTK cannot get monitor \n"); exit(1); } gdk_monitor_get_geometry(monitor,&rect); monitor_ww = rect.width; monitor_hh = rect.height; #else monitor_ww = gdk_screen_get_width(screen); // Ubuntu 16.10 monitor_hh = gdk_screen_get_height(screen); #endif #if GTK_CHECK_VERSION(3,20,0) GdkSeat *gdkseat = 0; // screen / KB / pointer associations if (screen) gdkseat = gdk_display_get_default_seat(display); // 6.5 Ubuntu 16.10 if (screen) gtksettings = gtk_settings_get_for_screen(screen); if (gdkseat) mouse = gdk_seat_get_pointer(gdkseat); #else GdkDeviceManager *devmanager = 0; // knows screen / mouse associations if (screen) devmanager = gdk_display_get_device_manager(display); // 6.2 Ubuntu 16.04 if (screen) gtksettings = gtk_settings_get_for_screen(screen); if (devmanager) mouse = gdk_device_manager_get_client_pointer(devmanager); if (! mouse) printz("*** GTK cannot get pointer device \n"); #endif if (! mouse) { printz("*** GTK cannot get pointer device \n"); exit(1); } if (gtksettings) { // get default font 6.3 g_object_get(gtksettings,"gtk_font_name",&appfont,null); zsetfont(appfont); // set mono and bold versions } return 1; } // set a new application font via GtkSettings // newfont should be someting like "sans 11" // use generic monospace font since app font may not have a mono version void zsetfont(cchar *newfont) // 6.3 { char font[40], bfont[48], mfont[48], mbfont[56]; char junk[40]; int nn, size; if (! gtksettings) return; nn = sscanf(newfont,"%s %d",font,&size); // "sans 11" if (nn != 2) { nn = sscanf(newfont,"%s %s %d",font,junk,&size); if (nn != 3) goto fail; } if (size < 5 || size > 30) goto fail; g_object_set(gtksettings,"gtk-font-name",newfont,null); // set dialog font snprintf(bfont,48,"%s bold %d",font,size); // "sans bold 11" snprintf(mfont,48,"mono %d",size-1); // "mono 10" 6.5 snprintf(mbfont,56,"mono bold %d",size-1); // "mono bold 10" appfont = zstrdup(newfont); appboldfont = zstrdup(bfont); appmonofont = zstrdup(mfont); appmonoboldfont = zstrdup(mbfont); appfontsize = size; return; fail: printz("cannot set font: %s \n",newfont); return; } // get the font character width and height for a given widget // returns 0 if OK, +N if error int widget_font_metrics(GtkWidget *widget, int &fontwidth, int &fontheight) // 6.7 { PangoContext *pangocontext; PangoFontDescription *pangofontdesc; PangoFontMetrics *pangofontmetrics; PangoLanguage *pangolanguage; pangocontext = gtk_widget_get_pango_context(widget); pangofontdesc = pango_context_get_font_description(pangocontext); pangolanguage = pango_language_get_default(); pangofontmetrics = pango_context_get_metrics(pangocontext,pangofontdesc,pangolanguage); if (! pangofontmetrics) { printz("widget_font_metrics() failed \n"); return 1; } fontwidth = pango_font_metrics_get_approximate_char_width(pangofontmetrics); fontheight = pango_font_metrics_get_ascent(pangofontmetrics) + pango_font_metrics_get_descent(pangofontmetrics); fontwidth /= PANGO_SCALE; fontheight /= PANGO_SCALE; return 0; } // Find a locale-dependent installation file or user file. // file type: doc, data, locale, user [ userlocale removed v.6.1 ] // file name: README, changelog, userguide.html, parameters, translate.po ... // Returns complete file name, e.g. /usr/share/appname/locales/translate-de.po // Output filespec should be 200 bytes (limit for all installation files). // Returns 0 if OK, +N if not found. int locale_filespec(cchar *filetype, cchar *filename, char *filespec) { char *pp, fname[20], fext[8]; char lc_RC[8]; // -lc or -lc_RC int cc, err; STATB statb; filespec[0] = '/'; strcat(filespec,filetype); // leave /type as default if (strmatch(filetype,"doc")) strcpy(filespec,zdocdir); // /usr/share/doc/appname if (strmatch(filetype,"data")) strcpy(filespec,zdatadir); // /usr/share/appname/data if (strmatch(filetype,"locale")) strcpy(filespec,zlocalesdir); // /usr/share/appname/locales if (strmatch(filetype,"user")) strcpy(filespec,zhomedir); // /home//.appname strncpy0(fname,filename,20); pp = strchr(fname,'.'); if (pp) { strcpy(fext,pp); // file type .fext *pp = 0; } else *fext = 0; // no type lc_RC[0] = '-'; strncpy0(lc_RC+1,zlang,6); // locale with region code: -lc_RC tryextras: cc = strlen(filespec); filespec[cc] = '/'; // /directories.../ strcpy(filespec+cc+1,fname); // /directories.../fname cc = strlen(filespec); // | pp = filespec + cc; // pp strcpy(pp,lc_RC); // /directories.../fname-lc_RC.fext strcat(pp,fext); err = stat(filespec,&statb); if (! err) return 0; strcpy(pp+3,fext); // /directories.../fname-lc.fext err = stat(filespec,&statb); if (! err) return 0; strcpy(pp,"-en"); // /directories.../fname-en.fext strcat(pp,fext); err = stat(filespec,&statb); if (! err) return 0; strcpy(pp,fext); // /directories.../fname.fext err = stat(filespec,&statb); if (! err) return 0; if (strmatch(filetype,"doc")) { // these files may be placed in strcpy(filespec,zdocdir); // /usr/share/doc/appname/extras strcat(filespec,"/extras"); // due to Linux chaos filetype = ""; goto tryextras; // try again using /extras } return 1; // not found } /********************************************************************************/ // Display help file in a separate process so application is not blocked. // help file: /zdatadir/userguide-lc_RC.html (or) *-lc.html (or) *-en.html // context: optional arg. show file starting at internal link = context // look for user guide file in /usr/share/data/appname/ [ extras/ ] void showz_userguide(cchar *context) { char filespec[200], url[200]; int err; err = locale_filespec("data","userguide.html",filespec); if (err) { zmessageACK(0,ZTX("user guide not found")); return; } snprintf(url,199,"file://%s",filespec); if (context && *context) // specific topic wanted strncatv(url,199,"#",context,null); // file://.../userguide-xx.html#context showz_html(url); return; } /********************************************************************************/ // display application log file in a popup window // The log file is /home//.appname/logfile void showz_logfile() // log file { char buff[200]; fflush(0); snprintf(buff,199,"cat %s/logfile",zhomedir); popup_command(buff,800,600); return; } // find and show a text file in /usr/share/doc/appname/ // or /usr/share/appname/data // the text file may also be a compressed .gz file // type is "doc" or "data" void showz_textfile(const char *type, const char *file) { char filex[40], filespec[200], command[200]; int err; strncpy0(filex,file,36); // look for gzip file first strcat(filex,".gz"); err = locale_filespec(type,filex,filespec); if (! err) { snprintf(command,200,"zcat %s",filespec); popup_command(command,700,500,0,1); return; } strncpy0(filex,file,36); // look for uncompressed file err = locale_filespec(type,filex,filespec); if (! err) { snprintf(command,200,"cat %s",filespec); popup_command(command,600,400,0,1); return; } zmessageACK(0,"file not found: %s %s",type,file); return; } // show a local or remote html file using the user's preferred browser // to show a local file starting at an internal live link location: // url = "file://directory/.../filename#livelink void showz_html(cchar *url) { static char prog[20]; static int ftf = 1, err; if (ftf) { ftf = 0; *prog = 0; err = zsystem("which firefox"); // use xdg-open only as last resort if (! err) strcpy(prog,"firefox"); else { err = zsystem("which chromium-browser"); if (! err) strcpy(prog,"chromium-browser"); else { err = zsystem("which xdg-open"); if (! err) strcpy(prog,"xdg-open"); } } } if (! *prog) { zmessageACK(null,"html file reader not found"); return; } shell_ack("%s %s &",prog,url); return; } /******************************************************************************** Translation Functions Translation files are standard .po files as used in the Gnu gettext system. However the .po files are used directly, and there is no need to merge and compile them into a binary format (.mo file). A translation file is one of: /translate-lc.po or *-lc_RC.po where "lc" is a standard language code and "lc_RC" a language and region code. The file may also be compressed with the file type .po.gz Translation files contain two record types: msgid "english text" msgstr "translation text" The text strings may continue on multiple lines, each such segment enclosed in quotes. The strings may contain C-format codes (%s %d etc.) and may contain escaped special characters (\n etc.). A text string may have a context part "context::string", where "context" is any short string, "::" is a separator, and "string" is the string to translate or the translation of a string. This is to handle the case where a single English string may need multiple translations, depending on context. The English string may be present multiple times in a .po file, each one marked with a different context and having a different translation. The context part is optional in the translation strings and is not displayed in the GUI. Initialize translations: int ZTXinit(cchar *lang) lang is "lc" or "lc_RC" or null (current locale will be used) Initializes the running application for the translation of text messages. It reads the translation file for the user's locale and builds a translation table for use by ZTX(). lang: 2-character language code 'lc' ("de" "fr" "es" etc.) or 5-character language and region code 'lc_RC' ("de_AT" etc.) or null to use the current locale Status returned: 0 = OK, 1 = unable to find or process translation file. Translate a text string: cchar *translation = ZTX(cchar *english) english: text string which may have printf formats (%d %s ...) translation: the returned equivalent translation If the user language is English or if no translation is found, the input string is returned, else the translated string. example: program code: printf(ZTX("answer: %d %s \n next line"), 123, "qwerty"); A German .po file (translate-de.po) would have the following: msgid "" "answer: %d %s \n" " next line" msgstr "" "Antwort: %d %s \n" " nächste Zeile" *********************************************************************************/ namespace ZTXnames { FILE *fidr; char buff[ZTXmaxcc], *ppq1, *ppq2; char *porec, *wporec; char Etext[ZTXmaxcc], Ttext[ZTXmaxcc]; // .po text: "line 1 %s \n" "line 2" char **etext, **ttext; // arrays, english and translations char **estring, **tstring; // merged, un-quoted, un-escaped int Ntext = 0; // array counts void ZTXgettext(char *text); char *ZTXmergetext(cchar *text); } // read and process .po file at application startup // prepare english strings and translations for quick access void ZTXinit(cchar *lang) // initialize translations { using namespace ZTXnames; int ii, err; char installpo[200], localpo[200]; char *pp, poname[20]; STATB statb; double itime, ltime; if (Ntext) { // free prior translation for (ii = 0; ii < Ntext; ii++) { zfree(etext[ii]); zfree(ttext[ii]); zfree(estring[ii]); zfree(tstring[ii]); } zfree(etext); zfree(ttext); zfree(estring); zfree(tstring); Ntext = 0; } etext = (char **) zmalloc(ZTXmaxent * sizeof(char *)); // english text and translations ttext = (char **) zmalloc(ZTXmaxent * sizeof(char *)); // (segmented, quoted, escaped) estring = (char **) zmalloc(ZTXmaxent * sizeof(char *)); // english strings and translations tstring = (char **) zmalloc(ZTXmaxent * sizeof(char *)); // (merged, un-quoted, un-escaped) if (lang && *lang) strncpy0(zlang,lang,6); // use language from caller else { // help Linux chaos pp = getenv("LANG"); // use $LANG if defined if (! pp) pp = getenv("LANGUAGE"); // use $LANGUAGE if defined if (! pp) pp = setlocale(LC_MESSAGES,""); // use locale if defined if (pp) strncpy0(zlang,pp,6); // "lc_RC" language/region code else strcpy(zlang,"en"); // use English } if (*zlang < 'a') strcpy(zlang,"en"); // use English if garbage printz("language: %s \n",zlang); if (strmatchN(zlang,"en",2)) return; // English, do nothing err = locale_filespec("locale","translate.po",installpo); // look for installed .po file if (err) err = locale_filespec("locale","translate.po.gz",installpo); if (err) { printz("*** no translate-%s.po file found \n",zlang); return; } err = locale_filespec("user","translate.po",localpo); // look for local .po file 6.6 if (! err) { stat(installpo,&statb); // compare mod date/time for itime = statb.st_mtime; // installed and local .po files stat(localpo,&statb); ltime = statb.st_mtime; if (itime > ltime) err = 1; // installed .po is newer } if (err) { // local .po not found or stale 6.6 pp = strrchr(installpo,'/'); // extract filename from full path strcpy(poname,pp+1); pp = strstr(poname,".gz"); // uncompress or copy installed .po file if (pp) { *pp = 0; err = shell_ack("gunzip -c %s > %s/%s",installpo,zhomedir,poname); } else err = shell_ack("cp %s %s/%s",installpo,zhomedir,poname); if (err) return; // installed .po file not found snprintf(localpo,200,"%s/%s",zhomedir,poname); // final local .po file name } fidr = fopen(localpo,"r"); // open .po file if (! fidr) { printz("*** cannot open .po file: %s \n",localpo); return; } porec = 0; // no .po record yet *Etext = *Ttext = 0; // no text yet while (true) { if (! porec) porec = fgets_trim(buff,ZTXmaxcc,fidr); // get next .po record if (! porec) break; // EOF if (blank_null(porec)) { // blank record porec = 0; continue; } if (*porec == '#') { // comment porec = 0; continue; } if (strmatchN(porec,"msgid",5)) // start new english string { if (*Etext) { // two in a row printz("no translation: %s \n",Etext); *Etext = 0; } if (*Ttext) { printz("orphan translation: %s \n",Ttext); *Ttext = 0; } porec += 5; // get segmented text string ZTXgettext(Etext); // "segment1 %s \n" "segment2" ... } else if (strmatchN(porec,"msgstr",6)) // start new translation { porec += 6; // get segmented string ZTXgettext(Ttext); if (! *Etext) { printz("orphan translation: %s \n",Ttext); *Ttext = 0; continue; } if (strlen(Ttext) < 3) // translation is "" (quotes included) printz("no translation: %s \n",Etext); // leave as "" } else { printz("unrecognized .po record: %s \n",porec); porec = 0; continue; } if (*Etext && *Ttext) // have an english/translation pair { etext[Ntext] = zstrdup(Etext); // add to translation tables ttext[Ntext] = zstrdup(Ttext); *Etext = *Ttext = 0; Ntext++; if (Ntext == ZTXmaxent) // cannot continue zappcrash("more than %d translations",ZTXmaxent); } } fclose(fidr); printz(".po file has %d entries \n",Ntext); for (ii = 0; ii < Ntext; ii++) { pp = ZTXmergetext(etext[ii]); // merge segmented text strings estring[ii] = zstrdup(pp); pp = ZTXmergetext(ttext[ii]); tstring[ii] = zstrdup(pp); } HeapSort(estring, tstring, Ntext); // sort both strings, english order /*** for (ii = 0; ii < Ntext; ii++) { // dump sorted translation strings if (strchr(estring[ii],'\n')) printf("\n"); printf("%s \n",estring[ii]); if (strchr(estring[ii],'\n')) printf("\n"); } ***/ return; } // private function // read and combine multiple 'msgid' or 'msgstr' quoted strings // output is one string with one or more quoted segments: // "text line 1 %s \n" "text line 2" ... // each segment comes from a different line in the input .po file void ZTXnames::ZTXgettext(char *pstring) { using namespace ZTXnames; int cc, scc = 0; while (true) // join multiple quoted strings { while (*porec && *porec != '"') porec++; // find opening string quote if (! *porec) { porec = fgets_trim(buff,ZTXmaxcc,fidr); // get next .po record if (! porec) return; if (strmatchN(porec,"msgid",5)) return; // end of this string if (strmatchN(porec,"msgstr",6)) return; } ppq1 = porec; // opening quote ppq2 = ppq1 + 1; while ((*ppq2 && *ppq2 != '"') || // find closing (non-escaped) quote (*ppq2 == '"' && *(ppq2-1) == '\\')) ppq2++; if (! *ppq2) return; cc = ppq2 - ppq1 + 1; // min. case is "" if (cc + 1 + scc >= ZTXmaxcc) printz("*** string is too long %s \n",pstring); else { strncpy0(pstring+scc,ppq1,cc+1); // accum. substrings, minus quotes scc += cc; } porec = ppq2 + 1; } return; } // private function // convert quoted string segments into binary form that // matches the compiled string in the source program // (remove quotes, merge strings, un-escape \n \" etc.) char * ZTXnames::ZTXmergetext(cchar *dirtystring) { static char cleanstring[ZTXmaxcc]; int ii, jj; strncpy0(cleanstring,dirtystring,ZTXmaxcc); clean_escapes(cleanstring); for (ii = jj = 0; cleanstring[ii]; ii++) if (cleanstring[ii] != '"') cleanstring[jj++] = cleanstring[ii]; cleanstring[jj] = 0; return cleanstring; } // Translate the input english string or return the input string. // Look for "context::string" and return "string" only if context found. // This function may need a few microseconds if thousands of strings must be searched. cchar * ZTX(cchar *english) { using namespace ZTXnames; cchar *pp, *pp2; int ii; if (! Ntext) return english; // no translations ii = bsearch(english,(cchar **) estring,0,Ntext); // binary search, < 1 microsec. if (ii < 0) pp = english; else pp = tstring[ii]; if (strlen(pp) == 0) pp = english; // translation is "" for (pp2 = pp; *pp2 && pp2 < pp+30; pp2++) // remove context if present if (*pp2 == ':' && *(pp2+1) == ':') return pp2+2; return pp; } // Find all untranslated strings and return them one per call. // Set ftf = 1 for first call, will be returned = 0. // Returns null after last untranslated string. cchar * ZTX_missing(int &ftf) { using namespace ZTXnames; int ii; static int next; if (ftf) ftf = next = 0; for (ii = next; ii < Ntext; ii++) if (strlen(tstring[ii]) == 0) break; // translation is "" next = ii + 1; if (ii < Ntext) return estring[ii]; // return english return 0; // EOL } /******************************************************************************** GTK utility functions ********************************************************************************/ // Iterate main loop every "skip" calls. // If called within the main() thread, does a GTK main loop to process menu events, etc. // You must do this periodically within long-running main() thread tasks if you wish to // keep menus, buttons, output windows, etc. alive and working. The skip argument will // cause the function to do nothing for skip calls, then perform the normal function. // This allows it to be imbedded in loops with little execution time penalty. // If skip = 100, zmainloop() will do nothing for 100 calls, execute normally, etc. // If called from a thread, zmainloop() does nothing. void zmainloop(int skip) { static int xskip = 0; if (skip) { if (++xskip < skip) return; xskip = 0; } if (! pthread_equal(pthread_self(),zfuncs::tid_main)) return; // thread caller, do nothing while (gtk_events_pending()) // gdk_flush() removed gtk_main_iteration_do(0); // use gtk_main_iteration_do return; } // Iterate the main loop and sleep for designated time void zmainsleep(float secs) { while (secs > 0) { zmainloop(); zsleep(0.001); secs = secs - 0.001; } return; } /********************************************************************************/ // cairo drawing context for GDK window GTK 3.21 version // 6.6 #if GTK_CHECK_VERSION(3,22,0) cairo_t * draw_context_create(GdkWindow *gdkwin, draw_context_t &context) { if (context.dcr) zappcrash("draw_context_create(): nested call"); context.win = gdkwin; context.rect.x = 0; context.rect.y = 0; context.rect.width = gdk_window_get_width(gdkwin); context.rect.height = gdk_window_get_height(gdkwin); context.reg = cairo_region_create_rectangle(&context.rect); context.ctx = gdk_window_begin_draw_frame(gdkwin,context.reg); context.dcr = gdk_drawing_context_get_cairo_context(context.ctx); return context.dcr; } void draw_context_destroy(draw_context_t &context) { if (! context.dcr) zappcrash("draw_context_destroy(): not created"); gdk_window_end_draw_frame(context.win,context.ctx); cairo_region_destroy(context.reg); /* cairo_destroy(context.dcr); this is fatal */ context.dcr = 0; return; } #else cairo_t * draw_context_create(GdkWindow *gdkwin, draw_context_t &context) { if (context.dcr) zappcrash("draw_context_create(): nested call"); context.dcr = gdk_cairo_create(gdkwin); return context.dcr; } void draw_context_destroy(draw_context_t &context) { if (! context.dcr) zappcrash("draw_context_destroy(): not created"); if (context.dcr) cairo_destroy(context.dcr); context.dcr = 0; return; } #endif /********************************************************************************/ // textwidget functions // 6.8 // -------------------- // // High-level use of GtkTextView widget for text reports, line editing, text selection // In functions below, textwidget = zdialog_widget(zd,"widgetname"), // where "widgetname" is a zdialog "text" widget type // clear the text widget to blank void textwidget_clear(GtkWidget *textwidget) { GtkTextBuffer *textBuff; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; gtk_text_buffer_set_text(textBuff,"",-1); return; } // clear the text widget from given line to end void textwidget_clear(GtkWidget *textwidget, int line) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // iter at line start gtk_text_buffer_get_end_iter(textBuff,&iter2); gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete existing line and rest of buffer return; } // get the current line count int textwidget_linecount(GtkWidget *textwidget) { GtkTextBuffer *textBuff; int nlines; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return 0; nlines = gtk_text_buffer_get_line_count(textBuff); return nlines; } // append a new line of text to the end of existing text lines // line should normally include trailing \n // if existing last line is without trailing \n, text is appended to this line void textwidget_append(GtkWidget *textwidget, int bold, cchar *format, ...) { va_list arglist; char textline[2000]; GtkTextBuffer *textBuff; GtkTextIter enditer; GtkTextMark *endmark; GtkTextTag *fontag = 0; cchar *normfont = zfuncs::appmonofont; cchar *boldfont = zfuncs::appmonoboldfont; va_start(arglist,format); vsnprintf(textline,1999,format,arglist); va_end(arglist); textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; gtk_text_buffer_get_end_iter(textBuff,&enditer); // end of text endmark = gtk_text_buffer_get_mark(textBuff,"endmark"); // get my end mark if (! endmark) endmark = gtk_text_buffer_create_mark(textBuff,"endmark",&enditer,0); if (bold) fontag = gtk_text_buffer_create_tag(textBuff,0,"font",boldfont,0); // prepare bold/norm tag else fontag = gtk_text_buffer_create_tag(textBuff,0,"font",normfont,0); gtk_text_buffer_insert_with_tags(textBuff,&enditer,textline,-1,fontag,null); // insert line gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(textwidget),endmark,0,0,1,1); // scroll into view zmainloop(); return; } // insert a new line of text after designated line (0 based) // use line -1 to insert before line 0 // line should normally include trailing \n void textwidget_insert(GtkWidget *textwidget, int bold, int line, cchar *format, ...) { va_list arglist; char textline[2000]; GtkTextBuffer *textBuff; GtkTextIter iter; int nlines; GtkTextTag *fontag = 0; cchar *normfont = zfuncs::appmonofont; cchar *boldfont = zfuncs::appmonoboldfont; va_start(arglist,format); vsnprintf(textline,1999,format,arglist); va_end(arglist); textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; if (line < 0) gtk_text_buffer_get_start_iter(textBuff,&iter); // insert before line 0 if (line >= 0) { nlines = gtk_text_buffer_get_line_count(textBuff); // insert after line if (line < nlines - 1) gtk_text_buffer_get_iter_at_line(textBuff,&iter,line+1); // start of next line else gtk_text_buffer_get_end_iter(textBuff,&iter); // or end of text } if (bold) fontag = gtk_text_buffer_create_tag(textBuff,0,"font",boldfont,0); // prepare bold/norm tag else fontag = gtk_text_buffer_create_tag(textBuff,0,"font",normfont,0); gtk_text_buffer_insert_with_tags(textBuff,&iter,textline,-1,fontag,null); // insert line gtk_text_buffer_get_iter_at_line(textBuff,&iter,line); gtk_text_iter_forward_line(&iter); gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(textwidget),&iter,0,0,1,1); zmainloop(); return; } // replace a given line (0 based) with a new line // line = -1: replace last line. -2: replace last-1 line, etc. // new line should normally include trailing \n void textwidget_replace(GtkWidget *textwidget, int bold, int line, cchar *format, ...) { va_list arglist; char textline[2000]; GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int nlines; GtkTextTag *fontag = 0; cchar *normfont = zfuncs::appmonofont; cchar *boldfont = zfuncs::appmonoboldfont; va_start(arglist,format); vsnprintf(textline,1999,format,arglist); va_end(arglist); textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (line < 0) line = nlines + line - 1; if (line >= nlines) line = nlines - 1; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start iter2 = iter1; gtk_text_iter_forward_line(&iter2); // end gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete line gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); if (bold) fontag = gtk_text_buffer_create_tag(textBuff,0,"font",boldfont,0); // prepare bold/norm tag else fontag = gtk_text_buffer_create_tag(textBuff,0,"font",normfont,0); gtk_text_buffer_insert_with_tags(textBuff,&iter1,textline,-1,fontag,null); // insert line gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start gtk_text_iter_forward_line(&iter1); gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(textwidget),&iter1,0,0,1,1); zmainloop(); return; } // delete a given line including the trailing \n void textwidget_delete(GtkWidget *textwidget, int line) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int nlines; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (line < 0 || line >= nlines) return; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start iter2 = iter1; gtk_text_iter_forward_line(&iter2); // end gtk_text_buffer_delete(textBuff,&iter1,&iter2); // delete line gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // next line start gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(textwidget),&iter1,0,0,1,1); zmainloop(); return; } // find first line of text containing characters matching input string // search is from line1 to end, then from 0 to line1-1 // returns first matching line or -1 if none // comparison is not case sensitive int textwidget_find(GtkWidget *textwidget, char *matchtext, int line1) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int line, nlines, cc; char *textline = 0, *pp1, *pp2; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return -1; nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (! nlines) return -1; if (line1 < 0) line1 = 0; // starting line to search if (line1 >= nlines) line1 = 0; line = line1; while (true) { gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start iter2 = iter1; gtk_text_iter_forward_line(&iter2); // end textline = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); // get text if (textline) { pp1 = strcasestr(textline,matchtext); // look for matching text if (pp1) break; // found free(textline); } line++; if (line == nlines) line = 0; if (line == line1) return -1; // wrapped around, not found } cc = strlen(matchtext); // highlight matching text pp2 = pp1 + cc - 1; gtk_text_buffer_get_iter_at_line_index(textBuff,&iter1,line,pp1-textline); gtk_text_buffer_get_iter_at_line_index(textBuff,&iter2,line,pp2-textline+1); gtk_text_buffer_select_range(textBuff,&iter1,&iter2); free(textline); return line; } // scroll a textwidget to put a given line on screen // 1st line = 0. for last line use line = -1. void textwidget_scroll(GtkWidget *textwidget, int line) { GtkTextBuffer *textBuff; GtkTextIter iter; GtkTextMark *mark; GtkAdjustment *vadjust; double upperlimit; int line1, line2; zmainloop(); // necessary textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; vadjust = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(textwidget)); if (line < 0) { // bottom upperlimit = gtk_adjustment_get_upper(vadjust); gtk_adjustment_set_value(vadjust,upperlimit); } else if (line < 3) // top gtk_adjustment_set_value(vadjust,0); else { line1 = line - 3; // get range of lines on screen line2 = line + 3; gtk_text_buffer_get_iter_at_line(textBuff,&iter,line1); mark = gtk_text_buffer_create_mark(textBuff,0,&iter,0); gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(textwidget),mark); gtk_text_buffer_get_iter_at_line(textBuff,&iter,line2); mark = gtk_text_buffer_create_mark(textBuff,0,&iter,0); gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(textwidget),mark); } zmainloop(); return; } // dump the entire textwidget contents into a file void textwidget_dump(GtkWidget *widget, cchar *filename) { FILE *fid; char *prec; int line, err; fid = fopen(filename,"w"); // open file if (! fid) { zmessageACK(0,ZTX("cannot open file %s"),filename); return; } for (line = 0; ; line++) { prec = textwidget_line(widget,line,1); // get text line, strip \n if (! prec) break; fprintf(fid,"%s\n",prec); // output with \n } err = fclose(fid); // close file if (err) zmessageACK(0,"file close error"); return; } // dump the entire textwidget contents into a file, using a save-as dialog void textwidget_save(GtkWidget *widget, GtkWindow *parent) { char *file; file = zgetfile(ZTX("save text to file"),parent,"save","noname"); if (! file) return; textwidget_dump(widget,file); zfree(file); return; } // Get a line of text. Returned text is subject for zfree(). // trailing \n is included if strip == 0 char * textwidget_line(GtkWidget *textwidget, int line, int strip) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int cc, nlines; char *textline, *ztext; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return 0; nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (line < 0 || line >= nlines) return 0; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start iter2 = iter1; gtk_text_iter_forward_line(&iter2); // end textline = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); // get text line if (! textline) return 0; ztext = zstrdup(textline); free(textline); gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(textwidget),&iter1,0,0,1,1); // scroll on-screen if (strip) { cc = strlen(ztext); if (cc && ztext[cc-1] == '\n') ztext[cc-1] = 0; } return ztext; } // highlight a given line of text void textwidget_highlight_line(GtkWidget *textwidget, int line) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int nlines; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; nlines = gtk_text_buffer_get_line_count(textBuff); // lines now in buffer if (line < 0 || line >= nlines) return; gtk_text_buffer_get_iter_at_line(textBuff,&iter1,line); // line start iter2 = iter1; gtk_text_iter_forward_line(&iter2); // end gtk_text_buffer_select_range(textBuff,&iter1,&iter2); // highlight return; } // get the word at the given position within the line // words are defined by line starts and ends, and the given delimiters // returns word and delimiter (&end) char * textwidget_word(GtkWidget *textwidget, int line, int posn, cchar *dlims, char &end) { GtkTextBuffer *textBuff; char *txline, *pp1, *pp2, *ztext; int pos, cc; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return 0; txline = textwidget_line(textwidget,line,0); if (! txline) return 0; pos = utf8_position(txline,posn); // graphic position to byte position if (pos < 0) return 0; pp1 = txline + pos; if (! *pp1 || strchr(dlims,*pp1)) return 0; // reject edge position or delimiter while (pp1 > txline && ! strchr(dlims,pp1[-1])) pp1--; // find start of word pp2 = pp1; while (pp2[1] && ! strchr(dlims,pp2[1])) pp2++; // find following delimiter or EOL end = pp2[1]; // return delimiter while (*pp1 == ' ') pp1++; // no leading or trailing blanks while (*pp2 == ' ') pp2--; cc = pp2 - pp1 + 1; if (cc < 1) return 0; // all blanks? ztext = (char *) zmalloc(cc+1); strncpy0(ztext,pp1,cc+1); zfree(txline); return ztext; } // highlight text at line and positiion, length cc void textwidget_highlight_word(GtkWidget *textwidget, int line, int posn, int cc) { GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; char *txline, *pp1, *pp2; textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textwidget)); if (! textBuff) return; txline = textwidget_line(textwidget,line,0); if (! txline) return; pp1 = txline + posn; pp2 = pp1 + cc - 1; gtk_text_buffer_get_iter_at_line_index(textBuff,&iter1,line,pp1-txline); gtk_text_buffer_get_iter_at_line_index(textBuff,&iter2,line,pp2-txline+1); gtk_text_buffer_select_range(textBuff,&iter1,&iter2); zfree(txline); return; } // set a callback function for mouse and key events in textwidget // + line selection via mouse click or keyboard up/down arrow key // + line and word selection via mouse click // // callback function looks like this: // void userfunc(GtkWidget *textwidget, int line, int position, int KBkey); void textwidget_set_callbackfunc(GtkWidget *textwidget, textwidget_callbackfunc_t userfunc) { int textwidget_eventfunc(GtkWidget *textwidget, GdkEvent *event, textwidget_callbackfunc_t userfunc); gtk_widget_add_events(textwidget,GDK_BUTTON_PRESS_MASK); gtk_widget_add_events(textwidget,GDK_KEY_PRESS_MASK); gtk_widget_add_events(textwidget,GDK_POINTER_MOTION_MASK); G_SIGNAL(textwidget,"key-press-event",textwidget_eventfunc,userfunc); G_SIGNAL(textwidget,"button-press-event",textwidget_eventfunc,userfunc); G_SIGNAL(textwidget,"motion-notify-event",textwidget_eventfunc,userfunc); return; } int textwidget_eventfunc(GtkWidget *textwidget, GdkEvent *event, textwidget_callbackfunc_t userfunc) { #define TEXT GTK_TEXT_WINDOW_TEXT #define VIEW GTK_TEXT_VIEW static GdkCursor *arrowcursor = 0; GdkWindow *gdkwin; GtkTextIter iter1; int mpx, mpy, tbx, tby, line, pos, KBkey; if (! arrowcursor) // first call, get arrow cursor arrowcursor = gdk_cursor_new_for_display(display,GDK_TOP_LEFT_ARROW); gdkwin = gtk_text_view_get_window(VIEW(textwidget),TEXT); // set arrow cursor for window if (gdkwin) gdk_window_set_cursor(gdkwin,arrowcursor); if (event->type == GDK_KEY_PRESS) { // KB key event KBkey = ((GdkEventKey *) event)->keyval; userfunc(textwidget,-1,-1,KBkey); // return key return 1; } if (event->type == GDK_BUTTON_PRESS) { ///gtk_widget_grab_focus(textwidget); // GTK: auto focus inconsistent mpx = int(((GdkEventButton *) event)->x); // mouse click position mpy = int(((GdkEventButton *) event)->y); mpx -= appfontsize / 2; // more accurate if (mpx < 0) mpx = 0; gtk_text_view_window_to_buffer_coords(VIEW(textwidget),TEXT,mpx,mpy,&tbx,&tby); gtk_text_view_get_iter_at_location(VIEW(textwidget),&iter1,tbx,tby); line = gtk_text_iter_get_line(&iter1); // clicked textwidget line pos = gtk_text_iter_get_line_offset(&iter1); // clicked position userfunc(textwidget,line,pos,-1); // return line and position return 1; } return 0; } /******************************************************************************** simplified GTK menu bar, tool bar, status bar functions These functions simplify the creation of GTK menus and toolbars. The functionality is limited but adequate for most purposes. mbar = create_menubar(vbox) create menubar mitem = add_menubar_item(mbar, label, func) add menu item to menubar msub = add_submenu_item(mitem, label, func, tip) add submenu item to menu or submenu tbar = create_toolbar(vbox, iconsize) create toolbar add_toolbar_button(tbar, label, tip, icon, func) add button to toolbar stbar = create_stbar(vbox) create status bar stbar_message(stbar, message) display message in status bar These functions to the following: * create a menu bar and add to existing window verticle packing box * add menu item to menu bar * add submenu item to menu bar item or submenu item * create a toolbar and add to existing window * add button to toolbar, using stock icon or custom icon * create a status bar and add to existing window * display a message in the status bar argument definitions: vbox GtkWidget * a verticle packing box (in a window) mbar GtkWidget * reference for menu bar popup GtkWidget * reference for popup menu mitem GtkWidget * reference for menu item (in a menu bar) msub GtkWidget * reference for submenu item (in a menu) label cchar * menu or toolbar name or label tbar GtkWidget * reference for toolbar tip cchar * tool button tool tip (popup text via mouse-over) icon cchar * stock icon name or custom icon file name (see below) func see below menu or tool button response function arg cchar * argument to response function stbar int reference for status bar message cchar * message to display in status bar The icon argument for the function add_toolbar_button() has two forms. For a GTK stock item referenced with a macro like GTK_STOCK_OPEN, use the corresponding text name, like "gtk-open". For a custom icon, use the icon's file name like "my-icon.png". The file is expected to be in get_zdatadir()/icons. The icon file may be any size, and is resized for use on the toolbar. If the file is not found, the stock icon "gtk-missing-image" is used (".png" and ".jpg" files both work). For a button with no icon (text label only), use 0 or null for the icon argument. For a menu separator, use the menu name "separator". For a toolbar separator, use the label "separator". For a title menu (no response function), set the response function to null. The response function for both menus and toolbar buttons looks like this: void func(GtkWidget *, cchar *) The following macro is also supplied to simplify the coding of response functions: G_SIGNAL(window,event,func,arg) which expands to: g_signal_connect(G_OBJECT(window),event,G_CALLBACK(func),(void *) arg) *********************************************************************************/ // create menu bar and add to vertical packing box GtkWidget * create_menubar(GtkWidget *vbox) { GtkWidget *wmbar; wmbar = gtk_menu_bar_new(); gtk_box_pack_start(GTK_BOX(vbox),wmbar,0,0,0); return wmbar; } // add menu item to menu bar, with optional response function GtkWidget * add_menubar_item(GtkWidget *wmbar, cchar *mname, cbFunc func) { GtkWidget *wmitem; wmitem = gtk_menu_item_new_with_label(mname); gtk_menu_shell_append(GTK_MENU_SHELL(wmbar),wmitem); if (func) G_SIGNAL(wmitem,"activate",func,mname); return wmitem; } // add submenu item to menu item, with optional response function GtkWidget * add_submenu_item(GtkWidget *wmitem, cchar *mlab, cbFunc func, cchar *mtip) { GtkWidget *wmsub, *wmsubitem; wmsub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(wmitem)); // add submenu if not already if (wmsub == null) { wmsub = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(wmitem),wmsub); } if (strmatch(mlab,"separator")) wmsubitem = gtk_separator_menu_item_new(); else wmsubitem = gtk_menu_item_new_with_label(mlab); // add menu item with label only gtk_menu_shell_append(GTK_MENU_SHELL(wmsub),wmsubitem); // append submenu item to submenu if (func) G_SIGNAL(wmsubitem,"activate",func,mlab); // connect optional response function if (mtip) g_object_set(G_OBJECT(wmsubitem),"tooltip-text",mtip,null); // add optional popup menu tip return wmsubitem; } /********************************************************************************/ // create toolbar and add to vertical packing box int tbIconSize = 32; // default if not supplied 6.2 GtkWidget * create_toolbar(GtkWidget *vbox, int iconsize) { GtkWidget *wtbar; wtbar = gtk_toolbar_new(); gtk_box_pack_start(GTK_BOX(vbox),wtbar,0,0,0); tbIconSize = iconsize; return wtbar; } // add toolbar button with label and icon ("iconfile.png") and tool tip // at least one of label and icon should be present GtkWidget * add_toolbar_button(GtkWidget *wtbar, cchar *blab, cchar *btip, cchar *icon, cbFunc func) { GtkToolItem *tbutton; GError *gerror = 0; PIXBUF *pixbuf; GtkWidget *wicon = 0; char iconpath[300], *pp; STATB statb; int err, cc; if (blab && strmatch(blab,"separator")) { tbutton = gtk_separator_tool_item_new(); gtk_toolbar_insert(GTK_TOOLBAR(wtbar),GTK_TOOL_ITEM(tbutton),-1); return (GtkWidget *) tbutton; } if (icon && *icon) { // get icon pixbuf *iconpath = 0; strncatv(iconpath,199,zimagedir,"/",icon,null); // 6.8 err = stat(iconpath,&statb); if (err) { // alternative path 6.2 cc = readlink("/proc/self/exe",iconpath,300); // get own program path if (cc > 0) iconpath[cc] = 0; // readlink() quirk pp = strrchr(iconpath,'/'); // directory of program if (pp) *pp = 0; strncatv(iconpath,300,"/icons/",icon,null); // .../icons/iconfile.png } pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,tbIconSize,tbIconSize,1,&gerror); if (pixbuf) wicon = gtk_image_new_from_pixbuf(pixbuf); } tbutton = gtk_tool_button_new(wicon,blab); if (! wicon) gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(tbutton),"gtk-missing-image"); if (btip) gtk_tool_item_set_tooltip_text(tbutton,btip); gtk_tool_item_set_homogeneous(tbutton,0); gtk_toolbar_insert(GTK_TOOLBAR(wtbar),GTK_TOOL_ITEM(tbutton),-1); if (func) G_SIGNAL(tbutton,"clicked",func,blab); return (GtkWidget *) tbutton; } /********************************************************************************/ // create a status bar and add to the start of a packing box GtkWidget * create_stbar(GtkWidget *pbox) { GtkWidget *stbar; stbar = gtk_statusbar_new(); gtk_box_pack_start(GTK_BOX(pbox),stbar,0,0,0); gtk_widget_show(stbar); return stbar; } // display message in status bar int stbar_message(GtkWidget *wstbar, cchar *message) { static int ctx = -1; if (ctx == -1) ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(wstbar),"all"); gtk_statusbar_pop(GTK_STATUSBAR(wstbar),ctx); gtk_statusbar_push(GTK_STATUSBAR(wstbar),ctx,message); return 0; } /******************************************************************************** Customizable Graphic Popup Menu void Gmenuz(GtkWidget *parent, cchar *configfile, callbackfunc) Open a popup window with a customizable graphic menu. parent parent window or null configfile menu configuration file, will be created if missing callbackfunc callback function to receive clicked menu entry: typedef void callbackfunc(cchar *menu) This function allows an application to offer a customizable menu which a user can populate with frequently used (menu) functions, arranged as desired. A menu entry selected by the user is passed to the application for execution. The initial popup window is blank. Right click an empty space on the popup window to define a new menu entry. Right click an existing entry to modify it. Use the resulting dialog to define or change the menu entry. menu text optional text appearing in the popup window menu func text that is passed to the application callback function menu icon optional menu icon: /directory.../filename.png icon size rendered icon size in the popup window, 24x24 to 64x64 pixels close checkbox: option to close the popup window when menu is used Left drag a menu entry to move it somewhere else on the popup window. The popup window can be resized to fit the contained menu entries. Left click a menu entry to select the menu. The callback function will be called to execute the menu function. If "close" was checked, the popup window will close. All menu settings are saved in the supplied configuration file whenever the popup window is closed, if any changes were made since it was opened. Icon files are copied into the same directory as the configuration file and these copies are used. The icon selected when the menu entry is created can disappear without consequence. If the [x] window kill button is pressed, the window is closed and the calling program is informed by passing "quit" to the callback function. layout of menu configuration file: popup xpos ypos width height popup window position and size posn xpos ypos ww hh menu position in popup window menu menu text menu text on popup window func funcname argument for user callback function icon /.../file.png optional menu icon file size NN optional icon size (default 24) kill optional flag, kill window when used menu menu text next menu entry ... *********************************************************************************/ namespace gmenuznames { #define maxME 200 // max. menu entries #define maxText 1000 // max. text size, all menu fields typedef void callbackfunc(cchar *menu); // user callback function callbackfunc *gmenucallback; cchar *menuFont, *menuBoldFont; char *menufile = 0; // menu configuration file from user GtkWidget *mWin, *layout; // popup and drawing windows GtkWidget *pWin; // parent window int winposx=100, winposy=100, winww=400, winhh=300; // initial popup WRT parent window struct menuent { // menu entry on popup window int xpos, ypos, ww, hh; // layout position, extent char *menu; // text on window or null int bold; // text is bold 6.3 char *func; // func name for user callback char *icon; // icon file or null PIXBUF *pixbuf; // icon pixbuf or null int size; // icon size or zero int kill; // kill popup window when menu used int Fnewicon; // flag, icon was changed 6.2 }; menuent menus[200]; // menu entries int NME = 0; // entry count zdialog *zdedit = 0; // active edit dialog int mpx, mpy; // mouse click/drag position int me; // current menu entry int Fchanged = 0; // flag, menu edited or resized int Fpopquit = 0; // popup is being closed int Fpopbusy = 0; // popup is active int deficonsize = 32; // default icon size or last size set void wpaint(GtkWidget *, cairo_t *); // window repaint - draw event void resize(); // window resize event void quit(); // kill window and exit void update_configfile(); // update menu configuration file void mouse_event(GtkWidget *, GdkEventButton *, void *); // mouse event function void KB_event(GtkWidget *, GdkEventKey *, void *); // KB event function void draw_text(cairo_t *, char *, int px, int py, int &ww, int &hh); // draw text and return pixel extent void draw_bold_text(cairo_t *, char *, int px, int py, int &ww, int &hh); // draw text and return pixel extent void edit_menu(); // dialog to create/edit menu entry int edit_menu_event(zdialog *zd, cchar *event); // dialog event function void drag_drop_event(int mx, int my, char *file); // drag-drop event function } // user callable function to build the menu from user's configuration file void Gmenuz(GtkWidget *parent, cchar *title, cchar *ufile, gmenuznames::callbackfunc ufunc) { using namespace gmenuznames; FILE *fid; int nn, xx, yy, ww, hh, size; int pposx, pposy; int xpos, ypos; char *pp, buff[maxText]; PIXBUF *pixbuf; GError *gerror; if (Fpopbusy) return; // don't allow multiple popups menuFont = zfuncs::appfont; // 6.3 menuBoldFont = zfuncs::appboldfont; pWin = parent; // get parent window if (menufile) zfree(menufile); menufile = zstrdup(ufile); // get menu configuration file gmenucallback = ufunc; // get user callback function NME = 0; fid = fopen(menufile,"r"); // read window geometry if (fid) { nn = fscanf(fid," popup %d %d %d %d",&xx,&yy,&ww,&hh); // get popup window position and size if (nn == 4 && ww > 50 && ww < 1000 && hh > 50 && hh < 1000) { winposx = xx; // OK to use winposy = yy; winww = ww; winhh = hh; } while (true) { pp = fgets_trim(buff,maxText-1,fid,1); // read next menu entry if (! pp) break; if (strmatchN(pp,"posn ",5)) { // position in popup window if (NME == maxME) { zmessageACK(mWin,"exceeded %d menu entries",maxME); break; } me = NME; // new entry NME++; // entry count memset(&menus[me],0,sizeof(menuent)); // clear all menu data nn = sscanf(pp+5," %d %d ",&xpos,&ypos); // position in popup window if (nn != 2) xpos = ypos = 100; if (xpos > 1000) xpos = 1000; if (ypos > 1000) ypos = 1000; menus[me].xpos = xpos; menus[me].ypos = ypos; } if (strmatchN(pp,"menu ",5)) { // menu text if (strlen(pp+5) > 0) menus[me].menu = zstrdup(pp+5); // get menu text else menus[me].menu = 0; } if (strmatchN(pp,"bold",4)) // bold text 6.3 menus[me].bold = 1; if (strmatchN(pp,"func ",5)) { // function name if (strlen(pp+5)) menus[me].func = zstrdup(pp+5); else menus[me].func = 0; } if (strmatchN(pp,"icon ",5)) { // menu icon file if (strlen(pp+5)) { menus[me].icon = zstrdup(pp+5); gerror = 0; pixbuf = gdk_pixbuf_new_from_file(pp+5,&gerror); if (! pixbuf && gerror) printz("*** %s \n",gerror->message); menus[me].pixbuf = pixbuf; } else menus[me].pixbuf = 0; } if (strmatchN(pp,"size ",5)) { size = atoi(pp+5); if (size < 24) size = 24; if (size > 256) size = 256; menus[me].size = size; } if (strmatchN(pp,"kill",4)) // kill window flag menus[me].kill = 1; } fclose(fid); } mWin = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create popup window for menu entries if (! pWin) { // no parent window pposx = pposy = 0; gtk_window_set_focus_on_map(GTK_WINDOW(mWin),1); // put on top when created 6.7 } else { gtk_window_get_position(GTK_WINDOW(pWin),&pposx,&pposy); // parent window position (NW corner) gtk_window_set_transient_for(GTK_WINDOW(mWin),GTK_WINDOW(pWin)); // popup window belongs to parent } if (title) gtk_window_set_title(GTK_WINDOW(mWin),title); winposx += pposx; // popup position relative to parent winposy += pposy; gtk_window_set_default_size(GTK_WINDOW(mWin),winww,winhh); // set size and position gtk_window_move(GTK_WINDOW(mWin),winposx,winposy); layout = gtk_layout_new(0,0); // create drawing window gtk_container_add(GTK_CONTAINER(mWin),layout); // add to popup window G_SIGNAL(mWin,"destroy",quit,0); // connect signals to windows G_SIGNAL(mWin,"delete-event",quit,0); G_SIGNAL(mWin,"notify",resize,0); G_SIGNAL(mWin,"configure-event",resize,0); // GTK 3.18 API changed SNAFU 6.3 G_SIGNAL(mWin,"key-press-event",KB_event,0); // connect KB key event G_SIGNAL(layout,"draw",wpaint,0); gtk_widget_add_events(layout,GDK_BUTTON_PRESS_MASK); // connect mouse events gtk_widget_add_events(layout,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(layout,GDK_BUTTON_MOTION_MASK); gtk_widget_add_events(layout,GDK_POINTER_MOTION_MASK); G_SIGNAL(layout,"button-press-event",mouse_event,0); G_SIGNAL(layout,"button-release-event",mouse_event,0); G_SIGNAL(layout,"motion-notify-event",mouse_event,0); drag_drop_dest(mWin,drag_drop_event); // 6.2 gtk_widget_show_all(mWin); // show all widgets zmainloop(); // after initial window events 6.3 Fchanged = 0; // no changes yet Fpopquit = 0; // not being closed Fpopbusy = 1; return; } // paint window when created, exposed, resized void gmenuznames::wpaint(GtkWidget *, cairo_t *cr) { using namespace gmenuznames; PIXBUF *pixbuf; char *text, *text2; int xpos, ypos, ww, hh, size, yadd; int bold; for (int me = 0; me < NME; me++) // loop all menu entries { xpos = menus[me].xpos; // window position ypos = menus[me].ypos; text = menus[me].menu; // menu text bold = menus[me].bold; // bold text flag 6.3 pixbuf = menus[me].pixbuf; // icon size = menus[me].size; // size if (pixbuf) { // draw icon at window position gdk_cairo_set_source_pixbuf(cr,pixbuf,xpos,ypos); cairo_paint(cr); if (! size) size = deficonsize; // use default if not specified 6.2 } else size = 0; yadd = 0; if (pixbuf) yadd = gdk_pixbuf_get_height(pixbuf) + 2; // icon height + extra space 6.2 if (text) { text2 = (char *) zmalloc(strlen(text)+100); // replace "\n" with newline 6.3 repl_1str(text,text2,"\\n","\n"); if (bold) draw_bold_text(cr,text2,xpos,ypos+yadd,ww,hh); // bold text 6.3 else draw_text(cr,text2,xpos,ypos+yadd,ww,hh); zfree(text2); } else ww = hh = 0; if (ww < size) ww = size; // menu entry enclosing rectangle hh += yadd; menus[me].ww = ww; menus[me].hh = hh; } return; } // resize event - save current window size void gmenuznames::resize() { using namespace gmenuznames; int xx, yy, ww, hh; if (Fpopquit) return; // ignore bogus call when killed gtk_window_get_position(GTK_WINDOW(mWin),&xx,&yy); gtk_window_get_size(GTK_WINDOW(mWin),&ww,&hh); if (xx != winposx || yy != winposy) Fchanged = 1; // window size or position changed if (ww != winww || hh != winhh) Fchanged = 1; winposx = xx; // save window position winposy = yy; winww = ww; // save new size winhh = hh; return; } // [x] kill window // Save current menu status for next session. void gmenuznames::quit() { using namespace gmenuznames; Fpopquit = 1; Fpopbusy = 0; if (Fchanged) update_configfile(); gtk_widget_destroy(mWin); gmenucallback("quit"); // inform host program return; } // menu changed, save all menu data to menu config. file void gmenuznames::update_configfile() { using namespace gmenuznames; static int ftf = 1; char pxbfile[200]; int64 DT; int irand; int me; // local me 6.8 char *menuname; int pposx, pposy; FILE *fid; GError *gerror; if (ftf) { // initialize random generator 6.3 ftf = 0; DT = time(null); drandz(&DT); } if (pWin) gtk_window_get_position(GTK_WINDOW(pWin),&pposx,&pposy); // parent window position (may have moved) else pposx = pposy = 0; winposx -= pposx; // popup position relative to parent winposy -= pposy; fid = fopen(menufile,"w"); // open for write if (! fid) { zmessageACK(mWin," %s \n %s",menufile,strerror(errno)); // diagnose permissions error return; } fprintf(fid,"popup %d %d %d %d \n",winposx,winposy,winww,winhh); for (me = 0; me < NME; me++) // write all menu entries to file { if (! menus[me].menu && ! menus[me].pixbuf) { // no text and no icon printz("*** Gmenuz: empty menu entry \n"); continue; } fprintf(fid,"\n"); // blank line separator fprintf(fid,"posn %d %d \n",menus[me].xpos, menus[me].ypos); // menu position in window if (menus[me].menu) // menu text fprintf(fid,"menu %s \n",menus[me].menu); if (menus[me].bold) // bold text flag 6.3 fprintf(fid,"bold \n"); if (menus[me].func) // menu function (text) fprintf(fid,"func %s \n",menus[me].func); if (menus[me].pixbuf) { // menu icon if (menus[me].Fnewicon) { // new or changed icon bugfix 6.3 menuname = menus[me].menu; irand = 1000000 * drandz(&DT); // 0 - 999999 snprintf(pxbfile,200,"%s-%s-%d.png",menufile,menuname,irand); // unique file name gerror = 0; gdk_pixbuf_save(menus[me].pixbuf,pxbfile,"png",&gerror,null); // write pixbuf to file if (gerror) printz("*** %s %s \n",menuname,gerror->message); else fprintf(fid,"icon %s \n",pxbfile); // pixbuf file name in menu file menus[me].Fnewicon = 0; } else if (menus[me].icon) // no change 6.3 fprintf(fid,"icon %s \n",menus[me].icon); } if (menus[me].size) // icon size fprintf(fid,"size %d \n",menus[me].size); if (menus[me].kill) fprintf(fid,"kill \n"); // kill window flag } fclose(fid); Fchanged = 0; return; } // mouse event function - capture buttons and drag movements void gmenuznames::mouse_event(GtkWidget *, GdkEventButton *event, void *) { using namespace gmenuznames; static int bdtime = 0, butime = 0; static int Lmouse = 0, Rmouse = 0, Fdrag = 0; static int elapsed, mpx0 = 0, mpy0 = 0; int Fclick, dx, dy, xpos, ypos, size; int raster = 10; // alignment raster mpx = int(event->x); // mouse position in window mpy = int(event->y); if (event->type == GDK_BUTTON_PRESS) { Lmouse = Rmouse = Fdrag = 0; if (event->button == 1) Lmouse++; // left or right mouse button if (event->button == 3) Rmouse++; bdtime = event->time; for (me = 0; me < NME; me++) // look for clicked menu entry { if (mpx < menus[me].xpos) continue; if (mpy < menus[me].ypos) continue; if (mpx > menus[me].xpos + menus[me].ww) continue; if (mpy > menus[me].ypos + menus[me].hh) continue; break; } if (me < NME) { // menu item clicked (selected) mpx0 = mpx; // set new drag origin mpy0 = mpy; } else me = -1; // indicate empty space clicked } if (event->type == GDK_BUTTON_RELEASE) { Fclick = 0; butime = event->time; elapsed = butime - bdtime; // button down time, milliseconds bdtime = 0; if (elapsed < 500 && ! Fdrag) Fclick = 1; // mouse clicked if (me >= 0 && Fclick && Lmouse) { // menu entry was left-clicked if (menus[me].kill) quit(); // close menu if specified 6.8 if (menus[me].func) gmenucallback(menus[me].func); // caller user function(func) } else if (Fclick && Rmouse) // menu entry or empty space right-clicked edit_menu(); // edit menu else if (me >= 0 && Fdrag) // menu entry drag ended Fchanged = 1; // mark menu revised Lmouse = Rmouse = Fdrag = 0; // mouse click action completed } if (event->type == GDK_MOTION_NOTIFY) // mouse movement { if (me >= 0 && Lmouse && bdtime) { // menu drag underway 6.8 dx = mpx - mpx0; dy = mpy - mpy0; if (abs(dx) + abs(dy) > 4) // ignore small drags { Fdrag++; mpx0 = mpx; // set new drag origin mpy0 = mpy; menus[me].xpos = mpx; // center menu on mouse menus[me].ypos = mpy; size = menus[me].size; if (size) { menus[me].xpos -= size / 2; menus[me].ypos -= size / 2; } else { menus[me].xpos -= 15; menus[me].ypos -= 8; } xpos = menus[me].xpos; // align to raster 6.5 ypos = menus[me].ypos; xpos = raster * (xpos / raster); ypos = raster * (ypos / raster); menus[me].xpos = xpos; menus[me].ypos = ypos; if (menus[me].xpos < 0) menus[me].xpos = 0; // not off the window 6.2 if (menus[me].xpos > winww-20) menus[me].xpos = winww-20; if (menus[me].ypos < 0) menus[me].ypos = 0; if (menus[me].ypos > winhh-20) menus[me].ypos = winhh-20; gtk_widget_queue_draw(layout); // repaint window } } } return; } // KB press event function - send certain keys to main app void gmenuznames::KB_event(GtkWidget *, GdkEventKey *kbevent, void *) { using namespace gmenuznames; int KBkey = kbevent->keyval; if (KBkey == GDK_KEY_F1) KBevent(kbevent); if (KBkey == GDK_KEY_Escape) quit(); // escape = cancel 6.5 return; } // draw text into layout and return pixel dimensions of enclosing rectangle void gmenuznames::draw_text(cairo_t *cr, char *text, int x, int y, int &w, int &h) { using namespace gmenuznames; static PangoFontDescription *pfont = 0; static PangoLayout *playout = 0; if (! pfont) { pfont = pango_font_description_from_string(menuFont); // first call, get font sizing poop playout = gtk_widget_create_pango_layout(layout,0); pango_layout_set_font_description(playout,pfont); } pango_layout_set_text(playout,text,-1); // compute layout pango_layout_get_pixel_size(playout,&w,&h); // pixel width and height of layout cairo_move_to(cr,x,y); // draw layout with text cairo_set_source_rgb(cr,0,0,0); pango_cairo_show_layout(cr,playout); return; } // draw bold text into layout and return pixel dimensions of enclosing rectangle // 6.3 void gmenuznames::draw_bold_text(cairo_t *cr, char *text, int x, int y, int &w, int &h) { using namespace gmenuznames; static PangoFontDescription *pfont = 0; static PangoLayout *playout = 0; if (! pfont) { pfont = pango_font_description_from_string(menuBoldFont); // first call, get font sizing poop playout = gtk_widget_create_pango_layout(layout,0); pango_layout_set_font_description(playout,pfont); } pango_layout_set_text(playout,text,-1); // compute layout pango_layout_get_pixel_size(playout,&w,&h); // pixel width and height of layout cairo_move_to(cr,x,y); // draw layout with text cairo_set_source_rgb(cr,0,0,0); pango_cairo_show_layout(cr,playout); return; } // dialog to create a new menu entry from user inputs void gmenuznames::edit_menu() { using namespace gmenuznames; if (me < 0) { // new menu entry if (NME == maxME) { zmessageACK(mWin,"capacity limit exceeded"); return; } me = NME; memset(&menus[me],0,sizeof(menuent)); // clear all data } /** menu text [________________] [x] Bold menu func [________________________] menu icon [_______________] [Browse] icon size [___] [x] close window [Apply] [Delete] [Cancel] **/ if (! zdedit) // create dialog if not already { zdedit = zdialog_new("edit menu entry",mWin,ZTX("Apply"),ZTX("Delete"),ZTX("Cancel"),null); zdialog_add_widget(zdedit,"hbox","hb1","dialog"); zdialog_add_widget(zdedit,"vbox","vb1","hb1",0,"homog|space=3"); zdialog_add_widget(zdedit,"vbox","vb2","hb1",0,"homog|expand"); zdialog_add_widget(zdedit,"label","lab11","vb1",ZTX("menu text")); zdialog_add_widget(zdedit,"label","lab12","vb1",ZTX("menu func")); zdialog_add_widget(zdedit,"label","lab13","vb1",ZTX("menu icon")); zdialog_add_widget(zdedit,"label","lab14","vb1",ZTX("icon size")); zdialog_add_widget(zdedit,"hbox","hb2","vb2"); zdialog_add_widget(zdedit,"zentry","text","hb2",0,"size=30|space=2"); zdialog_add_widget(zdedit,"check","bold","hb2",ZTX("Bold"),"space=5"); // 6.3 zdialog_add_widget(zdedit,"zentry","func","vb2",0,"size=30|space=2"); zdialog_add_widget(zdedit,"hbox","hb3","vb2",0,"expand|space=2"); zdialog_add_widget(zdedit,"zentry","icon","hb3",0,"expand"); zdialog_add_widget(zdedit,"button","browse","hb3",ZTX("Browse"),"space=5"); zdialog_add_widget(zdedit,"hbox","hb4","vb2",0,"space=2"); zdialog_add_widget(zdedit,"zspin","size","hb4","24|256|1|32"); zdialog_add_widget(zdedit,"check","kill","hb4",ZTX("close window"),"space=30"); zdialog_run(zdedit,edit_menu_event,"mouse"); } if (menus[me].menu) // stuff menu text into dialog zdialog_stuff(zdedit,"text",menus[me].menu); else zdialog_stuff(zdedit,"text",""); if (menus[me].func) // stuff menu function zdialog_stuff(zdedit,"func",menus[me].func); else zdialog_stuff(zdedit,"func",""); zdialog_stuff(zdedit,"bold",menus[me].bold); // stuff bold text flag 6.3 if (menus[me].icon) // stuff icon file zdialog_stuff(zdedit,"icon",menus[me].icon); else zdialog_stuff(zdedit,"icon",""); if (menus[me].size) // stuff icon size zdialog_stuff(zdedit,"size",menus[me].size); if (menus[me].kill) // stuff window kill flag zdialog_stuff(zdedit,"kill",1); else zdialog_stuff(zdedit,"kill",0); if (me == NME) { // new menu entry menus[me].xpos = mpx; // initial position from mouse menus[me].ypos = mpy; } return; } // menu entry dialog event function int gmenuznames::edit_menu_event(zdialog *zd, cchar *event) { using namespace gmenuznames; char text[maxText]; char *iconfile, *pp; int size; PIXBUF *pixbuf; GError *gerror; if (strmatch(event,"browse")) { // browse for icon file iconfile = zgetfile("select icon",null,"file",0); if (iconfile) zdialog_stuff(zd,"icon",iconfile); } if (! zd->zstat) return 1; // wait for dialog completion if (zd->zstat == 2) { // [delete] - delete menu entry if (me < 0 || me >= NME) return 0; for (int me2 = me; me2 < NME-1; me2++) // remove menu entry and close hole menus[me2] = menus[me2+1]; NME--; Fchanged = 1; // mark menu revised gtk_widget_queue_draw(layout); // repaint window } if (zd->zstat != 1) { // not [apply] - kill dialog zdialog_free(zdedit); return 1; } // [apply] - update menu from dialog data zdialog_fetch(zd,"text",text,maxText); if (*text) menus[me].menu = zstrdup(text); // menu text, optional else menus[me].menu = 0; zdialog_fetch(zd,"bold",menus[me].bold); // bold text flag 6.3 zdialog_fetch(zd,"func",text,maxText); // menu function name strTrim2(text); pp = strstr(text,"mystuff"); if (pp) { // this is a mystuff link 6.3 strcpy(text,pp); // "/usr/bin/mystuff" --> "mystuff" if (strlen(text) < 9) { strcat(text," [unique-name].txt"); // remind unique menu file name zdialog_stuff(zd,"func",text); zmessageACK(mWin,"assign a unique menu name"); zd->zstat = 0; return 1; } } if (*text) menus[me].func = zstrdup(text); else menus[me].func = 0; zdialog_fetch(zd,"icon",text,maxText); // menu icon file, optional strTrim2(text); if (*text) { zdialog_fetch(zd,"size",size); // icon size gerror = 0; pixbuf = gdk_pixbuf_new_from_file_at_size(text,size,size,&gerror); if (! pixbuf) { if (gerror) zmessageACK(mWin,gerror->message); // bad icon file zd->zstat = 0; // keep dialog open return 0; // do nothing } menus[me].icon = zstrdup(text); menus[me].pixbuf = pixbuf; menus[me].size = size; deficonsize = size; // set new default 6.2 menus[me].Fnewicon = 1; // mark icon changed 6.2 } else { // no icon menus[me].icon = 0; menus[me].pixbuf = 0; menus[me].size = 0; menus[me].Fnewicon = 0; // 6.2 } zdialog_fetch(zd,"kill",menus[me].kill); // popup window kill flag if (me == NME) NME++; // if new menu entry, incr. count Fchanged = 1; // mark menu revised zdialog_free(zdedit); // destroy dialog gtk_widget_queue_draw(layout); // repaint window return 1; } // function to accept drag-drop of a desktop file or file name. void gmenuznames::drag_drop_event(int mpx, int mpy, char *file) // 6.2 { using namespace gmenuznames; FILE *fid; char buff[200], filetype[60], dtfile[200]; char *pp, name[100], exec[200], icon[200]; char wildiconfile[200]; GError *gerror = 0; PIXBUF *pixbuf = 0; int ii, jj, uflag; int size = deficonsize; // default or last size set by user #define Ndirs 8 cchar *icondir[Ndirs] = { // compensate Linux chaos 6.3 "/usr/share/app-install/icons*", "/usr/share/pixmaps*", "/usr/share/icons/*64*", "/usr/share/icons/*48*", "/usr/share/icons/*32*", "/usr?share/icons/*scalable*", "/usr/local/share/icons*", "/usr/local/share/pixmaps*" }; if (! file) return; // drag motion event 6.4 #define Next 3 cchar *iconext[Next] = { "png", "svg", "xpm" }; // file extensions searched 6.3 if (NME == maxME) { zmessageACK(mWin,"capacity limit exceeded"); return; } me = NME; memset(&menus[me],0,sizeof(menuent)); // clear all data pp = strrchr(file,'.'); // look if .desktop file if (pp && strmatch(pp,".desktop")) { strncpy0(dtfile,file,200); // have a .desktop file file = 0; // and no data file } else // find .desktop file for given data file { snprintf(buff,200,"xdg-mime query filetype \"%s\"",file); // xdg-mime query filetype fid = popen(buff,"r"); if (! fid) { zmessageACK(mWin,strerror(errno)); return; } pp = fgets_trim(buff,200,fid); // should get major/minor file type pclose(fid); if (! pp) { zmessageACK(mWin,strerror(errno)); return; } strncpy0(filetype,pp,60); snprintf(buff,200,"xdg-mime query default %s",filetype); // zdg-mime query default fid = popen(buff,"r"); if (! fid) { zmessageACK(mWin,strerror(errno)); return; } pp = fgets_trim(buff,200,fid); // should get appname.desktop pclose(fid); if (! pp) { zmessageACK(mWin,strerror(errno)); return; } snprintf(dtfile,200,"/usr/share/applications/%s",pp); // /usr/share/applications/appname.desktop pp = strrchr(dtfile,'.'); if (! strmatch(pp,".desktop")) { zmessageACK(mWin,".desktop file not found"); return; } } fid = fopen(dtfile,"r"); // read .desktop file if (! fid) { zmessageACK(mWin,strerror(errno)); return; } *name = *exec = *icon = 0; while (true) { // save .desktop parameters pp = fgets_trim(buff,200,fid); if (! pp) break; if (strmatchN(buff,"Name=",5) && ! *name) // app name strncpy0(name,buff+5,100); if (strmatchN(buff,"Exec=",5) && ! *exec) // executable strncpy0(exec,buff+5,200); if (strmatchN(buff,"Icon=",5) && ! *icon) // usr/share/app-install/icons/*.png strncpy0(icon,buff+5,200); } fclose(fid); if (file) { pp = strrchr(file,'/'); // menu name = file base name if (! pp) pp = file; else pp++; menus[me].menu = zstrdup(pp); pp = strrchr(menus[me].menu,'.'); if (pp) *pp = 0; pp = strrchr(exec,'%'); // get rid of %f %u etc. if (pp) *pp = 0; snprintf(buff,200,"%s \"%s\"",exec,file); // menu function = executable + filename menus[me].func = zstrdup(buff); } else { menus[me].menu = zstrdup(name); // menu name = appname menus[me].func = zstrdup(exec); // menu function = executable } if (file && strmatchN(filetype,"image",5)) { // if image file, make icon from image pixbuf = gdk_pixbuf_new_from_file_at_size(file,size,size,&gerror); if (pixbuf) { menus[me].icon = zstrdup(file); menus[me].pixbuf = pixbuf; menus[me].size = size; menus[me].Fnewicon = 1; // 6.2 } } else if (*icon) // make icon from desktop file { if (*icon == '/') { // icon filespec is given 6.3 pp = icon; goto found; } for (ii = 0; ii < Ndirs; ii++) // search icon locations for (jj = 0; jj < Next; jj++) // try known icon file types { snprintf(wildiconfile,200,"%s/%s.%s",icondir[ii],icon,iconext[jj]); // 6.3 uflag = 1; pp = (char *) SearchWild(wildiconfile,uflag); if (pp) { uflag = 2; SearchWild(null,uflag); goto found; // break out 2 loops } } found: if (! pp) zmessageACK(mWin,"icon file not found: %s \n",icon); else { pixbuf = gdk_pixbuf_new_from_file_at_size(pp,size,size,&gerror); if (! pixbuf) zmessageACK(mWin,"icon file error: %s \n",pp); else { menus[me].icon = zstrdup(pp); menus[me].pixbuf = pixbuf; menus[me].size = size; menus[me].Fnewicon = 1; // 6.2 } } } menus[me].xpos = mpx; // position from mouse menus[me].ypos = mpy; menus[me].kill = 1; // popup window kill flag NME++; // incr. menu count Fchanged = 1; // mark menu revised gtk_widget_queue_draw(layout); // repaint window edit_menu(); // user can edit dropped menu return; } /******************************************************************************** Popup Menu GtkWidget *popup, *mitem cchar *label, *arg, *tip void func(GtkWidget *, cchar *arg) popup = create_popmenu() create a popup menu mitem = add_popmenu_item(popup, label, func, arg, tip) add menu item to popup menu popup_menu(GtkWidget *parent, popup) popup the menu at mouse position Call 'create_popmenu' and then 'add_popmenu_item' for each item in the menu. 'label' is the menu name, 'func' the response function, 'arg' an argument for 'func', and 'tip' is a tool-tip. 'arg' and 'tip' may be null. A call to 'popup_menu' will show all menu entries at the mouse position. Clicking an entry will call the respective response function. Hovering on the entry will show the tool-tip. The response function looks like this: void func(GtkWidget *, cchar *menu) ***/ // create a popup menu GtkWidget * create_popmenu() { int popmenu_event(GtkWidget *, GdkEvent *); GtkWidget *popmenu; popmenu = gtk_menu_new(); gtk_widget_add_events(popmenu,GDK_BUTTON_PRESS_MASK); G_SIGNAL(popmenu,"button-press-event",popmenu_event,0); return popmenu; } // handle mouse button event in a popup menu int popmenu_event(GtkWidget *popmenu, GdkEvent *event) { if (((GdkEventButton *) event)->button != 1) // if not left mouse, kill menu gtk_menu_popdown(GTK_MENU(popmenu)); return 0; } // add a menu item to a popup menu GtkWidget * add_popmenu_item(GtkWidget *popmenu, cchar *mname, cbFunc func, cchar *arg, cchar *mtip) { GtkWidget *wmitem; wmitem = gtk_menu_item_new_with_label(mname); if (mtip) g_object_set(G_OBJECT(wmitem),"tooltip-text",mtip,null); // add optional popup menu tip gtk_menu_shell_append(GTK_MENU_SHELL(popmenu),wmitem); if (func) { if (arg) G_SIGNAL(wmitem,"activate",func,arg); // call func with arg else G_SIGNAL(wmitem,"activate",func,mname); // call func with menu name } return wmitem; } // Show a popup menu at current mouse position // GtkWidget * argument is not used void popup_menu(GtkWidget *widget, GtkWidget *popmenu) // 6.6 { int px, py; if (mouse) { gdk_device_get_position(mouse,&screen,&px,&py); // offset popup menu from mouse 6.6 gdk_device_warp(mouse,screen,px+20,py-20); // wayland fails FIXME } #if GTK_CHECK_VERSION(3,22,0) gtk_menu_popup_at_pointer(GTK_MENU(popmenu),null); #else int time = gtk_get_current_event_time(); gtk_menu_popup(GTK_MENU(popmenu),0,0,0,0,1,time); #endif gtk_widget_show_all(popmenu); return; } /******************************************************************************** Vertical Menu / Toolbar Build a custom vertical menu and/or toolbar in a vertical packing box vbm = Vmenu_new(GtkWidget *vbox, float fgRGB[3], float bgRGB[3]) // create base menu Vmenu_add(vbm, name, icon, iww, ihh, desc, func, arg) // add menu item or toolbar button Vmenu *vbm cchar *name, *icon, *desc, *arg int iww, ihh void func(GtkWidget *, cchar *name) Vmenu_block(flag) int flag 1 to block menu, 0 to unblock 6.8 Create a vertical menu / toolbar in a vertical packing box. fgRGB and bgRGB are font and background colors, RGB scaled 0-1.0 6.8 Added items can have a menu name, icon, description, response function, and function argument. 'name' and 'icon' can be null but not both. name menu name icon menu icon, filespec for a .png file iww, ihh size of icon in menu display desc optional tool tip if mouse is hovered over displayed menu When 'name/icon' is clicked, 'func' is called with 'arg'. If 'arg' is null, 'name' is used instead. To create a menu entry that is a popup menu with multiple entries, do as follows: popup = create_popmenu(); add_popup_menu_item(popup ...); see create_popmenu() add_popup_menu_item(popup ...); ... Vmenu_add(vbm, name, icon, ww, hh, desc, create_popmenu, (cchar *) popup); i.e. use create_popmenu() as the response function and use the previously created menu 'popup' as the argument (cast to cchar *). ***/ namespace Vmenunames { #define margin 5 // margins for menu text PangoFontDescription *pfont1, *pfont2; PangoAttrList *pattrlist; PangoAttribute *pbackground; int fontheight; int Fblock = 0; void wpaint(GtkWidget *, cairo_t *, Vmenu *); // window repaint - draw event void mouse_event(GtkWidget *, GdkEventButton *, Vmenu *); // mouse event function void paint_menu(cairo_t *cr, Vmenu *vbm, int me, int hilite); // paint menu entry, opt. highlight } Vmenu *Vmenu_new(GtkWidget *vbox, float fgRGB[3], float bgRGB[3]) { using namespace Vmenunames; int cc, ww, hh; int K64 = 65536; char *menufont1, *menufont2; PangoLayout *playout; cc = sizeof(Vmenu); Vmenu *vbm = (Vmenu *) zmalloc(cc); memset(vbm,0,cc); vbm->fgRGB[0] = fgRGB[0]; // background color, RGB 0-1.0 6.8 vbm->fgRGB[1] = fgRGB[1]; vbm->fgRGB[2] = fgRGB[2]; vbm->bgRGB[0] = bgRGB[0]; // background color, RGB 0-1.0 6.8 vbm->bgRGB[1] = bgRGB[1]; vbm->bgRGB[2] = bgRGB[2]; vbm->vbox = vbox; vbm->topwin = gtk_widget_get_toplevel(vbox); vbm->layout = gtk_layout_new(0,0); vbm->mcount = 0; gtk_box_pack_start(GTK_BOX(vbox),vbm->layout,1,1,0); vbm->xmax = vbm->ymax = 10; // initial layout size pattrlist = pango_attr_list_new(); pbackground = pango_attr_background_new(K64*bgRGB[0],K64*bgRGB[1],K64*bgRGB[2]); pango_attr_list_change(pattrlist,pbackground); menufont1 = zstrdup(zfuncs::appfont); // set menu fonts, normal and bold 6.3 menufont2 = zstrdup(zfuncs::appboldfont); pfont1 = pango_font_description_from_string(menufont1); pfont2 = pango_font_description_from_string(menufont2); playout = gtk_widget_create_pango_layout(vbm->layout,0); pango_layout_set_font_description(playout,pfont1); pango_layout_set_text(playout,"Ayg",-1); pango_layout_get_pixel_size(playout,&ww,&hh); fontheight = hh; gtk_widget_add_events(vbm->layout,GDK_BUTTON_PRESS_MASK); gtk_widget_add_events(vbm->layout,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(vbm->layout,GDK_POINTER_MOTION_MASK); gtk_widget_add_events(vbm->layout,GDK_LEAVE_NOTIFY_MASK); G_SIGNAL(vbm->layout,"button-press-event",mouse_event,vbm); G_SIGNAL(vbm->layout,"button-release-event",mouse_event,vbm); G_SIGNAL(vbm->layout,"motion-notify-event",mouse_event,vbm); G_SIGNAL(vbm->layout,"leave-notify-event",mouse_event,vbm); G_SIGNAL(vbm->layout,"draw",wpaint,vbm); return vbm; } void Vmenu_add(Vmenu *vbm, cchar *name, cchar *icon, int iconww, int iconhh, cchar *desc, cbFunc func, cchar *arg) { using namespace Vmenunames; int me, cc, xpos, ww, hh; char iconpath[200], *mdesc, *name__; cchar *blanks = " "; // 20 blanks PIXBUF *pixbuf; GError *gerror = 0; PangoLayout *playout; PangoFontDescription *pfont; if (! name && ! icon) return; me = vbm->mcount++; // track no. menu entries if (name) vbm->menu[me].name = zstrdup(name); // create new menu entry from caller data if (icon) { vbm->menu[me].icon = zstrdup(icon); vbm->menu[me].iconww = iconww; vbm->menu[me].iconhh = iconhh; } if (desc) { // pad description with blanks for looks cc = strlen(desc); mdesc = (char *) zmalloc(cc+3); mdesc[0] = ' '; strcpy(mdesc+1,desc); strcpy(mdesc+cc+1," "); vbm->menu[me].desc = mdesc; } vbm->menu[me].func = func; vbm->menu[me].arg = name; // argument is menu name or arg if avail. if (arg) vbm->menu[me].arg = arg; vbm->menu[me].pixbuf = 0; if (icon) { // if icon is named, get pixbuf *iconpath = 0; strncatv(iconpath,199,zfuncs::zimagedir,"/",icon,null); // 6.8 pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,iconww,iconhh,1,&gerror); if (pixbuf) vbm->menu[me].pixbuf = pixbuf; else printz("Vmenu no icon: %s \n",iconpath); // 6.7 } if (me == 0) vbm->ymax = margin; // first menu, top position vbm->menu[me].iconx = 0; vbm->menu[me].icony = 0; vbm->menu[me].namex = 0; vbm->menu[me].namey = 0; if (icon) { vbm->menu[me].iconx = margin; // ______ vbm->menu[me].icony = vbm->ymax; // | | if (name) { // | icon | menu name vbm->menu[me].namex = margin + iconww + margin; // |______| vbm->menu[me].namey = vbm->ymax + (iconhh - fontheight) / 2; // 6.3 } vbm->menu[me].ylo = vbm->ymax; vbm->ymax += iconhh + iconhh / 8; // position for next menu entry vbm->menu[me].yhi = vbm->ymax; if (margin + iconww > vbm->xmax) vbm->xmax = margin + iconww; // keep track of max. layout width } else if (name) { vbm->menu[me].namex = margin; // menu name vbm->menu[me].namey = vbm->ymax; vbm->menu[me].ylo = vbm->ymax; vbm->ymax += fontheight; vbm->menu[me].yhi = vbm->ymax; } vbm->menu[me].playout1 = gtk_widget_create_pango_layout(vbm->layout,0); vbm->menu[me].playout2 = gtk_widget_create_pango_layout(vbm->layout,0); if (name) { xpos = vbm->menu[me].namex; cc = strlen(name); // menu name with trailing blanks 6.3 name__ = zstrdup(name,22); // (long enough to overwrite bold name) strncpy0(name__+cc,blanks,20); playout = vbm->menu[me].playout1; // normal font pfont = pfont1; pango_layout_set_attributes(playout,pattrlist); pango_layout_set_font_description(playout,pfont); pango_layout_set_text(playout,name__,-1); // compute layout pango_layout_get_pixel_size(playout,&ww,&hh); // pixel width and height of layout playout = vbm->menu[me].playout2; // bold font pfont = pfont2; pango_layout_set_attributes(playout,pattrlist); pango_layout_set_font_description(playout,pfont); pango_layout_set_text(playout,name,-1); // compute layout pango_layout_get_pixel_size(playout,&ww,&hh); // pixel width and height of layout if (xpos + ww > vbm->xmax) vbm->xmax = xpos + ww; // keep track of max. layout width } gtk_widget_set_size_request(vbm->layout,vbm->xmax+margin,0); // add right margin to layout width return; } // block or unblock menu void Vmenu_block(int flag) // 6.8 { using namespace Vmenunames; Fblock = flag; return; } // paint window when created, exposed, resized void Vmenunames::wpaint(GtkWidget *widget, cairo_t *cr, Vmenu *vbm) { using namespace Vmenunames; cairo_set_source_rgb(cr,vbm->bgRGB[0],vbm->bgRGB[1],vbm->bgRGB[2]); // background 6.8 cairo_paint(cr); for (int me = 0; me < vbm->mcount; me++) // paint all menu entries paint_menu(cr,vbm,me,0); return; } // draw menu icon and text into layout void Vmenunames::paint_menu(cairo_t *cr, Vmenu *vbm, int me, int hilite) { using namespace Vmenunames; PIXBUF *pixbuf; PangoLayout *playout; int xpos, ypos; int iconww, iconhh; cchar *name; pixbuf = vbm->menu[me].pixbuf; // icon if (pixbuf) { // draw menu icon at menu position xpos = vbm->menu[me].iconx; ypos = vbm->menu[me].icony; iconww = vbm->menu[me].iconww; iconhh = vbm->menu[me].iconhh; if (! hilite) { // erase box around icon cairo_set_source_rgb(cr,vbm->bgRGB[0],vbm->bgRGB[1],vbm->bgRGB[2]); // background 6.8 cairo_rectangle(cr,xpos-1,ypos-1,iconww+2,iconhh+2); cairo_fill(cr); } gdk_cairo_set_source_pixbuf(cr,pixbuf,xpos,ypos); // draw icon cairo_paint(cr); if (hilite) { cairo_set_source_rgb(cr,vbm->fgRGB[0],vbm->fgRGB[1],vbm->fgRGB[2]); // draw box around icon cairo_set_line_width(cr,1); cairo_set_line_join(cr,CAIRO_LINE_JOIN_ROUND); cairo_rectangle(cr,xpos,ypos,iconww,iconhh); cairo_stroke(cr); } } name = vbm->menu[me].name; // menu text if (name) { // draw menu text at menu position xpos = vbm->menu[me].namex; ypos = vbm->menu[me].namey; cairo_move_to(cr,xpos,ypos); // draw layout with text cairo_set_source_rgb(cr,vbm->fgRGB[0],vbm->fgRGB[1],vbm->fgRGB[2]); if (hilite) playout = vbm->menu[me].playout2; else playout = vbm->menu[me].playout1; pango_cairo_show_layout(cr,playout); } return; } // mouse event function - capture buttons and drag movements void Vmenunames::mouse_event(GtkWidget *widget, GdkEventButton *event, Vmenu *vbm) { using namespace Vmenunames; GdkWindow *gdkwin; GtkWidget *topwin; cchar *desc; int me, mpx, mpy, button, ww, ylo, yhi; static int me0 = -1, Fmyclick = 0; static draw_context_t context; static cairo_t *cr = 0; if (! cr) { // create once and leave active 6.6 gdkwin = gtk_layout_get_bin_window(GTK_LAYOUT(widget)); cr = draw_context_create(gdkwin,context); } mpx = int(event->x); // mouse position mpy = int(event->y); button = event->button; if (event->type == GDK_MOTION_NOTIFY) // mouse inside layout { topwin = vbm->topwin; for (me = 0; me < vbm->mcount; me++) { // find menu where mouse is ylo = vbm->menu[me].ylo; yhi = vbm->menu[me].yhi; if (mpy < ylo) continue; if (mpy < yhi) break; } if (me != me0 && me0 >= 0) { // unhilight prior paint_menu(cr,vbm,me0,0); desc = vbm->menu[me0].desc; // remove prior tool tip if (desc) poptext_mouse(0,0,0,0,0,0); me0 = -1; } if (me == me0) return; // same as before if (me == vbm->mcount) return; // no new menu match paint_menu(cr,vbm,me,1); // hilight menu entry at mouse desc = vbm->menu[me].desc; // show tool tip if (desc) poptext_mouse(topwin,desc,30,20,0,10); me0 = me; // remember last match return; } if (me0 >= 0) // mouse left layout { paint_menu(cr,vbm,me0,0); // unhilight prior desc = vbm->menu[me0].desc; // remove prior tool tip if (desc) poptext_mouse(0,0,0,0,0,0); me0 = -1; } if (event->type == GDK_BUTTON_PRESS) // menu entry clicked Fmyclick = 1; // button click is mine 6.8 if (event->type == GDK_BUTTON_RELEASE) // menu entry clicked { if (Fblock) return; // menu is blocked 6.8 if (! Fmyclick) return; // ignore unmatched button release 6.8 Fmyclick = 0; // (from vanished popup window) for (me = 0; me < vbm->mcount; me++) { // look for clicked menu entry ylo = vbm->menu[me].ylo; yhi = vbm->menu[me].yhi; if (mpy < ylo) continue; if (mpy < yhi) break; } if (me == vbm->mcount) return; // no menu match zfuncs::vmenuclickbutton = button; // 1/2/3 = left/mid/right button ww = vbm->menu[me].iconww; // get horiz. click posn. on menu icon if (ww) mpx = 100 * (mpx - margin) / ww; // scale 0-100 else mpx = 0; if (mpx < 0) mpx = 0; if (mpx > 100) mpx = 100; zfuncs::vmenuclickposn = mpx; paint_menu(cr,vbm,me,0); // unhighlight menu if (vbm->menu[me].func) (vbm->menu[me].func)(widget,vbm->menu[me].arg); // caller function(widget,arg) } return; } /******************************************************************************** simplified GTK dialog functions create a dialog with response buttons (OK, cancel ...) add widgets to dialog (button, text entry, combo box ...) put data into widgets (text or numeric data) run the dialog, get user inputs (button press, text entry, checkbox selection ...) run caller event function when widgets change from user inputs run caller event function when dialog is completed or canceled get dialog completion status (OK, cancel, destroyed ...) get data from dialog widgets (the user inputs) destroy the dialog and free memory *********************************************************************************/ // private functions for widget events and dialog completion int zdialog_widget_event(GtkWidget *, zdialog *zd); int zdialog_delete_event(GtkWidget *, GdkEvent *, zdialog *zd); int zdialog_zspin_event(GtkWidget *, GdkEvent *, zdialog *zd); // "zspin" widget 6.7 // create a new zdialog dialog // The title and parent arguments may be null. // optional arguments: up to zdmaxbutts button labels followed by null // returned dialog status: +N = button N (1 to zdmaxbutts) // <0 = [x] button or other GTK destroy action // completion buttons are also events like other widgets // all dialogs run parallel, use zdialog_wait() if needed // The status returned by zdialog_wait() corresponds to the button // (1-10) used to end the dialog. Status < 0 indicates the [x] button // was used or the dialog was killed by the program itself. zdialog * zdialog_new(cchar *title, GtkWidget *parent, ...) // parent added { zdialog *zd; GtkWidget *dialog, *hbox, *vbox, *butt, *hsep; cchar *bulab[zdmaxbutts]; int cc, ii, nbu; va_list arglist; static int uniqueID = 1; va_start(arglist,parent); for (nbu = 0; nbu < zdmaxbutts; nbu++) // get completion buttons { bulab[nbu] = va_arg(arglist, cchar *); if (! bulab[nbu]) break; } va_end(arglist); if (! title) title = "no title"; dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); // use window, not dialog gtk_window_set_title(GTK_WINDOW(dialog),title); vbox = gtk_box_new(VERTICAL,0); // vertical packing box gtk_container_add(GTK_CONTAINER(dialog),vbox); // add to main window gtk_box_set_spacing(GTK_BOX(vbox),2); gtk_container_set_border_width(GTK_CONTAINER(vbox),5); // 6.0 cc = sizeof(zdialog); // allocate zdialog zd = (zdialog *) zmalloc(cc); if (zdialog_count == zdialog_max) { // add to active list for (ii = 0; ii < zdialog_count; ii++) printz("dialog: %s \n",zdialog_list[ii]->widget[0].data); zappcrash("max. zdialogs exceeded"); } zdialog_list[zdialog_count] = zd; zdialog_count++; if (parent) gtk_window_set_transient_for(GTK_WINDOW(dialog),GTK_WINDOW(parent)); if (nbu) // completion buttons { hbox = gtk_box_new(HORIZONTAL,2); // add hbox for buttons at bottom gtk_box_pack_end(GTK_BOX(vbox),hbox,0,0,2); hsep = gtk_separator_new(HORIZONTAL); // add separator line gtk_box_pack_end(GTK_BOX(vbox),hsep,0,0,3); for (ii = nbu-1; ii >= 0; ii--) { // add buttons to hbox butt = gtk_button_new_with_label(bulab[ii]); // reverse order nbu-1...0 gtk_box_pack_end(GTK_BOX(hbox),butt,0,0,2); G_SIGNAL(butt,"clicked",zdialog_widget_event,zd); // connect to event function zd->compwidget[ii] = butt; // save button widgets zd->compbutton[ii] = bulab[ii]; // and button labels } } zd->compbutton[nbu] = 0; // mark EOL // compensate GTK broken API - entry widgets automatically 150 pixels wide even if expand=0 6.2 gtk_window_set_default_size(GTK_WINDOW(dialog),10,10); zd->dialog = dialog; // dialog window zd->title = zstrdup(title); // dialog title 6.8 zd->parent = parent; // parent window or null zd->sentinel1 = zdsentinel | (lrandz() & 0x0000FFFF); // validity sentinels zd->sentinel2 = zd->sentinel1; // fixed part + random part zd->uniqueID = uniqueID++; // increment unique ID 6.2 zd->eventCB = 0; // no user event callback function zd->zstat = 0; // no zdialog status zd->disabled = 1; // widget signals disabled zd->saveposn = 0; // position not saved zd->widget[0].type = "dialog"; // set up 1st widget = dialog zd->widget[0].name = "dialog"; zd->widget[0].pname = 0; zd->widget[0].data = zstrdup(title); zd->widget[0].cblist = 0; zd->widget[0].widget = dialog; zd->widget[1].type = 0; // eof - no contained widgets yet return zd; } // change a zdialog title void zdialog_set_title(zdialog *zd, cchar *title) { gtk_window_set_title(GTK_WINDOW(zd->widget[0].widget),title); return; } // set a zdialog to be modal void zdialog_set_modal(zdialog *zd) // 6.1 { GtkWidget *widget = zdialog_widget(zd,"dialog"); gtk_window_set_modal(GTK_WINDOW(widget),1); gtk_window_set_keep_above(GTK_WINDOW(widget),1); return; } // set a zdialog to be decorated or not void zdialog_set_decorated(zdialog *zd, int decorated) // 6.1 { void zdialog_drag(GtkWidget *widget, GdkEventButton *event, void *); GtkWidget *widget; widget = zdialog_widget(zd,"dialog"); gtk_window_set_decorated(GTK_WINDOW(widget),decorated); if (decorated) return; gtk_widget_add_events(widget,GDK_BUTTON_PRESS_MASK); gtk_widget_add_events(widget,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(widget,GDK_POINTER_MOTION_MASK); G_SIGNAL(widget,"button-press-event",zdialog_drag,0); // connect mouse events to drag G_SIGNAL(widget,"button-release-event",zdialog_drag,0); // undecorated window G_SIGNAL(widget,"motion-notify-event",zdialog_drag,0); return; } void zdialog_drag(GtkWidget *widget, GdkEventButton *event, void *) // 6.1 { static int bdown = 0, type; static int mx0, my0, mx, my; static int wx0, wy0, wx, wy; type = event->type; gdk_device_get_position(zfuncs::mouse,0,&mx,&my); // mouse position in monitor if (type == GDK_BUTTON_PRESS) { bdown = 1; mx0 = mx; // drag start my0 = my; gtk_window_get_position(GTK_WINDOW(widget),&wx0,&wy0); // initial window position } if (type == GDK_BUTTON_RELEASE) bdown = 0; if (type == GDK_MOTION_NOTIFY) { if (! bdown) return; wx = wx0 + mx - mx0; wy = wy0 + my - my0; gtk_window_move(GTK_WINDOW(widget),wx,wy); } return; } // present a zdialog (visible and on top) void zdialog_present(zdialog *zd) { GtkWidget *widget = zdialog_widget(zd,"dialog"); gtk_window_present(GTK_WINDOW(widget)); ///gtk_window_set_keep_above(GTK_WINDOW(widget),1); // help wayland 6.8 return; } // add widget to existing zdialog // // Arguments after parent are optional and default to 0. // zd zdialog *, created with zdialog_new() // type string, one of the widget types listed below // name string, widget name, used to stuff or fetch widget data // parent string, parent name: "dialog" or a previously added container widget // data string, initial data for widget (label name, entry string, spin value, etc.) // size cc for text entry or pixel size for image widget // homog for hbox or vbox to make even space allocation for contained widgets // expand widget should expand with dialog box expansion // space extra space between this widget and neighbors, pixels // wrap allow text to wrap at right margin // // data can be a string ("initial widget data") or a number in string form ("12.345") // data for togbutt / check / radio: use "0" or "1" for OFF or ON // data for spin / zspin / hscale / vscale: use "min|max|step|value" (default: 0 | 100 | 1 | 50) // data for colorbutt: use "rrr|ggg|bbb" with values 0-255 for each RGB color. // This format is used to initialize the control and read back when user selects a color. // Multiple radio buttons with same parent are a group: pressing one turns the others OFF. int zdialog_add_widget ( zdialog *zd, cchar *type, cchar *name, cchar *pname, // mandatory args cchar *data, int size, int homog, int expand, int space, int wrap) // optional args (default = 0) { GtkWidget *widget = 0, *pwidget = 0, *fwidget = 0; GtkWidget *entry, *image, *vbox; GtkTextBuffer *editBuff = 0; PIXBUF *pixbuf = 0; GdkRGBA gdkrgba; GError *gerror = 0; cchar *pp, *ptype = 0; char vdata[30], iconpath[200]; double min, max, step, val; double f256 = 1.0 / 256.0; int iiw, iip, kk, err; if (! zdialog_valid(zd)) zappcrash("zdialog invalid"); for (iiw = 1; zd->widget[iiw].type; iiw++); // find next avail. slot if (iiw > zdmaxwidgets-2) zappcrash("too many widgets: %d",iiw); zd->widget[iiw].type = zstrdup(type); // initz. widget struct zd->widget[iiw].name = zstrdup(name); zd->widget[iiw].pname = zstrdup(pname); zd->widget[iiw].data = 0; zd->widget[iiw].cblist = 0; zd->widget[iiw].size = size; zd->widget[iiw].homog = homog; zd->widget[iiw].expand = expand; zd->widget[iiw].space = space; zd->widget[iiw].wrap = wrap; zd->widget[iiw].widget = 0; zd->widget[iiw+1].type = 0; // set new EOF marker if (strmatchV(type,"dialog","hbox","vbox","hsep","vsep","frame","scrwin", // added 'z' widgets 6.7 "label","link","entry","zentry","edit","text", "button","zbutton","togbutt","check","radio", "imagebutt","colorbutt","combo","comboE","spin","zspin", "hscale","vscale","icon","image",null) == 0) { printz("*** zdialog, bad widget type: %s \n",type); return 0; } for (iip = iiw-1; iip >= 0; iip--) // find parent (container) widget if (strmatch(pname,zd->widget[iip].name)) break; if (iip < 0) zappcrash("zdialog, no parent for widget: %s",name); pwidget = zd->widget[iip].widget; // parent widget, type ptype = zd->widget[iip].type; if (strmatchV(ptype,"dialog","hbox","vbox","frame","scrwin",null) == 0) zappcrash("zdialog, bad widget parent type: %s",ptype); if (strmatch(type,"hbox")) widget = gtk_box_new(HORIZONTAL,space); // expandable container boxes if (strmatch(type,"vbox")) widget = gtk_box_new(VERTICAL,space); if (strstr("hbox vbox",type)) gtk_box_set_homogeneous(GTK_BOX(widget),homog); if (strmatch(type,"hsep")) widget = gtk_separator_new(HORIZONTAL); // horiz. & vert. separators if (strmatch(type,"vsep")) widget = gtk_separator_new(VERTICAL); if (strmatch(type,"frame")) { // frame around contained widgets widget = gtk_frame_new(data); gtk_frame_set_shadow_type(GTK_FRAME(widget),GTK_SHADOW_IN); data = 0; } if (strmatch(type,"scrwin")) { // scrolled window container widget = gtk_scrolled_window_new(0,0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); #if GTK_CHECK_VERSION(3,16,0) gtk_scrolled_window_set_overlay_scrolling(GTK_SCROLLED_WINDOW(widget),0); // 6.5 #endif data = 0; } if (strmatch(type,"label")) { // label (static text) widget = gtk_label_new(data); if (size) gtk_label_set_width_chars(GTK_LABEL(widget),size); // 6.2 if (data && strstr(data,"> link G_SIGNAL(widget,"clicked",zdialog_widget_event,zd); data = 0; } if (strmatch(type,"entry")) { // text input, single line widget = gtk_entry_new(); if (data) gtk_entry_set_text(GTK_ENTRY(widget),data); if (size) gtk_entry_set_width_chars(GTK_ENTRY(widget),size); G_SIGNAL(widget,"changed",zdialog_widget_event,zd); } if (strmatch(type,"zentry")) { // text input, single line 6.7 widget = gtk_text_view_new(); #if GTK_CHECK_VERSION(3,18,0) gtk_text_view_set_top_margin(GTK_TEXT_VIEW(widget),2); gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(widget),2); #endif gtk_text_view_set_left_margin(GTK_TEXT_VIEW(widget),5); if (! size) size = 10; // scale widget for font size gtk_widget_set_size_request(widget,size*appfontsize,2*appfontsize); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),1); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_NONE); gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(widget),0); editBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); if (data) gtk_text_buffer_set_text(editBuff,data,-1); G_SIGNAL(editBuff,"changed",zdialog_widget_event,zd); // buffer signals, not widget } if (strmatch(type,"edit")) { // text input, opt. multi-line widget = gtk_text_view_new(); #if GTK_CHECK_VERSION(3,18,0) gtk_text_view_set_top_margin(GTK_TEXT_VIEW(widget),2); gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(widget),2); #endif gtk_text_view_set_left_margin(GTK_TEXT_VIEW(widget),5); if (! size) size = 10; // scale widget for font size 6.6 gtk_widget_set_size_request(widget,size*appfontsize,2*appfontsize); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),1); if (wrap) gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_WORD); gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(widget),0); editBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); if (data) gtk_text_buffer_set_text(editBuff,data,-1); G_SIGNAL(editBuff,"changed",zdialog_widget_event,zd); // buffer signals, not widget } if (strmatch(type,"text")) { // text output (not editable) widget = gtk_text_view_new(); #if GTK_CHECK_VERSION(3,18,0) gtk_text_view_set_top_margin(GTK_TEXT_VIEW(widget),2); // 6.5 gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(widget),2); #endif gtk_text_view_set_left_margin(GTK_TEXT_VIEW(widget),3); // 6.1 editBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); if (data) gtk_text_buffer_set_text(editBuff,data,-1); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),0); if (wrap) gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_WORD); } if (strmatch(type,"button")) { // button widget = gtk_button_new_with_label(data); G_SIGNAL(widget,"clicked",zdialog_widget_event,zd); data = 0; } if (strmatch(type,"zbutton")) { // checkbox used as small button 6.7 if (data) widget = gtk_check_button_new_with_label(data); else widget = gtk_check_button_new(); G_SIGNAL(widget,"toggled",zdialog_widget_event,zd); data = "0"; // default data } if (strmatch(type,"togbutt")) { // toggle button widget = gtk_toggle_button_new_with_label(data); G_SIGNAL(widget,"toggled",zdialog_widget_event,zd); data = "0"; // default data } if (strmatch(type,"imagebutt")) { // button with image 6.0 snprintf(iconpath,200,"%s/%s",get_zimagedir(),data); // 6.8 data = 0; pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,size,size,1,&gerror); if (pixbuf) { image = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(pixbuf); } else image = gtk_image_new_from_icon_name("missing",GTK_ICON_SIZE_BUTTON); widget = gtk_button_new_with_label(data); gtk_button_set_image(GTK_BUTTON(widget),image); G_SIGNAL(widget,"clicked",zdialog_widget_event,zd); } if (strmatch(type,"colorbutt")) { // color edit button if (! data) data = "0|0|0"; // data format: "nnn|nnn|nnn" = RGB pp = strField(data,'|',1); gdkrgba.red = f256 * atoi(pp); // RGB values are 0-1 pp = strField(data,'|',2); gdkrgba.green = f256 * atoi(pp); pp = strField(data,'|',3); gdkrgba.blue = f256 * atoi(pp); gdkrgba.alpha = 1.0; widget = gtk_color_button_new_with_rgba(&gdkrgba); G_SIGNAL(widget,"color-set",zdialog_widget_event,zd); } if (strmatch(type,"check")) { // checkbox if (data) widget = gtk_check_button_new_with_label(data); else widget = gtk_check_button_new(); G_SIGNAL(widget,"toggled",zdialog_widget_event,zd); data = "0"; // default data } if (strmatch(type,"combo")) { // combo box widget = gtk_combo_box_text_new(); zd->widget[iiw].cblist = pvlist_create(zdcbmax); // for drop-down list if (! blank_null(data)) { pvlist_append(zd->widget[iiw].cblist,data); // add data to drop-down list gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget),data); gtk_combo_box_set_active(GTK_COMBO_BOX(widget),0); } G_SIGNAL(widget,"changed",zdialog_widget_event,zd); } if (strmatch(type,"comboE")) { // combo box with entry box widget = gtk_combo_box_text_new_with_entry(); zd->widget[iiw].cblist = pvlist_create(zdcbmax); // for drop-down list if (! blank_null(data)) { entry = gtk_bin_get_child(GTK_BIN(widget)); gtk_entry_set_text(GTK_ENTRY(entry),data); // entry = initial data pvlist_append(zd->widget[iiw].cblist,data); // add data to drop-down list gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget),data); } G_SIGNAL(widget,"changed",zdialog_widget_event,zd); } if (strmatch(type,"radio")) { // radio button for (kk = iip+1; kk <= iiw; kk++) if (strmatch(zd->widget[kk].pname,pname) && // find first radio button strmatch(zd->widget[kk].type,"radio")) break; // with same container if (kk == iiw) widget = gtk_radio_button_new_with_label(null,data); // this one is first else widget = gtk_radio_button_new_with_label_from_widget // not first, add to group (GTK_RADIO_BUTTON(zd->widget[kk].widget),data); G_SIGNAL(widget,"toggled",zdialog_widget_event,zd); data = "0"; // default data } if (strmatchV(type,"spin","hscale","vscale",null)) { // spin button or sliding scale if (! data) zappcrash("zdialog_add_widget(): data missing"); // "min|max|step|value" pp = strField(data,'|',1); err = convSD(pp,min); pp = strField(data,'|',2); err += convSD(pp,max); pp = strField(data,'|',3); err += convSD(pp,step); pp = strField(data,'|',4); err += convSD(pp,val); if (err) { min = 0; max = 100; step = 1; val = 50; } if (*type == 's') { widget = gtk_spin_button_new_with_range(min,max,step); gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),val); } if (*type == 'h') { widget = gtk_scale_new_with_range(HORIZONTAL,min,max,step); gtk_range_set_value(GTK_RANGE(widget),val); gtk_scale_set_draw_value(GTK_SCALE(widget),0); } if (*type == 'v') { widget = gtk_scale_new_with_range(VERTICAL,min,max,step); gtk_range_set_value(GTK_RANGE(widget),val); gtk_scale_set_draw_value(GTK_SCALE(widget),0); } G_SIGNAL(widget,"value-changed",zdialog_widget_event,zd); snprintf(vdata,30,"%g",val); data = vdata; } if (strmatch(type,"zspin")) { // "zspin" widget with range 6.7 if (! data) zappcrash("zdialog_add_widget(): data missing"); // "min|max|step|value" pp = strField(data,'|',1); err = convSD(pp,min); pp = strField(data,'|',2); err += convSD(pp,max); pp = strField(data,'|',3); err += convSD(pp,step); pp = strField(data,'|',4); err += convSD(pp,val); if (err) { min = 0; max = 100; step = 1; val = 50; } zd->widget[iiw].lolim = min; zd->widget[iiw].hilim = max; zd->widget[iiw].step = step; err = convDS(val,6,vdata); // initial value >> text 6.8 data = vdata; widget = gtk_text_view_new(); // GTK widget is text_view #if GTK_CHECK_VERSION(3,18,0) gtk_text_view_set_top_margin(GTK_TEXT_VIEW(widget),2); #endif gtk_text_view_set_left_margin(GTK_TEXT_VIEW(widget),5); if (! size) size = 4; // scale widget for font size gtk_widget_set_size_request(widget,size*appfontsize,2*appfontsize); gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),1); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_NONE); gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(widget),0); gtk_text_view_set_input_purpose(GTK_TEXT_VIEW(widget),GTK_INPUT_PURPOSE_NUMBER); editBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_set_text(editBuff,data,-1); G_SIGNAL(widget,"key-press-event",zdialog_zspin_event,zd); G_SIGNAL(widget,"focus-out-event",zdialog_zspin_event,zd); gtk_widget_add_events(widget,GDK_SCROLL_MASK); G_SIGNAL(widget,"scroll-event",zdialog_zspin_event,zd); } if (strmatch(type,"icon")) { // image widget from icon snprintf(iconpath,200,"%s/%s",get_zimagedir(),data); // 6.8 data = 0; // data not further used pixbuf = gdk_pixbuf_new_from_file_at_scale(iconpath,size,size,1,&gerror); if (pixbuf) { widget = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(pixbuf); } else widget = gtk_image_new_from_icon_name("missing",GTK_ICON_SIZE_BUTTON); } if (strmatch(type,"image")) // image widget from pixbuf 6.5 widget = gtk_image_new_from_pixbuf((GdkPixbuf *) data); // use (cchar *) pixbuf in call // all widget types come here zd->widget[iiw].widget = widget; // set widget in zdialog if (strmatchV(type,"zentry","zspin","edit","text",0)) { // add frame around these widgets 6.7 fwidget = gtk_frame_new(0); gtk_frame_set_shadow_type(GTK_FRAME(fwidget),GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(fwidget),widget); widget = fwidget; } if (strmatch(ptype,"hbox") || strmatch(ptype,"vbox")) // add to hbox/vbox gtk_box_pack_start(GTK_BOX(pwidget),widget,expand,expand,space); if (strmatch(ptype,"frame")) // add to frame gtk_container_add(GTK_CONTAINER(pwidget),widget); if (strmatch(ptype,"scrwin")) // add to scroll window gtk_container_add(GTK_CONTAINER(pwidget),widget); if (strmatch(ptype,"dialog")) { // add to dialog box vbox = gtk_bin_get_child(GTK_BIN(pwidget)); // dialog is a gtkwindow gtk_box_pack_start(GTK_BOX(vbox),widget,expand,expand,space); } if (data) zd->widget[iiw].data = zstrdup(data); // widget memory return 0; } // add widget to existing zdialog - alternative form (clearer and easier code) // options: "size=nn | homog | expand | space=nn | wrap" (all optional, any order) int zdialog_add_widget(zdialog *zd, cchar *type, cchar *name, cchar *parent, cchar *data, cchar *options) { int stat, size = 0, homog = 0, expand = 0, space = 0, wrap = 0, begin = 1; char pname[8]; double pval; while (true) { stat = strParms(begin,options,pname,8,pval); if (stat == -1) break; if (stat == 1) zappcrash("bad zdialog options: %s",options); if (strmatch(pname,"size")) size = (int(pval)); else if (strmatch(pname,"homog")) homog = 1; else if (strmatch(pname,"expand")) expand = 1; else if (strmatch(pname,"space")) space = (int(pval)); else if (strmatch(pname,"wrap")) wrap = 1; else zappcrash("bad zdialog options: %s",options); } stat = zdialog_add_widget(zd,type,name,parent,data,size,homog,expand,space,wrap); return stat; } // return 1/0 if zdialog is valid/invalid int zdialog_valid(zdialog *zd, int errmess) // 6.0 { int ok, ii; if (! zd) return 0; for (ii = 0; ii < zdialog_count; ii++) // find in valid zdialog list if (zd == zdialog_list[ii]) break; if (ii == zdialog_count) return 0; ok = 1; if ((zd->sentinel1 & 0xFFFF0000) != zdsentinel) ok = 0; if (zd->sentinel2 != zd->sentinel1) ok = 0; if (! ok) { printz("*** zdialog sentinel invalid %p \n",zd); return 0; } return 1; } // get GTK widget from zdialog and widget name GtkWidget * zdialog_widget(zdialog *zd, cchar *name) { if (! zdialog_valid(zd)) return 0; for (int ii = 0; zd->widget[ii].type; ii++) if (strmatch(zd->widget[ii].name,name)) return zd->widget[ii].widget; return 0; } // set an "image" widget type from a GDK pixbuf // returns 0 if OK, else +N int zdialog_set_image(zdialog *zd, cchar *name, GdkPixbuf *pixbuf) // 6.5 { GtkWidget *widget; int ii; if (! zdialog_valid(zd)) return 1; for (ii = 0; zd->widget[ii].type; ii++) if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 2; if (! strmatch(zd->widget[ii].type,"image")) return 3; widget = zd->widget[ii].widget; gtk_image_set_from_pixbuf(GTK_IMAGE(widget),pixbuf); return 0; } // add a popup tool tip to a zdialog widget int zdialog_add_ttip(zdialog *zd, cchar *wname, cchar *ttip) { GtkWidget *widget; int ii; if (! zdialog_valid(zd)) return 0; for (ii = 0; zd->compwidget[ii]; ii++) // search completion buttons if (strmatch(zd->compbutton[ii],wname)) { // for matching name gtk_widget_set_tooltip_text(zd->compwidget[ii],ttip); return 1; } widget = zdialog_widget(zd,wname); // search zdialog widgets if (! widget) { printz(" *** zdialog widget invalid %s \n",wname); return 0; } gtk_widget_set_tooltip_text(widget,ttip); return 1; } // set a common group for a set of radio buttons // (GTK, this does not work) int zdialog_set_group(zdialog *zd, cchar *radio1, ...) { va_list arglist; cchar *radio2; GtkWidget *gwidget, *widget; GSList *glist; gwidget = zdialog_widget(zd,radio1); glist = gtk_radio_button_get_group(GTK_RADIO_BUTTON(gwidget)); if (! glist) zappcrash("no radio button group"); va_start(arglist,radio1); while (true) { radio2 = va_arg(arglist,cchar *); if (! radio2) break; widget = zdialog_widget(zd,radio2); gtk_radio_button_set_group(GTK_RADIO_BUTTON(widget),glist); } va_end(arglist); return 0; } // resize dialog to a size greater than initial size // (as determined by the included widgets) int zdialog_resize(zdialog *zd, int width, int height) { if (! zdialog_valid(zd)) return 0; if (! width) width = 10; // stop spurious GTK warnings 6.1 if (! height) height = 10; GtkWidget *window = zd->widget[0].widget; gtk_window_set_default_size(GTK_WINDOW(window),width,height); return 1; } // put data into a zdialog widget // private function int zdialog_put_data(zdialog *zd, cchar *name, cchar *data) { GtkWidget *widget, *entry; GtkTextBuffer *textBuff; GdkRGBA gdkrgba; int iiw, nn, kk, err, Nsteps; cchar *type, *pp; char *wdata, sdata[12]; double dval; double f256 = 1.0 / 256.0; float lval, hval, nval, F, F2; float fdata, lolim, hilim, step; if (! zdialog_valid(zd)) return 0; if (! name || ! *name) zappcrash("zdialog_put_data(), name null"); for (iiw = 1; zd->widget[iiw].type; iiw++) // find widget if (strmatch(zd->widget[iiw].name,name)) break; if (! zd->widget[iiw].type) { printz("*** zdialog_put_data(), bad name %s \n",name); return 0; } type = zd->widget[iiw].type; widget = zd->widget[iiw].widget; wdata = zd->widget[iiw].data; if (wdata) zfree(wdata); // free prior data memory zd->widget[iiw].data = 0; if (data) { if (utf8_check(data)) wdata = zstrdup("bad UTF8 data"); // replace bad UTF-8 encoding 6.4 else wdata = zstrdup(data); // set new data for widget zd->widget[iiw].data = wdata; } zd->disabled++; // disable for widget stuffing if (strmatch(type,"label")) gtk_label_set_text(GTK_LABEL(widget),data); if (strmatch(type,"link")) gtk_label_set_text(GTK_LABEL(widget),data); if (strmatch(type,"entry")) gtk_entry_set_text(GTK_ENTRY(widget),data); if (strmatch(type,"zentry")) { // text input, single line 6.7 textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_set_text(textBuff,data,-1); } if (strmatchV(type,"button","zbutton",null)) // change button label 6.7 gtk_button_set_label(GTK_BUTTON(widget),data); if (strmatch(type,"edit")) { // text input to editable text textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_set_text(textBuff,data,-1); } if (strmatch(type,"text")) { // text output textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_set_text(textBuff,data,-1); } if (strmatchV(type,"togbutt","check","radio",null)) { if (! data) kk = nn = 0; else kk = convSI(data,nn); if (kk != 0) nn = 0; // data not integer, force zero if (nn <= 0) nn = 0; else nn = 1; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),nn); // set gtk widget value } if (strmatch(type,"spin")) { kk = convSD(data,dval); if (kk != 0) dval = 0.0; gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget),dval); } if (strmatch(type,"zspin")) { // "zspin" widget 6.7 lolim = zd->widget[iiw].lolim; hilim = zd->widget[iiw].hilim; step = zd->widget[iiw].step; err = convSF(data,fdata); // string --> float if (err) goto retx; Nsteps = (fdata - lolim) / step + 0.5; // nearest exact step 6.8 fdata = lolim + Nsteps * step; if (fdata < lolim) fdata = lolim; // enforce limits if (fdata > hilim) fdata = hilim; convDS(fdata,6,sdata); // float --> string, precision 6 6.8 textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_set_text(textBuff,sdata,-1); } if (strmatch(type,"colorbutt")) { // color button data is nnn|nnn|nnn pp = strField(data,'|',1); if (pp) gdkrgba.red = f256 * atoi(pp); // RGB range is 0-1 pp = strField(data,'|',2); if (pp) gdkrgba.green = f256 * atoi(pp); pp = strField(data,'|',3); if (pp) gdkrgba.blue = f256 * atoi(pp); gdkrgba.alpha = 1.0; gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(widget),&gdkrgba); } if (strmatchV(type,"hscale","vscale",null)) // slider widget { kk = convSD(data,dval); // zdialog widget value if (kk != 0) dval = 0.0; if (zd->widget[iiw].rescale) // widget value --> slider value 6.2 { lval = zd->widget[iiw].lval; // rescaled for more sensitivity nval = zd->widget[iiw].nval; // around neutral value hval = zd->widget[iiw].hval; if (dval > lval && dval <= nval) { // if dval == lval or dval == hval 6.7 F2 = (nval - dval) / (nval - lval); // then dval is not revised F = sqrtf(F2); dval = nval - F * (nval - lval); } else if (dval >= nval && dval < hval) { F2 = (dval - nval) / (hval - nval); F = sqrtf(F2); dval = nval + F * (hval - nval); } } gtk_range_set_value(GTK_RANGE(widget),dval); } if (strmatch(type,"combo")) { if (! blank_null(data)) { kk = pvlist_prepend(zd->widget[iiw].cblist,data,1); // add to drop-down list if (kk == 0) // (only if unique) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget),data); kk = pvlist_find(zd->widget[iiw].cblist,data); gtk_combo_box_set_active(GTK_COMBO_BOX(widget),kk); // make the active entry } else gtk_combo_box_set_active(GTK_COMBO_BOX(widget),-1); // make no active entry } if (strmatch(type,"comboE")) { entry = gtk_bin_get_child(GTK_BIN(widget)); if (! blank_null(data)) { kk = pvlist_prepend(zd->widget[iiw].cblist,data,1); // add to drop-down list if (kk == 0) // (only if unique) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget),data); gtk_entry_set_text(GTK_ENTRY(entry),data); // stuff entry box with new data } else gtk_entry_set_text(GTK_ENTRY(entry),""); // stuff entry box with nothing } retx: zd->disabled--; // re-enable dialog return iiw; } // get data from a dialog widget based on its name // private function cchar * zdialog_get_data(zdialog *zd, cchar *name) { if (! zdialog_valid(zd)) return 0; for (int ii = 1; zd->widget[ii].type; ii++) if (strmatch(zd->widget[ii].name,name)) return zd->widget[ii].data; return 0; } // set new limits for a numeric data entry widget (spin, zspin, hscale, vscale) int zdialog_set_limits(zdialog *zd, cchar *name, double min, double max) { GtkWidget *widget; cchar *type; int iiw; if (! zdialog_valid(zd)) return 0; for (iiw = 1; zd->widget[iiw].type; iiw++) if (strmatch(name,zd->widget[iiw].name)) break; if (! zd->widget[iiw].type) { printz("*** zdialog_set_limits, %s not found \n",name); return 0; } widget = zd->widget[iiw].widget; type = zd->widget[iiw].type; if (*type == 's') gtk_spin_button_set_range(GTK_SPIN_BUTTON(widget),min,max); if (*type == 'h' || *type == 'v') gtk_range_set_range(GTK_RANGE(widget),min,max); if (*type == 'z') { // zspin 6.7 zd->widget[iiw].lval = min; zd->widget[iiw].hval = max; } return 1; } // Expand a widget scale in the region around the neutral value. // 6.0 // Control small adjustments near the neutral value more precisely. // lval and hval: the range of values to be rescaled. // nval: the neutral value where the scale will be expanded the most. // lval <= nval <= hval // 6.6 int zdialog_rescale(zdialog *zd, cchar *name, float lval, float nval, float hval) { int iiw; if (! zdialog_valid(zd)) return 0; for (iiw = 1; zd->widget[iiw].type; iiw++) if (strmatch(name,zd->widget[iiw].name)) break; if (! zd->widget[iiw].type) { printz("*** zdialog_rescale, %s not found \n",name); return 0; } if (lval > nval || nval > hval) { // 6.6 printz("*** zdialog_rescale, bad data: %s \n",name); return 0; } zd->widget[iiw].rescale = 1; zd->widget[iiw].lval = lval; zd->widget[iiw].nval = nval; zd->widget[iiw].hval = hval; return 1; } // run the dialog and send events to the event function // // evfunc: int func(zdialog *zd, cchar *event) // If present, eventFunc is called when a dialog widget is changed or the dialog // is completed. If a widget was changed, event is the widget name. // Get the new widget data with zdialog_fetch(). // If a completion button was pressed, event is "zstat" and zd->zstat will be // the button number 1-N. // If the dialog was destroyed, event is "zstat" and zd->zstat is negative. // // posn: optional dialog box position: // "mouse" = position at mouse // "desktop" = center on desktop // "parent" = center on parent window // "nn/nn" = position NW corner at relative x/y position in parent window, // where nn/nn is a percent 0-100 of the parent window x/y dimensions. // "save" = save last user-set position and use this whenever the dialog is repeated, // also across sessions. // // KBevent: extern void KBevent(GdkEventKey *event) // This function must be supplied by the caller of zdialog. // It is called when Ctrl|Shift|Alt|F1 is pressed. int zdialog_run(zdialog *zd, zdialog_event evfunc, cchar *posn) { int zdialog_KB_press(GtkWidget *, GdkEventKey *event, zdialog *zd); int zdialog_focus_event(GtkWidget *, GdkEvent *event, zdialog *zd); GtkWidget *dialog; if (! zdialog_valid(zd)) zappcrash("zdialog invalid"); if (zd->zrunning) { printz("zdialog is already running \n"); return 0; } if (posn) zdialog_set_position(zd,posn); // put dialog at remembered position if (evfunc) zd->eventCB = (void *) evfunc; // link to dialog event callback dialog = zd->widget[0].widget; gtk_widget_show_all(dialog); // activate dialog gtk_window_present(GTK_WINDOW(dialog)); ///if (! zd->parent) gtk_window_set_keep_above(GTK_WINDOW(dialog),1); // removed 6.8 G_SIGNAL(dialog,"focus-in-event",zdialog_focus_event,zd); // connect focus event function G_SIGNAL(dialog,"key-press-event",zdialog_KB_press,zd); // connect key press event function G_SIGNAL(dialog,"delete-event",zdialog_delete_event,zd); // connect delete event function zd->zstat = 0; // dialog status incomplete zd->disabled = 0; // enable widget events zd->zrunning = 1; // dialog is running zfuncs::zdialog_busy++; // count open zdialogs return 0; } // zdialog event handler - called for dialog events. // Updates data in zdialog, calls user callback function (if present). // private function int zdialog_widget_event(GtkWidget *widget, zdialog *zd) { zdialog_event *evfunc = 0; // dialog event callback function GtkTextView *textView = 0; GtkTextBuffer *textBuff = 0; GtkTextIter iter1, iter2; GdkRGBA gdkrgba; GtkWidget *entry; int ii, nn; cchar *wname, *wtype, *wdata; char sdata[20]; double dval; float lval, nval, hval, F; static int cbadded = 0; if (! zdialog_valid(zd)) return 1; if (zd->disabled) return 1; // events disabled zd->disabled = 1; for (ii = 0; ii < zdmaxbutts; ii++) { // check completion buttons if (zd->compwidget[ii] == null) break; // EOL if (zd->compwidget[ii] != widget) continue; zd->zstat = ii+1; // zdialog status = button no. strncpy0(zd->event,"zstat",40); goto call_evfunc; // call zdialog event function } for (ii = 1; zd->widget[ii].type; ii++) // find widget in zdialog if (zd->widget[ii].widget == widget) goto found_widget; for (ii = 1; zd->widget[ii].type; ii++) { // failed, test if buffer if (strmatchV(zd->widget[ii].type,"edit","zentry",null)) { // of text view widget 6.7 textView = GTK_TEXT_VIEW(zd->widget[ii].widget); textBuff = gtk_text_view_get_buffer(textView); if (widget == (GtkWidget *) textBuff) goto found_widget; } } printz("*** zdialog event ignored \n"); // not found, ignore event zd->disabled = 0; return 1; found_widget: wname = zd->widget[ii].name; wtype = zd->widget[ii].type; wdata = 0; if (strmatch(wtype,"button")) wdata = gtk_button_get_label(GTK_BUTTON(widget)); // button label 6.4 if (strmatch(wtype,"zbutton")) { // checkbox as smaller button 6.7 wdata = gtk_button_get_label(GTK_BUTTON(widget)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),0); // reset checkmark = off } if (strmatch(wtype,"edit")) { gtk_text_buffer_get_bounds(textBuff,&iter1,&iter2); wdata = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); } if (strmatch(wtype,"entry")) wdata = gtk_entry_get_text(GTK_ENTRY(widget)); if (strmatch(wtype,"zentry")) { // 6.7 gtk_text_buffer_get_bounds(textBuff,&iter1,&iter2); wdata = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); } if (strmatchV(wtype,"radio","check","togbutt",null)) { nn = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); if (nn == 0) wdata = "0"; else wdata = "1"; } if (strmatch(wtype,"combo")) wdata = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(widget)); if (strmatch(wtype,"comboE")) { if (widget == zd->lastwidget && cbadded) { pvlist_remove(zd->widget[ii].cblist,0); // detect multiple edits (keystrokes) gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(widget),0); // amd replace with new entry } entry = gtk_bin_get_child(GTK_BIN(widget)); wdata = gtk_entry_get_text(GTK_ENTRY(entry)); cbadded = 0; if (! blank_null(wdata)) { nn = pvlist_prepend(zd->widget[ii].cblist,wdata,1); // add entry to drop-down list if (nn == 0) { // (only if unique) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget),wdata); cbadded = 1; } } } if (strmatch(wtype,"spin")) { dval = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)); snprintf(sdata,20,"%g",dval); wdata = sdata; } if (strmatch(wtype,"colorbutt")) // color button { gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget),&gdkrgba); snprintf(sdata,20,"%.0f|%.0f|%.0f",gdkrgba.red*255,gdkrgba.green*255,gdkrgba.blue*255); wdata = sdata; } if (strmatchV(wtype,"hscale","vscale",null)) { dval = gtk_range_get_value(GTK_RANGE(widget)); if (zd->widget[ii].rescale) // slider value --> widget value 6.0 { lval = zd->widget[ii].lval; nval = zd->widget[ii].nval; hval = zd->widget[ii].hval; if (dval > lval && dval < nval) { // lval ... nval 6.6 F = (nval - dval) / (nval - lval); // 1 ... 0 dval = (1.0 - F * F) * (nval - lval) + lval; // lval ... nval } else if (dval > nval && dval < hval) { // nval ... hval F = (dval - nval) / (hval - nval); // 0 ... 1 dval = F * F * (hval - nval) + nval; // nval ... hval } } snprintf(sdata,20,"%g",dval); wdata = sdata; } // all widgets come here if (zd->widget[ii].data) zfree(zd->widget[ii].data); // clear prior data zd->widget[ii].data = 0; if (wdata) zd->widget[ii].data = zstrdup(wdata); // set new data zd->lastwidget = widget; // remember last widget updated strncpy0(zd->event,wname,40); // event = widget name 6.6 call_evfunc: // call zdialog event function if (zd->eventCB) { evfunc = (zdialog_event *) zd->eventCB; // do callback function evfunc(zd,zd->event); // 6.6 } zd->disabled = 0; // re-enable widgets return 1; } // zdialog response handler for "focus-in-event" signal // private function zdialog *zdialog_focus_zd; // current zdialog int zdialog_focus_event(GtkWidget *, GdkEvent *event, zdialog *zd) { if (! zdialog_valid(zd)) return 0; if (zd->zstat) return 0; // already complete zdialog_focus_zd = zd; zdialog_send_event(zd,"focus"); // notify dialog event function return 0; // must be 0 } // zdialog response handler for keyboard events // key symbols can be found at /usr/include/gtk-3.0/gdk/gdkkeysyms.h // main app must provide: extern void KBevent(GdkEventKey *event) // private function int zdialog_KB_press(GtkWidget *widget, GdkEventKey *kbevent, zdialog *zd) { void zdialog_copyfunc(GtkWidget *, GtkClipboard *); void zdialog_pastefunc(GtkClipboard *, cchar *, void *); zdialog_event *evfunc = 0; // dialog event callback function GtkClipboard *clipboard = 0; GtkWidget *focuswidget; int KB_Ctrl = 0; // track state of KB Ctrl int KBkey = kbevent->keyval; cchar *type; int ii, Ftext; if (kbevent->state & GDK_CONTROL_MASK) KB_Ctrl = 1; if (KB_Ctrl && KBkey == GDK_KEY_c) // Ctrl+C = copy to clipboard 6.0 { clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); zdialog_copyfunc(widget,clipboard); return 1; } if (KB_Ctrl && KBkey == GDK_KEY_v) // Ctrl+V = paste clipboard { clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); gtk_clipboard_request_text(clipboard,zdialog_pastefunc,widget); return 1; } if (KBkey == GDK_KEY_Escape) { // ESC key 6.1 zd->zstat = -1; // escape = cancel 6.5 zdialog_send_event(zd,"zstat"); gtk_widget_destroy(widget); return 1; } if (KBkey == GDK_KEY_F1) { KBevent(kbevent); return 1; }; // these keys handled by main app if (KBkey == GDK_KEY_F10) { KBevent(kbevent); return 1; }; if (KBkey == GDK_KEY_F11) { KBevent(kbevent); return 1; }; focuswidget = gtk_window_get_focus(GTK_WINDOW(widget)); // find widget in zdialog 6.5 for (ii = 1; zd->widget[ii].type; ii++) if (zd->widget[ii].widget == focuswidget) break; type = zd->widget[ii].type; if (! type) return 0; Ftext = strmatchV(type,"zspin","zentry","entry","edit","spin",null); // text-edit widgets 6.7 if (KBkey == GDK_KEY_Left || KBkey == GDK_KEY_Right) { // left/right arrow key if (! Ftext) { // not a text-edit widget, KBevent(kbevent); // pass key to main() return 1; } } if (KBkey == GDK_KEY_Return && Ftext == 2) { // zentry widget if (zd->eventCB) { zd->disabled = 1; evfunc = (zdialog_event *) zd->eventCB; // do callback function evfunc(zd,zd->event); zd->disabled = 0; } return 1; } return 0; // pass KB key to widget } // event function for "zspin" widget (text entry works as spin button) int zdialog_zspin_event(GtkWidget *widget, GdkEvent *event, zdialog *zd) // 6.7 { zdialog_event *evfunc = 0; // dialog event callback function GtkTextBuffer *textBuff; GtkTextIter iter1, iter2; int KBkey; int ii, err, Nsteps, state, incr = 0; float fdata, lolim, hilim, step; char *wdata, sdata[20]; int time, elaps; static int time0 = 0, time1 = 0; if (! zdialog_valid(zd)) return 1; if (zd->disabled) return 1; // zdialog events disabled for (ii = 1; zd->widget[ii].type; ii++) // find "zspin" (text view) widget if (zd->widget[ii].widget == widget) break; if (! zd->widget[ii].type) return 0; // not found textBuff = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); // get widget data gtk_text_buffer_get_bounds(textBuff,&iter1,&iter2); wdata = gtk_text_buffer_get_text(textBuff,&iter1,&iter2,0); lolim = zd->widget[ii].lolim; // limits and step size hilim = zd->widget[ii].hilim; step = zd->widget[ii].step; if (event->type == GDK_SCROLL) { // mouse wheel event gtk_widget_grab_focus(widget); incr = - ((GdkEventScroll *) event)->delta_y; if (! incr) return 1; state = ((GdkEventScroll *) event)->state; // if shift key held, use 10x step if (state & GDK_SHIFT_MASK) incr *= 10; goto checklimits; } if (event->type == GDK_KEY_PRESS) { // KB button press KBkey = ((GdkEventKey *) event)->keyval; if (KBkey == GDK_KEY_Return) goto checklimits; // return = entry finished if (KBkey == GDK_KEY_Up) incr = 1; if (KBkey == GDK_KEY_Down) incr = -1; if (KBkey == GDK_KEY_plus) incr = 1; if (KBkey == GDK_KEY_equal) incr = 1; if (KBkey == GDK_KEY_minus) incr = -1; if (KBkey == GDK_KEY_KP_Add) incr = 1; if (KBkey == GDK_KEY_KP_Subtract) incr = -1; if (! incr) return 0; // must be zero time = ((GdkEventKey *) event)->time; // track time key is held down if (time - time1 > 300) time0 = time; time1 = time; elaps = time - time0; if (elaps > 5000) step = 10 * step; // acceleration table for else if (elaps > 4500) step = 8.2 * step; // hold time 1-5+ seconds else if (elaps > 4000) step = 6.3 * step; else if (elaps > 3500) step = 4.8 * step; else if (elaps > 3000) step = 3.7 * step; else if (elaps > 2500) step = 2.9 * step; else if (elaps > 2000) step = 2.2 * step; else if (elaps > 1500) step = 1.7 * step; else if (elaps > 1000) step = 1.3 * step; goto checklimits; } if (event->type == GDK_FOCUS_CHANGE) goto checklimits; // focus change = entry finished return 1; checklimits: convSF(wdata,fdata); // ignore bad char. inputs fdata += incr * step; err = 0; Nsteps = (fdata - lolim) / step + 0.5; // set nearest exact step 6.8 fdata = lolim + Nsteps * step; if (fdata < lolim) { // force within range err = 1; fdata = lolim; } if (fdata > hilim) { err = 2; fdata = hilim; } if (err) gtk_widget_grab_focus(widget); // if error, restore focus convDS(fdata,6,sdata); // round to 6 digits 6.8 gtk_text_buffer_set_text(textBuff,sdata,-1); if (zd->widget[ii].data) zfree(zd->widget[ii].data); // clear prior widget data zd->widget[ii].data = zstrdup(sdata); // set new data zd->lastwidget = widget; // remember last widget updated strncpy0(zd->event,zd->widget[ii].name,40); // event = widget name if (zd->eventCB) { zd->disabled = 1; evfunc = (zdialog_event *) zd->eventCB; // do callback function evfunc(zd,zd->event); zd->disabled = 0; } if (event->type == GDK_FOCUS_CHANGE) return 0; return 1; } // process Ctrl+C (copy text from widget to clipboard) // 6.0 // private function void zdialog_copyfunc(GtkWidget *widget, GtkClipboard *clipboard) { GtkTextView *textView = 0; GtkTextBuffer *textBuff = 0; zdialog *zd; int ii, cc = 0; cchar *wname; char text[1000]; widget = gtk_window_get_focus(GTK_WINDOW(widget)); if (! widget) return; zd = zdialog_focus_zd; if (! zdialog_valid(zd)) return; for (ii = 1; zd->widget[ii].type; ii++) // find widget in zdialog if (zd->widget[ii].widget == widget) goto found_widget; for (ii = 1; zd->widget[ii].type; ii++) { // failed, test if buffer if (strmatchV(zd->widget[ii].type,"edit","zentry",null)) { // of text view widget 6.7 textView = GTK_TEXT_VIEW(zd->widget[ii].widget); textBuff = gtk_text_view_get_buffer(textView); if (widget == (GtkWidget *) textBuff) goto found_widget; } } return; // not found found_widget: wname = zd->widget[ii].name; zdialog_fetch(zd,wname,text,999); // current text in widget cc = strlen(text); gtk_clipboard_set_text(clipboard,text,cc); return; } // process Ctrl+V (paste text from clipboard to widget) // private function void zdialog_pastefunc(GtkClipboard *clipboard, cchar *cliptext, void *arg) { GtkWindow *window; GtkWidget *widget; GtkTextView *textView = 0; GtkTextBuffer *textBuff = 0; zdialog *zd; int ii, cc = 0; cchar *wname; char text[1000]; window = (GtkWindow *) arg; widget = gtk_window_get_focus(window); if (! widget) return; // widget for pasted text if (! cliptext || ! *cliptext) return; // clipboard text pasted zd = zdialog_focus_zd; if (! zdialog_valid(zd)) return; if (zd->zstat) return; for (ii = 1; zd->widget[ii].type; ii++) // find widget in zdialog if (zd->widget[ii].widget == widget) goto found_widget; for (ii = 1; zd->widget[ii].type; ii++) { // failed, test if buffer if (strmatchV(zd->widget[ii].type,"edit","zentry",null)) { // of text view widget 6.7 textView = GTK_TEXT_VIEW(zd->widget[ii].widget); textBuff = gtk_text_view_get_buffer(textView); if (widget == (GtkWidget *) textBuff) goto found_widget; } } return; // not found found_widget: wname = zd->widget[ii].name; zdialog_fetch(zd,wname,text,999); // current text in widget cc = strlen(text); if (cc > 995) return; strncpy(text+cc,cliptext,999-cc); // add clipboard text text[999] = 0; zdialog_stuff(zd,wname,text); return; } // private function called when zdialog is completed. // called when dialog is canceled via [x] button or destroyed by GTK (zstat < 0). int zdialog_delete_event(GtkWidget *, GdkEvent *, zdialog *zd) { zdialog_event *evfunc = 0; // dialog event callback function if (! zdialog_valid(zd)) return 1; if (zd->zstat) return 1; // already complete if (zd->disabled) return 1; // in process zd->zstat = -1; // set zdialog cancel status if (zd->eventCB) { evfunc = (zdialog_event *) zd->eventCB; // do callback function zd->disabled = 1; // 6.1 evfunc(zd,"zstat"); zd->disabled = 0; } else zdialog_destroy(zd); // caller should zdialog_free() return 0; } // Send the event "name" to an active zdialog. // The response function eventFunc() will be called with this event. int zdialog_send_event(zdialog *zd, cchar *event) { zdialog_event * evfunc = 0; // dialog event callback function if (! zdialog_valid(zd)) return 0; if (zd->disabled) return 0; // busy 6.1 evfunc = (zdialog_event *) zd->eventCB; if (! evfunc) return 0; zd->disabled = 1; // 6.1 evfunc(zd,event); // call dialog event function zd->disabled = 0; return 1; } // Complete an active dialog and assign a status. // Equivalent to the user pressing a dialog completion button. // The dialog completion function is called if defined, // and zdialog_wait() is unblocked. // returns: 0 = no active dialog or completion function, 1 = OK int zdialog_send_response(zdialog *zd, int zstat) { zdialog_event *evfunc = 0; // dialog event callback function if (! zdialog_valid(zd)) return 0; if (zd->disabled) return 0; // 6.1 zd->zstat = zstat; // set status evfunc = (zdialog_event *) zd->eventCB; if (! evfunc) return 0; zd->disabled = 1; // 6.1 evfunc(zd,"zstat"); zd->disabled = 0; return 1; } // show or hide a zdialog window // returns 1 if successful, 0 if zd does not exist. int zdialog_show(zdialog *zd, int show) { static GtkWidget *widget, *pwidget = 0; static int posx, posy; widget = zdialog_widget(zd,"dialog"); if (! widget) return 0; if (show) { // show window if (widget == pwidget) { // restore prior position gtk_window_move(GTK_WINDOW(widget),posx,posy); pwidget = 0; } zmainsleep(0.1); // GTK bug: make focus work FIXME 6.5 gtk_window_present(GTK_WINDOW(widget)); // set focus on restored window 6.5 } else { // hide window pwidget = widget; gtk_window_get_position(GTK_WINDOW(widget),&posx,&posy); // save position gtk_widget_hide(widget); } return 1; } // Destroy the zdialog - must be done by zdialog_run() caller // (else dialog continues active even after completion button). // Data in widgets remains valid until zdialog_free() is called. int zdialog_destroy(zdialog *zd) { if (! zdialog_valid(zd)) return 0; if (zd->zstat < 0) { // destroyed by [x] button or GTK if (zd->widget[0].widget) zdialog_busy--; zd->widget[0].widget = 0; // assume GTK dialog is gone } if (zd->saveposn) zdialog_save_position(zd); // save position for next use moved 6.0 if (zd->widget[0].widget) { // multiple destroys OK gtk_widget_destroy(zd->widget[0].widget); // destroy GTK dialog zd->widget[0].widget = 0; zdialog_busy--; } if (! zd->zstat) zd->zstat = -1; // status = destroyed zd->zrunning = 0; // not running 6.3 return 1; } // free zdialog memory (will destroy first, if not already) // zd is set to null int zdialog_free(zdialog *&zd) // reference { int ii; if (! zdialog_valid(zd)) return 0; // validate zd pointer zdialog_save_inputs(zd); // save user inputs for next use zdialog_destroy(zd); // destroy GTK dialog if there zd->sentinel1 = zd->sentinel2 = 0; // mark sentinels invalid zfree(zd->title); // free title memory 6.8 zfree(zd->widget[0].data); for (ii = 1; zd->widget[ii].type; ii++) // loop through widgets { if (strmatchV(zd->widget[ii].type,"combo","comboE",null)) // free combo list pvlist_free(zd->widget[ii].cblist); zfree((char *) zd->widget[ii].type); // free strings zfree((char *) zd->widget[ii].name); if (zd->widget[ii].pname) zfree((char *) zd->widget[ii].pname); // parent widget name 6.5 if (zd->widget[ii].data) zfree(zd->widget[ii].data); // free data } for (ii = 0; ii < zdialog_count; ii++) // remove from valid zdialog list if (zd == zdialog_list[ii]) break; zdialog_count--; for ( ; ii < zdialog_count; ii++) // pack down list zdialog_list[ii] = zdialog_list[ii+1]; zfree(zd); // free zdialog memory zd = 0; // caller pointer = null return 1; } // Wait for a dialog to complete or be destroyed. This is a zmainloop() loop. // The returned status is the button 1-N used to complete the dialog, or negative // if the dialog was destroyed with [x] or otherwise by GTK. If the status was 1-N and // the dialog will be kept active, set zd->zstat = 0 to restore the active state. int zdialog_wait(zdialog *zd) { while (true) { zmainloop(); if (! zd) return -1; // 6.0 if (! zdialog_valid(zd,0)) return -1; // no error message if (zd->zstat) return zd->zstat; zsleep(0.01); } } // put cursor at named widget int zdialog_goto(zdialog *zd, cchar *name) { GtkWidget *widget; if (! zdialog_valid(zd)) return 0; widget = zdialog_widget(zd, name); if (! widget) return 0; gtk_widget_grab_focus(widget); return 1; } // set cursor for zdialog (e.g. a busy cursor) void zdialog_set_cursor(zdialog *zd, GdkCursor *cursor) { GtkWidget *dialog; GdkWindow *window; if (! zdialog_valid(zd)) return; dialog = zd->widget[0].widget; if (! dialog) return; window = gtk_widget_get_window(dialog); gdk_window_set_cursor(window,cursor); return; } // insert data into a zdialog widget int zdialog_stuff(zdialog *zd, cchar *name, cchar *data) // stuff a string { if (data) zdialog_put_data(zd,name,data); else zdialog_put_data(zd,name,""); // null > "" return 1; } int zdialog_stuff(zdialog *zd, cchar *name, int idata) // stuff an integer { char string[16]; snprintf(string,16,"%d",idata); zdialog_put_data(zd,name,string); return 1; } int zdialog_stuff(zdialog *zd, cchar *name, double ddata) // stuff a double { char string[32]; snprintf(string,32,"%.7g",ddata); // increase from 6 to 7 digits 6.7 zdialog_put_data(zd,name,string); // 'g' uses decimal or comma return 1; // (per locale) } int zdialog_stuff(zdialog *zd, cchar *name, double ddata, cchar *format) // stuff a double, formatted 6.1 { char string[32]; snprintf(string,32,format,ddata); // use ".2g" etc. for zdialog_put_data(zd,name,string); // locale dependent point/comma return 1; } int zdialog_labelfont(zdialog *zd, cchar *labl, cchar *font, cchar *txt) // stuff label text with font 6.2 { GtkWidget *widget; cchar *format = "%s"; char txt2[1000]; if (! font) font = zfuncs::appfont; // default font 6.3 snprintf(txt2,1000,format,font,txt); widget = zdialog_widget(zd,labl); gtk_label_set_markup(GTK_LABEL(widget),txt2); return 1; } // get data from a zdialog widget int zdialog_fetch(zdialog *zd, cchar *name, char *data, int maxcc) // fetch string data { cchar *zdata; zdata = zdialog_get_data(zd,name); if (! zdata) { *data = 0; return 0; } return strncpy0(data,zdata,maxcc); // 0 = OK, 1 = truncation } int zdialog_fetch(zdialog *zd, cchar *name, int &idata) // fetch an integer { cchar *zdata; zdata = zdialog_get_data(zd,name); if (! zdata) { idata = 0; return 0; } idata = atoi(zdata); return 1; } int zdialog_fetch(zdialog *zd, cchar *name, double &ddata) // fetch a double { int stat; cchar *zdata; zdata = zdialog_get_data(zd,name); if (! zdata) { ddata = 0; return 0; } stat = convSD(zdata,ddata); // period or comma decimal point OK if (stat < 4) return 1; return 0; } int zdialog_fetch(zdialog *zd, cchar *name, float &fdata) // fetch a float { int stat; cchar *zdata; double ddata; zdata = zdialog_get_data(zd,name); if (! zdata) { fdata = 0; return 0; } stat = convSD(zdata,ddata); // period or comma decimal point OK fdata = ddata; if (stat < 4) return 1; return 0; } /******************************************************************************** Combo Box widget with Entry --------------------------- stat = zdialog_cb_app(zd, name, data) append to combo box list stat = zdialog_cb_prep(zd, name, data) prepend to combo box list data = zdialog_cb_get(zd, name, Nth) get combo box list item data = zdialog_cb_delete(zd, name, data) delete entry matching "data" data = zdialog_cb_clear(zd, name) clear all entries zdialog_cb_popup(zd, name) open the combo box entries These functions map and track a combo box drop-down list, by maintaining a parallel list in memory. The function zdialog-stuff, when called for a comboE widget, automatically prepends the stuffed data to the drop-down list. The private function zdialog_event(), when processing user input to the edit box of a comboE widget, adds the updated entry to the drop-down list. The drop-down list is kept free of redundant and blank entries. *********************************************************************************/ // append new item to combo box list without changing entry box int zdialog_cb_app(zdialog *zd, cchar *name, cchar *data) { int ii, nn; if (! zdialog_valid(zd)) return 0; if (blank_null(data)) return 0; // find widget for (ii = 1; zd->widget[ii].type; ii++) if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box nn = pvlist_append(zd->widget[ii].cblist,data,1); // append unique if (nn >= 0) gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(zd->widget[ii].widget),data); return 1; } // prepend new item to combo box list without changing entry box int zdialog_cb_prep(zdialog *zd, cchar *name, cchar *data) { int ii, nn; if (! zdialog_valid(zd)) return 0; if (blank_null(data)) return 0; // find widget for (ii = 1; zd->widget[ii].type; ii++) if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box nn = pvlist_prepend(zd->widget[ii].cblist,data,1); // prepend unique if (nn == 0) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(zd->widget[ii].widget),data); return 1; } // get combo box drop-down list entry // Nth = 0 = first list entry (not comboE edit box) char * zdialog_cb_get(zdialog *zd, cchar *name, int Nth) { int ii; if (! zdialog_valid(zd)) return 0; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box return pvlist_get(zd->widget[ii].cblist,Nth); } // delete entry by name from combo drop down list int zdialog_cb_delete(zdialog *zd, cchar *name, cchar *data) { int ii, nn; if (! zdialog_valid(zd)) return 0; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box nn = pvlist_find(zd->widget[ii].cblist,data); // find entry by name if (nn < 0) return -1; pvlist_remove(zd->widget[ii].cblist,nn); // remove from memory list gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(zd->widget[ii].widget),nn); // and from widget gtk_combo_box_set_active(GTK_COMBO_BOX(zd->widget[ii].widget),-1); // set no active entry return 0; } // delete all entries from combo drop down list int zdialog_cb_clear(zdialog *zd, cchar *name) { int ii, jj, nn; GtkWidget *widget, *entry; if (! zdialog_valid(zd)) return 0; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box nn = pvlist_count(zd->widget[ii].cblist); // entry count for (jj = nn-1; jj >= 0; jj--) { pvlist_remove(zd->widget[ii].cblist,jj); // remove from memory list gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(zd->widget[ii].widget),jj); // remove from widget } widget = zd->widget[ii].widget; gtk_combo_box_set_active(GTK_COMBO_BOX(widget),-1); // set no active entry if (strmatch(zd->widget[ii].type,"comboE")) { // stuff entry box with nothing entry = gtk_bin_get_child(GTK_BIN(widget)); gtk_entry_set_text(GTK_ENTRY(entry),""); } return 1; } // make a combo box drop down to show all entries int zdialog_cb_popup(zdialog *zd, cchar *name) { int ii; if (! zdialog_valid(zd)) return 0; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 0; // not found if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 0; // not combo box gtk_combo_box_popup(GTK_COMBO_BOX(zd->widget[ii].widget)); gtk_combo_box_set_active(GTK_COMBO_BOX(zd->widget[ii].widget),-1); // no active entry 6.2 return 1; } // save all combo box entries to a file in /home//.appname/filename // returns 0 = OK, +N = error int zdialog_cb_save(zdialog *zd, cchar *name, cchar *filename) // 6.2 { char file[200], *pp; int ii, jj, nn; FILE *fid; if (! zdialog_valid(zd)) return 1; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 2; if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 3; // not combo box nn = pvlist_count(zd->widget[ii].cblist); // entry count if (! nn) return 0; snprintf(file,200,"%s/%s",get_zhomedir(),filename); fid = fopen(file,"w"); if (! fid) return 4; for (jj = 0; jj < nn; jj++) { pp = pvlist_get(zd->widget[ii].cblist,jj); fprintf(fid,"%s\n",pp); } fclose(fid); return 0; } // load combo box entries from a file in /home//.appname/filename // returns 0 = OK, +N = error int zdialog_cb_load(zdialog *zd, cchar *name, cchar *filename) // 6.2 { char file[200], data[100], *pp; int ii; FILE *fid; if (! zdialog_valid(zd)) return 1; for (ii = 1; zd->widget[ii].type; ii++) // find widget if (strmatch(zd->widget[ii].name,name)) break; if (! zd->widget[ii].type) return 2; if (! strmatchV(zd->widget[ii].type,"combo","comboE",null)) return 3; // not combo box zdialog_cb_clear(zd,name); snprintf(file,200,"%s/%s",get_zhomedir(),filename); fid = fopen(file,"r"); if (! fid) return 0; while (true) { pp = fgets_trim(data,99,fid); if (! pp) break; zdialog_cb_app(zd,name,data); } fclose(fid); return 0; } /********************************************************************************/ // functions to save and recall zdialog window positions namespace zdposn_names { struct zdposn_t { float xpos, ypos; // window position WRT parent or desktop char wintitle[64]; // window title (ID) } zdposn[200]; // space for 200 windows int Nzdposn; // no. in use int Nzdpmax = 200; // table size } // Load zdialog positions table from its file (application startup) // or save zdialog positions table to its file (application exit). // Action is "load" or "save". Number of table entries is returned. int zdialog_positions(cchar *action) { using namespace zdposn_names; char posfile[200], buff[100], wintitle[64], *pp; float xpos, ypos; int nn, ii; FILE *fid; snprintf(posfile,199,"%s/zdialog_positions",zhomedir); // /home//.appname/zdialog_positions if (strmatch(action,"load")) // load dialog positions table from file { fid = fopen(posfile,"r"); if (! fid) { Nzdposn = 0; return 0; } for (nn = 0; nn < Nzdpmax; nn++) { pp = fgets(buff,100,fid); if (! pp) break; if (strlen(pp) < 64) continue; strncpy0(wintitle,buff,64); strTrim(wintitle); if (strlen(wintitle) < 3) continue; ii = sscanf(buff + 64," %f %f ",&xpos,&ypos); if (ii != 2) continue; strcpy(zdposn[nn].wintitle,wintitle); zdposn[nn].xpos = xpos; zdposn[nn].ypos = ypos; } fclose(fid); Nzdposn = nn; return Nzdposn; } if (strmatch(action,"save")) // save dialog positions table to file { fid = fopen(posfile,"w"); if (! fid) { printz("*** cannot write zdialog_positions file \n"); return 0; } for (nn = 0; nn < Nzdposn; nn++) fprintf(fid,"%-64s %0.1f %0.1f \n",zdposn[nn].wintitle, zdposn[nn].xpos, zdposn[nn].ypos); fclose(fid); return Nzdposn; } printz("*** zdialog_positions bad action: %s \n",action); return 0; } // Set the initial or new zdialog window position from "posn". // Called by zdialog_run(). Private function. // null: window manager decides // "mouse" put dialog at mouse position // "desktop" center dialog in desktop window // "parent" center dialog in parent window // "save" use the same position last set by the user // "nn/nn" put NW corner of dialog in parent window at % size // (e.g. "50/50" puts NW corner at center of parent) void zdialog_set_position(zdialog *zd, cchar *posn) { using namespace zdposn_names; int ii, ppx, ppy, zdpx, zdpy, pww, phh; float xpos, ypos; char wintitle[64], *pp; GtkWidget *parent, *dialog; parent = zd->parent; dialog = zd->widget[0].widget; if (strmatch(posn,"mouse")) { gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE); return; } if (strmatch(posn,"desktop")) { gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_CENTER); return; } if (strmatch(posn,"parent")) { gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_CENTER_ON_PARENT); return; } if (! parent) { // no parent window ppx = ppy = 0; // use desktop pww = monitor_ww; phh = monitor_hh; } else { gtk_window_get_position(GTK_WINDOW(parent),&ppx,&ppy); // parent window NW corner gtk_window_get_size(GTK_WINDOW(parent),&pww,&phh); // parent window size } if (strmatch(posn,"save")) // use last saved window position { zd->saveposn = 1; // set flag for zdialog_free() pp = (char *) gtk_window_get_title(GTK_WINDOW(dialog)); // get window title, used as ID if (! pp) return; if (strlen(pp) < 2) return; strncpy0(wintitle,pp,64); // window title, < 64 chars. for (ii = 0; ii < Nzdposn; ii++) // search table for title if (strmatch(wintitle,zdposn[ii].wintitle)) break; if (ii == Nzdposn) return; // not found - zdialog_destroy() will add zdpx = ppx + 0.01 * zdposn[ii].xpos * pww; // position for dialog window zdpy = ppy + 0.01 * zdposn[ii].ypos * phh; gtk_window_move(GTK_WINDOW(dialog),zdpx,zdpy); return; } else // "nn/nn" // position from caller { ii = sscanf(posn,"%f/%f",&xpos,&ypos); // parse "nn/nn" if (ii != 2) return; zdpx = ppx + 0.01 * xpos * pww; // position for dialog window zdpy = ppy + 0.01 * ypos * phh; gtk_window_move(GTK_WINDOW(dialog),zdpx,zdpy); return; } } // If the dialog window position is "save" then save // its position WRT parent or desktop for next use. // called by zdialog_destroy(). Private function. void zdialog_save_position(zdialog *zd) { using namespace zdposn_names; int ii, ppx, ppy, pww, phh, zdpx, zdpy; float xpos, ypos; char wintitle[64], *pp; GtkWidget *parent, *dialog; dialog = zd->widget[0].widget; if (! dialog) return; // destroyed gtk_window_get_position(GTK_WINDOW(dialog),&zdpx,&zdpy); // dialog window NW corner if (! zdpx && ! zdpy) return; // (0,0) ignore parent = zd->parent; // parent window if (! parent) { // no parent window ppx = ppy = 0; // use desktop pww = monitor_ww; phh = monitor_hh; } else { gtk_window_get_position(GTK_WINDOW(parent),&ppx,&ppy); // parent window NW corner gtk_window_get_size(GTK_WINDOW(parent),&pww,&phh); // parent window size } xpos = 100.0 * (zdpx - ppx) / pww; // dialog window relative position ypos = 100.0 * (zdpy - ppy) / phh; // (as percent of parent size) pp = (char *) gtk_window_get_title(GTK_WINDOW(dialog)); if (! pp) return; if (strlen(pp) < 2) return; strncpy0(wintitle,pp,64); // window title, < 64 chars. for (ii = 0; ii < Nzdposn; ii++) // search table for window if (strmatch(wintitle,zdposn[ii].wintitle)) break; if (ii == Nzdposn) { // not found if (ii == Nzdpmax) return; // table full strcpy(zdposn[ii].wintitle,wintitle); // add window to table Nzdposn++; } zdposn[ii].xpos = xpos; // save window position zdposn[ii].ypos = ypos; return; } /********************************************************************************/ // Functions to save and restore zdialog user inputs // within an app session or across app sessions. namespace zdinputs_names { #define Nwmax zdmaxwidgets // max. widgets in a dialog #define Nzdmax 200 // max. zdialogs 6.6 #define ccmax1 100 // max. widget name length #define ccmax2 400 // max. widget data length struct zdinputs_t { char *zdtitle; // zdialog title int Nw; // no. of widgets char **wname; // list of widget names char **wdata; // list of widget data } zdinputs[Nzdmax]; // space for Nzdmax dialogs int Nzd = 0; // no. zdialogs in use } // Load zdialog input fields from its file (app startup) // or save zdialog input fields to its file (app shutdown). // Action is "load" or "save". // Number of zdialogs is returned. int zdialog_inputs(cchar *action) { using namespace zdinputs_names; char zdinputsfile[200], buff[ccmax2]; char zdtitle[ccmax1], wname[Nwmax][ccmax1], wdata[Nwmax][ccmax2]; char *pp, *pp1, *pp2, wdata2[ccmax2+50]; FILE *fid; int Nw, ii, jj, cc, cc1, cc2; snprintf(zdinputsfile,200,"%s/zdialog_inputs",zhomedir); // /home//.appname/zdialog_inputs if (strmatch(action,"load")) // load dialog input fields from its file { Nzd = 0; fid = fopen(zdinputsfile,"r"); // no file if (! fid) return 0; while (true) { pp = fgets_trim(buff,ccmax2,fid,1); // read next zdialog title record if (! pp) break; if (! strmatchN(pp,"zdialog == ",11)) continue; strncpy0(zdtitle,pp+11,ccmax1); // save new zdialog title pp = fgets_trim(buff,ccmax2,fid,1); // read widget count if (! pp) break; Nw = atoi(pp); if (Nw < 1 || Nw > Nwmax) { printz("*** zdialog_inputs() bad data: %s \n",zdtitle); continue; } for (ii = 0; ii < Nw; ii++) // read widget data recs { pp = fgets_trim(buff,ccmax2,fid,1); if (! pp) break; pp1 = pp; pp2 = strstr(pp1," =="); if (! pp2) break; // widget has no data cc1 = pp2 - pp1; pp1[cc1] = 0; pp2 += 3; if (*pp2 == ' ') pp2++; cc2 = strlen(pp2); if (cc1 < 1 || cc1 >= ccmax1) break; if (cc2 < 1) pp2 = (char *) ""; if (cc2 >= ccmax2) break; // do not copy large inputs strcpy(wname[ii],pp1); // save widget name and data strcpy(wdata2,pp2); repl_1str(wdata2,wdata[ii],"\\n","\n"); // replace "\n" with newline chars. } if (ii < Nw) { printz("*** zdialog_inputs() bad data: %s \n",zdtitle); continue; } if (Nzd == Nzdmax) { printz("*** zdialog_inputs() too many dialogs \n"); break; } zdinputs[Nzd].zdtitle = zstrdup(zdtitle); // save acculumated zdialog data zdinputs[Nzd].Nw = Nw; cc = Nw * sizeof(char *); zdinputs[Nzd].wname = (char **) zmalloc(cc); zdinputs[Nzd].wdata = (char **) zmalloc(cc); for (ii = 0; ii < Nw; ii++) { zdinputs[Nzd].wname[ii] = zstrdup(wname[ii]); zdinputs[Nzd].wdata[ii] = zstrdup(wdata[ii]); } Nzd++; } fclose(fid); return Nzd; } if (strmatch(action,"save")) // save dialog input fields to its file { fid = fopen(zdinputsfile,"w"); if (! fid) { printz("*** zdialog_inputs() cannot write file \n"); return 0; } for (ii = 0; ii < Nzd; ii++) { fprintf(fid,"zdialog == %s \n",zdinputs[ii].zdtitle); // zdialog == zdialog title Nw = zdinputs[ii].Nw; fprintf(fid,"%d \n",Nw); // widget count for (jj = 0; jj < Nw; jj++) { pp1 = zdinputs[ii].wname[jj]; // widget name == widget data pp2 = zdinputs[ii].wdata[jj]; repl_1str(pp2,wdata2,"\n","\\n"); // replace newline chars. with "\n" fprintf(fid,"%s == %s \n",pp1,wdata2); } fprintf(fid,"\n"); } fclose(fid); return Nzd; } printz("*** zdialog_inputs bad action: %s \n",action); return 0; } // Save dialog user input fields when a dialog is finished. // Called automatically by zdialog_free(). Private function. int zdialog_save_inputs(zdialog *zd) { using namespace zdinputs_names; char zdtitle[ccmax1], wname[ccmax1], wdata[ccmax2], *type; int ii, jj, Nw, cc; if (! zdialog_valid(zd)) return 0; if (! zd->saveinputs) return 0; // zdialog does not use this service strncpy0(zdtitle,zd->widget[0].data,ccmax1); // zdialog title is widget[0].data for (ii = 0; ii < Nzd; ii++) // find zdialog in zdinputs table if (strmatch(zdtitle,zdinputs[ii].zdtitle)) break; if (ii < Nzd) { // found zfree(zdinputs[ii].zdtitle); // delete obsolete zdinputs data for (jj = 0; jj < zdinputs[ii].Nw; jj++) { zfree(zdinputs[ii].wname[jj]); zfree(zdinputs[ii].wdata[jj]); } zfree(zdinputs[ii].wname); zfree(zdinputs[ii].wdata); Nzd--; // decr. zdialog count for (ii = ii; ii < Nzd; ii++) // pack down the rest zdinputs[ii] = zdinputs[ii+1]; } if (Nzd == Nzdmax) { printz("*** zdialog_save_inputs, too many zdialogs \n"); return 0; } ii = Nzd; // next zdinputs table entry for (Nw = 0, jj = 1; zd->widget[jj].type; jj++) { // count zdialog widgets type = (char *) zd->widget[jj].type; if (strstr("dialog hbox vbox hsep vsep frame scrwin" // skip non-input widgets 6.7 "label link button zbutton text",type)) continue; Nw++; } if (! Nw) return 0; // no input widgets if (Nw > Nwmax) { printz("*** zdialog_inputs() bad data: %s \n",zdtitle); return 0; } zdinputs[ii].zdtitle = zstrdup(zdtitle); // set zdialog title cc = Nw * sizeof(char *); // allocate pointers for widgets zdinputs[ii].wname = (char **) zmalloc(cc); zdinputs[ii].wdata = (char **) zmalloc(cc); for (Nw = 0, jj = 1; zd->widget[jj].type; jj++) { // add widget names and data type = (char *) zd->widget[jj].type; if (strstr("dialog hbox vbox hsep vsep frame scrwin" // skip non-input widgets 6.7 "label link button zbutton text",type)) continue; strncpy0(wname,zd->widget[jj].name,ccmax1); if (zd->widget[jj].data) strncpy0(wdata,zd->widget[jj].data,ccmax2); else strcpy(wdata,""); zdinputs[ii].wname[Nw] = zstrdup(wname); zdinputs[ii].wdata[Nw] = zstrdup(wdata); Nw++; } zdinputs[ii].Nw = Nw; // set widget count Nzd++; // add zdialog to end of zdinputs return 1; } // Restore user input fields from prior use of the same dialog. // Call this if wanted after zdialog is built and before it is run. // Override old user inputs with zdialog_stuff() where needed. int zdialog_restore_inputs(zdialog *zd) { using namespace zdinputs_names; char *zdtitle, *wname, *wdata; int ii, jj; zd->saveinputs = 1; // flag, save data at zdialog_free() zdtitle = (char *) zd->widget[0].data; // zdialog title for (ii = 0; ii < Nzd; ii++) // find zdialog in zdinputs if (strmatch(zdtitle,zdinputs[ii].zdtitle)) break; if (ii == Nzd) return 0; // not found for (jj = 0; jj < zdinputs[ii].Nw; jj++) { // stuff all saved widget data wname = zdinputs[ii].wname[jj]; wdata = zdinputs[ii].wdata[jj]; zdialog_put_data(zd,wname,wdata); } return 1; } /********************************************************************************/ // move a window to the mouse position // widget is a GtkWindow, which may or may not be realized void window_to_mouse(GtkWidget *window) // 6.8 { using namespace zfuncs; int px, py; gdk_device_get_position(mouse,&screen,&px,&py); // get mouse position gtk_window_move(GTK_WINDOW(window),px,py); return; } /********************************************************************************/ // popup window with scrolling text report namespace popup_report_names { int popup_report_timeout(GtkWidget **win); void popup_report_destroy(GtkWidget *win); GtkWidget *mWin[10], *mVbox, *mScroll; GtkWidget *mLog, *mHead, *hSep; GtkAdjustment *vscroll; char buff[500]; int bcc = 500; int wii = 0; } // open the report window with given title and pixel dimensions // overhauled 6.7 GtkWidget * popup_report_open(GtkWidget *parent, cchar *title, int ww, int hh) { using namespace popup_report_names; GtkWidget *xWin; xWin = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create main window if (! xWin) return 0; if (++wii > 9) wii = 0; mWin[wii] = xWin; gtk_window_set_title(GTK_WINDOW(xWin),title); gtk_window_set_default_size(GTK_WINDOW(xWin),ww,hh); G_SIGNAL(xWin,"destroy",popup_report_destroy,xWin); // connect window destroy signal if (parent) { gtk_window_set_transient_for(GTK_WINDOW(xWin),GTK_WINDOW(parent)); gtk_window_set_position(GTK_WINDOW(xWin),GTK_WIN_POS_CENTER_ON_PARENT); } else gtk_window_set_position(GTK_WINDOW(xWin),GTK_WIN_POS_MOUSE); mVbox = gtk_box_new(VERTICAL,0); // vertical packing box gtk_container_add(GTK_CONTAINER(xWin),mVbox); // add to main window mScroll = gtk_scrolled_window_new(0,0); // scrolled window gtk_box_pack_end(GTK_BOX(mVbox),mScroll,1,1,0); // add to main window mVbox mLog = gtk_text_view_new(); // text edit window if (! mLog) return 0; #if GTK_CHECK_VERSION(3,18,0) gtk_text_view_set_top_margin(GTK_TEXT_VIEW(mLog),2); gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(mLog),2); #endif gtk_container_add(GTK_CONTAINER(mScroll),mLog); // add to scrolled window gtk_text_view_set_editable(GTK_TEXT_VIEW(mLog),0); // non-editable ///if (! parent) gtk_window_set_keep_above(GTK_WINDOW(xWin),1); // removed 6.8 vscroll = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(mScroll)); gtk_widget_show_all(xWin); // show window zfuncs::open_popup_windows++; return mLog; } // write a non-scrolling header line void popup_report_header(int bold, cchar *format, ...) { using namespace popup_report_names; va_list arglist; char message[1000]; va_start(arglist,format); vsnprintf(message,999,format,arglist); // stop overflow, remove warning va_end(arglist); if (! mWin[wii]) return; mHead = gtk_text_view_new(); // text window for header line #if GTK_CHECK_VERSION(3,18,0) gtk_text_view_set_top_margin(GTK_TEXT_VIEW(mHead),2); gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(mHead),2); #endif gtk_box_pack_start(GTK_BOX(mVbox),mHead,0,0,0); // add to main window mVbox gtk_text_view_set_editable(GTK_TEXT_VIEW(mHead),0); // non-editable hSep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); gtk_box_pack_start(GTK_BOX(mVbox),hSep,0,0,0); gtk_widget_show_all(mWin[wii]); // show new widgets textwidget_append(mHead,bold,message); // 6.8 return; } // write a text line void popup_report_write(int bold, cchar *format, ...) { using namespace popup_report_names; va_list arglist; char message[1000]; double upper; va_start(arglist,format); vsnprintf(message,999,format,arglist); // stop overflow, remove warning va_end(arglist); if (! mWin[wii]) return; textwidget_append(mLog,bold,"%s",message); // 6.8 upper = gtk_adjustment_get_upper(vscroll); // scroll down gtk_adjustment_set_value(vscroll,upper); // (GTK did not need this earlier) return; } // scroll window back to top line void popup_report_top() { using namespace popup_report_names; if (! mWin[wii]) return; textwidget_scroll(mLog,0); // 6.8 return; } // move the window the given x/y distances in pixels void popup_report_move(int ww, int hh) { using namespace popup_report_names; int posx, posy; if (! mWin[wii]) return; gtk_window_get_position(GTK_WINDOW(mWin[wii]),&posx,&posy); gtk_window_move(GTK_WINDOW(mWin[wii]),posx+ww,posy+hh); return; } // close report after given seconds (OK to leave it open until user closes) // also connected to window destroy signal (secs = 0) void popup_report_close(int secs) { using namespace popup_report_names; if (! mWin[wii]) return; if (secs < 1) { gtk_widget_destroy(mWin[wii]); return; } g_timeout_add_seconds(secs,(GSourceFunc) popup_report_timeout,&mWin[wii]); return; } // private function for report timeout int popup_report_names::popup_report_timeout(GtkWidget **win) { using namespace popup_report_names; int ii; if (! *win) return 0; for (ii = 0; ii < 10; ii++) if (*win == mWin[ii]) break; if (ii < 10) gtk_widget_destroy(*win); return 0; } // private function connected to window destroy signal void popup_report_names::popup_report_destroy(GtkWidget *win) { using namespace popup_report_names; int ii; for (ii = 0; ii < 10; ii++) if (win == mWin[ii]) break; if (ii < 10) { mWin[ii] = 0; zfuncs::open_popup_windows--; } return; } /********************************************************************************/ // execute a command and show the output in a scrolling popup window int popup_command(cchar *command, int ww, int hh, GtkWidget *parent, int top) { char *buff; int err, contx = 0; popup_report_open(parent,command,ww,hh); while ((buff = command_output(contx,command))) { popup_report_write(0,"%s\n",buff); zfree(buff); } if (top) popup_report_top(); // back to top of window 6.0 err = command_status(contx); return err; } /********************************************************************************/ // display message box and wait for user acknowledgement // may be called from a thread (uses xterm message). void zmessageACK(GtkWidget *parent, cchar *format, ... ) { va_list arglist; char message[1000], title[50]; cchar *posn = "mouse"; zdialog *zd; va_start(arglist,format); vsnprintf(message,999,format,arglist); va_end(arglist); printz("%s \n",message); // output to log if (! pthread_equal(pthread_self(),zfuncs::tid_main)) { // from a thread, no GTK allowed zpopup_message(0,message); // alternative popup return; } strncpy0(title,message,50); // 6.8 zd = zdialog_new(title,parent,"OK",null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",message,"space=5"); zdialog_resize(zd,200,0); zdialog_set_modal(zd); zdialog_set_decorated(zd,0); if (parent) posn = "parent"; // 6.1 else posn = "desktop"; // 6.5 zdialog_run(zd,0,posn); zdialog_present(zd); // help wayland 6.8 zdialog_wait(zd); zdialog_free(zd); return; } /********************************************************************************/ // display message box and wait for user Yes or No response // returns 1 or 0 int zmessageYN(GtkWidget *parent, cchar *format, ... ) { va_list arglist; char message[400], title[50]; zdialog *zd; int zstat; va_start(arglist,format); vsnprintf(message,400,format,arglist); va_end(arglist); strncpy0(title,message,50); // 6.8 zd = zdialog_new(title,parent,ZTX("Yes"),ZTX("No"),null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",message,"space=5"); zdialog_resize(zd,200,0); zdialog_set_modal(zd); zdialog_set_decorated(zd,0); zdialog_run(zd,0,"parent"); // position on parent 6.5 zdialog_present(zd); // help wayland 6.8 zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat == 1) return 1; return 0; } /********************************************************************************/ // display message until timeout (can be forever) or user cancel // or caller kills it with zdialog_free() typedef struct { zdialog *zd; int uniqueID; } zdx_t; zdialog * zmessage_post(GtkWidget *parent, int seconds, cchar *format, ... ) { int zmessage_post_timeout(zdx_t *zdx); va_list arglist; char message[400], title[50]; static zdx_t zdx[100]; static int ii = 0; zdialog *zd; va_start(arglist,format); vsnprintf(message,400,format,arglist); va_end(arglist); strncpy0(title,message,50); // 6.8 zd = zdialog_new(title,parent,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",message,"space=5"); zdialog_set_decorated(zd,0); zdialog_run(zd,0,"20/20"); // mouse position 6.8 zdialog_present(zd); // help wayland 6.8 if (parent) gtk_window_present(GTK_WINDOW(parent)); // return focus to parent 6.2 if (seconds) { if (ii < 99) ii++; // track unique zdialogs 6.2 else ii = 0; zdx[ii].zd = zd; zdx[ii].uniqueID = zd->uniqueID; g_timeout_add_seconds(seconds,(GSourceFunc) zmessage_post_timeout,&zdx[ii]); } return zd; } // same as above, but message is big, bold and red zdialog * zmessage_post_bold(GtkWidget *parent, int seconds, cchar *format, ... ) { int zmessage_post_timeout(zdx_t *zdx); va_list arglist; char message[400], messagebold[460], title[50]; static zdx_t zdx[100]; static int ii = 0; zdialog *zd; va_start(arglist,format); vsnprintf(message,400,format,arglist); va_end(arglist); snprintf(messagebold,460,"%s",message); strncpy0(title,message,50); // 6.8 zd = zdialog_new(title,parent,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",messagebold,"space=5"); zdialog_set_decorated(zd,0); zdialog_run(zd,0,"20/20"); // mouse position 6.8 zdialog_present(zd); // help wayland 6.8 if (parent) gtk_window_present(GTK_WINDOW(parent)); // return focus to parent 6.2 if (seconds) { if (ii < 99) ii++; // track unique zdialogs 6.2 else ii = 0; zdx[ii].zd = zd; zdx[ii].uniqueID = zd->uniqueID; g_timeout_add_seconds(seconds,(GSourceFunc) zmessage_post_timeout,&zdx[ii]); } return zd; } int zmessage_post_timeout(zdx_t *zdx) { zdialog *zd = zdx->zd; // check unique zdialog active 6.2 if (! zdialog_valid(zd)) return 0; if (zd->uniqueID != zdx->uniqueID) return 0; zdialog_free(zd); return 0; } /********************************************************************************/ // get text input from a popup dialog // returned text is subject for zfree() // null is returned if user presses [cancel] button. char * zdialog_text(GtkWidget *parent, cchar *title, cchar *inittext) { zdialog *zd; int zstat; char *text; zd = zdialog_new(title,parent,"OK",ZTX("cancel"),null); if (! title) zdialog_set_decorated(zd,0); // 6.1 zdialog_add_widget(zd,"frame","fred","dialog"); zdialog_add_widget(zd,"edit","edit","fred"); if (inittext) zdialog_stuff(zd,"edit",inittext); zdialog_resize(zd,200,0); zdialog_set_modal(zd); zdialog_run(zd,0,"mouse"); zdialog_present(zd); // help wayland 6.8 zstat = zdialog_wait(zd); if (zstat == 1) text = (char *) zdialog_get_data(zd,"edit"); else text = 0; if (text) text = zstrdup(text); zdialog_free(zd); return text; } /********************************************************************************/ // Display a dialog with a message and 1-5 choice buttons. // Returns choice 1-N corresponding to button selected. // nn = zdialog_choose(parent,message,butt1,butt2, ...null) int zdialog_choose(GtkWidget *parent, cchar *message, ...) { zdialog *zd; va_list arglist; int ii, zstat, Nbutn; cchar *butn[6]; char title[50]; va_start(arglist,message); for (ii = 0; ii < 5; ii++) { butn[ii] = va_arg(arglist,cchar *); if (! butn[ii]) break; } Nbutn = ii; if (! Nbutn) zappcrash("zdialog_choose(), no buttons"); repeat: strncpy0(title,message,50); // 6.8 zd = zdialog_new(title,parent,butn[0],butn[1],butn[2],butn[3],butn[4],null); zdialog_add_widget(zd,"hbox","hbmess","dialog","space=3"); zdialog_add_widget(zd,"label","labmess","hbmess",message,"space=5"); zdialog_set_modal(zd); zdialog_set_decorated(zd,0); zdialog_run(zd,0,"parent"); zdialog_present(zd); // help wayland 6.8 zstat = zdialog_wait(zd); zdialog_free(zd); if (zstat < 1) goto repeat; return zstat; } /********************************************************************************/ // functions to show popup text windows namespace poptext { char *ptext = 0; GtkWidget *popwin = 0; char *pcurrent = 0; #define GSFNORMAL GTK_STATE_FLAG_NORMAL } // timer function to show popup window after a given time int poptext_show(char *current) { using namespace poptext; if (current != pcurrent) return 0; if (popwin) gtk_widget_show_all(popwin); return 0; } // timer function to kill popup window after a given time int poptext_timeout(char *current) { using namespace poptext; if (current != pcurrent) return 0; if (popwin) gtk_widget_destroy(popwin); if (ptext) zfree(ptext); popwin = 0; ptext = 0; return 0; } // kill popup window unconditionally int poptext_killnow() { using namespace poptext; if (popwin) gtk_widget_destroy(popwin); if (ptext) zfree(ptext); popwin = 0; ptext = 0; return 0; } // Show a popup text message at current mouse position + offsets. // Any prior popup will be killed and replaced. // If text == null, kill without replacement. // secs1 is time to delay before showing the popup. // secs2 is time to kill the popup after it is shown. // This function returns immediately. void poptext_mouse(GtkWidget *parent, cchar *text, int dx, int dy, float secs1, float secs2) { using namespace poptext; GtkWidget *label; int cc, mx, my; int millisec1, millisec2; if (popwin) gtk_widget_destroy(popwin); // kill prior popup if (ptext) zfree(ptext); popwin = 0; ptext = 0; pcurrent++; // make current != pcurrent if (! text) return; cc = strlen(text); // construct popup window ptext = (char *) zmalloc(cc+1); // with caller's text strcpy(ptext,text); popwin = gtk_window_new(GTK_WINDOW_POPUP); gtk_window_set_transient_for(GTK_WINDOW(popwin),GTK_WINDOW(parent)); // fedora, set parent 6.6 label = gtk_label_new(ptext); gtk_container_add(GTK_CONTAINER(popwin),label); gdk_device_get_position(zfuncs::mouse,0,&mx,&my); // position popup window gtk_window_move(GTK_WINDOW(popwin),mx+dx,my+dy); if (secs1 > 0) { // delayed popup display millisec1 = secs1 * 1000; g_timeout_add(millisec1,(GSourceFunc) poptext_show,pcurrent); } else gtk_widget_show_all(popwin); // immediate display if (secs2 > 0) { // popup kill timer millisec2 = (secs1 + secs2) * 1000; g_timeout_add(millisec2,(GSourceFunc) poptext_timeout,pcurrent); } return; } /********************************************************************************/ // Show a popup text message at the given window position. // Any prior popup will be killed and replaced. // If text == null, kill without replacement. // secs1 is time to delay before showing the popup. // secs2 is time to kill the popup after it is shown (-1 = never). // This function returns immediately. void poptext_window(cchar *text, GtkWindow *pwin, int mx, int my, float secs1, float secs2) { using namespace poptext; GtkWidget *label; int cc, pmx, pmy; int millisec1, millisec2; if (popwin) gtk_widget_destroy(popwin); // kill prior popup if (ptext) zfree(ptext); popwin = 0; ptext = 0; pcurrent++; // make current != pcurrent if (! text) return; cc = strlen(text); // construct popup window ptext = (char *) zmalloc(cc+1); // with caller's text strcpy(ptext,text); popwin = gtk_window_new(GTK_WINDOW_POPUP); label = gtk_label_new(ptext); gtk_container_set_border_width(GTK_CONTAINER(popwin),2); gtk_container_add(GTK_CONTAINER(popwin),label); if (pwin) { // position relative to pwin gtk_window_set_transient_for(GTK_WINDOW(popwin),pwin); gtk_window_get_position(pwin,&pmx,&pmy); mx += pmx; my += pmy; } gtk_window_move(GTK_WINDOW(popwin),mx,my); if (secs1 > 0) { // delayed popup display millisec1 = secs1 * 1000; g_timeout_add(millisec1,(GSourceFunc) poptext_show,pcurrent); } else gtk_widget_show_all(popwin); // immediate display if (secs2 > 0) { // popup kill timer millisec2 = (secs1 + secs2) * 1000; g_timeout_add(millisec2,(GSourceFunc) poptext_timeout,pcurrent); } return; } // Show an image file in a popup window at mouse position. // Re-use most recent window or create a new one if Fnewin != 0. // Returns 0 if OK, +N otherwise. namespace popup_image_names { GtkWidget *window[10], *drawarea[10]; // up to 10 popup windows open char *filex[10], reqfull[10], isfull[10]; int Nval[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int Nw = 0; } int popup_image(cchar *file, GtkWindow *parent, int Fnewin, int size) // 6.0 { using namespace popup_image_names; int popup_image_draw(GtkWidget *, cairo_t *, int &Nw); int popup_image_scroll(GtkWidget *, GdkEvent *event, int &Nw); int popup_image_KBevent(GtkWidget *, GdkEventKey *event, int &Nw); int popup_image_mousebutt(GtkWidget *, GdkEvent *event, int &Nw); int popup_image_state_event(GtkWidget *, GdkEvent *, int &Nw); if (Fnewin) if (++Nw == 10) Nw = 0; // new window, re-use oldest up to 10 if (! Fnewin) while (Nw > 0 && window[Nw] == 0) Nw--; // else re-use latest still active if (window[Nw]) { gtk_widget_destroy(drawarea[Nw]); drawarea[Nw] = 0; zfree(filex[Nw]); filex[Nw] = 0; } else { window[Nw] = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create new popup window if (! window[Nw]) return 1; if (! size) size = 512; gtk_window_set_default_size(GTK_WINDOW(window[Nw]),size,size); if (parent) gtk_window_set_transient_for(GTK_WINDOW(window[Nw]),parent); gtk_window_set_position(GTK_WINDOW(window[Nw]),GTK_WIN_POS_MOUSE); gtk_widget_freeze_child_notify(window[Nw]); // improves resize performance G_SIGNAL(window[Nw],"destroy",gtk_widget_destroyed,&window[Nw]); // set window = null if destroyed } filex[Nw] = zstrdup(file); drawarea[Nw] = gtk_drawing_area_new(); // new drawing area always required if (! drawarea[Nw]) return 2; gtk_container_add(GTK_CONTAINER(window[Nw]),drawarea[Nw]); reqfull[Nw] = isfull[Nw] = 0; // not fullscreen G_SIGNAL(window[Nw],"draw",popup_image_draw,&Nval[Nw]); gtk_widget_add_events(window[Nw],GDK_SCROLL_MASK); G_SIGNAL(window[Nw],"scroll-event",popup_image_scroll,&Nval[Nw]); // connect events 6.5 gtk_widget_add_events(window[Nw],GDK_KEY_PRESS_MASK); G_SIGNAL(window[Nw],"key-press-event",popup_image_KBevent,&Nval[Nw]); gtk_widget_add_events(window[Nw],GDK_BUTTON_RELEASE_MASK); G_SIGNAL(window[Nw],"button-release-event",popup_image_mousebutt,&Nval[Nw]); G_SIGNAL(window[Nw],"window-state-event",popup_image_state_event,&Nval[Nw]); gtk_widget_show_all(window[Nw]); return 0; } // resize image and repaint window when resized int popup_image_draw(GtkWidget *window, cairo_t *cr, int &nn) { using namespace popup_image_names; PIXBUF *pixb1, *pixb2; GError *gerror; int ww1, hh1, ww2, hh2; int sww, shh; double area; char *file; cchar *pp; file = filex[nn]; if (! file) return 1; pp = strrchr(file,'/'); // window title = file name gtk_window_set_title(GTK_WINDOW(window),pp+1); gerror = 0; pixb1 = gdk_pixbuf_new_from_file(file,&gerror); // load image file into pixbuf if (! pixb1) { printz("*** file: %s \n %s \n",file,gerror->message); return 1; } ww1 = gdk_pixbuf_get_width(pixb1); // image dimensions hh1 = gdk_pixbuf_get_height(pixb1); sww = monitor_ww; // 6.6 shh = monitor_hh; gtk_window_get_size(GTK_WINDOW(window),&ww2,&hh2); // current window dimensions area = ww2 * hh2; ww2 = sqrt(area * ww1 / hh1); // fit window to image, keeping same area hh2 = area / ww2; if (ww2 < sww && hh2 < shh) // prevent GTK resize event loop gtk_window_resize(GTK_WINDOW(window),ww2,hh2); pixb2 = gdk_pixbuf_scale_simple(pixb1,ww2,hh2,GDK_INTERP_BILINEAR); // rescale pixbuf to window if (! pixb2) return 1; gdk_cairo_set_source_pixbuf(cr,pixb2,0,0); // draw image cairo_paint(cr); g_object_unref(pixb1); g_object_unref(pixb2); return 1; } // respond to mouse scroll button and zoom window larger or smaller int popup_image_scroll(GtkWidget *window, GdkEvent *event, int &nn) { using namespace popup_image_names; int scroll, ww, hh; int sww, shh; double ff = 1.0; if (event->type == GDK_SCROLL) { // mouse wheel event scroll = ((GdkEventScroll *) event)->direction; if (scroll == GDK_SCROLL_UP) ff = 1.33333; if (scroll == GDK_SCROLL_DOWN) ff = 0.75; } gtk_window_get_size(GTK_WINDOW(window),&ww,&hh); // current window dimensions ww *= ff; // new dimensions hh *= ff; sww = monitor_ww; // 6.6 shh = monitor_hh; if (ww > sww || hh > shh) { // request > screen size, fullscreen 6.5 reqfull[nn] = 1; gtk_window_fullscreen(GTK_WINDOW(window)); return 1; } reqfull[nn] = 0; gtk_window_unfullscreen(GTK_WINDOW(window)); if (ww + hh > 500) gtk_window_resize(GTK_WINDOW(window),ww,hh); // rescale up or down else gtk_widget_destroy(window); // if very small, delete window return 1; } // respond to KB events F11 (fullscreen/unfullscreen) and Escape (destroy) int popup_image_KBevent(GtkWidget *window, GdkEventKey *event, int &nn) { using namespace popup_image_names; int KBkey = event->keyval; if (KBkey == GDK_KEY_Escape) gtk_widget_destroy(window); if (KBkey != GDK_KEY_F11) return 1; if (reqfull[nn]) { reqfull[nn] = 0; gtk_window_unfullscreen(GTK_WINDOW(window)); } else { reqfull[nn] = 1; gtk_window_fullscreen(GTK_WINDOW(window)); } return 1; } // respond to mouse button - destroy window int popup_image_mousebutt(GtkWidget *window, GdkEvent *event, int &nn) // 6.5 { gtk_widget_destroy(window); return 1; } // track window fullscreen state int popup_image_state_event(GtkWidget *window, GdkEvent *event, int &nn) // 6.5 { using namespace popup_image_names; int state = ((GdkEventWindowState *) event)->new_window_state; if (state & GDK_WINDOW_STATE_FULLSCREEN) isfull[nn] = 1; else isfull[nn] = 0; if (isfull[nn] != reqfull[nn]) { // compensate GTK bug: FIXME if (reqfull[nn]) gtk_window_fullscreen(GTK_WINDOW(window)); // the window fullscreens itself after else gtk_window_unfullscreen(GTK_WINDOW(window)); // being requested to unfullscreen } return 1; } /******************************************************************************** File chooser dialog for one or more files Action: "file" select an existing file "files" select multiple existing files "save" select an existing or new file "folder" select existing folder "folders" select multiple existing folders "create folder" select existing or new folder hidden if > 0, add button to toggle display of hidden files optional, default = 0 Returns a list of filespecs terminated with null. Memory for returned list and returned files are subjects for zfree(); *********************************************************************************/ // version for 1 file only: file, save, folder, create folder // returns one filespec or null // returned file is subject for zfree() char * zgetfile(cchar *title, GtkWindow *parent, cchar *action, cchar *initfile, int hidden) { if (! strmatchV(action,"file","save","folder","create folder",null)) zappcrash("zgetfile() call error: %s",action); char **flist = zgetfiles(title,parent,action,initfile,hidden); // parent added 6.1 if (! flist) return 0; char *file = *flist; zfree(flist); return file; } // version for 2 or more files // returns a list of filespecs (char **) terminated with null // returns null if canceled by user char ** zgetfiles(cchar *title, GtkWindow *parent, cchar *action, cchar *initfile, int hidden) { void zgetfile_preview(GtkWidget *dialog, GtkWidget *pvwidget); // private functions int zgetfile_KBkey(GtkWidget *dialog, GdkEventKey *event, int &fcdes); void zgetfile_newfolder(GtkFileChooser *dialog, void *); GtkFileChooserAction fcact = GTK_FILE_CHOOSER_ACTION_OPEN; GtkWidget *dialog; PIXBUF *thumbnail; GtkWidget *pvwidget = gtk_image_new(); GSList *gslist = 0; cchar *button1 = 0, *buttxx = 0; char *pdir, *pfile; int ii, err, NF, setfname = 0; int fcstat, bcode = 0, hide = 1; int fcdes = 0; char *file1, *file2, **flist = 0; STATB fstat; if (strmatch(action,"file")) { fcact = GTK_FILE_CHOOSER_ACTION_OPEN; button1 = ZTX("choose file"); } else if (strmatch(action,"files")) { fcact = GTK_FILE_CHOOSER_ACTION_OPEN; button1 = ZTX("choose files"); } else if (strmatch(action,"save")) { fcact = GTK_FILE_CHOOSER_ACTION_SAVE; button1 = ZTX("save"); setfname = 1; } else if (strmatch(action,"folder")) { fcact = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; button1 = ZTX("choose folder"); } else if (strmatch(action,"folders")) { fcact = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; button1 = ZTX("choose folders"); } else if (strmatch(action,"create folder")) { fcact = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; button1 = ZTX("create folder"); setfname = 1; } else zappcrash("zgetfiles() call error: %s",action); if (hidden) { buttxx = ZTX("hidden"); bcode = 103; } dialog = gtk_file_chooser_dialog_new(title, parent, fcact, // create file selection dialog button1, GTK_RESPONSE_ACCEPT, // parent added 6.1 ZTX("cancel"), GTK_RESPONSE_CANCEL, buttxx, bcode, null); gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog),pvwidget); G_SIGNAL(dialog,"update-preview",zgetfile_preview,pvwidget); // create preview for selected file G_SIGNAL(dialog,"key-press-event",zgetfile_KBkey,&fcdes); // respond to special KB keys gtk_window_set_position(GTK_WINDOW(dialog),GTK_WIN_POS_MOUSE); // put dialog at mouse position gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),0); // default: no show hidden if (strmatch(action,"save")) // overwrite confirmation gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),1); if (strmatch(action,"files") || strmatch(action,"folders")) gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog),1); // select multiple files or folders if (initfile) { // pre-select filespec err = stat(initfile,&fstat); if (err) { pdir = zstrdup(initfile); // non-existent file pfile = strrchr(pdir,'/'); if (pfile && pfile > pdir) { *pfile++ = 0; // set folder name gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),pdir); } if (setfname) { // set new file name if (! pfile) pfile = (char *) initfile; // 6.0 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),pfile); } zfree(pdir); } else if (S_ISREG(fstat.st_mode)) // select given file gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),initfile); else if (S_ISDIR(fstat.st_mode)) // select given folder gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),initfile); } if (initfile) { thumbnail = get_thumbnail(initfile,256); // preview for initial file 6.0 if (thumbnail) { gtk_image_set_from_pixbuf(GTK_IMAGE(pvwidget),thumbnail); gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),1); g_object_unref(thumbnail); } else gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),0); } gtk_widget_show_all(dialog); while (true) { fcstat = gtk_dialog_run(GTK_DIALOG(dialog)); // run dialog, get status button if (fcstat == 103) { // show/hide hidden files hide = 1 - hide; gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),hide); continue; } else if (fcstat == GTK_RESPONSE_ACCEPT) { gslist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); if (! gslist) continue; NF = g_slist_length(gslist); // no. selected files flist = (char **) zmalloc((NF+1)*sizeof(char *)); // allocate returned list for (ii = 0; ii < NF; ii++) { // process selected files file1 = (char *) g_slist_nth_data(gslist,ii); file2 = zstrdup(file1); // re-allocate memory flist[ii] = file2; g_free(file1); } flist[ii] = 0; // EOL marker break; } else break; // user bailout } if (gslist) g_slist_free(gslist); // return selected file(s) if (! fcdes) gtk_widget_destroy(dialog); // destroy if not already 6.5 return flist; } // zgetfile private function - get preview images for image files void zgetfile_preview(GtkWidget *dialog, GtkWidget *pvwidget) { PIXBUF *thumbnail; char *filename; filename = gtk_file_chooser_get_preview_filename(GTK_FILE_CHOOSER(dialog)); if (! filename) { gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),0); return; } thumbnail = get_thumbnail(filename,256); // 256x256 pixels g_free(filename); if (thumbnail) { gtk_image_set_from_pixbuf(GTK_IMAGE(pvwidget),thumbnail); gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),1); g_object_unref(thumbnail); } else gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(dialog),0); return; } // zgetfile private function - KB functions int zgetfile_KBkey(GtkWidget *dialog, GdkEventKey *event, int &fcdes) { int KBkey = event->keyval; if (KBkey == GDK_KEY_F1) { // F1 = help KBevent(event); return 1; } if (KBkey == GDK_KEY_Escape) { // escape = cancel 6.5 gtk_widget_destroy(dialog); fcdes = 1; return 1; } return 0; } /******************************************************************************** print_image_file(GtkWidget *parent, cchar *imagefile) Print an image file using the printer, paper, orientation, margins, and scale set by the user. HPLIP problem: Setting paper size was made less flexible. GtkPrintSettings paper size must agree with the one in the current printer setup. This can only be set in the printer setup dialog, not in the application. Also the print size (width, height) comes from the chosen paper size and cannot be changed in the application. Print margins can be changed to effect printing a smaller or shifted image on a larger paper size. *********************************************************************************/ namespace print_image { #define MM GTK_UNIT_MM #define INCH GTK_UNIT_INCH #define PRINTOP GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG #define PORTRAIT GTK_PAGE_ORIENTATION_PORTRAIT #define LANDSCAPE GTK_PAGE_ORIENTATION_LANDSCAPE #define QUALITY GTK_PRINT_QUALITY_HIGH GtkWidget *parent = 0; GtkPageSetup *priorpagesetup = 0; GtkPageSetup *pagesetup; GtkPrintSettings *printsettings = 0; GtkPrintOperation *printop; GtkPageOrientation orientation = PORTRAIT; PIXBUF *pixbuf; cchar *printer = 0; int landscape = 0; // true if landscape double width = 21.0, height = 29.7; // paper size, CM (default A4 portrait) double margins[4] = { 0.5, 0.5, 0.5, 0.5 }; // margins, CM (default 0.5) double imagescale = 100; // image print scale, percent double pwidth, pheight; // printed image size int page_setup(); int margins_setup(); int margins_dialog_event(zdialog *zd, cchar *event); void get_printed_image_size(); void print_page(GtkPrintOperation *, GtkPrintContext *, int page); } // user callable function to set paper, margins, scale, and then print void print_image_file(GtkWidget *pwin, cchar *imagefile) { using namespace print_image; GtkPrintOperationResult printstat; GError *gerror = 0; int err; parent = pwin; // save parent window pixbuf = gdk_pixbuf_new_from_file(imagefile,&gerror); // read image file if (! pixbuf) { zmessageACK(null,gerror->message); return; } err = page_setup(); // select size and orientation if (err) return; err = margins_setup(); // set margins and scale if (err) return; printop = gtk_print_operation_new(); // print operation gtk_print_operation_set_default_page_setup(printop,pagesetup); gtk_print_operation_set_print_settings(printop,printsettings); gtk_print_operation_set_n_pages(printop,1); g_signal_connect(printop,"draw-page",G_CALLBACK(print_page),0); // start print printstat = gtk_print_operation_run(printop,PRINTOP,0,0); if (printstat == GTK_PRINT_OPERATION_RESULT_ERROR) { gtk_print_operation_get_error(printop,&gerror); zmessageACK(null,gerror->message); } g_object_unref(printop); return; } // draw the graphics for the print page // rescale with cairo void print_image::print_page(GtkPrintOperation *printop, GtkPrintContext *printcontext, int page) { using namespace print_image; cairo_t *cairocontext; double iww, ihh, pww, phh, scale; pww = gtk_print_context_get_width(printcontext); // print context size, pixels phh = gtk_print_context_get_height(printcontext); iww = gdk_pixbuf_get_width(pixbuf); // original image size ihh = gdk_pixbuf_get_height(pixbuf); scale = pww / iww; // rescale to fit page if (phh / ihh < scale) scale = phh / ihh; cairocontext = gtk_print_context_get_cairo_context(printcontext); // use cairo to rescale cairo_translate(cairocontext,0,0); cairo_scale(cairocontext,scale,scale); gdk_cairo_set_source_pixbuf(cairocontext,pixbuf,0,0); cairo_paint(cairocontext); return; } // Do a print paper format selection, after which the page width, height // and orientation are available to the caller. Units are CM. // (paper width and height are reversed for landscape orientation) int print_image::page_setup() { using namespace print_image; char printsettingsfile[200], pagesetupfile[200]; snprintf(printsettingsfile,200,"%s/printsettings",zhomedir); snprintf(pagesetupfile,200,"%s/pagesetup",zhomedir); if (! printsettings) { // start with prior print settings printsettings = gtk_print_settings_new_from_file(printsettingsfile,0); if (! printsettings) printsettings = gtk_print_settings_new(); } if (! priorpagesetup) { // start with prior page setup priorpagesetup = gtk_page_setup_new_from_file(pagesetupfile,0); if (! priorpagesetup) priorpagesetup = gtk_page_setup_new(); } pagesetup = gtk_print_run_page_setup_dialog // select printer, paper, orientation (GTK_WINDOW(parent),priorpagesetup,printsettings); // user cancel cannot be detected g_object_unref(priorpagesetup); // save for next call priorpagesetup = pagesetup; orientation = gtk_print_settings_get_orientation(printsettings); // save orientation if (orientation == LANDSCAPE) landscape = 1; else landscape = 0; gtk_print_settings_set_quality(printsettings,QUALITY); // set high quality 300 dpi gtk_print_settings_set_resolution(printsettings,300); gtk_print_settings_to_file(printsettings,printsettingsfile,0); // save print settings to file gtk_page_setup_to_file(pagesetup,pagesetupfile,0); // save print settings to file return 0; } // Optionally set the print margins and print scale. // If canceled the margins are zero (or printer-dependent minimum) // and the scale is 100% (fitting the paper and margins). int print_image::margins_setup() { using namespace print_image; zdialog *zd; int zstat; /*** __________________________________________________ | [x] (-) [_] Margins | | | | Margins Top Bottom Left Right | | CM [ 0.50 ] [ 0.50 ] [ 0.50 ] [ 0.50 ] | | Inch [ 0.20 ] [ 0.20 ] [ 0.20 ] [ 0.20 ] | | | | image scale [ 80 ] percent | | | | image width height | | CM xx.x xx.x | | Inch xx.x xx.x | | [done] [cancel] | |__________________________________________________| ***/ zd = zdialog_new(ZTX("Margins"),parent,ZTX("done"),ZTX("cancel"),null); zdialog_add_widget(zd,"hbox","hbmlab","dialog"); zdialog_add_widget(zd,"vbox","vbmarg","hbmlab",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vbtop","hbmlab",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vbbottom","hbmlab",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vbleft","hbmlab",0,"homog|space=3"); zdialog_add_widget(zd,"vbox","vbright","hbmlab",0,"homog|space=3"); zdialog_add_widget(zd,"label","labmarg","vbmarg",ZTX("Margins"),"space=5"); zdialog_add_widget(zd,"label","labcm","vbmarg","CM","space=5"); zdialog_add_widget(zd,"label","labinch","vbmarg","Inch","space=5"); zdialog_add_widget(zd,"label","labtop","vbtop",ZTX("Top")); zdialog_add_widget(zd,"zspin","mtopcm","vbtop","0|10|0.01|0"); zdialog_add_widget(zd,"zspin","mtopin","vbtop","0|4|0.01|0"); zdialog_add_widget(zd,"label","labbot","vbbottom",ZTX("Bottom")); zdialog_add_widget(zd,"zspin","mbottcm","vbbottom","0|10|0.01|0"); zdialog_add_widget(zd,"zspin","mbottin","vbbottom","0|4|0.01|0"); zdialog_add_widget(zd,"label","lableft","vbleft",ZTX("Left")); zdialog_add_widget(zd,"zspin","mleftcm","vbleft","0|10|0.01|0"); zdialog_add_widget(zd,"zspin","mleftin","vbleft","0|4|0.01|0"); zdialog_add_widget(zd,"label","labright","vbright",ZTX("Right")); zdialog_add_widget(zd,"zspin","mrightcm","vbright","0|10|0.01|0"); zdialog_add_widget(zd,"zspin","mrightin","vbright","0|4|0.01|0"); zdialog_add_widget(zd,"hbox","hbscale","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labscale","hbscale",ZTX("image scale"),"space=5"); zdialog_add_widget(zd,"zspin","scale","hbscale","5|100|1|100"); zdialog_add_widget(zd,"label","labpct","hbscale",ZTX("percent"),"space=5"); zdialog_add_widget(zd,"hbox","hbsize","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vbunit","hbsize",0,"space=5"); zdialog_add_widget(zd,"vbox","vbwidth","hbsize",0,"space=5"); zdialog_add_widget(zd,"vbox","vbheight","hbsize",0,"space=5"); zdialog_add_widget(zd,"label","space","vbunit",ZTX("Image")); zdialog_add_widget(zd,"label","labcm","vbunit","CM"); zdialog_add_widget(zd,"label","labinch","vbunit","Inch"); zdialog_add_widget(zd,"label","labwidth","vbwidth",ZTX("Width")); zdialog_add_widget(zd,"label","labwcm","vbwidth","xx.x"); zdialog_add_widget(zd,"label","labwin","vbwidth","xx.x"); zdialog_add_widget(zd,"label","labheight","vbheight",ZTX("Height")); zdialog_add_widget(zd,"label","labhcm","vbheight","xx.x"); zdialog_add_widget(zd,"label","labhin","vbheight","xx.x"); zdialog_restore_inputs(zd); // recall prior settings zdialog_fetch(zd,"mtopcm",margins[0]); zdialog_fetch(zd,"mbottcm",margins[1]); zdialog_fetch(zd,"mleftcm",margins[2]); zdialog_fetch(zd,"mrightcm",margins[3]); zdialog_fetch(zd,"scale",imagescale); get_printed_image_size(); zdialog_stuff(zd,"labwcm",pwidth,"%.2f"); // update image size in dialog zdialog_stuff(zd,"labhcm",pheight,"%.2f"); zdialog_stuff(zd,"labwin",pwidth/2.54,"%.2f"); zdialog_stuff(zd,"labhin",pheight/2.54,"%.2f"); gtk_page_setup_set_top_margin(pagesetup,10*margins[0],MM); // set page margins gtk_page_setup_set_bottom_margin(pagesetup,10*margins[1],MM); // (cm to mm units) gtk_page_setup_set_left_margin(pagesetup,10*margins[2],MM); gtk_page_setup_set_right_margin(pagesetup,10*margins[3],MM); gtk_print_settings_set_scale(printsettings,imagescale); // set image print scale % zdialog_run(zd,margins_dialog_event,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion zdialog_free(zd); // kill dialog if (zstat == 1) return 0; return 1; } // dialog event function // save user margin and scale changes // recompute print image size int print_image::margins_dialog_event(zdialog *zd, cchar *event) { using namespace print_image; double temp; if (strmatch(event,"escape")) { // escape = cancel 6.5 zd->zstat = 2; return 1; } if (strmatch(event,"mtopcm")) { // get cm inputs and set inch values zdialog_fetch(zd,"mtopcm",margins[0]); zdialog_stuff(zd,"mtopin",margins[0]/2.54); } if (strmatch(event,"mbottcm")) { zdialog_fetch(zd,"mbottcm",margins[1]); zdialog_stuff(zd,"mbottin",margins[1]/2.54); } if (strmatch(event,"mleftcm")) { zdialog_fetch(zd,"mleftcm",margins[2]); zdialog_stuff(zd,"mleftin",margins[2]/2.54); } if (strmatch(event,"mrightcm")) { zdialog_fetch(zd,"mrightcm",margins[3]); zdialog_stuff(zd,"mrightin",margins[3]/2.54); } if (strmatch(event,"mtopin")) { // get inch inputs and set cm values zdialog_fetch(zd,"mtopin",temp); margins[0] = temp * 2.54; zdialog_stuff(zd,"mtopcm",margins[0]); } if (strmatch(event,"mbottin")) { zdialog_fetch(zd,"mbottin",temp); margins[1] = temp * 2.54; zdialog_stuff(zd,"mbottcm",margins[1]); } if (strmatch(event,"mleftin")) { zdialog_fetch(zd,"mleftin",temp); margins[2] = temp * 2.54; zdialog_stuff(zd,"mleftcm",margins[2]); } if (strmatch(event,"mrightin")) { zdialog_fetch(zd,"mrightin",temp); margins[3] = temp * 2.54; zdialog_stuff(zd,"mrightcm",margins[3]); } zdialog_fetch(zd,"scale",imagescale); // get image scale get_printed_image_size(); zdialog_stuff(zd,"labwcm",pwidth,"%.2f"); // update image size in dialog zdialog_stuff(zd,"labhcm",pheight,"%.2f"); zdialog_stuff(zd,"labwin",pwidth/2.54,"%.2f"); zdialog_stuff(zd,"labhin",pheight/2.54,"%.2f"); gtk_page_setup_set_top_margin(pagesetup,10*margins[0],MM); // set page margins gtk_page_setup_set_bottom_margin(pagesetup,10*margins[1],MM); // (cm to mm units) gtk_page_setup_set_left_margin(pagesetup,10*margins[2],MM); gtk_page_setup_set_right_margin(pagesetup,10*margins[3],MM); gtk_print_settings_set_scale(printsettings,imagescale); // set image print scale % return 1; } // compute printed image size based on paper size, // orientation, margins, and scale (percent) void print_image::get_printed_image_size() { using namespace print_image; double iww, ihh, pww, phh, scale; pww = 0.1 * gtk_page_setup_get_paper_width(pagesetup,MM); // get paper size phh = 0.1 * gtk_page_setup_get_paper_height(pagesetup,MM); // (mm to cm units) pww = pww - margins[2] - margins[3]; // reduce for margins phh = phh - margins[0] - margins[1]; // bugfix 6.2 pww = pww / 2.54 * 300; // convert to dots @ 300 dpi phh = phh / 2.54 * 300; iww = gdk_pixbuf_get_width(pixbuf); // original image size, pixels ihh = gdk_pixbuf_get_height(pixbuf); scale = pww / iww; // rescale image to fit page if (phh / ihh < scale) scale = phh / ihh; scale = scale * 0.01 * imagescale; // adjust for user scale setting pwidth = iww * scale / 300 * 2.54; // dots to cm pheight = ihh * scale / 300 * 2.54; return; } /********************************************************************************/ // connect a user callback function to a drag-drop source widget void drag_drop_source(GtkWidget *widget, drag_drop_source_func ufunc) // 6.5 { void drag_drop_source2(GtkWidget *, GdkDragContext *, void *ufunc); void drag_drop_source3(GtkWidget *, GdkDragContext *, GtkSelectionData *, int, int, void *ufunc); gtk_drag_source_set(widget,GDK_BUTTON1_MASK,null,0,GDK_ACTION_COPY); gtk_drag_source_add_text_targets(widget); gtk_drag_source_add_image_targets(widget); G_SIGNAL(widget, "drag-begin", drag_drop_source2, ufunc); G_SIGNAL(widget, "drag-data-get", drag_drop_source3, ufunc); return; } // private function for "drag-begin" signal void drag_drop_source2(GtkWidget *widget, GdkDragContext *context, void *ufunc) { drag_drop_source_func *ufunc2; GdkPixbuf *pixbuf; GError *gerror = 0; char *file = 0; ufunc2 = (drag_drop_source_func *) ufunc; file = ufunc2(); if (! file) goto cancel; pixbuf = gdk_pixbuf_new_from_file_at_size(file,128,128,&gerror); if (! pixbuf) { if (gerror) printf("%s \n",gerror->message); return; } gtk_drag_set_icon_pixbuf(context,pixbuf,64,64); // hot spot is middle of image 6.5 return; cancel: printf("drag canceled \n"); return; } // private function for "drag-data-get" signal void drag_drop_source3(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *data, int, int, void *ufunc) { drag_drop_source_func *ufunc2; char *file = 0; // char *files[2] = { file, null }; ufunc2 = (drag_drop_source_func *) ufunc; file = ufunc2(); if (! file) goto cancel; gtk_selection_data_set_text(data,file,-1); // drops text // gtk_selection_data_set_uris(data,files); // does nothing FIXME return; cancel: printf("drag canceled \n"); return; } // connect a user callback function to a drag-drop destination widget void drag_drop_dest(GtkWidget *widget, drag_drop_dest_func *ufunc) { int drag_drop_dest2(GtkWidget *, GdkDragContext *, int, int, void *, int, int time, void *); int drag_drop_dest3(GtkWidget *, void *, int, int, int, void *); gtk_drag_dest_set(widget,GTK_DEST_DEFAULT_ALL,null,0,GDK_ACTION_COPY); gtk_drag_dest_add_text_targets(widget); G_SIGNAL(widget, "drag-data-received", drag_drop_dest2, ufunc); G_SIGNAL(widget, "drag-motion", drag_drop_dest3, ufunc); return; } // private function for "drag-data-received" signal // get dropped file, clean escapes, pass to user function // passed filespec is subject for zfree() int drag_drop_dest2(GtkWidget *, GdkDragContext *context, int mpx, int mpy, void *sdata, int, int time, void *ufunc) { char * drag_drop_unescape(cchar *escaped_string); drag_drop_dest_func *ufunc2; char *text, *text2, *file, *file2; int cc; text = (char *) gtk_selection_data_get_data((GtkSelectionData *) sdata); ufunc2 = (drag_drop_dest_func *) ufunc; if (strstr(text,"file://")) // text is a filespec { file = zstrdup(text+7); // get rid of junk added by GTK cc = strlen(file); while (file[cc-1] < ' ') cc--; file[cc] = 0; file2 = drag_drop_unescape(file); // clean %xx escapes from Nautilus zfree(file); ufunc2(mpx,mpy,file2); // pass file to user function } else // text is text { text2 = zstrdup(text); ufunc2(mpx,mpy,text2); } gtk_drag_finish(context,1,0,time); return 1; } // private function for "drag-motion" signal // 6.5 // pass mouse position to user function during drag int drag_drop_dest3(GtkWidget *, void *, int mpx, int mpy, int, void *ufunc) { drag_drop_dest_func *ufunc2; ufunc2 = (drag_drop_dest_func *) ufunc; if (! ufunc2) return 0; ufunc2(mpx,mpy,null); return 0; } // private function // Clean %xx escapes from strange Nautilus drag-drop file names char * drag_drop_unescape(cchar *inp) { int drag_drop_convhex(char ch); char inch, *out, *outp; int nib1, nib2; out = (char *) zmalloc(strlen(inp)+1); outp = out; while ((inch = *inp++)) { if (inch == '%') { nib1 = drag_drop_convhex(*inp++); nib2 = drag_drop_convhex(*inp++); *outp++ = nib1 << 4 | nib2; } else *outp++ = inch; } *outp = 0; return out; } // private function - convert character 0-F to number 0-15 int drag_drop_convhex(char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; return ch; } /******************************************************************************** Miscellaneous GDK/GTK functions *********************************************************************************/ // Get thumbnail image for given image file. // Returned thumbnail belongs to caller: g_object_unref() is necessary. PIXBUF * get_thumbnail(cchar *fpath, int size) { PIXBUF *thumbpxb; GError *gerror = 0; int err; char *bpath; STATB statf; err = stat(fpath,&statf); // fpath status info if (err) return 0; if (S_ISDIR(statf.st_mode)) { // if directory, return folder image bpath = (char *) zmalloc(500); *bpath = 0; strncatv(bpath,499,zimagedir,"/folder.png",null); thumbpxb = gdk_pixbuf_new_from_file_at_size(bpath,size,size,&gerror); zfree(bpath); return thumbpxb; } thumbpxb = gdk_pixbuf_new_from_file_at_size(fpath,size,size,&gerror); return thumbpxb; // return pixbuf to caller } // make a cursor from a graphic file in application directory // (see initz_appfiles()). GdkCursor * zmakecursor(cchar *imagefile) { GError *gerror = 0; PIXBUF *pixbuf; GdkDisplay *display; GdkCursor *cursor = 0; char imagepath[200]; display = gdk_display_get_default(); *imagepath = 0; strncatv(imagepath,199,zimagedir,"/",imagefile,null); pixbuf = gdk_pixbuf_new_from_file(imagepath,&gerror); if (pixbuf && display) cursor = gdk_cursor_new_from_pixbuf(display,pixbuf,0,0); else printz("*** %s \n",gerror->message); return cursor; } /******************************************************************************** PIXBUF * gdk_pixbuf_rotate(PIXBUF *pixbuf, float angle, int acolor) Rotate a pixbuf through an arbitrary angle (degrees). The returned image has the same size as the original, but the pixbuf envelope is increased to accomodate the rotated original (e.g. a 100x100 pixbuf rotated 45 deg. needs a 142x142 pixbuf). Pixels added around the rotated image have all RGB values = acolor. Angle is in degrees. Positive direction is clockwise. Pixbuf must have 8 bits per channel and 3 or 4 channels. Loss of resolution is about 1/2 pixel. Speed is about 28 million pixels/sec. on 3.3 GHz CPU. (e.g. a 10 megapix image needs about 0.36 seconds) NULL is returned if the function fails for one of the following: - pixbuf not 8 bits/channel or < 3 channels - unable to create output pixbuf (lack of memory?) Algorithm: create output pixbuf big enough for rotated input pixbuf compute coefficients for affine transform loop all output pixels (px2,py2) get corresp. input pixel (px1,py1) using affine transform if outside of pixmap output pixel = black continue for 4 input pixels based at (px0,py0) = (int(px1),int(py1)) compute overlap (0 to 1) with (px1,py1) sum RGB values * overlap output aggregate RGB to pixel (px2,py2) Benchmark: rotate 7 megapixel image 10 degrees 0.31 secs. 3.3 GHz Core i5 ***/ PIXBUF * gdk_pixbuf_rotate(PIXBUF *pixbuf1, float angle, int acolor) { typedef unsigned char *pixel; // 3 RGB values, 0-255 each PIXBUF *pixbuf2; GDKCOLOR color; int nch, nbits, alpha; int ww1, hh1, rs1, ww2, hh2, rs2; int px2, py2, px0, py0; pixel ppix1, ppix2, pix0, pix1, pix2, pix3; float px1, py1; float f0, f1, f2, f3, red, green, blue, tran = 0; float a, b, d, e, ww15, hh15, ww25, hh25; float PI = 3.141593; nch = gdk_pixbuf_get_n_channels(pixbuf1); nbits = gdk_pixbuf_get_bits_per_sample(pixbuf1); if (nch < 3) return 0; // must have 3+ channels (colors) if (nbits != 8) return 0; // must be 8 bits per channel color = gdk_pixbuf_get_colorspace(pixbuf1); // get input pixbuf1 attributes alpha = gdk_pixbuf_get_has_alpha(pixbuf1); ww1 = gdk_pixbuf_get_width(pixbuf1); hh1 = gdk_pixbuf_get_height(pixbuf1); rs1 = gdk_pixbuf_get_rowstride(pixbuf1); while (angle < -180) angle += 360; // normalize, -180 to +180 while (angle > 180) angle -= 360; angle = angle * PI / 180; // radians, -PI to +PI if (fabsf(angle) < 0.001) { pixbuf2 = gdk_pixbuf_copy(pixbuf1); // angle is zero within my precision return pixbuf2; } ww2 = int(ww1*fabsf(cosf(angle)) + hh1*fabsf(sinf(angle))); // rectangle containing rotated image hh2 = int(ww1*fabsf(sinf(angle)) + hh1*fabsf(cosf(angle))); pixbuf2 = gdk_pixbuf_new(color,alpha,nbits,ww2,hh2); // create output pixbuf2 if (! pixbuf2) return 0; rs2 = gdk_pixbuf_get_rowstride(pixbuf2); ppix1 = gdk_pixbuf_get_pixels(pixbuf1); // input pixel array ppix2 = gdk_pixbuf_get_pixels(pixbuf2); // output pixel array ww15 = 0.5 * ww1; hh15 = 0.5 * hh1; ww25 = 0.5 * ww2; hh25 = 0.5 * hh2; a = cosf(angle); // affine transform coefficients b = sinf(angle); d = - sinf(angle); e = cosf(angle); for (py2 = 0; py2 < hh2; py2++) // loop through output pixels for (px2 = 0; px2 < ww2; px2++) { px1 = a * (px2 - ww25) + b * (py2 - hh25) + ww15; // (px1,py1) = corresponding py1 = d * (px2 - ww25) + e * (py2 - hh25) + hh15; // point within input pixels px0 = int(px1); // pixel containing (px1,py1) py0 = int(py1); if (px1 < 0 || px0 >= ww1-1 || py1 < 0 || py0 >= hh1-1) { // if outside input pixel array pix2 = ppix2 + py2 * rs2 + px2 * nch; // output is acolor pix2[0] = pix2[1] = pix2[2] = acolor; continue; } pix0 = ppix1 + py0 * rs1 + px0 * nch; // 4 input pixels based at (px0,py0) pix1 = pix0 + rs1; pix2 = pix0 + nch; pix3 = pix0 + rs1 + nch; f0 = (px0+1 - px1) * (py0+1 - py1); // overlap of (px1,py1) f1 = (px0+1 - px1) * (py1 - py0); // in each of the 4 pixels f2 = (px1 - px0) * (py0+1 - py1); f3 = (px1 - px0) * (py1 - py0); red = f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0]; // sum the weighted inputs green = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1]; blue = f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2]; if (alpha) tran = f0 * pix0[3] + f1 * pix1[3] + f2 * pix2[3] + f3 * pix3[3]; // 4th color = alpha if (red == acolor && green == acolor && blue == acolor) { // avoid acolor in image if (blue == 0) blue = 1; else blue--; } pix2 = ppix2 + py2 * rs2 + px2 * nch; // output pixel pix2[0] = int(red); pix2[1] = int(green); pix2[2] = int(blue); if (alpha) pix2[3] = int(tran); } return pixbuf2; } /********************************************************************************/ // strip the alpha channel from a pixbuf // returns 0 if no alpha channel or fatal error PIXBUF * gdk_pixbuf_stripalpha(PIXBUF *pixbuf1) // 6.2 { PIXBUF *pixbuf2; GDKCOLOR color; int ww, hh, rs1, rs2; uint8 *ppix1, *ppix2, *pix1, *pix2; int nch, ac; int px, py; ac = gdk_pixbuf_get_has_alpha(pixbuf1); if (! ac) return 0; nch = gdk_pixbuf_get_n_channels(pixbuf1); color = gdk_pixbuf_get_colorspace(pixbuf1); ww = gdk_pixbuf_get_width(pixbuf1); hh = gdk_pixbuf_get_height(pixbuf1); pixbuf2 = gdk_pixbuf_new(color,0,8,ww,hh); // create output pixbuf2 if (! pixbuf2) return 0; ppix1 = gdk_pixbuf_get_pixels(pixbuf1); // input pixel array ppix2 = gdk_pixbuf_get_pixels(pixbuf2); // output pixel array rs1 = gdk_pixbuf_get_rowstride(pixbuf1); rs2 = gdk_pixbuf_get_rowstride(pixbuf2); for (py = 0; py < hh; py++) { pix1 = ppix1 + py * rs1; pix2 = ppix2 + py * rs2; for (px = 0; px < ww; px++) { memcpy(pix2,pix1,nch-1); pix1 += nch; pix2 += nch-1; } } return pixbuf2; } /********************************************************************************/ // Create a pixbuf containing text with designated font and attributes. // Text is white on black. Widget is ultimate display destination. PIXBUF * text_pixbuf(cchar *text, cchar *font, int fontsize, GtkWidget *widget) // 6.2 { char font2[60]; PangoFontDescription *pfont; PangoLayout *playout; cairo_surface_t *surface; cairo_t *cr; PIXBUF *pixbuf; uint8 *pixels, *cairo_data, *cpix, *pix2; int ww, hh, rs, px, py; if (! font) font = zfuncs::appfont; // default font 6.3 snprintf(font2,60,"%s %d",font,fontsize); // combine font and size pfont = pango_font_description_from_string(font2); // make layout with text playout = gtk_widget_create_pango_layout(widget,text); pango_layout_set_font_description(playout,pfont); pango_layout_get_pixel_size(playout,&ww,&hh); ww += 2 + 0.2 * fontsize; // compensate bad font metrics hh += 2 + 0.1 * fontsize; surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,ww,hh); // cairo output image cr = cairo_create(surface); pango_cairo_show_layout(cr,playout); // write text layout to image cairo_data = cairo_image_surface_get_data(surface); // get text image pixels pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,0,8,ww,hh); rs = gdk_pixbuf_get_rowstride(pixbuf); pixels = gdk_pixbuf_get_pixels(pixbuf); for (py = 0; py < hh; py++) // copy text image to PXB for (px = 0; px < ww; px++) { cpix = cairo_data + 4 * (ww * py + px); pix2 = pixels + py * rs + px * 3; pix2[0] = pix2[1] = pix2[2] = cpix[3]; } pango_font_description_free(pfont); // free resources g_object_unref(playout); cairo_destroy(cr); cairo_surface_destroy(surface); return pixbuf; } /********************************************************************************/ // move the mouse pointer to given position in given window // widget must be realized int move_pointer(GtkWidget *widget, int px, int py) { int rpx, rpy; GdkWindow *window; window = gtk_widget_get_window(widget); gdk_window_get_root_coords(window,px,py,&rpx,&rpy); gdk_device_warp(mouse,screen,rpx,rpy); // wayland fails FIXME return 1; } /******************************************************************************** xstring class (dynamic length string) xstring(int cc = 0) default constructor xstring(cchar * ) string constructor xstring(const xstring &) copy constructor ~xstring() destructor operator cchar * () const { return xpp; } conversion operator (cchar *) xstring operator= (const xstring &) operator = xstring operator= (cchar *) operator = friend xstring operator+ (const xstring &, const xstring &) operator + (catenate) friend xstring operator+ (const xstring &, cchar *) operator + friend xstring operator+ (cchar *, const xstring &) operator + void insert(int pos, cchar *string, int cc = 0) insert substring at pos (expand) void overlay(int pos, cchar *string, int cc = 0) replace substring (possibly expand) static void getStats(int & tcount2, int & tmem2) get statistics void validate() const verify integrity int getcc() const { return xcc; } return string length *********************************************************************************/ #define wmiv 1648734981 int xstring::tcount = 0; // initz. static members int xstring::tmem = 0; xstring::xstring(int cc) // new xstring(cc) { wmi = wmiv; xmem = (cc & 0x7ffffff8) + 8; // mod 8 length xpp = new char[xmem]; // allocate if (! xpp) zappcrash("xstring NEW failure",null); tcount++; // incr. object count tmem += xmem; // incr. allocated memory xcc = 0; // string cc = 0 *xpp = 0; // string = null } xstring::xstring(cchar *string) // new xstring("initial string") { wmi = wmiv; xcc = 0; if (string) xcc = strlen(string); // string length xmem = (xcc & 0x7ffffff8) + 8; // mod 8 length xpp = new char[xmem]; // allocate if (! xpp) zappcrash("xstring NEW failure",null); tcount++; // incr. object count tmem += xmem; // incr. allocated memory *xpp = 0; if (xcc) strcpy(xpp,string); // copy string } xstring::xstring(const xstring & xstr) // new xstring2(xstring1) { wmi = wmiv; xmem = xstr.xmem; // allocate same length xcc = xstr.xcc; xpp = new char[xmem]; if (! xpp) zappcrash("xstring NEW failure",null); tcount++; // incr. object count tmem += xmem; // incr. allocated memory strcpy(xpp,xstr.xpp); // copy string } xstring::~xstring() // delete xstring { validate(); delete[] xpp; // release allocated memory xpp = 0; tcount--; // decr. object count tmem -= xmem; // decr. allocated memory if (tcount < 0) zappcrash("xstring count < 0",null); if (tmem < 0) zappcrash("xstring memory < 0",null); if (tcount == 0 && tmem > 0) zappcrash("xstring memory leak",null); } xstring xstring::operator= (const xstring & xstr) // xstring2 = xstring1 { validate(); xstr.validate(); if (this == &xstr) return *this; xcc = xstr.xcc; if (xmem < xcc+1) { delete[] xpp; // expand memory if needed tmem -= xmem; xmem = (xcc & 0x7ffffff8) + 8; // mod 8 length xpp = new char[xmem]; if (! xpp) zappcrash("xstring NEW failure",null); tmem += xmem; } strcpy(xpp,xstr.xpp); // copy string return *this; } xstring xstring::operator= (cchar *str) // xstring = "some string" { validate(); xcc = 0; *xpp = 0; if (str) xcc = strlen(str); if (xmem < xcc+1) { delete[] xpp; // expand memory if needed tmem -= xmem; xmem = (xcc & 0x7ffffff8) + 8; // mod 8 length xpp = new char[xmem]; if (! xpp) zappcrash("xstring NEW failure",null); tmem += xmem; } if (xcc) strcpy(xpp,str); // copy string return *this; } xstring operator+ (const xstring & x1, const xstring & x2) // xstring1 + xstring2 { x1.validate(); x2.validate(); xstring temp(x1.xcc + x2.xcc); // build temp xstring strcpy(temp.xpp,x1.xpp); // with both input strings strcpy(temp.xpp + x1.xcc, x2.xpp); temp.xcc = x1.xcc + x2.xcc; temp.validate(); return temp; } xstring operator+ (const xstring & x1, cchar *s2) // xstring + "some string" { x1.validate(); int cc2 = 0; if (s2) cc2 = strlen(s2); xstring temp(x1.xcc + cc2); // build temp xstring strcpy(temp.xpp,x1.xpp); // with both input strings if (s2) strcpy(temp.xpp + x1.xcc, s2); temp.xcc = x1.xcc + cc2; temp.validate(); return temp; } xstring operator+ (cchar *s1, const xstring & x2) // "some string" + xstring { x2.validate(); int cc1 = 0; if (s1) cc1 = strlen(s1); xstring temp(cc1 + x2.xcc); // build temp xstring if (s1) strcpy(temp.xpp,s1); // with both input strings strcpy(temp.xpp + cc1, x2.xpp); temp.xcc = cc1 + x2.xcc; temp.validate(); return temp; } void xstring::insert(int pos, cchar *string, int cc) // insert cc chars from string at pos { // pad if pos > xcc or cc > string validate(); int scc = strlen(string); if (! cc) cc = scc; int pad = pos - xcc; if (pad < 0) pad = 0; if (xmem < xcc + cc + pad + 1) // allocate more memory if needed { int newmem = xcc + cc + pad; newmem = (newmem & 0x7ffffff8) + 8; // mod 8 length char * xpp2 = new char[newmem]; if (! xpp2) zappcrash("xstring NEW failure",null); strcpy(xpp2,xpp); // copy to new space delete[] xpp; xpp = xpp2; tmem += newmem - xmem; xmem = newmem; } if (pad) memset(xpp+xcc,' ',pad); // add blanks up to pos for (int ii = xcc + pad; ii >= pos; ii--) // make hole for inserted string *(xpp+ii+cc) = *(xpp+ii); if (cc > scc) memset(xpp+pos+scc,' ',cc-scc); // blank pad if cc > string if (cc < scc) scc = cc; strncpy(xpp+pos,string,scc); // insert string, without null xcc += cc + pad; // set new length xpp[xcc] = 0; validate(); } void xstring::overlay(int pos, cchar *string, int cc) // overlay substring { validate(); int scc = strlen(string); if (! cc) cc = scc; if (xmem < pos + cc + 1) // allocate more memory if needed { int newmem = pos + cc; newmem = (newmem & 0x7ffffff8) + 8; // mod 8 length char * xpp2 = new char[newmem]; if (! xpp2) zappcrash("xstring NEW failure",null); strcpy(xpp2,xpp); // copy to new space delete[] xpp; xpp = xpp2; tmem += newmem - xmem; xmem = newmem; } if (pos > xcc) memset(xpp+xcc,' ',pos-xcc); // add blanks up to pos if (cc > scc) memset(xpp+pos+scc,' ',cc-scc); // blank pad if cc > string if (cc < scc) scc = cc; strncpy(xpp+pos,string,scc); // insert string, without null if (pos + cc > xcc) xcc = pos + cc; // set new length xpp[xcc] = 0; validate(); } void xstring::getStats(int & tcount2, int & tmem2) // get statistics { tcount2 = tcount; tmem2 = tmem; } void xstring::validate() const // validate integrity { if (wmi != wmiv) zappcrash("xstring bad wmi",null); if (xmem < xcc+1) zappcrash("xstring xmem < xcc+1",null); if (xcc != (int) strlen(xpp)) zappcrash("xstring xcc != strlen(xpp)",null); } /******************************************************************************** Vxstring class (array or vector of xstring) Vxstring(int = 0); constructor ~Vxstring(); destructor Vxstring(const Vxstring &); copy constructor Vxstring operator= (const Vxstring &); operator = xstring & operator[] (int); operator [] const xstring & operator[] (int) const; operator [] (const) int search(cchar *string); find element in unsorted Vxstring int bsearch(cchar *string); find element in sorted Vxstring int sort(int nkeys, int keys[][3]); sort elements by designated subfields int sort(int pos = 0, int cc = 0); sort elements by 1 subfield (cc 0 = all) int getCount() const { return nd; } get current count Sort with keys: keys[N][0] = key position (0 based) of key N keys[N][1] = key length keys[N][2] = sort type: 1/2 = ascending/descending, 3/4 = same, ignoring case *********************************************************************************/ Vxstring::Vxstring(int ii) // constructor { pdata = 0; nd = ii; if (nd) pdata = new xstring[nd]; if (nd && !pdata) zappcrash("Vxstring NEW fail",null); } Vxstring::~Vxstring() // destructor { if (nd) delete[] pdata; pdata = 0; nd = 0; } Vxstring::Vxstring(const Vxstring & pold) // copy constructor { pdata = 0; nd = pold.nd; // set size if (nd) pdata = new xstring[nd]; // allocate memory if (nd && !pdata) zappcrash("Vxstring NEW fail"); for (int ii = 0; ii < nd; ii++) pdata[ii] = pold[ii]; // copy defined elements } Vxstring Vxstring::operator= (const Vxstring & vdstr) // operator = { if (nd) delete[] pdata; // delete old memory pdata = 0; nd = vdstr.nd; if (nd) pdata = new xstring[nd]; // allocate new memory if (nd && !pdata) zappcrash("Vxstring NEW fail",null); for (int ii = 0; ii < nd; ii++) pdata[ii] = vdstr.pdata[ii]; // copy elements return *this; } xstring & Vxstring::operator[] (int ii) // operator [] { static xstring xnull(0); if (ii < nd) return pdata[ii]; // return reference zappcrash("Vxstring index invalid %d %d",nd,ii,null); return xnull; } const xstring & Vxstring::operator[] (int ii) const // operator [] { static xstring xnull(0); if (ii < nd) return pdata[ii]; // return reference zappcrash("Vxstring index invalid %d %d",nd,ii,null); return xnull; } int Vxstring::search(cchar *string) // find element in unsorted Vxstring { for (int ii = 0; ii < nd; ii++) if (strmatch(pdata[ii],string)) return ii; return -1; } int Vxstring::bsearch(cchar *string) // find element in sorted Vxstring { // (binary search) int nn, ii, jj, kk, rkk; nn = nd; if (! nn) return 0; // empty list ii = nn / 2; // next element to search jj = (ii + 1) / 2; // next increment nn--; // last element rkk = 0; while (1) { kk = strcmp(pdata[ii],string); // check element if (kk > 0) { ii -= jj; // too high, go down if (ii < 0) return -1; } else if (kk < 0) { ii += jj; // too low, go up if (ii > nn) return -1; } else if (kk == 0) return ii; // matched jj = jj / 2; // reduce increment if (jj == 0) { jj = 1; // step by 1 element if (! rkk) rkk = kk; // save direction else { if (rkk > 0) { if (kk < 0) return -1; } // if change direction, fail else if (kk > 0) return -1; } } } } static int VDsortKeys[10][3], VDsortNK; int Vxstring::sort(int NK, int keys[][3]) // sort elements by subfields { // key[ii][0] = position int NR, RL, ii; // [1] = length HeapSortUcomp VDsortComp; // [2] = 1/2 = ascending/desc. // = 3/4 = + ignore case NR = nd; if (NR < 2) return 1; RL = sizeof(xstring); if (NK < 1) zappcrash("Vxstring::sort, bad NK",null); if (NK > 10) zappcrash("Vxstring::sort, bad NK",null); VDsortNK = NK; for (ii = 0; ii < NK; ii++) { VDsortKeys[ii][0] = keys[ii][0]; VDsortKeys[ii][1] = keys[ii][1]; VDsortKeys[ii][2] = keys[ii][2]; } HeapSort((char *) pdata,RL,NR,VDsortComp); return 1; } int VDsortComp(cchar *r1, cchar *r2) { xstring *d1, *d2; cchar *p1, *p2; int ii, nn, kpos, ktype, kleng; d1 = (xstring *) r1; d2 = (xstring *) r2; p1 = *d1; p2 = *d2; for (ii = 0; ii < VDsortNK; ii++) // compare each key { kpos = VDsortKeys[ii][0]; kleng = VDsortKeys[ii][1]; ktype = VDsortKeys[ii][2]; if (ktype == 1) { nn = strncmp(p1+kpos,p2+kpos,kleng); if (nn) return nn; continue; } else if (ktype == 2) { nn = strncmp(p1+kpos,p2+kpos,kleng); if (nn) return -nn; continue; } else if (ktype == 3) { nn = strncasecmp(p1+kpos,p2+kpos,kleng); if (nn) return nn; continue; } else if (ktype == 4) { nn = strncasecmp(p1+kpos,p2+kpos,kleng); if (nn) return -nn; continue; } zappcrash("Vxstring::sort, bad KEYS sort type",null); } return 0; } int Vxstring::sort(int pos, int cc) // sort elements ascending { int key[3]; if (! cc) cc = 999999; key[0] = pos; key[1] = cc; key[2] = 1; sort(1,&key); return 1; } /******************************************************************************** Hash Table class HashTab(int cc, int cap); constructor ~HashTab(); destructor int Add(cchar *string); add a new string int Del(cchar *string); delete a string int Find(cchar *string); find a string int GetCount() { return count; } get string count int GetNext(int & first, char *string); get first/next string int Dump(); dump hash table to std. output constructor: cc = string length of table entries, cap = table capacity cap should be set 30% higher than needed to reduce collisions and improve speed Benchmark: 0.056 usec. to find 19 char string in a table of 100,000 which is 80% full. 3.3 GHz Core i5 *********************************************************************************/ // static members (robust for tables up to 60% full) int HashTab::trys1 = 100; // Add() tries int HashTab::trys2 = 200; // Find() tries HashTab::HashTab(int _cc, int _cap) // constructor { cc = 4 * (_cc + 4) / 4; // + 1 + mod 4 length cap = _cap; int len = cc * cap; table = new char [len]; if (! table) zappcrash("HashTab() new %d fail",len,null); memset(table,0,len); } HashTab::~HashTab() // destructor { delete [] table; table = 0; } // Add a new string to table int HashTab::Add(cchar *string) { int pos, fpos, trys; pos = strHash(string,cap); // get random position pos = pos * cc; for (trys = 0, fpos = -1; trys < trys1; trys++, pos += cc) // find next free slot at/after position { if (pos >= cap * cc) pos = 0; // last position wraps to 1st if (! table[pos]) // empty slot: string not found { if (fpos != -1) pos = fpos; // use prior deleted slot if there strncpy(table+pos,string,cc); // insert new string table[pos+cc-1] = 0; // insure null terminator return (pos/cc); // return rel. table entry } if (table[pos] == -1) // deleted slot { if (fpos == -1) fpos = pos; // remember 1st one found continue; } if (strmatch(string,table+pos)) return -2; // string already present } return -3; // table full (trys1 exceeded) } // Delete a string from table int HashTab::Del(cchar *string) { int pos, trys; pos = strHash(string,cap); // get random position pos = pos * cc; for (trys = 0; trys < trys2; trys++, pos += cc) // search for string at/after position { if (pos >= cap * cc) pos = 0; // last position wraps to 1st if (! table[pos]) return -1; // empty slot, string not found if (strmatch(string,table+pos)) // string found { table[pos] = -1; // delete table entry return (pos/cc); // return rel. table entry } } zappcrash("HashTab::Del() bug",null); // exceed trys2, must not happen return 0; // (table too full to function) } // Find a table entry. int HashTab::Find(cchar *string) { int pos, trys; pos = strHash(string,cap); // get random position pos = pos * cc; for (trys = 0; trys < trys2; trys++, pos += cc) // search for string at/after position { if (pos >= cap * cc) pos = 0; // last position wraps to 1st if (! table[pos]) return -1; // empty slot, string not found if (strmatch(string,table+pos)) return (pos/cc); // string found, return rel. entry } zappcrash("HashTab::Find() bug",null); // cannot happen return 0; } // return first or next table entry int HashTab::GetNext(int & ftf, char *string) { static int pos; if (ftf) // initial call { pos = 0; ftf = 0; } while (pos < (cap * cc)) { if ((table[pos] == 0) || (table[pos] == -1)) // empty or deleted slot { pos += cc; continue; } strcpy(string,table+pos); // return string pos += cc; return 1; } return -4; // EOF } int HashTab::Dump() { int ii, pos; for (ii = 0; ii < cap; ii++) { pos = ii * cc; if (table[pos] && table[pos] != -1) printz("%d, %s \n", ii, table + pos); if (table[pos] == -1) printz("%d, deleted \n", pos); } return 1; } /******************************************************************************** class for queue of dynamic strings Queue(int cap); create queue with capacity ~Queue(); destroy queue int getCount(); get current entry count int push(const xstring *entry, double secs); add new entry with max. wait time xstring *pop1(); get 1st entry (oldest) xstring *popN(); get Nth entry (newest) constructor: cap is queue capacity push: secs is max. time to wait if queue is full. This makes sense if the queue is being pop'd from another thread. Use zero otherwise. Execution time: 0.48 microsecs per push + pop on queue with 100 slots kept full. (2.67 GHz Intel Core i7) *********************************************************************************/ Queue::Queue(int cap) // constructor { int err; err = mutex_init(&qmutex, 0); // create mutex = queue lock if (err) zappcrash("Queue(), mutex init fail",null); qcap = cap; // queue capacity ent1 = entN = qcount = 0; // state = empty vd = new Vxstring(qcap); // create vector of xstring's if (! vd) zappcrash("Queue(), NEW fail %d",cap,null); strcpy(wmi,"queue"); return; } Queue::~Queue() // destructor { if (! strmatch(wmi,"queue")) zappcrash("~Queue wmi fail",null); wmi[0] = 0; mutex_destroy(&qmutex); // destroy mutex qcount = qcap = ent1 = entN = -1; delete vd; vd = 0; return; } void Queue::lock() // lock queue (private) { int err; err = mutex_lock(&qmutex); // reserve mutex or suspend if (err) zappcrash("Queue mutex lock fail",null); return; } void Queue::unlock() // unlock queue (private) { int err; err = mutex_unlock(&qmutex); // release mutex if (err) zappcrash("Queue mutex unlock fail",null); return; } int Queue::getCount() // get current entry count { if (! strmatch(wmi,"queue")) zappcrash("Queue getCount wmi fail",null); return qcount; } int Queue::push(const xstring *newEnt, double wait) // add entry to queue, with max. wait { double elaps = 0.0; int count; if (! strmatch(wmi,"queue")) zappcrash("Queue::push wmi fail",null); lock(); // lock queue while (qcount == qcap) { // queue full unlock(); // unlock queue if (elaps >= wait) return -1; // too long, return -1 status usleep(1000); // sleep in 1 millisec. steps elaps += 0.001; // until queue not full lock(); // lock queue } (* vd)[entN] = *newEnt; // copy new entry into queue entN++; // incr. end pointer if (entN == qcap) entN = 0; qcount++; // incr. queue count count = qcount; unlock(); // unlock queue return count; // return curr. queue count } xstring *Queue::pop1() // get 1st (oldest) entry and remove { xstring *entry; if (! strmatch(wmi,"queue")) zappcrash("Queue::pop1 wmi fail",null); lock(); // lock queue if (qcount == 0) entry = 0; // queue empty else { entry = &(* vd)[ent1]; // get first entry ent1++; // index pointer to next if (ent1 == qcap) ent1 = 0; qcount--; // decr. queue count } unlock(); // unlock queue return entry; } xstring *Queue::popN() // get last (newest) entry and remove { xstring *entry; if (! strmatch(wmi,"queue")) zappcrash("Queue::popN wmi fail",null); lock(); // lock queue if (qcount == 0) entry = 0; // queue empty else { if (entN == 0) entN = qcap; // index pointer to prior entN--; qcount--; // decr. queue count entry = &(* vd)[entN]; // get last entry } unlock(); // unlock queue return entry; } /******************************************************************************** Tree class, tree-structured data storage without limits Store any amount of data at any depth within a tree-structure with named nodes. Data can be found using an ordered list of node names or node numbers. Tree(cchar *name); create Tree ~Tree(); destroy Tree int put(void *data, int dd, char *nodes[], int nn); put data by node names[] int put(void *data, int dd, int nodes[], int nn); put data by node numbers[] int get(void *data, int dd, char *nodes[], int nn); get data by node names[] int get(void *data, int dd, int nodes[], int nn); get data by node numbers[] A Tree can also be thought of as an N-dimensional array with the cells or nodes having both names and numbers. Data can be stored and retrieved with a list of node names or numbers. The nodes are created as needed. Nodes are sparse: those with no data do not exist. Node numbers are created when data is stored by node numbers. Node numbers are also added when data is stored by node names: the numbers are assigned sequentially from zero at each level in the tree. nodes array of node names or numbers nn no. of nodes used for a put() or get() call dd data length to put, or the max. data length to get (i.e. the space available) put() returns 1 if successful and crashes with a message if not (out of memory) get() returns the length of the data retrieved (<= dd) or 0 if not found there is no assumption that the data is character data and no null is appended data returned has the same length as the data stored (if dd arg is big enough) example: char *snodes[10]; // up to 10 node names (max tree depth) int knodes[10]; // up to 10 node numbers char mydata[20]; // data length up to 20 Tree *mytree = new Tree("myname"); // create Tree snodes[0] = "name1"; snodes[1] = "name2"; mytree->put("string1",8,snodes,2); // put "string1" at ["name1","name2"] snodes[1] = "name3"; mytree->put("string22",9,snodes,2); // put "string22" at ["name1","name3"] snodes[1] = "name2"; mytree->get(mydata,20,snodes,2); // get data at ["name1","name2"] ("string1") knodes[0] = 0; knodes[1] = 0; mytree->get(mydata,20,knodes,2); // get data at [0,0] ("string1") knodes[1] = 1; mytree->get(mydata,20,knodes,2); // get data at [0,1] ("string22") When data was stored at ["name1","name2"] these node names were created along with the corresponding node numbers [0,0]. When data was stored at ["name1","name3"] a new node "name3" was created under the existing node "name1", and assigned the numbers [0,1]. Benchmark Execution times: 2.67 GHz Intel Core i7 Tree with 1 million nodes and average depth of 8 levels (peak 15 levels) put() all data by node names: 2.1 secs get() all data by node names: 1.5 secs put() all data by node numbers: 2.0 secs get() all data by node numbers: 0.72 secs Internal code conventions: - caller level is node 0, next level is node 1, etc. - node names and numbers in calls to get() and put() refer to next levels - number of levels = 1+nn, where nn is max. in calls to put(...nodes[], nn) *********************************************************************************/ #define wmid 1374602859 // integrity check key // constructor Tree::Tree(cchar *name) { wmi = wmid; tname = 0; tmem = 0; tdata = 0; nsub = 0; psub = 0; if (name) { int cc = strlen(name); tname = new char[cc+1]; if (! tname) zappcrash("Tree, no memory",null); strcpy(tname,name); } } // destructor Tree::~Tree() { if (wmi != wmid) zappcrash("not a Tree",null); if (tname) delete [] tname; tname = 0; if (tmem) zfree(tdata); tmem = 0; tdata = 0; for (int ii = 0; ii < nsub; ii++) delete psub[ii]; if (psub) zfree(psub); nsub = 0; psub = 0; } // put data by node names[] int Tree::put(void *data, int dd, char *nodes[], int nn) { Tree *tnode; if (wmi != wmid) zappcrash("not a Tree",null); tnode = make(nodes,nn); if (tnode->tdata) zfree(tnode->tdata); tnode->tdata = new char[dd]; if (! tnode->tdata) zappcrash("Tree, no memory",null); tnode->tmem = dd; memmove(tnode->tdata,data,dd); return 1; } // put data by node numbers[] int Tree::put(void *data, int dd, int nodes[], int nn) { Tree *tnode; if (wmi != wmid) zappcrash("not a Tree",null); tnode = make(nodes,nn); if (tnode->tdata) zfree(tnode->tdata); tnode->tdata = new char[dd]; if (! tnode->tdata) zappcrash("Tree, no memory",null); tnode->tmem = dd; memmove(tnode->tdata,data,dd); return 1; } // get data by node names[] int Tree::get(void *data, int dd, char *nodes[], int nn) { Tree *tnode = find(nodes,nn); if (! tnode) return 0; if (! tnode->tmem) return 0; if (dd > tnode->tmem) dd = tnode->tmem; memmove(data,tnode->tdata,dd); return dd; } // get data by node numbers[] int Tree::get(void *data, int dd, int nodes[], int nn) { Tree *tnode = find(nodes,nn); if (! tnode) return 0; if (! tnode->tmem) return 0; if (dd > tnode->tmem) dd = tnode->tmem; memmove(data,tnode->tdata,dd); return dd; } // find a given node by names[] Tree * Tree::find(char *nodes[], int nn) { int ii; for (ii = 0; ii < nsub; ii++) if (psub[ii]->tname && strmatch(nodes[0],psub[ii]->tname)) break; if (ii == nsub) return 0; if (nn == 1) return psub[ii]; return psub[ii]->find(&nodes[1],nn-1); } // find a given node by numbers[] Tree * Tree::find(int nodes[], int nn) { int ii = nodes[0]; if (ii >= nsub) return 0; if (! psub[ii]) return 0; if (nn == 1) return psub[ii]; return psub[ii]->find(&nodes[1],nn-1); } // find or create a given node by names[] Tree * Tree::make(char *nodes[], int nn) { int ii; Tree **psub2; for (ii = 0; ii < nsub; ii++) if (psub[ii]->tname && strmatch(nodes[0],psub[ii]->tname)) break; if (ii == nsub) { psub2 = new Tree * [nsub+1]; if (! psub2) zappcrash("Tree, no memory",null); for (ii = 0; ii < nsub; ii++) psub2[ii] = psub[ii]; delete [] psub; psub = psub2; nsub++; psub[ii] = new Tree(nodes[0]); if (! psub[ii]) zappcrash("Tree, no memory",null); } if (nn == 1) return psub[ii]; return psub[ii]->make(&nodes[1],nn-1); } // find or create a given node by numbers[] Tree * Tree::make(int nodes[], int nn) { Tree **psub2; int ii, jj; ii = nodes[0]; if ((ii < nsub) && psub[ii]) { if (nn == 1) return psub[ii]; return psub[ii]->make(&nodes[1],nn-1); } if (ii >= nsub) { psub2 = new Tree * [ii+1]; if (psub2 == null) zappcrash("Tree, no memory",null); for (jj = 0; jj < nsub; jj++) psub2[jj] = psub[jj]; for (jj = nsub; jj < ii; jj++) psub2[jj] = 0; delete [] psub; psub = psub2; nsub = ii + 1; } psub[ii] = new Tree("noname"); if (! psub[ii]) zappcrash("Tree, no memory",null); if (nn == 1) return psub[ii]; return psub[ii]->make(&nodes[1],nn-1); } // dump tree data to stdout (call with level 0) void Tree::dump(int level) { cchar *name; if (! tname) name = "noname"; else name = tname; printz("%*s level: %d name: %s subs: %d mem: %d \n", level*2,"",level,name,nsub,tmem); for (int ii = 0; ii < nsub; ii++) if (psub[ii]) psub[ii]->dump(level+1); } // get node counts and total data per level // level 0 + nn more levels, as given in calls to put(...nodes[],nn) // caller must initialize counters to zero void Tree::stats(int nn[], int nd[]) { nn[0] += 1; nd[0] += tmem; for (int ii = 0; ii < nsub; ii++) if (psub[ii]) psub[ii]->stats(&nn[1],&nd[1]); } fotoxx-18.01.1/Makefile0000644000175000017500000000677713222767271013343 0ustar micomico# fotoxx makefile FOTOXX = fotoxx-18.01.1.cc # defaults for parameters that may be pre-defined CXXFLAGS += -Wall -g -rdynamic -fstack-protector-strong PREFIX ?= /usr CPPFLAGS ?= -O2 # target install directories BINDIR = $(PREFIX)/bin SHAREDIR = $(PREFIX)/share/fotoxx DATADIR = $(SHAREDIR)/data ICONDIR = $(SHAREDIR)/icons IMAGEDIR = $(SHAREDIR)/images LOCALESDIR = $(SHAREDIR)/locales DOCDIR = $(PREFIX)/share/doc/fotoxx MANDIR = $(PREFIX)/share/man/man1 APPDATADIR = $(PREFIX)/share/appdata MENUFILE = $(PREFIX)/share/applications/fotoxx.desktop CFLAGS = $(CXXFLAGS) $(CPPFLAGS) -c \ `pkg-config --cflags gtk+-3.0` \ -I/usr/include/clutter-1.0/ \ -I/usr/include/cogl/ \ -I/usr/include/json-glib-1.0/ \ -I/usr/include/clutter-gtk-1.0/ \ -I/usr/include/libchamplain-gtk-0.12/ \ -I/usr/include/libchamplain-0.12/ LIBS = `pkg-config --libs gtk+-3.0` -lpthread -ltiff -lpng -lraw -llcms2 \ -lclutter-1.0 -lclutter-gtk-1.0 -lchamplain-0.12 -lchamplain-gtk-0.12 ALLFILES = fotoxx.o f.widgets.o f.file.o f.gallery.o f.albums.o f.area.o f.meta.o \ f.edit.o f.repair.o f.warp.o f.effects.o f.combine.o f.mashup.o \ f.tools.o f.process.o zfuncs.o fotoxx: $(ALLFILES) $(CXX) $(LDFLAGS) -o fotoxx $(ALLFILES) $(LIBS) \ fotoxx.o: $(FOTOXX) fotoxx.h $(CXX) -o fotoxx.o $(FOTOXX) $(CFLAGS) \ f.widgets.o: f.widgets.cc fotoxx.h $(CXX) f.widgets.cc $(CFLAGS) \ f.file.o: f.file.cc fotoxx.h $(CXX) f.file.cc $(CFLAGS) \ f.gallery.o: f.gallery.cc fotoxx.h $(CXX) f.gallery.cc $(CFLAGS) \ f.albums.o: f.albums.cc fotoxx.h $(CXX) f.albums.cc $(CFLAGS) \ f.area.o: f.area.cc fotoxx.h $(CXX) f.area.cc $(CFLAGS) \ f.meta.o: f.meta.cc fotoxx.h $(CXX) f.meta.cc $(CFLAGS) \ f.edit.o: f.edit.cc fotoxx.h $(CXX) f.edit.cc $(CFLAGS) \ f.repair.o: f.repair.cc fotoxx.h $(CXX) f.repair.cc $(CFLAGS) \ f.warp.o: f.warp.cc fotoxx.h $(CXX) f.warp.cc $(CFLAGS) \ f.effects.o: f.effects.cc fotoxx.h $(CXX) f.effects.cc $(CFLAGS) \ f.combine.o: f.combine.cc fotoxx.h $(CXX) f.combine.cc $(CFLAGS) \ f.mashup.o: f.mashup.cc fotoxx.h $(CXX) f.mashup.cc $(CFLAGS) \ f.tools.o: f.tools.cc fotoxx.h $(CXX) f.tools.cc $(CFLAGS) \ f.process.o: f.process.cc fotoxx.h $(CXX) f.process.cc $(CFLAGS) \ zfuncs.o: zfuncs.cc zfuncs.h $(CXX) zfuncs.cc $(CFLAGS) \ ### -D DOCDIR=\"$(DOCDIR)\" \ ### SUSE only \ install: fotoxx uninstall mkdir -p $(DESTDIR)$(BINDIR) mkdir -p $(DESTDIR)$(DATADIR) mkdir -p $(DESTDIR)$(ICONDIR) mkdir -p $(DESTDIR)$(IMAGEDIR) mkdir -p $(DESTDIR)$(LOCALESDIR) mkdir -p $(DESTDIR)$(DOCDIR) mkdir -p $(DESTDIR)$(MANDIR) mkdir -p $(DESTDIR)$(PREFIX)/share/applications mkdir -p $(DESTDIR)$(APPDATADIR) cp -f fotoxx $(DESTDIR)$(BINDIR) cp -f -R data/* $(DESTDIR)$(DATADIR) cp -f -R images/* $(DESTDIR)$(IMAGEDIR) cp -f -R locales/* $(DESTDIR)$(LOCALESDIR) cp -f -R doc/* $(DESTDIR)$(DOCDIR) gzip -f -9 $(DESTDIR)$(DOCDIR)/changelog cp -f -R appdata/* $(DESTDIR)$(APPDATADIR) # man page cp -f doc/fotoxx.man fotoxx.1 gzip -f -9 fotoxx.1 cp fotoxx.1.gz $(DESTDIR)$(MANDIR) rm -f fotoxx.1.gz # menu (desktop) file cp -f fotoxx.desktop $(DESTDIR)$(MENUFILE) cp -f fotoxx.png $(DESTDIR)$(ICONDIR) uninstall: rm -f $(DESTDIR)$(BINDIR)/fotoxx rm -f -R $(DESTDIR)$(SHAREDIR) rm -f -R $(DESTDIR)$(DOCDIR) rm -f $(DESTDIR)$(MANDIR)/fotoxx.1.gz rm -f $(DESTDIR)$(MENUFILE) clean: rm -f fotoxx rm -f *.o fotoxx-18.01.1/f.warp.cc0000644000175000017500000041627213222767271013402 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit - warp menu functions m_unbend straighten curvature added by pano or improve perspective m_perspective select a tetragon and convert into a rectangle m_warp_area select an area and warp interior with mouse drags m_unwarp_closeup select a face in a close-up photo, remove distortion m_warp_curved warp an image or area using a curved transform m_warp_linear warp an image using a linear transform m_warp_affine warp an image using an affine transform m_flatbook flatten a photographed book page m_sphere spherical image projection, variable radius m_selective_rescale rescale while leaving selected areas unchanged m_waves distort an image with a wave pattern m_twist twist image centered at mouse position *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // unbend an image // straighten curvature added by pano or improve perspective float unbend_lin_horz, unbend_lin_vert; // unbend values from dialog float unbend_cur_horz, unbend_cur_vert; float unbend_x1, unbend_x2, unbend_y1, unbend_y2; // unbend axes scaled 0 to 1 int unbend_hx1, unbend_hy1, unbend_hx2, unbend_hy2; int unbend_vx1, unbend_vy1, unbend_vx2, unbend_vy2; editfunc EFunbend; // menu function void m_unbend(GtkWidget *, cchar *) // overhauled { int unbend_dialog_event(zdialog* zd, cchar *event); void * unbend_thread(void *); void unbend_mousefunc(); F1_help_topic = "unbend_image"; EFunbend.funcname = "unbend"; EFunbend.FprevReq = 1; // use preview EFunbend.Frestart = 1; // restart allowed EFunbend.threadfunc = unbend_thread; // thread function EFunbend.mousefunc = unbend_mousefunc; // mouse function if (! edit_setup(EFunbend)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); /*** _________________________________________ | Unbend | | ____ ____ | | | | [____] | | [____] | splinvert spcurvert ////////////// | |____| |____| | | | | ____ ____ | | | | [____] | | [____] | splinhorz spcurhorz | |____| |____| | | | | [ grid ] | | [done] [cancel] | |_________________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Unbend"),Mwin,Bgrid,Bdone,Bcancel,null); EFunbend.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=10|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=10|homog"); zdialog_add_widget(zd,"icon","VL","vb1","unbend vert linear.png","size=64"); zdialog_add_widget(zd,"icon","VC","vb1","unbend vert curved.png","size=64"); zdialog_add_widget(zd,"icon","HL","vb1","unbend horz linear.png","size=64"); zdialog_add_widget(zd,"icon","HC","vb1","unbend horz curved.png","size=64"); zdialog_add_widget(zd,"zspin","splinvert","vb2","-99|99|1|0"); zdialog_add_widget(zd,"zspin","spcurvert","vb2","-99|99|1|0"); zdialog_add_widget(zd,"zspin","splinhorz","vb2","-99|99|1|0"); zdialog_add_widget(zd,"zspin","spcurhorz","vb2","-99|99|1|0"); unbend_x1 = unbend_x2 = unbend_y1 = unbend_y2 = 0.5; // initial axes thru image middle unbend_lin_horz = unbend_lin_vert = 0; unbend_cur_horz = unbend_cur_vert = 0; currgrid = 2; // use unbend grid takeMouse(unbend_mousefunc,dragcursor); // connect mouse function signal_thread(); zdialog_run(zd,unbend_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int unbend_dialog_event(zdialog *zd, cchar *event) { if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) // dialog complete { currgrid = 0; // restore normal grid settings if (zd->zstat == 1) { // toggle grid zd->zstat = 0; m_gridlines(0,"grid 2"); // grid settings dialog return 1; } else if (zd->zstat == 2) { // done CEF->Fmods = 0; if (unbend_cur_vert || unbend_cur_horz || // image3 modified unbend_lin_vert || unbend_lin_horz) { CEF->Fmods = 1; CEF->Fsaved = 0; // done } edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // cancel, discard edit draw_toplines(2,0); // erase axes-lines return 1; } if (strstr(event,"splinvert")) { // get new unbend value zdialog_fetch(zd,"splinvert",unbend_lin_vert); signal_thread(); // trigger thread } if (strstr(event,"splinhorz")) { zdialog_fetch(zd,"splinhorz",unbend_lin_horz); signal_thread(); } if (strstr(event,"spcurvert")) { zdialog_fetch(zd,"spcurvert",unbend_cur_vert); signal_thread(); } if (strstr(event,"spcurhorz")) { zdialog_fetch(zd,"spcurhorz",unbend_cur_horz); signal_thread(); } return 1; } // unbend mouse function // adjustable axes void unbend_mousefunc() { cchar *close; float dist1, dist2; float mpx = 0, mpy = 0; if (LMclick) { // left mouse click mpx = Mxclick; mpy = Myclick; } if (Mxdrag || Mydrag) { // mouse dragged mpx = Mxdrag; mpy = Mydrag; } if (! mpx && ! mpy) return; mpx = 1.0 * mpx / E3pxm->ww; // scale mouse position 0 to 1 mpy = 1.0 * mpy / E3pxm->hh; if (mpx < 0.2 || mpx > 0.8 ) { // check reasonable position if (mpy < 0.1 || mpy > 0.9) return; } else if (mpy < 0.2 || mpy > 0.8) { if (mpx < 0.1 || mpx > 0.9) return; } else return; close = "?"; // find closest axis end-point dist1 = 2; dist2 = mpx * mpx + (mpy-unbend_y1) * (mpy-unbend_y1); if (dist2 < dist1) { dist1 = dist2; close = "left"; } dist2 = (1-mpx) * (1-mpx) + (mpy-unbend_y2) * (mpy-unbend_y2); if (dist2 < dist1) { dist1 = dist2; close = "right"; } dist2 = (mpx-unbend_x1) * (mpx-unbend_x1) + mpy * mpy; if (dist2 < dist1) { dist1 = dist2; close = "top"; } dist2 = (mpx-unbend_x2) * (mpx-unbend_x2) + (1-mpy) * (1-mpy); if (dist2 < dist1) { dist1 = dist2; close = "bottom"; } if (strmatch(close,"left")) unbend_y1 = mpy; // set new axis end-point if (strmatch(close,"right")) unbend_y2 = mpy; if (strmatch(close,"top")) unbend_x1 = mpx; if (strmatch(close,"bottom")) unbend_x2 = mpx; signal_thread(); // trigger thread LMclick = Mxdrag = Mydrag = 0; return; } // unbend thread function void * unbend_thread(void *arg) { void * unbend_wthread(void *); while (true) { thread_idle_loop(); // wait for work or exit request unbend_hx1 = 0; // scale axes to E3ww/hh unbend_hy1 = unbend_y1 * E3pxm->hh; unbend_hx2 = E3pxm->ww; unbend_hy2 = unbend_y2 * E3pxm->hh; unbend_vx1 = unbend_x1 * E3pxm->ww; unbend_vy1 = 0; unbend_vx2 = unbend_x2 * E3pxm->ww; unbend_vy2 = E3pxm->hh; Ntoplines = 2; toplines[0].x1 = unbend_hx1; // lines on window toplines[0].y1 = unbend_hy1; toplines[0].x2 = unbend_hx2; toplines[0].y2 = unbend_hy2; toplines[0].type = 2; toplines[1].x1 = unbend_vx1; toplines[1].y1 = unbend_vy1; toplines[1].x2 = unbend_vx2; toplines[1].y2 = unbend_vy2; toplines[1].type = 2; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(unbend_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * unbend_wthread(void *arg) // worker thread function { int index = *((int *) arg); int vstat, px3, py3, cx3, cy3; float dispx, dispy, dispx2, dispy2; float px1, py1, vx1, vx2, hy1, hy2; float curvert, curhorz, linvert, linhorz; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); curvert = int(unbend_cur_vert * 0.01 * E3pxm->hh); // -0.99 to +0.99 curhorz = int(unbend_cur_horz * 0.01 * E3pxm->ww); linvert = int(unbend_lin_vert * 0.0013 * E3pxm->hh); // -0.13 to +0.13 linhorz = int(unbend_lin_horz * 0.0013 * E3pxm->ww); vx1 = unbend_vx1; vx2 = unbend_vx2; hy1 = unbend_hy1; hy2 = unbend_hy2; for (py3 = index; py3 < E3pxm->hh; py3 += NWT) // step through F3 pixels for (px3 = 0; px3 < E3pxm->ww; px3++) { cx3 = vx1 + (vx2 - vx1) * py3 / E3pxm->hh; // center of unbend cy3 = hy1 + (hy2 - hy1) * px3 / E3pxm->ww; dispx = 2.0 * (px3 - cx3) / E3pxm->ww; // -1.0 .. 0.0 .. +1.0 (roughly) dispy = 2.0 * (py3 - cy3) / E3pxm->hh; // -1.0 .. 0.0 .. +1.0 dispx2 = -cosf(0.8 * dispx) + 1; // curved dispy2 = -cosf(0.8 * dispy) + 1; pix3 = PXMpix(E3pxm,px3,py3); // output pixel px1 = px3; // input pixel = output py1 = py3; px1 += dispx * dispy * linhorz; // move input pixel py1 += dispy * dispx * linvert; px1 -= dispx * dispy2 * curhorz; // change sign py1 -= dispy * dispx2 * curvert; vstat = vpixel(E1pxm,px1,py1,vpix); if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Convert a selected tetragon area into a rectangle, converting the // rest of the image to match and keeping straight lines straight. namespace perspective { int PSP_pixel[4][2]; // last 0-4 pixels clicked int PSP_npix; // count of pixels char PSP_pixlab[4][4] = { " A ", " B ", " C ", " D " }; int PSP_corner = 0; editfunc EFperspective; int dialog_event(zdialog *zd, cchar *event); void mousefunc(void); void warpfunc(void); void KBfunc(int key); } // menu function void m_perspective(GtkWidget *, cchar *) { using namespace perspective; cchar *PSP_message = ZTX( " Click the four corners of a tetragon area. Press [apply]. \n" " The image is warped to make the tetragon into a rectangle."); F1_help_topic = "fix_perspective"; EFperspective.menufunc = m_perspective; EFperspective.funcname = "perspective"; EFperspective.Frestart = 1; // restart allowed EFperspective.mousefunc = mousefunc; // mouse function if (! edit_setup(EFperspective)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); PSP_npix = 0; // no pixels yet zdialog *zd = zdialog_new(ZTX("Perspective Correction"),Mwin,Bapply,Breset,Btrim,Bdone,null); zdialog_add_widget(zd,"label","lab1","dialog",PSP_message,"space=3"); EFperspective.zd = zd; zdialog_run(zd,dialog_event,"save"); // run dialog, parallel takeMouse(mousefunc,dragcursor); // connect mouse function return; } // dialog completion callback function int perspective::dialog_event(zdialog *zd, cchar *event) { using namespace perspective; int ii, px, py; int minx, maxx, miny, maxy; if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (strmatch(event,"focus")) // toggle mouse capture 12.01 takeMouse(mousefunc,dragcursor); if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) // apply { erase_toptext(102); // erase points warpfunc(); // do the warp zd->zstat = 0; // keep dialog active } else if (zd->zstat == 2) // reset { edit_reset(); zd->zstat = 0; for (ii = 0; ii < PSP_npix; ii++) // show pixel labels on image { px = PSP_pixel[ii][0]; py = PSP_pixel[ii][1]; add_toptext(102,px,py,PSP_pixlab[ii],"Sans 8"); } Fpaint2(); } else if (zd->zstat == 3) // trim { if (PSP_npix < 4) { // wait for 4 corners zd->zstat = 0; return 1; } erase_toptext(102); // erase labels warpfunc(); // do the warp edit_done(0); minx = miny = 99999; // find trim limits maxx = maxy = 0; for (ii = 0; ii < 4; ii++) { if (PSP_pixel[ii][0] < minx) minx = PSP_pixel[ii][0]; if (PSP_pixel[ii][0] > maxx) maxx = PSP_pixel[ii][0]; if (PSP_pixel[ii][1] < miny) miny = PSP_pixel[ii][1]; if (PSP_pixel[ii][1] > maxy) maxy = PSP_pixel[ii][1]; } trimx1 = minx; // set parameters for trim function trimy1 = miny; // 17.04 trimx2 = maxx; trimy2 = maxy; m_trim_rotate(0,"keep"); } else if (zd->zstat == 4) { // done erase_toptext(102); // erase labels edit_done(0); // commit edit } else { // cancel erase_toptext(102); // erase labels edit_cancel(0); // discard edit } return 1; } // mouse function - click on 4 corners of tetragon void perspective::mousefunc(void) { using namespace perspective; int ii, minii, jj, px, py; float dist, distx, disty, mindist; if (LMclick) // left click { for (ii = 0; ii < PSP_npix; ii++) // check if very near a previous corner { if (abs(PSP_pixel[ii][0] - Mxclick) < 0.07 * E3pxm->ww && abs(PSP_pixel[ii][1] - Myclick) < 0.07 * E3pxm->hh) { PSP_pixel[ii][0] = Mxclick; // yes, set new corner position PSP_pixel[ii][1] = Myclick; PSP_corner = ii; goto showcorners; } } if (PSP_npix < 4) // if < 4 corners, add a new one { ii = PSP_npix; // next corner to fill PSP_pixel[ii][0] = Mxclick; // save newest corner position PSP_pixel[ii][1] = Myclick; PSP_corner = ii; PSP_npix++; goto showcorners; } mindist = 99999; // all 4 corners already specified minii = -1; for (ii = 0; ii < 4; ii++) // find closest corner to click position { distx = (Mxclick - PSP_pixel[ii][0]); disty = (Myclick - PSP_pixel[ii][1]); dist = sqrt(distx*distx + disty*disty); if (dist < mindist) { mindist = dist; minii = ii; } } if (minii >= 0) { // set new corner position ii = minii; PSP_pixel[ii][0] = Mxclick; PSP_pixel[ii][1] = Myclick; PSP_corner = ii; goto showcorners; } } else if (RMclick) // right click { mindist = 99999; minii = -1; for (ii = 0; ii < PSP_npix; ii++) // find closest corner to click position { distx = (Mxclick - PSP_pixel[ii][0]); disty = (Myclick - PSP_pixel[ii][1]); dist = sqrt(distx*distx + disty*disty); if (dist < mindist) { mindist = dist; minii = ii; } } if (minii >= 0) { // replace deleted corner with ii = minii; // last corner jj = PSP_npix - 1; PSP_pixel[ii][0] = PSP_pixel[jj][0]; PSP_pixel[ii][1] = PSP_pixel[jj][1]; PSP_corner = ii; --PSP_npix; // reduce corner count goto showcorners; } } showcorners: // show corner labels on image erase_toptext(102); for (ii = 0; ii < PSP_npix; ii++) { px = PSP_pixel[ii][0]; py = PSP_pixel[ii][1]; add_toptext(102,px,py,PSP_pixlab[ii],"Sans 8"); } LMclick = RMclick = 0; Fpaint2(); return; } // Keyboard function // KB arrow keys tweak position of last touched tetragon corner. void perspective::KBfunc(int key) { using namespace perspective; int ii, xstep, ystep; xstep = ystep = 0; if (key == GDK_KEY_Left) xstep = -1; if (key == GDK_KEY_Right) xstep = +1; if (key == GDK_KEY_Up) ystep = -1; if (key == GDK_KEY_Down) ystep = +1; ii = PSP_corner; if (ii < 0 || ii > 3) return; // last corner touched, 0-3 PSP_pixel[ii][0] += xstep; PSP_pixel[ii][1] += ystep; mousefunc(); return; } // perspective warp function - make input tetragon into a rectangle void perspective::warpfunc(void) { using namespace perspective; int ii, jj, tempx, tempy, vstat; float px3, py3, trpx[4], trpy[4]; float sqpx0, sqpy0, sqpx1, sqpy1, sqpx2, sqpy2, sqpx3, sqpy3; float cdx0, cdy0, cdx1, cdy1, cdx2, cdy2, cdx3, cdy3; float px1, py1, dispx, dispy, sqww, sqhh; float f0, f1, f2, f3; float vpix1[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (PSP_npix != 4) { zmessageACK(Mwin,ZTX("must have 4 corners")); return; } for (ii = 0; ii < 4; ii++) { // get 4 selected tetragon points trpx[ii] = PSP_pixel[ii][0]; trpy[ii] = PSP_pixel[ii][1]; } // sort 4 points in clockwise order NW, NE, SE, SW for (ii = 0; ii < 4; ii++) { // sort top to bottom (y order) for (jj = ii; jj < 4; jj++) { if (trpy[jj] < trpy[ii]) { tempx = trpx[ii]; tempy = trpy[ii]; trpx[ii] = trpx[jj]; trpy[ii] = trpy[jj]; trpx[jj] = tempx; trpy[jj] = tempy; } } } if (trpx[1] < trpx[0]) { // sort upper two left, right tempx = trpx[0]; tempy = trpy[0]; trpx[0] = trpx[1]; trpy[0] = trpy[1]; trpx[1] = tempx; trpy[1] = tempy; } if (trpx[2] < trpx[3]) { // sort lower two right, left tempx = trpx[2]; tempy = trpy[2]; trpx[2] = trpx[3]; trpy[2] = trpy[3]; trpx[3] = tempx; trpy[3] = tempy; } if (trpx[0] < trpx[3]) sqpx0 = sqpx3 = trpx[0]; // rectangle enclosing tetragon else sqpx0 = sqpx3 = trpx[3]; if (trpx[1] > trpx[2]) sqpx1 = sqpx2 = trpx[1]; else sqpx1 = sqpx2 = trpx[2]; if (trpy[0] < trpy[1]) sqpy0 = sqpy1 = trpy[0]; else sqpy0 = sqpy1 = trpy[1]; if (trpy[2] > trpy[3]) sqpy2 = sqpy3 = trpy[2]; else sqpy2 = sqpy3 = trpy[3]; /*** sqpx0 = sqpx3 = 0.5 * (trpx[0] + trpx[3]); // rectangle bisecting tetragon sides sqpx1 = sqpx2 = 0.5 * (trpx[1] + trpx[2]); sqpy0 = sqpy1 = 0.5 * (trpy[0] + trpy[1]); sqpy2 = sqpy3 = 0.5 * (trpy[2] + trpy[3]); ***/ cdx0 = sqpx0 - trpx[0]; // displavement of tetragon corner cdy0 = sqpy0 - trpy[0]; // to corresponding rectangle corner cdx1 = sqpx1 - trpx[1]; cdy1 = sqpy1 - trpy[1]; cdx2 = sqpx2 - trpx[2]; cdy2 = sqpy2 - trpy[2]; cdx3 = sqpx3 - trpx[3]; cdy3 = sqpy3 - trpy[3]; sqww = 1.0 / (sqpx1 - sqpx0); // rectangle width and height sqhh = 1.0 / (sqpy3 - sqpy0); for (py3 = 0; py3 < E3pxm->hh; py3++) // loop all output pixels for (px3 = 0; px3 < E3pxm->ww; px3++) { f0 = (1.0 - (px3 - sqpx0) * sqww) * (1.0 - (py3 - sqpy0) * sqhh); f1 = (px3 - sqpx0) * sqww * (1.0 - (py3 - sqpy0) * sqhh); f2 = (px3 - sqpx0) * sqww * (py3 - sqpy0) * sqhh; f3 = (1.0 - (px3 - sqpx0) * sqww) * (py3 - sqpy0) * sqhh; dispx = cdx0 * f0 + cdx1 * f1 + cdx2 * f2 + cdx3 * f3; dispy = cdy0 * f0 + cdy1 * f1 + cdy2 * f2 + cdy3 * f3; px1 = px3 - dispx; // input virtual pixel for px3/py3 py1 = py3 - dispy; pix3 = PXMpix(E3pxm,int(px3),int(py3)); // output pixel vstat = vpixel(E1pxm,px1,py1,vpix1); // output pixel = input virtual pixel if (vstat) memcpy(pix3,vpix1,pcc); else memset(pix3,0,pcc); // voided pixel } CEF->Fmods++; // image is modified CEF->Fsaved = 0; Fpaint2(); // update window return; } /********************************************************************************/ // warp/distort area - select image area and pull with mouse float *WarpAx, *WarpAy; // memory for pixel warp vectors int WarpAcc, WarpAnew; int WarpA_started; int WarpA_areanumber; editfunc EFwarpA; void WarpA_init(); void WarpA_warpfunc(float mdx, float mdy, float mdw, float mdh, int acc); void WarpA_warpfunc2(float mdx, float mdy, float mdw, float mdh, int acc); void WarpA_mousefunc(); void WarpA_expand(); void WarpA_edgeblend(); // menu function void m_warp_area(GtkWidget *, cchar *) { int WarpA_dialog_event(zdialog *zd, cchar *event); cchar *WarpA_message = ZTX( " Select an area to warp using select area function. \n" " Press [start warp] and pull area with mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, select another area or press [done]."); F1_help_topic = "warp_area"; EFwarpA.menufunc = m_warp_area; EFwarpA.funcname = "warp_area"; EFwarpA.Farea = 2; // select area usable EFwarpA.mousefunc = WarpA_mousefunc; // mouse function if (! edit_setup(EFwarpA)) return; // setup edit zdialog *zd = zdialog_new(ZTX("Warp area"),Mwin,Bdone,Bcancel,null); EFwarpA.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",WarpA_message,"space=3"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"button","start","hb1",ZTX("start warp"),"space=5"); zdialog_add_widget(zd,"button","reset","hb1",Breset,"space=5"); WarpAcc = E3pxm->ww * E3pxm->hh * sizeof(float); WarpAx = (float *) zmalloc(WarpAcc); // get memory for pixel warp vectors WarpAy = (float *) zmalloc(WarpAcc); memset(WarpAx,0,WarpAcc); memset(WarpAy,0,WarpAcc); WarpA_started = 0; WarpA_areanumber = 0; WarpAnew = 0; zdialog_run(zd,WarpA_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int WarpA_dialog_event(zdialog * zd, cchar *event) { int init = 0; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit zfree(WarpAx); // release memory zfree(WarpAy); return 1; } if (! sa_validate()) init = 1; // area invalid for curr. image file if (sa_stat != 3 || sa_mode == mode_image) // no select area active init = 1; if (WarpA_started && WarpA_areanumber != areanumber) // select area changed init = 1; if (init) { memset(WarpAx,0,WarpAcc); memset(WarpAy,0,WarpAcc); WarpA_started = 0; WarpA_areanumber = 0; return 1; } if (strmatch(event,"start")) // start warp { if (sa_stat != 3 || sa_mode == mode_image) { // no select area active zmessageACK(Mwin,ZTX("no active Select Area")); return 1; } sa_edgecalc(); // calculate area edge distances takeMouse(WarpA_mousefunc,dragcursor); // connect mouse function WarpA_started = 1; WarpA_areanumber = areanumber; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(WarpA_mousefunc,dragcursor); if (strmatch(event,"reset")) { // undo all warps edit_reset(); memset(WarpAx,0,WarpAcc); // clear warp vectors memset(WarpAy,0,WarpAcc); } if (strmatch(event,"blendwidth")) WarpA_edgeblend(); return 1; } // warp mouse function void WarpA_mousefunc() { static float mdx, mdy, mdw, mdh; static int warped = 0; if (! WarpA_started) return; if (Mxdrag || Mydrag) // mouse drag underway { mdx = Mxdown; // drag origin, image coordinates mdy = Mydown; mdw = Mxdrag - Mxdown; // drag increment mdh = Mydrag - Mydown; WarpA_warpfunc(mdx,mdy,mdw,mdh,0); // warp image warped = 1; Mxdrag = Mydrag = 0; return; } else if (warped) { warped = 0; WarpA_warpfunc(mdx,mdy,mdw,mdh,1); // drag done, update warp vectors if (WarpAnew) WarpA_expand(); } return; } // warp image and accumulate warp memory // mdx/y = mouse initial position // mdw/h = mouse drag vector void WarpA_warpfunc(float mdx, float mdy, float mdw, float mdh, int acc) { int ii, px, py, ww, hh, vstat; float ddx, ddy, dpe, dpm1, dpm2, dpm; float mag, dispx, dispy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (sa_stat != 3) return; // area erased ww = E1pxm->ww; hh = E1pxm->hh; ii = mdy * ww + mdx; if (! sa_pixmap[ii]) { // if mouse drag outside select area WarpA_warpfunc2(mdx,mdy,mdw,mdh,acc); // then use alternate function return; } for (py = sa_miny; py < sa_maxy; py++) // loop all pixels in area for (px = sa_minx; px < sa_maxx; px++) { ii = py * ww + px; dpe = sa_pixmap[ii]; // distance from area edge if (dpe < 1) continue; // outside area dpe -= 1; // rebase edge = 0 ddx = (px - mdx); // pixel distance to mouse position ddy = (py - mdy); // before drag dpm1 = sqrt(ddx*ddx + ddy*ddy); ddx -= mdw; // pixel distance to mouse position ddy -= mdh; // after drag dpm2 = sqrt(ddx*ddx + ddy*ddy); dpm = 0.5 * (dpm1 + dpm2); // mean mag = (dpe / (dpm + dpe)); // 1...0 for pixel at mouse...edge dispx = -mdw * mag; // pixel movement from drag movement dispy = -mdh * mag; dispx += WarpAx[ii]; // add this warp to prior dispy += WarpAy[ii]; if (acc) { // mouse drag done, WarpAx[ii] = dispx; // accumulate warp memory WarpAy[ii] = dispy; continue; } vstat = vpixel(E1pxm,px+dispx,py+dispy,vpix); // input virtual pixel if (vstat) { pix3 = PXMpix(E3pxm,px,py); // output pixel memcpy(pix3,vpix,pcc); } } ww = sa_maxx - sa_minx; // update window hh = sa_maxy - sa_miny; Fpaint3(sa_minx,sa_miny,ww,hh,0); CEF->Fmods++; CEF->Fsaved = 0; return; } // warp function when mouse is dragged outside select area void WarpA_warpfunc2(float mdx, float mdy, float mdw, float mdh, int acc) { int ii, jj, px, py, dpx, dpy, dpe; int xlo, xhi, ylo, yhi; int ww, hh, vstat; float ddx, ddy, dpm1, dpm2, dpm; float mag, dispx, dispy; float mdmax, mdmax2; float vpix[4], *pix1, *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); #define distance(px1,py1,px2,py2) \ sqrtf((px2-px1)*(px2-px1)+(py2-py1)*(py2-py1)) WarpAnew = 1; // note select area will expand ww = E1pxm->ww; hh = E1pxm->hh; ylo = sa_miny; // select area enclosing rectangle yhi = sa_maxy; xlo = sa_minx; xhi = sa_maxx; if (mdy < ylo) ylo = mdy - 200; // extend area to mouse posn + more if (mdy > yhi) yhi = mdy + 200; if (mdx < xlo) xlo = mdx - 200; if (mdx > xhi) xhi = mdx + 200; if (ylo < 0) ylo = 0; if (yhi > hh) yhi = hh; if (xlo < 0) xlo = 0; if (xhi > ww) xhi = ww; mdmax = distance(mdx,mdy,xlo,ylo); // find max. mouse distance from mdmax2 = distance(mdx,mdy,xhi,ylo); // enclosing rectangle corners if (mdmax2 > mdmax) mdmax = mdmax2; mdmax2 = distance(mdx,mdy,xhi,yhi); if (mdmax2 > mdmax) mdmax = mdmax2; mdmax2 = distance(mdx,mdy,xlo,yhi); if (mdmax2 > mdmax) mdmax = mdmax2; for (py = ylo; py < yhi; py++) // loop all pixels in enclosing rectangle for (px = xlo; px < xhi; px++) { ddx = (px - mdx); // pixel distance to mouse initial position ddy = (py - mdy); dpm1 = sqrt(ddx*ddx + ddy*ddy); ddx -= mdw; // pixel distance to mouse curr. position ddy -= mdh; dpm2 = sqrt(ddx*ddx + ddy*ddy); dpm = 0.5 * (dpm1 + dpm2); // mean mag = 1.0 - dpm / mdmax; // 1...0 for pixel-mouse 0...mdmax dispx = -mdw * mag; dispy = -mdh * mag; // pixel movement from drag movement ii = py * ww + px; dispx += WarpAx[ii]; // add this warp to prior dispy += WarpAy[ii]; dpx = px + dispx; // source virtual pixel dpy = py + dispy; // (nearest real pixel) if (dpx < 0) dpx = 0; if (dpx > ww-1) dpx = ww-1; if (dpy < 0) dpy = 0; if (dpy > hh-1) dpy = hh-1; jj = dpy * ww + dpx; dpe = sa_pixmap[jj]; // distance from area edge if (dpe < 1) // outside area dispx = dispy = 0; if (acc) { // mouse drag done, WarpAx[ii] = dispx; // accumulate warp memory WarpAy[ii] = dispy; continue; } if (dpe) { // source pixel inside area vstat = vpixel(E1pxm,px+dispx,py+dispy,vpix); // = input virtual pixel if (vstat) { pix3 = PXMpix(E3pxm,px,py); // output pixel memcpy(pix3,vpix,pcc); continue; } } pix1 = PXMpix(E1pxm,px,py); // pixel is unchanged pix3 = PXMpix(E3pxm,px,py); memcpy(pix3,pix1,pcc); } ww = xhi - xlo; // update window hh = yhi - ylo; Fpaint3(xlo,ylo,ww,hh,0); CEF->Fmods++; CEF->Fsaved = 0; return; } // expand select area if pulled outside the original bounds void WarpA_expand() { int ii, px, py, ww, hh; int blend = sa_blend; ww = E1pxm->ww; hh = E1pxm->hh; for (py = 0; py < hh; py++) // loop all pixels for (px = 0; px < ww; px++) { ii = py * ww + px; // find pixels that have moved if (! WarpAx[ii] && ! WarpAy[ii]) continue; sa_pixmap[ii] = 1; // mark within area } sa_map_pixels(); sa_finish_auto(); sa_edgecalc(); // recalculate edge distances sa_blend = blend; WarpA_edgeblend(); WarpA_areanumber = areanumber; WarpAnew = 0; return; } // blend area edges according to sa_blend void WarpA_edgeblend() { int ii, px, py, ww, hh, dist, vstat; float *pix1, *pix3, vpix[4]; float dold, dnew, dispx, dispy; if (! sa_blend) return; ww = E1pxm->ww; hh = E1pxm->hh; for (py = 0; py < hh; py++) // loop output pixels for (px = 0; px < ww; px++) { ii = py * ww + px; // blend changes at edge dist = sa_pixmap[ii]; if (! dist) continue; // outside area if (dist > sa_blend) continue; // beyond blend area dispx = WarpAx[ii]; // warp dispy = WarpAy[ii]; vstat = vpixel(E1pxm,px+dispx,py+dispy,vpix); // input virtual pixel (new) if (! vstat) continue; pix1 = PXMpix(E1pxm,px,py); // input pixel (old) pix3 = PXMpix(E3pxm,px,py); // output pixel dnew = sa_blendfunc(dist); dold = 1.0 - dnew; pix3[0] = dnew * vpix[0] + dold * pix1[0]; pix3[1] = dnew * vpix[1] + dold * pix1[1]; pix3[2] = dnew * vpix[2] + dold * pix1[2]; } ww = sa_maxx - sa_minx; // update window hh = sa_maxy - sa_miny; Fpaint3(sa_minx,sa_miny,ww,hh,0); return; } /********************************************************************************/ // Unwarp closeup face photo - shrink magnified areas closest to camera int unwarpCU_started; int unwarpCU_areanumber; float unwarpCU_warpval; int unwarpCU_cx, unwarpCU_cy; editfunc EFunwarpCU; int unwarpCU_dialog_event(zdialog *zd, cchar *event); void unwarpCU_warpfunc(); void unwarpCU_mousefunc(); // menu function void m_unwarp_closeup(GtkWidget *, cchar *) { cchar *unwarpCU_message = ZTX( " Use Select Area to select a face. \n" " Click on the center of distortion. \n" " Move the slider. \n"); F1_help_topic = "unwarp_closeup"; EFunwarpCU.menufunc = m_unwarp_closeup; EFunwarpCU.funcname = "unwarp_closeup"; EFunwarpCU.Farea = 2; // select area usable EFunwarpCU.mousefunc = unwarpCU_mousefunc; // mouse function if (! edit_setup(EFunwarpCU)) return; // setup edit /*** ______________________________________ | Unwarp Closeup | | | | Use Select Area to select a face. | | Click on the center of distortion. | | Move the slider. | | | | [===============[]================] | | | | [done] [cancel] | |______________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Unwarp Closeup"),Mwin,Bdone,Bcancel,null); EFunwarpCU.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",unwarpCU_message,"space=3"); zdialog_add_widget(zd,"hbox","hbw","dialog",0,"space=5"); zdialog_add_widget(zd,"hscale","warpval","hbw","0.0|1.0|0.01|0.0","space=5|expand"); takeMouse(unwarpCU_mousefunc,dragcursor); // connect mouse function unwarpCU_started = 0; unwarpCU_areanumber = 0; zdialog_run(zd,unwarpCU_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int unwarpCU_dialog_event(zdialog * zd, cchar *event) { int init = 0; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (! sa_validate()) init = 1; // area invalid for curr. image file if (sa_stat != 3 || sa_mode == mode_image) // no select area active init = 1; if (unwarpCU_started && unwarpCU_areanumber != areanumber) // select area changed init = 1; if (init) { unwarpCU_started = 0; unwarpCU_areanumber = 0; return 1; } if (strmatch(event,"focus")) // reconnect mouse takeMouse(unwarpCU_mousefunc,dragcursor); if (strmatch(event,"warpval")) { // slider movement zdialog_fetch(zd,"warpval",unwarpCU_warpval); unwarpCU_warpfunc(); } return 1; } // mouse function void unwarpCU_mousefunc() { if (LMclick) { unwarpCU_cx = Mxclick; // capture central point unwarpCU_cy = Myclick; } else if (Mxdrag || Mydrag) { unwarpCU_cx = Mxdrag; unwarpCU_cy = Mydrag; } else return; LMclick = Mxdrag = Mydrag = 0; if (sa_stat != 3 || sa_mode == mode_image) { // no select area active zmessageACK(Mwin,ZTX("no active Select Area")); unwarpCU_started = 0; Mdrag = 0; return; } unwarpCU_started = 1; // unwarp can proceed unwarpCU_areanumber = areanumber; sa_edgecalc(); // calculate area edge distances unwarpCU_warpfunc(); return; } // warp image according to slider position void unwarpCU_warpfunc() { int ii, px, py, ww, vstat; float hsize, vsize, hpos, vpos; float warpval = unwarpCU_warpval; float ed, cx, cy, dx, dy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (sa_stat != 3) return; // area erased hsize = sa_maxx - sa_minx; // area size vsize = sa_maxy - sa_miny; cx = unwarpCU_cx; // unwarp center (nose tip) cy = unwarpCU_cy; ww = E1pxm->ww; for (py = sa_miny; py < sa_maxy; py++) // loop all pixels in area for (px = sa_minx; px < sa_maxx; px++) { ii = py * ww + px; if (sa_pixmap[ii] < 1) continue; // pixel outside area hpos = 2.0 * (px - cx) / hsize; // horizontal pixel position, -1 ... +1 vpos = 2.0 * (py - cy) / vsize; // vertical pixel position, -1 ... +1 ed = sa_pixmap[ii]; // pixel edge distance hpos = hpos * ed / hsize; // scale pixel position vpos = vpos * ed / vsize; dx = sinf(PI * hpos); // pixel displacement, -1 ... +1 dy = sinf(PI * vpos); dx = dx * 0.1 * warpval * hsize; // pixel displacement, -10% ... +10% dy = dy * 0.1 * warpval * vsize; vstat = vpixel(E1pxm,px+dx,py+dy,vpix); // input virtual pixel if (vstat) { pix3 = PXMpix(E3pxm,px,py); // output pixel memcpy(pix3,vpix,pcc); } } Fpaint3(sa_minx,sa_miny,hsize,vsize,0); CEF->Fmods++; CEF->Fsaved = 0; return; } /********************************************************************************/ // warp/distort whole image with a curved transform // fix perspective problems (e.g. curved walls, leaning buildings) namespace warpC_names { float *WarpCx, *WarpCy; // memory of all dragged pixels float WarpCmem[5][100]; // undo memory, last 100 drags int NWarpC; // WarpCmem count int WarpCdrag; int E3ww, E3hh; float $mdx, $mdy, $mdw, $mdh; // warpC_warpfunc() and warpC_wthread() float $D, $span; // use these $ args int $acc; editfunc EFwarpC; int WarpC_dialog_event(zdialog *zd, cchar *event); void WarpC_warpfunc(); void WarpC_mousefunc(void); void * WarpC_wthread(void *arg); } // menu function void m_warp_curved(GtkWidget *, cchar *) { using namespace warpC_names; cchar *WarpC_message = ZTX( " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]."); int px, py, ii; F1_help_topic = "warp_curved"; EFwarpC.menufunc = m_warp_curved; EFwarpC.funcname = "warp_curved"; EFwarpC.FprevReq = 1; // use preview EFwarpC.mousefunc = WarpC_mousefunc; // mouse function if (! edit_setup(EFwarpC)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); zdialog *zd = zdialog_new(ZTX("Warp curved"),Mwin,Bdone,Bcancel,null); EFwarpC.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",WarpC_message,"space=3"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=8"); zdialog_add_widget(zd,"button","undolast","hb1",Bundolast,"space=8"); zdialog_add_widget(zd,"button","undoall","hb1",Bundoall,"space=2"); zdialog_add_widget(zd,"button","grid","hb1",Bgrid,"space=15"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=4"); zdialog_add_widget(zd,"label","lab2","hb2",ZTX("warp span"),"space=8"); zdialog_add_widget(zd,"zspin","span","hb2","0.00|1.0|0.01|0.1","space=1"); currgrid = 3; // use warp_curved grid NWarpC = WarpCdrag = 0; // no drag data int cc = E3pxm->ww * E3pxm->hh * sizeof(float); WarpCx = (float *) zmalloc(cc); // get memory for pixel displacements WarpCy = (float *) zmalloc(cc); for (py = 0; py < E3pxm->hh; py++) // no pixel displacements for (px = 0; px < E3pxm->ww; px++) { ii = py * E3pxm->ww + px; WarpCx[ii] = WarpCy[ii] = 0.0; } E3ww = E3pxm->ww; // preview dimensions E3hh = E3pxm->hh; zdialog_restore_inputs(zd); // restore previous inputs zdialog_fetch(zd,"span",$span); // save span value zdialog_run(zd,WarpC_dialog_event,"save"); // run dialog, parallel takeMouse(WarpC_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int warpC_names::WarpC_dialog_event(zdialog * zd, cchar *event) { using namespace warpC_names; int px, py, ii; int fpx, fpy, epx, epy, vstat; float scale, dispx, dispy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) goto complete; if (strmatch(event,"undolast")) { if (NWarpC == 1) event = "undoall"; else if (NWarpC) { // undo most recent drag ii = --NWarpC; $mdx = WarpCmem[0][ii]; $mdy = WarpCmem[1][ii]; $mdw = -WarpCmem[2][ii]; $mdh = -WarpCmem[3][ii]; $span = WarpCmem[4][ii]; zdialog_stuff(zd,"span",$span); $acc = 0; WarpC_warpfunc(); // undrag image $acc = 1; WarpC_warpfunc(); // undrag memory } } if (strmatch(event,"undoall")) // undo all drags { NWarpC = 0; // erase undo memory for (py = 0; py < E3pxm->hh; py++) // reset pixel displacements for (px = 0; px < E3pxm->ww; px++) { ii = py * E3pxm->ww + px; WarpCx[ii] = WarpCy[ii] = 0.0; } edit_reset(); // restore image 1 } if (strmatch(event,"grid")) m_gridlines(0,"grid 3"); // grid settings dialog if (strmatch(event,"span")) zdialog_fetch(zd,"span",$span); return 1; complete: currgrid = 0; // restore normal grid settings if (zd->zstat) { if (NWarpC == 0) zd->zstat = 2; // no warps, cancel if (zd->zstat == 1) // done { edit_fullsize(); // get full size image scale = 1.0 * (E3pxm->ww + E3pxm->hh) / (E3ww + E3hh); for (fpy = 0; fpy < E3pxm->hh; fpy++) // scale net pixel displacements for (fpx = 0; fpx < E3pxm->ww; fpx++) // to full image size { epx = E3ww * fpx / E3pxm->ww; epy = E3hh * fpy / E3pxm->hh; ii = epy * E3ww + epx; dispx = WarpCx[ii] * scale; dispy = WarpCy[ii] * scale; vstat = vpixel(E1pxm,fpx+dispx,fpy+dispy,vpix); // input virtual pixel pix3 = PXMpix(E3pxm,fpx,fpy); // output pixel if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); // voided pixel } signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit zfree(WarpCx); // release memory zfree(WarpCy); } return 1; } // WarpC mouse function void warpC_names::WarpC_mousefunc(void) { using namespace warpC_names; int ii; if (Mxdrag || Mydrag) // mouse drag underway { $mdx = Mxdown; // drag origin $mdy = Mydown; $mdw = Mxdrag - Mxdown; // drag increment $mdh = Mydrag - Mydown; $acc = 0; WarpC_warpfunc(); // drag image WarpCdrag = 1; Mxdrag = Mydrag = 0; return; } else if (WarpCdrag) { WarpCdrag = 0; $acc = 1; WarpC_warpfunc(); // drag done, add to memory if (NWarpC == 100) // if full, throw away oldest { NWarpC = 99; for (ii = 0; ii < NWarpC; ii++) { WarpCmem[0][ii] = WarpCmem[0][ii+1]; WarpCmem[1][ii] = WarpCmem[1][ii+1]; WarpCmem[2][ii] = WarpCmem[2][ii+1]; WarpCmem[3][ii] = WarpCmem[3][ii+1]; WarpCmem[4][ii] = WarpCmem[4][ii+1]; } } ii = NWarpC; WarpCmem[0][ii] = $mdx; // save drag for undo WarpCmem[1][ii] = $mdy; WarpCmem[2][ii] = $mdw; WarpCmem[3][ii] = $mdh; WarpCmem[4][ii] = $span; NWarpC++; } return; } // warp image and accumulate warp memory // mouse at (mx,my) is moved (mw,mh) pixels void warpC_names::WarpC_warpfunc() { using namespace warpC_names; float D, d1, d2, d3, d4; d1 = ($mdx-0) * ($mdx-0) + ($mdy-0) * ($mdy-0); // distance, mouse to 4 corners d2 = (E3pxm->ww-$mdx) * (E3pxm->ww-$mdx) + ($mdy-0) * ($mdy-0); d3 = (E3pxm->ww-$mdx) * (E3pxm->ww-$mdx) + (E3pxm->hh-$mdy) * (E3pxm->hh-$mdy); d4 = ($mdx-0) * ($mdx-0) + (E3pxm->hh-$mdy) * (E3pxm->hh-$mdy); D = d1; if (d2 > D) D = d2; // find greatest corner distance if (d3 > D) D = d3; if (d4 > D) D = d4; $D = D * $span; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(WarpC_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window return; } // working thread to process the pixels void * warpC_names::WarpC_wthread(void *arg) { using namespace warpC_names; int index = *((int *) arg); int ii, px, py, vstat; float d, mag, dispx, dispy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); for (py = index; py < E3pxm->hh; py += NWT) // process all pixels for (px = 0; px < E3pxm->ww; px++) { d = (px-$mdx)*(px-$mdx) + (py-$mdy)*(py-$mdy); mag = (1.0 - d / $D); if (mag < 0) continue; mag = mag * mag; // faster than pow(mag,4); mag = mag * mag; dispx = -$mdw * mag; // displacement = drag * mag dispy = -$mdh * mag; ii = py * E3pxm->ww + px; if ($acc) { // drag done, accumulate drag sum WarpCx[ii] += dispx; WarpCy[ii] += dispy; continue; } dispx += WarpCx[ii]; // add this drag to prior sum dispy += WarpCy[ii]; vstat = vpixel(E1pxm,px+dispx,py+dispy,vpix); // input virtual pixel pix3 = PXMpix(E3pxm,px,py); // output pixel if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); // voided pixel } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } /********************************************************************************/ // warp/distort whole image with a linear transform (almost) // fix perspective problems (e.g. leaning buildings) namespace warpL_names { float *WarpLx, *WarpLy; // memory of all dragged pixels float WarpLmem[4][100]; // undo memory, last 100 drags int NWarpL; // WarpLmem count int WarpLdrag; int E3ww, E3hh; float $mdx, $mdy, $mdw, $mdh; // warpL_warpfunc() and warpL_wthread() int $D, $Dx, $Dy, $acc; // use these $ args editfunc EFwarpL; int WarpL_dialog_event(zdialog *zd, cchar *event); void WarpL_mousefunc(void); void WarpL_warpfunc(); void * WarpL_wthread(void *arg); } // menu function void m_warp_linear(GtkWidget *, cchar *) { using namespace warpL_names; cchar *WarpL_message = ZTX( " Pull an image position using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]."); int px, py, ii; F1_help_topic = "warp_linear"; EFwarpL.menufunc = m_warp_linear; EFwarpL.funcname = "warp_linear"; EFwarpL.FprevReq = 1; // use preview EFwarpL.mousefunc = WarpL_mousefunc; // mouse function if (! edit_setup(EFwarpL)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); zdialog *zd = zdialog_new(ZTX("Warp linear"),Mwin,Bdone,Bcancel,null); EFwarpL.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",WarpL_message,"space=3"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=10"); zdialog_add_widget(zd,"button","undolast","hb1",Bundolast,"space=5"); zdialog_add_widget(zd,"button","undoall","hb1",Bundoall,"space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=6"); zdialog_add_widget(zd,"button","grid","hb2",Bgrid,"space=10"); currgrid = 4; // use warp_linear grid NWarpL = WarpLdrag = 0; // no drag data int cc = E3pxm->ww * E3pxm->hh * sizeof(float); WarpLx = (float *) zmalloc(cc); // get memory for pixel displacements WarpLy = (float *) zmalloc(cc); for (py = 0; py < E3pxm->hh; py++) // no pixel displacements for (px = 0; px < E3pxm->ww; px++) { ii = py * E3pxm->ww + px; WarpLx[ii] = WarpLy[ii] = 0.0; } E3ww = E3pxm->ww; // preview dimensions E3hh = E3pxm->hh; zdialog_run(zd,WarpL_dialog_event,"save"); // run dialog, parallel takeMouse(WarpL_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int warpL_names::WarpL_dialog_event(zdialog * zd, cchar *event) { using namespace warpL_names; int px, py, ii; int fpx, fpy, epx, epy, vstat; float scale, dispx, dispy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) goto complete; if (strmatch(event,"undolast")) { if (NWarpL == 1) event = "undoall"; else if (NWarpL) { // undo most recent drag ii = --NWarpL; $mdx = WarpLmem[0][ii]; $mdy = WarpLmem[1][ii]; $mdw = -WarpLmem[2][ii]; $mdh = -WarpLmem[3][ii]; $acc = 0; WarpL_warpfunc(); // undrag image $acc = 1; WarpL_warpfunc(); // undrag memory } } if (strmatch(event,"undoall")) // undo all drags { NWarpL = 0; // erase undo memory for (py = 0; py < E3pxm->hh; py++) // reset pixel displacements for (px = 0; px < E3pxm->ww; px++) { ii = py * E3pxm->ww + px; WarpLx[ii] = WarpLy[ii] = 0.0; } edit_reset(); // restore image 1 } if (strmatch(event,"grid")) m_gridlines(0,"grid 4"); // grid settings dialog return 1; complete: currgrid = 0; // restore normal grid settings if (zd->zstat != 1 || NWarpL == 0) // cancel or no warps made edit_cancel(0); else { edit_fullsize(); // get full-size E1/E3 scale = 1.0 * (E3pxm->ww + E3pxm->hh) / (E3ww + E3hh); for (fpy = 0; fpy < E3pxm->hh; fpy++) // scale net pixel displacements for (fpx = 0; fpx < E3pxm->ww; fpx++) // to full image size { epx = E3ww * fpx / E3pxm->ww; epy = E3hh * fpy / E3pxm->hh; ii = epy * E3ww + epx; dispx = WarpLx[ii] * scale; dispy = WarpLy[ii] * scale; vstat = vpixel(E1pxm,fpx+dispx,fpy+dispy,vpix); // input virtual pixel pix3 = PXMpix(E3pxm,fpx,fpy); // output pixel if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); // voided pixel } signal_thread(); edit_done(0); } zfree(WarpLx); // release memory zfree(WarpLy); return 1; } // WarpL mouse function void warpL_names::WarpL_mousefunc(void) { using namespace warpL_names; int ii; if (Mxdrag || Mydrag) // mouse drag underway { $mdx = Mxdown; // drag origin, window coordinates $mdy = Mydown; $mdw = Mxdrag - Mxdown; // drag increment $mdh = Mydrag - Mydown; $acc = 0; WarpL_warpfunc(); // drag image WarpLdrag = 1; Mxdrag = Mydrag = 0; return; } else if (WarpLdrag) { WarpLdrag = 0; $acc = 1; WarpL_warpfunc(); // drag done, add to memory if (NWarpL == 100) // if full, throw away oldest { NWarpL = 99; for (ii = 0; ii < NWarpL; ii++) { WarpLmem[0][ii] = WarpLmem[0][ii+1]; WarpLmem[1][ii] = WarpLmem[1][ii+1]; WarpLmem[2][ii] = WarpLmem[2][ii+1]; WarpLmem[3][ii] = WarpLmem[3][ii+1]; } } ii = NWarpL; WarpLmem[0][ii] = $mdx; // save drag for undo WarpLmem[1][ii] = $mdy; WarpLmem[2][ii] = $mdw; WarpLmem[3][ii] = $mdh; NWarpL++; } return; } // warp image and accumulate warp memory // mouse at ($mdx,$mdy) is moved ($mdw,$mdh) pixels void warpL_names::WarpL_warpfunc() { using namespace warpL_names; float d1, d2, d3, d4; d1 = ($mdx-0) * ($mdx-0) + ($mdy-0) * ($mdy-0); // distance, mouse to 4 corners d2 = (E3pxm->ww-$mdx) * (E3pxm->ww-$mdx) + ($mdy-0) * ($mdy-0); d3 = (E3pxm->ww-$mdx) * (E3pxm->ww-$mdx) + (E3pxm->hh-$mdy) * (E3pxm->hh-$mdy); d4 = ($mdx-0) * ($mdx-0) + (E3pxm->hh-$mdy) * (E3pxm->hh-$mdy); $D = d1; if (d2 > $D) $D = d2; // find greatest corner distance if (d3 > $D) $D = d3; if (d4 > $D) $D = d4; if ($D == d1) { // NW corner $D = 1; $Dx = $mdx; // x/y distance, mouse to edges $Dy = $mdy; } if ($D == d2) { // NE $D = 2; $Dx = E3ww - $mdx; $Dy = $mdy; } if ($D == d3) { // SE $D = 3; $Dx = E3ww - $mdx; $Dy = E3hh - $mdy; } if ($D == d4) { // SW $D = 4; $Dx = $mdx; $Dy = E3hh - $mdy; } for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(WarpL_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window return; } // working thread to process the pixels void * warpL_names::WarpL_wthread(void *arg) // algorithm change { using namespace warpL_names; int index = *((int *) arg); int ii, px, py, vstat; float dx, dy, mag, dispx, dispy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); for (py = index; py < E3pxm->hh; py += NWT) // process all pixels for (px = 0; px < E3pxm->ww; px++) { if ($D == 1) { dx = $mdx - px; // x/y distance, pixel to mouse dy = $mdy - py; } else if ($D == 2) { dx = px - $mdx; dy = $mdy - py; } else if ($D == 3) { dx = px - $mdx; dy = py - $mdy; } else /* $D == 4 */ { dx = $mdx - px; dy = py - $mdy; } mag = (1.0 - dx / $Dx) * (1.0 - dy / $Dy); // pixel movement / mouse drag dispx = -$mdw * mag; // displacement = drag * mag dispy = -$mdh * mag; ii = py * E3pxm->ww + px; if ($acc) { // drag done, accumulate drag sum WarpLx[ii] += dispx; WarpLy[ii] += dispy; continue; } dispx += WarpLx[ii]; // add this drag to prior sum dispy += WarpLy[ii]; vstat = vpixel(E1pxm,px+dispx,py+dispy,vpix); // input virtual pixel pix3 = PXMpix(E3pxm,px,py); // output pixel if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); // voided pixel } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } /********************************************************************************/ // warp/distort whole image using affine transform // (straight lines remain straight) float WarpF_old[3][2]; // 3 original image points float WarpF_new[3][2]; // corresponding warped points float WarpF_coeff[6]; // transform coefficients float WarpF_Icoeff[6]; // inverse transform coefficients int WarpF_ftf; // first time flag editfunc EFwarpF; void WarpF_warpfunc(); // image warp function void WarpF_mousefunc(void); void WarpF_affine(float po[3][2], float pn[3][2], float coeff[6]); // compute affine transform coefficients void WarpF_invert(float coeff[6], float Icoeff[6]); // compute reverse transform coefficients // menu function void m_warp_affine(GtkWidget *, cchar *) { int WarpF_dialog_event(zdialog *zd, cchar *event); cchar *WarpF_message = ZTX( " Pull on an image corner using the mouse. \n" " Make multiple mouse pulls until satisfied. \n" " When finished, press [done]."); F1_help_topic = "warp_affine"; EFwarpF.menufunc = m_warp_affine; EFwarpF.funcname = "warp_affine"; EFwarpF.FprevReq = 1; // use preview EFwarpF.mousefunc = WarpF_mousefunc; // mouse function if (! edit_setup(EFwarpF)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); zdialog *zd = zdialog_new(ZTX("Warp affine"),Mwin,Bdone,Bcancel,null); EFwarpF.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",WarpF_message,"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=6"); zdialog_add_widget(zd,"button","grid","hb2",Bgrid,"space=10"); currgrid = 5; // use warp_affine grid WarpF_ftf = 1; // 1st warp flag zdialog_run(zd,WarpF_dialog_event,"save"); // run dialog, parallel takeMouse(WarpF_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int WarpF_dialog_event(zdialog *zd, cchar *event) { float scale; int ww, hh; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"grid")) m_gridlines(0,"grid 5"); // grid settings dialog if (! zd->zstat) return 1; // wait for completion currgrid = 0; // restore normal grid settings if (zd->zstat != 1 || ! CEF->Fmods) { edit_cancel(0); return 1; } ww = E3pxm->ww; // preview image dimensions hh = E3pxm->hh; edit_fullsize(); // get full-size images scale = 1.0 * (E3pxm->ww + E3pxm->hh) / (ww + hh); // preview to full-size scale factor WarpF_old[0][0] = WarpF_old[0][0] * scale; // re-scale new and old points WarpF_old[0][1] = WarpF_old[0][1] * scale; WarpF_old[1][0] = WarpF_old[1][0] * scale; WarpF_old[1][1] = WarpF_old[1][1] * scale; WarpF_old[2][0] = WarpF_old[2][0] * scale; WarpF_old[2][1] = WarpF_old[2][1] * scale; WarpF_new[0][0] = WarpF_new[0][0] * scale; WarpF_new[0][1] = WarpF_new[0][1] * scale; WarpF_new[1][0] = WarpF_new[1][0] * scale; WarpF_new[1][1] = WarpF_new[1][1] * scale; WarpF_new[2][0] = WarpF_new[2][0] * scale; WarpF_new[2][1] = WarpF_new[2][1] * scale; WarpF_warpfunc(); // warp full-size image edit_done(0); return 1; } // WarpF mouse function void WarpF_mousefunc(void) { int mdx1, mdy1, mdx2, mdy2; float x1o, y1o, x2o, y2o, x3o, y3o; float x1n, y1n, x2n, y2n, x3n, y3n; float a, b, c, d, e, f; if (! Mxdrag && ! Mydrag) return; mdx1 = Mxdown; // mouse drag origin mdy1 = Mydown; mdx2 = Mxdrag; // mouse drag position mdy2 = Mydrag; Mxdown = Mxdrag; // reset origin for next time Mydown = Mydrag; x1n = mdx1; // point 1 = drag origin y1n = mdy1; x2n = E3pxm->ww - x1n; // point 2 = mirror of point1 y2n = E3pxm->hh - y1n; x3n = E3pxm->ww * (y2n / E3pxm->hh); y3n = E3pxm->hh * (1.0 - (x2n / E3pxm->ww)); if (WarpF_ftf) // first warp { WarpF_ftf = 0; x1o = x1n; // old = current positions y1o = y1n; x2o = x2n; y2o = y2n; x3o = x3n; y3o = y3n; } else { WarpF_invert(WarpF_coeff,WarpF_Icoeff); // get inverse coefficients a = WarpF_Icoeff[0]; b = WarpF_Icoeff[1]; c = WarpF_Icoeff[2]; d = WarpF_Icoeff[3]; e = WarpF_Icoeff[4]; f = WarpF_Icoeff[5]; x1o = a * x1n + b * y1n + c; // compute old from current positions y1o = d * x1n + e * y1n + f; x2o = a * x2n + b * y2n + c; y2o = d * x2n + e * y2n + f; x3o = a * x3n + b * y3n + c; y3o = d * x3n + e * y3n + f; } WarpF_old[0][0] = x1o; // set up 3 old points and corresponding WarpF_old[0][1] = y1o; // new points for affine translation WarpF_old[1][0] = x2o; WarpF_old[1][1] = y2o; WarpF_old[2][0] = x3o; WarpF_old[2][1] = y3o; x1n = mdx2; // point 1 new position = drag position y1n = mdy2; x2n = E3pxm->ww - x1n; // point 2 new = mirror of point1 new y2n = E3pxm->hh - y1n; WarpF_new[0][0] = x1n; // 3 new points WarpF_new[0][1] = y1n; WarpF_new[1][0] = x2n; WarpF_new[1][1] = y2n; WarpF_new[2][0] = x3n; WarpF_new[2][1] = y3n; WarpF_warpfunc(); // do the warp Mxdrag = Mydrag = 0; return; } // warp image and accumulate warp memory void WarpF_warpfunc() { void * WarpF_wthread(void *); WarpF_affine(WarpF_old, WarpF_new, WarpF_coeff); // get coefficients for forward transform WarpF_invert(WarpF_coeff, WarpF_Icoeff); // get coefficients for reverse transform for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(WarpF_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window return; } // working thread to process the pixels void * WarpF_wthread(void *arg) { int index = *((int *) arg); float a, b, c, d, e, f; int px3, py3, vstat; float px1, py1; float vpix1[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); a = WarpF_Icoeff[0]; // coefficients to map output pixels b = WarpF_Icoeff[1]; // to corresponding input pixels c = WarpF_Icoeff[2]; d = WarpF_Icoeff[3]; e = WarpF_Icoeff[4]; f = WarpF_Icoeff[5]; for (py3 = index; py3 < E3pxm->hh; py3 += NWT) // process all pixels for (px3 = 0; px3 < E3pxm->ww; px3++) { px1 = a * px3 + b * py3 + c; // corresponding input pixel py1 = d * px3 + e * py3 + f; vstat = vpixel(E1pxm,px1,py1,vpix1); // input virtual pixel pix3 = PXMpix(E3pxm,px3,py3); // output pixel if (vstat) memcpy(pix3,vpix1,pcc); else memset(pix3,0,pcc); // voided pixel } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } /******************************************************************************** Compute affine transformation of an image (warp image). Given 3 new (warped) positions for 3 image points, derive the coefficients of the translation function to warp the entire image. Inputs: pold[3][2] (x,y) coordinates for 3 points in original image pnew[3][2] (x,y) coordinates for same points in warped image Output: coeff[6] coefficients of translation function which can be used to convert all image points to their warped positions If coeff[6] = (a, b, c, d, e, f) then the following formula can be used to convert an image point to its warped position: Xnew = a * Xold + b * Yold + c Ynew = d * Xold + e * Yold + f *********************************************************************************/ void WarpF_affine(float pold[3][2], float pnew[3][2], float coeff[6]) { float x11, y11, x12, y12, x13, y13; // original points float x21, y21, x22, y22, x23, y23; // moved points float a, b, c, d, e, f; // coefficients float A1, A2, B1, B2, C1, C2; x11 = pold[0][0]; y11 = pold[0][1]; x12 = pold[1][0]; y12 = pold[1][1]; x13 = pold[2][0]; y13 = pold[2][1]; x21 = pnew[0][0]; y21 = pnew[0][1]; x22 = pnew[1][0]; y22 = pnew[1][1]; x23 = pnew[2][0]; y23 = pnew[2][1]; A1 = x11 - x12; A2 = x12 - x13; B1 = y11 - y12; B2 = y12 - y13; C1 = x21 - x22; C2 = x22 - x23; a = (B1 * C2 - B2 * C1) / (A2 * B1 - A1 * B2); b = (A1 * C2 - A2 * C1) / (A1 * B2 - A2 * B1); c = x23 - a * x13 - b * y13; C1 = y21 - y22; C2 = y22 - y23; d = (B1 * C2 - B2 * C1) / (A2 * B1 - A1 * B2); e = (A1 * C2 - A2 * C1) / (A1 * B2 - A2 * B1); f = y23 - d * x13 - e * y13; coeff[0] = a; coeff[1] = b; coeff[2] = c; coeff[3] = d; coeff[4] = e; coeff[5] = f; return; } /******************************************************************************** Invert affine transform Input: coeff[6] coefficients of translation function to convert image points to their warped positions Output: Icoeff[6] coefficients of translation function to convert warped image points to their original positions If Icoeff[6] = (a, b, c, d, e, f) then the following formula can be used to translate a warped image point to its original position: Xold = a * Xnew + b * Ynew + c Yold = d * Xnew + e * Ynew + f *********************************************************************************/ void WarpF_invert(float coeff[6], float Icoeff[6]) { float a, b, c, d, e, f, Z; a = coeff[0]; b = coeff[1]; c = coeff[2]; d = coeff[3]; e = coeff[4]; f = coeff[5]; Z = 1.0 / (a * e - b * d); Icoeff[0] = e * Z; Icoeff[1] = - b * Z; Icoeff[2] = Z * (b * f - c * e); Icoeff[3] = - d * Z; Icoeff[4] = a * Z; Icoeff[5] = Z * (c * d - a * f); return; } /********************************************************************************/ // Flatten a photographed book page. // Compensate for page curvature at the center binding. editfunc EFflatbook; // edit function data namespace flatbook { int Tmx[20], Tmy[20], Bmx[20], Bmy[20]; // top/bottom mouse click points int Tnm = 0, Bnm = 0; // top/bottom click points counts int Tbase, Bbase; // top/bottom points, low values int ww, hh; // image dimensions int E3warped; // flag, E3 image is warped double *Tfy, *Bfy; // derived top/bottom y-shifts [ww] double *Tfx, *Bfx; // derived top/bottom x-shifts [ww] double Tstretch, Bstretch; // top/bottom x-shift stretch factors } // menu function void m_flatbook(GtkWidget *, const char *) { using namespace flatbook; int flatbook_dialog_event(zdialog* zd, const char *event); void * flatbook_thread(void *); void flatbook_mousefunc(); void flatbook_draw(); cchar *title = ZTX("Flatten Book Page"); cchar *guide = ZTX("Trim image to isolate one page. \n" "Map top and bottom edges with \n" "4+ mouse clicks, then flatten: "); cchar *stretch = ZTX("Stretch curved-down surfaces:"); zdialog *zd; F1_help_topic = "flatten_book"; EFflatbook.menufunc = m_flatbook; EFflatbook.funcname = "flatbook"; // func name, no preview, no area EFflatbook.threadfunc = flatbook_thread; EFflatbook.mousefunc = flatbook_mousefunc; if (! edit_setup(EFflatbook)) return; // setup edit int cc = E1pxm->ww * sizeof(double); // allocate memory Tfy = (double *) zmalloc(cc); Bfy = (double *) zmalloc(cc); Tfx = (double *) zmalloc(cc); Bfx = (double *) zmalloc(cc); if (ww != E1pxm->ww) Tnm = Bnm = 0; // clear prior points if image if (hh != E1pxm->hh) Tnm = Bnm = 0; // size is different ww = E1pxm->ww; // set image size hh = E1pxm->hh; E3warped = 0; // no E3 warp yet /*** _________________________________ | Flatten Book Page | | | | Trim image to isolate one page. | | Map top and bottom edges with | | 4+ mouse clicks, then flatten: | | [clear] [flatten] [undo] | | | | Stretch curved-down surfaces: | | Top: ========[]============== | | Bottom: =========[]========== | | | | [done] [cancel] | |_________________________________| ***/ zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); // flatbook dialog EFflatbook.zd = zd; zdialog_add_widget(zd,"hbox","hbg","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labg","hbg",guide,"space=8"); zdialog_add_widget(zd,"hbox","hbf","dialog"); zdialog_add_widget(zd,"button","clear","hbf",Bclear,"space=10"); zdialog_add_widget(zd,"button","flatten","hbf",Bflatten,"space=10"); zdialog_add_widget(zd,"button","undo","hbf",Bundo,"space=10"); zdialog_add_widget(zd,"hbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbs1","dialog"); zdialog_add_widget(zd,"label","labs1","hbs1",stretch,"space=8"); zdialog_add_widget(zd,"hbox","hbs2","dialog"); zdialog_add_widget(zd,"label","labs2","hbs2",ZTX("Top:"),"space=8"); zdialog_add_widget(zd,"hscale","top","hbs2","1|30|0.01|1","expand|space=5"); zdialog_add_widget(zd,"hbox","hbs3","dialog"); zdialog_add_widget(zd,"label","labs3","hbs3",ZTX("Bottom:"),"space=8"); zdialog_add_widget(zd,"hscale","bottom","hbs3","1|30|0.01|1","expand|space=5"); zdialog_restore_inputs(zd); // restore prior inputs zdialog_resize(zd,300,0); zdialog_run(zd,flatbook_dialog_event,"save"); // run dialog - parallel takeMouse(flatbook_mousefunc,dragcursor); // connect mouse function if (Tnm + Bnm > 0) flatbook_draw(); return; } // flatbook dialog event and completion function int flatbook_dialog_event(zdialog *zd, const char *event) // flatbook dialog event function { using namespace flatbook; void flatbook_draw(); if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { zfree(Tfy); // free memory zfree(Bfy); zfree(Tfx); zfree(Bfx); if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // cancel or destroy return 1; } if (strmatch(event,"clear")) { // clear all mouse points Tnm = Bnm = 0; edit_undo(); flatbook_draw(); } if (strstr("flatten top bottom",event)) { if (Tnm < 4 || Bnm < 4) return 1; zdialog_fetch(zd,"top",Tstretch); zdialog_fetch(zd,"bottom",Bstretch); signal_thread(); // trigger update thread } if (strmatch(event,"undo")) { edit_undo(); E3warped = 0; flatbook_draw(); } if (strmatch(event,"line_color")) flatbook_draw(); // refresh lines after window redraw return 1; } // flatbook mouse function void flatbook_mousefunc() { using namespace flatbook; void flatbook_draw(); int ii, jj, mx, my, dist; int Fadd = 0, close; if (! (LMclick || RMclick || Mxdrag || Mydrag)) return; // ignore mouse movement if (E3warped) return; // E3 image warped, ignore mouse if (LMclick || RMclick) { // left or right mouse click mx = Mxclick; my = Myclick; if (LMclick) Fadd = 1; LMclick = RMclick = 0; } else { // mouse drag mx = Mxdrag; my = Mydrag; Mxdrag = Mydrag = 0; Fadd = 1; } close = 0.01 * ww; // 1% of image size if (my < hh/2) { for (ii = 0; ii < Tnm; ii++) { // compare mouse position with dist = abs(mx-Tmx[ii]); // existing top points if (dist < close) { Tnm--; // delete any too close for (jj = ii--; jj < Tnm; jj++) { Tmx[jj] = Tmx[jj+1]; Tmy[jj] = Tmy[jj+1]; } } } } else { for (ii = 0; ii < Bnm; ii++) { // same for bottom points dist = abs(mx-Bmx[ii]); if (dist < close) { Bnm--; for (jj = ii--; jj < Bnm; jj++) { Bmx[jj] = Bmx[jj+1]; Bmy[jj] = Bmy[jj+1]; } } } } if (Fadd) // add new mouse position { if (my < hh/2) { // add to top points if (Tnm == 20) return; for (ii = 0; ii < Tnm; ii++) if (mx < Tmx[ii]) break; for (jj = Tnm; jj > ii; jj--) { Tmx[jj] = Tmx[jj-1]; Tmy[jj] = Tmy[jj-1]; } Tmx[ii] = mx; Tmy[ii] = my; Tnm++; } else { // add to bottom points if (Bnm == 20) return; for (ii = 0; ii < Bnm; ii++) if (mx < Bmx[ii]) break; for (jj = Bnm; jj > ii; jj--) { Bmx[jj] = Bmx[jj-1]; Bmy[jj] = Bmy[jj-1]; } Bmx[ii] = mx; Bmy[ii] = my; Bnm++; } } edit_undo(); // erase and redraw flatbook_draw(); return; } // generate spline curves from mouse points and draw on the image void flatbook_draw() { using namespace flatbook; float Tnx[20], Tny[20], Bnx[20], Bny[20]; // top/bottom spline nodes float *ppix3; int topmax, topmin, bottmax, bottmin; // limits of image update areas int ii, px, py, qx, qy, npq; topmax = topmin = bottmax = bottmin = 0; npq = 3; // pixel block for drawing nodes if (Tnm > 0) topmax = topmin = Tmy[0]; for (ii = 0; ii < Tnm; ii++) // draw top mouse points { px = Tmx[ii]; py = Tmy[ii]; for (qy = py-npq; qy <= py+npq; qy++) for (qx = px-npq; qx <= px+npq; qx++) { if (qx < 0 || qx > ww-1) continue; if (qy < 0 || qy > hh-1) continue; ppix3 = PXMpix(E3pxm,qx,qy); ppix3[0] = LINE_COLOR[0]; ppix3[1] = LINE_COLOR[1]; ppix3[2] = LINE_COLOR[2]; if (qy > topmax) topmax = qy; if (qy < topmin) topmin = qy; } } if (Bnm > 0) bottmax = bottmin = Bmy[0]; for (ii = 0; ii < Bnm; ii++) // draw bottom mouse points { px = Bmx[ii]; py = Bmy[ii]; for (qy = py-npq; qy <= py+npq; qy++) for (qx = px-npq; qx <= px+npq; qx++) { if (qx < 0 || qx > ww-1) continue; if (qy < 0 || qy > hh-1) continue; ppix3 = PXMpix(E3pxm,qx,qy); ppix3[0] = LINE_COLOR[0]; ppix3[1] = LINE_COLOR[1]; ppix3[2] = LINE_COLOR[2]; if (qy > bottmax) bottmax = qy; if (qy < bottmin) bottmin = qy; } } if (Tnm > 3) // top mouse points { Tbase = Tmy[0]; // find lowest top point for (ii = 0; ii < Tnm; ii++) if (Tmy[ii] < Tbase) Tbase = Tmy[ii]; for (ii = 0; ii < Tnm; ii++) { // copy top points to spline nodes Tnx[ii] = Tmx[ii]; // and convert to 0 base Tny[ii] = Tmy[ii] - Tbase; } spline1(Tnm,Tnx,Tny); // generate spline curve for (px = 0; px < ww; px++) // generate top curve y shifts Tfy[px] = spline2(px); for (px = 0; px < ww; px++) { // draw top curve over image py = Tfy[px] + Tbase; if (py < 0 || py > hh-1) continue; ppix3 = PXMpix(E3pxm,px,py); ppix3[0] = LINE_COLOR[0]; ppix3[1] = LINE_COLOR[1]; ppix3[2] = LINE_COLOR[2]; if (py < topmin) topmin = py; if (py > topmax) topmax = py; } } if (Bnm > 3) // bottom mouse points { Bbase = Bmy[0]; // find lowest bottom point for (ii = 0; ii < Bnm; ii++) if (Bmy[ii] < Bbase) Bbase = Bmy[ii]; for (ii = 0; ii < Bnm; ii++) { // copy bottom points to spline nodes Bnx[ii] = Bmx[ii]; // and convert to 0 base Bny[ii] = Bmy[ii] - Bbase; } spline1(Bnm,Bnx,Bny); // generate spline curve for (px = 0; px < ww; px++) // generate bottom curve y shifts Bfy[px] = spline2(px); for (px = 0; px < ww; px++) { // draw bottom curve over image py = Bfy[px] + Bbase; if (py < 0 || py > hh-1) continue; ppix3 = PXMpix(E3pxm,px,py); ppix3[0] = LINE_COLOR[0]; ppix3[1] = LINE_COLOR[1]; ppix3[2] = LINE_COLOR[2]; if (py < bottmin) bottmin = py; if (py > bottmax) bottmax = py; } } if (Tnm) Fpaint3(0,topmin,ww,topmax-topmin,0); // paint modified areas if (Bnm) Fpaint3(0,bottmin,ww,bottmax-bottmin,0); if (Tnm + Bnm) CEF->Fmods++; // necessary CEF->Fsaved = 0; return; } // flatbook thread function - warp image based on input parameters void * flatbook_thread(void *) { using namespace flatbook; void * flatbook_wthread(void *arg); // worker thread int px; double Tslope, Bslope, slope, Rt, Rb; while (true) { thread_idle_loop(); E3warped = 1; // flag, E3 image is warped Tfx[0] = Bfx[0] = 0; for (px = 1; px < ww; px++) // loop page width { Tslope = fabs(Tfy[px] - Tfy[px-1]); // slope of top function Bslope = fabs(Bfy[px] - Bfy[px-1]); // slope of bottom function slope = Tslope * Tstretch; // mean slope, stretched slope = cos(atan(slope)); Tfx[px] = Tfx[px-1] + slope; // resulting pixel x-shift slope = Bslope * Bstretch; slope = cos(atan(slope)); Bfx[px] = Bfx[px-1] + slope; } Rt = (ww-1) / Tfx[ww-1]; // make overall width the same Rb = (ww-1) / Bfx[ww-1]; for (px = 0; px < ww; px++) { Tfx[px] = Rt * Tfx[px]; // (using separate loops causes Bfx[px] = Rb * Bfx[px]; // GCC optimization bug) } for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(flatbook_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image is modified CEF->Fsaved = 0; Fpaint2(); // update window } return 0; } void * flatbook_wthread(void *arg) { using namespace flatbook; int index = *((int *) (arg)); int px, py, vstat; double Rt, Rb; float *pix3, vpix[4], vpx, vpy; int nc = E1pxm->nc, pcc = nc * sizeof(float); for (py = index; py < hh; py += NWT) // loop from top to bottom { Rt = 1.0 - 1.0 * py / hh; // Rt from 1 to 0 - top weight Rb = 1.0 - Rt; // Rb from 0 to 1 - bottom weight for (px = 0; px < ww; px++) // loop page width { pix3 = PXMpix(E3pxm,px,py); // output pixel vpx = Rt * Tfx[px] + Rb * Bfx[px]; // source pixel for (px,py) vpy = py + Rt * Tfy[int(vpx)] + Rb * Bfy[int(vpx)]; vstat = vpixel(E1pxm,vpx,vpy,vpix); // get source pixel if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); // off page, void pixel } } exit_wthread(); return 0; } /********************************************************************************/ // project image on to a sphere with adjustable radius (flatness) namespace sphere_names { int E3ww, E3hh; // image dimensions int Xcen, Ycen, Dia; // center and diameter of sphere float flatten = 0; // flatten parameter float magnify = 1; // magnify parameter editfunc EFsphere; // edit function data } // menu function void m_sphere(GtkWidget *, const char *) { using namespace sphere_names; int sphere_dialog_event(zdialog* zd, const char *event); void sphere_mousefunc(void); void * sphere_thread(void *); cchar *title = ZTX("Spherical Projection"); F1_help_topic = "sphere"; EFsphere.menufunc = m_sphere; EFsphere.funcname = "sphere"; // function name EFsphere.FprevReq = 1; // use preview edit mode EFsphere.mousefunc = sphere_mousefunc; // mouse function EFsphere.threadfunc = sphere_thread; // thread function if (! edit_setup(EFsphere)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); E3ww = E3pxm->ww; E3hh = E3pxm->hh; Xcen = E3ww / 2; // set dialog defaults Ycen = E3hh / 2; Dia = E3ww; if (E3hh < E3ww) Dia = E3hh; flatten = 0; magnify = 1; /*** _____________________________ | Spherical Projection | | | | Flatten ======[]========== | | Magnify ==========[]====== | | | | [done] [cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); // sphere dialog CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbflat","dialog"); zdialog_add_widget(zd,"label","labflat","hbflat",Bflatten,"space=5"); zdialog_add_widget(zd,"hscale","flatten","hbflat","0.0|0.999|0.001|0.0","expand"); zdialog_add_widget(zd,"hbox","hbmag","dialog"); zdialog_add_widget(zd,"label","labmag","hbmag",ZTX("Magnify"),"space=5"); zdialog_add_widget(zd,"hscale","magnify","hbmag","1.0|2.0|0.001|1.0","expand"); zdialog_resize(zd,250,0); zdialog_run(zd,sphere_dialog_event,"save"); // run dialog - parallel takeMouse(sphere_mousefunc,0); // connect mouse function signal_thread(); // trigger update thread return; } // sphere dialog event and completion function int sphere_dialog_event(zdialog *zd, const char *event) { using namespace sphere_names; float scale; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) // done { edit_fullsize(); // get full size image scale = 1.0 * (E3pxm->ww + E3pxm->hh) / (E3ww + E3hh); // scale up the parameters E3ww = E3pxm->ww; E3hh = E3pxm->hh; Xcen = scale * Xcen; Ycen = scale * Ycen; Dia = scale * Dia; signal_thread(); // recalculate image edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"flatten")) { zdialog_fetch(zd,"flatten",flatten); signal_thread(); // trigger update thread } if (strmatch(event,"magnify")) { zdialog_fetch(zd,"magnify",magnify); signal_thread(); // trigger update thread } return 1; } // mouse function - get new center from mouse click or drag void sphere_mousefunc(void) { using namespace sphere_names; int edist; if (! LMclick && ! Mdrag) return; LMclick = 0; if (Mxposn < 0.1 * E3ww || Mxposn > 0.9 * E3ww) return; // ignore if near image edge if (Myposn < 0.1 * E3hh || Myposn > 0.9 * E3hh) return; Xcen = Mxposn; // new center Ycen = Myposn; edist = Xcen; // find nearest edge distance if (Ycen < edist) edist = Ycen; if (E3ww - Xcen < edist) edist = E3ww - Xcen; if (E3hh - Ycen < edist) edist = E3hh - Ycen; Dia = 2 * edist; // new sphere diameter signal_thread(); return; } // thread function - multiple working threads to update image void * sphere_thread(void *) { void * sphere_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(sphere_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved Fpaint2(); // update window } return 0; // not executed, stop warning } void * sphere_wthread(void *arg) // worker thread function { using namespace sphere_names; int index = *((int *) (arg)); int px3, py3, dx, dy, vstat; float px1, py1, *pix3, vpix[4]; float s1, s2, D, T; int nc = E1pxm->nc, pcc = nc * sizeof(float); D = Dia / (1.0 - flatten); for (py3 = index; py3 < E3hh; py3 += NWT) // loop output pixels for (px3 = 0; px3 < E3ww; px3++) { pix3 = PXMpix(E3pxm,px3,py3); // output pixel dx = px3 - Xcen; dy = py3 - Ycen; s1 = sqrtf(dx*dx + dy*dy); // dist. from center to output pixel T = s1 * PI / D / magnify; // sine of subtended angle if (T > 1.0) { memset(pix3,0,pcc); // pixel = black, alpha = 0 continue; } s2 = D / PI * asinf(T); // corresp. dist. on sphere px1 = Xcen + dx * s2 / s1; // input v.pixel py1 = Ycen + dy * s2 / s1; vstat = vpixel(E1pxm,px1,py1,vpix); if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); // pixel = black, alpha = 0 } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Rescale an image while leaving selected areas unchanged. namespace SLrescale_names { editfunc EFslrescale; int Fsetups = 0; int dragx, dragy; int E3ww, E3hh; char *sqrow, *sqcol; int Nsqrow, Nsqcol; int *npx, *npy; int dialog_event(zdialog *zd, cchar *event); void setups(); void cleanups(); void mousefunc(); void warpfunc(); void *warpthread(void *); } // menu function void m_selective_rescale(GtkWidget *, cchar *) { using namespace SLrescale_names; cchar *message = ZTX(" Select areas to remain unchanged. \n" " Pull image from upper left corner. \n" " When finished, press [done]."); F1_help_topic = "selective_rescale"; EFslrescale.menufunc = m_selective_rescale; EFslrescale.funcname = "selective_rescale"; EFslrescale.Farea = 2; // select area usable EFslrescale.mousefunc = mousefunc; // mouse function if (! edit_setup(EFslrescale)) return; // setup edit PXM_addalpha(E0pxm); PXM_addalpha(E1pxm); PXM_addalpha(E3pxm); zdialog *zd = zdialog_new(ZTX("Selective Rescale"),Mwin,Bproceed,Bdone,Bcancel,null); EFslrescale.zd = zd; zdialog_add_widget(zd,"label","lab1","dialog",message,"space=3"); zdialog_run(zd,dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int SLrescale_names::dialog_event(zdialog * zd, cchar *event) { using namespace SLrescale_names; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (! zd->zstat) return 1; // wait for completion if (zd->zstat == 1) { // [proceed] zd->zstat = 0; // keep dialog active if (sa_stat != 3) zmessageACK(Mwin,ZTX("select areas first")); else setups(); // start drag and warp return 1; } if (zd->zstat != 2 || dragx + dragy == 0) { // [cancel] or no change edit_cancel(0); cleanups(); return 1; } edit_done(0); // [done] cleanups(); m_trim_rotate(0,"auto"); // set up automatic trim return 1; } // do setups based on select area data void SLrescale_names::setups() { int ii, spx, spy, sum; cleanups(); // free prior if any dragx = dragy = 0; // no drag data E3ww = E3pxm->ww; // image dimensions E3hh = E3pxm->hh; sqrow = (char *) zmalloc(E3hh); // maps squishable rows/cols sqcol = (char *) zmalloc(E3ww); memset(sqrow,1,E3hh); // mark all rows/cols squishable memset(sqcol,1,E3ww); for (spy = 0; spy < E3hh; spy++) // loop all source pixels for (spx = 0; spx < E3ww; spx++) { ii = spy * E3ww + spx; // pixel within area? if (sa_pixmap[ii]) sqrow[spy] = sqcol[spx] = 0; // mark row/col non-squishable } Nsqrow = Nsqcol = 0; for (spy = 0; spy < E3hh; spy++) // count total squishable rows/cols Nsqrow += sqrow[spy]; for (spx = 0; spx < E3ww; spx++) Nsqcol += sqcol[spx]; npx = (int *) zmalloc(E3ww * sizeof(int)); // count of squishable rows/cols npy = (int *) zmalloc(E3hh * sizeof(int)); // predeeding a given row/col for (sum = spx = 0; spx < E3ww; spx++) { if (sqcol[spx]) sum++; npx[spx] = sum; } for (sum = spy = 0; spy < E3hh; spy++) { if (sqrow[spy]) sum++; npy[spy] = sum; // squishable rows < spy } Fsetups = 1; sa_unselect(); // delete area takeMouse(mousefunc,dragcursor); // connect mouse function return; } // free allocated memory void SLrescale_names::cleanups() { if (! Fsetups) return; Fsetups = 0; zfree(sqrow); zfree(sqcol); zfree(npx); zfree(npy); return; } // mouse function void SLrescale_names::mousefunc() { using namespace SLrescale_names; float R; if (Mxdrag || Mydrag) // mouse drag underway { R = 1.0 * Mxdown / E3ww; // ignore drag not from NW corner if (R > 0.2) return; R = 1.0 * Mydown / E3hh; if (R > 0.2) return; dragx = Mxdrag - Mxdown; // drag amount dragy = Mydrag - Mydown; warpfunc(); // drag image Mxdrag = Mydrag = 0; } return; } // warp function void SLrescale_names::warpfunc() { using namespace SLrescale_names; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(warpthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window return; } // warp thread void * SLrescale_names::warpthread(void *arg) { using namespace SLrescale_names; int index = *((int *) (arg)); int spx, spy, dpx, dpy; float Rx, Ry; float *spix, *dpix; int nc = E1pxm->nc, pcc = nc * sizeof(float); for (spy = index; spy < E3hh; spy += NWT) // loop all source pixels for (spx = 0; spx < E3ww; spx++) { if (spx < dragx || spy < dragy) { // pixels < dragx/dragy: spix = PXMpix(E3pxm,spx,spy); // black, transparent memset(spix,0,pcc); } Rx = 1.0 * npx[spx] / Nsqcol; // squishable pixel ratios, 0 - 1.0 Ry = 1.0 * npy[spy] / Nsqrow; dpx = spx + dragx * (1.0 - Rx); // destination pixel dpy = spy + dragy * (1.0 - Ry); if (dpx < 0 || dpx > E3ww-1) continue; // necessary, why? if (dpy < 0 || dpy > E3hh-1) continue; dpix = PXMpix(E3pxm,dpx,dpy); // source pixel >> destination pixel spix = PXMpix(E1pxm,spx,spy); memcpy(dpix,spix,pcc); } exit_wthread(); return 0; } /********************************************************************************/ // make waves menu function // distort the image with a wave pattern // independent horizontal and vertical wavelengths // variance option: make waves more or less irregular // perspective option: wavelengths lengthen going down namespace waves_names { editfunc EFwaves; int ww, hh; // image dimensions int WLV, WLH; // vertical and horizontal wavelengths int AMPV, AMPH; // vertical and horizontal amplitudes int VARV, VARH; // vertical and horizontal variance int PERSP; // perspective 0-100 } // menu function void m_waves(GtkWidget *, const char *) { using namespace waves_names; int waves_dialog_event(zdialog* zd, const char *event); void * waves_thread(void *); F1_help_topic = "make_waves"; EFwaves.menufunc = m_waves; EFwaves.funcname = "make_waves"; EFwaves.Farea = 2; // select area usable EFwaves.threadfunc = waves_thread; if (! edit_setup(EFwaves)) return; /*** ___________________________________ | Make waves | | | | Horizontal Vertical | | wavelength [___] [___] | | amplitude [___] [___] | | variance [___] [___] | | | | perspective [___] | | | | [apply] [done] [cancel] | |___________________________________| ***/ ww = E3pxm->ww; hh = E3pxm->hh; zdialog *zd = zdialog_new(ZTX("Make Waves"),Mwin,Bapply,Bdone,Bcancel,null); EFwaves.zd = zd; zdialog_add_widget(zd,"hbox","hbw","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vbw1","hbw",0,"space=3"); zdialog_add_widget(zd,"vbox","vbw2","hbw",0,"space=3"); zdialog_add_widget(zd,"vbox","vbw3","hbw",0,"space=3"); zdialog_add_widget(zd,"label","space","vbw1"," ","space=1"); zdialog_add_widget(zd,"label","labwl","vbw1",ZTX("wavelength"),"expand"); zdialog_add_widget(zd,"label","labamp","vbw1",ZTX("amplitude"),"expand"); zdialog_add_widget(zd,"label","labamp","vbw1",ZTX("variance"),"expand"); zdialog_add_widget(zd,"label","labh","vbw2",ZTX("horizontal"),"space=1"); zdialog_add_widget(zd,"zspin","wlh","vbw2","3|500|1|50","expand"); zdialog_add_widget(zd,"zspin","amph","vbw2","0|100|1|20","expand"); zdialog_add_widget(zd,"zspin","varh","vbw2","0|100|1|20","expand"); zdialog_add_widget(zd,"label","labh","vbw3",ZTX("vertical"),"space=1"); zdialog_add_widget(zd,"zspin","wlv","vbw3","3|500|1|50","expand"); zdialog_add_widget(zd,"zspin","ampv","vbw3","0|100|1|20","expand"); zdialog_add_widget(zd,"zspin","varv","vbw3","0|100|1|20","expand"); zdialog_add_widget(zd,"hsep","sepp","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbp","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labp","hbp",ZTX("perspective"),"space=3"); zdialog_add_widget(zd,"zspin","persp","hbp","0|100|1|0","space=5"); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,waves_dialog_event,"save"); // run dialog - parallel return; } // waves dialog event and completion function int waves_dialog_event(zdialog *zd, const char *event) // waves dialog event function { using namespace waves_names; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active edit_reset(); zdialog_fetch(zd,"wlv",WLV); // get user inputs zdialog_fetch(zd,"wlh",WLH); zdialog_fetch(zd,"ampv",AMPV); zdialog_fetch(zd,"amph",AMPH); zdialog_fetch(zd,"varv",VARV); zdialog_fetch(zd,"varh",VARH); zdialog_fetch(zd,"persp",PERSP); signal_thread(); // calculate return 1; } if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit } return 1; } // thread function - multiple working threads to update image void * waves_thread(void *) { using namespace waves_names; void * waves_wthread(void *); while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(waves_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved Fpaint2(); // update window } return 0; // not executed, stop warning } void * waves_wthread(void *arg) // working threads { using namespace waves_names; int index = *((int *) (arg)); float wlv, wlh, ampv, amph, varv, varh, persp; int py, px, pylo, pyhi; int ii, edist = 0; float dx, dy, *pix1, *pix3, vpix[4]; float f1, f2, ffv, ffh, red, green, blue; wlv = WLV; // vertical and horizontal wavelengths wlh = WLH; ampv = 0.01 * AMPV * wlv; // vertical and horizoneal amplitudes amph = 0.01 * AMPH * wlh; varv = 0.01 * VARV; // vertical and horizontal variance varh = 0.01 * VARH; persp = 0.01 * PERSP; // perspective 0 to 1.0 pylo = 0; // py range pyhi = hh; if (sa_stat == 3) { pylo = sa_miny - wlv; // rescale to select area height if (pylo < 0) pylo = 0; pyhi = sa_maxy + wlv; if (pyhi > hh) pyhi = hh; } for (py = pylo + index; py < pyhi; py += NWT) { if (persp == 0) ffv = ffh = 1.0; else { ffv = 2.0 * persp * (py - pylo) / (pyhi - pylo); // 0 ... 2.0 ffv = 1.0 + ffv * ffv; // 1 ... 5.0 ffh = sqrtf(ffv); // 1 ... 2.23 } dx = ampv * sin(2.0 * PI * py / (ffv * wlv)); if (varv > 0) dx += ampv * varv * sin(2.0 * PI * py / (1.37 * ffv * wlv)); // make irregular wave for (px = 0; px < ww; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; edist = sa_pixmap[ii]; // distance from edge if (! edist) continue; // pixel outside area } dy = amph * sin(2.0 * PI * px / (ffh * wlh)); if (varh > 0) dy += amph * varh * sin(2.0 * PI * px / (1.37 * ffh * wlh)); // make irregular wave vpixel(E1pxm,px+dx,py+dy,vpix); // source pixel, displaced red = vpix[0]; green = vpix[1]; blue = vpix[2]; if (sa_stat == 3 && edist < sa_blend) { // blend edges f1 = sa_blendfunc(edist); f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); red = f1 * red + f2 * pix1[0]; green = f1 * green + f2 * pix1[1]; blue = f1 * blue + f2 * pix1[2]; } pix3 = PXMpix(E3pxm,px,py); // destination pixel pix3[0] = red; pix3[1] = green; pix3[2] = blue; } } exit_wthread(); // exit thread return 0; // not executed, stop gcc warning } /********************************************************************************/ // Twist the image with max. rotation at mouse center and none at farthest edge namespace twist_names { editfunc EFtwist; int twist_cx, twist_cy; float twist_twist; float twist_center; float twist_angle; } // menu function void m_twist(GtkWidget *, cchar *) // 17.04 { using namespace twist_names; int twist_dialog_event(zdialog *zd, cchar *event); void * twist_thread(void *); void twist_mousefunc(); F1_help_topic = "twist"; cchar *title = ZTX("Twist"); EFtwist.menufunc = m_twist; EFtwist.funcname = "twist"; EFtwist.Farea = 2; // select area usable EFtwist.FprevReq = 1; // use preview image EFtwist.threadfunc = twist_thread; // thread function EFtwist.mousefunc = twist_mousefunc; // mouse function if (! edit_setup(EFtwist)) return; // setup edit /*** _________________________________________ | Twist Image | | | | drag mouse to set center | | | | twist ==============[]============== | | center ==========[]================== | | angle ======[]====================== | | | | [Reset] [Done] [Cancel] | |_________________________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Breset,Bdone,Bcancel,null); EFtwist.zd = zd; zdialog_add_widget(zd,"label","labtip","dialog",ZTX("drag mouse to set center")); zdialog_add_widget(zd,"hbox","hba","dialog",0,"space=3"); zdialog_add_widget(zd,"label","laba","hba",ZTX("Twist"),"space=3"); zdialog_add_widget(zd,"hscale","twist","hba","-1.0|+1.0|0.01|0.0","space=5|expand"); zdialog_add_widget(zd,"hbox","hbp","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labp","hbp",Bcenter,"space=3"); zdialog_add_widget(zd,"hscale","center","hbp","1.0|10.0|0.1|1.0","space=5|expand"); zdialog_add_widget(zd,"hbox","hbc","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labc","hbc",Bangle,"space=3"); zdialog_add_widget(zd,"hscale","angle","hbc","-4.0|4.0|0.01|0.0","space=5|expand"); twist_cx = E3pxm->ww / 2; // initial data twist_cy = E3pxm->hh / 2; twist_twist = 0.0; twist_center = 1.0; twist_angle = 0.0; zdialog_resize(zd,300,0); zdialog_run(zd,twist_dialog_event,"save"); // run dialog - parallel takeMouse(twist_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int twist_dialog_event(zdialog *zd, cchar *event) { using namespace twist_names; void twist_mousefunc(); if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // reset zd->zstat = 0; twist_twist = 0.0; twist_center = 1.0; twist_angle = 0.0; zdialog_stuff(zd,"twist",0.0); zdialog_stuff(zd,"center",1.0); zdialog_stuff(zd,"angle",0.0); signal_thread(); } else if (zd->zstat == 2) { // done wait_thread_idle(); // insure thread done float R = 1.0 * E0pxm->ww / E3pxm->ww; twist_cx = R * twist_cx; // scale geometries to full size twist_cy = R * twist_cy; edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(twist_mousefunc,dragcursor); // connect mouse function if (strmatch(event,"blendwidth")) signal_thread(); if (strmatch(event,"twist")) { zdialog_fetch(zd,"twist",twist_twist); signal_thread(); } if (strmatch(event,"center")) { zdialog_fetch(zd,"center",twist_center); signal_thread(); } if (strmatch(event,"angle")) { zdialog_fetch(zd,"angle",twist_angle); signal_thread(); } return 1; } // get mouse position and set new center for twist void twist_mousefunc() { using namespace twist_names; if (! LMclick && ! Mdrag) return; twist_cx = Mxposn; // new twist center = mouse position twist_cy = Myposn; signal_thread(); // trigger image update LMclick = Mxdrag = Mydrag = 0; return; } // thread function void * twist_thread(void *) { using namespace twist_names; void * twist_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start working threads start_wthread(twist_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); } return 0; // not executed, stop g++ warning } // working thread void * twist_wthread(void *arg) { using namespace twist_names; int index, ii, vstat, dist = 0; int ww, hh, cx, cy, px, py; float qx, qy; float vpix[4], *pix3; int nc = E1pxm->nc, pcc = nc * sizeof(float); int Dx, Dy, Dnw, Dne, Dse, Dsw; float D, DN, Dmax; float T, Tp, Tq, Tmax; ww = E1pxm->ww; hh = E1pxm->hh; cx = twist_cx; // twist center (mouse) cy = twist_cy; Dnw = cx * cx + cy * cy; // get distance from center Dne = (ww-cx) * (ww-cx) + cy * cy; // to farthest corner Dse = (ww-cx) * (ww-cx) + (hh-cy) * (hh-cy); Dsw = cx * cx + (hh-cy) * (hh - cy); Dmax = Dnw; if (Dne > Dmax) Dmax = Dne; if (Dse > Dmax) Dmax = Dse; if (Dsw > Dmax) Dmax = Dsw; Dmax = sqrtf(Dmax); Tmax = 2 * PI * twist_twist; // twist angle, -360 to +360 deg. index = *((int *) arg); for (py = index; py < E3pxm->hh; py += NWT) // loop all output pixels for (px = 0; px < E3pxm->ww; px++) { ii = py * E3pxm->ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } Dx = px - cx; // px/py relative to cx/cy Dy = py - cy; D = Dx * Dx + Dy * Dy; // distance pixel to center if (D == 0) continue; D = sqrtf(D); Tp = asinf(Dy/D); // angle of pixel line to center if (Dx < 0) { if (Dy > 0) Tp = PI - Tp; else Tp = - PI - Tp; } DN = pow((D/Dmax),twist_center); // distance normalized, 0.0 ... 1.0 T = Tmax * DN; // rotation at distance from center T = T + twist_angle; Tq = Tp + T; // rotated pixel angle qx = D * cosf(Tq); // rotated pixel position qy = D * sinf(Tq); qx = qx + cx; qy = qy + cy; vstat = vpixel(E1pxm,qx,qy,vpix); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel if (vstat) memcpy(pix3,vpix,pcc); else memset(pix3,0,pcc); } exit_wthread(); return 0; // not executed, stop g++ warning } fotoxx-18.01.1/images/0000755000175000017500000000000013222767271013127 5ustar micomicofotoxx-18.01.1/images/save.png0000644000175000017500000001176413222767271014604 0ustar micomicoPNG  IHDR@@iqbKGDtIME uIDATxݛy}?{wg/iWVIۖ̕|+U8U6*N򏡰 1@R6vLGbĎqca!{f==3JZPlVwL~wzsW)?LaHcc@ e֡7;zvVQEvW eʓ9~;C 8P^vVB @]?!$DC;pB 27D; q@LMf"q1M&B4촳17Xq@"@ w8  "Q 6= "hph-JaH" /VqH gO =?A|%A?xCq@qnq һd"TK8BM#+q̩Hi5廡O8}MNPҚ{MPvksߎ-3T)(`+Z hWɦ &=g~TY _l8Ol_k Jkѩ_ ٙF8>VJNIҌ_FIK8(${[a,"q)d6N&/*rjS'^ψp:O{%? _eS,lKPX$D>n@ q>{t?}&~rJf oØ|w-|-|\/>fvVĖ%r8"#8"-s8"˓obBA ͇He2dMZif˄e O> [ou͖%KHIJ8ÿp==e70O5|ۘ+&gubY wϞoq|i:-J_r_|4AEwx #3MF3:mg GOz!0&A*Т!S>99of餬]6A!y>uNueQz-s/ZuCkʑpye5f_q^yLLNRU uH.%rԾ}Wo|?<|蒧5'Mk=88Hͭr߿ǼAJ@|Y;F!$Zk0$.K7QfXyJ_rD>F͝~,]%=A!~b2vdWHA bYrm_Z~ڹ\a<'AcN]h#0?CQ ?C/DL#BŽ E9>F`jÀ hm"YH,0L&h3%˲ʞ=<R? hƠ-w#~ ˛H1謍3uȊ"}vkL aHMP=\%$kt )\)CgJa%*ay>| .x+]T*ZX.1 {>|[n~k=q\^ E@IN:'>FH 0`hxD;3x.6.XHݭ!v楗^"reׇ D DDJT)%B*:>6|m/=`8u>?Mv˻?GR $J(GP!8B |ף^!ׇ1&UWu*N J6\.o9r!24(١@0 %FjTɯc]mMoa՗y;XVuTz!8HĜ$R ,BJ@:ajrqU笴w7'冷-_eXh|EoGJAR!dXuL~?buTH ,e;8 Ek1] Ŋ;իxPl] ecYT ۱FU&T.Q籔"3ۊֿ>̫55= B],RFrLr! RJl+K__?he8=ݽ:DHHg2H)SZb]˒S,BU&Nګ@\bPJQE\NK*^iYXs*=!e䳹ň~GV,EL˩qWJe׭Swtvv3l܎pB+pwA/}򹎄LdVR~4Z9V|1E˘>GƎ \ϣYxD򾓙Pqg.3d RU1Zb|(}}u,b(e,dxzǎ<3 ,X[I^g``MV+_rH#7,\ifg˄LOO1p( MyN$<y- ,puҩ4 }#@HyիlVfQ-Aѱ#U G86`_?9G?1wJuG~^t(@I]mK8-nK˵ Sp>m'lCG1Sfppb;1 o!q8'NЯD<‹Yt1b)+vBE㸞%I^uWo91L6tuuY@vQȈ T*ez{Y48l,:ioMĘ%]Z!k3>1mۼyZC VcAPT'Nz}=yZfRQ.ѡ^9ttBh): c6nIb&ہi~1@5AyjQbx0m٢lKV}bJ}糛wܑ{-[.|ߋ+h jb\.11z]Iyr<ŮnRy^ZNa1&vgE%-ST@`!!F( 7VuHMTժ,Y"xw}}u' k58}}SttvP((JE]E\=c_3;[Ƙ(irҙ,tt:c8N*QV@fZyžAqRr ZOznS94&733q I0qʕ8V}BfK,[:H./P*`4h'-oD:CC^֛'2`nVC;tV84r+FdN r7Ya2=3Ej^|J)NIKcX[PB`K٤ED6 A'ŏ辔IfJ3tvt219sϽEҡTAkQ{|l|p9@| `A?H%E<#GQO!ĹwINH\F;aw^Q0]BڐͤR]dtt90U Rw]z{ƌ~}"COq֬YG2KIWH9?(/ D/clHf2uw-B`3[8tr@DdzzkG^LOOGm;M9>)Ld ^7qo8 'f+k7?EiSOo@X@Raw,yll]P jiض}'ld|1Ѹ6"TI%QyM) 24YE:yTlA@k$xƲR)?@V*xhrf bZmkN i{[5>{/_@Za۶g-,J3%~JYݻ{Ŷocfm;v-[uٿo?eA!_82P V"ێ FyXùJU ohbse;E:'15^3%f Vѓ.sn,(` BHH"ԱhRYo " >z5 ݎo<IUM (@\⥗_@#<53ti׳|50 L 0lV)'Ǝ8mB,jyacΨb5! QX)\hݾDkiʳVA'/Кh01FhEu[vi&WlqM@G0LDm7u[ L8F'zDI }xC269y+5=9U: w~~-Z P!.NwǔR+J]bA)-r NM'$L\^;DZ!l“MhV2˳[@s[VJQ9Q"3#dW{]6DZo׎IU"-Z Mimgm&u`0ݯ1C;g#^ ?{4'"!E@|5z?!h~hk] [?sWr,;,4\9ֺ4\9ֺ4\9ֺ4\9ֺ F6  zTXtRaw profile type APP1xRQn0=x=U.KE2`z>*\xBA$4WECqlow6:ꅼ*#Ǜ ,ZXuIfdf䦄Gn9f4r` LiDٯV K&Uh9hz8i8x6Qx=z9%ڢgCލۙBm`D_~і`"iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-18.01.1/images/sphere2.jpg0000644000175000017500000007575013222767271015217 0ustar micomicoJFIFHHExifMM*JR(iZHH023060100Fotoxx:resize|trim_rotate| Fotoxx:resize|trim_rotate|http://ns.adobe.com/xap/1.0/ 856 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?((()h(^iN(RGzJ)hb\RPEPEPEPK)+ J&TJIJH'js@hO5wNШ_@T^zpñZfOu(bg)xnu*wڋoګ^xKt"c9Yjdqӵs.(ta&jVV7/?jA[hcĐ۹,0ss.0iJ;Ml},43R> 3kuN+мALַжF3+Vy.!#ݎOZtj'MOכw tRy4XnVI|Xgsݎ I+ɩ~7|"eZ'ZCPJ]!8MSTItk>`גP3^O;Oja #sqs޺#-xI/ˑk⮟cy%桧qã9q\-F) Oaޕ; ώ3rj\i H?O[?m_M֏¥L3gcwHX '_҇-Ol+i׳y6z#_% QU.An۬lQ8f0ۈm4oCψ&"#Ra][MҼwMOHifbA>߅xߋ<>z P@`D]֧gN1Uo N/$їr,*IaOξTm.:b(FѠ8rQHǼg`t\ 6pxWEW>#ZB墆K8-x并l*Ŵ2pqߊ'k O7և݋{v~΍g$1ϖH!9=?:Ee5e-tWV3c&I})ĂT ji^(sedk|SF1 eEXU VOvRǧ;5O\)c[WzjځEH(NM:̖Vzԋ!wsWR|zB NXiL8ȏ7.o1H{X^I# B+V[U-y*’=+:UTL[>+|,-| Aot Kmm!AL ~{ZiN$B s^yC3xզ.bB9==Mt%}ErTLsfU۴BqbO׊묿"8.4(܍ \֮.}#|{`?z.Yei|ok>II;YBQH|gkJbo-C}rʘ>|_|G;<ΌAV{Ej70xmfr\vO=*ma|QPAw Km"C)"QKJ((S}(_m@0ĺ5-B{$Pjato3WaKj(XD1lˆqӵq/ %N+&TbVec—Ǯkal|[ψ7WV"o6 \:i ]?ƶdS9{ j;95^mF)08zRֵ;hsXdShv~X[> 4O `r1ְ|g-ވ ;~M4K FDHqxKs<'+[jDw5t*$M5 uy l;n#xo|69MgWwJ>vzwͳI,Ȗa ;5+3Iı#w 5mNS49u7̲<trzޙVS`O#ϩV;;5ϕN\cV_2!FUt4(5#=j]ϒ,^M>iGŶazv+t $FSE۩. h6r1F)}Af6d]9dJRBhTo((@EPKGJx8e%W#k8RA.4="ygRYN;Vin`ɻ#ʬjkper'&"bDy9e0UuX*lÀGUow[\]LFUyMr|FəO"C(䷉澼 `c\cloǧ=z[hT7mUmoZˍb)XjAIZ‹7,m$bK~k;㽍+( ̠ӥ]No+4oINOUi~#k_ Z,nA{5w6;cW2D(K>fVh2+/ n7Q1 gh\^4JK?x-xt\ڀ^+4!}fx\Lr'8=jwWM,i˒6/no)[֭".w gp7:webW@ղ{MGJ׼7}dMGJzz{q^'ZRto=)`NgqYybtRUP$:Gli]Ab6CہkUcg7<.t>oNO-#ڹWydKs0+|UbގU:.F+GEv8k<)ormubo1}xT_: %Ju=j>ܗ|݃sշ+p0+r s/t7QhT.Fx HuKƑn!>]aƣR:PFJ6V(*v5ȕ)r67bYda pTt=bV'%?JMokf2vC\a 艕odʷ*o _Z)ݻQYiM}7=*3޾w[KXi ϛx[ztG+)\FNWu4ycUX{x-f U ;Zn+д]GXrPĘ̇|={dM$g{4Ek3 +!lrq:nuU//fR[7om Ez4^;h0\rA ~NmաG:ʐ?Lǣ[H.sR}*$9,N,ہ8^$D /\8Rj/ ^C ib m0/˟E{7]M:NX~6ҳECRl^dHg {;?QEض[{J r+:X%s#@ko_SD# >=W})DtٻQ0ס]G[u]&|O v8w#vuG΢Z!( R+O­s{c Q'*=Ԟc onXzzey-4>xg]'cӆ y,#4弚}6HsZ:V1 %C3=k Ӭ4n#)QS]G63Ş225En[CvZ$} ~T\4^$ oATAA8iROrqI{TOEOĆ95 4>iaRznq&iV:s!1pּsniJOcڽWvO>N_gq,r~N:tH]\j:f.B{_z_Ku'Z(R@w/ח>ƤnsuEl!8z1Rg⦴> )[uOpT]l@#>N;\DEo19xKn4[]NDkMyj`t7}ӵͪ^\\YU |v71'.^2g9ZP8qj&mn2WP7P xSuXzbG19fS~1R.}bqP)-R4UkܭI<'f].Gdr>%LKbR偎+7nDF?l&dRxRAkө=NRƖ0@`('Ckկ>5[iAi66~㽁vG 7.I=u𕧇!cm+^iOq#F)MBmnծdq^)w|K'1mzzjδ=̺qM#n3KV=Fۀ/#Y^)#dS$f Y摸tӭ^}Y}U2ir:a\Hx*j|Ml綳MSL~iÍ\xM?+ngiA$K`_d@ǗK#ָh+.yJ۷2 Ur4=NXxXH?tL[×^)c Úىղ6pVVɯAj!.p̽iTU-_Dž//`\r\w1K`'ppX~ X-[< Gg5]pTU `ㅔu<E h֓n_s5|^E3x4Q*'΅G%!qr~sh]hVRF2%)Chpi슷pOr>+ռ8"C&*n:iDVEF9=\Aze_,{8Rܮvڷ?VZ<ёG oY"|#밝=ȉ»X9`FQy$sڻYȋvHO\XQ@(CU5Kve]H$l,3G$i3КνEJLB7gxOWad?V'G+GojSKkceB+owYvtՃCK kxw6^U W+|=WP-eY5؜\YKW:|;'#wzzױ-%"{X[͚PrúG5TsZ/_ydbw 3mk:洎0q+OFʽHp$n~U|-|cpJѝI{GI|-+xʕ SM-ǔd s^w.+F έPMZY")F(i~!uye8S>IEI,ϭseؖ6b ,%gc#!@FQͱ(.ۍtΒZKļwCf/[84}"Qp"|/$vEN;r|{NPX!I6-( ޺5lF;{+4дR;R-ύra8z&y;NF} ՚T|:8hw$A84y~I snؐs+9=/tE±ua$ q$ly 6HI3ަwH1ԯEV"yQ|t<; N}W˻^|rC]UJ5-IeS0Es0ZB,ͼ!,3\Jdc4v>KOecw m;pzZ8\IA õǒ;lת0FF|;WcVOtsWev&_m#%V`>zԼExk[vcdGUoI'H MAF<|g]YrHP?ZUR*yGqSWJ[bn&ֻ2Ei0 OSoR#3sMƝqj'Fݴz Q1r՜S|R񩱒/T쐧WI㩅l˟ָkmszf5{M&!vNn{jZ6VdY6Zýr%UV 9?'>~9upO§c Ś>ו vxZUG(o#4rC7SU?]ЍHQ]? I쯡uCmb>_NJ񅄯᫨`OSk|c0C?gī\ϼʺ?Ijny Q~\ |uUŭMwCqz8QM ŝhv^AU,MnUgI4Wno⹭JXnȮAr+XaT8M]]oMҼ'*b]Cұ_AT'֫٩EDҴOC񍴋<^Sdv+N[œO^ {WVӑ$tXs8&,ͬCuk! X:=0G:}zN-$]On|:m'Yԓ_MjҴ.ܴJ9)+ʎu!ȣ,6@ bi~Ir:%8:q[Z:awA?ΩXIet"sveCFˌ>|Kom=)xYk!/@/ZX>n8^+Z\ zs_=Z,*B=_TTilHF⹽z7n<dv8~)4**sYgZPN&c-cI)hK[\LtEk#FY b 'nAS4V9 kPNJߗY- FyfGi\ mOIY^?^sK&!lgb:y*EsxXh!ɒ8Z+R29v9_ b2@fhHR2FzWLoUc|M$c֯>GiE)=Fk[L`ЌY ƾ E.,9%r[U p!fcYLVJmg^LKA*`y'sqw$WJӓYk,NLN8?^|LQ~H.rGqshvF 3PWh y͜Mj&sN=N s;ԚNmdnlSb41iU-Vvo+"݃|Ex& ~[#b?kj7oHѸsMX c_JSB[ʍM?#E.NwqZkDr cA{G$}U@70 ?:;m &JFpzqYbjNn> Sօ͵H#h8_ b//e!'QWUB[ـ>czƖJy>e]85Ԥz+O,ڧW]RuϘ"ԓ|ii[kTi̍IN3_A~UYѮT;xCR={mh㧵rZ<|V=F"KyWV|^~zwܑO?$97#bϖ}N+IofkS%#0$汆C'|7)= I> ?m}fInSx PrsX>Rjד8Y!O|/L0wAo|JjԚ}YOLO C9Y: X^ʊӚ5]2kˋ{FN \&smgS9R7wN*NWv]kw6P_%21ּ[ƾ!J7&!9 w?<|vv"̖NO jZD0zl1h'x/FZ2PtmIcYH+[C<(׺ƱotQ u:g95BZ.n`yǭgQ/(L7nrqSSYҌ~-aK.f%~=n:Or֏ƫUEjE*;]RYjpUB1+!Ao1!D~Ԛr SY728&ktN2"ChW1xvH ]뎂iosp7 J}nİ5+&0^\l4nOVkZ3ɑN=qY:n<- $x\٣\Bķ.t9[Ei$>ڼP$< xҳﴹ-"`ųCҲ&ĆՊ,9GH?kA5捧ȪJ)85-J&12^|)dۀI1\:UF;,nz{fpnH{x_-.K*# ٻ5dZX4/ & *<[Y${."m͎OКIZj?l,n12oү7Ot G}to}7x&KV|vNUUPz\o{WZR[[t}sZ)~:INŎ4m2T kxt؎AHc&8N1-YǪ[N5x8ٓB? i\ou6TC |vRib -=ԏÊJ +ɮI-t8\]楆M;6b >s=Րu1ηҾB5G!/('qӍ4| щSW0iD49x\Ƒ RKSn*3^=.-S4cҎZC LjvDegڪ7ň8a^*JUfT;3h.axgL !v< o/Ϩo Xnӥ[Ef| Ѯ^flPqAѼEq \_^P ~\5mmuM>;k8EA3F=e_;6M&ʧ쥡7~iD7ع" r]_C!}nVFHY7.+roXy̛;P/R %kV ?p-[ԡWr\97'㏮#] hI:2',YM.KA'>k%Ƽ-ஂai+SBȮRiu0'ʱ}J) %Y_k'VJ:,]HO'ΐCxIY |_8L"/KMsOft㩷#KIQ`b W xt22\&JIC}Qr?煯?8BUFҭhpGoƺ dH4~^%Vmđmgx4ɸ7rOP?:rߏ8#դZ]٠c2Fb:*EY n5~V'TH fbpr3ֳ",1R3Һ~:ufmj4m>H. rIϮ k9x#KwY: 7p?nT$0A~ ~h. g{X܎zI$}23Ey o%Ɖ\%ʢ.y$A2]Yj;<<|N>59,%S;ܛ?ZiV˪ UgKg%hWpΏ}FY!$8ƪ:/%i#nbn;}1_ wwϩ<1i|-|E>$Tg$t(P%kym.;v,6 ]kZy^BѝzWMx8X7cq&Io-m&eǠ99C1Ԭ:VP)v=ڦl6[MqocXjwFy`\[Vx`$j@JCl) \jVr_D}8)\hu]1[!N> z+Ee5]"b8jŶ-FzԵ_/oθGsIq(J{÷L݌ڵVڌH]tȣi#Y>bC{3=~;]z?o.d_9;JdF>aR*KS{W/ꗷɀ3ҏZ|%0vik޹rGQZDw?'R;dyMz.<5=һ} noc4_!n2ާo\Nk-ALq]­iu\,2 ͼ݇'ڦڝĥmCڽ֭ęs,52[̆%y=H洴G"d@d1B.:|5yv΃̤w|k.YVFxa*-tY3Jo ́ku vQônmsҴHHSP8RքäZMw=y?Z_Y_VKh"|`=c:6jwfbǵyR85;*Kh4UA!\~kZܺC Oûһ%my/6|LD ,}1Whz]ֺ4K1ed95k~&<)o -m뻀+Gc쿌|W[Mr))%SеXt1!SZ̵*FHWT|LZn\VNK:֬{PzQN(t} efOpVY~l=}+S}+ku5.X4 $ K^;rSMI|?I|mv\5Q+?\>`EGG=3zFƷ5IbHR-K.0_^*)?n- 7x!#X8?JV!/|^XʺOG u=OT-d2?ZYs@3 n?x?Ҽ/|S q/rz"HPF7}5<EINM>,ejCQl.@^ԤӴko$m4 TrG[39ԬdWpuHj5T,)e^?wrYXLdbhb1%k2L`I 7חZ[o.P%҃ʌ:I좳pe{?@CN.6 .chvO5ZYn$z/8uiv$7lW PzUx,6r l9zWCG/ ݇esŗ_b<8?vdhay:W:LJ+tӢPBʓ@?5/]KHdoXEP\8No$Ot*v+ ]Z 1Xqһ͜%*ęF*Y#9Q^3ҊW;hG}h3D܂gRImٰĜ:q^|1ϲ5]_\0𿀭-җ t$?ºabC'QF'vǯL F9zBp _^X-M,y%p+g᷅|=kѶFX_Td-n=tx s;Oʄ8+&b$ukvh\qY=:׃*yq dHyymJ3XXV+u^嗆9 Cxk Ygk-A\%ʱFHQGSRI.;".'rTVNHPKݼ.eIJqs+Kf[RգΕG|]n?F8|((e'='a[[x]`V!/rk~+lg]IKbY-.n1g V-3[q3UFqtf^ho#C^[̺5ZQBcR䚴SoмSvr&PķU$U =~)H;rGOKAwU%N>^V-f}?Jk Z49WA̤EeLdjipw!v\wwŪn'4CYh{]0$TaOW- z#yjYqq|:o xp`JlqH$Źno&t>jWPvZ],cXU–ֹ+kDR9 յn46NfbӃڱ짖Y?" .IbzեmБ F?yݹHyzo/xj[LW*ŀH+Ngxȝ!R@4k۩G֛ڛGNҔ6iC7/$mHԯ UJk]|,P< ֏pZVJ𝱳nzV]VJpMpP!5 3Fj-΅c& Ne?Y=M"U0^if5H`[oO",H=jͅ@rILXgRY#|JΖ?gi{:)OmDiv^Ӓɢ4'o\ޥŦ[ZF`5ZjsiuvxfrHbjwisp̨UrO&ኞ]S MM;<-iwoMpwqH34m/8x%_b(iB潿 < *8ܯ(h:@ZkۙKOq#hBXߑѥRn 8t6)ϭb]Fҕm-%RwXҵ}'lERB?˘BP9S_}^*Ҿ:VPM-jC]HҪwjq]˺Ee%\Y߆.l&tK10ڋ\8ݼ[%ZrVәM@^j_M)(Z?>àabGPbtt֬-O]W[y;`mҮ{VC0q(teKYAFV5_n73qYTLڼWQ_|B.^VGO4FĿҼw4ϊzͦ3_Q_H<,z|%R|Oô6iٴ߱Ǩ*l3>5v$K˂ Nw<'uU1^ڹehi*gFw(H |9M=Ֆjl#~яJ>`9<=ku#A$vk*qKWC]rm8bq?/-cowp%+  d{ hL7NV/֗U)V"I8~y519tG-jTqᶭhI 0~FoÙ_?ȬP7{P̠hH>R].D671U< #0,FEyӎ_pn[sN]~q=C-ԋx9<Y>BfosQpdNkUW“;{RxfLGZ~$-UvϘQ\*_YEؠgv|!ԮAnc$9=GZç*ʨ#QIE{~7:;u]ж_+i7Fl#;}* #t=kcNDŕa$m/ PUe):-+v,pc֗>kdPy+7W , p]=PYܤEd`1ߎ͆RՅ M>I=UƫgcZ#%?tr/Lkey~ <'( Pt+ =b#^kZRXy:pPŬ6.ZoI;]?5y wsŬ=~¾q~^=k`OՃa*Et%n:Cg 2;h<;zѰe.3^5¡."++|ܚ;&-?W*k7ܺ,_.#lYSO(&#ufi^z8`]U2<Ҍש/4gWU_.q>*^ ih{hΣ' zN oaZapt4EJhDFw]|"oXu؂MR}Ȩ/,YEʹYp2kOs.TxeM6N]NbSZ֢Ldˀ~+fEF\ u#I][݈ˊ\ϠMC4G:6 ߸*h2nqsR;#s{km(Gu@su#mvO|w3ySe}]Qtm/' K"8x[WEn}*4]GؿwIQ_R [EmlNZClVְ{^U_E:yv$'{xB4ЏCNz}-n\1I'R %6VkcϘ|kko/nk-I=95A*y2W;9aU Ԑs $.+k8f@ 8#bө73p äopm°~u~\;q5pto0U[&$Sms.yҹAY3RdcK4 c4HG7͜ J^O&6,-קigKh'HR7d׹S.?:}>? '>x(渋6!؃hJ5hldm91d5]FseAw|!koy ޗXm& tf]H?21尼UnN.v\2ʰ|ҎMH|!1`'ː)Tri~ ^XdD`.7zr$OS]F>^}MCp߈>u_jjT)Q<-&nh)_Qc|zO*I8(jxnMJ<=F+gƒꍪfg,ghǭ{p9ޠӥN,Hǁ5Ն iTlglaʹaNV>urqW6DYWo`Il5|m]dh8Msyqc_i[1챆s°c+v.Ty`$MmPaGJ NY̲A0b1`pnc… Q7IĎ:m$*9Ty-#t:w%{4AsdWԼs4$:Ma'(Ҵi%q8+N"i^=Iɴg[#A;z]IA]ǎ [iR^^#@rFy+_?cCvuͦu¯EIZk1L#ZxJ? vNnF_y_Y4&^ج}B*3˹Q:ʃυ6éW<ȦH^?vO`$C_OڥsqrN(^OgUV~?&5  oB%;K aBxi\Kt5Uc>hR #1XkRۚ`uMy$| oRռ5-@DFB$&ا]Sk<sSHрA[_7\ۛ={R`Aֽ:{0<OWMΛ'\ @}sz[{ǟ'tAZީW5GWKWItpt`Usq?ޮN/Mj<}郞_O_uxQ@n?z~Q)yfŶG_ޟExk7pGh%_E{V- K8-*[Ǜt9&<8`?߇-F;E#!3q X_ll\WQX :U: @VcDw>W]xR[.i`H8 4["$sP2It-e5M* ц榺7`6|]2)b?WOq: Xxu.a ҩ1 ݰ*?)nm&c{$oVfh_\\'"]j%tpTוNUIr-Tyz]ETa.x^'MH|9&?ko꣎+ɴGe6M£5};Yai ,pġT`+)ߚ818SZ@,1Ҍ'ougE;$0HqjWNV=D1}ZWc(/HwB#}shyfmkvGm9=jijGHIUMVӠP($,cunz#4̪;ԭ/+t%JH$\઩*J|cԥ|AMD|?#izuw[F  $"H^F#=1$Hnfe(9+咂Zө3gG-J\1Q <Z7_J;\|=Q+0qf|Uzz=i1jόVMm K >won#aB_]?ڼ^&הaa*Q:>ce=k5Qчs⇇h^Kk+oܡ+̪P=L&&.>΢.m๵5 "m6'$?.A/Pox?pz_ R)'{;_ {"4| ekvfc%O\?@kI@yv> x—a#N;׶iC _n?x h2ǩ<"Q0j'G@ZhٴeOkt%oN9"@O7ki Xs0 Ӵ"jUUUV͓O 95O6rVsRFǸq'+XiynmJy,Q{]Bqڔ[XOm^#o0>E&7McjEP5HF@zoųiq9昞?>V4cң8"-I^\$1 Z..bYh,d3x'=^<8M=|st:i6e޳xQi3)Ρ$ֳƹX>)˲Nە jǏe G?ZpR9|5+`y/_\[>;[Co8?.k0p:}^6wc˫ Z[]&>XW4VAcz$4]V8hG5hkG~5W/'bJ^Дξ>)?zK("E#d-+hiH -a Y|?.7ִ-(P6Tjߍz6[Eϗp|q^ |qSYq3vIt _˧O4#-[JүTfuj_{ְXLl*Fj'CuipQOI`Um{Ŷ cU_ưxzsv!=Rdw7GPUcWZZ^*PL٠O¨8^22}kh}MIﵫ1 #>կ?|V~t4Vۄ9׈乛GĪVE?#PS4*>ĐDġQFRƋUvɳޒ))dVJJYշҀ>_F2Ļl ~"]@qj y֭|~yן}k)ЅUi#"7M7z=igmtKr}믽[*kR!+kt}q^⿊3[x4 2nҁH5YOR5n=x)|UӠff5$Q˽YR[mX<:4CƜv6cM7S5dfSu=![y)WpcҼ7aA=4k'?ږὠ+_W̰\'7SkRi[c"\X>zkc҄$gPoq€+MRd}imp7\x厛r,kJ>*|bmϒ9x3O us^ZTFO<9nw9]8\!a1ֺ Jjn.`t!K6 活X+^ӽhZ]NK:G0!R0@AŧV(vzc }kKYP \8*5;y#HcSEI D&sBƼ(3ּ4ӝQl߷=[A pCԌnpj((QQEP0Ȣ0|/O%bPC?ý|Kt rPҴuͻ`_ji6Hv>l|=wYPmʓ ״h;Š2 zUS:6blq ?5uo5sE܊U>ٴݺsy7f ,m+գ}\iy ,YXvl儱>ƯT:N]tKˊ+ÑǬxOKYe+߭ע{fBŞk?7~ֳMTRR}H`(H Mji1lb^Qkߊ[!mj'@KiYyRA5DWtjy~st5y`7NtV9dNa'5?NhoFmf zWSg<>$VibmmZN: ]/öe̗PC'zgwX(([}))dVJ%Ů{EӧI>Q=n@/Zˋv- ¢mtta?x鬼y4/]«qsu&6s^X@򙔗d P[jXWErlR޼og4ɝڬܿklڠW9,^R/knĞw ]cu[)RIn:_W~c/iOgiof*ŭٰ,1]>BEԳZo_wP>FRVwyZ4/JƟj21]=^#;֥V1bO5V}/K:}h(SxNM9ź y_{Y4m(Y89DW/6{XD䣒r$Eq|qL+V5h|;in1\skӫ)߀X$G^iN]L+5̤ͭ-qNZ4h[bu J{jQ !-\o&'9 k2>QEW5ЎQjQKを79L|B>Y 9riT{^P:Q\(((4PQ@RQ@){RQ@ 3IE.h4PEPEPEPK'o%,qQk|?#y28n"_!]@KlP1Ye-bi52k*C )]~05c*9w_O) r? ȵx X@k>8o{BC߅xiYIÛc8YJdž\\HI1g i\ $Wj W%5VuPK ^92IȪSFn"2R<WEbܛrL\ttw=6O` =GW޻_V|χ7(trȼO4Ի rNiv"?3qZ[W+m3.w?KD5w9^Ӝ 3U=W/xJ=}i;=:FkwHn2H@(6*:\/mM9kNWFc6nXd?V`W\3k#w{XЫckyQk*Ӈ&i־GwRҸFArcWIG12~T8\}E==x+ᦽ?W.c2yy?QקJ:v3n ıVFy?k)ȯ[_o |z-{jG Qv;cWgE+w]1`sھsoڴL_ja C[ sF){<šŴLؓtVͧW jLmܜx߆mc$w;\⾉>0q:t#rhh(bt"[)Yb?WS=x:HkI>tZa9+)N:ʊufotoxx-18.01.1/images/whiteball.png0000644000175000017500000001662013222767271015615 0ustar micomicoPNG  IHDR@@IDATx] |TEү~3ɄpK@.AADAw=C@cQQEWq9VVQ$r2d*uW骮>^[PII\构U|D-GV7 r}ԢIED5ޮF5A%a^"cbjr/!rswiFdk%X9kI$ڢ:p%f "/oCbR UxQ"U )< t Dd;Dps)DC(x\٤h7mm'2UrpCD$YوM&$jXz^;h݃`ow!~T~}6"d# ]ռ݉!:v[{,Հ(s_#ڱW|7~Uչv/lkD hgl((IJm &#;F,&HX-j+*^Y1%a<:?== 뉚pI(z |WrPG$9Py]`%PVt,xB+^[y|$?e K'qSsw5J tJ)QDAA@ 4-2(eH[Ǵ<%EYBp'!~0>_;^f| E޳S&>@~Yj(J DGǏ(kyE9ljmL@ڞW}6ex}2:F@+jvcv>9T]}Yhx*=< ܩnVZ/OY[JDHZE+ĭwؘоڴj@(@+\) DіprP&ebQ0L5P@ *t"kʺ.]z 8* b|{A(+hXrM9wmXe64 qW Q]ڶ/Q95Ԙ 2ۡ{<<ݑ;^[Z(تjU'Vg"8DT!Շs w]CsUhkQg$ PH1To` H(oAiGzPwba . ſ.'2q ,NY= tG5? ?ff 6b6Cs0Ld ŒILW3)Bv#D0ׁ %<\/HQ5N{?<ôwM3|Oà {X.XluhWZ9a]!;,ؕ1⇺wE! XR, @-AڕXSH9u.cLwc#*ԄST!UFVFx/ڄ E01O+|SO6;8>*MO0m`q5`0 Lb wms9nkGTgT)*Qio7}dY-~P]%MavWiJcƠLg ̸͝:ƙ3~zcF-6u5aЌ!LafPa/JP gl+/̶o"oYؙ1c`A~~G;V LHQpzb2/gp cRcHgtQg8?7qe*g7ʾ30]; ED?9D.3>EQEaa8E2v St*׆Y{ uF:Gcg0j̠:0L*_}7RmpC%. )*ՇOx_5n/sW5?@`x>_zF;uVBYa4c30KKHբ8 _udofR))>F[@{;joZW7cv1nfmCff:XY RZ8>xdd tufv/Ӄ?*{37 al\4F?f. t8"ZZɟi7В)==Ksl5\}8ZOo/ [3Fa&ѧ^PEHСB{ è{1l_D^@4YriWw2eƢ,[d4ř*5)8s"ZXQ+l,*#"L\=@S.',P|%)Rta oH oA,/D*8LAc0o3wkxʕ%EG,-RBPޑR!czvD]sO0 *f9~VBASƪ YRF@VvjP1s9!-\-r!Ui ؟Ƿ<ʟHR!D VvhA '3=XߣC2Znw( c}] F{y?ث0g)]^y%`~GR9;sϙ"9q ^_k#^ -r6>=heY hVJH@=(,oJ*]lf)Oa.$eV|SW<~"l@ďr;rPT)8XOc ^ ~%26[+T@ 2'_P)QlAj `$ H?یOl>+|yM8M}}PTǛ%QkUGq ~>"7+%LRZ*B®lkv3 h]Mܯ->5@F ԝS3?kzěˊ^)?{oJK6ZWtpP dQ8EN:76W[KgbH{ņcybbTs>2x{I|qf絗c>~ h?wՆak_͘Xմpj~lZ:F5t(TM-8H'#8'̀].z1C>+<:/쫱Qfr"$4#MB㊐4(JTpdqu#~ Yxɧ V;Y/ӕxh rJJ֒4Ĺd2I^Xy)*ρP7L"L?|"iΊ8 bIg;q.UJ6W&?ǃC BUUj8%OQ˺ncYdbbϨИ᷺JK]Θ]w&jNtfz\4xɳ)VjSv |7 3ⳡnB 3&᠛kj5D&i"{K ő&]1hOO[υnr_ C.Zm%#NC선A6bk@R)T-?5lN2֩bp1'_|lgLqn)oՆ P\M_nojI^t}A0o{WIe ,uغKtvYqCp[/ 0K0}N:,+~h|xToݞK`]Ϲ!wq݉gb4l TP:8;[Y.T5I"zl·E>uBR$}^;f@@a]O--5)[mbIsfVkB0P/}O(| ]A輔LUnm, vVqE~88JFƢȂ+6[-,LkAb1ت 3>j #ƒ LEd-W0˕Fa܊(JT 6>|=_?8{up~'iwDX9!f#ɷT& ^mnWтթO~6҇3nt ׮sw}؞݁}c0Z@˴e(gG ~ֱ8(rC&lC*~kj-Z'DPU'g)n|!+s%5pfD‹&!=Dæi@I3X ]XBֈ*.BNȔo^ɛr|f _ynkڴ%C!?)P+I8}2'ycI4>hjD:*bS{*{ =>Wtp)gͷk]3xȟee9pD}ZV@MWn` Av~-?@6zo3Չ"uQ%=&.XlTRSVk?+ҏ:Ʈԙ:!#TegHC"T)_yeR S1Ɛ.Q>,!c xOF"r|^d]5vڍP3TP30nk|hHx=NʎB);~h˲c>^s33)c+ 'w\ó?CxKI |ꞻ'3&F2-.JqQpzMv/p(FI"hA V@\\+>yh5: |"+~6*:J!.bF&`fLn|/!4ZMC9 s{4ۊ9+_-ꃱJeP{v2(E¾z?#!M2%i\5JiTXtXML:com.adobe.xmp 132 134 CIENDB`fotoxx-18.01.1/images/printer-calibrate-chart.jpg0000644000175000017500000004314213222767271020343 0ustar micomicoJFIFHHExifMM*JR(iZHH0230q0100Fotoxx:warp-affine|trim_rotate| Fotoxx:blur| Fotoxx:adjust_RGB| Fotoxx:resize|resize| Fotoxx:color-depth|resize|http://ns.adobe.com/xap/1.0/ 7014 4960 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?bvn."c>YAxv5 +6iBwf^ w3n%Ҿ9<5Hgӕ>j#Ѯ@\Ur .a^<h82ˏZ/(Y?1^F;4_-η4a=k6jGqs,fx?样m֍ qoCr}=õ}:O6yg|[HU>9p2x'Rur6WV %,j(M*d_TᵖWCgOʗc|/|}U|=x2}Whl4+\ggϣ/c ./ZZ-ɋ8}kO go`&έK[i)\~5me S]]'-s ^7<:SgӦAH.N9#:]kL^uC H=+4(t*9zo|1<95b){Z_Ċ]rg}1m/bsJ "]Ewc޿3xZ7ɻFd~ N%3Wg?7U#ap?_=E]A>q<w^& VϠ٩/}2jPi|\¾yB a] iW#J .v&y /{կ|?~^¯m Mє=C+ʼ:@r зNk)JSĞOg/]_nVi, Fwl\ i/osCYҲmHsZ˜뻝E~u+ D/ 3h:8j4SMH3!u8Hq¼6G?|Cׯash ybjjq[*>;Њ5¼.=Dڴ-ھ/VGW$tGCO_ ҵΈjG`^-kH#tmWxL%:i8ͿSĩao|WOk$sݐJ5_i}qq0+Aҧ<(V -wjiw)yC lX?Z_FO~/< ,%iQ7GF}*r+G}+C|qxE`uWCMظc֔8ׯeƗZV|5=j?2PCcWX\7nz+駂i^_K,'nw|M |N1VhiX2r]~4IdSv_Tg=o~ˁK Diϳҷp 9⾲J4oq̪ї2Gj_6-ʔ+4xJOKT+ߣ },sۚ?:th7(E知~Qd5M0Wm#5ua÷)˥)W"(r,-r~Ӑ2MMOM8ٗ_'ҒqMA<ƿbҢwsǓv-]-;q`0]fOpWq' OOEʬԥy>`/-NJ2x~b_믎Zet(r{T?u}-~YF2y>v>1чH* j:t@?Zk`ke9~*m|h!*Ze"Gy? +gµ-0Xg_qKQ Ts'}i" XE,g E2apvc5%C)We}h`[)c, YQ.Ybbʝb.>[1-(Тyk,8!lK61ֱ$FnH+5t8gJԗK,3Vm??'Y-Estli<]M⯻y>i}i ;u{U>$k^uq_KR/_/*RlV=xW+Fg @NÏZG2ۂx,+kF1z:@[i?ZgDZ/wk V#‘|jPM_gW}9vZJX֜ZZ^4iYKTdFUJ5_3ūskE7Z6EWSu;Vpػ'oξ†kٯ*fo{~'t\N{;rxqsB0~}=ms}j-g·s־#rw8<^jbsq5eITm1mP-$ժrl5:ŭ7pR8=S(]~Cy'hl>E@f?缾*;:+*]lS$J ~o*: >*q֯E++3KyU3IO>͎^ڴ_c=zj_E#F/v$=kI-M=w܊󳜲kOPiMktޛ>!IrJKV>XRE{yN9nuvt'?Iߝ!g'ZnђYp)ҵ;%@Wi}9@li>}Ͻ|~&[ed|{`ܒ 1O?wEvl:W)c̘o^^CӼz7/{[5S)GDT"F)q־|ӵ,a#=|kZZ*i@z`JnR$An3r0EyѮ ӏתr LOθmkZ^]ܥ *8Ϧk x)cJodcFc(BSoja׏>]?R/ƍ{hէ?O[';V sLN-sSG䖔l_5AfƯA[Vƕ<=pl?>Sʱ9]H?S1` P~qן͏ n5|GiGT?X6޸\Ei[rA}7AA` QL5_ku|V7cHFӌ>:DVzpj`R:5BK[كdqrxh+Zɶ{y>3(# =O]8ƽ|6 V`35wYRFq|}1ڿ/rVf}N|8Ֆz2Vc35v.]3?Wg'"1Ӎv \\ڧn~Z,̲YJW3Ŋ~m>/묬LV{_z">]o?QKt*‘bCG|W=}Mx+p??EGTsQN7[i@)"JFPmPj>-DjYUo~YƩh9 ƆiG֧X|Fa4auh+ Bc^? LW{5y$n+]? lss+,.]"/2M2OqQvMAk\9Fmz#+'?ݯNEWPFzHt8Bz+\Qr8 A9⴬tܾrzc9oOPtYϡax:T[RGϗ Е?}i) i[79q\]Ƃ^2G5{+ml26cU${#u[[jq/Y2x%?}ؤ's&vZؿ3 M!jv2?+V }OOunsOQU]6~Uvk]x3m_Y'ezn+ZG?hqҮ-s{hZj_1woQ5&6]ϕyc{|#-Ћh g?sgnm$go^fKu;I~Ȟh'?|pq1x"Jz]t>/LT`Q]bG$,9jX<>7*~|a4.(dL\r1O~>6l0 adU?}R|W9SK$&zeb!i)-tQ|w?i%z|5ͯ#~6z 4n"fz&;.JW=ip 8y'ƈ5Awj|*И}V`MR#?_eɤ{=Fƭߋ{5cG[& , Ӓ2K9_{FT}͏w}kPYa+'8f$v9|;s35<ݢa&sM,?x-]v~?fʸ⌭tVx:7.ޟYOνJFe51?Ḏ6#ީĸf/;ь$c?Io{W/L̲Oh ni6S8YW~NEۙptSiỖ?;w?^ G|{ɟP'lb>Sݫ>:&7>aO:2»3GY~PJѴ¾.|#c+<$y'ϻKeeG F Mw<*7̑cW/f69j=f؎ux^[`@EKaָGE&U,`_}ds-7R"=bJ:qךֶءWSi5LMkOxskH >he,c^ O?h V>FkM5Y[1Cxqĩ7 iI_QM1w}`A  w]ivvoX?h`J-9iƾ ZI[O3Us/8ާQxnJJ`jiHMՇ`|@,PFcFְeè[L,`;:~ pl?hOG a7 )it;NPt ㎧qڒGB[jE \v~gUc#Ə.c{sƄ2j5l6?Vr~I9-qkc$y(_=L0EZɊFeiobfju-X-c ׈3}k $Jq+Nd^c 3G5iԵL8!hԶT0##|kd<'_g!猍zo pO:Qwku=/DRzhCsj?W5d?P*E,d+0, %H%mòZT=/xh.OEs+*]ɎZ|#or r8dҌCx[UwKm%9 ƿPnϯ_<1Y:Xñ1OڿHF6LLWf ^>o>euGo:Ñ"޿4VSCx8e'=xP&T"U/ccZ蝀j˂n@퍿z|6uAќŕ59~Z|?㣨_洫9ZEy5nV'1_y,F׻ZL_Wk&o׏1__^6/hx7Oet]<-wVJDfq ҷcV_Dq>&_ni\֥jqiQtjkbuSO6?9p LmQϟWo$p2tE4,-d^ M2<}󿊚b8l#q^k%ΫI-RKmx0TJrJW{kilbatɛ ^%?Ҽ{vшԉtb}?y i?E __1{DmӬ?;$K" ht0:!9ƯGDĔkFq sf#"ډaW#eͪ*Oz5"\hx_yxgxoȏ8ZvSⷎAzK$r~iR'UAnտ½,&k$3 %_yğ|is&w"?f^cnn9+-_MVm_C0}7tx,ғHkp8ta$83ƣ0 oCZ'l1_v֯eo!i o__fӆIx)&-=?NfY"U}/-?|z c֫?{m^~ڠT>+ll-RDuv~, j30"_EVK^U iEJRO/L>+I3uc'i3poyo]krWWʶOQT~yS#eܫo׋57q_ס5]0ԿGGRϼj> AԿZ θ:֜^Mz//ܶ,xXnCKJ>_x\> |41Wb=}4b#hhb֩k}W^@ꩣɌvH<ĞR1&}ynv$x')v{hٚE!WIx"ܒE%}VYƔG֞[z270?<Ə)=tTTh@TRrqW^/KϠ|a|mS?Ũ.nBr{_N_{<_u_0fxV֮tɟ|V|%U@"r Q2pzcxm; 2f]ҩGh䜺m_ǡQ) ^OO5]vO&ض,l|ⱳY w/+[}:\Hq] \F%b7yz 6u絔/mC~(3¥Q͌|q^bɎɊ|#a,R}[۶ }(մ[oSESxV ϋ^dwKM癱]y~UJ\mnz|/Lѿw>ՋRG焁ثq3MO\H+xk]Sp*שOSO&xh<īHxwE_M^<|{M[ZzhSU#٫3X G SIdA"lW |æin&D+.+\[jWqfJϋ:Sθ.\^{ëW{nv<O0kf%(ߒr|hL{s`Y4WK[HCG2^x}~"{N? 4ێZf^ء^.lfunc8zWGA||YG]Zll?b6Yp8?#3fyfwCrII'gCܣvZ4ccǟsܿS\nn³' /^PȿmaoouWu~ 77/V +*M2㯁}~17kF?O-:sҟGPg؏z/>3CMz?R סe¼L%\攕/{%w?:iΧ"~+,4wu^W+ ~71H  Ǐ>$L?WyVk%%7O5ZՖؓƔ>QX[Hv j|z>Ho[_*8B $ʷ?~kC94}Z_7@{{Q[<6͚ݑsoh/Zɺ`yq\K?+xBϦ9e$Ļ1_>@ZLXgOWhi|374+0QgiO?|ʗaҖɭߩN<Қ|.+5e>kR\BkjMS]ү&..jM9 _UĠƆ ``6h< YIӛ~gViƪEm>FXQ'Y>~<>~Anh4hgԯ*smU~TIxrn'ˣrιs?|W0[gׯv i=xB2O_;ʴ,uό׻SI>k)'&lsԱ&Kpzz/Z\cո|`sϪW*IjqsH'?诨&YϪQ_PĘwQ?D}GV|uOEGBb Y`S<Ҥ>$/וj$}@_g mٷ9vߴ1FO/Y)Mٖ0&O\0[8&Z^\e(ʏQz]lt:G?#?)W?W+u ۬vm^ɪwk1asN۾3pE8(=k}5m"qh^0?sx^ׂ-?}V]dmh 2\*rEEi`_WtUGW͐Z}~;sjpT0O8*p'gѲ=oЯ92%d_zU0C"׋[^ H8K Z4Mk|$s:SF6H5J~8ž?42EvqՍd7!Cm ?~QUUTݾգ<9YsF'gW4J퍢Duչ|wՈ%e 9sdjm.޴?>P?`DыcHƾwa@c-O>cg5f]b8g\7'9OϤ6'úI WCl-3gN?Z'Qk.jִ"Ay}M\jףI=,۷xrQ)s-gS/D@/m?Ƽe)oi?Z|~lfA|[ZR/a]E>_VO񢼖v 74Q_MUí;Kw:?{< ̲C)xjG>1%i~GIi5mQN jmSWHW"勃$!_`x5ҿpJB/٣%#W=yWs}!tK}ZmAZY@FY8gg:WOaKR=>d5?X?X0akN4VgIw\)fyzW>!+?9Е<0|0IGJvPrbJ/ ܐ_xz {U=h?1kc~AN|g_B/dOj?ړ`o>&toUȿhkaxҰ1,N9ۗ=WvU??ٶ JxNr.B?uf97q ϡ.hwAR'ÝL`_jĊc[UV/+kl _>CҎU5z(cGU~—h>Qk.?N_#Ī?^,*Ir}<,{AK/Φ.N}W;苶%OU~QΒ^Ӕ(G=Cgs^ͩ F7Z6/[JNOUig?_eLN*W3y}Q߅z0۝0j>j qZS7-؟kS\2 ]?h=.3,KvއM?/z~"|z|j+ɢM 9hh_yL'R]BbC.{7O>-"g TF_W]/®ET_1+q}'d3 [3ʟ*nb9_ҽY|&ǜߕO|_u:v)gn*r?[l7V|?)b!ތE}mk6#?jn٢I~sK_s-ҡfKT`!.TQ_q Ts(ƔTW*[>? ɬ+X*4K? tMK=`Jo0*`Y{Uޏ 7K7 r65J7rUXXV_[~aӑ஫.Ĝ;w-؁n"20ÆH  c0[0+l5tMvoʧ#d0ŷ%n2۽ ffqd@6Sw1X3~i)d9`ꇠ@U)0}<6q`~86c`38 )O=w$=9 w7@bc8daP]Gv$l(okAAvpHf>..v4-e<ᬟ3 }X{A*U nl9ndȽ9`Z0Sςҡ17#`WR~,WNRjr2#\7_JuWR3C0( 0R)\jiC=YF%A +@qHEa|1zFl'ť!qii8h-+}?281u:zjr sd5ޠ*,|v*]U-u79F;PqiГ6kT[(Р㨉kG]AĞǭ  :`SPctttm1fyY\s[] {%]]AG[А|dYcc;6"«ί"t?, GwַB/#>ޝ]`*1!;Ɵ!;-R6sry v_ кQ<7I _ϾPW9gy{z-`/ھVnnȆU\ +Y ;'r⟱y*:-ėogq)qeQ'}AfHd\{o9̠(2kt﹃{8i 8/y j3_4Hq:2H5V;i8ltPǭD]`lb^^*c .CF2WP't㼐f'OB.L,i-,nR:ǧCwN\sHs;P/6BF Cév1bo'].i.Qノ^ȇ;1ua@Ju(~-x#sh|?Y|Kgqkq|,7Huҳo*ET/ahDug$8Uμ_Rsį`|~/ՒSt- v#.h/uLi&8"^Brg:ύL|/xp/Pw yts"Jqٰ=8~ JI`#b&]Ue]o/w@Lw E}n6+&9~60<ΩrMl(Bjҳrq ʺ/6Hߎ0muʐbQȃ~f..!)Ncr*M~r[@ֳ\f!,ze!\'cPiSotLxFˆ*/hq>X >7?3 v eG8:^\ف; Vic*𛿂t>kv?;^Oj.}15ZmQ2էo* Sʕ5ѣ?U OپI潭oB@`S&AJe).]•1+~HK9saVwpR:X{*Vw15}0[keDY^MT/@UC~|߸Yb儽IE0P3U2!]c80,y +S,'E\)0pIP,)Xl>`,Vz3&zk^jNr~ԎapnvGƭ.}^Oʚ=tz(t陣Ra}w3lj7|;6GEZbY:^&`ҥ}K ~utO`m?}x=K^q\MWQ#UWyuW=33黵#??pfj(Rs3+GJCu yrcjv;"Nb:D_hpړ͞\bJ‹^Gv^yA{6 1`&if6_gVeW[es"B|g~9kݚ3@jo.鮨ZV'Z#^v5<߼VަX[.z_hzaM9(: s6I.[: GEsUܾZ0G`ZMz=x/ni0~Iy/h.94ߢ<ӢqGRI?TR9#7ڬ5OX3JU^=x[˕V^131_KQ+ M7Wga{U:IŪ{ABP!(#Zr. ]ΡܟAjN;dZ.u[ʏw/@B"00-X>r5/4KzTXtRaw profile type APP1x}P[! ) 132 134 CIENDB`fotoxx-18.01.1/images/unbend1.jpg0000644000175000017500000002767313222767271015204 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 299 161 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOc޽9ɪ}KTAghQ;fvD\'?“`ۜngϵ{&zEìO,Eg`-G1AT_ <Ou-PrU%*~nIi?3>}G+<1~6 .GyƧof} $S@>Zqj/1foW:)m{-b%WB]N3nC to4 k<ސ:/)a6S]ݎZ`/ ׅ5xWo#],3Nҩʷ8UH,ԯKSSkh+7# tQedk o+<[6m t-Jdj95E I!ڑs&k&5Ki8$ NO W+Wݟdfi>oA]#S;ڪ"D!j/mW'f߯vdI'ֿAw|1Q!v^ڜIoA]#f?=~}kO/D>7L[pΌ;|WvG;|WvE~ZoiY hVh.M,cdnǯZGis_[Z.&bȌa-1$q1_hù~wmù~wmY_ג|M^7}~-Ιi%MBn٭Y2rFȭNUGaRki%$*7Yq5;|WvG;|WvK.|Wk`g~$ezqkHxIWg5 oA]#oA]#ӳ .ij6Y]vyH dH3БۭUNbP欧PrAoA]#oA]# iJ %8;5}|`& ,\J5-jYepɁjr7.r7.l33~gN4U/ks>Wo;eGڭJI}a^g|!;2um%!RvHeXr;q[QEoIk+#Ҽe-k5'y;H80Uiqgi ZZ@"HK#lF@jk[}?Bgw{w_m_Shy~+AZ5e#'V@Q@y$ -s-HN~(?=R,>/&D^~U,@eoGR }YaJY$@΋>C?kZ[V]6n첕F#?+) 褐 ͳ;[hIpHGgr8f'*(SRKS>/?tPOKA-O>_*@,4شpѼ=ľtO+HUrY=GV袀 ( Wik7kf$TIp!y{3>]IIn?:?"^YR7>_:?"? .>_:?"?E bYm^iemWR1Gc8ps]|uJ_MA'iXe8q_bCAEbI(Rdz2=E--QE|A5 Ce~6Odr1|Ҋ?*dr8U$Q@Q@5 K9D@#E}K_;6yuˣF8_DSE ߆ֵۛCúU䚮)$l^Y Ҩm|5Ԡoau95 UmOO=˼;}EPg⎷fi:Yym4~8vdbB*0 +=0~?΅>cZ;hba}2@W;LnWqp|U-q; ּшm⍦>ly6~)Ac I}-DU$qw+,!v;`ſMB?W7- 5Q@)дM#W56jX%4G*$5Y_1mhQEQE׍->jتTڽ+m?ĐǴ2ot sEr$Q@4QE}!.Yy~.py{uy?_hLsȯX,+7Z=&m9˅ -̲8?uQ5E0,ý^-b躝uju/5) N'8#F LE K995E,;"Q {^.n{^n <ӒkJ)QEQEr_a:W6WC=7B91A toI, ,sD''M[ߥ |T z4 z4+ddϕ_&||m S1 S1pOM[ߥ ?l/W|_C<_C hCVɲ(%v?Jlx~7FOFH ڡÒpx{ NŠ(((((kiufܺ:pƽZvuL֢΃5e;ᕭ;;IuX }p#ma]xW2\xwJ,Q3;I$I>ov~^Sk2jWXeVOۆV v.I בº 7 hu?4o\G_΍בº 7 hu?4oҸ6c#_h+FiMmm>%h4p պ@QEx’xW = sGzr??Əo@κӬ|5Deүb)L:xSFSP]eVE#訮G[OhoG?ÿ ?tebN#xU(Q@0?##ZxA#|WѾ5:^_3-i%qq8=G<֯}mG Bm&LZf'V(+a =y:]= tOϪA]Gȑ_i[][30 93TԵk%m:dh✴k hHAE| W)N.ifXtM\~_۵N߿Ѵ^$մsgx,ෞk)4Tވ Ҭ ^#RzjnN憭oE3ǽ~(t tqsOwq-c/M?z 6$?f.[@"Ky+  d#5o{K[\/b8c.-#۟\nl}MS/~ͭxY*Ǩ ‚ٱ@Yj[62xؓc |Y\COkAs}I6J&66 pZƗk|?tO -J-oTi~u c)I4F'&q\ t~/V]/U|i 'ڴ׍`&-7; JAv\6VNגN='Z/7Sm"n~=sRqwP0a>&iQkqh>&Ӛ]&k+g8YqRFRX**+O kM}6wlݢ*FexKavJ#Am{]+]FnJ($k=ssR!tyH&^Eyn5q隿1 O$PO$t~9鷐-nb?G'@~KEyxhjiW@񔑒WLהnzE1/]Ħ@ fkOUɺH9Kr^+Hb \¸;Y ]/b.cIb*v)d؊ (dtxcEk1ױ*^Ey&JNOJnߺSIK:nXto쫬^Eyx95Ӟnu>9N=̰Kln2Mj=3?n@Q@fotoxx-18.01.1/images/mashup7.jpg0000644000175000017500000013237713222767271015232 0ustar micomicoPNG  IHDR IDATx]|T=lJ: !B4)`E}(W X UEtN5@z}7iߙR EEd;̝sg920lm;=tL&sdЀA7t:]ZuIi)VW)F;]h{66Vr1 }C 0hZ-jjjQ^ʌ5))g]}QxQ[{7~4`ͫz0667JK{cܾ}l7uVo0꛷ %3h) +-wqv$FZȚp̠;.76`]pZ;+P1q~*}*ϘbnnNQaiiѪeeJ4cf[Z-jZce|y-ܞrrN?ZԀJ*uK 3'Ԣٓ 5Jà/lm; !9%bPB¢__ʱ3QrS$nN-a%a ZU8[¾,&&--٘QC(i\¨kaf7Wk!yE(UU³fqS \.W*HMF <`͊1I"dBgb6n0QoL oay dԠ0 / =3.q^Ya9h.>>MQՒtxy6#y9y;`g.tԜ*V֠HS Ń[rĨm6 U35P[[HNFfV-.FdF=z&[XXɁ猑R:Wk!'%- zEO^t刑 0(J=6{arX*e\[ [\L23sJn$GKM7\ cH4P] urf#-3=3k6s5ұHB55(QD:XE<Dik_"Bk_nT=h &}b?p4Mk+3TWA5pr񆿋eX:OـduΣW!3E]UxNf{'Gaתld}~ ׬8uXqNN6;}kZa rNyLCn^K=E2X<^q #i-:Nq^Q#s$1N20IWUU6+GQ*xlo//OT*Mr%9JQkuHd m-;Z:vbp2Ar|)Oь.q% 1J\9T:>9aVjj8ZcO';)M=p+V, bڱ#hbҘ&SF TpD 0eش G`áLظ GSγ5_3ph061)nWjPɌTN9~grY NP,`HKhE ֌Erg"5+*1)Gh gԊL XL-$QG זO??_Fߡ$It*!'7xMMM ѩ&$$bMv<|_xF ȹ#''r$WZȱ`?ɶzb%u8k;V=L q,Ne=\&urQ*iKf,|l<3HQJʰDv63W$må/@AA"hqSE_W%rwaPb^FډMXLcl 㢮jۡװdW;cca. AaƱ9/w޸ ؼ??,vJ4*{~tƅYdh4'aӡJ?4!Gw$t U&ltu1C2c{ ^ӟ~8o)>`=;m1f`WX딈;wGc# OwZNOhXtFs'9NGcx*qXN]lpd\(wdgүW3p2AXJi`-3AiJ<)ұ7I º,tz5IpvZf+Fx;]h9W?*i̶R ;L:o8f9 (G -L!gൖ/[3u4jFOY$cVwhى f[3d`GKsB׀7 1ِE񙭘c~ꂬxY(a%E򨽣5r31a'G[ Q5&")]ݕeώ;`M_?gd>ȑm` 2z ҷ/յܺ}dq:) oh:~?\)?(>n1J*T"*dUJS{(;6b֢ `c򤇲?"jl1(a+9#E4:_|H|Q+i)d5a ZR]F*JBbb cD0J]H" Q*9auZD|.%(1&b}yp$U c,ђ8#zrMÌs3xZ|5r3\vS_B& \.H] QcLl;58^MkJ]q=x>+# ||Y.WjTI!CkSEeKݯ?j@Ogiά5,Q]Fc՜Z986!^W; KƨrY]::_pln{\)Ca?k9\びi%(\.M4Sܝ0Wdub0hpB:q- 9p@*Ob0 jNãm' 1agľs~_z+S9ϧ'Ip\+Eu72q V$c( ,^ ׵SB!B5&5RQ{K*pgDg#K.],j5ND0b_TR֨Qԩ8~tparEC9:v@l|%&#S^:{0+=j?GFf`:/au]%#GimUaSMnhJT8*ڵo/9ptvAP`Tt#; ժjRyŨ 8gI8qhpjr3PllPM&%=nk[3 ϣ<>Qǧ7`⋯`pKřl! ^*0hII)oڤ 7:1 ~ĶM1BIr ^F!%77.VWܸ"VrpZ-CW3Ty-`cr &ڣUjYl>Sd-6BJ1}1vnYX ?V\[ cc IӇcq8. =PVZ҄D-!>ǏK!ǚ%@|ƒq5\ +KX&ejޭЄlzt@8!cEp(bi˧Oqt C(K:OAk놎~.p4ň;o%dH+g.8c{ GlXW%t4' /GNm] sYyZd0H9o"9y\KuNl|¥ w#%1_{t`h$ыJh.z٩-rtM(+D89+:)gu 0n /4fQWƒ؄ҿ_f W]f+,,UyY=B.y\jʹ߶ICB:<خdL]e,&/pU+x\(x*#ںe zQR%u`L*Uo$I5'bb^>E ,`lp%qgܕhQg r?T }tytMm_ eRtߥ-Uz]5NN%+NNܦ C5ji_Gp.|p3k`N._)9uꮭMݱk꟫;V7u]ݱk+u3-bDc)!WW%ruqnV 9/cR4J9UkF>NPM-vI2*mq\ǥ刼ZHWtԌW@34殹zi}ƴ/6GoxtMK焌?;_?.1 qGR$oF CKZ \d =(!GLl#" 9bm'u "XXߖ1A=׶V5wcB1FL9N*9" 9v (Jr$mz/{Ki%7/G-CnHm*m+8$Fo@]y.^v-%iֱ[1y;:^ޞw7jM8OSjtr0hOSiؼ[)цc s0}](=rl0[B ObЀ1P  4pɰŢ!4`%&&4gx  'C2h[KuoZOex0Ԁoz5<\7ߚ0Yk``x[Sþ5Tq \aŋICYSU 4IDH!hDhƷˏ\~/WZHBcQg pkk%ަ:)E\gnP5:FUƫioD\Mԃ!=jhƨrIZmՕ*܄ ? "Eaҭ8uYbߴFUC>&3EE^m7 Ό[9H/fO!,X?tiA<& tHGP|ƣ  RS]z%|҉w,'|q'ߜѩ=|0gFX)-!_~?wd5:u+_koyɘXj ]L]'f"8BCp9Ta]ל_+ zy~Ct8ᅫs_fab^$^Zɴ(HlXo8?~1W1{u }8:: 8܍rd:DvLBc/~mGOc( ;LUlvԽ'!:]Q!HFMW^eu dTdt3o*x>6nWAWY{!s ZYG6xetw/ +ayrb,iJ4r ٹx6vȍ=sKA(CLRڶqE Z#]Mzye"77C6L -yH^p#KV#Yz3-(dp"G0 U8r<Ix)*(#/ dlBZJ9c+{bCrY]aGIsXbػz dԩ2e[ zdV/30ΝOAGvjC xcf,KoGWpID~ 'Iy8A,uS.XS~_oNȧ_E5HO*(J٬ˤ"8LAD;:嵈O8/ճ]%.(+ߵ5Y9( fwx( ,Sv~a{7x;†h%A]Y'X kWxx" iƨ! {碾C" )I*nر^}0F|l Ieh!?+8;c1{C/ZiIH"K5Lѵ`T##+elp&qBui.R>DKR`%pX(9H9w e QJIXL~U-#iSq` 8W{[V: _uODI3s?Cg9w#czYgtJiL6`#Q<28R<8l?.} i덢,md18 ~ |5w>=Q'im ^xYIi[-ǚ?2M\FtJgr9,)ocpX"fyKQJ劾L]0u&ᅳSIrA!kGH|էpLǼ9sh9wmsޘ=n'OYϒ^ ^:ԤTl۰=;܎1o jۅz c˃0Ʈٱ $ۈ16PNQw@Ov GI6oن\v]| o5^J@Wvr{f&ݛM'LLefH">ϙb!;B $ f^]F}>ynữp(QOXw&7ga8q32޵ %ZxG6+-QR='TBt <1ئc`Mzz{ix PHRG nC"æ3Flg_};~5yM`k爠vҳ?:ul 2!t IDAT8.a+KI&>)$`蝓qP6%LHµ_*n=]>vXQn´p;ikK.E!Gn p4^Io|L~f_Kl z~T/0F?>* } ~KHF~Σ1ir) ń}./Łu1k<8yn ;1m2'nEF"v$b4 _lmI܍3q㟒* AѮZqzv9e{DoB_ nnR8Sء /vo2Tym6mOMu,];UˏMH(CG7hRe[p׬0XQ+}%Z[W+$yȯ+Wع᧟C_]#EKkF] aM[@%lCOҳ(ǔgcy rk00 .13f?:$Ə7Y1Nl/~?LCvZ`nd'^~\/s‹b1OȈ߇]g~e-dIy߁po{˺CfKp,OH"qwvڍooG{Ǟę g돐r 9K},۟gFY¥hgollp11? -C8t뿝T)R> pDv,]_LƤ o$,1q?>y%>vzS݇)Is[:#+B;o#)mqO^XՇ?aL׹!=z[5BT+qX4OusY[ҫfkF;⎇SQ|0w3SΚ5iX[*4J:bNOm쐔m`$G%/[=;o"dyjc 쎎Z=Ӳ+gVbuˉ.ֿs~fVKny:zuMm6o<#w #DB ˌm Z9`o:`2\5(`pG}^Vx@$X4Sr Iɶ+<ѦR[Jr!dr~5*tO,%=ĕʕ7/ /!%`r9]chi]ڵ.ăOOBn++p8 w ! 6CZ8FsIUnәIK5B4f-.˿3 Qa{b~[+nLV͛T:ؿ.d̑X{ ]e"푲dOȈpN`42ˆxddl}©:w\?Zn+hFua2s/|ut᱑1:Nn"x6ۣ!ۈ{w~O`T3>,,Vϙ]zͅߠ_GjS(,Ȍ;s#!*䕅x1y&\&/gf6R\1a5o RN;z yd@m6F@!OYSУ@,ߓO/l ^@5}`T83#yʊ`K5IMXb0LbdY vߌ|-*:9q^XQqՏ%4۰;37Z A,#S_^fzCT`ߺؗ B%*I;SܨrXxSctFzR>sDu ߽UE9Swү~KPbDOkC M_zXy ) b!'F-!`7NKz kzMgE}"6[nQו$/'3R;f=g>Ċ}9{7Ԩ3T[4;Aa،8x+֛W$u%զ#+fJ^f6hcb ]6,F("OjR>.KρKл3!A:sr%P2:9OH{b0ƛ~=su(pAV .ߪ C 4`0[2 b@ ] _n!  Lã4Paiנ[Hr_YW%%%Dyi2;Zʢ"XZ6|Qf{lR~2jڵo}nn="[νcǎi줗xBD%CGU-2vi2> t#7-UTT,U_XQ{r^Zu"<dc_{zis4doր%qFX}#j`r 4*N:C/Rg>d ~ҏ03.l,D |zۈxR_ AtKk߿Bwo<;;2.݈) IPcP4 pwqF`l]4J{-esbI*1  މv80PE*ʉiSӑwxcƢsSrӏWޣ<,Y˔i+hx`38[^^twiM b@x GQP~W3Ze6NTȑ`\b޴pL>q'#" 0T̤UbbѶV|NηM( Ln?4姥X0jӻ/$+tCRM9@bElPqs0 ū;1۱>ERkrQqsE^=ΨRBUE-y P ֗NK4,_9zcYi)pÌY@R@&ڑMD~FR F_Q"ڍ!(4WSh[5zזJ^3:H\P Sv ![[aBo;RbW-. av$ 8?gVGq~V.*ҧG0,pho8K$ ;8tPU@nQ! >jRXYO>F5muY%՛-dUqwFŸXrEm YGFgSw M]gQ&u7prf,yA&%k"ʔCPs ~ڭޅs"^ [WAX|< `moпo_DtIϽRi4z2 :tDn=_0Nl4X|J.] ( Qh=?D0jx|\ v9iڛUАj?NЋ0N? >[}חgC.4d9뷤yԪjXìw' }C#ciYb٣C{!dsEP>0J#7G0.P;ͭz." RcQXTαEGAT9PH"2T Uy.$l9{2Ǭ޽'y],*b%"aaߠvW/B^Q#6u:p ΘIM ѽt:i)Xˡ,n ꪋqnnVAAE6 cv(Vh\6!& e1x0v8s+1QI-Ci38gb;O}\  B~A,}c׉{IG,iS ?aE8|">9ɉ#`2}9V}=ZNCC$ݏc/#ȯV u)暆aK9 {Mϋ=-"NRFLRڕR"틫 AB`61۝aƑUWn]Bਞٴr;.Dd@Nΰs@ dzm!s#55Ec}Mom='R.OO?c^MoI\wuc+>\6@0)x*K{x;Sѧi6%']ڎ{nT5k9oNކ*tCBL,F y[Y4yqݱ;̈]v&6 Ρ!)sTdc88y=q݉Gv5ud\qEppەlFJm~Rm|^ AWmEnۄ<I9I&o c}"&]* ;#M/&bqS-;G"T-ѰsQN8ȶmQEh:ɊпܧՅDc=lFpUCGLz @824,XyPE\*%2gG s6<IlE7{}—fee[yiskw! 8w7RS&"gDؒIyJέ3ґPĚ(),F IRՆ&0XXΧ‚6ttF4>^Ȼ^]*rēBu)so-D7Ng3v")6bylA\r0گ@r Mt.@{$ûa! ;uG*Uמ9p& K[xc(L+p惪JŹ,8M̱k讇yq"Ք$Yno|R`OcSiisWa9HWm6Pbc%~ 䆲!{&"LRȈۏ}kLFRe/n RϨ6'\0wRmyz6=K8qȝƖN9!̙s$$$1[Ȍ~ތJDeT3Jk2ڃx5Fv%AY!Of6lo!of;mlNy擝R؂+nūX\f_[(TKO_@9{6cz)Iȅ&zؑ7jn7Z3 z%sC˫]5c96(K2;o{JmYTM$GN ;lX[:v "勾>ܼҡjrbnWޞMNeK[gdl\xctÛ*KF+%q;7TɾXHy~h0?x(㒘%}INOA:@Z 9*2 ٛ|-l]pӢ\lusRx=1<6jnoN;zKg{ƒv젌K/1F?*Mc@Z R8jySș g?L, >u]rYmz|}'@GBמ3޾?轩9?&z8K4K?яHX IDATۑ8 NCN '&⫅: ֚"r؍lFBJI(;)~Lpow)pnwsD2}( ҉nBIc;G~Ȥ_ׯGme{kZyǍu&X9laa&{+A~N!Lm@mTKzr&} -) -Ih.cKvlyrVa:#4 W_8Vd:fRg#66Na&Lp吚\u%Jykw⺾ֽ5nkӪ\35>48&6_^6c4/PJ er ^OK%18XD|'qQsڒE@Mfi<=5Lx}fMLқO3Ybل6mJ.&!'8䈿 D%/ҥa^s/Ե)um~пSz`p'a]ʉ$fU ]u$3#a=$B=# +⧂rhߊ;oK.97O$"?sfL&ܮ)SG6֐j. 9tVlCy! jv]i$7g֐ hn#Hn .j@"6Nmܲ#uU"11MlijL$vT_ ˬ~7`7w...Ua_ Nތ 7c2py3C\G L gحZj&a I5`cߤc(Aףa_ 4pj`7ieh`ף=ý ܤ0MZ1b4p=0hpA7 }VX \ }=3kMγb[feeD ,Rf֟.H~~7|/|]ݻwe4]hI؉HR="MhRù0< Bii 9Ν;Qw܁o$uٍ`N`-$ys5j4x|} 3/O!9__isIYx YAپ{Lw/|I#ݜQ:?vM4Y&fJS13I]VBisVIZ+b d)`rV+0M-,w״5uRW2*gNxq L6*jUK͘ ǔEY|y4H-CC JP4HP? L;kўGhgW7פ%sUrƄ@3a*+AXvx'#(&Ȣ`' :ؔ~*NިF_}ͤz APѸJ<ə~ ~+T}03W'cv2-I5NXLF̞V ;F`ѧ]]JH>w<]::u>ESԖbKѢ$+=$h,0#tKIgCW](oOxy&::ukkXxߴt^>"h`bslٲ(ehzh{˖,OwAY&xu<33~|pbcO !7Ċ?[=YW}T $}sTR"a9\/m 'rqc Mby|d|7o9~>yBjp$GroM ]#D3ﻤGݦH0f<(I*yVLG| D>|ICM1,"8VDLγÛVXx ?߉SgKx8B<2$ {HbuAWQ.(@baY9Þ.STSC^ĩ>Cۈt)Mx/1ws~FǨ>'K(R9l7]ǾG6 k |3l7@rJ ܈~:d bv޽c.2wމ e9^sY4:JlA;W.E1mXj)~a (lsϺO]+޸l#QNR?AT;삉{8ƏG-z6!!.v= CfLZ%%Y(9#E6tCOW?r9ºCMͬRmCPR#A)W ;Yr&GIv&rJ8#/QGrNPc_w wBifUU~>w;Wt o7c`=HIN*u׈A( 8싎EXuAd$cT!+)V<[}4 2nBx_|g30橗a_Go{3%+G=ɭ>=#JsR/5C;@[o*f5JI2!rC9z3hlԱVj Y"!86 8R;{ЮIc9m\{))Μskk4DBlB&=aCʔVyFYDy8u°.?EmNDqt" wi{EA^}}9߱e--Q‘VȫQ^A % M`rl,l>*щā> 6oݎ@4aچbޭ AS\\r(:sA|3} {-n:ኋyvS ]ѣ׻/(JΉسCxb<6,teQ~4lvn"́<f$'$I ߚZeL,!a|Ƅ ic\Yz~[O]L[D@O(&ε\ YeRKHm@{<@o/sRDiNJ\*>(n "lCX#zoI9K$'".W?ݎӦb*˖,ѽEM'Fi]) ~) ./KbPj@X5i5SU.E|Q̘$NT)+ ,rѣT )Юk%]GpC22> O_ջ}m3kq~47R*RA,%>{ZIGT}w4 ]TiƠRԃ\ET(!3"*dxdr[Z;ؽm;>-j 5bJL+D<ߠao%P @PhscgC(L:bh,jҰ4w=_ G}" 2XʩR &j֛[w.'%JbSD},b9yk~ϋsiE\WP2rCx\BxLDF|TnȮDƥIZM `c V6~}b{0pKԃ0Q/K`f8?\UZ<-&OcPjݏH *s9هٲUV.\8ed2~DS@B_I~"/>}z{/~Eq`Uw}~MN'hhq9ne"]$uNdSd<.fJR,f*OVߛ1q1qi"ÙC'PΧR(P=(626lNS!ђҔZ2%kuVK?SQ#J]>M;%Ð0J?.)-Ebsb~.6^/$N'6'rFJ1_=B%\c+\%4S;u9_ KR,S2P#bj4 Dkrn*<)F Jh>o( ˹T E:q7#zRZiGt4:E\D+akkUbVRb3dElh6֚Pxٴ(k [;yjZr-5V]*2>Bf#O&SM-X((鷍Q3s O Ih2aMH7scEIzz7&EtL%)_yk/蒝T Y v-JH(ݰ~J z 0^{ | TqmUj䣪QJx!<{BW_/u)=GBynڄ"BU#l'Ca3vp$at/kDep8$oXb, uIx)a7ū> =Hɭ⢚F%zkcwHv͍dUH lѧ,XO[ Uյ`<>zk9uFsptF~C_t D1{H%Loxyz®ĉQ0~̋N 2XQ4 DPʏ8)OffaƎ#Bx}L 8z7i {2=C~U9~nF=!0:~kk8:V?Ž}po5YIx 8 up%" ܍I+Xkfa}P!xƼN߁a¬܏+0|"$=_㭈Ll[Ua:]`,3Qq<!~,·p.:? 9ݥL0]EoҷG^Xt9^Y>*ÜK{%$کc 18;wg7[$FMb`90sSc:dPS]_N#"*;#øL=?a)T8<도Ug Rv7|)8Zߗ8^[3! 'h'N[[F+BW z.c ggAX:k(BBIdI*̘ V[aͫbO1b&\鏍Ok)%ط%w;?+W :68s37! ! $88}Z;i-h %?'^6՛0q6"{0Db<A>ڎikBݛi\ں y(wׄr&w!T5IJe_ BϱKabl)&@{Lb?s KȪ;6|C0rIBw;mL{|9L~Nv#O hS%,sa ǸWXfv;ys`baNtܐy>t&N{ uE 1jhXZ`X0;%4 Eƿ 9iHxJl1HRǾ?90JVY?y.X%Qcv$豱7]J8Ca <v!v 8zt#@C"#42yPRevCN1&C?" R XHb;΀g 5Hˀ<8x o^'JE|X*!v4ؤ)k"+(  TS) A*޳?T2V "$kS2bA싄* L2!ܜP j(M@@I0Ceb0.$ e IDATP`Rf^9zji*m7 |̤8&SQ_Fmnòssљ Я; > pؗaLh|XXv1mt78"OI2VC?cFcJcr> ]68eH&@FUYM@'6-rs hxD\IF Iz($ƕkh$ Gң HY ;CA2FШQi\y|_QHnS;TfP=U74<cu|,霨 b%J_4rf8 Px*K,rI#TaGzMOA'8bկ*= <D -$$NGWѯPo-g"ZXL* ՘[q۸S{@#>V~Ũ'E3e~eҒ/?\Vqnn& KJϢ @`h2Y&wՕ~u2cYy\El+DRi#˿a\)@ϑB~`ok#*@HXQ\3dz̦p%4M=b2ǟ[?A2Z{s"*&.DLne'_, PBrTs 2dd̲ƿnpW~r #^Us}òJ<-~NIfDVS{w{HBB} 1}!?D\@QLܹ| Q*;\z ՝HyD#Lշ䍇qr1s<ʌ֪D̚p [_401P%YmJ/o}I{_S'@> <;I{V 1d!=39+cK $if 5Ƭ2#1cp= F f>^?đMיP<σLY GE8Q (ފzu3y{ M=S>ӣiL<*RBxm{܈Ί撅pp"91鹅|҆<q]v,_$wOV."ЏxX<հЊs0%0tZ*Mb( "St%JiUZRRs``fMN&v@C #Eyu+=Xi-8$vvpWS2MqA.tmbSgYnz,^acasue;p >ؐ7%B%՜5jD,O?_D؃6WiiwϧM2rss:g!>hIru^aD=3tZ>[/S$ k`h?'Z.g x5JlLlOܰ[Qb޼~`ѻ%Nq.31‡ϐKh%=X/-rǙ LqݔLrf#~0ߌ p!,1g (hݦʑRe%iAW?黊t>"Go_9\*nS;UUUBqn4̝a >n6/kv1l5bOPvظi1݇ (mIp#3$J51$5D|ޛ@?ϤEصy=4keYi/?">x=/OWĞA>4w?|K]|i7334x:=fTV{б(2;a }CJ[+6)*!nӂZ=vU Ң`ɛXݿ8s,v&?\Kʗvo=Fk2xW2B91װ(N;3_߻p5!Iw? Ú򍫸 ,+Up/Vj7îI?1(NF` xN!C,#Tͣns0{ ĜWfجWd8Ȇ+.周+/£Q#(_t1áZ%pOM7Cs7,̼tw]y1Cއ'U86V'r#kb5<,W_.؞՗Gu*@,0@*o(Yay&7?m18aUxl  #~ݸ>+")yﱛww_-0ӡo fDr ԑHHfz(wX_jڵ/VAiA&QQb0)Z..~p5Br"5lօ@Xq\ŋǮ~8v/Y[J0y)&YHsqExkg>px_|a"eqzj-ٚZmBp',>_4֖ ; 5#L7.""W\XHC>K/$8tcXJYẎ2TpJOn;95އSVS\I|T>ԉd@,TX|z+֏~:3MYM]Y~ָ%6nP(2 '!ټ|m|4X +EMea+j*A 1E" EJ޴iOTaL\]eوO";?H"#eq%!D@Ȯf8bռY}Cz`6jDcߺO 0!7$^]Dk r}(K3l|q W\ Ɏ17/P7v}GgckoFd eX]xɰ@~AjXDM`@'l_Ζ p"Շre*Fy?;>pTvƷvz{w^fOva\{yobe5iI^j#"}ԩudzip3B[`nO!Vz nAfђR zl*g _!8|.f }T]^k ٽAfN AH1!rTmopF^@:I^GwZYeH$4wWnbP4fAlmڥhZKD|ͻu_-w]FT_?qhXb-IcfEu8U%)p7 {Ms."JW3]s,=|1}jٯv162bFSx;%qbyطǖzCbʦaSzLVtXQ-ҕH^zg@3lb43*{fU9+%a` DQqNr 4.gjjQB RzZT\_ !@c5G kha+++qDxj'&'rM u٨VLݰXYKO hֵ022h|RUbo،\Ҹ\<$jپ})l߾>I5B2&U֦?|;++FKr(Gxxn*OQuĉׯ<777wxyyK}{kG<@3lJ6\F1Q}Sw9kj\oq_Wʏ5 +"#11_3cb4pȤl14w4嗗-{r8ȘR4npj|}}abb͕HEbnzʪ>&? o36ya3ss3Y(\mm̝; Eܜ3lFsm 2V`ccdG^^.Z"uafx[rXqut233 1OHJJ#F:1J"֫zbQq??Y󙗯˭]d @ [[NZ^#| G]㊋ }*mVˌ#=]gMk hxmÖ! 6Hr9B%O:Z1BEEĥU-=AKS(4K5UxkUG~,k@"W<#%W/#P&RY^(DD=DA&*d>ÍH!H g-6ni6IS-O{l!ʲCr ~dT<~r ,lapvr7"<=ѽ+͝sapn +:ٱȰӣcѢEtJImӗxwQ%jg̨OB.õp; řw}#wK:G 8[ٺ\ i+e(C7=2\.f"#8qr"#d޲g\VPZ|Kw%ǣ^;leFC&< nsDlZyaKsf2Ԗ~Ö%obvK/a|͆'MĠ#84*3K_@[ a+m7654Vlk@"fE%YQd` ^.60&ؗԈ GTR&AX =DO$Һѩ(UֆSOLPa4eFccaEe!"4!mTT0*CY׊@0iPi 1Hȩk7$ F!\=֬.~n$\RCO[fCqj+o錭AneLxq4: Jpu"pHJJ hy)",0C;2ihdЖ`T\:XGtOL":FFT+ΣWF;bOq+6j`ݷ\P̄;w)UQ!uAV,Q'VAn ~g$__rH G-.yK 6KAg-Xz8)̐?'"GUji@](#APעDd(MGNr* 4'&SP_M<"eV|O"% .f0j!6{k{O,ݼƥi(Nv"4 itf| Eɂt\;ӡEXa+|PA.8t Bstp/$*=2r JX*Dq8^uA[;{I->&E Զ5qHF@Ϲ Utޟ$2lVypp4!ѵ xpw@ᱻP0?EDE7p=0 +ĨX<*,h\ffi'õ+8ZȩV*)iGO(V@;g*+)@I!Ѱ3 6Ft/aȈlTgy5,"#%R_^Qؤ\^7#4ۆNtAq!TtoM{eDOZB2@>__AT|>1wXآ22{`G[b ,>=2\S%mDC1DJRGnlml92*8t ihhqs" JSׄ3r%ryKZO%w}vl*-<U>pF#ʸ,%Stlƕ0ZT]Sh@"fSP%%/ tm3;$ݸEoϘ1y{’YJٴǘG=mQ6my @j:  M3'K89(VuR#]{!IRD"#4,eH#If?-#-ah>Ž"N(h6?:b@W|w ^].X[#ړ,yfYxUM{*{wPqnAk. u/j<Ė#;@v#2H|gzY篯պc=tHTgpqCF4ns N>nHGqIS1K`I?}oVk B^Y)Tgnjá=b Qۜ/) +E*>vxZy ^t&_M]{}Gϒ:%43Ne ‰38.zMY )b?JAwwC(%"HBC1uTrsi= 7zo;Z3g`ԨQtY1Y'O$ʪ]B.zlYsȕӅ8&zi¾sp'Ff++E8͈xx IDAT[$1)ΤNi؜iQ'Qġ_ =cY@q,fǤXC%-R<ԅ@5lz~&l#2DkEh6ĂVf83laOhegHCΠة %Π~8[gĬ֭[[]҂3€CCC6c [l!mu{*O@3l8pɴJ֭[_8F33x*;\#PM\&?ݎQꃍa-^%v[oK\qi';r=6S=[l\FfHl $0O-Or F]ΰCdd$1op=c1w}k[|e"9b輒2zggγ9C@kMhC8%$BjEEń5/:mvRR"w ѿmCӅartSjc(WS`8ZB,11AAwS]ރС,PaQmþu=29FaۑP&oaaA11w,3,,-* < MLB>:eFc5 l"06]~-ryBmjʋuT219lVXW?I<͘V:[b ϳ+6GE)F%˅ђM Ҕ]^lj Dj!pI^v@sEHEČHlP|¾MTQ^agf5 (|G?Azly 4]2 UЎ*ڇ]NKh_5yF221n\MuOq1X3^|Y5)_BD9p~AR(C Y;tBAL{ BRn$&=?kkj"Ö~`U,D⃫/Z 3|{& kTt*W Ԓ’T[_ %Ֆd! ta%JUHOA6{h =[`oaФUFC,n/!mz 53udKȉyDT]aB j]En̩q/WmSH̥E|d=JF|J& ^X@ ]\lϯBh?$ ghLFbΜ uR@vr,2K mР]WZ(#w1y/$D`%\`oe]uʉ .*1YPЇ5,Mֲe7'+$]Eb9"%.\L5E|'‘ dG]!}xqvX)CEŕZSd [X{O yUP+QEÐoecpY>g@1 ϣ]s VtzoƿDEGCHhkQr1Wꩠ(`1}j0V6I QMP4gI:lG8:\5=_e>x*ώƹ}[Ϻ#Rwmؕ8u"NƼL!Wbκxun"~R__s/lLxQҖEC)W瀯ƒmW 06U1:zܵ?$@ $7)xnb_jrL5~! h\KEg1.YN[m-`B|zBL ^-rqbE1!V"nhzͭ+ʆvk?K%!".NNaѓjC=0tZuSHW!p^={xԌGҥ4*("YIUDԣ!DQa.cx8.r7ElP6Cڻg>- x酑XdHuGhYc6Usne8|ܟz6V2Tn֟>%!CPJiO!`\f!?>7= b$zDkĎ9Z[5+o|K?ƳD{Siݖ[#|CSj^9!Y6 z`4+aԭ?fM b)XN␗Bs ڟ_3!4r<.򡣭̸(sBMke((텃Ēk1{6Oz7;llЀx$]RgQq ԉqt򇐡7|&5 m1`<\OTZZ M:oSsذ^ 7nV_o1K~wMUo۔16k\,Hu``% F2ʂ(m&CgH"##g1Z[̓|}#TҚN))ZH iarbSֲ,bdrʹ^byyh^}}ɦۚf\3/)< yk5蒆Vv 2Ri&;#̋`A4yk5rcl! AHH0qC Q2p}uu5s _m,Ԝyu#`rd8Tܸ21ՒDK{Le-J>L p\ӯ~Ȥ%HFF裏1h ΀ BZZtQ#T3oc<_-ݻBT?ذ1N4{{"TԩSO6nPԶ9r9nF\Зi3<"<6p !þޚNan}ug=˪Ue01:WB+߮3ds`X{ڨ5*vO&ψXg;ڨ%6f0"c0 );staNU"kO[..3"kbD+j%/i t9fL Ge°q-]YY0?]'wk< +U4] s /q`!/ٯbp?{48F{;t-?=wbۯ 5C_X uwːv X|g9uT1r8SkNQyӥHfU(MĂyٞvח䂊 %7f-&:g/AP~2\kɝ;kt|$I٪/ b!kX!;T*Ctt,*t1v8AVN?w zeZJfG`9p [ij'24U'Y$X-aW ua xgY,@@S8*(%r.ZYi1.%pDEQT=.OqA"*5SCU,<ʯ1U4^܏rde c=-j ~k~+KQE^( :֞0}>k6RsEL -nYEy;D eH'.:cA1 LnyHƙ}q3'kݭ֭p:daP~22p3#ȿd_*q xkXۚuvH|78r)Dole~9lm(z!~qadnV}7g<ŷ?x.8ol|ק ~ۇ}/0}D(o?N!F8{9V> _̝C r0eulؿP ıa2~j5yO:7VNu呌mSHsE%2gP L p|~aYALONX.&. w1K),x\ǘaJSbL0BZ*{f4mc8\sbXmw`C+QQqWϕ0l:u~8W.!-z "=&3|{ ,$ r-fp&.n%#J 2dᵐLlՓO V!$c˞3ܼNw|p_9{XLQb؛{Ure(CfX~F- ̐"_'XIEЦ힥yB,Bǀ)IܕǷI뫀n &LW0z4:i- exalhkcma="DN5a@n|f#DD%@ DWQJr,"yvU9*-Mx*Ȳ+3][;]\+H :A\wzCOYˑkC@@ly[6_ G%ƙٺa Ev=\ G؆`A@-+n>Kgv`œ1gx04Z<2T۩,%w9 NV,ڒhFuZ@ܠPRJ:c#-%tی YӀRz?h)'縩pQIAr8g􈼯$6f1$ ZI;gh̜ŕ piϢDIC\udްrcp{'@LT*D)qχa6F9j<& NGވtޘWp?zfHP5O=l;\ūPCw1w :Ȱ32l:.\ǟo[W {F)0e@/B=BA4awQ5Jd?PP1biХْ3a߈nx}^?tJ&-,TZpbS8 ODE$ ׼> j2!F\D#/q!nmG`l?v k7`joP;nj?;Y> oߊ{sc>xc:1v XƁf(Ϩx1$a8t3|Ab1턢MBФ@0SyU{Fnx9o?q~p QD+ȣ`P)m61{3>q9x%42rwCg6/ښCX9U kCm>V3gBÍdߌ@e6nì1LMwou Ip^~8>"|mi7a ~,҈ur!1a;qwC7&6sr9Tu;X®Kσ=liND3ɾXA= ^"u]&qv9RS1Z۟Vٹp+߁7(bqg!&fr=ld)6]-OY=h(bt ?k d4Vp$jJFU|8VR7 Uxuxeɻ(ܷ:4'9{Mk>k+Q4@Eӡ*r*I~q ~5 u֢N9*FÙfL zim<5 HdxDCK%ErDA(?ՉӋU )ZtV͹w ]>I(sJ*dꓢkꊮU ss+؎0HꊷH;vGkX|2@[F ttY[3Ry8 46aKe5RIa@QizLJ 05y B"bcȹ0m@k'cZ[P86PfSLN&oĆabW[^UO hi.yؔf}F.g |Dը"Ҕ&Xt/`bzqvbF6?Nvvv8|0GU%#=`GxpIz'"f9i$ ]i01փ0988QZG ΃`Flg׭Y:Z^yy.iزZ5: tY%[Ρaw$Rfi@nRb:''Jr^.\r ܂_buȃgbMȿx4 wş)Xo! zGk@a5!kҀܰ)xbK|B-`;摸8#$f[M٦,**Bp}N.C.Fc;w3o߾xڡ& !LfefOwjkB.Unh w 66O*DD8lm`jj*{>x4 `dd9CC2P%$YHlGb@U"{"YOvaBαXahhxBe[L7(L>l]rRQQFϞ mаðpB5v/^,6D qe7d\-o9aWp φW<$YIL啍dm@٧+i ;' Z@5F =㧁^]Ol0)MZZXȆ %fi=G]n{3.UzЫv# &;9pC(B`P cyؿ/\MUrb0ދɀȈxD` G; hFS^q]'wF qByIDAT&2+51țͱY3lZscƫb 6dDvWaӸlױʮ`bɘ׫ϱkj+->Ǿٵʥ "0y ~6a,gux̝>V0|E8zo<^_Pp!.S]sGv9r%{X[=6w]YˡU7@XhcDTs׃8麡[zLƽ ܰ[]՝*xUe:Ӡ x<@eSR"S"S$B<.-e t\Ġ"j*:pU DY_ahS(Ҙ\?|( -Tb>ؼyAF3,uB}UWc}&lPdD~ѭ.Y(AE^ v\>} [H"+Bn؍iSo8x& ťGxe(;'5{.L0w{UI2%HB @H,UD$"SEʊʇ(b/|qA\)@)̄d2 ΰ~~"d3&o;{){#2>pza͗X=؃BqWf@H57 i3pp,yo *Lqg`#rF[89wߋ%spO'c+xLhg% ̂)#Uλl2[ވů cFǝsnEox0ys8hc֛YȾj<L/GjtareGI7GnW_c{NJZF2u$b;Ѹhp+,]%K0oƥxgBRDs- n0{$<̞:}cc3a )=!vh9N9Ⱥd {fpQUcY˅Dj.Ǻ5`Z\y|,}u_`G^X2j¥q5ñP͸O= cn“O"{0sYef7I}e:y73{vLWk%i$3)rѸ3f<_ܻ+e(Qr#T,u,B5bfs5bڍ.E~pyHzLg64t:1[>BQS2r EbX榯qtt]5hYz)"@aQ)0}#@y6ď+(Ic5._>뱧};T1U\VۍRQR.ALʗ]ϯƵ/ČGhª0bjO8r^< x2!q{n-ZCQ|!gOyB_v1jQ\z$D!R7A~FrNc쯵bƢ[Q_ۈC⊋6C95UA#L.BTĵhh4(MD*"(R./=+g]қSy\-8jKt;ψ/?Lq3t}S //Ac/;!1 =1a0+| ;m} @dD6j1yx{5=6ӆ-1"!};#Ћ[YjE+U7Щ}m50i?\v *`fnk_ȫ`<|f{0zƣ|{\%IZ3\|&K]xD{aI@6 38R0m춎q)d&?/1x ~غ k^:ڳݨwabrkc-JvQ!ۇ*Ɔ+˕@PS^XC^K`^X%'V]`3V7>SBqV̼iryEdF)& zC ^3ZqӦNó/MH(8A|b*bQy-0gkEAmYnڍ3s"I?uE8Z9!9;-Oĩģd?j6jF5s Wp2ΊPpRWa k ~56=geSaV\^wtjdЅ6a <8?Tf-i5c0ႅ1sV-_Bm6+% V-^{)<>D\tvܣ:$&];U #kPrmqh kkW 8bjW_tގ=liB3W(:0"/1OtvW7&8B0qe;aװtd^8&ںp@hI/Q/0'.Np؂7_X-f ++Jœ9W 7.kOM>ɷE˄ovSsQN-Ҋ̍?FmmdpL)Uq},mglB-jc9@;n!V;= Ɍ!qFq .ӳhW <6 =D BRƅxjAwTԓH ;6SXHVM}Mi0?2dSo*ϣ.v\w< {![ODLB n5:qC(:P$gPX"̿+ўyZ%AZ$27_LΧ=tq90Gݑgd\KR#cL$CurB=aoJu"v%YeߎTrn.}e(*Q`NIKF\NufZLf!=PYaz;>6r%/V#itzhaH.mh9Ez @8lW) L3Cf\IךERn݊#G2!t"KI60#2>)0)zCiR41Ƶ-~6B\<- PK[&8hA0YsY ~L%MVsI])FP֗XV~ SqE|fNlζR4YGT(+iO2FKml A zKS3B db,mf:xOR v|fVnJttJQTͩ-tN0o{i%S/ 9YKs sMz_s64~U Nxzm꛱j2{+ޠqSM_3V( Jkg!qr}=ZJe7dy^ApHF3PpC }̾ DFF)ܝ; tiI1PPJK-?#E|3iY|I숈H{ZgB-eIltיR~)p&I0)y(g~L;݉ۏj础;O[1D3v'jl?@-לtEXtSoftwaregnome-screenshot>eXIfMM*bj(1ri%HHgnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE 'U1iTXtXML:com.adobe.xmp 306 273 0 [`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-18.01.1/images/sketch.jpg0000644000175000017500000017435413222767271015130 0ustar micomicoJFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?/էwD4=/ILPFJѝ\0b#_ mƫ57zxPҙ+Ws6id{6GnQ {[Uj^d{6GnQ {[Uj^]u[o5G6GnWޏ{vGd|^mu[o5^zCC|-xWzz46J(7RɜwT $}šd|^mu[o5\M_뚬45o-Cr~mr|뒀m\jh~ oU6ghq}y,簑 9dcx_ ?6GnQ {[Uk5}vϪirh^" lLC kF~ UllizZ\\}[q#;ɴ;Fak^A4uu[o5G6GnW!uSSuX/חNH@q``>6&xG^-cZKi6xIUcUPX wkamlvu[o5G6GnW!'~#u;{i[G{|d{A#)lc2H^y9h/cUF\.iLjcmo'/%bvEwWK~*#_ mƨC󿃾Ҽ}Hj]:L)ckr#v+An76t ۩4?ܽ#hׄۂNJ5`_"6GnQ {[UiuIkXiQLOtWYAlNk/H?OB++ MLE/ujDtu}UN~i귢_.a}o7d>lCVlټ+&uȳƥIUSf w`dmwiMZC?>/uj;,jrJwڽ]e\ QG%oc"NWVyl4Y%8V)c.Cq u; x/N L2[IM;Sʞšd|^mV_o/3gXwאxGMOkcO" 9L}  -z7x@E,W_+޽ѯp?>/uj~?$?=Hm:gIC3F݉Ɏ9 r 6? /I54tE]Ŷ+I$yLO\[3yX89Fr}U39B6M}_Oȳeh[x>0嵷(0q5G_Ɨ>gĚqE{Y,;A'5/gD[7S-cӢqg]8 ㌀kQmWv  ]5V3̰rDշ_{Z[t*r>_R;ouN 2s/N/?z}v:vqs7nLcd-*9+z0~^H)|Vtx*QYw p+9+kxMbH"Cչ{=똹X'V:Ǹ!Tn${c5Y!$wm_~8Q#O:~/k$H0LN=6?&͟Xv孼g9_N9 IS'`˓>ڒxJDݼ@~cdyI$'rYSƹp@ 1o-_$edsDGqpkKɐ.3RU:s3W h7 hnRq ITt2GD"ԥ!c>>^qb<{9eqPcPy.3rɖ hVnGyn1E ќR7q\w4RvwǺ)4#m#sVƚj8g1_>|N|7 D܄ v,xQO> |Sv-zUar©+xT޲:iүS8b<*~{QJu6A#"Rj1J7,#st$8E_5څvi䈩8noƏsգVNOB%)YR  <sRZ~бme9lYN0דWe#,REq$-*:bt]:UO<^(7b3ډې_>㼬, Ys Q*i2ͨ4%تDdp:g9_4wz"Kg`bAPIs뫹Rڌo;]Ŧ:m 6=jYmn泰ե7?V*I0:q~j~}-44,y|ʾl~>&,ij^&kT]ڝ9Fxlqx(!P<~,:u֗[$mt+FFVCHDi]XzƏXz?^ Hϧ/OghE'bĒ9PJ ˒ u:?O[6V+,9*H\h?a꾖_KƼi{1)ntŵ8 y9%eVϣJ+'^/>YԤO[b  M r#™.;QZϯ7%~5A"J~}k__ß|9}\,˿j>}_O ?asߵZ>}k/w'eqrx_Wn0ڏw_/]'Y/ xoW1ޓ42v:I܌2Ghrx_Wnw'eqفg#Eu=tCOܼs*\:J09jg%lSNNfj<39,A9^ ?OqƝ0.>%h-]H m.kA:0v19i:K/`ß|9}\\[ e <2(Я"6ڗ|O ~o;Æo i5wfnxQs+q^ ?Ow󿄼Gzmai$Ley{5H>`05߷xBt ãiyiZm)kX1DYGqw'eqrx_Wn=joJ5Uß|9}\,>e5;37& --t-S^ԞHLm^!s_O ?askvY}S>]h>N5ޏxc(:[/ t `d'  ?Oחi\Ml֌;e<ѧ2I]_`$ a"@̤w<29ۿf߇L_ /w'eqw^<5g. {,$w3r[ twa0STcճD5߼$x1VB%fܪǜ=9"[>1#.1čK{dgƖjҙ'eyKAOpӎlE!Y# &F0|鮇^LOT` H"O+J}ݴ qֺ{J4 2AsңӖD[b@7FGNlϖ^lb3yFA&'yF+n+݃ϧnXZ 09*0#zn90]cR`3=/[02J4U @ "ʢ.dQ)D XsscgHPO@D3:s|K[N6&7tr;ErSŹ(׏/50v AqZo_lt'3Ȃ9oަs/w۾q^->"wLre2,/̠`O= `.fܓvyc}Ǣa\Ϛ] Kj/3+, G,<~uvf otێ[!P@>dM<[(;mVrzIsٸ6+D ±`z=<׃(+=LL;ϖ$b1 @R9g5{(Mqcr2nrk ~D Ea@=$浮]sآIg LdLQPaF9,{Hs^m\73=J6{V8uI N/q#A8ON=kӼg䠽a YjG;`'%]7FGAv?VաKЩMV \ B#RWNV=LK[x4&3+|*'o\u'qnzm ^-n qU(Esd#↟d{i*^yrG<}==)x96T/.YIiaLua4g.9-⯄i2o{zKă ~5ɿnH>_ެTw5&o0X[P}v/I%ҩVTIrw/!qam''pԪ/p|G֣ (?u|9MmwxI'׮VL8ZI |ȅ aGaq[("l/7usKuss{oQx_ɊW99@sI,ON+ѫ^tޞo8Yߍ|qgs:C#w7x`k{_}i'3ZI >ϰͱ_6S2F&5;[;!gW͆eOL*%6 Ol{NZ,u++E;~^ d[~L7ҹaҺIO:W(w͌ڿOͱQI"y˸FTA"e B:ƃ1GEu(e#"$R3E%~g ػRDl  r\?π> Q܅%i|pp59# B+?ß'y/j1iXaf숽Y1/wHɠ?n jXA]9?0#?5\lw=7Ǟ0t/O_}N彎Tm.m O}{i 5:Gč]#uyĒA&`Gɓn*׈kBk_]vVqZϪYV:6"%Up,JJ`YSŗo^KsH_6%f R'FH#T]o|2^i5.բZ4.G$Dr;$M66am%4Ctw]jWπ\ @̇qvm 3?$iuwm-u!4 %\FI8m9t!&۷_o>.akv 4qb2I\G"HQi>IuiSzg7=ݘQMOiFQJٴNV4Cz{/Adi⯉i qci/٢Ǟx~|X-;3j}?*iǛ_/>/Eto]7ڣ]+qivx;b칖866$Fj c/ī ·ogIu^v~#.eMww:۲D 1qKUrʊ^*ַ![Ojz|:|]2ČFvwʪ0I ~\Q\x~}K@qsf6rm\LG8?>9ФA~(*W4/ͣ&e$I^_!B|rE?ПQ_!5?]ٵ~i2#+/hO_( +&ͭ?>K4'/WMc Xf?__  +&B ?kw/_}{E| XB|rE?ٵv/>BB ?>9ТBk~K[^AMus*o $T M`6V4м3dmIoc#̱jo|Rz't59=ڼ  $$Q#-늭Q$ڿOW:UhF4rZv,N$u0I E $uFTdV`Tx4=>?7Q>$)7~+ Yq.6;+_?Qq ?uEp} .?еfR+`-;2G.x-'A] i<_ljr|WBLrrOeM\Nu=I$vb^RBvcs~UGReA('z 쎩O-bXs@9l DU^8. *b8Nߚ\oBeGHaTm8ILӆ$!L\SϽS[w4$ 1w̡cw"?ĭ7zmMQR}rON~_o R7\c'o$}kߊ>/I-6GpapZBA<rpkc!Gi *ϙ3O-KϋTIRZ \zd4d,CmHU#!rdcl:tBi!4Ѵ'$hӆ/8$^jڼ ]8umI";%P ` dbGTu(.ml+yo,)J/Y u}s YEs`n Ó#$GNH;Sgưl5[j@P8%!]n4d4Y!C ,+K(Wov#<]mnmmf8QnGSӰM>D«/Ȫ6lqV 4bGfLu$'=ysrԣNNҝ #9Ύ6P[.\C D3,''98`VG{}:&(&w`t`q9'62ݘpyyaGBk*DCw0G%R,sU=淧KkAݑ*莢{%D$[fayc@ޥ1[ $ȖEA?ʕbE߿Sנxk@o47(;~5^5i+]Sp|$[urU;U~-6epv1Bx ECiZ#)nzTxsmxz0cI_~Adz5g{3kw^!]Vu1WKc=BHI!>nؒe"=xLV0}/>x{{V<ƴe_a 5j_>. 1!'x`YZ<>"é.vH%'{MSC+inS}x`?Z᾵bDH+[o'q(/m6ŷ&?<*)2)+ҒGMxy`[UUmcrAqZ l+ yQ8#>[eE =<: j#DCÓFoJ3s⺰e4{=:MIO:W,:WSC3މ[JJ {<‘PeM-Gpaʘ4̀{"GѴOW\]1{{kh 32JK3bȥz)ieɌy{b.8mDk2+m,,Eq+3o]q3.̀/gPW*H"$n@K{[ter<]ƅz}$y!YbX+0HU^3e9GulȒy{:AZz f.:P,dK.JB[hL9^O2V'-uy,6Ck2*Do2G~k,|O _ah.ܛ@R nUW߈t}&-[Z]=\@ !U*1⦧g=@$0AY`5Ur~fsԌQ\B@.dt%s,{m93g ??:Q[>4߇ YJRSf8??:kEo ϦͥhsrY`Q`G$je xEy?oy|Ougo;hPhFKMCDkb)fOECsPr|#xRtnm5T1`.6`PF8cOMF37+ zw,z u4SnI%cԾ #WkK ]@ײ8Iaq/g pyLվ x\; ian-- Ò| e2Ir9ފC8T |B<5,aUpP8+`QE ( ( ( (rxG5_\۱IL1MT3GҼYoz}ݥ拫D#FRFLldg(fwqY#3D`Pk^,ЂxV_`vs+ I )j쨠VMvv1/r7-z\r썣M'vpa qpe nEtZjS20٭*xœ=;Wk#!F3ٮ_R$G#b4silUOh\k:Ƨ Ƒm`L}O3kW ?V`R4`tS$ w\G$6_ƟztKYzlB lN8 5? gSxnu (6ؘ,0Z'ۖ!06灌ԩkQv3Krqfx{jrH$\G}k]i=]xd g FpN2<{kᯊOOs3hqjve T g U?Ue+i #$QO=+|*Noh/-%Z)e[A8DC+})#,}Jt!LXQzOM|Qiz΀V[֙G4[Ҧ$TV!ݳv3kdR<ƶ6yx{M:#)}<*}#`@_, w9 Ddood5 ,w:gv쩮64^æ[L$H$$Dqu[s)bT1 n b{+(5k7¾!]kP {q"fv_0o1FNN@'`>JzY1kmj=^и*zݹI<0*r `׎滯_XK6;?{$ s9UB=}+5x}:$EeX 8gLAl w-υ/5_'63kp#֮.)D[rU,)?7|_5?VMݞM*|M+b99V k'Zבj YjH"Bcv*ۃ7>\׈,<-}_X݋Wbb#UN񵕀9FTfWS#n-pI'=**]ğy\Ag'&Qcn UI'(ͻtWaI__įOBk ]dT^PwgyxO?E¦+:+х|,bg[.ܨ3 ?>M _|̏a*>Lc}4_*mxO?E¦+:(|='3 ?>M _O0+ۃFΡg54;^x"Ė oTQaN:Wz]XF!G={Ry5Q=3q]D3pˑ{=s֫mK ז _r\pc OpFMq֩Jr(r[gU(TIs?K/+x+y37=y7E$cp \}[MhдHA*u:X,oltg`noo!hT#az@ֻN޿Z78Zgt?罇 #ފ@qta{G]?罇 #=?avP WL0Zgu@G-]3{:x\ᲀ#ɸ n<+oqi? v#L]&=M>@ <W3}mKs%K@HِǠ~ռpLJ+ʣKݠ4$|(?)y~<|GQizM̗GwA# IKkO7Hl)POH3^+ĺGfnV _[J\Xp{\(C`jYxkGn᳀16q_p+VkCi0Dv鍪8X ii.$ۥCLǵE>/|Yq\+ w!$*j~ԓ׫ zΑ]>8X$d@1D7v?C2~*ҢӣTUXrzgq[$!]z{՚:[#`Mes8ٍVH=Kz$c1Wv!r67=F}k2\M7ӡHy%E+x^k4 w>Hc1Ƥ$=GZR~VT"?aC]T yiwvwi#pp?>0OzsQnfP J*Ĉ Y6rbGPs:҉"3 j$)|xNKg%kGkr^/'wǷw/4V!Ԣ\!Rx#F*+X@u Bґ#)?t tY59T1 gZ YtVT'!aA:-,:џNb+z͢ƐЧG}I<%UMF/{ju֢Nm׿7?uF;6u*x$t>IO:W'(s T'q}>y7;mQ|##%"\Igx77zLsOyq*Z覊7C*Jb>kx#m(jWSϺ|qI FP0Tp[ RWGk^sf r]j"Å!̇@8+?-#^4QMKfƛʨEӖξLf1" !,|Q&p:DC|JLTE9CuV MwZ2M5ۗ&]yG9lQ~.=Vy9lP1Q<E})<t񷌼=$i41M?ŰB,T[)a'G5ޡ{xu&spxGhՆ⥏4#Q:|:.{A6[oLBc *&·BoSJ5iEYe6bC1prƽ?M6v0Bި7ж9?ɮ:IQ5{khDs)ya@q%GX m.+[Khc =*%(Q@Q@g|SiZ:t\$_B hQ@Cs+xFi|4[ĊdH5$0sA@#?#^1wC6{ꖱiװŴ)$l0GoCKhټ  #S~1ZB1\1֥K(pcS;xɓLXj-* ֻO,\u6& p7 pi(o4xW]mⴻn4( tSȑ@˅N:93VGiRϊVtCۜJNP~t-CTѼ[DcHpp~|Y@4ˍVC4KW4x Q/Vf{7=JG j~I.i5yi@@ܬ9%S?j3rx$mVRn|Yrhq]=\Fu$gDŽt/x+J[:yP$œņ ::\+Ƭ~"Z6I>Ȯ&GQ^# wxM~ gAqR}w ?ޏnj7Zۦ7K3Q}@ៈtϋsNfgԭ.d܆*#V5pjiyZx5-9=gF ,[IQMt}kb`(E*ȡ=EyIxvo\[/lQ4+y?He_\:Ҋ5{[G @tW? #Ͻ?PAEsϽ?Q #tW? #Ͻ?PAEsϽ?T>+)e:@p < le6ko4`~|Ŋ/9'+A ּDN)@`1'P=kO4YgTLvq+!FK92*czI/c >wIСIn\Jxgՙ )燭< s-ު#)quiDݍz us/6e۰PL`r}^?>'z!h`HU[ t8|,4,jv{Jqȥ֥HHmdc3[E rďï^{Cݸ k|r]fabU71 jFmĒ9q«8UgK? %dv+gHsCCsV 2DUsN?JF]I&l;KBYhC2yʟV0c77aVԂ!2&9kGc&֍2Il2Whw~Q@Ns='Nl$#[2w@lb/c4kV@ GQϽudnĻq@^ib{IKrs+)]G.u9HQF=Ot*nsJ+SZ's|Z߃kj6i9W ?e٤>;9В# Y-R^ԥl.s~lL,U,`֠GޕߑGCJ@xԀ\AVW1X3רP %td6mzu eeRmY 6+/O'u6=c$ʎk"QƊ3ׁۡ֟ ZiuD#в \e*kbbs^H$=-Yk7ƮLˈ qGOFizlr\xoX\<^[9/m .'qtr0H8Q>@@n88gA|G{mTu5"Bk+gGZQ:6< bgq2=j]ʘ44,y`&-qzTxV 728mDc~G N)Atv4wfmCm`X|1?}Yf~?댗Y?BOxОA>]b E2/ "8<9mƳ176!.L_hNzmTc#ì˙l}E?;Y{(Vo+*\g =OҺIO:Wu+'YhʴM8yq燴-TcZ#&@J _c6ҼU/o1ZbItشQ[]Gbv!n%B ӼI/gDu?~^&y'3eO0 v'>$x-Zme7[J夷HI 8 pHg!VPW,g$cߠ?֫JwIn]4"(mXyG@dS$6 piQEy3n5LL[hMzY_Ceg"(I< oq~V-ӵ qi( 8'hcs8W~|on"|y <`pHQ0?>G۵HcuVM\0LDwc >v9$3~8,PK2ib6l(޾ۃ 5#XBET+{?D|A.:KCpB} I",r QMh3ٹVx)W u:;P8W;a׿g0nM\ykZA,T%X&Gl.~BU nd+dQoMiڜx&=) x㊷ okzGEsu%贍1A@+#,p$`:x_ &we}nv$g]WRA`jh~*e[Yì_[XL֬&t T32)ę@l|-jV&tn"OgiLxܕu,$O-}ӞU6ֳ6T9lsy3Vtiɫ 1L6Xdt7NG_HgH$gFm\cN=EKs[I!W`dH[A`L7-½nƇ bxW]K P=yȌYz\FuG'~ϚσŮ_R }[TR=IsILy{[jTg568C?^uڟm<*Hdsr]Ck%0מ=_bwީUv'kq6?N_p`$0Tʝ5O-'5+_i)ygЖX0Jf"z)YZljI>km;zNjlu+]?RշA"-'[o\?;^<1}[KmsZ}e2ɖ (V kp<guj?<3}u+M=ƕ ,K33$I=ICOs̿ᘾ2 hbED'M}9#/f/_L'x??ᘾ2 kh}rG__xO41|"e?צG4ˁ=ik0 !Ui@vZOmݮ6-'A@|Fx^<$s# |w8^=GEf%Q*w<9Qd#WQG]d1q6 ܞzcҼĝv;/ g$'~8fԶEKھ]|_,%ty#;w$qȸo khX85OK/#5[& `A;V.PL %T־.G;Mqqg$a$q-gIPzk#z9„^ UUǮ Uԉ ʕ uK8]۶Cs7"Şi  OZ%R`A\#NMxΔs1~=7@c,e#h)4HGʫ3|7o[a ']r uM^\Z_y1tgs;[fR35wOb;8hB tOx PR#'-ni3 J̪Ć({z*vIꎸ 6|C09Ϯ*O |څK%ΣpQ-NVI>AZj[iфRȹ+F68gx^Hbnz-+m"{qg|湯ڏ*K4ͳb;N2k}WvRϨo-V4~dcќju*F>?h >NRCIWn9ںֈ'¸ .,(eW0xǿ-<;:,_2mO6e8,<'w>\z~u_3kډutxm\SO^c $#{Zçj9 ?t׾ ׂd1#275-/u5qmV4[ƨ3^o"W5_,vjIѯ4[Y~FA0_\s-  tp89{-4d1t_$Nw.__,i"k{G,s:upUQ,b[i}tw9a0 glΊ^{RG2ӯ9<ӈ[ßd%Εz2dP R eX23ׯ<ӣ]*[yL;CtaPH鈌i.gĪ2sv[\Ty"Gk[a9ק* -]fFiu#dr:cAuɤh属KY6RN%yW|uc`#Mg q~32J:9sg&}-C3މ[JNݓҺ!1f9ִSr8d{*ςlź6\"K9zM;_IKUhq@M`Gn󼒡BTU(8|OmM#xwIN-R>IqYa i35'x'( U m\NAq .3Bmc+]xz16+Y嘜L…uelrk!e k_ZFM1NXXUSSC"چ-rU2O@OljAR >;ٖ{ [i7>e3:,ZVK4czbO>٤3R S0(jzAyvw݅jȮvColHpNE<[0c|sZw=ȸn$ k&$dC#+"Ǐ 4\zUھtĶq 5ǖhܡB; gw3$jVٻ5_* 4I0D tQZ].=QI ,w,k4˩X;W5ωoͿ˧m]GeENwH BEg\zx^(!;ÜJ\>>Cev9Bꖛftus"}E@qS@GAgjԒȷ1F3Jc瞞a(4m#ƺ6ԋ Ida$1fkrQvQX8 NɧiURmab&RO8*Z yoŸ3Q^)޲Z\ޜ㞥 WoEl0QKHD6iqAiomDQEilZ g |g[qsRx[/ B52@*A ~i_ um^YmfH +Z(AYFܻZϯ{t4WNi6N2|^kԲp2p2:J+B IM##Vli^ƿ15:Z'>hVaCAq>p \ܓh́2E}:|9/ k|g*j~Wlw*%s;0n awş xs~ZjqC#.BO_/C7ok:Tm4r{keVr`duϻT2O94fncYj 6e*J1 }^\3Tqsy]G1;dƿy*X5kд1Y\:`[OUE?4$cSOHɃ?{Pi4vpvF%Te=?ASU`IxJ7l@Cm|΍09le9~΍lhyr]̉dPC*8*U?¸/ 56 )u4RKΤА~bǒ22J~KOٗᵘ{J}oU3>KFd*\7! )0?Bc?Hu?K:6ukWE5^2⊹"+p%Ved5 ~f2Jwq]ב3־!7vSi0Z/,P Of>S1c 1Ǯ_to^C@ɪci:֑ HE) YJF9\s=Ptv#>8xcc75F/{k ƂX$n8S4z3j7߾-]^$ ~Z9u«0$#!>yk:^M)8at,EQBT#ğ x*u%ɨaդİ,F7QHʤ+:K~6=>9t^ٝ+[oX n l*f; cX? |>ԗORX& tDo&Ak'?|M}#^,ݮ6kOi_hXvC.TxYx{&ua_{/*I|ѣLcTwd.wm ꗿ|!cR49m;)~@BfPtx(;UmC\tہ.Vv( ,RlIπeҡ}-$2\)0""A9- mƗiM*U{빯yo$y \,1(w®pv~G/F:[_ď$j(fb9&*> B?cU{y5-I{Gg8 FeRA#]T(ߕ_s_KVSR:OiR<3(ƹc0DC "Hn@C)築?Pug+c:OiR<3(ƏiR<3(ƹcoğP]t|=$‚'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_G4?)_\1O(.?፾_Awɂy}Û'4?)_[մ?o6a[k"8`]9m8eN:n'=y1O(.>w -z n/vOD9'9ұ:_o%jNI/'h:K,h?vaA{eR;r+ҼI SBuH>ӧȍosq G+$gŻmi=V軚Y5h:xUIvv^fx]ߵAJ76?3cjŏNAiZA"8 ©a=6_*𝎟{5̓E{.A&5i? v촟MĖ[$#|{#AyoQ8sv8 %9l-z6''–+/|TcHLNQf3q̓5Y_o(4Eo U,V+!H<>u :8"MP:IXd\."Aq[ͪ,qXİ9xy |LM['~}$)B<>zTH:CA?eI/ns=r"/P{|ޝkuf[ ]DڶW$ qNx"A$DyH&XߡyC˭2~C|.u2=c',rz-GMJuEd@m ^AxkM$"ߣZeUOqW{'It= ;n==kSY#) eP}l\WxWe w8tW]]iT_$dQ[S.fK\_Vde-y!}0и*ٯmh4-Er2l~׳7|a>7#PV;PKS WlSlH?λmsEOH`ҸZ4ic`$^eiņQڸ0MY״%WɭW1Vs< sO=:sW4}#ށ,ޫ,2۝P|}<^*ƱDqsY)hmF>QڅUT8[檲x$UM2[()?0zttb@>r61O,t4}Zy(9_#*PZWtx{R%LlHѹ`zמ+C3މ[JSψf۪KHD5emڥ`=dW.OOT &Pj3ąf?3bd<+_7ďh]N\l6"'(/$T˫Ŗ_֭m-io.,$lômQ괟|mOQמYC̲ ŝK(2\,g-?0j_~#TҼIqjYၡF]NRXSpZxSՃCu,q?J3_w6m;=9"$hgW@ts8%r~ooյ 2+LԱtgP?d9|1魭{-#v[}&lj2YPBY?Z7$GԚHE±HШm\nxݸ [ |^mF]7_{fVEQsWو:|Tc,haT$Dfp]!ԬoY3KtF4H.ճgX!dEh'Q1cYyF=Am(E߻ #ϼkI+-tŎY.`txH:J.A !VՖY!Dr Z-cH.s(@BWm{RD}Y&<&8?ଘy@{B{ٿWV? I_kr\J:-ޫm-fPi1A\r@c_KnStfk0v!+=ck˿(;u>e>2<4no>+=v-bTId6eU #s i5je i< 3 H$2*yp|Osj[K,ok%r4lci"cbg< $'[xYOOkw4v!R_]_\R"Vw F[ntYe#Xd]z(|m"3#GxʱA A*߈|{ z?l-qsqvf ,!~| "ouEԼAsv2 IF*w{O隔--` sǸ2z=[4? Hlָ!kXUBbO65B(UTp*D'};o跰l7 `X2NzW_I4o{/%J{K \`x&?RCl1\Z90> rJkp;NX]ǿ 4K;H,5 ,1 b< ˀ+}FMt{ge|Y|JԵfiD,h8lcЖ;O75&j7SE F2X%Z_AX 'gZ[S[%ǘѤmNO=~\q}N"/Gj_idxۛ V0rd -6%SYO֚GD.Jj)m-FI4TK3JҾ-[[լ/Gl^\6;QL7*{~;pɤɭ=הC7^kR4Ow 1Xh:jx)ݸt5Hy5/ I7b)'pz/$е$mu FRS_L lv1( z+|.zBMTԬɦDꭙn3Z7Ưɾ_xf$YŸi(~>Y ]C_ѧ`if]f9͉ 6>,>6zm;:u\Zj[M  KN蘪2~aBEQXiԵbOd 2x2#xA,N@ [RlK}}{H $8;J~n94ܢO<)oM\E QD7*tW~73%o -* u $ +y{/>ѼOG$6z$Sc|yc8M;1'utQE!Q@V4]V-⺷qd Y9<[̒ 67(e%UI cں008PEP] kIm(.SO\sN}Ig#* b6]Rՙ10 ]Zoѡ̑.y>Τ4)m;(\r,h]."{>Fiи+ZJ)A){p_x?D\,q$p|gK]VX}"~G0kX[IFLA2 $4?xh.BV&2=}k֚KԬ6).}mB>3>FVw_,!lƸɭ="P9#L"&۹`c&˙%EjyYޱ98FuDhAJRw <9UݎZB~ass5+n\C"1x{2b(IPJ$kAF$s5X<]gNA\5e*zSV?7mgKԵ;` 5ǃ`cWuU5+/NH go[$vYCd'-R ?]g#os$6>QկUlϥڦQݑL1`F젎\׬~v7V?wje'ծ63cw8_:\XFV+f⾝.5o7Ӭrqiˋx⸈XԜ>p˔\u#<WdTsT{I[Þ6*Xy.dOru'ڶ@9"oКho&?i\_sUS֭c:upe#lW10!$9IJM ·ki{ $-ō}Hb\K\h9+ϝ.;Tգ׭`h2ݛ wQ)l*^m|6ోTn4vE%8$I,οw~[Rs#]4b4R6B$כR_{<, T1<~VpqO éuem_R1,2ȯm)U 2s ^Hψ~*m[Z֤X~KRyͲ1% \cdoYxOt8u\[=?ÓZE<\1<˴Gc#b]CZmMo Zo$yoO\4qD +QI?(> ~Κtt2O`$vèj68#f0!)3(N5)š;-ݶ1ȱinZmlFB6,Y@q/M+=0Q&]&aF8uBQ-Fτ,b bz5xgGjU#9Awn>p8ZBC jTz:PQo3 {ПPlΈZ^3 \*%tX nHּ7~ ~$]uk;Dk12ҽ:KK.Uw (Ko xS9B]YK+mIzzU)6$;I,pf)T''MteIiZC{k qta U<)VU91.~yǽƚ׆Gy R9I 4*Kߐ3 +|N_|?;-t{m3WVM=ʔ ʟ1$`ISQB1:n'imn-oYkDBUQ381z袛wwVV (Š((((-'A] iuP09kژϦM^4H@!듁$xzy%i[b9pK'#jon^B|Uszd6;qr[3 ))4hV IY q+6tţHT*_&Zyuub:4kkb ]Jjp`ǫԢl)uI`U{-#8WGE2=WҴkc\x]CU:iq’+n'`lSzrjs5&WnG$\ Oj'pnֿ"UJ=gPHh:)Pyk{ֱ\>oZ<|Ȏ bM=8ߌ.2sּ9ǧ#$xt` dn'zxn䓹n`]thQ*brkՏ!yIP aCZ"%x;sd*w0* .d@۳JU|-YnXu_Nk2}*٧ʀ ǞsF3U/cjQGjLG %V$,J\ 3ק\}%dHא?θ;=y0Y"`H'zZvKBRYs}S.F?~}h&@?E,{PO5;G5)`P.=lo׹ĬMg-z"K,⺒G(?r1+ 8uj/ ׬KZox~u re&\Wv%2^C*dnEǸ'_־=u<3p~Z eq8mG ٓ*Ҕ[>oDӭrűݺWC3މ[JJxlr,:䌩^aIy*[--DG/* pEzyOh^Ҵ}oz͖XjQrkA6Jr0 J$25_5`uv 8 A?0Wcm5-"d[)lB<+ᑒ9$ս&/J>cb캝qY"fRBFWڠ`vW[<:O;'5E{ҸTA J!A-=V'WvZƵe? VWXZY5y&gKk=>wq.Na;X<nOG5z>jX۫EolFYNR};eޔlux/oq0VJ~Ыêoea-y[3% YDsˬg[4I4o6}|ư(Б3K ~t1 OWi^}BDVno$DhA \2]#W@Ҽ;qIm<h̀Ic@KJ.2՟q5fIe$kZi׿@R݄)wfʪe l`1-^}cQY.$I6?dyQ!{hepč ͻ{- 9X1=dLInn*L/e]3W>{~u 3x]'vF9'-Ǿ)x ǣ[sv7j,p\$.IHJ*0WHNc)cWY"p;>.xAo.ZvMBL+ d,"+oXf0S[mt)$cq#9K0yT+HHUEݸfWKRm.Z^jPi`bV7>69籩{xL-.,/ʙ_z)9z֕ ( (>|Śn[8Nby͏<FQ9jLUnA;f5T-a[~ܾ>~$!ҵ}p/5j`,V#NkX#=sg 5ѵ `eXL#qk>*k4E&qXms2j ŢR26IZ<3=K_re!fmlgZ|MZΩ:\Iʵ,"!xʒ+|kcǺ\NJ}y+I,lK1#_xCÑYմk]j:=ۃAn?1bۤ9x5oO>X%6O{xz +}NssYEy-b^]sc/xO4淲N<:UOp\rL҅M +6A_eO诘|a85/'f>V6I=^jZ\BDQ,E"̤u_]sN:/&񥦃;KYZM"EM|h+k\>ņ iG+Zʲ*ȧ 8`x#.x/:5ogX.'Ӭ-t +q+cЅ23c*[^̔cU4+'ԯmD 5ԫjI1d*Ұ` #|ĺ3zώ|Q7txuoD%0Y*$D{I)={ ~0|5 I]5tb6DHK+:30{uÙoϤhb;F=姑u,*`(6G5uTxw𷜗76iO2"؀T~f{ßq諊_!I#^#Mj9xƫ~?k{O/T4SH.oW9gc` 0pzpAbq}L@]芺5. .+;k[.]uoFf#^ ToyFwxW9KEWoj<+ sui-( (>OW'*K{!I"RJ!3qһ/]?u/. ՘1]:D! Gjxߩ? [3 c]%uI574Pxv]iw0UC 'd1真*ٮd[ Xx$ XȊ&_|7Go%G%X)bAmt3:c8'՟2Xj2?bP(׾:UGזu+%-9y2.G=+웑0 <)2o(bX_Zj0AT#rsJQ&{3g\>}xy&mI%2[(pǯ_k-r Cuּ Ma7z{H&^x~,F}+ 3WYopiBþNq~=.k$j2O8ZS咹O<^ea5')8q^X^`Q^S<{˲f#9N)4Y\$̌} {ucBy-NzUj*:L@Ѿ2 ]k^IWPџ#Y?c/.iY#Y"'«__X&yfPv5_O@ũxvqF:uc->jm[YXhPf!@SRNJ3Ӝ *rj!uS)6s޹n zŖcJuT .Vlo,d%U=FxbO1RF=֋qzv6'[64q}к +8 `e%GݬmQI'ʪ\*wjNHX:cCc=+OoDm8>ǩ]av*1,y{u_':~Žw{A3RAhRihI.g+O%ƛ' \p˹c_ ]p} N+$?hpFVˋ, gW<^s;4~~L7ҹaҺIO:W,:Wd3du20J__71{BQqay0[FG11\MԲ!c`V?6:xBQ[hsec+ImfT dwOvJfC$*$P (H$5yGѾ-"-[2Bؗ,d̗,9güyk |I DҤԠ&"ƎT1^s( .Mje=ݭIFs(h2DcbP;x8"]YҜFgGNr c5u?-ּ-2,!4|XghSFGA4PnGʬGR3F?/&Ĺ9j隬 sԤ`(A.yU*\1珺3_<paGCI<_.g^0-yҵKk+Auk* )ȯftQ!Qz"rsNFv@4m c`YD ̖Qkjo1mkc4rG[vK,&a_U1\o]ni4IYa%! F ,f!߅^F4sګ9%xF  a}9$v]Q 4>OyS˗2+2I>ΥkX_]iB.,_t޲h]1[XuvY.Hn~tE2\Z6,>⿄z?GOtd}oՀ,9;UEÓ j7SޱMMd1ˇqq(f TkA`'Ὲ> g3Ai͵wJ ܮlx&/g_ l-vij -H2e (f=B0{g5_ ~Q q*lSI .b_)Bʣ%-!:6e&Lӭ` *8RDz>aYZY[0Yۭ 8>UAAZQR (($rG5i:6[HWyT,O<_goj1avo2B`sɦ}{ ׎B3pG1]P@:^;g> ׎kr"|T?_gc*[@j_܏h'<-x/Bt-:ImX,# @bG$\Fu@NOy]x/Bաhtb9 +w\'ŸIsNi-J@qWUEx?_k67vi}tO è5e'BT'ʧ`#D>BӮ ҭ^m| xD̈́U^{ WGE>X|G@fmjEZO5]6g*]#>OGӞ/ k[4:F񤀨0IAb;aEc־xĖ6:4]J)E 1}V|/xJ]6JyᠴFG;8I=ɭJ(0_ 4}.]OR=܋0xW7+?Щo& W8cI:hw *k.?T?]7_%GԼB:·Hԡkufp[Ab0Y7sI[rf%Pr ^iBdb@yχgmu=FՃwmĒáV xWwTNv|BVV,&- I$QL&Z{ohVjRHTr ʭY> i94}cW6nb-1scWQH0a'IKu5_[wm#Kx@.KR'%o"z7% ƱcvqshqYM/v>k Qw+I%h䍜9%&I$oSZ=ͼZtQV a&g`Ud0 +ЅH~g#~ ^xoš>vљ8rAeʀpJNs_0ދq63?0ڀ9eNZHFS,5>ў7Roq½QX7VG{[ jp!gcbXz!d9 ]ũxW;AzG"Imml$~i? v hMKp̒˹"` ub%UAd#h>+WZFۏe@秷3!iE *F:4<[#GWTm圳ڔi3E<*`A8k隞}T3 FBF:BFVޕ=kk6eb28XkEXQnn#n^O$!QE #d?0+isTv^Gž*Э5mwD7hlfVy%w**w±mYt4qfY08L{jڽ͆j~ѭYoYV<Cӓ^Ы 02 2c`;wqT3Z3WR|w m&[ɐ7 Q8Ɱ<1eRC9hQlR+®Dp8?Î1OUCp`9-康*F=W:ՄTm)Ek^4 EYe $yuQ45 e"s{S]b!3۹ p1;x­ܦѫ4@0rEeWmJFkFɹs#zVE:$_OSYTe*I  A~@DH'$OŤb[M7Њ2HFE̒TgU+mdr@x>-ͭMPvfUku֛)tɣ($$x|'xћrÐƞ|YtfT<uOWѵ^'Q,#yPO?mR:nҙS4n{w]xyQ˛{]kiHGwogd_M-8t4mC澨kOӴ[rC>Tmot?U']VK)2=%%5pxM~k|lѬl-PTOOH(@}: aq ~> ֗6ڽıkl%Z8,lաC$:kO٫W;qYO{ Ky?3`[ewrFNe#u7~5i^)kPwSula:ce˂AR[BӴ.X4`!*ȡ(?1 XKm-|D߿"<TPT$7=Hm!@m <_Z SQݠ划pr 9l]{_|{e{[\ +.uM+_<9{;aKZ]=2yNL@> c D] ͧ3Y-rEi⪣h.NRT.ߔ>{[_fkjJL@eZgσ~|)LrK6zEO 2" Z k8,e݉Ҧ및|+ÚV|Ku9v4L-_%!C8*@(?ɼi7^M%;:HCbGv6OE]kwj%eBve,>E'x/~j[ֿimnIu24ʢrj!ی!lB$*,mL˱՗*(H9犻]Iڍ&מIF~RXԓMZ,&ʨdޅ@F#[~1Ͼ6˥*|/sᏀص:䒟4Y%V|W3E}-spmY6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?UJK2w\Rp ''&%2S?XTycOcJ\GF##Q@<_|l1~>6_jB{nť֚&YsߝzOcJ\@|_|l1~>6_j}_ҿW/$QOcJ\@|_|l1~>6_j}_ҿW/$QOcJ\@Zev均cλ'AכiZ. bhF@Y {:Omݦ|EnFy*o2z ~kk#SuZ/,+R9*d̀|SN>fETn;qZݯ@˴+=ts}) 6ah"VjU6G'< Y z;hyb\,nwCp`p9PO xGoUԮ8k o4ɶJ`D(⾼oom6O;( 099⼓,K/rHR>Fl{+*D:(Aݟ6/i+{GDm`AyXIUd9Ʉԏ@kAvНo^8[V) T@om̀|^'W^[jtwOM~{W\¶ SP1oTOێȮK }iDK0 zU,,uS=WBU\z~G/[!s{&M)Ypyo[m~QsUHRĎ uP%jNr/D;്5]p[l<)ysh8ܡ)'8nτOF]# O ZZLqS.~kTlr8K\Lۉ *1xÓҤc_g?P+:Ucz/#~S_>pR-5 Hc^r}Ozw@O#43ō/P^k{9L>\ :GbC˟%nQlԑd"yRtUb*a`@;sKm:?_YxliWwQki#d9#\b@r __5%{m&A"~c0'9YnLNn^U?%[!Q2\|8ֆtr]=4/Mkun11Dے)24C%GAߎ IOFca?pm6k"aH6*NqO ojiWk4w2[2R+U|62.@ = 9%ykXyg*c0;X#G2@UF[GاtkGdٵLʏ;slq!ɑ9P|>(ʲM4v!Sܩbsv+ogopXp@FD`E$߇YFШ8WY`\b, l~Zuim h!@(XQE gm|S\YOH U'b@,*i}<#A,kº񞙦xԞ'h,3.G¶7`_ |S;WSե3Zi|y<ɉ' `w |Uj!qq\AGCŒkࣞ6>u[HVY2%I݌F5.A<7'Oݐ͍ PՋHn=c}{k~-,mϕ%DK4J!On|+ӴkM6-bȃw^"b|֞[蛥+~_I)'=-ռ#wx6& ܩ2 rኜٽ{avA!S$`m%CVQéRAt4y)4FZ#j4LѸk?' |x{)۽D',qa Y$rri )*#>V *sr;%:Ö] 4Mi~mct7!ch?u'sX)Z7)$syʱ$.>c]7LހfK]KMg&x']dn63_:vm&쓳ʘI=uRU'/{Ts9ΚJڙ)73VXB6䜒}kBH_#A=Xs;(U#ǐB/ 1^٤h[99 ڽEMޔ\<95}b2BNCZ-Mln ~d>?,.@r+n}:u 9><`[ڸ+{:Iw3=/TzxHr퐒=9{ýn-:XPH\c|&q7}WW  #1Ih{jL5($r>fvG-'Sbp_)Qx1ˆ;]Xٿ#Xgo8ǭJ%ȩI^Y |$ jwҌm 3ps6▭|=fݞ"zW|5j4M2Zdw*nί3RTޅo 8[kHA,*OSWks9S8qؕ#KypiZ6d;V*ұJ7VRPY 2pO>&]X2𶒚onBԻ#;9~"oך4#G,Lr7c In[m%%4Dhg;V8`zt1%ioSMG qm`lQИrqM~ 6*6*d S__g!o/ ^|Rռ CSe}K6ݙ!MGH1a@}1wG텘ɒ酼֯!I#lO ErW|!Oi{ I `@셕Y$+6 U힐wyrBm y2$()-.FOGEEei>*5o" }EY’2Gjoe(9$=2x>:ydy|l}O+K [mRtk4c9?:-_ۛj][ť۸;(!O05qhxPmU!E2Om *~]I 8?pP4|jϨ Ӭx[L> i.BB&1H,r"bv0zςtkZOXCȵ ]|@`Go!33qͪL"o%}דoA5R;bf'@?<7;H'-$[JdovIb `e[{aQXcޒG"7^Sl3u{ےy2y AE]hpWqE(=QE,EwQ`8z+]X(pWqEP`LRFpp}rq]iGasO^E+mk%mk%{EXv3k_-jԺSԤ>X1mBF yFOZ׌/˻ VkFPdWoC6f,»4»5Q`<_ͭͭh:bR\]\^.7tTU@';mM.ٕ)^9b_)`<^5]JQ(U9\nBDDW"JT۹z3:2# . ?FEIS"urר?JQhn~ c^$f3(^M.^~vfeM<(n A %H*!BYM',!3ңt zڽu4i | q[tY=n`θv/@}KW |;Ww;"@">su{Spd,cy FN1eQրW& 5XFA,^sG w\G^EO(XZiVo oFs~hՏ]znڎꖺ\͋t%Z) c%*?)`xk\ӯ4CɪͨS|s+ew}j~6~/2k+bb)I28{gܿ8'?ٯAռM{PkQimtK܋ˈJJAt*%dBT+|h{ .h/6odE&rX2z1[#GFm\]Mqn h,+T8$Cr kp_k+z1j^j0k75I%w+nUȧk>ЄD ɽ& ,} QZR AtS`oQln#t"3dWHs n9ڛUHa -'[4s\8 9sRXCJ<;s߈M#F?$r~_1sFMv߳_9{MGrX$%nuČ1%T~Q_\Jc|?#zr&eة[@⴫(,xQ>j{__u~Z0{لP3 ]ƛk'Cɠ TG@,8u fQ c󪇁pf`:QV #.cN- [F֋_V LwC`K3!7ΝkڅwPa\f fI pGM'b2~:`O&1E210|ݷ \$) ^qg@O E2W2 3N]: r*<|@,h! P~̵Ì 33/P%p)= Gdx#3!Ŀ) zJ xL}7Wh$If0T,*gW d́8wgT32FS/`@32LT .еiCn?~g`:A  Tc0 V V O?a8_>2 Cx<ãOwAlSdԬ& 2L&Hz XP3P90@@1 *Ф҆>`&0[>νGHP_*6cW'0iZ$C T0p|!qyE(! (~ڹA3\]HCB<  W^z=ɳE*p,0@eb~8{OHA LjHLl ߀P@ t5=H 숀`R@=@6(e`Z=`È1oz1 TwV7I^ e,@Y` IHx2C ثceoT0C Л?}?@ P (Q`~\F Kʎ Xx"fׯz Һ ČaXFma!Y`?E*,-<\$3*'Ï̖ 3pp,ٓ(b!ᰴ1P׏rVfJO=Wh?~r;d&C Yn.Ϳ1@fpj`5eac T13}Dv@t8rXX!-bMDXaj0@On0m23`'ZES#;7XrǙ /<6Eex%|l$@u<&ZtO%O IS$Le`-9LG`:uó Tڕc`Xs~oA55dcb$H&~LU ~ ?X>3}. n BF6`nCfb3L:¾.! u >a~ï?@i` |.`7m:'')dG`??~CSݟf>lbxP[ HԆ3CW CA@"kcx./`G+h8eap5b3[o~b~)}=?헿"ŲN_0oo̼Sэ 6K8ˁyn@KB)E-%!&a 32p, _p߁<2@C,`]AZN=''P(Cb%ûW_K_8J:(_bx k` ?dge _|Bu8.#y, >ɐ]p=2 PGb8foƂC_9CWp ö:1|<6 KYvyp;0C 8QVQyeC}>c_0ٱa ޾v/9)b b%[a -x=2vA] bC^3pӘ,CZ~e>Cʼn vmax)xt|9d P=pDW6ɾ=al ggdVo0to,~B̈Ӳ?^}xpSo2T˾gT;/ "X`ͳnB>a0UcCݭ w2ۯ?aXg}^AZŬy bbE8i 4s lN?čkk|\Ч1ai3$;Cv PfHSmdܚe_ 5 E R++?<}o3GT1d \9 x0N H  ޿PlOMϢ]Kn>i`}n€ (f3qM=yTnj@;OSBgz 5F3Dz@E5СK2vJIENDB`fotoxx-18.01.1/images/script-files.jpg0000644000175000017500000004054013222767271016240 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 228 251 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?B+hQFqciJ63T5]\&;ף<owEzҟCv~l[\ѷU9P9ݬߛ?1G# bQʃ# 9ݬߛ?1]F(99ݬߛ?1G# bQʃ# 9ݬߛ?1]F(99ݬߛ?1G# bQʃ# 9ݬߛ?1]F(99ݬߛ?1G# bQʃ# 9ݬߛ?1]F(9;ݬߋ?1Qj1j%դ`'Kc 7l19+`x a9mJQB+EDsc"kKPq 3D8aU{{@h+6v Qss"K-Z-STMԾf1XHWr38SxpN>f7D4;[Ri1yF+^گ .ۖI߃un/n.aqV@$eUr/@+msW[5&|褕g2Uy]r՛I?ړNERPZMK,$GFƋIA[=MfFŚEpgf0lZ մQyW1$;\gzzORzėځd0io'phf$bU=㧊Bv{pS,禧K9EZ=Ηb /Qe=5?].t`YMO_r /Qvskzj4`YMO_r{.(s__Szj4]qF+ ._S-K1\iu( .ah:\Q濰,禧K9Giu( GW5e=5?]?,禧K9EZ=ΗtKV'渶xw,hH\h_Ru;9V ltq Jۑ%yU.NғV*<ܼxEDPm( lH]k YvM:yw{\׬4H`R 'r ڨ88F{ucmoFFpqXa~#)&NH 3I'W$RxI| h'f7Tpfdq¾q_c*~uy6O݂@O=]HtMIᴖ{Zhy2ϙ!z_N@d\.|zekZzUΓ#j7b2ay7gr+=-9/ FuΝ;fH< (5FIdm4y%_Ӧ=PY2_%$-zQa^{kd,o$I" [qA95vO]ZAGbm ; n!y9A}@.-t۝>{kF&Ur!(>f`o hc.Ek+6Qڵ-Мïp:=}7m6Tq^7\B|$D6O4=}.{~0T垫mv^ѠҤ#A.,7儮ͻ Q5W0CsOʻKY|.P_)-VXHD9'<`?V-.Q$1YM RQFIWxBҴt_ gr40X#x[x;Cŵ_`{Hne`^OfRgVz<Ӹ ]ǎjZhjw~֭bA#IVVlS }oc?x{DytڮM3[ʂ@ߨнo;@ۚ ?55gCK>ߨнo;@5(Ϋ=iBX hhވ,U*Hªd暷ſyqv/nR!W-.bK*rH AAPQEW- v S  x fXvL.%wIdպ(((V[_Aunxá V( ncۛ}2x5y'V袀 (![k"JIOx |Q1$Q"(UE @aO ( ( ( ( RRN؏*eٶgkP[d6,~qE-\YUI9'88]KSXG U2I*leǕ?zo5-3k}ԭ(H兢i\o!YOA898t}s_f˧mxxX+Iq yI!ʆh N>\֙wokѵC'JZ&vO] ;{2;Kgg$V' TE/dӭ4/i/$$y\<'li]jؚR$/ <0$gk_6~tq6l!M&& 8-mtcw7luA^i:mĺ/pNŮjHPZP IŷˀFHfӺLu"{+MV{7y\#Inܠcpzdzs{I7~g#۹sd8}+޽qm]ޟ6-]L&6.[#oLVվFÓ˧jM:ʬg ]r=nLF'LXq21 -! NG#W񎍥iR-r-h*J;g,!g G 汕İgr.ѐ;oZ,DѤoTʎhw ۼ'=ś`cOQkz+GSV{nW1jW1,6:2ݪH95fLAJ//,!r^/Ʌ J+MCTQiҡV&=mC u)>٬ GBG'!%5{,AX\ckA p 5m$Os\dJTmQ[3(rS;գ^_SSaPWhmn`!̌w*s^K]B((+Kxcy~TOA0Hp~*ԫr֖qp_@7lNPA@cF#VQ援7 j?ұa ?I(;>(.y7 jLj?O1F4LDR:YTniTN%Ko1fāl[b ~1RkbN(&kYbdg HM]5 n 䕼 SG4A$ovHpAt מϋOyj4˫I핞%snPš$[wbm=Ftf ׆`̒YI' X7qO4lQLvΕ)ۊO es!]3_sg gSA}2McO?5WLs? .?@x}+g'Luo34!clc3,$=\VjZdt~F{fEq )Q[pr0p8*T4j((((((((((((VgkZ5lJxQ'k͟~t~$^wpj^=iVKׇHq[$NӼŦMj+K݋ch7U# t&֋ƶRk"Z^e叉cis17]>Z< i[5ՔˈnJr9oSk2/E}nc"΃WVKv&@o,gn;}=jƁ9t|)s.k%7T}[_O=,Vc/3))AQkT۫)kkM+I 'tdx{X{tyb!,1!pPk]<T@`FFxz-< 5sI4q[sm;2ǼHq`d}H@ M:;;ĸk+%FM($d]+T|vv6tӡXMd/b!w dsYƝ^Kmc:+2[T\3m?)_bi׺]ޙڋ;y1`cG<Է?- զO6Ŷ=d~|r$t8|?z66j윥ϑjRF8\> nR٭G5ڛٚ,UIݎy4OwjLڝwWjE%wfN7d@ہ]!f-#6Wvvr PCK.tGN6jAvpS܂FAQQsÉ(J[3&O.3n q']V]iyOfM<͙AI]ke"X І71 `!qQygdK2U=n.ݷK{ /Yg]Xy ,|deho$*'R }>U`9; o=g9 }o!akdrTp[h\"8o.mu+;yC&DvI|d`"u -,[P~sIך7ڕ&&|0Y[ID##7'/ⅲ뚅,n/hm9/*'H挒#-F{5{Uhkp s6yTPy(  ~bk4ocOԭnMvSβz \x.]sRRKg A7N WNŽGv:ţXj3[H y$۵[.U`7 :/>q_ 4KcEUm\ `NO=+ЮEPEPEPEPT5-2;_:{{eGpA8 *䑤M9CzCcQ,H^f: z$?G Կlֿ͟#٭ߛ?GAs4}[ +.2wBɟ!~V6 ea,3$I$I$Ӿ?H=n!ƀ'$?G zCdhz*As4}[ >?H=n!ƀ'$?G zCdhz*As4}[ >?H=n!ƀ'$?G zCdhz*As4}[ 訒(2L?"jZ((((((((((((4Z~na ø{|2UpN3֗ ~ ?AjvGKP˵㸙*; CY8u ,IAveܹcf 1q_0Q?JbI +nn#HGJ!1w9 AOkϵ̈+hbd`'$S48I)Wx747o.~ThdTZ(2Jf$ʟsRxoZԵm>(-/%Z) ' 306 596 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2T" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (,R?Fo6l/_NH*ڣtӮ-NǍ>1O|(2cZ#<_Kh&S\_Dlj4Ek+7M>]=Sh*>W73Q(M"&(QV[Lܯ&ҡx&R(QEQEQEQEQEQEQEQEQEQEQEQEQN9%m9Q@ `qOuGԚ' M1_H]w[Hb?xLWRBk8).yhB?LWQkqF( xKVnFY%\w88M6JxE{ȇMvX@x"^~Ӈ]E9A#?~ڂ߯uP| /kgi="?U5Q@Kx&}ۛs,?F פsP՗QAP5dfzm5}RX!?ʠ{[A*Wl10@?Z+_W}NZG 8 K4K#"S?WB_]ʟ\Ҏ(QAŒ }?hE )@ܒm*)x?RO5j='MjPHc_T RR!ii)h{QEQE-RQ`--%P!2ɣ>Տs-02/Y<ݎhݐ\AEUypoR=#o+▋ xX;&?F k0}:c^KEMm=42D}Hu{A{>\Ͱb{`sK9$"z4v)a?CsYӓm~IPwGEt7 ɍa?+c MgkT(((GXgcA&7\m=!5au\V- v7qF=?Ҵ& 1~?tSP.c6úEܰЎ$vƊEAҊE"{T-'QWh=hGb< 85 q@Xf(--&)1NQKI@’b\Q@ E-LQKE6S01NQEZm Z(R@ )ԔP;4ZZ3IK@ E%--QL)RRKIK@ E%-1QLLњ:Lњb)4Ϻ֬N$g .^{きy}_k̜Cŷq^,9!U9&[ naEQ@Q@Wo.:'_w\oT:RȠh'SP1OU_w^Vnr8ZV-F[m̏.4M~PGo$ 8\7c=CV1V򚉢d8BX[hUe()1Rm,2v)1@N&(b\Q@ IKE%RPFhRJ(H- )hbQ)ibKJ)أSF)RRPRъ)Lfy/-bpLDV^6`DZFo1ر΀, \߼a\/ɨƛnw4s2\Ƈ݀r/%l1˪]j@Hm1t( \/UZ:P4pm?D'O8a85\M?FVsQEHŠ((_w\p:WkTi~ZiiS1ej1#kSP`W+MeeVD$@#5Qtc!'$ ] ]7>WHN$4f&'-+bqUPI69Qʌ~Y+gMw\{r#=+j\$6Ϙ=NAiE%-IBRB+ǵDAj9S)M)V^<}ڌHD;i1R4"6JMҘ DNYMr:Hb#0VQ=-HU[y˿ҧ+M"Xm%)T 5x/k&g԰G+ETQEQEuλrq\G?کKqyO74)1x)+a6P<Py^皳 JQHf,to3+>A?(zP!f?#ڏQpJLS@Ri2}M> 4@))i:})m-0ʿ^CX#&MlP+>%COQH|Mr=4\n-=K@X' @ I u`mSkyf6˜`Y\.i4~isQ/0nszb0Max[-)"nR͌;{T\a)lu¤R q(K%lq.?Yo ru0٣L4Px)hNҫ>y:jqz5oTSA} 17Lke`ІՄtc9I]W`Wx,\w ꇎ$le!hHaJQEdQ@Q@Z:@=kGrMec2譚H4mP: $uA8:Y(htE㰪 ֓.[XƯ8"ScG*x_(Prnin]Gb?>T{,{ Eաl g.ԩϭ;4NFʡi W*YC٥f;*Tln?D>R. +T;Y&|]B4}i-j킴Q7yVTڰ-'ֶ&.wyqnzS,栛W;ohCi"Ӻ4K*@>CM3rF3N J(!(Eޜݾޜݾ #jv:'^Mu \xlg{.T QNԒ4qe)Q:ÝB0Ka $['^gnNrr1KZp[qßVoܪUm6XiہX yFFI݈\źh䷷Li'Muz/U.,-D,5 sH]j.A7#Vv.Q)(;ӍW?oa&OKcU#I[,}$U{)5 M#B> k9j7 7xV߹#ޙuāǴ*[$$5IDG4R[dI!qg#5leҪ=(ʍ#ZqٚQKo+|^X=3Lź*Anj0WQBDʤgxH"&w *ۚZ}hAE -wjO]_ 9GG|d?R  秩E>C9$8Jd4QXR?1H,|j6Q4xWKrֹ";Wa,fְ)6.O!HÊƣ:F~0L=2(Ð((Іe[;k@oEnb|GD>V9ɩB`: /&oUjaIq(R҉<ֲs[!lAY+ݵVbu[3BʼnD^fy KFNs eOZmF ]Grk[$+3W}\*Ÿ|!O]T ۞6jnqȫj(󫨴dRz^Zmɛ7iЌ2i.5?GDv_\ t;YxVn4"F)#-XD$#3^m oW?R/%B$`6BNas21<61RɥZJ)M% QE/zsvRSPMXԑGl@(wprǵosԠELЪ-ƥJˆ_:y%l,{ip9dYW }=r=jK"WodJ}QUD#'ʢܥsB Moiě'9=h֊QE&h4:cWjCԌe-rF|Ly'?¶!EuUX֬khd hz~uGTzۣ#co'qһ$1߷jdlFlъfU*mwT[tH >:ny{8hi6tX# nS-eUB縭uxS+8^}nY;֣{jhPM‰˶xV XX0OltbD i]GlΧeYr^gj֏Fkt_) x]^OEF xZPM.M(HVm1 ҕ!a\ζʷ˒~fw.wv#66cjtcQaݙ2^,"X3V* ؇] iQXg8꾼 VPz i'v>H})ĮHs)`3 5!ISؙÏ7iQ'.*M( ^vJpn;Q`8Fu]d7?vqZ lyoo#?dIq RZ+IUXTFyUve&RKBuK}h3F) )h1J^І|GgJ]_/_p9<-:YgY&19ȌAo_&Γ!+uC]0؁-\3@?ڮ6b{G~U2 2#G*"h8TLf7֣[\uO-en o@zP?z'*:I[5COM;d0+x@ܣoק!tS"ac@BO -%-=q}i]Fx`lfERC w5q?'IjJ}z?znꏏSoϤ?Obq`_?_k/~_!Iz(((s3uхssTᰁq柁z1dh EOUQjٯ֓إ@u^u x5!YM61:tkz;3:I$q!@=BK `AqcX<6;)?Y24b"'ο龟ւ̐P2Gk+Tbnܪ!Olxm&KJ<_9&YrsSD+}Gu.9 i?j?OiZO.\a*(laʙQOPާ¶wicMbCؑt6Gin%Hzty-xe\:F )rXepʗmL;bI(?m(Z,xKXv#Tz2a2o8S'#ڶ-$9>{ǵ@M %fy/]pGeup{«F%+{X<:Loy$k mS1*[UF- `9߁\fy&;fOݩGjK/e] VڲUvΪ%R8H#?A[@JU 1\[Ώ m$ϥtEЍmem%*sTnt.1+,3/TB/2R C[=ӒY3?'V.!Mj[.t9Ȼ&ycֶ\U%r'6no-Q! Fr},xa9@=AkqL6ҝ)r}))P$8O]슰r:W-2P>Hﴁ]9lgtN} |WGkKs"As~vmǥ%fN;~ <Mo YFIpg z'ǥTҭ.1G! qNLV&eWd޻ &-e+8<<Jθ}%xfV I(mme|sӾ_Zh:k%Arg9x_M/ºōR\ʋ@I} a On)O|ןj_0!m˺W֧[5<$)q2.L&7982&@=MxM$rيo3Ѓ]xImXd@YX,s\Ԥ٬nC8ES~ g=@\QSqњ -4<ʛC8?*f4ue v#6iYgrRB͎OO4+k̄!=w'9ߧ׽66 +JBpB}c]^z\ƢL3<ҼQj_^q71 W;Zy^kXm`N}O"M'4$Zk>cqU Ymw"2XH$5›YUdG.@zna%Σ&UYN{ȓrxq9U`rs! Qgs,m1ɮb;*{$zj0X\ː:&Ζm#KwXTc\Z^LKji.Oxա*p8$Yp. zW=G8Y3#1'͚Wcz@?ZUEhMQ'f_M,zHRU`:T3"VǨKS|@?58_qxv@=}*h4jљRCtq*+(M9+'n-3BOҴfH拵Cqm+^r;M{8%mOcR]jrI! ?XqIs;udՙosR`[TIo+m@0?ªkrHVܶAϦj5yXI,{o* "*}[DqFυC^IY$>kˡ'}PV5NUFms Y1N~cH*HkPb(OTI\0Y+L)@)6sSq))iiW"\/ n\H 89'WV1EX!X8Vuwkd2!WuǾK?)>Ea L KjS[ .0s9{5뷔H̛>d yªj:^L>A?gA%կ>~v=c񨵍ٜܘN j7{  TcV4ly&8@CH6gYW̔=& iÅHOZ-sXt %,=y^,n`w汭tBSyk4A"v>bm uB+y]- VoQuj7E㠍v܎=k5/]$f0py49ݥLq8뗌1Ĩ91ǟeQ+UuBjZrI$KdsXIrc)]t$6ޭIl?X}?WpHĚ2?NP9{ɤ]F~c^)>u339VBIח\ ŻXw\q, 11[q]8jv6Sm;NsӌqT|?e*_mw t72F-+Sk jN:˴WɱL%yn)T^(RU{XFP<[Ǿq/$wwL߻aG[7Ou>gt#  8Uoc[L,lUQCW W+ -no{+V=E R%g!hV#h6vz)$ҙ3>W&; .-5kM@BOcVQrG +FyEat5vwǂ[P2 Y==Vδ\wH#J|FjʖO-8g9xC\4z~|[jª~͹c'9O޹K94Rۻ0Z94J5OXM29<~@P: ƙ-ř鼒Pt[L W-[Sh[&f J8ŵ{ubE.u$ȭ(tʮ怦OżZ  Һ sҷFG_iz#dÂTT[HQLnX TMlbr:?xjswa9cuZO8 Vksa>IWin^t2od1kUNiԉsi4xQRPQEQE: tK\SQr? k ) |Ǟ fنJVi ؒ#V=کFzyqRy4tdE.GSL@ZȻVX }kl>X?:#7sXC0s=됽ihUI$ { mcos.mu)'*<~`9S9tɔAԼXmȣz:J 5Ӗnf?fΉQs T;vR95m̡+;$oKsiϹ@ZĐ͆ǿ$# 'z+za<  9>ln{?pi0OkOۘf]L~<~5= $ԐLIt6JLTkڦD"V"E1*R)1H N00KƜ[ 2HJ+Թ>\U'i>[3W888fKQSmVV|hy ZxP=N:]Imc->\iޥx%j2H:Z]ЌpO5KVԓP(:z~]znݛv9Rj XtVg*ˏS^nMrFa]b$cOO+rJE(9OI=i9_x8>~yotqRGc׭ MA{ܴ$m1elc#-5K\(f&@Ǣf nkQ¨ dltfH.([ 3J$?gH/P@)|s3sO:\kl,*1ڻsОe˖Oi z=TRV%)1^j3>)p)Ԍ#TR3֮́U&;xSRZeYIec'Z;_YI g?@X?jy>9UV_)]āprm^돩.u}J}F+S{*)*@n9l;[2۟26G;CmISFA+j[;!5wL+eJ"2jmI Oo#uY]D|}b ?Na.c t\ge|Y$,zV[ғ{T)S[" WST$,{T.)bȤHEiSN @ L"y.E1u+^Qk]\mF:9iks47$O::ab#uSgڜ1J<##<^])JW{Ilм{$R %pj#R\烚w(Ҧ3}k ({ n펽)EFZV+dݫitEC[PN})qM60?{vT|m*<*?}wWY= l"ŒSKsJU$uLly*mĆ8UU ..(I25rISOs֘)d9V>Ơ`U>t}S<~[Z)I^3K@*5- q^ՠ̩³`+ô 18dE{mlc)|UQDH Ɏ\ !H.0gˎxj}<AJLhJfjֲF?OzGP94):HG2pGz0 :d $Hoj,W-nb\c;օۨSzsY\ϭXV>>M=æo$>+援Q &3#gQZ>pL4\Z&v*j;MJK5d֛rkB*zLJVb8U8#u&W$hpVY?>voB.EyTgi "5vie8}Foq մ 37 $I'Z3.c(RPQEQEx0[ƵLo# ק5e[+Z"YfوfȪqHU_: 9CXz:"o"/^W'ڜf AKs"Eڴ0 w=)tSGރF"¥Rz.vdonH̻uPG T.LVL,o75)uyvƝ3܊aF#9 NX%mLE2>A.< l#sJzFQ*vsG,}O5\E[3'E9zKnݻןڷ|SkX_,@_Q\|*4yָhQԡ23a*x*V eI VL0OP1]<ܪÖ_SP408JJIZKUU0&ڐLSih /Sq~sP.vCzXWlaQ0I8!ڨCg^Wlû=+9H+E&CH)?{U2 wu'VBY2}ʩ +GirUG&eLU\ե$ZE(IuQOc£*"ڿ;~6wTp*uRS\IǵfZZ=WucV AҢ9sU&К3򎼞i3V #:j 8'cV"RdcV^sAbpi搊7rLD5n-RXl9EnY> \-$/x10cln¼ ,s?wnwon!G3REl7JOsz;o{wpp:=X*Qxp_Nkvtq6^Zz'iF9)JsFTqT.-8uV9cu^w-`r1&|c׭B<4 ='NI'w4mxB3E `1fw~5A ]#&kwK]pG"ԓ%U$/ATpfZmH ++`!W#sW .dβj8=c\]fr}R?gԦ?#H¯SE EX LxO[,V ]'1\y7YPN3rvGw:tFy!b@֢ۢһu:賋`}w>J7#9敧{Tw=OeNSrd*(D5\@I_I99Vu&Ee_O5b;QųT/KbOji f~jQR}ҳ̇54s˸#]cRxIFql$ZY' oŪDZ#=g/+9'eaך$Cn]DХ)V 5E;gQ̀s] ډd:ͺ2Z[f$6aR8#_PuyA͍q[#YF7jڔJJjCFY\`業 gUFr6Z>^o~-g{۔, 5ԋ/ 9xciWȎT$t!X?e" i*?85[cNUyYcT5FPԤ +Vrұ B?Ua2H<cWs7ߚGyɦ}ճWC;ۍGN澶5A&#NsBsŘdSŻrI3oÀ2Qj+Zy>jv&[(|ˌTĮVE?u'Qo{$:ڕĶq'UAօ@8Tܦ1җ]~vĚaQ(p8ѳR5kMLdn=2VMoof ڕ2v3;@WPkthzD2nY ֙{^fȱґ%-AYMH )FIHY?^9w{a^OY\zeNy\O皱r>j. ^-Ï]z~iحzv?59< [ F=zM"ւG3\;j (1V^BjeO}(VGR~‡uz&*n8sizN v)Kti&[E%ߜp:cNy0+<܁ [q?R9aˣ/P⺛Rv.֏||:}}+}n87J\35i;o9֐QEՎW+_;W=sbvοKJ-cMԢov靭U|Cen5 hO0Z.+8TG#y1R]k2~M2ڿ3_ͺWbڬ(`lEt~nJWtlJGƧ`,8HΣk[ˆ#l;Vl~)J-·P 4GouGNdFHN>RGכjHC(Qy jv7KȕUINSIijwKEa ]c#^R\_;C&Suiy4e{`}cV'@<T)pZզ=\WUЕ"Kkok1-Ų W#,` k͏_n:峕[ۺͥ;}MYKT͑Lj+N1tmǖ5Q ;zj; fܦN̵@@[Zvҹx fMʩ#ԱSTMh[7T,+t8X Oڄ䀌`flnf.I<ܧ4)J,tJMzуMt$@gJFʩ5]jwȪD^jpK6)')hQ*S^5*s42qAw+qP!8*fAڨ/7QiZE^%#3 jZn?I=.-Ts)r|#wAS 4eB/,ܧRd:L—$@qҥXPZ?,3P?`g?'r(j:FءS2I0귒1rGP\Xy1{djL8E\+K< 3aA y%7>hu8O?#Lp)^sǽh[HГS( Aʃ"ss7x9RCRLO'N?LR+cNs`[✲:q+UewqT2>_Ҥ)Ar>oS;Wv-SNh*Tu4  R'|Z1ܻjw} Y@00Kch!+E#&>>3UUrHؙv.zkk3m3#&-7_dq4\{?p3m?\rۧI[رhЇVVcqfogxWs]ZsgMԍ ?Pz%!NK. Ud#滑R}J0_R+TJH>y]6(QEQEQEQEQEQEQEV>/эEEZ]N?P6ݻnW_5E.TDn<4Hל~#pl X4Tq$tĶmfPN@#ӓ^bw;*OI҉Jئpi鏥L5{]GϩqRT.c*xڡ='3>o'ϝ+.},,MQYFO_¼ƊǔJs+7y1uVP u7 1Noƚ;V%X9WGZc~ˢs2ҷfdcQ6pc5V,du;uDEQEtb>3?f'#[KMWݖv?RVtzJu;_tT9=d/ۮW9=eji7QQ@]}nsF!#ƙE=I?%PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPfotoxx-18.01.1/images/zonal-colors1.jpg0000644000175000017500000007440213222767271016343 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 451 252 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj.aou[+mOL"%)`AVFpzǚI'(aOt㿉ڊZh],-파cG ;/WS|3𯎮<hwƝM{L5)1O}n,&ThWcCRҗ}?eJn=6aOk}S }ZZSI ce(Q#Wω>fDiiĘldN@8C\_T[Z7XoDQ aO㻿i,lk]6 ]bZYHr䀠gxZXZ.c$ 2c36O[v+}b 71H4}'?½j>*EMsw]#Mo9qx,qj'fkO/KVZdXNя3玴ocZ}G+֋o ;Z[k wA^{ZLF8#'x9>Ek-R}͏DS$Ll̒C# 2k+^5}gU'l".TN?"z]Xo,(P{RvsMEbs*@xom͔c,c`'^]KOOb|Ici rpy~!o Rubs PkQNrQ]I?Y Fu!13T&uk;FN%#}AC^ӬkH&ݑtTT%!s_ּ/ ֣Fl]:82~R~`2zk0Ϸ8Z2v]g RpyW/_꺽%㙃!E84WrGq#0\hIЏwÏwÏ̛i>}G;YVG;YVE\I'ֿB??? z??? z,Oi>qAU#qAU#f?=}kC)|+ZjMгԭǔm2Քu_|øşkøşk$֨NF|m~u2R6cqi=!FBs )v[ p+?Rm_#-zz/#H:|??gZ=?gZ=%l;sEE:|Omi- ;EL^i$!5Zn%η:^D*\+m_oøşkøşkrod_?gZ=?gZ=ask[x<Ǚ#.)ᔭO,XϮrsЏwÏwÏ0l7lerYV'$OvIe[owÏwÏ0^b8oM쑦ŸfԎ_xxLְC1\v_xøşkøşkʰ+{zp/EOf82V=߫W?=E~øw|VRUי|z Dm}: V;-iʝcϦWYOC:Gi$X|=^]* W_'h_6- 7Tk3PYHQ F$V6220_!i ίrjh$Ӥ,8wP1Gp+; ]Dz7Mv/)ӬK;i^@o)abbx `4 ߆ZA|Fl^-ӳ&FHB$-j%=|>ʶԭbUi`Yx }+v~oŭԶ4 eA6H!,{kr2EyU񇅵}2mF+MhU5!iB2LUBδφPkּ-$|Y4no0G)BsaqMV]|}>YHXtYޒZ`k{kbϙ$S+*}HՇ_mxu+I`E$UT$>b koqϛLzq_6"?.|%[Fj:Lc.muY%M&qq{-fI5I%Bi QXl,B"t ⷇!x'OMiIbDäS#9\zZ&VV 2\ kcFҭ4 |MWռ3ekiMcmz^`[VX.Ѵa+$^o!xs_uGSH7H7P e$d^ Gz>F?tG sM6/z5 Sf p&_-OgyOA k:m% l$䛄1>E/X&_k<m. i \>2HC(3Cym>R{Lt7|RSx{9qV*E3$,2>`z_&ߝ->YྶKʄuc~xºC܋|I,Oo"<[y ww |oQUφDH=km q6[2D@g|ykV^unxv?\,ltiwK5n}>jTH40ele彦T}h:[cyn.!Wy$n@OӚ']FMR] o8\;6aY{B1*r~ph uc/3|wxXk"nDLQTɓn= G7Z-oĞ&m [܌ife{9,s,q7= K3Oּ=[VnVBIz5Xvl9ǖvɒA>/C izmkB}?[̚Pݝ HAS"dbyW=ڳ|E/=ƓXA|k )Ǩ`=gnyu#αXiVD;. Qܨwȥ!CWx3Ŷ;𾛯pKː Qkv <m<&s1RҾ1,p5En=(EPEPEPEPEPEPEPEPEP\o4NH.gO;<΁`8ֺ(=O'K>7m;[ ߳Ms\y\`nk-|+=;M/,he k}4O@ (z0Z;_~-%>0>yah%vZK3[2M*++S('ׄ?e˛[KX|^Ϫhׂ/ y4|+{ #ɰI.<:ٻīul6ڥz>kp`Ʋ,gz5< 3sxcQm40Oa c9 8JŠ(0((((((((((ŝj񷃵FQ$0:Nmu˕l  *u3^_=xG:Y\I!MHXMc>,S UĪyr1  E&aakY#/\PѫY c:+M7)=Քp(,IcY&lFOrIzl_$mԫ'ZuV6/KwIc܌yj^sY؄:r!»Om= 8|FkK~">15;Y_Č 0 l885Z5]k.:'%YHe  s?}|5-/[HR6; SэH<YKsj{UH  #V#;M[h@uu3Yo_V8Vz\>n<@EC?1ck,64?+>uKY j721$(ң9n# v{V6񭤩pC%ߞMy#5rOjYhIȺڡe_1cKy<`A'~ xσMԧP-=-m4L!8qUb\[\YJM͕f? A0,>o`uK]Qmfoc;MA" 1JAdm2ɯ/c= ^Kko03pc'B$/|='αij|CS֭L[\aA,\ܸcGYB]OΆ8&x$cbgo\ \߅\i+ChB1HQ{^} 0xB_[xLl ֚%Vq}(P2J^Y~8k_F4-&ծf.M@XDlWi?SN/SxC|iƛ-ggfP@f9 |:>x>|WixTT,l,P`:/?륾[勵ǥW? N]kI?o%gEK"bAdWŚmݶN]=@imdU>J//>ЛwV?}f{cu`(~ʸM>.-;JVf6XyI)8!'x-CK-?v6:q4A"-pJ-!_Gy %x/z[LbR4gKNXc'd`R Io׏ ! uSyE|+ݒ5o/=GOį;ZmN9'>Dđ 3]es? <_h=*;cs7ߝovlMDFW ( H p x.Fx_xWzO 뗖.0F'ϴnǽ\.hש&hMݕƥudEhdvڛ0W^vMOq6z\xDR[;YoY'Y1Rrw1(.vWǗ4fML6ɟ4^a%ibC (94Q@WxO<=4nYi ]D+o-yԯC֞Wxs͔Pxx \_SLy%Xx9Gr Ar0OG{nw~1-t}RO*k6pB6~lwqKʓq <}/ĿEc\O GmwcHlL qЎMv4ڰ/@R'< G4OxY-],|c,J+'k B'#3ZRǪG&dbDK,K煸lzm]|wucoOsm]w<Ѩ]7&O#p#>jz~`My `N~lz杀h2/v~.[Kxxs9ܮuʟJyЊBU+#e(7}N>?JO^(t?m?LPG#8="Zy-淧ZD@ r2?B*XIkGp>o xFoI?_6Xi0^Z\ N;+mQ?>,ySx["X7WR^\C2\eX ,nXI?`xc='c_o?g >ekkLzwr53ď;u8+2P:WV, ǤlUhz杮$_ߤg,O8+W _xWӢ5-\i]-]xl6ʩ9+|^>{|7E*]\/$~s[5%r^i"\r0Ug 3?c6?*Z}ds~2%>_OoV.-"N=7^LhxF0bYFA9z'MΕwj&% oms'weG zOQ 1?B|yX{O(y>K6\[n6gQi䱓@۝ 1?ZZeK{N Q~mon&Iޤ14Ҵ.5 ;N$ϹQ{ĺoAV׋ԼCKo)'iXcwQ̊~Nq'J?`xc='cZ;/x7+wP\'.uV, é٧^]R'(m'vx?^:|c\WX thmil`xc='c JׯjVCM,f@'jIY[Hl&6IlQ-ĩeXK YV_g|=.յMcDXaPE$+"c85Mۻ2m+_~wAa] X.ciSE&ߴE׋ ɣx>7nm඗yg9(6,$u?~W FL[dWNAzd#@3j#ߎ=o ^j5-nXdO,[Nv~y$d-j~>uoYZ\2\C#E4LTJ0$dWM^k<+};P{+JkIn>5弿n&*sq6gXVTXZIyg?l5!kWmK3 rfhCo+'- +<piTްEajBddx α/ĿxZ f])=n${Gf QX޶|t]z$_?u"C#6O mQ.'kjpG(P<< ɐĆ m|+7> k&=>&sl^kTld&Kg;I>^>ꭤ7o0rw]%\#v\M]pZگsAj͚ <[x .]ZFKQxлgm2X1_ 4 q4"fwNa{ qXh͡?Z5[Kٴ?dTIݎ8^-OiΝY}o."\~bC>)mg^C^^-يS& #6X׌E\ aIfm+yUia^G<y4Ey7/yT5:5غnfͳ́w;67gzOWơa@]?`fYeZiIep1lJG?<+s߄ ķuΦGio &>axb>"ЮRP) HƲ#+ zM+W_x=72x/|#^ AY#i|<"<|pL>#5¬xJXWGlOnۣ=EzOW-i07@#}| a>.=񕆥mrbgF evVTC`D [&ӢvwՈ/KTa|o,4KsKՠZݵֵkۘwIs-7+^]n۵V"+k( 7įzuxGwuܗʊw j6CUr[w.9w.I-:A+p,ȆF0D5?=/_COO W" p.@;x$`WmEUɶZձbmS:ZWO5 ^{[X(.-3n,i_ lXxSvmw ]jm,ǵ@ 7?Q?"дw6\+˿k Oȴ(hQ TUp-Yg 'ZM'To QyZ}UV1 LP|EG(e!;_>!^xB_=ݎ6{x_٭bnc$*$Gw"@G}䕍 d*vzW˞ivDtWhV_I4K;\ս&>/)^̺χ+ZA ߑC ˸#E,yVI |-YԼUsVj>,Zh--i41͸Z3"[!k>~ wv57R#HGrŀʬ1pƪ*[oOxCwMxQKb!LӶcTzk.z?*%ƝM u#ڬA(c@LY|;$HXPPI> /?柣[^Ѯn&kĀ(o-%U|eTiK߯%O+KWDzA232] WU\>/Ox;JZmRDI2NeF`7. 3>u].QgkGk][Mb e }<VI63m+Vvx)6H}ָ^?x[XzemlN,r:oEފYrv~7p 65xU}]Mη-ç!m)eE k EKHyϰl/Qc>V­LeOK[mYa"R >5j7j0!5- u/Eh^Me9w pX >7xw~#CW.oe8"CDrI8 "?|@|i,nԵK V7+K0E8>0~*|xV­L 6?o~+Π!4TA{wQ?z)Y˿|o?#X3?l/Qc>V­@ʣI"^]!x}Z_ȑ֓v('+3O KA$qZ뚍 f4Hт.U\#-+&{-uT,H I'W?_5c{M[K.ḟNֵQ+<O zR˸i *+4k³}qYk>H^V2+Mb_]/>+XZfkzuvnKOʕv;+pr @a]A|:dԯ5K7LKsw;+>F7b@RI7-:H/u{! 3}r{EyM |3OIZym C Q*M4e*yy_tI%I͎*8 N¸ Q<3g/mﯖ]Auf"|=z[?nb< }YԼaq.%I(8'3>|Au@jp&{󅬠Bw&]W=wzg Q λ@=3+k[?/&WoH5wzg Vu_^[45XL $\u__#bkm"F6eocHmƤ<@׵i4:yY[Sh+2I洿9ݿdW_AY5yk򳻱$°>|CucWJV5+k_pyd sa8ʶ\ʹ֮;_Nu7o1G':WoH?__#bh _Nu7o1YQ]\77O7 D]g__#bkⷶKXb@~R`jXx^:.(=3UNu7o1Y- >k g%HR4HUI 5|VV[o T.EL[HQ]w.2s ֻ?9ݿ?nb__#bhP19ݿc[OIΓcpPmi2rTddzWe -\T/<O߻ZL (t+ xi~Yxw/D]j3ҵФ"| I8 kF"4ug'ݐ-x:&-CΌm4m2?Hk"GuH@VgQ:-v\s;)Fmkȯ|?]ٮuabE˻cǙ&<8:-GD#V&vD D 3g)k|GeחVE5Ķ-4_2 ncv#x#"C?lg=}BZ=j^h2jZI2Gy(5kYi4k-3P+LUٿWȎQ;ylr|>_I_//[K@Ϗ73o;j(N˗⶷+fV]S`oBl-Fk揅n<<#F\L;x-G; I:2l'( b}ĪKد)"5~X!s&+s"/*2w; +"/*+?n UO/?_ǎRu e#ȮNr~_ u<קgW~$ӔIaY^gusчZ^пsT+'fPOGaEq^пsTyyBEQp; Uz 8%iͼEخH8"'!v}ˢ6.f $$c!6ђvp _oŏ8LԴ RO:R]xHyRRTQk|i}gZQ"1[sV<~"Ũ=Ϳ͝yyBEQ ?E+|"GZꇏ.χq_URm+EʅN@e@~E&QH((((( Þ$ռ_ufrF^+ +R${g[wH𽉞M>b-IϚ-hJWk E#.Iu=I gkOG׾3% ܪ#I$a;8Tu?u|ϭR4¥?  !kS/N?Y\zjVkZ셼8V$ f.-SFW&.?XP= mR9,W<X Sswh [k> ZHxıqW>!oSFKVMᵷy2O0X?/$zx/ߴUڥxB]: :k1慖 K4b݁sVoI|-k?R4¥?  !kS/N?Y\Ynggk wWh5_1W1OZ~]פ/_MxkDN4IYdy "*BdMVY7Ÿ7m.4ۏiR¼N)ThK!j7zh]YXekx.yB嶠g2NO-Q5(oo% WFAJhmͻ7[_Y~ KųzmI-ƣvnͤg>ˁRǡ=R+,ۿueͻ7[_Z)B³ QnM$VuƱ_i:ZʱEt3v;K Ÿ/ߑ\?ꖺ}vv!aKA*rqIa,xWUh.Mo5WH8=u?fЛ/HÞM潯ok% GO)gv=Wc|2w2[xoNkQ_iټ"hв6ӆSW{swBn"Y&+S?_"V~~5/m Ν,Rq=m+8 V+޺V~~llխXm㻻TGz{R`iil-˼1,ek<V?fЛ/H9kU &{c+r*AffM3`y4ˣe}k}Kgqk0IȮd`4 6oYEnM$V+? пa~E)Bj_,ۿue) h֗Q76p,{me@;W^>xTk!'$'$i07h@QEQEQEQEQE3PsቶK&Л P |sT?3_)[ol_G+f9Kw@$}9~šOjzWC=ld-g̬htYSУVUm K[\W,Q?HT(՟$QqpZگsAj *ou+* B}cZo.X#hrv1sCj~&hKw]zVc̚B5-FHָo33~~Gd^.M;SHZ5ixpKBўS# fk/l&p\8(;V,Q?H:;[5i\W,Q?HT(՟$Qp;ZUz7 :g`:~43qqq*Nљgb*̹ ir֟U}?Ft]WPͤ7=#P,DQFA ĊL z(Q@Q@Q@Q@Q@|j^+\L anQ>lX? |BE]KDem$s, cbmp:e_C52[HmV`T zMCo4^G Լ/q=Wn$ L\ ȅ#(Gjp\׿BO½t½uLg9 C_qyoh2H$a3f&5$Ozy/HkRݢ)QEQEQEQEQESޤQnVO[sFxX zn2Z<-?źgԧ$m{x>l y|7"/R6j ;d }G|^N?*/[‡P¢(u?F_|^ֽ:^-Bvœ3Z;o C4-NKEn&`H9JG4HM Yމrie `OXǝ˞Np3+蚷յKX?t5cTIY {s'|Q{=VmWT?Qxw:C5'V~?x:k hSe rFUAkEP~K$ ХY&`gW.L`XI4tQE ( ( ( ( (4XvvoZ~/y,֥t vp܋dy_RxWŖAFWr*KI0kW F3|;tOB| ڤ[I4& BMW' N4Sa{i-w7%zŮCO#) A0<wNt u !+qo\ǽRq:;[5^K/m;+Ͼk7G-m}]c;UzϾk7\s;RHn"IneDB#;2,c&}a7xˠݤf\$pD_/NR| {oqvdu[ bRGu#zm t{k;-ZM$r-ө!@ Ÿ|BƑjfelv}4@"/?>K]}??Y5> ;_ȑֺZ'?&u* m|X>4oos(Yֳ{9]Xg Fg?rҢFk-XEs9'zMpZگsAj|33XSo4Ϻ2H"O  *8K|#_IZi]/ēNb 2prB:VOkK#= 7$dy2U@ bF@s׿i_^ {;/]c BlUyG\n6r){?x=f?Z 4oos+ VhW9cج5+ty ii*F$Iu)Y˿^[¾iZu]k Ch@2}W-/|U?4? xÎJp}|< }gAՍH<G4RC*XQЈ k~:/]hw|WwmkKMmo6*_߰g3Z_^Ӭԡ|jV;=PDH۾[ ˹FhՊ=(1|'.|.P6wڧ!"/pm#ȍȥ`_F+*  -4+ [f;k \PmHW$Ա9c_CSX|1OZ2N/MHcW w ?hQPEmo?w~!I_k+6O k0S\snVQ?Y,WE5ASxa>QvF<ýgl_^C|2\Esqo#LetO >x_2g(]룋B'G&Q ;; ^U$I/J4W,omeό|M>f-t,k6M#k9^]ڈԵmJ}gPQ$Pqϖ}NOt U2&AW˿;]?*whUe?*whUe4Zvms.$Fļ_hÍ`vH=;W_E򆁦m4D|W'fGG=ęڶ>0T l;/:&Wܷ\Za,s@c,6N8覴ؖQE"1*29LJk=o>. _{t`Z[*(7z|]J?GҢ3-k=o>./WRcXsg4H?`Z[z|]q/|>D!PFiNIf ~ݟ[kX6lpBXeC 4Ҹlv??T z7 h3_-/ ?XO{6HПQX;(|CP#88>Ңu/\^dY< CR?G|e4z,m +nq@b`zƬJ_^. _{uG з&C<5Bލk=o>. _{uh^-.t.N7FdHV+ ~E/!R񆏧^^\=6$Tb#?-#N4v_ WC.mVE= (8. _{uG з&C<5Bލk=o>.oo*Ȳ]Sg,Ak%IkFaam<|[C;AfP=ȫvGH,-gY!F$A[h9砠(EPEPEPEPX1#[[VP^BmHz _jou#{A%FFCg<>_r/M._Zzm^M*[%n$A*n%L}obWA$-r47&K+:C,b)$v !JIB޷x]M:I_P]f̲Y։YHu!Yw5]hDSHt 6&e$Z̗<}*} |kf- lKb!>K/<#:BhҤ'1,S `?5OSfwgoD3mw(cV]aK0VW?J=l5ͬ! CJ{5SiT7ڼwmOឡk dyap#F"\7pJ_Ӵ_ n=BNMF왯'XIL@Ϡs^a_XTֵ]?^.t#[.ʓbFsf>}M>2k[ fHerۋ 599 398 0 C     C   f" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?½5El`ӄ:X.9~a7 cp'oMA}G l p8#Ŀ|-_xRjv(]2m#@硸--?4Q쒼)CA1v6؃Pr[<[?I$ =Iu u~%nZ]'Pچ-欑)f ka ޛź Ēh6 jNE ToG'?DWᏅ|C}}'?»i ?ajZQV[DSAQ^I6m]v}/IKWο& ص[8\&v7u>\nO2yQ dGSு;xީ8MrZɎ{WHM\c9QȮO_gQKj]XO5(51n|?z>N6>}G+^}ދ $ W>EuFVPz2X8l&[ "s #w{x/7xٱx+e# kԛ_fK6~^zsמQVY#uÐvBR[N&%O-Rرƒeet|=Gf<6t}3VNY,V$os$o~䏥f[pjWѵ%@s iFOZ,dW]Gkklre!ݱSYu{-$ $BOAIBEu ZjsI(kܱ,d)oVE;=j+{vbm6U&EOX)@|Fr֔/>#, G9TJ*[} \D]DJp~S:+Q'1AaV  +??lfh8_?O_=VOd))*O}AVfw??}hIwYCG>{|,ĿiYτtjFVwN¶7[ԒK)+Iʐ}|{忉iM+Vfr.b>z_7y\o4x?w~źx[U5=+vm,wi~*:R|u˽RO,>pwumowοvR'qـv ?h/=o4x?4>>"x:uՆ}gu& |E-^U/$ԯein9ydnqީ};g %OG}#Уm^'֏Z{|,Ŀiý_zvas'֏Z{|,Ŀiý_z,O#X 9FRYz /wG| >*j6[#*y`\Ey==+w=Y=sWÿ,ߏtWҴ^\ޝ8I4WEf 3@N& е_JuVQIF7)cKu'&U8$=o>aMxHIt5;}WF{ mL7a'Jo?`゚|-o~M~Y)I qj-؂ ?(p0T)m]\|-S _O.lrفPXm{$f[%}/~0i_ɦf; [lwL М<%I1 ;6FXxgȯ#$_-_Ziv i$ NtVy[cn/ W$ֿe _Km lHs`һڿ}qៅvwxZ{DKd!Y?ճ†O^a=F<8.Lp]}Uzj +N8tKMjHeZH4 u#I14]g25A%HF0Pz]4[F9^7I>Ʒ MV8SutMCWԼ;]\hDqfx>|"|;E|=Sk_i;n - cI)eiZϠ'."FrޣFCDC0$ ˝oey:kHW}?K^Mm46 b{Y5,i<_iXݐ8k艪E} ,H푔t%¯O Aih~2V]R p$n -~Yo/X7Ž3A?[ef`M^+xؒIgq?{m~V_C;;[v>o|0M: Ӽ27"Tz*BOG~|3CZ>˪j1vgkmNp #$W:wUd~˞;_›MM_Y^˦hZ:r֞l_kHUHUl}5eng]/F>&mO;`"*Sa_ssᯅV_lEBR[w6g%aq1_'|!jڠ[m-biYD^]rXcp'9 kwl%5pX\\m#b+A9%$Uk_~ Ih:5λy6K-[Hm㼊In#FX6qy oWxZ>4sKo ^[D`" zM>?Nq]:?E i:p6%65u 3.p{֮ڱik~ƣamz;Au0K"'8@궚JMGc(6✷ ((:Si54L"t=k2o|UY>_ : K L|ȧp\ CF 4 CZkykdeX\8{ےp3^%?*|O&VVxzLlM=5@lffV34*\#>[0xV):^b %eݴ)<Ҿ\|\gn{^_f閚c,0J̇* \,>PWᅝ-~$M WOu[;\r쌩%\ եwm_Ķ__y'[xFWer"^i3+`I #Wx|7-OOQpnFb"_~y!_i 54ѭel%?‹xVtKpx+"&Iq c9ka}2uk%s]mk "Ҵ.*I`9xD~j־߄.ռ668 a%i$;& 2[v6_O Y|fk Y:C%&LJKirc[>fm_̱XNDXH`A"Y> /ͷپ!S#49C}4L=b݌nF+1r@xM5Z/յD|[[~m,G@h+ 7F/ @nK/ˌ=i-~5|?(6F&[{p#:#8~ |<4Ooc\mObI0 %Oo |5j"MX?Z_FϰW2 |kpuZgឣj zzj46#aC I_ 3X^1L܌4Ji'DefEV zs_5 NF~xw>WL"̑+z>]Ӵ}]8xVZ ;۱eυur*Ias i'6eD$gqOhknnᏗʍrq]ogS%lu? YZ5%cLȆ d $ [*~P0O#Mxg>,e6TzMܱML]$L-`NqTer ۅ彿? k!ޟr45{R+4\ Ǻ  WҦx;͵{q,eFߒe!"c;|snimc¾l~$|m.=ظJ+>K.H湭o|'P/%uvhEu%JdDs>!bKP=2/7N|AO&b=.P;26rx???oWM=ku.3PHn%W[ 3WZ̞/ӼU V uf#Eo G0?.ӻ8iv~կ; [;Xkd죍dyDb>M9BoM??PosqMx]h7,b [Jנi%DU$ Ս;-_@u>Ѵ։ou5h c#*8l.'Exo!6D_|N4N}8[#feI\t!)<׆|W~ O?Gïw`_Kym`3\U;)/IF?<)*^%|Min)o#Hب`P RzC]yW j$flo ^B)UG.~E>bp13^H(a^i*?Gz]y?X_ w_єw_єP9ֆڄߧg8_ė_%扥x4\Ğ*SHXcIu q梨,8q^ᗏ>)AfYjt-ÔlqQoG1RY5O{g"8)->H=x{?w >xWº]j:uGu4?c˕mڶj>1ro_袊(5|Hͧja.^wfTeqpkо8k]Ծ$F5֖m妨]\9D7\ ַ+gO/WѤ汤OefL"_1h%9ϵp??f=3JS/{Bj:F=Q|w /, 5)g}Mu=7&>\>FkWiM&``@~$ίu}- F,4R B)UP28Q{ ccz/Zju^ʢ( [bN4>_iΟ6%w˷+bG`ҋ*rov+]y/|yדo5kHlM:+Ď `%G`XPxz[җCե5Μ+rDl ⏊$>^U4KaibrG]0dki/=DOC|AAx3o j]z9m`{[G$%ٔ1ЃP|u{uk˟X%奶{*L<4 H*Xn G|H_c7z&~/֭5;NSk{! "\,Eq^-[þ5˨jq%,fPd0m8Hh؁G൵OPyhx?m^N|MְMtf{{i,ƱOfU$dU?ڿ]~/t{Xo.%nk+ ,$A >x'=Jx xwO1+Mw=tnM\[&^'M_ u=K?iZ6U<9ay%jrDcyG!@0);?[9q? Z&Z;ZBX#VT?+4,F^r:sk .qŸ/|oiחWfEQ eb2jotUm{nb$p6|"glWSC[&$yJ9LyP`/42Ƌr qFҽ7y|ZohZjz6׮ m"U$q:ş>-gmxnG|Ik}bIos56dD/qi#y8Rﶟ7Ioo\6<[iXu'Iu! @m]16˴F6> t WNMִ'Rӯ43-a*E*%OW3izޥ[9Y(r+6ֶ >a’Uw!|87ඝj:k-VMvIFYu"38A˕sox/_>xTѼ+ysm%: vrֶS C}n_طశ `ť[,nTjow~8Kx}<|-t-jᵝ' (ݱdz B3uoúwn|)maZխ><\$Vld ψ4_zæjsil^TYI%I\ V'߉$} V1)0!Ֆm1c\p:/ u xƣMi%5d[YB+reT:mRS?<$NX^VAݞ20~ /iZ]Z^4rH⍹ua.7eτ5/7N}'-),hF]yVk(xxD?1vƁMm=[]f ld¾g Uz[?D9|K,m57Pf+-VșD' u9 OZ'u[S3Z]vZ7EnPJBk1N1'o<<=Ǿ(nL Φ&Q(۵W9Kll 糬:tмWFT3^Im_.ߴvŨIx ^kM%-س*ΌQFdܸ^o|ku?Fkm{ĖMs#iۃ"0 #ήgo[7u}rZ^5{N3ru!yk!ueq]z/ğF?YZ^ƞԧ'ԮN2iqjѭ2pC#{+=nQku`OS/-GLˠZŢfCyZFa\T/l9a_Ҿ |{RJ-MTXӡCU<}'^5.^xOҭH#ǚVv-$n f<ŷzeEV+S+$sT]^ǚ%>il,sotߛwzO}i<-uBxv4 yg2‘m0V@53מ'KlD Dea+3-6=|i6YmwݨBVhn`㜩oGiO GUnjrܭQV(eJmo^]{:?t F%"YL$1ȃZ ~SjB1ȶ\qw$5.YK `OW_ΡGv\[ZtʵS+PO$W$}J({)mLJ$)ȯ}voNߍkOatFYo -7L,8|v9_ ^dNS_Wl"hJ#| 6ŒiX٬^Ac b6U߳W;]OеmZXӵ/ CuP*D ,_:3ߨ ./|/|=}"hڭgh-eDGkf-Qp*ceͿ+]>uQ.4٣.w,Rn_:#fCy6km6{C2O\*ʧ#>x[IW\YZ.ryXI /2Լ|5ƣk>od[`]$1GHT)*Fk]tU:VXldmծVE-q!fڭri/MVꚵ͍^Xy7Vg$lށHmOo3iy/nV teOA _!fo>5xSGݥV6Cs$ITtZMያ2VoVI(ndXX<|+ea95 /(I{fytY$Ԙ|%|4'y٣\o!|_;kz 'K>f8: 4\ݗ󽼷j+_=#"'ǃ|AIm֣$I72?".PzO<_ch$Vt-ː#  >%_MZXNiw`xI yWRj?yu|=>J> v,#EFiY@Qri-_|% ?`(Ӭ^Jm:F{y- faq"@Nk~$}=B ’7 G'Z,Ӯ5RťXjzWߥ=܋33X+2q^OE:ZY_jd;nZRY"y%Q/v`J+4V>cP6?~&ҡl=Bk5UeG^Z-8_I7B~jz=Y@qjh&4 w> ^~4(e4e|l>Inmr1pv:a_+w9 &w{Ǚ} Ŷ}?AX3x~N֥/,hk"K9G`кdH7m~?<o<9hoc d4r(7F0y*`+.[T>*:=v}gvhj+{81qei6ri쭨x^}w cCmO$ ))RU$Tkmw鶿 ׊)5߇/`YLdir3.qs]sþ tkZCqAdl6r@jݯQE!Q@Q@Q@y?X_u,c`YzR`^*@) jFSi jFS@c5>Æ-{Vtk;L52ڬ.FH5foֵwg[_jY!.,`cyKVr>NT*W5iqNhnpHE,eA-ľ??]ja*wufbXXVw'4kgi=.)J/>S3Y75H8'xe$3X*1|EM i1M"DX6Wz2A ^8Ƴ%޼l7~qb$!ai/BYM}-MtmԤcr enDvo~ޭVмEsea&[;K2$GGO%0A^sEWJ^%.$F\д,f2"J@PȜa@=~9[U]j߉#UƏ jvxnDC3ēfpp׈?~eťxšUqyow=iK9uv2L@[WÿxB{M;8nqc{8\/foVK[+9D~܇q|3*m:{)#};k ^Yg2Esn$fB< VK: O~(|*.|7YS]EYQU#2@$,'SX jE/Xo {,d)&oJehfG։Ο᫄6$>XH@gld J]_?32gıiکkx~B*@d6F[y5󯌿 t[GEԆ8l/%2V/E\%NqXFҐAk,$ 7 h nCJ 5QV o){mdVf=JHcؒhde1 '/xXι}hkx,ż"`gTu/ĩ^&S_:x⮱=%.|&m2?>d])j^ U 3L"G!FR@*q޼[Ν{=S➷->X4:;lm`cHh@88TOR9Z4/> ɡxվ[\;G*\63W?h4~^(֠, <' o m -o5ۋo Zm%T,j$$@;pxVŏſ E]wp:T\Y\ۣ2ːx $A"+ 9}h^./exW:Y<lC[,O DYNg&Kow/M]qak5-[l`@"?woIēxyy- ch- y?gvyKds[PeMY~ͭ6o~ݭѽk*QxdqQ}-_-m>6yu%XkzբM:@m?[D;wŠ)߈|?m|:mRV7HghcfhdvAǤ׏<;/ о&x·zVˮCEfbԥ1Yv@LM;=%n._L~6Z/c~caR\{D r rAQHZ37wF:Euȓ5H 1V ̃w$ÿ jwWw k)Qvl@ KX+;|.3X῵ӴKyL(kK"NHI|I=_;7[k+!I>4ꋠI=N`R1p|'x5* yB{][%q$$3e~^~:K;-rK632?}s/𿈆/eD#sy ᡖ2I_mK\7R5MS֩iv=eQV+ @rtڟRWŶѧu_]ivg|J"nhe2m54mvbKedb` |ZFM?H!xE?mN#\7@ʅ<)!OɁhcRk/I~~yV,!3yua40ܭk,Lc@#mʺ=-o^$Լ[|K,/#8F0P@GU+ OV,=+Cx2KɼoF:uܢ If\FJ}k.AҵMGT⳿R}&ue& ZL^-WW}}5uј9,3vavRj򝝭|_~&x¿:oh94!5.9`U`FC4[%FJS^'>$xGAh$Y%8lbf0|~Qݷ?g ]]?=k:˩VPߨEr9R]*pk?f&OV:wD#VBq.=f!Rʹ5g%X?{ZWҵ}-?\ʌ)sAylİC) Qo-7'“h䰵4AVh$y3H$a䌂 dzxĖZ׉4)o+ 'Thl龍V<5V T8l[w_ϾtQE1_k>귚{Miɨi% v9^Ku_2:4RUʶ>t^~>ޓrumFZPQy QW4g<_ ߈|Ic/yujmh (bOSYf>+=1Qu5vHE#vF#CA.?|Dgnkhۛ}CCt{ԕ.RN, %РحAφmþoOb"ru+!I^;iHm¬Gpvg'> |K}> \Omuuʒ&/,;ۡxVT>'u;HlKS3b$>>t Ht}/_t~nj077u (fR0R@qS[+|?_\t|}xkK5Q<ۡ 2-#9;'|>c/a_ZꚿAQkgrI!~v'ZG83ᖕMz?ag4 xoVִBi2S]+TLxfJzޕ:z_A`ce' Y>z⛍BQQuFER.woMt-:SԿ+ƵhrjjnR9"e!P2n'4yy~6wl=5__uD|=~7-Mo}i$W4| :/l_a{]>>צ/JY 3"%b pm-xV=>+O\u b[.Fi~UQWí7o'jՂ` Q o'99S}h8|WGWtkGTV]fiP%cX,:p7Jo>%.sg{]i&o:\YVxEp9ܒ'=_H|M#S4NVuM6{xX[C #5~e/?-m/TH t ZPiBT!M_ro%1׾C45C[95 :H.cY-C[[0SVC.ܔ |[xs񝮟%XxFVkɦ\{vȈ=T|99AAxI#Ԗ{=h\'nmru&1/[bҮ$_4[)Fbz`.:4o̙'%oOy?ij"uMC@Ѣj M)mnHP~FY LGO|:77׊,N$]A5 +ʀ1/x3Þ5ĖJ׷zq;w+"4pK Bmnl5xjZ\^F%WDcW1( nڠTJ_֏vկ_>*QEQIg0$FE-QEQEQE,c`YzWךxJeIz(|.aM|.aMS/> KKo=E|B d.ٔGo8Ǎw?>_x ܉{ȅ : !y';n9/%ЯS u4I‚Qd' 98i7skq#VC&xv# p:yt6QE" #d}c}{^Z|7zuĐOCAdExe?>6xCMxn@д_M"2HO)p\sw>'xCRLjd5[s-Hc̉ʌFAG|xm$os,-$`+g 9Y*D=|Qខ{M3(X3>ZMq*,R"M!]:Ne5:/#֗ͫiڒ=p#qc6ĒPHJ a^>Xh ZO+7K19uCyCfq|<A'P:Zvd{!k\jO H7׼2÷W6h>gu,O;0 1Pp9'ҙO▧? !Il$U2Ba*GPl9)"}6]v=]Μk1rzr7Zֿfχ~#,]}FKs5֩y#]eڳ/Q`gsi޻=<;_X HN !x3@]λo4:γ/33K+#,8^~Ο/z/=|?{ۙq'qKK/%W^6~i8t|u7VWuP2Ii3N壌ae7a {ho]5i$ Aic)U%I~KOxM״ [[x\Zgi5 {dE|mR;qߵokMNVMz^{e+JSv>|n N1/hiz{^=o什Ygwsrx?▷aV1ׯ2btO0y3n63ҚDIqf忋g/xǺgu-@vPIurVEAy\rFI#ğtuƚFGAEm5 {[A#uٛƽ--ԗvg9,egMh<bXks}q5(5Iu+,Fp͸͜⚵o?yr)5j3;"[M5+oY7Leo0Rp@u6|m/? 3oۢӠ 6tY"XKZt*YK4Q~QS?h4难k-Vcj:[Co4s>T3a ⾚ogSaP5ݜ,l0T>WO c\Ixs.v7U|˖@X@ߍ vk߮꿯ynj|3|mckw7GxDrڍ7K6+l%Ѓ,#O_!q<5z_2Z 2\E(؁ h-ơIO\>.?4wy_[]Miujλ_˞I08ݻѯv"ꟴ׌φ^>_3Z~fXX$.0$BvihxLx[T.._r@79T%Abp+_ZA춚e*Zj%+)WPY7}ko_#D mOgZȨ*TPw19it̻/}_g?z?mM~%ς4ˆiu ,T [j8(-6|-%kVi"a,II~o 'nCnkn_MNU~ HQw9|Ԋ_&DE,c2X/ƱBWPVbn;#hm|!]$Gfcjc-%mݿ+~z_ş'^6,)}m#jmZ/Esm$e;M =Myš.o-VkZf{x_{o̲0AXfPizt:% kO%:^4] ;%@-K_3HOx/In4o OY$:^,I\m;Hfs?_w 7zٴo]G/-J~n>+~vچm.HmClچm5i44J\qY]#]/-<:̈́Zmw2іlI4EJm/=NYƓ:ͬrϷ}Ad;I#~xJe]H)o*I 9'xJeU-oa-(aN? ]oW2K$5IhѼWx{tbS|vwI3E@*}jQYt A Ϋe<HVIpXZTQEQX-W\ӟUPKX-fq?I_;O¶%ݴ HʮQ7`ë) tTQUuMVCԯ-u5ԫQ34j67WaIFC+ V(5_ej:}!"AJnG iEPEPEPEPEPEPEPEPEPEPEPEPEP^i*?Gz]y?X_ 7h>gOoo?i~&jL.I)v>(\ (f~:|Ggi_Kx'E&GGw/uE@Rԋ*"n1_xoHJcVO[1ҫ/|:6+qv5ۄ1RoOkV?.'YuF{^,4 3o=EI|"@ o^2Q|.qa1t/L~ʞ9ohGi=Y7IJg˝m2 uk H|-?jxa%qlw~]iShZd\91Y=myvGSw4meT@U v?5rJóq^[~7@#<Zo_/C:E \2+[]>O^XΡah&Z7$(IvwOȤ9rћW)uW~vlo`h' U:0=뫠i֯iϫݭԡ bsP'sF;M]VOKpb ":*+,:4k"FFTCȩ(((((((((4gkҽ.şU /JL QEH;u ^m;u ^h9)EKçO&WktH߂195 |h"|3Ά}:ɡ@"nۈώµ|q ᷆/|CMF=/HPeYIUQAgv$*H@W^,.~uŔ夁Z9GInKDէu:(Q@Q@Q@Q@Q@$-O>xoXi+{&-6;[+Rᤖ3${YF*HlKxKK-{I[$[Lug?a#'c?hOh^6o kRŵiW[p khv,Wu8e$KLq:uޙz~|169YSuqko_Zӯ㻸S 9uՆC=1Je%k%3tCvXw~2ePb,<_7O@u(-j'LbZhk)ܶۆKW+{nV&6ӭ {o#Ķϧ֓;O=/r*oZfoa{ԑ {;|)M'iR|Z?MI \jӼGj3[N]% v }$nk:)w"Iu޹{>.~!ˡzγ9fխLvq,FTe)$K S9l]]BZhR2#,7NlQVIPq MH@I7IɩJ ( ( ( ( ( ( ( OV,=+ߕ#)QEQEQEQEQE3@j1WixN ]fHҤO4뛈VQqr"tD/5JG"c_O[xZTM;3ZGv632eN̰<`WW?m>j/[$d0&*[]}gnQ1 v u A~'?W]DžSx<˝ -Fp /ue;ZQEvכ~_yw 9i͜ڷFay&q+Zep:9^a-grM/$*kZ̖ͣ\ }Q3(V%>[=#~k')ƽ-^{ \eu'i۱>R*/_O]l Ogv O[=JܽJ;;?g›ktjVօfH_˥Da K0ڻ!ۄm`X?<-K-7V5ŷ;h.6xYs,wc<~5񎏢 ;'A[31]c*A'$k>x߄<jL6RCd !&A pWW\qNW{;^߀_H< qZDMu|׷,eg8TQ ̾(7j1Ѯ|]V֐,oĪ"?;=~’v>S`o}K`kCHt9 g~/x:ύ|;^ڬ4roV-N*I6Zvko6O_¾Q-DŽ.m3-iR &E3`@]OUƣ^EF?l/GKcܗ-?BeYJK`7"RH ^Tnֵ-PkB)iR+XaGz~/O|Alj;Լ/?|N[ y 'r<V奲LJkŽ;ƿC&m+QC.V.',["h[c>fʚl[̴Rݓ'7c-^|vzy~'eEfxGUkI\;(w*C ( ( ( ( ( ( OV,=+Ln.[?qTQE"(((((Ͽ~1j0fMǖ:| #%`3fn>V`2I4i 34 ߇Rx{߈g)5ŵ\" 2\_Io-G"&K8KtLg ݃oM"Ia61fi"VicR֑XXȥ #o/MI')w{k/apk.5;kRd;nb*# w2' W}bD]:{x?4M8<O~4x[4:}Οu$I4%TZ!=aGk\hzV`&39#(n֓o F͜'eOW⮿{-Snt,S]v؝2zl?q% gǡiwiU^!99W'pR3Zz_i;F}3H6R/ 7B?-bD  C]-%OOE;-&[k|#WD1|2ዻ/Nm<4 F.4+&[YZbKy@M1F1\FGË|n4#}y*AA># J^SVMY/^3ƍX0C*qޓϽd'({rվgyD :Qӭ&'%X1|je–oͪ[Rfe-@?{>D#G8+i_TZߌx}luK\z.*,.%Wx_s()} oI",ݎD2#nI}9V5c_?ik}3z5k ukfs"X 5=ͬ{G1 +(oJו(PQEQEQEQEQEWxJe^^k*?G&(@e6@e47B9Ծ$;M4ZU&=hfȧ @:3vl~HBIx-mq\x;dWclhf-ta[BO(^Y`K}8cψEmĚr&;Tj6PhdˌYvgpz&QMŽ߉E ( ( ( ( (>$XkN-'^/][GGf+o76kT-lU}[x?ܥrND"l}SC|G׼?xKMӴ-j=&kx[߄"6- 'th - G~О nϠZA-?!2 Q#rd% L-tWa/y?MN{[ZWV1ÿ'&cէJx'\7@7G%Lml(9$MuQx⎳ =ńqkͼl+4##2 k-uemy m5YaB2r{[Z|S|68%,V痨GysyWJ2Eɕʆ^^3a]VU>-O:&}>Xu#L}v7U{[M%kQK}SV؀NXq/ \k33^iӭi,%+g̉.:)30RTz_̖k5| x!M=kTӡ[7wVE6 1z1:n-`/#!>|qyZE4{)fFP2q#z|B?O?SQ&+MNźL Fnc*X <#9uoSD=G1Whpb XdAuu~"[gG<]xSt4藍5=(0AD 3os|okxOtk/\ZD4˻PhS*xX1QJ>~w kt yoW&Fկ49 _ŕy ь$)'s)R[ே!7D<5x2.ǧ}\N7oDFDVܪ0@q?ЮuxF?|AaqYCmYDĎ,[mls.+>_xwV^H6CT=)Fݮ0 kmdܿDߎ 5_՝N cK}Gwٞh Rа Wvୌm-xJRy/YwPi6`t 稘V$s-{kZεsoFkYeeyc[k8Gc^g~x_?-VOu?e>eekqwpֆ|=GG8W*{ԫ]򯸧ѿ?9m co?gM/sxy5+ţv<4CT7 S:|ǝJ^׋mkZe+6Qm煣mc8#]+Ljo&:Vb mDh¢l.\g褕K?'xNj4u/FFx_*=^Qom H8 $)"r^aEPEPEPEPEPEPEP^k*?GzUy?X_h w_єw_є&|.ů ͢Eurb[iYQ$w.x|5GZKv(. o0@P$^m&}SZԬ251$@9}:D:X\۔L5KKKtQEQEQEQEQEQEx"aw97..E[;She`)l 0[.fc(\ ˞!|au>j>!XK>˖U\Ԝ.O4(t/&~t]}#~_iU߆oSq!mF+:ʪۻ,aSY o5~ Uբ6Ɨ$0cl&w*lK|#_ 4 x"']6$ ( IrJ^0<=IG\CV{/6x~bj<c8(=ϋ?h_ xZmJAԡ9/$+' ,xWWl9j5hzqyjFW7w\tG]|_S o 猤u'(쯔f0ɑ}+ỽRB4ZH\]}8_xڿJ4v_RM=]TQEP((((((((şU /J_V,=)0.E ֿ5{)ֿ5{);"I4Ab8cH?\Wɼ[{;k˝^H[q7P~QpTUck::tm;\; 1%F|:,֭藟z袊`QEQEQEQEQEx(3נx7O)=m)\A [<3Dd5 ~&׼]\xSM{뺏5H'{̖@5،zBqW9n3VڏmGMWУ:]ڢiB*|9;WƢ>){oF@G?'dvOJcgxD'<VmK$c+Fd7= >~>!26ڴk.fO%,&Bw1!:U5wr_>GzT7z FP qVFH=A"ekϊ Ōr nMϐF3ēmw^xͽǔh[FхB2r8 㧉< 0x6ĺx )rS2x)Z7{Zߏwf͇.b3:e%e-||AN6gsdzx^T=aiꩮjQ[-q,>Go^m*DԵk!0/q#<9cf<=jW6Nd7r.u70H_ljbP9~7RN~MДWi7VxCH|=(oSaIqyD89vRQQ뿵HH?nj,|"ڵ֥*%8@"۴d_%'ZuaM_2xgo|o[ >n:~+_igxF\1RC.31ǭWPu][j3̰Ea$Goc/O2վUc+.F)O[^ZZjKP;7AM5%h[{QHaEPEPEPEPEP^k*?GzUy?X_h w_єw_є|aQgGYմkWo-,o";@9c>CߵߌCt#kv|oo ?qqsm_zǃ>EtmG2{:5fdabe`dŸ'm5VkSʸ tI*# `o99-~ ',|G<#sİ_E/e8D'ހ G5gú5Oa]ZNӢo$.$0Hbx^Hs {{]=jh3i$eo8*a(Nk˼=(xwDgU|;jzCq4ʒ5N6T_Gs*n WDWԴ-O@mCO4#ΊFH[H1'g~WzI7^, +5\8t]2k?iWTwZ_C!LӼwM@^H$qr 'Š%{o[nvq,<5MbRAX-z b 'E0_1P&Eqm?IմO'Mrͭ><Y]F+wkѼeOxn MgZ4ɮ[HU"9' ~?_^&O<*ھxxre/] 3ʉSv7I)+w+}??[O>nx?_kvז2ma%3Hg}ڃ qD]sƷmi>1/[ߚƊ>Y#88gx^]խC,r$i14Z`7˃j.iOhZ.[*u)#RKSFcxuB-?}ƚo|\i[>$OM6O׵緂59 "Bc̐`c5⯂ nX7^!Z~e}e\H[xdܖeG.s>\"h/[D:}qqqW,I&ER#8;֓_kgW~)[h&w[VY{)gˍ8F6֍IIߵLij% 4nU` MV@݌k){"ߡ-RJ /4M8xH|:gt+nKݬʹP%@p~`cxOE?kOjZUZ̷א- Heʡ* һKͯ\MIǸQ_*ML|E(_w²^|U<xvi&s}H!O'ύ/>6^xOŒY5t_EqwisG~%, df}_r[O袊((+|Y%ZJ5gkғQRNA ]kW2NA ]kW2ᡠ+ɴ:\vW1t9\N@WO߃>)QEQEQEQEQE}{}> b{[gw5+BA9 ASc|6C|s+hڎig?kv-+gUy\#~&5<9[m2 u aM>8cc8\HqݴqT'oگŭ/mLj_hދKp:&8V.I&1 ]UM/ }_ZyවjًZӭ|2\ y<6XьFMg?>@8ylt饚o6wHĦ8˖܀I+uOat/Ro>h$1>n10 1P+|u?C:4XvvvEjf2w,nVWv K%KIaK[V}}\s֣o_-|Hq_NȤraudp:]sĚOSⷵ5]k&;g&Ӧt #\ +|zuR,4GZKIя5(΍ Fm]<$8b22@ 1KS{ywwyiyX/$Z !6e,TŔ.~o~җvռo> #R.>'3$ 䭭>{~VҒ.}T~h|pRtT@3 wl߽GϷv3+Կgoj14Wj77V6702SKnDP@J'~ u7e<NѰ;ûp c \_~0x⎦ _A5q5tKfy'pH `ĖtoboFM# _|goz,_$.ݵ 9PrzO?~Lj CV֦Gִ^_A2ż2H+<Zm [Cv!VIbVAL2?* [YoKwW\u=1fg!(nlT(tZVn\ooҴg֗oC]厱vu ^cdIdj6 4;Z&.5[RMn[q1z W  P[t|/x^M.6Ӥ{IF.yF`|DB}{ޗ]AK0Kaio`k$ơ&b#2"ԕh )Mok=Է3Oq3oGVgfbz<2u55_jVZ͕Ժoa}i"~m|Um6h2ڋfٮ 3V7dHW/ş-\u/t9HO{ERwI'DZBF60bc~ [~)x^t]RRGFˢDo"JY$5O?Vm?~(0((5gkҽ*şU /JL QEH;u ^m;u ^h /~=:u'4;3Ml3Oa]H3s'j k|:6KxKTw+˳#1yU?eC V?rO^Y"cv- E3=8Z[Z[=Ɗ(EPEPEPEPEPx'/:u jB,N-2U6{ oM'׏5g:.GOm٤2yEե{3[i5ֶ>,ůHF1۵m#=Ǩǫx2}Kߵ HKXҧY_ WFU~Lq횒IKIWɍu~I~x5v];Lǘe9B@;rdd?v]Fzr\9#B_mIF,$'־.0awj~՗N4sݽݱYVh?d[_ MƉxlL7vh8pL㟆ҩcm+LGLLCk#.B#׵yOퟢ\x~d-B(lf*qø^Ve|^x᧌5d_4hRydgKt{;޿⏃.coZO> C$'P aLdFHE&z _.<#\9]HŶS,eLnt0x+P|ium|9 Em%ydR+ /@ ǟc|b]^/ WUI9NvuvW\ZO{י JQr[Z#?߃oM_jjHLģ7q8.-{[ufɱ>^2 gɯ#KK/ ~ҟ5Ie▸om.WKU"Fqneϴ }W11|Xsk:e{rWԮ#D ʱD-ܣ<9Le~eI⏤9=|67?K֡>k u$W?Ꮑ|ExAIyC]YUf OvSt=z%[ylcRp cɾVKnj)o4{Ȳ<W;+~7f>3MO i^S-7MWXeF ˹Ry5x @UP"4?2odpq/ w߂d> ]mPu VṯsD'`p20pkKHAa/ x⎥}'iYSk .W`|Y5Nn&U0gdd +p i+)v~5 P$Eyq=.f <־{mi߆W+nMDvAlUF۟r;>GKk._ j+yͧ p7HOqqJ\o=Zw/W&tK[_ܲ`^@r *s |/šuei>fI2Iq<+kZ02šj(kh-L lHcϳTR^_ovQEQEQE,c`YzWWJeIv(| .aM| .aM~𧇤>9t=B&ψnr|^r֧SVgwz߆ oj|;$/eӆDN@kmzM׌x'Ut~ *$M^uկ';|NmG{ 4Wrja\`pkCG "Ju=7^LږzW^am@ۉ9y|@|C]Kc6i ^(bgg"[JCfKol~'ioNj[; \jzOjo2@ʒ|❵Nm]g"-6 }E@HsuM^S:z|rYY %@"5; 3o|!ῇλïޛs\\[V5wan鉐;\ŷĽJ[1x/˻ I5{Dۙard4RrRB*rF&NO"|9Z]=Kt2oqYok0~uuMN;:VB#NOUUTtM%<EҼu]i76DW.K872BMo$l ] }I&+xsJi.{w}kh]frAR>o@,|h~6¾-w,:&,e,6rI8CLiźG ?¾t>ã][iֳm<*D$tL+oh>M[jz֝j7!%[ۨw=B,a{?^0Omk-dk[u K- 65VW}] U`kle| N[ aehEonQCOE]Cr~צhMVm*sL7˓E# WƭDxM׮|Q/u'FsPKXQO$j7k8V /xSkv>/ :۝!mCJ4ٟ{>vRow~VV'Ώ?-!e"Y>Y+z3]ï~+_ xccYn5Xt{O!Ys<(U ]/'5G㾕yLxQ ]j3گ|6G*J#@v!wkQᗋnf"If(f'$s%/hK5y{6K?|@NcĚ[M};Ae9t}M3Ե]CSTG{vxb ,pp˞Kxχf j_|q^m1.UC+po$' &ź:/h?[ϳ9 c n8U_q-l(x?~RJ.qm ȴv3:nLrr ߲M/wZsN ܸX/wFʢKItD[{g=mKJdѴ5 %$OkT2٣Yc ,y_}o "3[kXEubb:(,| XxSlqHg!`3FF:8( ( ( ( _V,=+ҫ|Y%ZETSZӾZ|U: FI,̸$q$r~j+gDҮgceS D$9Y #kmGTYcwa^i1 ]Kvђv q5o_u?Zk .xfJr/ 7m'4 w:+dW⿋ {]aki 74)X$~N+>*%'m|4E4OG(+n{ X|/>Wt5ݮK:ۉ@|%WvkF{~_&}oO=k[k{ina䰆p]-N'p: D^ %6gycRP2FcF³0Ey |P^$汣xx\^ײVgm& ,H?j)c(m 5_>* HCuwmynda$bI[h:(EPEPEPꟴ> 5./:ׅ5o dI$Q,HF@g 6Ft e/$^cxfk)bn sbq\TŶ5Jѿ?@(ʾ/.ֵ/>!jVc6#hENJ2o/*ג] mçjŗ.myHtK8*9#F|k:o?f ö g".bqfJvb@Cœf6~'Zڔzjx;QR .%7!H`"I$ )Bٵ_~~K⦅b8^e!UoK;[-5su Dؒ*D.wiJmO8nw.NK{qmi'mx6=J8=륦եAzn}qJFF") Mǥ b0( ( ( ( ( ( _V,=+ҫ|Y%ZETSZӾZ+Gi6.[T-ݦ4wsg6&%%A!ZFep#'߇4W~'Ӆqqkn%̓oPLj9n٭ٞ={t-.<qgEfj8͵mPɳ *B8_߇L%o_/TQE0 ( ( ( ( (Ѿ/iqIxq$cqio~f1K 6xf*Lc}lotl%?+~=l}_wQgEuXY^Dd݇%WWE׆-5KD/iըW/RBW~gmNݮKX}kM#mC}[8dݴN3=U*A|/#0JǠW'9|%[oz|!1.sO>*0ߵK!cUl&pY×^5}GN4Mߧ4EI. $+[;mOEfKgM_ϬEfRtv7䁷9|(o_ῇ|akcqkVi{S,J 6Fq^὎W7'hݬZF :ddQ8F۸kdRG4+U"1YYO ('ОzOG> φ'׵-NC4[, biB<UoмaxvVMVѵy4K|Id"9'㾕i|G|4čMԖ]$ I)x"R_' V|C/&Qi~ĭiѾ܈Kp O̩Z_ ]l}oZGXVwIa49peWVx@eiZ7V | Dۏ>44>xF񦹬k1Rno7̀y|*_.i2ּ#y-ng4-ÈR WG\}+_1KI[G(OZd~:hQ5K Hѫ#,`AVĿ_$*.#la`!b$7JOog/ 4O Sogm[ϨM0HPow8jO'8|^_^Y\oqH#Qs o-ᵿ6wA|?Wj4mL8!2d"D*q? x~êNUYwl$0*px#mw 9j].}mv-3/ 0޶ pAʰR 7uGƺMԟNKykr-*/wb92n RZ駪O-i դ]ẐHäFC+>O2ˮV4ޙa2v'2zYZ5f)&QE!Q@Q@Q@y?X_U,c`YzR`]*@)? jFSi? jFS@I⽏ Aj:mo6itIq21m9Zj<[koH[atE my`"&J@Yp2Gs?*Kӭx#W_PBՔg G8~z5w:( ( ( ( ( (<~:|;?! }3Qh^RV*5ty@~=6bBƺ,~$iGkI`1u?F|1}cR ZӤŧ4ê[ Oq +qrΒ/-L ;mbHZwIh,֍ EVaqw1,x\ylwyKxƓ'sE _jzYuYd eclqk^->ї:Sye*I4;;Oޝ~d@Ze?ɥWIۿ4zߊ?|5ej:mouYK"2,3,1N'Y(]޳<`} c\wc᎟c}]S8n +k 3e@ad?:>+ 񿅭xN6}6%ќ)2F"Fzg5osD2]?JO94xH_!l.F  Ü ?x"D [M{GFq/̿0^YxW¿x Sl4?m֞AŢ_ )QEQEQEQEQEQEQE,c`YzWWJeIv(| .aM| .aM7_/as K-Wӄ1e+&Ic{V?_%k>Γizɿid.`Plk+ S필q}sa<0G,Z4 ;A$d?3W x'š%G(JbE5{~eo?ߩQEQEQEQEQEQE|yc>? ]VJ-Un# FH0nOe~x F\ӵ{mTg$`(bbF`?{opxZH-&_,#1  n 38C9fx1'6xjQ?OQ|-/^}q7U֯ݿZ_Fsu)cчC6o"BֱuyEҼMQ9WtТF* gp  ~T~6iwt_X\[t@d@pImˌ9⮯W=O^:[MsRids46cw3|һik ;NJjOM}z]zΝٲRwڰHDG22 a<WxwQ𞣨xSW|C?t9VoJR٠|Cqa TْY>7wgcxJ*/3y;,Qğ| E't ϡxWUؒ[[9RO*܈w;FrQmi_WOT-?oCgxsš542D Xdn<5W-nu:,I;moq&]' o:-G]x⻛%;ux#[;C8'OfX49cԼ*Y[ƺ~a,-UVIxc4[k~VOKG;x_ލеBPnu#xxUR8q20*>*= 3;uY 1crֱ]M*f.#g w0}O^xA4]sP5>iך*ͷE(My:G:\?wUE/o|_ڛDE$iQV'v+ҴuE+}tO袊EQ@Q@Q@Q@Q@Q@Q@y?X_U,c`YzR`]*@)? jFSi? jFS@E⌿ < g gVIn&X;TH+C׎u/O,4;Ժş IJ$Zyc9.߁e.Z_Z}yͼ*o@Ad%v8'uo,#㫽RQA!QN=o-?[oh`QEQEQEQEQE|lmsO/o %b=2Ql!]1U?^~~|cm຿lToIKxg H21Bg?3ķnlu8ʂ)LoOzskxuY%b[d; L/-כ=ۏm&㩼W}c7K{8!9YnJH|ݮ[anr?l jþ+ӛJ{$WwS#2`NJWP>eO~-RGGǂv4jSk12,O9 `h~"-F.?>g$pQğh]%#%)户k˲c{V/#IFS2բNi[_4]eQrn~ZΝ// x>)mPm2#wdOЂ1x*T⦫3xƳxQ4+McWBҤmxt@ >FӇa T]5>WN0=ku]&OGn^w6^'ѡhZPʸXexD񲑓Ҹ_|i|;tOZGou`1Cj䑒o@?xV_ڇ¯h͵ޫ,P%*0*& x+G7~ |; fmPiqeUd6l N.Zwaz_A/ڏWF-of$tq>p\JX0RN~UbC> M/6hu3ְAkX4C'K ezn7c#/|YqKhMh (Cg#M@Ngvj|9?6q\xR=«&Y5-IFyZWHk8m+7:#xtjgRQi=5id"A Y$`1#|.|)g6:Naiږ2z=d խcuGݷTC Hu`9/ti4Ki'Q7ci  ᛁx)&i> Ѯ]4I"&0ۼ, @$3CT/_t/c(Q@Q@Q@Q@Q@Q@y?X_U,c`YzR`]*@)? jFSi? jFS@d;|+wd_NLyrîpNjj+w7Qh^L{[Om7|&>'Cf"aCs讑>!߃09'5̩{ ۙo28KR׾(xW]ӵ|9:ψ䶾_;{ow d;ds坤8E{Or|I c:tZ,}F*L.08܇nk4/UG׼=xgR𕝔: ؍CI7 c-w~k1n6 a,I]=!䳨M9vUd~-w_mQE"((((((((((şU /J_V,=)0.E ֿ5{)Wt{o:t13cϸwb/i":G쯤ixgk} CSk?|}7茚ܺF♴Ii{_Gtmۂ|$Z9]Oᦅmik~&hIy^]!٣*LR@9;eCW޷rCc$=ٻ2[0 WhIYA߈M[P𶴚嶥2[,sEE$awOwsNe˺oOma&;yd_1R_fwm >-x]Û]O>Xvk+yR(X@\ xɵ?75x?e=6 W.4?Y7<}IfZBr{.=~I~i~[_ )((?m9aYZ#D=LJmF&|!YcM.H1j|#p]foXo9%bg[HYPbFP/hׇ|GM i.5o}MEtȥD10m <:wj߅g<'ix<>֪KC4^V!G (޶mKV>¡WK^׉mk-q~"ԉ&Q#qw `{#o!k]RC]Zv3cđ*w szc*[H:U{7R%t²EJݜl7\ ~Z4ֆq{źzT,ZZX*C`',l +Udۿr][?zQRPQEQEQEQEQEQEQEQEQEQEWJe^^k*?G&(_Y?]z5p0ZcYxK.h0[:uܪ]rssži*/C=j{G} %T??QР h}(Z*/C=j{G} PhGڡB%T??QР h}(Z*/C=j{G} PhGڡB%T??QР h}(Z*/C=j{G} PhGڡB%T??QР h}(Z*/C=j{G} PhGڡB%T??QР h}(Z*/C=j{G} şU /J_C=yNJ]dbQr?ץ&( ت38L/)B߄ni)&YWM:vffbrSԚ(#OкY7G" to"z(S.MQ]?,ފ?o )O &+8E<BdEx7ɿ#OкY7G" to"z(S.MQ]?,ފ?o )O &+8E<BdEx7ɿ#OкY7G" to"z(S.MQ]?,ފ?o )O &+8E<BdEx7ɿ#OкY7G" to"z(S.MQ]?,ފ?o )O &+8E<BdEx7ɿ3PyK{JF%xL`ƄGP2U3SS.MQ]?,GSY?u5Og OкY7G" to"F?izͣ/4UhH ~خ#-]?,OкY7[_p<5:KU4n?,j" to"E<BdEnn?,jб??@_x7ɿo>էi3E\*H#f*hn?,j/4=By{^1BJ͏S;-K" :oh(EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\MW>e;<,N?@w࿈ͪi?QCw}s;DTENB8xm\_޷cax4&I7%/mn͜d9dhId@5OX5_I̾=7ÍWGMN쇄t" c ժeՕt稯QM |B?o~5TZ|DᛍJд54VWcD]K#bF䲎awTxn;?#캞4VpB\Y*2ȔS=|zIl[{ۙѬgwIc%,'{}M[\cͷk<91*FAF2gh.o4X,Yu/0M, ˗,)7ǁ57Kk֗^Ra C`)çq;]?A;^Ku;; [ nXI Ef$ʦ8]ݳTt_x>6kXmP#HY䅚0͞pIxI'mKZ]=5 .ĬE<dG >MRmwe*J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( |Q_I^J+1RUzWUEy/Dؾ2M4P Y]{cȮ^M&9.3{e+f?'hl7{N>4b}4OW@}/?>o1G/Dz ؾ2M7X/b`rԲg@Q@fotoxx-18.01.1/images/paint-edits.jpg0000644000175000017500000003400313222767271016052 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 229 255 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? { x<>}XjCSc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G-M}ƳχpJoɗ|ȏ+~b1o '¯K^]=#Ŷ>%ީg*]ڬco R̓~C–A(}8 >0{xXT3G5hrЄ3yw/ x zE-o 1\] $7#AI\OA >}]g/FiWV1}SHHr* 4;F7.mculLrG3S>tC̒!q=oW`FA +< k zHǚjE8T^q;:G?]Z]jWzͭ`H57K凓j@Q9\K1#aOl>|o|3KL|#s$p]k˨Oga" r23jڵ:'aO! B;zbu.{Ye ʹ1P`6,5+sqe]]>8T)qp WQwYNG+! P m8 Cy+ؙ5kx̍ ppӲ֒^jS:}ڄËx,pimrVgr|Rk#f{٦HI3I?̑>sԷb'ү% mrZG'.fEq=vyu'܂ާp=gVQ_?m(pOo1E\I}pOo1G;2x[,O'W;2x[?>Qf??}Ei>@>Q 'b0-WtYiƦUF~b{W㗌`JItY&6#.zm(pOo1E_gR^N+M$SU< bС+KM\9J%k[8.f62 "ɢA ق$$H=k_w?d?ø|C'.{j_'{Y[Ke'tdy *^k2+ThwQ~}k\1*::V ƿ@>Q 'b\z7I팒`.\j_h+59T^ܟZg2>a y"[9im7)c [:[FX,- JV(<ǥ} 'bw?d?Uʰ{y[E x:UmEu_Տϯ8GGQ_篈L[3n}cEUQEQEQEp/^o[^5DKvp ,qWioq42(t*FAuWVUv᫿Zzm]J*ʫaGRs*ᾩ3H&sa:eՎ_]"/.T21Ci$oKWH?ma'oyx/,n HE6$LXTY>9t[WW]o,-N;ۈŤ,oR7شm(YR7 `5oRskZ9ꚭ]#e*';w/[wJ^lPI ^Qx|YkGP-TR/ʏTvg=ѼkXZ&n4k.dEQ40zmy3tkOզ[)tH.YN8aU,cj$cqF]i\+\@Y9^q:x~¶:tZ _\,h<3lrrǒI+ԵMOÿaei}CA׼qc_j4X9V3"FwT&WO^/Gu-+J:Q5&teYnYsԜU89t{+~a'nѭ{espw3ex^E #ծڵ=/5O tUOK@S%ht ?_&-T.5qi{F*oP 2v((((((+5+iwڅmre4\i$q2ț>o +~*90xL {\Z[ChHMaFF|/?{ihλ_riǗt x7W>J? GҵV[JW(1< xן^|(6mB_ͫYrCrvl*)홆?+A9!|Qcu=eΫe>= \  s pʛi&Oq{L F[{}Qojs:ٻ*$Y1b //!;I蒽Ȗݿ{?>,oޥiwښv<ˆ#Sz+yn[zW1\K]Eg5裺h.ɱCR~&( F+,<!w"ӾDwŤ^fyI/9c I筼/ڣGm=%ڭZދ~ʷIT7#=ƏCQLY֚}E #Kas18}[__:b>Я4[k/K/m !j~"}6/6>4 koe  VM hȆuZuv{}:9LK9nF1 Ȼ*O[i҄~xJ.ޣwsmM`1KVyRCqmվa+?(J}JiOzo?CCKKs9Za#4D<Ȼy$7^ԭ<5]%:aʽiRvui.ZfwUې _) wĞ|1*!D2 I'pF@#<4|K;i9/ڗN{[;K{r;4Q!K*ǂԾki}O.?_=FďxVpIig=l[Jx|;R{d8q^c <'gxxk\m' fKv^[ϗc8_sn`xEJȱs]!ѩX5,(@QEQEQEQEQEQEQEQEQEQEOS),Spl G*ڞ!??$nͪ$s_At 42GG! ?,me*C/?.?TX +?BNp$, FPqЯuߋ?c5屹meYM&(C ~f#T|ETCXCi]Gh0$s$q ] y_{[x+ ^#DSGpYyc$nr)PxGò[-$># v?y\1R]C\n x]m5hu0-ۆEQC,2O5ICjbfӴ՟OG9%>h~j`[k2x'|O.h\Iuj04X$WjdF@hj-w]/MΧ}jwHpdY , xj^$_EfvDRO8u>5>&^^xF87AX䷾QneXO#3Evy%N~v׀-Xh\ﵺ{%7%aFm͓;bմW?~'xoF՟SXOew6|+,%!m+(h|Q.bּ)xEJ<9uoXDF"h~GeᐑfkOuzUz4 VXcI5uV#j>au4rC@lP"EݳI]ջvvio[IAoxzTjP%j@+>#-۶[O'K6n.a䉎I2I,NI5x?W9eIm!H!I$|z')-#g~Z_ Ao>0_ .uluUT-/QB땜T ^[|C׍J ep5qϿ ŷ^#̶кZ9oK|((9Lg85/tAX8F|i? %[ǏzWM>~wj7$6Uy/n|)xnIn.Okop- Ԟq<5Og ^tx[}NPtךVp 4n !)`潃/6IͬZ\[IHʰКgo\&M՜)OaGf q+ñ @'$T?@zNj|?2x{o Und;e_&';~x~^d_O<<.NѴOF/&Kg7h-T,eԜWGٖ閾 (ڞi%y\ ٦PW`=3GtۍBk>OnmKu1sR,S"c['[^_OU7vpN4-,j7 $gd{Wto^\iQUݤ9b૎PR80Fr"l* NK'P-m/ln0 e8 إt_š7O<+FW`{[3dD"D=b~|/WakVkۛDǾYw!P8;i}6 -[wh_zw?4y߆qފodwF*r3>4TEhV{4_^[K *Eq$J$h綫k.Ft +kN6sG2T˫\ܢ綫k.Ft +?{j6`i6Kq%ֲLVvpI9=1nQE̠z~+*z([Gح_>o<":;hb}#c@8" ( ( }(UT #V(>o<"b }xE| jF(TP:Scƞ&ռ%Yt;uHˈxDMQІ\#9Ϣ"u=X[C]K%HɆo,d aϥy>$K5KAMֹ'[R M2m} 2XՋߋ4jjW+xdX Pj,^w KyB3*ǂ,kмK>2\wBM.-;O2I'8O^ mr_6E+EY5 an̤M <+SO$.&Ҽ?Hnͺ:JG%xɋǚYPǖGWl&ᯉn<#zԺ%:Q22:4P&fQ5ikk6^I]aiK [j]7<%?G'WI4ɥh/B <{U5|],6VR[Y7C$q:G_'hwZyocq3Fdin~{C?v'q;H'̙Y|xL$h\t e?ٗatEgf\ZqAk7@4Vwe?ٗatEgf\ZqAk7@4Vwe?ٗatEgf\ZqAk7@4Vwe?ٗatEgf\ZqAk7@4Vwe?ٗatEgf\ZqAk7@4S!Qi]EPEPEPEPEPEPEPEPEPEPEP\-σMs_.-g-!Y.c2?۲sOu[,y=3J*ʨvp-}PG]ѭ4-QQ&3&w28dVR0r?*=[gtSTKCZMj],+o0́'nyï)IڥvM3Mg$-%%2cp8`RZoj) (((((((((((+ǣ|[=D.eZ{+ѹh/+?C^X^ =΅L-$q1$htxfil5% {oQI4,zE^G(D'9,ӵz >sI? Oӻ[jv0Wk}x(θX%0DUcVcѴ Y>Y ;5X=GrO O~t넞DӭgCHї@ 5袊fotoxx-18.01.1/images/graduated-blur2.jpg0000644000175000017500000006313113222767271016621 0ustar micomicoJFIF.ExifMM*JR(iZ02300100Fotoxx:paste|paste|tonemap|gradblur|tonemap|gradblur|gamma|bright-color|resize|trim| Fotoxx:paste|tonemap| Fotoxx:paste| Fotoxx:tonemap| bhttp://ns.adobe.com/xap/1.0/ 1 2 3 0 8 258 510 1 2 2 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Z( ( ( ( ( ( ( (BHJ6{E4BO,v*3y8rS¹Ao8Hߦ;zgұW4p[5co[Rс4幅вH PWkkI7V|(t;뛵Hm*㟯<Ӹ斳u+g.&H۟Ҵ*(ȣ4QEQE% ZAK@Q@Q@Q@Q@Q@Q@TS -4Q;JIGX7*.[ ×Ǐv [X+<ކ,wX*Ś$$ HT/]>x/mwD6UszjuPRPEQEQERRQEQEQEQEQEQE&hi0)y;-m9cKvKzoٸn?S5K]:2Ƞ Yo2;78mro[<CJ7,>-ᣴLr83YEwu2=ԄRg=ڄVHcTgp3L3Felc,xQ좣Ӕ`l~$aV YwyE߱vEU=@#'˧w j͞ictl@ z3o >X ,V2^*<*;N\\@ دeڮw,0'T\-SZ܄K#ҬAkuyRxaLE2 2}iW0|Cm#.F)Q}r.Ybm@]ɱr@r2 4+,7AT褥E%(QEQEQEQE% *":u>2,A^0xOq/6ref}f\ֿd.k'w;^™q{og$k#gUrK#XUT3nR4՘^aKK4]& h2&-9)JDҪDOxujֳs1d;Wh5i#,[KHboSEÔa5Mʯ'u6]~k&7Hfl;zӼV~ w)$dfRҪMtpW5hCaHWSY_;جdwը΁fx]aP[dsPuxMkf>5=7 ZuV:%ոP]W`QaH,QQ23]k¬uE,s'WSe9I7"0ў^Uxqٌ M8]/ڣ!hm8އ?\~#9D(kQɖH/Xp(((((A8V}, H˶z ތË1#^x-vGSk]bk 6v`ږs5@H,L< U.~ȦmXy'֠ܪ[k:֢[Hhb8-+|5vB\T g֨^j̫c=yys$mdr14_11 `U䭕z?]6nĽHhHaZd"%*{iݕOGֺ$bMF8k\.IjDV?Gqqjp@W jXMI*yPF΄בT.)zcҪ-65CF2d5l :vJm~dl a]-GF!\(RcR<֗kqsne`4%K 6Uzrj(joU$#jڢΒMom<Xbz .RΕs''ɾ|m#\V֞waHݒ1>58eoA-/Ty!FX_ sۚMKEdQ@Q@%-RQMlHT`sY1}ˉZF ;Wjj3?*$sHDmܗi*4 N좷J QEY!KIKL(((()ay0[h'c[\y6$8>V…1+ [!m!2tH]{XKKf,#=-in#D ⢰۷!2sX8-z'*u545Nsҹf:ہ,8\haVkoc jd?Z,-F/8L""NᓁhU-%#On]DpgԳ>EF @uEͶ(qHϵCm|ܾ[dqʞӾ54hO+xMEsIV1–+˔Po*`zg5kFWAD3g8'`QrNu%p@`dcL0-Rz?Jܸ/:F~F]((Lz7fPX#pc&ɵb`ԋ͹9@+'w !plD2)9@8dq\EƠ̂6yjñ'Vee=qQYڕI>E,{wzs.y>PvYĥqwtO5ԙb_+]n ݽ+K#i'dh_%<3! ZGJ28 #9'ҲLs|N2GZ{kiYc)܈bYVI iKuB:jEj%n{).P۱[@ CFvTMN>hkrEpmrͤ~Ub) Q?hT V8џʤKf?yHiqr'!y;DqR>j6V-TQF L䲒y'PuH6!FM_a#5彺G J']6+CMtlrĀ\novP'N:WE$h Jh-}8QRV营(((J}yϲSSK"EHQMqڗ2c~@\v)+uYD_/IZ栳+ēzؖ5ei'!A?X_YǛ(Zr6#K1wskJqQ.Kl.Սn*>|O +fTƙyb/)i>g?Ye2ƠEWp  Q{S-H@@3q\޿ ?FXo$+o]V(3Ҹ+U7lD ) ̿(M!n%-T qowc>i AW=ʄu4-tW+8TDHyG r>$V>BdȾ[maqHEu(ݺF 89%L |1B#Z1ziGrUsĊZG'cj=95Rl2*M*0*@1!H hs{Ief$Ѿs<Es^Kn ²G`߂M (UQET3fI'޹-GP7ҫ"$9O+܏-CoH=+}X׹;?3_`ӚH3s9oZr:9FaGj-y~yo5{Y$^==3O}X׮-ѹ[ʱDsK'.'ar S`K}ZnpI;S̨-r>(;(Ӗ{~xm{ȯ:+3ӇQH1c$c<= 1wxEF+YȪQ~c) CF2pUPT:QxTskǂi.A 0T.@ QVdfL,U\qI\{Th~~ISS"1ZDF ϣz{qZ\{zw/,YkGjG b`C pŜZm2w8#ֹe#(8i,$#$5[5iA#:UD PC?++xGQ(K˒HF0~r?QX:p ʪya 뫀vÁ8EsL{y$?t9R {o'-Xc4Xú K Y5;u%ll5s86) `x}쥗*0H䁓֥ծD |X*+@rԞDixN[9nePw7cx8Si푊/y{y~Cp;_$y\1$q!O#QYn=9)lKBHV8Oz颿Rc럧Yi{u^R=xYvI!NylpJUàpzq֘ѓ9Ͻ1ߘF3I$H\$`mjf?teP +ĒA=)[8{(h-6pW%$cquV s}*'?Va<>pIN5Z1B9ޯݔQY#:faPG+Ѕ`Xc3Zȯu!G2HCKpAzإcp +&H(tIA8o",㔎H턳*(~cVX`S*jD#P6d8[̨O;ϰrڝKpf>$QBZOfytH.MԌvЂG?Q]- bl}?f1i-zm,n.|ݒ&)׊/kd~Ϧ2Ǡ>?.Fcj4B s]Z΃6O#JwwU+lᑃ/υ8x[ӆvgD}+-Q郁K\Η$;;kh@e>V7NqU݅W *:UL$I'Mәc&>w^Eph /̪}kTJaSf ^l֢`! uJkhGi,ZJ/US1"QEQEuD,993WjP7`9q\]w j3H"ܣv UW5snTI}*Χ4v-}),kj F^Zf(}8n<l+3ospTqjKəK|I,[qoJXMcPVQy =>2'vV䋫;3ȫ7r mI@=3L /`B*D=+C/2+\ -K`><61Xܳ4gFzUF 6\:ne';f6^ϱ#B=ַtd9pPmSC pcҏ)Vi$f6*^\ 7&Dqjًvp@͕$y]\Oi X1tJV\weEt`i֪Q9͚KVtAޭZWqXz n?uЂ2aNy⹤u-Q2I&G:T hPģw''}l1;A,m8V5.’~U R&DcC2bKRGO [b3a\HH?.U.dKCk'k7AWȉcB ֪ꑘi7}#]f WGQ+cx6bjt#A `VNe'seLfU *q5Ve ssҪG6kܺTr+J4tk2$w\k[4fԑp+ZlU ϭt#ke/d3Jw?sV4=ٲ[d+6 >&uK]9ҿ̆gx: |EMTz|&>fR@=k;Q #/zzn zѥi3,Ů1OaMӠy>[j{YSkjIH(~u=ؿW5MLPH# Tݭ:}+/>"]"lX)B9jӜbN}+sN>P:W;DL^v3McPuIe `EO$L޲$=:T-JlW~5jv*FA`*۴bkxbv;1Xݺ>Hr%V5ď ۗf?٨MtWRdQ9dQO*a5SÚ%LNkķ>]yi؉J˚|o 4m'Zv!l1=j5{#UcSL[mH<UB`b --%-lHQEQIҀ*j7PڳLC|$ XRmIlN LL- R;T<=}+4b:c)%x5n;DG 늭eӀ9Yf_1U.>U;K{lcWS=> cO %ؕFpXrs@B䑜xLR(+J/olՆ =ET PgvA1>tDv"\fl/'tE-$űɬ+?x  gFBϥqvQViT<};V(-ʪ=2dƝ1 qR4 Jert0+$$sVInX$SGB,>`_\Rh :p*Õ XiFJ]tPoB.}i`UG?aX̱Cc޹~[[6`B+v`(#zuئ7BW9xOw!\by?Zlvl ϵS1T[[q1sPY],̱=@Λx 6TH 1XOf\6(ePҥmA71•+w6\+Gx5Zdbܻ9'NV Ѭ^[8ThQd]=:~=ci*:DǷҴnppA@2lRJEHkdA^䩍#[x<#`^3ҫKUMq nI\]c1k\Ť\ ՋDJJrz m,|9/?nJYrOZu̫okh`E] @<۽ObpIK!'=yQ_k֐sU&;;VVNhQw2]K;[&ۙ*Wxo ɮG{t+hi_R:OU$u'93[*>d]ճmkGZ{T)%"ҺB2=Ƥ!ݣVQH,aQU:IivZ4vD|ZD"@ѱAQP"pzWCFE-qںI\vV3q!KpUՌsv̼2W[gxS2lV.AOz@c l[ ܢm[?'ԏSb7j};u:&wljtKe{%%^#JBrgW%:y+˹to8_Ck5M̑֠$yIݟ=lnob#Irs"c j=vfwӌNV";}6742UsNJW=a#) Ll6Jbf-QLO&r͆vU]<:l=_5;u6bXyk0z|=$` 91Y4ø9ۑs\u>#W>`Me,td^ԁX!~;bIp F潊K;nAW#faXkN@$qZ(N `DT9c՗)"E;H*sJ[ L,I`geĵڽ,x8#z-( >8?ʹ{P۩s\3 WRۯfZ(se;c i>82>j׮2~UdUL<"_#"KS nZt"O[Ͷ%#5DJ=Bi&XycxE.Wt8׊|7#OOҡ?8ۦRUs Lt2SK܇zgڴ Y7jb9IW^ ꁈ(k|bv URI=={ )g O@9M0ev6f%ߟ @ǧjGgܯ A ~|W #xU4*{k.ft'rҶn- (Iݫ qV@ʨ)zR=IDh8➇h+A'=)C㡩6E {zTRcSy9gRzFtԑtD|UH}9BU cöڢ(ve M1\- }2*v+v}+ ɧBi4LlI'QѠ&)S[ӏƫM5V'՝w(Q}='kf^TaҴu=J$fq'XʹXHSberh. 9R#(=*(ʌTn 'NHJ~rͫ$0{mnm7Ҩ間"]r @8$NkXI\eY…a6wFOUZI#ETH:6svligM5mMq0Hk2YeH{+ARfkr-좓%^\Λ #9r1y5ZJdz$kLO+jHpo@)BZYwߣCnoz܆/D{$8֣cDqZ!:w۴ZՕD ~NP iǴ;Dnp'\fPjOuv5)tzhrJZ'Lk3䁳R@5Ns"wqҳ/uCJE3ir4A:Vt85rN:xOqCU>NA#d7W/?4isq$մMʟ V 2 &q]n\`zmm >n;c.J͊TkIˌT2Oքr{M#603Y?|ƕng "# UZXs8bp%|uN8ï2cۏʛxGHџu%#jf\G4{VU?2M#<&w*p޵<+kJ[b*pz*yQEOxWqlwW5h 1_epRK0VO-I:rTV*7,}]p9w:oٵU^;V%7 2TwVƊw_-䈭G$pݿ:.5z-kӖy=I5m^ۏ°7H PPs&`i-RSi^JT9\-ep6eH<+B>ZӚyP YQ#s3׎i}cc}Óch[6ڍPC8qڱK?Zʲ^B}b  gڪ\'ʹ4r3Xl~7xH$k>JW!=jgsV#=j bnnx2~Ք$~5F9T@.3ԳK'=*ǚ/ǜzT dac9HkqR;k@nP">S@*ۣǀѦH*s26SZv[^"gpTnj|!窿ZHI:/;o2'5oh,-n W].Xe }zV6j U\)I-mK FNӎ}+pApWstw HQ +m38}GRd@ ƻ0zG[K` swZRE!zq]u#O,rg@seBs0:dk.q5>R1O޺4јY-Jm4l.Vs"%-R[)` w1Cl=~7kSG+BFqt5 R8 dnFp n,l1=*l̖&e xkNma7LO4l$j|縮+DW௶FfDDq}GM|V&O?VmeXnm53~-b}&9$2=E`oKUh䭋bGGhn~-RMtY?*+,nĿP Up򖑽[Pn&l DH?zǷsf |v"jG8izҀٺqW=d(2R!;hcPQj~I,a\r5mJ.TK?3fֺ2mA+6}|huoj3H] HkJŷ6ʾVaZ>#GĎEr*D;5v9'D {T˔^I+J wn=ROʮEs.ŸڔȜIdH;4Γhݶ/,p]N}+C8y!\>+GuQX-!yKc 7R1>CiLeV1!+'3J'XL&x&ihۣ[<(p{Véڢ\[r~=ꍦ%ֿk0lvyu6Y Aj[0!+wdv sOw!p Ȃc! 'ٚ5b?΂sZmP*)eǒ*b7x8k6r=WTbly@p+˴mfOR|XU'&TQGKOUlq#5EhGRMQM]LٖVz #u3ְnmc ƌUNْpbSnܤJqzIvaU@}ch kX˄cj]rƜzCeics<*] K4&@-=xJ@Wcc=]G}"J3C{F=˳vhyP|e֜A2rzΞGݝc5X쑈RZL˛B  ս/Su}LhFd#h<zE\Ņa=MmA8` bxQڨ  O91Cd \ sT-nb7+g]0ҠSeON^k4a}X21Ҡg;ݞyE_ᗯLQy$e7p3tϹ j d'*&L䗐n֤ރ=G|9;*vҫJɔC9Z2rg\w5FIc, C}a$(9ǵsWW,=x=5NRiסۀe8*H\9IV{>HHY0=\t}M2[Y>Ќ+j D`cN~G'5}4y-H,L@>g.&G7EeBg=s-0jcT6UzPI>W-DoX^y %ցE-ed}-k}{V!Y1Ȯ2A kRj0Ġ4Fgڲ>$$6P\M|@^'θ5Yn̥vH%wkrR좆<ӖOns!oUc>3wh[ztn$޺E?$Ҕls~'@croKw{HZ?JפӬل?t(:oZF1WbnRҚM>+9c|ǵjV*x~cZVTrޔI@wb1{;Mp$2(fOz. vAgmrõF]\n]2e9TIP tH̱x1AnEo*k&h2@Z{I#|gZ,V[泱bʀ泮ҮzUwnN(W(ǓO(>Bb\֪J0Mj3*sOIޭzWW:VW9;ԗU7ΣVԡS/ak;U*/9^[ivJ8z}剝[CWx_*JdIݫKK]JgleGW]:3}k4i< Ztڃ.`~ZpƩk,@, k_K\'P)LccRVO,D'Q[ڎږi3 26PgV\Œ1HqİjVK<]Mf<@;SQh8nYxz)c,O`rUkwxJ0֖Ծs͈2EhIkr$cxo^|ȚZڤ Vgh8=O$#K,I `>B=*Ιb,yn.,im#H9yRfombk--7B(<\NӼ:ѽ$Px95j7Z֧y#Kcq_OlVuM/D[ϴ3Q=uZ2ؖc ;j{1#gcNke5# nSRsޜ&iNxZa2isLf$w|nJִw`Ts[,2ž% nj:Cv}?`p>T],e׭_=NJ99RŰ#rk&w c^ŪBLxemjGr&Z+ٍbI,yLjktKE)x ?Ttt-Ԯ3Id#g:"v{ "4Oc6!mjvi".=(x_~^Gl֎$&^Kizk$4gw5Ii\w ѷʌ1Xi~Eg5|9C?qh-u_0OkEGi꓃$RӃ3%qS|I|֯DhD@O\9 c݌ J@#('tz>iO5j^\iVqY=)J}.s.D k's_gc`~5xrOYTcj53`Id̀\!֋ J, g"7R)+JƺO.czV,2ny<(hQ|H8#v}*rD(o=horb?Ђ OlS-h'YN)(QtQD?! `v5ok( k^$nt  r}ud;HppNO vH1SA~ WTS]~]A#yU6*_fxIϒEļ4߲+nCҹCM|U$.?\ƣ{UpN#Gc(IiATdnO# ;qT ˗sI52AFӎ21rَμ{ԁ*pe;A?QW-lMns-$"cڴL+oqmiS؏T%M%ƅu,\*^ ]0H  v4ãxr'R՞prnb ^F{ (ljږ U.9nAظ$Yv+\k Vڳp;ym5K.巕1ܧPq}Eli^ŧ,W`[614+rOZ,$F$V ~{k][\[ec!ܤҼm=K7^]P-u^5^Dα#$I>l8= e2^dHiu׮҆XYl1oK+^*1|@`X۽y]GŚT7WL(Ikaq׺J|UW/)+oV .Su.D#QڜMkX(=\[M0*y)#Ȋ (\F%"GlyH}OIǵlDt1fntԴ8WCX-uO<*A5Ҙ[3gj+7+y\,ҹ>W >{oFZ o@Ɗ+Nkʬ}|ջ1ߝVgJ%2ɕ}ZIeXߝT}?1ߝR(cxZ]FGv3޴ߝTLouj&oЗM^]|k)E)n8Abp?d(|ED&cGJ#?u:[K[.g}.ZiX`Ă(k-'|Gî}-kPHG5pm^1bүnP#]LT2+ܡy57g];[[edi>g0GujGHT73>]QZ)9s>Fosc9Ey701 qE#L]'i>?4QPP}lI}Y 發 w#0>sVq)-]'i<+F,̓ԡxE!FIG'}VZWe,2  VlxĐ cW=1LA<ǚ(!/#L0tSI+ļLV TQU=e}kq,^袱62gO%q5:4nEzn>Q\5'Rw#8u3YzIMWSfs1 ]ΣyH=QZGs)d@ E3fotoxx-18.01.1/images/gradients2.jpg0000644000175000017500000011130013222767271015667 0ustar micomicoJFIFHHExifMM*JR(iZHH02300100Fotoxx:trim_rotate|http://ns.adobe.com/xap/1.0/ 600 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?MVVF8S#Wn8+,'v2bEyw2g43jZ]=Ҹ3P:բ8yW BX9mRQNNqӟMHA}+E3Mg950 ZE/uՖTfg&}9UzQF&srֺk,ڤon$1]'kϯEZIXu4~IY\v֫ BTvT,48/i$nO己rGjl &+3s]#[M0GOAxUsEڮyva"z׷xa_?v*y<״x[P 'Ҳ=6r׭K"Uьj& CVc%aTEjyz{vdܞ) .r.ӚhآNmh+آ,Zg:M̭As[u 8rCMGZ< MZHeN1|/tΫrqEmhyWH=BxEKc8w/J_Ej64CSQR0j 2Ú\ 2?tWS$w\M:5XTM]O%8#S5lױۢ{8eA"fPC:VSkHi7sN^jb11YAYI  }*(ԛs/ZϙkZzlvSf3vmsSZKWE# f{XؒÊR<'"k8JY*s0M$Z#i&¡F`݃^LdYӬp=)uvT*A$*CoqQj yr(XacI/)BUsyо0Ym_h^&n,2z3Ln ֬r#ҴOVjl`EsTB=SDbsŞ%5ҽ2['#5gMi>,88j#ͼnjsNE18%I?Gzl9d;S](?ZQrU7oIȢV<>.d=>7vo3FASƾ757K99T*EK XL:Ud52H$7'R5t3g֮OҪ0t64B~lf \lu;IדM\wg-G5'\Bk2W1Կ,wD˜zg9r NM }[-fnn-4*.1ֵ|tK +V43|bU__/"=A+TebTNu_5Әʨ*~5b#cy2n,渓1PxN4`Hd8>]O8w"YaA0+=>]:.U#њ"*I4݌yaRĪn95 B#=xߍ,~pNW n;<ŭOsQnbe*[#p_ƠsmH85>"4׆w{ׯx`%s5H[KюyXy%3W94nK6xFUuî}*br367 z3G3W!T'F)`$FXI#rqOQVTiK:화E64x0>٩eOSJ7gc+ 绉}ÓּVcݢExߎ<=̓|WFiCҥF7G!~*`٪jUy8=AOMhʾ vBFVeR3M)Й{գi1-zE^+:I dZF͜q,3E ]0Ә3rĜW5c6`ލ0qۊoʸFxp喠|26޾rZ^ICWֺLj-ZBJcR|ui59= ש_FR8=ӦEjÓAd c݂oipW4f Wi0xu+nKSn#]P1]vFz([1;{ƽ\ZQ5v0Ҟ1R[N?*|#l+ȫ{ #$.U)} ꧷ui0;ٮ6mVc? 0QD*Mw.ON@!~n"&pn9.2>C4rl0T⦉WIw$(^RI%Rq+w6ejqlݸֱhT'䚹3gU\q[CBEFx=B풢6Q"SEː^[N+ʲEu ].[)@kC,D^}?i^+PvP]veAMUU>}xn62;Wi6 _`VZmk Nrec?*D EnwPrcp~q1E++.`m2\]" xA">L|;/@+*4.'i"!OQڼ.Ŝ)Bѐ썎8&ĖhW-MAP!O_u Yhy-+zn 4pp;s\=$u ;W%ef=D@" WHA".1.v%t H'ϓO#-n 9\.7o^!] Ю6<"&t=8h|mG^0\W 㥌$ǑELP *Œ,VGX<,;QIXx{TpLӥt&5vyҠO/""r5z3Qk,3"ܐ:ף@K)dYX㧥R eiU޷ˌxF `m lr92vpھFR}k>fnfT K%9QeO}2 ,p1^tUuk|nw%6<SrKx"vVGֳiſ/tfRw? ~o+ʣt+`Lq('{֟YŪjs:%[H#lqy A޴F<хkbؤNGOҲZO<q- g卯Д2WKgoq=Ƌa29F t+85{mlQ "*X9.#xy xC#ӤXޅ,OR=0k {7T6IcNVI]mƖn]ܹ<;#H3 ye*mCQ1l" 69- 2./X6k9Qh7=)M0?L-K]r9J6q٠r۰dzV)n4y%dgA>X֖o^e1ޫV5ba;H.8 Moie \c>,)9^tԢv_֊Q&ў DέؚM[x"Fk:{Ԭ7t r0Gx)OA\lJoG#q~,Гȧ9(ΎJFh`uש&º-/·bzc\ ÚdPHKz-DyAoe W2A-:{*ݜztjO03\u"g{ܙ Zռ9-@cczף\ydL,1׋Rk6ڌ:sv?SD3iY1kQH1l~:ϼƼl}*_ 7.$ӈ^xCȂ?2FIߝ\RswG>v9tz¾tWOaQi3DK8$@u/l)jԺUZ5Xӷ׽e['8>RowcƯ^zIL>J." p |'+: [8e.1ֽ3K{(4 S~xF֡pĒ:q/r|Zl<7B -ެ?sZ61{I=A7vi_6 Eubx-Pw',I>Dg^K l +acyq NdEr\g鞱e{[~8y nVw-Դ JW` :\G;vOlx[݉'; K!fHtd,h`^O:GH{zUi~өPzu:w ڃǻG0*>}&oS·hѼTFpzgTRhnϧV Kd,̥uy0gyu詢9'I8 A*;^;V8*)I IbH4I+4@ǯ:Ml(2[̲jG :4|@qѺNLTOq7-"x?o#h~p N=ϥKݍo6ƙ`~L$ =>TXL)1߷N+@&v$YXsn<IUu&ios05VET qmJn nr~vMdF #<{TMenqH>Л^Xs,|ve8Q[ܲ,V>NJ$,TӊIo 'c:WK2Q Ma?ڋmZXHH0S-odqeUX`*Gso+1j}sWZq!bI26[=e\y|}FQ)99Osh:P1ڭ.m%}+dG*|FCnҗs+qn9<-WÈ'a#ȸp;W;hDx#&Xz\)IWkʣ%^q9fV&Am׊EЎ[CYiZ3y8k6T).Tߺɲ9H-Xc Xz g:N+ve]EIm[{|IB2Ir]zf.I*dfg䞹98v渹hܫ.{In)kvryIt tgviٲ9g9O?h:/ qJpc&' ҝ)%;Ν_4([z47L"z ܉mϗ9j7e< ?Jt9^&ʜ+#'ͱsWS-4=;@8=sDE$QD^lx>"}CC{K,;``9=ң4eȼqb>s.H-8uJFN], pN8unkw,`8'+K&]-V三RU@d|'8?J.4P)۴cj=hJ G 95bM&qnձ&%!Y :'9U ,6+ρ#Pqs߯{6D 4q9Ϡ^NU43'҉|;5&W|0vg}3ꩵ-U"S6JN0 ,x>皨{!mr+(Ȥ;XkLٌqD5]mƑvItd%[dI$VbNGr~"f54k~je =NwFuOǶ;^wgX-% :d<sGG08<>Uc;aXns 1އE$>Tai[IPd\.vD 09@lHݲI4|ܕ=.Uk >m~Ϋx;Ej=s,X}:tǵ$(%TWsp,#J5;fq$7`n X?jXYZIH]e9O⽞eƵR keI 9|׼k;[R%[Շ 0l zp=sJQnbWw=zW3a^Ovڤ,I \9ޖo=hfޠ00z͔g='ծ/$cpʍ0FIgz*A"y&;ib&VVHcA/x U偂T@6㑐8waͨIeŲgf0kj=:[ԶΫmPAϿJϗMF d $`ֵ'EIXZ#\6N^1ޜ3 iؽAY2LfT?}:WlMldD)m98=K{svAnS8v=RK|hnǹ~Gӏz- E˿b}xL6'v8Z$%Sώ3'{q+ 8wa#2`3co^G\/|ukfИdnslOQfxy˝E0w8^223ߞŜZ+N1Ikfv CǑs]1} Qy,g址ف7ӥl#P){.W &x$wE״K붺G&"HCxtCyu(#2$˼FKO8{Fr4oo{{Ëy"\O96AAH$6 OYY*`=Oi:2[#9k e}׌]XPYQ֩5q"[Ys?^_A<#jSX&2CkHH9ず=/m 9 XњW)H,[](*."H[Twdglv+~-cy6=9:֩ZI3Kĸ❠hj\2,YܲJ 眃Pp$'^pxszޥPơzebm%mB'=+Ryl-Vdu$\2]On F\uoi,爙զ,iE'pF{D#@d>Վ3$߃ֺK1M>*ܓx-bUΕJ7֕K]CS[u퍙@#qjik}-;vk†A+r2Aniw_C~0|ha-b8 S 9=‹©yhuV߾ P:TvϩU})⻞I%',v~Z\Zm PDlcg';GN:VNi%u0{}O#qFrH#5zh-"1.瞜*Oңd,y$c9큞ޔ;eH`p'qY6dC1bEknB4 N֩"d+w~d#=?+Ӵkx~{t) uaS{1pr}}3<<ڼJdN*JHP1۞U2io$P uϥ?UC\i۵E60pq3uWifETx©x!IȷeNfq؎OQw4b3A;z/^O_n Aq֦-4DfwoXĘZVl?ps$5iK0zgJuT AqO85Uգ6PJO<1C5/r*jޤ0ǧ/k+];` וK;ߠY|Am9 HܞSsG^M[F#j9<j]n%ժOpnTdqFU7 Q s u $s1yIgb0KKZeb֩-/`g>ʳO5Ty!crUVe;rFsԜұ,j˔K<N~Չ^Lϱ#,Ó 71S88<}S^k +@7VvrHēm?6:dsJW+{۹TiJTn8c>W8!Յkty$RCz@SӭrQ_f[$л; ?Zria,;L7 ^E N 1Q˃=kFp䈫Rg;h"-{ee+ܓ!k*K+[H I*z':\1յKo 6;[mIV;%I&DUa!u>SU;DWf>3|=%LА9bS@@;A.`V ` 3ePy z^*]Vm>ռeOv0ʤ ʠRϯ ׅ5_ ,y&'Һ%FsMz9|A/ ƶ?KI0VpɌ O w~ZEz]+YU*Yp@OOA⥷Ľ?˧&e,QT{wb7n۴_ H4k=9 ĭmm /B2w)ًY3GNմ=Qitb1-ȫz>8=OLڀ;&I]9;û!u.`-5$X`ȸNxpvt-Λ hHd2 29.'ҹq4Sh$ԣgA3?ȩQEtޔ7rۮ`WN8IƭY>;+a5/3GB8?+"Kf;Hao o,xǿ ".ڪ0GU89k7;vrޭ*h>q$8$=yߴ\#5K 4 @#v3=kJ)ʊNY-äL-dԾ"R7r-k z`t$&scYM4e@1Cpz p8xRZp1=v˒hGrjI-NI$y~0x+k+&[>u=0 >qSo1nC۸$|s]vvVq ҿڝr2I2qcf"!$8swɮod:~ɧXmF \1ےv=zqn6q H%~] H@88=ǭG+]h~+xTvr8W7/?'=1@M 9?xK%LiMJKC<z{W˘]-NkbpI=^=vKp'\^y 'zM󩯖|GF<9aM)J-YnyѢpƘ OBpI'9u {i9+r :FsrGâb*x89 ,qڗx'O`#yKu+W0>ZZYE$-CDm@rK7^)ԣ?:'Zy1n@%$ ~>$+H(eQ c?¹)ьqJ^{F\fX[Az1k+Ϯiww<9`XsڽLe('dkCc.fAp2.G} 鳻jw FB@`O s3mZݙm-(# z;՝Z.it0 c$[ ss^J&F;fb֒WbE9\c\wRIXšqkk8'=wֶA?ش"ƹ ^& 2T\2Xp:8SRS2i N~|@c({c8d8՘? rnuyAMry* aqg`oPy2H8 8`U-c|}?^z lJ{̙KiE?z3'|)w ^H-lSIC\ ÜG=0H=FZY3]gkQwmm1$mvˠl\jwN#i+:Ad/yo_Rnu[e\c&Oo+Ŗ>M4-(u cm7+޲aunby3T{wp92`2@&v>RZ(po7p鷾I =T˹;G<{VtM,F;[ycĶ59d_2DvMw$eiDI)`Cr6.YT`HMlB=2፜bf@a@^<yZ;PWWB} [ӵ7u?Omqu#%Ȼ\.XF ubxI/3 `G8&..-071v?Н-8R-h-=܇8b9J}}J K2i0f8,7N][K"Fbx3μYhw~$xHɌwSd/@ВGnXZ0Z æ"9xN^&Hw=a^jw"mA:|ݰp3ӓdz5e̷_1LqSk (S<[ItƽJHBUvo ֝veEǔ$c&6o%. I8兦c`?P2 O Ky gzu\i&pO~#xkA{FwE+לw.~/;yoש>?=t=п!/MWDhSfANTodhaqENqr7c~\T``r KA$D݌d`q;zԓgwx9ҸT!Գ5( q§CמIv{8/3upTm.(vQB}:ul{Zh؊al.4h7ǀyOaU^/Dk9$ijdbQ׊юInƪ ]J?($]6 %hTd`gcZMfX^pq8ozbѦLzvMJORwY-#8'8\^wzEcBi# Dc )G@G[T;9lfԳ|㵅68]͌9#?^zլ Kݣ*pϠc}amPsQsVSS6VnYݭ+%eY[  54љRsH\D6;IO9 >bsU^Yɢ>R``{Ci 4 {xxgDܳ E'hϷ_\Ľ4]>MnNhDp$^6e=:ՙ`5eDVvGpOSB`H&.\`CpaZM2Crz!|w#<U"='vV2?57]Tt(䃑qz٬H{w=3[:eDJ©@?S|FAb"MsO_g= 2-$s8¹ǶJӬb}+";XHYn]:.{"[YK@^Tea)3Z\V7Gy矯ަRqG_oU㜬:*z/-v+ 2lm#}3YrK:KH$QE8POè'`9Rs֐ {sJNkIT2D`{z2 1sQhQ<>G:}(SV=ҼŞ ookkkGE&V`?kxI4U2v:{9F+\gM9Ulќ'S37ncŷG4yhlv tnEQ)̾c(#/ju?W6.yn?qnrpv=ה2Z'QѴ+ML֭o] ߼=j'KhYo ]'_:uk?5 Ot¯2=H9~ ![2Np:5[I=M9SP{SYԫG 8ryg1I};â{ ~B ,\9r\W[q9HvF9s:u=q[OVw4C9rrAC,O}pkrh@!ѷ< =x>Ч֥QF)`qJHs]]3[A\<(%\o`~v[b[q8=q= Zj7zMF\;ĸ8B؃~ubK1ǒɜ{uwҮur/DL7Cq?21{_h~XK5ϧyFz qӌβxkz.ܱ~Qx?7MԬ?jT&9R$jр7tM O6٠!&| "b2xF{ΜKC, ǹ>-hc%Fq?h}O.q}` ^6$G${s-ܱfD91qjY1u N9+J8IX|8;Qd~I6a ˜ qGSy/Va(Lg?4~Ѡ;=8S춶(/^]rű ɞ 2Gzd[ۑl<ˏ&59 9sYG=3[66B~R#+HTV-BWh$2N7tQ&6FyXO̫뎿jQo$'c s϶? 6FY qtuUaOVrtFƑ`avLww(Kxv^qSxK7o$"&p [EݩLޞSc4) sxF %7W't7K2' U7AW(uYEwpuT]W"1 _2)ޠQeO?*I!䵕e`UH<֜WQIݎNx&r)5FM~$wڬ,e1{GqzMG_UP LIz抓vCHoZK$4Ҕ,{v8= ܟ0IqlXxReFyѱbLcW=N?ҢEFFB>E=VM[{u'+.q~x^;ŸoމaxbwÎGbz{r'xFy`3lלףZAn0NHSO޺Pq%N–ˆ rXk{v 2}$WjvE氄8#<,{דA".۹{X\FXtb@|{JT$*I$;k6lX6.Rni ϰl?{[ S!Pnaxo=|_TG]Ir؛bĊnEBF0pVȷ˳r 'hnu2.WsoVi2qs^T%rOQ:|RtxX|}m2!yq=xNAmr?$xg[\ݽރAs$'`>=A=@K,?zZ#xjJ7Ql(,##OLEl\ev,A^F;W7𗊭KoL"hwLnd gx*;מZ~XU8t6n{WUxZNuU,.u[oجUtnXgӤ61DYW?1nݎ8u֍LNOt#D7]1rrx*:c}RfrHG]r#^{vƚH`a%zq^!|-ncol Tdą䟘Dfxn,-tEGJB9^&ڌ0@[jlu9g9易xjKUo/AGrlTx{jWy31Ir&+i[c1#20<`U9oKfxqO_x<i,R *$r%Ē9Oat,IK3\G_NrN4q~o&)ʭ8'?]Ţ˨x F*{ {7ӷGjp0;>c D9??(o\;zV$yed'nuʖ™?5u=G,wzպ졠,'W0'0N8ɂ "%WpܾI57FH2+٥j6Jm$v V|9֟cSaf.$ez#EoRc:j;B]߻A89ϡ3]տ]y]q-̊0,%#oMǟ'޽)'s7 M+QY#mt7 y*9;pj}VkXݳ$yj0:dsޯX#(P@cA\g|C$wkdUd A)jjHh7BQnomq# ;랾\t}6)彀;7$eX.1zS;s\東eBH :Nc2ZV/߷oA1Էa*6*x@;ƵQn6,I꽾^SKk$Qc3MV-B)- ρǭOst%9uML[yaN zz/SZ,z C{4S;ժ^bS<98'p}sZ4eCa[rHְE|A{\ 1}p kK43؎ =jTkݴ04fx  I$-צ:ԖzdDQOnʶ;rہH qyw&e+AM6G J|eۊX0;K p,X5-opۢftsS,#Qs?q G%E>-Þ=sIWq4PyBeln݁mC*O$(HR8;&r͜IOh[` u =i<^L3@ uԖ%hnҹs ֥%w+pO&4l+1rwt݂??Ҝ{t3N6%`WD7KSvU[nf%?1Z1?$o37ﭦ6G\+ ׾+V NGJ@I)2JdZk׎,`Y!VI$xD&IͿ> x7'whЈ sA~.]td+kS(Ttսb-gJV-O1cqWi+٢l7d!3iѴ+dm8wqԖO[`N,\ 2UFބ75!v@wlI^yɕd%{rr{}p9 36.~cNXEfdcgN-X0f3OiTר*GUuq#7/Z,ahǛ2%'#AԎx 鐫:JqɂJybw8+= z]Ӵm=ⳅԔ﹒BL_;W<ͭtfj^E(W鞧WG|4XHHG%r_^~ mŭA 9#޽ ӭp#B)tcFƕ`dQq"swmcQaf|26yڬm^w'  e寈!VG>Gmsz6 +Z:2}(tE d[,$`1N>}sO1^,--ƿv[%pdo.h]r?AQm % ?%]s\tK&X+ QzС~zӈh,19bZNi֧7KGizwW6JHy;I cnռ7oaw$b_: 2I<N[c:O,:=?,Z4E)aN;uDt D"99PIa'sTh2[,¤aK[gO}rwM9{mK{ TŶ$pb>P:\@THt$:\srnqpߌ~5'yƻ4VK->9$ghEr3sNM"1 I#>DHB.AS?D K=)mHEoLUwmNPcR ozZEn y66.!U]Ӝuֹ{DqpY=z@]QQûG{ygkWW,YdtEsҽz|.{T1(R(f<Fe&t؃Zt6?6Hk+BeY <x[xB\+K3G~4meKR(#攐1+NJꑂS<$u ľMS,yAϾH{g<)iKUt\ f8x=k-5}/\hy|xD#۩1ڷ3\)Qʸ>Z)Sq#vH+Ր|[=SavP]Ij"+=#LZ†.Bs"Hݐ=OOJ?(wmoH>Fp0=$t6 ٴsb2zy+voZH>PHW'+ U#ެmE_$ˎ=8v6#WsqzdsYe|Xq!Y:.v`N[\'K QIUUOsg.ze[IrP$vˬJcB2YR=:֍ $8P`8sWHYf*ރS/iV5w-UINI'j1޴V-Q2jqw#Q4qW';.[v3"sDEs_62sz. JL)fvw6M#HÑSAzp܆k|rQ[:EtQeMsBH-<+,[jdj3vIq +}ǥck^mm'!G|}Z͹g[e4þG٧j;)EW-$ēu;Nvω-5%QmXdf ڼ4mNL_I GGbB;g8R_3R4Օg703֞o Ol3׊tQP\!肤Ԟ esG pd_220FcYgZҹ\dhqgmA#Ⲥ4֩H8G\5֖+%)}0N?*˶eLUaҢXPrU1SnUfT$:洍88O5|.~#jF0=zՍ$zyVT $zUVʻFFOA\h %%ہ7sơGT,sI=zi&NYFd:W43mկR?T}Ďx;N0=QfdY#**FSVm 9tnʹ8]MeM:56w-ye ;={f9OnF瞜֍iRw_O_R(#>b1T_HH^K-ӎxA+LN3N$2ǓUSVՂ**;oj$rH=*i(@1X,F}?Lf=rqTIϽ7c֕G@z⣍H9TyU3?:~lتR!mi _jyEl rH'Q:'dRБ= 鑞=vt~N涊(]N d'rvž'~&{-̭kd#p=+ ;;XqHaqo 6A) xbVէG&2w:ù9oԆksgpmˇTw~&A[wPFdc=Oƴ%-1Sto-σ3q,91\ w6C>zb|W!1Ît H7t;d=z؞I/wr$ևU:ͩ]. ݈iZU iXnZ!#:SM8KP{g:IAكІ(UcP0* 7VaP&ەg6.PȟyPSiwнj:.XWp:ܧ* 3*6uK}z)zɉ ɐx3֤ڊ zޗU*|SAn#ҥy;KOOw`Z&tc/;}*j=QGk 0bx;>";dnGz2cʘ0y2{s|%Q*P=[QZ,;c".T`´G rt4!T}z{Ԝ&U IS\- MI ;T߸1-IrGJceFNj'p ǩUp0`FGFi]1zWO]õ O1w U6sAXr6TG }*[fr~am,1,nE') l vto V*hy¹1{c_-&9H7g͌<{zM@sXi6&O%,Ȫ26sg_iͬ f:Hrg*{ף)KcNf%m|O3ܔVg;Lڽ5iq}0T,HK2Iٞ 1yN|<]m{8($;#O'OۭBߴ45[/AVwx56YAuA&q;sV~k?Q~_Ěf5ɤg7 coN08=1Zhɿ#5^6/H.%[>4JՈU$ҹ6È@<*5X 4kU 69gVWƬk%ڊ {<!W;dcy]O]k4x\]K4)p$_>yun%x49Wmo $spkLfUb-|{<2SAel1qEIcWV 8#ҖR7d ǰ>wf>d:BNծ!Y.e\79`Аǥ3CԞn֞c ^"0}*|;ia;O,"MU׸oZbrNȨ,\s1x=uMꆴeivZLFI1%Ԓ-Ўsyt˻F!Gx!P=p"fc1HIR|$W#Bp @jnz{U\m_Sy渒8lnj^š0!2|1=}ik#V!9ߜۑUL-F[^GEsOgp<}+ļQu"L$WRu1q-Y\ҶtM'Zty9z°uk;;MJ[ULarcTehރeڅtUQ>br,qv$ 汓EG |Gvl⮺* 3H=)٢lAœs׭.[ZaHn,! aCyE?W i lczi9SH: h9r{G$l@Թwp4p8_<>^qO$!ϭUVf-u=] ڼrBYdpJ'[/Pִ蠲ՔoII. {޿( 'Csl/%TŸqXNȸrZ*GS6XIl'#Q$ 2}@Ij [^n "dힸ]p+Bè98ףj$h AOzp8ua״^C8ut,a98p=]󯿽?_G_oe4"Xcɯ=7?SSwb4eڹ5A/ ,}qJo VcwB.x:rz]Sne^Far=mANHDž/+]vRH/e{]^̰V;zr_%i>Ց7ZuU~kɩͶiQX]i+ F^u,y&[|4q0=5iVޑet&RKCKFUʞqLdG J?&}vC+%'n^٠0UftDVbw1yȧy>HOR5mJ6I$S^m>xyFEf*}k{U֍4ط9vOiBL:k199^5xAuNl_"n9=;Pv|ۥF?7ԑD|z*ҤPȈ2\db]7=.?UpZGeSzeIx3ANԐǜQSIkŋ&HfW#&_sf\oŽ#SPH*{zVT8ifѫ`],ʣ,6=W"ZVvXxہThܫgtpa֭kuoz;!?, %r!:~?rVϋie*@ڶVDB2Z2't?bb6TKO h|7gn 0$&xJoȷ{+r 170 246 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{׶+?o[ Cd="yHlC(7Dݚ_gZ&hm*ȶ2 qnOM}CGH@?}'+</ _BaXX_\xofw&Ko)Ҽ:{'Ya>Pr烗UIaҟ.[Z~}G+|Kg°ٷW:^(;i6ۛ I0Ƣ7ROs!=^Y\[\C0o qF @?+ УweߗJ?g};PpXF6N0 /+ .$LHTxn '~ߍ[#~}G+׼kD5{#DPŞ!uчt(YAe'*X:o˩=tF^ɵkkm%,QWB3BO0m%3>}G+Mὶ_ZxaMjVqM48r^)H|J\~% , c$ "P] >}^~ yfG- I =+[ٌ\Hc~ v_~_srmz]W曤u!ho %AY|敮 .ol>|?ֺvy8 v_Eu-̚Λc_heX,f 2'9!#,YXZ?u8#FUzuڍ7m TzEO[<9mw{s-Υg]Y HȆ}UIŽW8! wI30z+ WmAڵ K0NOcԴ+մs XeI5~:[x lT0PgL9>SXHmRԏ2U\W%.3('늜ҋ4βΛ5u}/C8\hIПww'Ï3n~{}֚U_ { {V@ &_{ù>şkù>şkrOƟtxw^:ubV*1L9@ܟ?? j?ܟ?? jopl~'xL}m[@a%6_i guV?uZޤo\ P(H p"l ,_F,_FpjQĶPa |p3VVIĄg>pe@_uù>şkù>şkrz__ϊW9?IirdE+2s5~ 𦗬iF6:^E+ N.#%ЕH> ,_F,_F˿_o#{߉^%tiįe}#k*c8m_0$# v$db_28P ykB/hd%6zS%ճE ,ǠQù>şkù>şkrovw_y7>5x6VRaqilWs:FA= P_7",=eW+w'Ïw'Ï(\T{I(@*QVévڤȂ5xTg秩ܟ?? j?ܟ?? jvasSk+8 m`PI',qߓn3Z"c' <qؓ_tù>şkù>şk͈tFcue_ .Y~|=> Vf`dzq^s_Og5?{z6ΌlXίO[vIy$}UY:w}_]մk-F WIiKo1KGvܠZ[ 7^dks-kIϖʊJǔݓgӴW:hwzǎFNχV$Pߺ̒vH_UZյK=sV{b4v9RBN H:_ }E|qψDŽ5m>O |Bkx"UQHa7v1I]8^[>=k NJtKĖZ7+ioK=y;ZBI|#S^!suuy)DEh6~"!+ "7 yv}oWq~\o_?6unݎsER}jK; "<=ԑKxLЧӡ/Q+1V`;O94CC+'w~&~\g8ҹ?{_[EؗdkO4p9 t&&r[ZH+G5l6%Ԝ܊ЮGL|:e-dvGWSxsUsz`"IM=i^uFJmnƃݏ|C}bTigoiWxZӴO 3%|Ajح) <=;I<.׺ @`[,u9meO:]ބH皷En_Hnlpp2B/Ov5v[FܶӪG#7}En^KPjU M8meF#>EdxM;F,HX @f謋rf}3O b xf6k#H,LO==遯Ee_+Z?Guc4Ee_+Z?Guc4EgWr[H] s<+BQ@Q@Q@fk7>eϔfsVdx͕.lD9ǚJ_~^|SV޸+CڍNNXH#f׊ǍF_js?dKD ̬N2ך[ۦ31 pqj5m&Ok~5m"WVEJWk˸1(Aʒ1\ŧ@&{]:M;B6SYV-]e!؎#Fr&4kqo74]JkY.zf`X˽탐ffq~:6AMY/cҮHI$B$ ӆ5|֣'n-WONI"H# 469}?ளQ-^\k;d8#*'N3]W/h~粺a$wI#!#+;2bV' $}2U/H fWϏGxS?u~';^'n,61h9xd_.Z(Ħߘ(}9i*]Εwd"EW-oòmR焼o4=}:t1'ʜ qXgddvj>qa\Ij0&yi8+9G9 6{ߩ*Ilj7]OE/+]t7K*MoB N8Lon"o_Vn2[ddO3vGx;9CpJ$74ۭ/O;Vd{'>aPn0y8Ɔ?MFCJ'i,{S)G9##qm!>kѾ8ig[>sGlܰ=\pU^o(&V֝J/i&=:%7zNR ōSm :k< % u eܭnQ)/jږo5=KPKԸPxeP(99|G|}EOźmEb]$p]Mk(2ζzl\n>|>~5k-tP'ݼ[gRn6g,8*K{lj4˯4>F))Ȗ.puV-o2JØ|u{^_=um[ɥK-̍{Şg/[=/Y| h^O)Qn5w`d n`|{} [KKyn߅ͯ7q^)F VN~sM^M༕P[J؜ 7|GQL +"oh6z%荌qt1tm'?(n"Fmc$j'U}\3 :+xÌ ExFq]7o1 iiiՍ7  ,EQEQEVEƇs>뚆q=V EEdȖ'W鎾կEe_Z6?GصucթE02ůߛFZjԢ3۸ }jnعTi5E(((+].c/iirϖW&*k"`OOin{qi םxV?xSǫOuVk#uE0_=.AqDPՆϊt>6K[xh$Fpml$61-JݟÞ('oO}BHTOt\#1ѝ}uE]__?+u/i+59R1stcfRyQ/tc}&7t[[K"XjIIuw:oסu.;.I ou;$u1*IaER((((((((((xHXa 1=eƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*յ<76V-œcrĉPT`hύ?U_x:_ e> )hןύ?U_x:_ eQair2#:dw\ZTyc/RX.oRi7c2 ͧs!,&_I >~>6_j?W/ 5U~ )h'3_I ^~>6_j?W/ 5U~ )h'3_I ^~>6_jW%Fc@55ޭ_^[Ihj5ߑijڄjYԅn7olXm95WG/lxwꗚޓ{F[E[̆uF rQWiy7㾖}jy.dx.c+=ƪ0G<;$Դ 6_m0LwB,A1C%}G~:,o<+໽v&luEE<1n1m>tخTco9`+65]XhK3k-HaVk|]s#6͠]+2x{GӴƺފhz%QB`' 2# ў}'BhZ ?ź퟇R}UtdUv.h٘I$gSv ^?̱W~ xBSR{I&`]ɹS;WWGh]F;t[Q Ԍ^NM65ET((((((+|5ZƧysjl܀3k8V}oµ?3Ί?ZQ 7x[B.GVGEvtP;&J}=lш(Bµ=Mo3]µ?3[vtP ϭvbV}o@85=Dw$WM{5=uQ@fotoxx-18.01.1/images/cancel.png0000644000175000017500000001374413222767271015073 0ustar micomicoPNG  IHDR@@iqzTXtRaw profile type exifxڥVQ& 9HB r?gfUAV3e)"q.]lV\ݻè -qxw߻$Z_?|] ~5s;>P[hAFj;u&ʌ5¯Zom>Ļ}m4.i7B;;F>?0=(h+W۫÷KJ~%ƞm}Ry+q~z>6 yG۞PidpgzSҤSh?A:o tؚhs;zg0 ?W>?di\/7rv 0lp ^^=ۆq%<|Ϳ:$*T@4n N$ɽ57,ceW?< LHSTCd.Џ MiEDdԦ5iͺlm!f01͇ω5'"O-'zXf@>CBbLΖ=%5-GzMR}Kٺm{hӏ=v3?Xw  ?Lqz^my 30Ɲ] hAerVQHR.7Iur/ob9_+ފƿ\OʖOfҧU, @ iA~9!F *0`(xŲP3>۬h+ V8xf@neqc#ɮ]*CƧ&ayP紡4() ;aOg(dY;:{1Q"fBy8 FˉҮ , six:|Q_az?A&8M2ؗD4d:kB2PQqؤ$a$ Cɽ1 =wmi佹C uOp]YYofzio0mvy=(,mSk m%iFDn_W,X'6/y\ oxǍw뵇""1FƠ m+{w,=8nڷNOgڷx8+/{W!-mJ9㝣* U)]Z+6ݒ[zo7߲0/F$(9E/1#o;^ t/{w{qt*:ze 181#*yeZ÷Y[~x[։f3F)FqeXcGoZu>qGADO '1%JȚ1b9R#kDA/9R| )vpCwZ8k mVu5Ӵk J fBxkƏ灅:?u)+"(X1`-CQU3XAXRJcTۺgOi̟iPn-߹~}@H gM53#U*_c~KUO.c{L ̕6Mz"9SДc$jX+h2L5!ʰzf/qoMGUȊ#Dc^ScUTc1b@ZPO%dUg-YUeVbJkʊAqEIhY9KΉl'ʠ 5 ٗ@S^)Oo>zл@Lh֢9DTRZjh׏mhB[Y-/+UrZP&%YkZZDKkcs[g_UNs9 wmJF}yr sĘ!ň0FYKt@R *Ai B-IWx65ӤW7??q+ ^ &7Y轮47uiCUec46FD ca:q#9FrNb]w YC "#kq1Fn_?psd4hcgY,T"5ykUtmY)Lآ<ugAFbLxМYOn_:qsOo~ؼwBi':}V0KUAY|Q"Uދ0c0B 9S" )%rJs1,KqH k5Yr41`Wמz?N_j;%QVOU*qN$ecȩ1%RH180aÈ`+jDМJ - RlNjN|})|r NSۓژ/Y12 c)%jtkE""h䔸8Azeu]akR( 0CUzj8ƴ=au] Fk/:}پ맊j&(Y, U9%)ϭ>`\bѻ\.ZAy#'RV-u$,bccEDH9#"kD5cĈA:/9gќ!%-M+6S/FR>{%&Mڤ3I\W%P n0f=H =b2njm+kF!}6(qf\sosV=)Ddb\Lo͙8t1cBDj>F.3k6룼 5/Cj>R~څC{{xc֒sYGh[c!pw>%f%X *+꺦(K 漏1G7>~~sڤe T!g GUݞC -{uD u!vdU\YY=^9q֥O^ڻ?F+I@uhwߴwAV%'52x+4mKh{^ )SqVbDn̳WB޵ Nvt? e%iC HxkHzq7҉$#EQBY~6XkEخH1b}ܷugGN 'Lw~#0:+K1b!uUxO,UQRfNA39T1SzY!6EA1{ܹt(Np^8gMS&HV%LV4kȊԠAE!fk2WU+Mh nn;c#UYXCJsH ȉlPB8k)f~8kc k&NlibĨ|Fh%jGjJQtƍs7&^h$\ {gu7튗fZ.D'd2`!ErVir*BUU8y,_r(tfm;שL9䁯mO>dn 0pϭZo(r&wY-eU0mZauLrf"6RHM @HEsBi ΀ׯ,<}ÙI ^aN-m7\UE/I<튛HglY { :F٘6dT꘤F纬r{)9Ka UhH*6͡/lvz. O~`ip<_QA:Aet2VcXäiH;Sf$>sKcDQhʄ}-YQk-ۣ1Ҙ&r~Rke uS+MMK @mʂᘔ&ѕ)fXKʝosfdU}7=>'. xiY2lg'5̳[{fxܤyFc~,种Lc"gEDX6UN+ ,W+Y~F#|m'syF~pOOnQ\!wJaiO>kvt%tBQ-\YZW.wyIy\UuWqWqWqWCrݴ܊IENDB`fotoxx-18.01.1/images/gallery-sort.jpg0000644000175000017500000004064313222767271016264 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 231 221 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^?<3xɬlYE樺QrHܑpOQi_Q3aO*H"2]Ÿ+M:w/<=;J'% 2VHE#9( y9gǎ|7xN[~֮+8<0E&So5IZ/vb¶:}'?½cFD -زTaiDY<zUKx7K$פѵ]6-Z]p UK+_G}'?DWX|:ߏG,<x=wEHu+&7Q=ن@Dd87C5iq+%8<8Rm.5Jz'sl>|?->!mo?vڿtc,.vi0XC ~!Hn;slRwܭc.3Ÿ@Xdk_3Kk a\s5Śes"8•QӢjo5KU-N[4R.Ad1Ɯ~>yAs7GDfVQEݽ@5TnM O}k#+_G,_G.~{_?gZ=?gZ=as'֏Zq8?*q8?* i>Tmflۡ}pA??? z??? zkahύO.w]wҮᳵʕ9ġ 8`sҢ׏%4}`xڞii% ʪBCHORkwÏwÏQqPL:eqoea|p$q.<ʡ$+SX>x/yZ{ |YvX@f <A_høşkøşkY| GYD^xnbkg 8 *2*^nCnZkeZknU%;s ,_G,_Gi>(WƯx۞#l&6[,24 _is(K[2‚)ԂX O|?gZ=?gZ= 5^x{)=G_u,}Kwn>qϞ/P]YnDg`>i)#5|FV~.дk"IcR&ٽT2c$^W/j_K#ZbFc0,Tuaxw>ּ5yWzq:i j ZL ꅷRIsYu6nzƝ<3c3l7ӣ %Psu5_|}oܼP Q=G>O xVš_o.DWd:zAq[x"ucx̟ۀQ-᧌kz>{goH@Q>l\dwѿfnֵG4kxi(ʜ5#HeaGB+>:iw~5ٵ$% x^"U/ Jxa%4 P>ONRSnޟ_*J ( ( ( ( ( ( _rc/,{XzY]({hn/cF24jb?>x}.[ڗ׺%͔X7l1j^T pjݴQżRXA|uT~!AviT 4ǎr?)>UqwcR}nmqĀzTvWv8>? |kxnYaԬ>wgʒ% .~bLzgKĺ| O=桮xVOfX/"*>p,Ųi)8"'bFw`% cCsM,u^&f#zc>㼵R)UK*a ^gǩ|. MM'X}͇9kUCqo;}ƬC)Gzkʑmꛎ㌟Jo1Ӵ{jd>ul M,^?>i(=<{XjQKg8#w |Sx4%|O :}6渷X$j4NK XpHbsꬪXnM:FWuG῎5w1Zo쑡#:s|#-*ꖂ++iL[ug2eIPU;voA3Ѱp)2jX"\'X+叉|j˻SXk7SiP;:VI\b{K>Q-5_ik]˷:]희h@:horKGW'xgS};Tg>˫m/ȒtgWk`|jo_#>ĺO |׼kce֗e5X"H8!88#얣[t4x^VGm-|``ǁxF}1t6]2(nPKpN<ͻ(6~^N]%ñE4P!"g(9?S*/?|kk/?j05s Ģ{mz5t|^՟; Miaiq:]\NRKFql$]ct^hzoF`gO&F° K"0NJϹ]᛿'%Kk }BPV($768Yr 水'ޫXZ)//n8Mp͵"(}=M6mfʮJsG1㚭 ڞEay4I&m(V-Xƒƒ~5߇,RR[w Fr@97O<1oM1]IF(0hW_ՁgQ^~ZqxR= ?Kci H`-,1}+)]J/󧏾u|9ij/i~é$rhbEE#H3vwF8%oZx]q4HAr+DȤ6s4U5o/Kio/|[k*k\\]Bio< n/<?C O&=+P$}'K)@'fs$L+2i 2M}EBз.q6Wg7SIesb`i<3dv~^-𾇡iLg0>.nc9mZa9G,C r|ŏtS+![wgXq}|7=f7Vn<1c R91ZA_|P8/ *3ΊXj]\pOhHS(=:^4&`gekqh烶 0NG~>#;Gէ$֯|Mt$RS(H%6FN>}ݿ$As^yg' ^\K=WJ $WQ!u? ¾վhrM ]j>y#q=T _SI r.,3CO5ws=VӾ$ʆǺ]WP4b"n0٤|Լg?/<:5xMKd$vgҾO)Cτt[J5YNqbGH\ '^LhY;XFzE[[QHaEPTuMUtĈdCm7p2@HV Ƒq7o%ź9%-Uji]kv(6v>;aĐʁB5_< #~jQpK6e[͔e{qd]F}o Sq,P=m54gDw$NR =v=N&[xt!5H%oD>lq<$$h@+֭]7nja>ºKue_O];Z#vT#g+ .~'tW.:?oZ kz4^Blkv8`x_h|(ѯ4o'JfMʠI.?}E|mV}*Tk:~1m{MM!c7i%XR#11i 5-żC?UĥrJĿ֯羛Ntյ@3>moI9ڪ sR:d/$R0P XRXΓuom0\"O7vC4q1L@$W;kRY|}jz  G@ ^a}>k}o}Nr;n[h{UsjSJX&JJnxtab,$焊joWrOru+A 1 0wWQ.ᫍsPռCEht]RKgxe6\ AQ%_4W3H'Ɲ7[㈹׃Ed8@eIFmf/+6󺹨9m#¯|_rnL;D%Q2oi&t%_ßq_jU/'n5io5oΧ,7SMqpϧcI>$q|3О"mxVDYHRfl2wמAJ޿9o>*K iڭ]ijBocVhr?i"eNV $z|5( p ( ( (-v$H2#>rzzs@ ni-VVЧkMֺ`f#xS{z oA s5жgc.anF㞸~;x:o Zֻ:4V֚*׍w0|+kvz}iX^ḯjEXI`aMx/}Wta9P51ܛ,3$x-ut>Ծ$xKF-u> ֚MΡ ww&``3T]xAM_L{=8Ŭ(Icݹ/%5Υƞl+6%M?P.>̑wGsm ՀS>Y}7~OQ9ҢWN%C_jdvQfZ st1bб8e#V |B[ZX.C8!2#݃q2ӥ-44mN+yX-Ӽ?>]_Hleejvr+prx5kμ!sj`E[[% X9G< {,.42!{-tmA:sTG5xSNDyYA,ɣk6rHcL#a1+X1ď + EMK,ZP@W9Hoqy3^}]3.uOöVs+<כ41R@H !q?ɴ۝ė6w~-NTZj“C25ؖ!E#;H i.kz ׷s3?xwOF Aq,n1R*8+]+AERQEQEQEWijװi/\.``!I vU(YITvu~>'G /<-spRIV­",jT* @[k@hpǺ>Fna# ~UV%J]}xJt=E5 ki)q$R(@e`;H#|>nshڭ7WRHmrn^qᇇ5oxĺqC4Zl7"V KBxhiSOF_}xVHҒ@-^G첬8#U$]{=?iZk7En !d%HBۏxFڟCc]_x_d1|O{~[!;@rz `ֿ3^!NV j'[kh+أWM!\.B~\ewMV=<5++]wTK `1I$^7a'[ߏJ%=Yjqh𤒴FJ!URʪA S'7j? [ 6廘ʠ,aRr㎙9 KNhp+-V+:ľq,yHXKneoz]DOAKͶ `FEofCdk44o(bMSPxnvzI'Ճz1A7࿉|gS-ZÆ8i,$ `q$x[ʶ@%}Sw.!6?Z J-F bLn#H`"/~..xY0$X֘*MŴ+5 ZxLC>qLaDGi4!06lfFEY޹ͨmMF[qE[I"Wca@l*ˆ gF=ǀ5 R/Y ]2(纖]фGpV\<idX8cMjJәPtBH$m M',b?L_L乵S7.4p‰׊~2|m{l>闏ghECٴ|p^݅tvG~[Mq{VjPt6Գ/#eP'zcyGKcHK#I3c9߆_ kT0 \Z)wS^O;$"U-#Œ?!xSஷ=MկG=i>;HyO\ȉukuv7OǭjZ^ -m-dy*# %Jb_Bu%v9U]6)+ Uŀ q^w&M^MNMl^B ,7{`\5߳߈,nD- KMcTbVV6&9S|`iURL⯹'Sx:x}mu!eCї cr+VY?g_p{ѭrZ:Y4l((xNY2=ˊjeohJ57yYҢ|_ F?_v4#W/;G *+7jeohJ57yYҢ|_ F?_v4#W/;G *+7jeohJU6\9T O#W/;G P Z57yYҢ|_ F?_v4mVt2G,FPcY5Q@Q@Q@Q@Q@Q@Q@Q@Q@u@&|$k[y'ҳEڪXC>bԟO_ǎ-%H4ffJ܋qJeHH R=z g`ϬG gW_ך-/мQc2yi,-m$崭r']*+U_`M܀ ˩ӻAdlm ^Ќc((3fotoxx-18.01.1/images/viewF.png0000644000175000017500000003620513222767271014723 0ustar micomicoPNG  IHDR@@iq /O$e9sRNwg}SOޯ< E1>;S' yJ'K)0ظx穀@ fڌ_9,=2霛j_PRI(Eŵ/K(^U*+/2 /[F+MY i$YLoK}xJEcBH/-.^orM,~}֭~wcDnZVV(~,Y*c$4Ρ!e3gjUfqe?#1-NZk~=Ӽø!2˖\.:}}fny-ϫ{\ZAP;J/gRkSWk&2fJ}w!ܶl{y f1ךZw0n%O2]S)jfvfU(t UIV<&OMpAIH]R5J壔pg仵۶6GtQǕAm{ Vܞ&oȈmAF29rwz,chhxWCsBBe'1vxRڻv1$%=h[BDL;y'BLlNscBV禐QAKat ~Kpwa( 8R5w &5İSđ~];ׅTBuwHDkLGnr-ڈ[F S+,.Z&Kq"^ecF+HZs^oNs@X5(|Bc9c i Po. 0M(re2ON0/52؇Y.ct݅PM@FBpyyEFXudPro=q2M܈#&&'tDαhމ|`M匆ܽe}T:/ŤUX;Pj+Ch-9 w(DA# m•vs`fTS!2lIX'xtDS01}ꦚ㦧3zW)C͊Vr Q-X8n[+.Be~P㺋 7!xZG oB\ͭyT. NMApz?^Prցqnwr:\ p= ;iܶPoP܋A@p8]Zy[8:O+mQ)q<@ CCƸS!.R 3B[23.rv5训Pa#Μ=-d >M!Am,٘0$X ;[ -\$Zr+"HEgP0sBmåf7FUǀVF 7&(/4nzTXtRaw profile type iptcx=LA 0 >벴{Lo?v &B"uY^40tlcjmMWvٔaJ G* 1 2 3 0 8 2 2 sBIT|d IDATxMWmI]fǔ ɞDbDB5tKPd~Aްez$=5eOym\X]FE%~׹:!!`%L#@HH#"~JI@FJ $ Bc1JSwH)B)pO#06wu/n&ʂG0JmQ-E{~|I)RB ƈs#)%|pH9C֐IH2E0 DqEBJ() HJR&xOR( 2@JRPݯ^ѷ7RMGQcSnSMG@J 5J!'H&s&LBB4MC$R8%1xBu[kH1F)3PHmQ b!A*eY'ρ̒au=پbl~jU@v=J"ڷ(Ub@}Z`vk1yKacS7"zB?-<J es=~ ~{P [T${FR 0i[ _rl^=cY fvь<4$ t=N'Lk`ce?]!Oo)%y8R)AK^|;\A+60-"ZQWٜۿū($53O0Zkv^ۼF-IxɄRk^^.#{fUfZ%; \MŀRUOO)wupdͱ֒S"Lw '-o_|Ûg@R eIDa P4mGY3d}x-RI;TqRc}^t}pz*xjMg镥 \q~Q~̎!~Z 1tƓ0V^Y@L5~z5}+2.P EQwD0G깥q)q6 &n#!J|2L)ˊRe& ,!%d}Եܯnt'G{ 1b"EH1`@+NVSfeQuT]C]DZ6s8Xѕ@6Cn߲?x "dF %34.r{ %j:AQ$X=لRVHeߠO` 2]?e&'Oy5pϢJiN+"ޏS 0Rc":Ҋ)t:j|^PU 7,Jق!>&\PY@R”]cQyJD8 eKJwV5{fR3J)g@&bCt;<p˪XՐBg6)+ k bF&l[sWq4-LT:p|^pd):Gۀ@#g1YP؁F*3CE-9O?b@J^ "@䈒_0Wyb61uaהU)ӓ#r<Wl5Nj_ڰ;t;I{H, &ŀB*eQ04}L)q[O. ..LEK{T{Y48 }OH 27ߡݞ*HV3 YI]jǏ9:=*|:S!KD_\oie:E F+i'*x|;KYhѓÂYыWm~aQ<^H)BD7yCsRS*0 #QV`@L$2(1'ZJ˒Gs.>ß}/o_3SV%ZR 1Dd&4;r kV5+4J݃[0s_x@,%(-F[ք0 JI5nR1)KЁR)"3)4h ,% !i5RI,BA5ɟ~1bwXCE][dDNx|Z$u]ӢubUuM2!׸V3]X { нo"@pž[_|E#cƓ3Df'+eFĀ"HHCPʀGQpr7k~ŷ(i\HH1"K*V*kR`^H[e1pz>o7+4 ID"F';Zg(ʂZJiDΑ=5;w8?茈%)ggG|}z}%uURIa H S0w gMNVTڠցγy`~lвI ż\rOЅ) 3d>"|Bwt͞}?ah;jJ Ȉ<#Jc)@ e$1ht]C[(l!J/>?_b9b M鄦0KzjgX %C q"G;NkTjt]Ը*lÎݺ DZc"'I=JDH JKH!BFD%{>ۭol%c`2'3~/~Ǜ7x:#S R%B(;-O GGEG UOJR _BLB Rk, B.`ɼ`z( H9,FK$;\Ai|<-_ԓOLkrFY wh#J* "ĀP)ibE}v&(8~eA7 !I1L.dYp:̪;CL )DI(ہ~'X.I>qe5eb6]0Ϩ}on29$)˙46l[rR褠^W\z -f3g;V#";w!$",$HASL%ݞ63 EN""J0gW7-9:Q[,KyфD٧O1$ VerUSך@<~??>ppe#KBjy@KmHIPbXǸ@ʒ /XzN|ނ27[BbdZM9=9IB0XxCl}ռ߇% _28s'QX ;Ҡ'?:b*y֑lQBcm,BV|=w-+fVPYca4@F$"Kdʄa9, G)4l[Ȳ"gZ(wdVUms>hb?9-RFrJH-uA=+ĢrI"#3 ve QBDa8q,(&R$( @D0X2* MOd:%vM~h]ô蚆 1Gz?%|9W/黆/)+ Jhg61(%1ʐGI$k&ufu8()j?''H'DA)&拚2?PLKtQG.3cP>H(ZQ3@%mٷ|Χ?v=Ąșغ%\0FK 3~.!AȬQYcX"ǤVL&sJg >hFF6 ^vX0HAG5JrrHuXYM+_xOkA)BdRE&ܿ}Fz{- vG肛rXHc2lͳ {by/jh)ԃdr"eF5Eǧ@rZssuKLc 9SZ蝣X Xm@ k<>@Kvݞ%~cƚ;d2<׷s#uU1 n#HIJ4A" %O9c{ah> 4cDHLU9QxCl~غ9@`j{*8n"Y\eZT=瓚=fO1?=%[R,'" Cd5VMs꘯_(D#"Ai*NrE9;2e& [4)#e#!Ɵ2e眜vag7ψ~ $ͮa11Ii-!xՂ”pyFdz)45JYJTVr}5fbQꈛ6r{ٶ{ЁiQ" oo6, --R@6Ѵ;#U(:b$VY:%M]i6[D5K, ^]q~~ƴ(9;lskI&,JvZJ#9;\f6_b+ݳWX+9Z 0cgח$4;h'WW-(!>!U~t Tʐb;0_cu)k4ҷ=eY[JQ4Q峷|hj1)-)O??k "y~}!Jo0fZ` /%w3p!R@%"VSVՑ0B9 !2B$g9ܿg_rT)ЦNhB&冧g ϑ>ceb9;i-J+|97[VI%ntC(JV$YG?R*ImË=Lb2մL -S90No0gyz- msఽ{G_CfKHL-y%%=l^QNVL Ld6- 3RnA?a]_GEwd#@QQR@KHjLd$J؁Lx߱"CKK]3,& Mٵ-oONd9(6,VGL3mG~$7kBkb:a^7,Wc>_/o='IF!HhJ!Ĺ- V{oX-YbiLʹ&LH\t)"+|j:c>3$Ƅ*3W;|{mwoˉx'2sZp?ݯo #*%D*0@gA$Vh!8ѻ!&L;'{hM)a18*4ߵL*P2j $1"b OstY#Eza`}v>~Ce qaᔔZ!F+r~ĀT=ޖ+F>6C՛+^]ޒ0p!aq ٜL|9k? öZ^KP ۛaG&#iz9`%)UiQDT# $P*eDH?^ὢ*"bR'b` vRCوH#) ss{$d'ښOn_ @<8.o8F[;BټdyH~5Ѽ><,VNS\D+DPGa#!yHCHI<=RzrC7-fù)z3 nӋ3>)9$"FY,8n7W}[NﷴWt7[7 HQ3tbTiHRGD gOΚƣ($.k Tl{$r* oeA?`u1*SYеvÓcOy⊦Sid IC%jvpW_<27['%BQooGLX)(,52dž;xj=?$!i̖tG3JdH K mG0ܐx!51Gtf5Nׯ]$װT2/3ؑ 5 Ih )H*)!̧>z:ei#Ցմd:`9DʀP8iځ>"%oq9$|KqjŖ=>ZRC7PCnh;)* B$e$9kBxd6̛/|y=*@<uX{=D I?xbL#ې=LHYLe23=sEt]qľt60taI2E0%6 -?k` KIENDB`fotoxx-18.01.1/images/unbend horz linear.png0000644000175000017500000001644013222767271017313 0ustar micomicoPNG  IHDR]ٹ.JIDATx]ےnyum 0x)nqLd]{?~p0_epJUdS^xe_~sNnvM~r{}?~e 瘾1k.Eo7pG 9b}2p`Gjt jb̽ O'͏W {~ | ^% ۫o`z_E8aRү^]LokV ~M7A׀qt4:-A9W1PAFTU:TȰ: g4B (Qi4O?ډzfZf.59EjmtnG|G0((e6*8Vzv.AɞQ/㪜4Zkφ}ZEfr@$.nM%$qrC6cFm% 3+ ͘Wv_00#r|-=:oB-VJ|0[T!5`*#(G ܫT H`#11vg\;qԜ=~iK1hhN]^0ۯG; |͊ ,6OG ; 濔X6Sa9n$>:dOs+hPF.B#]<:+4A0bf:R,0ڂt۬dei'Y+F:(s=ᛃ;/lv"֏_B9T U?dRFtlfm~Ƈo? (m^b6 Etk;{u8aX9}阝l t7r($H~)m(Ar5?``7kz񎎫680!zjĜ054KM969xAـI h̘?N;`̂Tiـ xjسN]'^O٘K\- YU;1ƣ04~0!ϫǩY' k-樔1-j?Rk:c>XcWnǩߵ:vPmOiIyoހk"O c+IF#řQ_m9CiǛΩĝWK;HW%gt]`D,R=jؼ=8t{w!:?\X}|癏з^Ǔ cG&&e3>HݸBTKw>+H/E;'[ (ys /Oz_PrKԚh'^b Jjf`RG:æC}_\rY7|F},!oJ1S_M5|d(9nNcxP +u+!&g#kku M*;rNjkn֧9 #.9^6_'i[m+`.`1e r19/<'z0uv_ZV *0[n]q߸lj5Qjp*HnY&}tq4e*XzpxrBiILo>vpjBzpșI՟iM1HqC<t뾬o =r/5f ӿݾq9^4ߵZ֞,} =FVn|uVӾ_Z\jj6]3qZ>i0.81{o?X6)!S FsKh% UAis' ǜ^ީ>懊3(,܋ -vJЮb";t.l"m6߯QxKBs䴾1ő VVS]]L9~[x@{EIG+E=#66}pQ_ܚIJf1]Lxsnf?a] <.LXXBq}؊'fם33>=4s0߾ESw7Zq|N1n f"M(""x+?& ꑿ(pX3l4ҷ 0gjֱ|C酪]d%ܶYnZ@-ݷ%Ύ1՟ʫZ<OwB])Vozz[~-0=X;.6/=0{8-RFIi<1f-W1x›pRx{/re m&hu*},a IR&XD$8xOQG:6㸐eOKn K P6C>62%X&X z:vߐGtSJ4P0|ZΓfNxBPBp S(^'P$m:cn-RM< *Q,9z[Qq`LwmVȣ KGbYe۷qJ׾0'7A ?CGaOAfhvh 21G Kp+Jt4Qs", V~ăW^։ekQ9[}-`++ #6g8A1BV{x2gQ)C ;X ՋY0+0dg A3 j.`(\ 16!ж\! $Oh/'nЬ8ǙZk}jn®^O{Uo6r\D$ M> ry~~y\V]w:;跧v{A/m4)m`! k"|6"n se"7 1fSdTU ]RX'o$xIj(bt 9?qj |P g: h[ᙓ=6o#rj$ND.2ti(8t2G_YZx %Eӛ)M6ku'q2[S }nLT/rX,Լ~⧹\;HIWG = &+?e 7 u`ZA\5fצVק3~tΟۗȝ&069`unWTO>-s+ذ-/ެYږAFAF޴PB'z-H .UFŅtYB?,B(w<}A*^"]S3b`͗Y<%<K[ Ը]*%0z@:|D!pAk`?a.v)=i+!_  M.wH2||}޳=֜vuMey\T|C3_Ox:|NX'ߗg'"f}˙BCQycNG8j=K~巊p gihӆ Uy*D=#^Mp=2m~1>h^jѤf+m]/W,şpǓtf +w k[AU6 r"3/߹˻ nBq~Zv1nt ?]z2d8FڡޢHS?5\; X*7=R>G֠qƙ)[,ߣ4r"GIܾe6ƥL -"G4gXK?Q&'UFhÑ^>G8bǙiޒʁc?qN.0B\ "XMnsщӱp"GpW`n+὾R;`+<U~Fѿ^f t)rC:&ԋdR5 EϹoS~WrBklRgܪff( OXwqQ.\vO7ߚ+lN~t#},9{dS134Zi(h-{l_#6XLW+n*lתO7m&QG}ԚLK۸17**[Ǿ [\fh*{\Qxȏag)3~7s4 _|O7qbkoЛ\nLzXᑯyp0yw,gAq Yu7O<*؊Z56Yj pM=6֍4@vq*5jyvU-ǡg'Aبk,ƂZ?)bѼ{J;, 4zƇB*t m!g]*z<`aWng +v"jU mMr Xf/C+t$m &Y ³@},񡏢4Uy!b~+2MM^zR4] ;9 dEcӥCD喫7M0`/P}?nzv9u,Uqb+#!X%/ R34s50ܮe!3fYdۍOSbg%Zs>Kq zTXtRaw profile type APP1xeP[ => Ĩ?\6'Y} ̽<˾彿(fi c] @ y٤۵n;ȗ7B],Mq:\|a8r 30f}lhFCn3X_K}$p+dE[$@ϺPy07[itiTXtXML:com.adobe.xmp 128 128 IENDB`fotoxx-18.01.1/images/select-area-finish.jpg0000644000175000017500000003537713222767271017313 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 138 288 0 C     C   |" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ W뎴X LIuk7f@z%e# R }^|=\}BYExf>9 zO M,5mnKKXH|[ga@QmyXQwKHu#y%xZT=2;SeԜp>fj ~xxi~GxʀfO;(rdb΁i _CנƗ6kKu4ovؒ+ !rT0He_DQ ȼú\6|VӾ'X?>L u{hЬ=FR~lvɣ|<_ }YEOӭNiGjҾWzx;Z\K,Q*v\z)sW,5CVFGSʫC,=#jomm!1W=BXa-|<<m"眜Gl_R. q vI+%ԟH,hZq[xP # G&\L4woq@>pk5xMѭ!;vtTEPc2sO|ZZ𶇫K<7ZM uڳ'?x c zу'e~hp' Κ^>u躮sw k8`D88\Ɗ^<_Ulrp9'ֿC? 5 z? 5 z2n~y_W[=W[=as'֏Z s5׊2s5׊2 i>}g;UG;UE\I'ֿC? 5 z? 5 z,Oh'~ùk_oùk_or|ME5-siQTl`Ap=?<]mqKY-A=EQ2I xs5׊2s5׊2as>ziZGu-b&!dvSu~/ Ƶ>My.Iš0P@M^*G^*GpF-c꺶h.J$l0 "3].F 5(٣}2,!s_iùk_oùk_oZ/oxoė f]GD\n9m1'ǵ]㇎amWT-H"AF1mPgs5׊2s5׊2jς|W]cηh!D&(ߵBVOO~ùk_oùk_om s'֏Z s5׊2s5׊2م?Zk7:Eٸe%lGЎyOw?_ x-w?_ x-0Ǐ$y20!}JẖXRIcԟ\_W[=W[=asM6q#O nw0UH?J{}.q4l$QU좿B 5 z? 5 z,7{H࿶7[a=HX~%}߉̫okt9_zùk_oùk_o+{zp Of82V>Wv;4Ww;|SEzg?=KDмi[?DKqt4цr6Hgk[T5ψ&ZUM_M,ټX!x%)C>k`_1=@4o[hZV5,!DW2a$7r u^3x?úԚ`C=4Mp:Gu&5O5'$ػZ֢9p|`d V$~RYҼ{hRj#1C痌FVu mxZՒq iyq=70unH]W#iomۍfWǮ漣~Zy-ݽxkmEqVy3ŏ)c`y^ԗS] kCeӅn}mPI[~_][_3 _W omNbtfeڤtFyX]>k+eݦV}^]kYe)PUV6%1boχ"Wɤk=K}VmWNݩZd{ y |qZ^v=Zť~#t kI=gKK$hU.g.QK$z.ʭXKH>ҟ ' OuMKG? Io{m;gbOV?qOPy.C!Kk$MLcmpx# Tv^!ҭ!Sc)90N>_!>%ᰟWc oi$J` #qFxBMrFڽ԰)ƒ[*2G߄5WF5ΣR}q١&6dEcgޒ*^V= {M/ZYѤ%Xk"uZ>ybik |2;Uz" ? Mӵ#;݋4vj7&,@&C06>kKgM~ ԭ$i!e.$1pIVkUkǟ4p\^ܽݝin#7(̥E]GҾ%xC@׬$[}:RK CчQ"+/¾0}O&$:mf J[wG䢐m*WMWoDFo)uƜH;sܾ|=A^H&_wΟ Wwֲvę̄ESd״n]yOӨ}Cmۜ pA_.~ E^^%RMFC,b$ykBYѼ uOQTޣt9JK=Ck,E"2G0f@C(kZ9;;/K>Gt :ܒΗe-u4.B08ԁRh.ӵ]?JIԭ+ rFpv= 'Mz?4kφ zL haan"`KknNx*Q:o4z氛þ v[+tP=L)em*)Uv[ }l2m9E_I}moZW|IӵO k(L`'}~0:}sMl_P[2sτM|pִ/)3ogJrOČ0TH>fw;r5.i~">7u(/I1rWw 8漏'Ex6trDɖi%W aś᧎tmbP.nuk)KՂxd&+_% j_c^w|K{Zb!uF2y`$c8k恪iTlIuD{ 1 T9|;5|qoG:^T2B x,๭"ӦMVRU2FKUYV"B8_9-zZjVR]Ko2XH2levP ][_!7mϢ5;:mNe/]j1Gڃcb` _)MS`%2kdF0[pE[K `%7ڤ:Cjl.aͼv \ hZׅ~G ONPš;u+Σh#&O#/H߮}m|b/Ѵ?h-Y,(epiR}/?'xž!0YjǭI[4 DmfRgi#߆_ K Ԗ>Ma{R,ݭY |Ob&?IksJռ"`#ԒYeF *\?=,>.'Ru{hw1 ADY88^Ug|'_J wN,SZ}nRF}>-o.HΟuѧ,oT dC ȾsFe;=H'_Z4:Ikwh(d@3qkO_u WJ%_I>c e9na`H?x 2vO'}5!ȗV"I#jĶI"/`7oD~Ghr:۲Bm`wt6|ko jxN+K&{漳g!RvLy#8p?|c|m(tvѵ[j:ѹe(Gd 2 cW+'+t'|ȋ#RwI.4hqpes|IM?|$XJHګhjxMuQuW5 HM:'u/Co#"\pCsW)3ߋ+4] K#ZLG2B;ɮk.z7A]=vO(ZHP#}P|֎B]_m PM"$[&8rko xA|vXwzԋJ$;o~𞿤3auΚkV|bXXIw,òsT}.W%)%ڜ6}X cpXVσ~#W$_z_am>v۰m㡯5~9x|xxÞZRKH-`0LeU žMzWk69McZ֚lMPp#.G mQѥ8|?K:x[?1se~h%ьH_#3Zf˧$Zŏ4ǃ3׼Hm<|Dj+ #FH,x8ih5+C[ÖKYgafff}@ixTG[__ȩh}Ηe|?km fIfbU`$xnFFT?|1yϯhj_3jv6HY G9?{T7>!sx[Fy*|z4l P1X8/)kU?>%E hYo!h^2QöhwųT6o_xպ<;ɭLemI<Ō*pp3+B߇7:FᛍLk%[0)*FSpPǕ$s]m4AQEa^|R&JN Md;Q&_ugY^9[7eWS7FޙQE ~S. ֡-od5SIMf6Oʯ$ *rFH$f}#YjPrg9n o 'cD_b0/>l,3oqwu(,DqBćp \5j?>4Z/ngam&KؤhfQnr;pS[9[׊!kr#9Ktz$¬ N%_i.nuU#nYab3[KԫE6 zįki[]5ܲMX k<J O3Zv60jWsŊxG ֫5异[AºCG 1w g63#֟^O4^]GK$Eʼo=X:8ϗᾴ$-_XI4u S) -V%Ԃ4bj{[-W|ϥo>"xsNz]&H2X tewC hdԴY#}qr_.i?>=u}o(WF7W-Հ_`1_8 úzvhtٛ\8?c(>R@؅Wci{ =I~3$O~$_g- $IC.: Hs[NC+,'ïC?>#P|B)B>csPﭻ~:þ%yxPԬ7m)oEٸ%HLJ]^/5I&OPdi5E uso? .|+vx}5 _-g>d 9?Ay&$-hZÝGZLѭCZyKyKh)߸u,{Owj&7Ui4vz~u=b9l 85m2x$4 |C$}ON- 627 883 0 C     C   5" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?[7\ׂoa4K&qq@&U9PzsNhz7/ƯvG7/Ưv\2|[ړijvP& yo0]7<-&/A uaa\*qy\6pnS;n__;? ?n__;? ?Koi&QO&׆J[oxsZj:WwA/"U>^4\vrjWarjWa|(GyR[;57 I-U<\`yrjWarjWa^x pxzNM;W~^{$ `8\z־^FMũkk 0ܠ`g#{n;_c?~5+z?~5+zI^;]JV-nb?1.H jo|*\Ki79/{wXBy E;=WGWG hzżb"kHZIF@;fxxVeۻOex(e;8;⋎ǩrjWarjWaYT]Jo[/#kyZCPTFs->V1AYIFI ̖[J )b&^DCC|S+O߅5 7TC}iVgLO{jޙgiyy$nbeܨPHs=~(oE1jW/#+q66AE/n__;? ?n__;? ?? r."QFX]]f "{-;Iw~5Mv-f4KĢx#"?1.z?7/ƯvG7/Ưv^QÚwWno (-RE %FCך5]/W+w dF}L_6n__;? ?n__;? ?7.#xj57jp(q0gA.v}`>qVSd7)ku7n__;? ?n__;? ?C¾It-+rj$2ƍ̂UMGnND+[;:$v.2*B 5y{xwuܿv=ܿv=pS]4BMGhgt1Hp0{wp+73Gwq#Y(<担yCC}(q g]# g]#ׁnQEn__;? ?n__;? ? wҍJ.rjWarjWa[nQp=WGWGwҋܿv=ܿv=x\}CC}({7/ƯvG7/Ưv^F~5+z?~5+z-Jlw=E?n JV,ХOۣD|nqX\7Y*yn]U'eGﴉu)lK%Wd622=y4%nKngKe^JjZw㬷Fa^M={W;n>ň|]7T率hBQq ?>k ߄?ƺl>0lX#f©' =8?F.%+.MHG22봐FkLjWrY"QrIZ(`|u̺\'_cG|]o+tXu^Q)D9y[ F^4A+2|jgg}M 7L XUc7;L.|{Q{W^#_+:kt̞gg}7 ?|5?ႼGC;7E\ႼGC;7V`{1$*ўLG.|{Q{Wӏ7di]N ~?ႼGC;7E\ႼGC;7G0Wuq 2yyWVV?5AtYϕj h^#uH|^}?ڿ.eܤ.19h44rqȼ23O7+:ktù'on?ix2k^!ߌyH;7qú}acem55x&ˡB6Ӟ=+:ktx_ ~wn(e]j5kJ8m5;eXN6dӣ 9]1לwoN2B4qx\xÐ9 >q~{>#CEtz$vS<776ϓv9l{=-hk!5AR܈ gm݁p3 ?^#_a4 &KhuFCkVz'4x#մm/MmVS<&Hi<3c` ?|5,*hkDO40K' 8i#ѵ= çq~?`ݍ;_cƢ<^IƷ=fȑV#0}Moj,<ɣCR(hbeٹ^ ?|5?ႼGC;7OW$Gq Iy#fa N?՝sqګ4 1J4[)){ B1qx^#_+:kt EEoFhjM̳RCL ڹ{[_ftrsme2:prH5^#_/:ktZ;O _ 4[&w-:mȖA!,#=6۬vkxn/H>ZȘ/:xD 'H ͂?;_to7M_bN[:`uXLt 9Cg>m=`>[6$aϰkw] 7m,;sn|yowOB-Vv_LI& ly#qV>+ůխSwV׳3lbU_+bNvmw] 7>-AdYWviְ]eU yDN=*k=3i"TY.h"Ԫ(ʮ8 ow] 7f;ƿ2?nwe9=opc{s,"Wmł`W}ExYsGÐBs3_to7G;ƿ2?nMl C&` xV]Ip1\ͨ[4$ssV=Wo6шl3^ 7_to7O[&mAv]y6~[W%uMQ6P7d%I!$vaw] 7a|HW==we9ˠs,gg}Q 7_to7E\_?5AtûkC. +yG;ƿ2?nwe9as3ڏ3ھw] 7.|{Q{Wˠs?5AtYϕjkT5AtûkC. EᏈd|n@Y*wϭeƶZi`N.qz_to7G;ƿ2?n(\"K\-vѸ91GsZ4~{߭!!BĘBNc^S^ 7_to7E'#ZZX%O.eO9 =k>+Q!{˺2Cz=z;ƿ2?nwe97 5M1{*iB#koONY?=C59%cw:uIn.bZ@;xT/ n$8?ĝVUZA#,*ȡ@Vq\XDy")ddOvsiu) e?OykXM&0ܒkkN?|%/>xW]E+jwc$ '5mWu;ɨm N\v)+jb5~ǹ =.u]> ~/iSދ(Q 2$H{rpp31 =G.~)iֺPͫ@ėVXyNAn+[¿${(:w)wSIȃƞ>9E7nB>+ w dqIxsP{I'e<"2\,\QiQGo~3o#EgrTg=W??\XOᖞ-lf7֗^ +0;p*3PtGl5 W&sbe-4r8V$+'YV|'CCrjFeY̛CwӞ:<;K-VRkUG=..P[ Bj[Uw3>I_31~V9k^ktHmqEZw#&^*թxƷ:ntI:ά"Ȳܭd40;sJ;#.'Qo%Zπ U! ek~!RRy#o4P+#0uW'eVeem d%峼5psaKcR{`HGprʚm .+z~ooH׶c-yqϭx[CHu^jr59a,hʖ0>pBP:>-ޛ|4u֢ =*-D)128@T0rFE .^hџ` }+ƒ^=Hu䚗e|T54mLKo,_Ȼ/PZRzuwf:a #9В{gi]4[}LC%>JI 9oM7#1*4+p<|cҼI ltKYt(m-1 !Y6;1HgCI(1[HRy kgԼ6Yec=Mxuφ5[kE.o57) 恆m e j͢}j@h/mX=@=ƀap1m 箋v&0?Jp6JLDnGk5 /][ƧDOm,'a9]LJl,tA5=+HZ5Mv>_9 ;΋Bk;BjFQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE!RxLAQ'H*j*j`QEy5ooc,g ~>g#ezh~/x<Yh>sO=|F>8xNYO Ȓ }OxzT57B)Ao^9? BڅIsjlU5vˉd$*޸.-_Hsu.n[ ٣B4d(t '$z{Wy9xgö~"o@3 q$+ Sgg=i;N.[Ej2a^Ib=s_%FqҺKnQ -k_񍇊)hSLv$7 @J >j }Y37pB-atnʆe?:W.gA[ME|m3,lZ6V&<'kܵ]jC5 R :e=}YXj.mmqQB/HoZ_kWĚ{gS]fմХ*awoE4Zu[H{rF9`HrK?ξ'$7-j Z'f$:^glmL'?ŚNw-e{u|-dtʩ$sG$(:'$7-jԞu[kf b#?ITm15'Ԕl#fwgŚM}ƽeo>6J꒶za ?J9%>GކX^h6WgE%FTbWZf+D(-zOQut1) (ԵM:)bW=p ŽIvQuWZ?!kU~Ě}:ͭR&L989'44jhRJ@;Z9%>Gޅ{OVNӣ)#Y^|l%l.+C(-vNQ&~#?XC!kU~HoZT.iE!IvV%s)nI˨\6[JMrps]}OHoZ_kSz|YԓY}4)cxL ~vi${Gmomc$('^gyx6Yvm4Z[Kz,[:"`T0h`_z!ӥS Zgx, yoNk_k4f&bkM)`ql]xIcu+k1gTNN~rK}bC}B֫-е_?LGއ-ޏ_Isu"EBw__kGލ+N&Z͆k}m!+".NYIK׌L,#76Yvgv9%>Gލ+f^geY^]ŝH89PI4/4ԁp86gwOj9%>GގYj]kVceΩ+g)uOiZGkzt]_.?ξoXztu]>K+gQsM:M]BYO` Ua ÓXF4ԣmdӔ7TKoIxN-^Y[R@]}ܢtzi垤bf)LqMfgeY^^EH89PI4rK}bϏi-0kGR:q;j.YvZk3J''?J9%>GގŽ|8ٝ=`_z: +f_cwYZ^ɍӺ*'u=vNiխ,EXFRxLAX:45[$8InƮzX F#Aqҋ54HT~W5C5EPz]-/VGPӖTY nX|9 >CF+'&9\cO躮tcpɨZP *>fd= |}%֟6lWIUw3tϦ1XVFiJTZ}BIԌ}S|}}cm(i#`[3rkgּ/,1Q|{Ӿ)^|fF9;ii6ƹxCzR3ףiF7o?a̔ZS> բh;N]ֵi_i:[fɷz CxB}>=/Yе[=rK5NTak x Ě x X-SKԤԖKx+6Wb]eP|co9n-#.ٶ||cC.3|ނT6ߑsO껫~og]GF4?Z[B-/fM)TFOJ,a]*w{nL 3W4y5-.^{P$T\,Фę o 'ts|VMcM -2[K"4n[HN +|8o|/ Y|5`5=sPo[x7j ŋriM/kM+O%BM5~w6 E|V- /_˧F1ɨHuHBDoB$q_1x[5xwZqkm`4V$IPKnq#Wy_x%VN|!t;M%Fi]1!jFzVZ|EMcQ.M&-F^ (SY8!H=u[[-"~:I[u[iX|HCpB+u_:xrKW\әA%pZ*@9⹏~s^F:ni- tlGܒ+pwd rio(ӜSo>>(x7I/[Yjj JQ<۷,D d?~#|-Ԛj2ǢٛcEFv#|sZUΫ}ji5E%OP|ڬ̲F`H?"-?Q,^;=*Q$Pzxc'('zxŇ4\k'JI$BR$ 2;IH#<{]M'U쬴u+:[أK2o xׇj6_'4<ۙ,$Q Op隭;7PSQξ&ծVhm_X`%)˗Œdg= _|?۝Agߊ.,*[ԯ#IѝddPjlc ACsiZN˻4[, V" {U,Q!fWx ~ U״ j^ˁb$ZIBN̈́ ==|3ּ}7Yxs^Su?j+k4Cܾ 1cyWYW5--c(t[I,L%-n7 $V"'RYsO]W(;G]'oQ'6W<1Kk]bKXouk},!C7ǟ$s\!Ь)l!T`c$z^Gs} mjYǡm^vg_ B@s[i>ԭ/w藚^wZa9%ЙbFO&ᨾO/~ nZ6ܑ(%ݸ`xdžCG<7^˪FX2K;\Iw27yONAL>|5<17~(k)DK;J+ݑ.YwKqʝ%W{]yuگ^/u>$Ō hɭh.MľZ ubpkKx!xBM[F8t?ys-徥m34DN0:dw}MӼ1k{]OG}JTR.$dBY6V9Y]/ k ˖y}Kq ZAM?aRI/UTB[rY@V%5㞁_hޟkzMޙkD.lhh=2&@ၷ`)yh<M.xR4߈ߊDPy...n7.lRlU疚^7ƌ9\|}2&Fk.9%uK$Uݔ L>&xCķ:US̾Qg_JA_"x+߈tix{GI!IʞwHTn%YFXrH_|1-2Z;o- nZtFPdXgNrsJ׷g_N5%mYVBIUGb5wxǾ6>Mgc5BGXY(??8Ulᯅ~)qoSMOPuKKTZ%2Gl fJ\n5PKj)PSm=G4ig 36L 7*RFć[|Z4^~CV=M5H ȨȲX,n@koNQ}c5m|Gi}o եGYW*&1֢-ּIe|-);5-R/[骊ܧV+8T+AfS99rvpFR}_v]#Z`tMRY,."p `kǝ/Ő"ҦհլYT/WG#ྃZּgӬ|K%݊ȁVhCH"I^ykڟ6iB(A$D!pTNR_8^WG|-"[oA+{n(@ )+ @ kO&rl!baQys_=|=<?A hK CK߉T#JȬ RÃOӬA<[wZ\yVvHAUx\owJD+}:"0darWs#XF2BsnxK#/t4u),mT ~SWtgIu{[v2HrʡG$q< 5hHS㦱MR?,hԶpX`ԟu10VgRWMգW nES`s,lHH/t _)߃mw;YZHv,EXQW_XNyoע~x×ńwq- ͝.`?'?0$0H5/-SK]tr!om8>NJn\>^Er41Sw,ͧƐnfAG K)JZ[{~62F4oPOM4MAm;MnOlO}wQY+Megk0^i:aYVrv[`m~-ťnm+A-&ٵܤ.sQT~,~&Okw/f- eIJ r@5n[?_]57]t_ҿkcetvޙVrp + c1u_mv +<{[Y}5HXɕ`'sRwK_Y+;}_yޡK]vkkӄ1n׆[h<ҹt FAojpTr@|;0g xO^U[VFt[Iue%m&VIswxC<3oO^%ehO0w/nHeV'y\J_wSW}֟}|UO8W*BJ:D fVR2C:ԚhM7WTI ޣ W]h[0l38_5b|>ŤZ[TMl4*W).],,{/jcXL<ǎt[_hݞR"wdǰ/5\ϚvG?ϩ|Aui)u nդK2P[Y012Z5qxS-?NME<2ğh<{ ,Xj/x7^egojw_̓[ѴX!;ٔ93#4FNQRzj4>[Gׯx~!VQxGb11#xC;84 Nn W^/=m,5(g¶ĕá1%[Fe߈-].=$iG-úK$ꎛ>eR~6t/ iwF}>Gt.Nmb2 \67._xhْh'Z}J҆AwX={w?0+е/j#ᴗw i~$UOm;rn|QWTvw/}zMn}Na*j1;JV4i]mj;KmF5#Xbr@kǾ |;<_ ]z]wK, (H5H_Ap&ů~1kGzֺ֥q, V,68xU^uk>YS GpAWبP8πneyL\ƑWVUMȖeq3n= Brw+N7zYW_{vt9K ZZlW)nUU@KO9/N"S] $o<@RGr~|M=Futgy lK hU]A$WaxRMgF3ㅖzU"[#7ڢ*(R99'buRrv]}ͭ~:47{-߫>~)xjD/|AU uh%:n҆U0@$W=/Wς'b^pڦjRan~dtdjP78b2keQ-wN]RRd:Ŷ 0XYxeW(aRXƛ^g%}q528ͣv.X+-9rD7]q=KT+“k7Z}wCiisI̷NDEFBw@!= AqlŌ}Э^E$)*K109O>Tz|L<'] (㍮nm8(V, T=|C|G/YOMXI*N !csuݵ Pq x?4xㄞ 79|WmnDg[H퍺 {m߷.,uho,< _{T, hb AsR|m»mrGgq302>63"TIGN^\{|18KRwW?h[}~cUuegXKEGOƸ~okg!f 'NKɀB ̀IyGm#⷏tO 뚎VO+oo; w3-Ivw%j'b*WK|_uqz'צh~6G/iJMjtӲm}Ծ ./)d` iL bU@Huo.MwqC.ɯeyi~5ru/w_k1Ɛڑ$2:d 8GnҰ(,@SmR=B-WO&n瘕o?jK"FNv派 ;ֵO|a ldiBeܤ 635T[Ui(o Ycywm-'#,/oMOâ_s*:ޠm@3+IFer\g*˪>x][~Js:m/]ޗ]B MGU}7=Ŀ_|,O$w:4Zb3$H73HI\lߴ実^Ax?[aiE7.gO2-cw.\q5[OF^5޵GKjnImf]_+veWE%G*=?//|SiK4"9eeXFxpHcʴox3Z1.5UDEgj> sx)ifLj屻0Gq!)Ñ^dx>Ck^l5)WӴm f@|!@o\jPk> 4д"X4k['Y^3 ](kv(v~m^t=_zVyiqi`"B, U'j 6;c|@]K-KP*bK6ح"# e WHۿ|@[ä#ע_7Mo?1kd~ﲹR|_~8,'n5?  $ KS^_>~6~<񏉭~>&uowWڍ΅-hgIrU~lV oO/a{R֫c-7A-$#1X(nOo />0I+ c=q,& 衒$vU'yo>xXj> ķ{ 6nb7! c1ddv[?EGo\yxoHn|>ڟ<ثJ'̻SgN&y<b>8o|+nIY5!?jgcf* }EO/xPt?Ú|A2ZYIYI#EPTr%{yݝu)yi^t]j\KuK7 O.,{v' b|_^/zO'Imx>!αY±dLB68oWtMSÚG2ZJTєad O /Ś煼j;Mk([n/᳴#џi%71 v>񇊼a7}b k;}/T֟WT43,'U@p*.WI]} Jk}-۶Ҷ-.|j4𮴺 k;͵H1l2yw@6BBwa[IJDbFp)uumG4SxټV'"]& J˸J)crQMo&iʳn_?SZ//|Gtnw6v4˽Wd,0L3*# x㞙]|y}1I(-_iV9I9ȄPN0N5: Y[XΙr&0jn@(Y#=3J YePrK0^i;o~/*֠;]=ЉA!!\⻿z_jBOik4 \ %5ÿxGH x@<~!Q{3,7 8d /ȮFAw|oE:wݿN[Is?2Wޒ%[-Pҟ4)^W.Xr+~CSSF{kyY!X,y$7c82N5~&Lj>*yu7ejJ>tJnI@~.]|Ai FQ}WN;[Ȥ*nIlWa b_qn;[Znx^𮁩kZk]#5,~^7mVY[WW|INYݭjvM)`hI 3ks\W/ kדxZtI4{n[;e3r[nCy5᧍<ReMr 1)ndݞHVjmգf*pA-]]}N| ߗC>|UkLJ_ ~siJo1q$2:2pH#axz=G>! xZLcUeFJURʄ%>!|Zn5ZIcY§M/ڿ.z߉xxI-b Ǟd)m Iv wW/ǰ(SVjme-2xG\ko6_ |_lwy{sI/xQt?z|AKsHYI#E]9lyǍxagK hx-r^Ćk;.t$.($N3_t/x6𶊞&}" >G$W"E$xW*e\r4.]맧]_[]?j =COd߃jd֙fmNc o6UĊKc|}L9>#?^w>ƌ+q3@XRdoÒےzYkmV|Kw=ƞ ƃ-+*m5n|Ucj1\[o.N#gVǻoO +i}4l,Ky`7DT,’xדxW5xWԾxĚw㶛2CK:y[tkkŊE"yApf^VY]ık}nmsedHfnAm!qI_}z+4_ו%Q[EP^E|zd}>"w Ow-T zQW!IQzZ'|c¨|[n'mt j"E95o_:iF.2~;xÚÏ~!]*}^NP'DuC̨A 5^fNkxš/Z˦^Mk41ܻs: ~ƹ> Q_ͯ $Gb6xT\>fݠs\~ͺ߉|/M+ýQKZW+}wksJ򘋤+*~f';ʁYv_;oNgBi}K~YN CU]N[[dfUY-5$# 3F;Ϯ|efOx[Tvd(K: rUc> zO,-g7:k_;h"&mCVvTHD;EkX|Z?x'AD47vhwPLlPfBpgl4+? tO4wWxw߈E^ssgn&e)D . 7ʅ+3ſLJOwk mu({8YJFbeɖUڪrH ayE>Y,mlj$e?z<96vn{z΅c:_:^u$VI&0B$ͱm m.0Q]z[lћ' MRY[Z-om2{Hɷ@\U`r5w/~<5>;{ Qmp^GtjIb% 2;qt!0rB*Wc[~;IEUEP:KծhuC\8T~U5C5qNQEy<_4u}>9m$'bO䬿f_vkA2;[fU`X1)#ѥA`fJOUmh+c߸~GJ}-Z ¿w׭irAӭR!Wx J"l]MI\R$E|/#0b܁}\_ڶjn؈^pqT*v3Hն-|Iqxg’#EyQ\?ۦ1U !#^Eϧw]ݵ-I}? m랃gxri54p~sUZF3`(#ڴo*}j`ÞF$/ܚyDg"DŽs_-k֯g-uD92] ^i4f )$ d zG;g}L>/+=;i5O^mLswnl>G"p"Q;dX y#ydg%Fu T4fң[R1??c xsm#uG?=S*-|$J]q~i6~R hOȣOG~ʗh3^jEєXr3]Xwĸ\p)}z W'9@g)Ù/|/hO)=J1$`]M^YVCƱ FޯJ۝ /¿l?K ]S7J?BiZFԳ8\H'8)DbHU 'ql`"D`UusޣNJ,=isφtB}}ݜ3Up2zdM|IE~~h/=7ZSBc{H onˑp+돂2^|Ĥe#rI6ѓ~aVf"K5xW%$_pge)OG7{-i|1UO\F|3* sCGcՙ[b[NK"TF2&`ǂ5zx.l/SO -pQCsyz>[ɟW=/mӡ5Y\=T ? Ck 9?0gJ(M/ZWRyWyJr2 1VX_dF󊧆<.. 9wPo2< xT ?>g؏^ɥZ4Wyzq)~G5{ .Klҗ!ݑQvҟ'er(IjSO:j'S^1rɒ >pY P/[r:GotCn "%\)ܟ˽zt;o ۭ 7o2CWd#'mY:KMSx'=-HxmSeWjlx籭[ڳnc,%" ܫ €xz׏-]Meʹ9{"q(|czt|rH`8X쁌f.3\~_}6 }dpR9xnKTWLP}е/Ԥ4:fGwb-F{?ָ?? lgqKG\:v360Tq\4G[W4B]M4c';ⴗKxCҹKkFzt~'[*]c‘2Y#V 'FYh9Kld2p# lzWxú' eGB̽"3_ &mw+;#UNN.>R '"p`i+^H$VB.[QYp]w^yzu"{\nO:@-Nz&bapsAOS>*ߡtwՓ KÞ0$ya6p|GLq_izGۈ䑣 I;cl쓅Ip2lvFSƣBkR3x[M/1rU~z׼R`|2Uߠ"̋;Ih\$Rx0pOS(JfYfj u">Ó U,"\/?gV'^'[ך `&.8Z ccm͖.\V Jmm&BC\C_^EZv8H#wMP1 \u7m FɹsJ[] Nqss$E9#dیuy+Ce`o)ye$}3{HʞU7-QO"m?gGe)rg") dLy8|}X]r4H11EwͧqSAӅPlz=QGg%Z8XZt>o\Vqcݢe68`&sۡ4WDgu䐻ēwB=V"^).Z6,Gwˆf A#n1V+];W×vb̲2Ę [@W qx﯑ Š=^ is6wlٛv29$xWTY RP<ҎvV g]J{O L#!̃#7q+20ggRJ< Ḓ ?G= {?_X{Iw=ߨῑl(xC D!~.|Hýbk>Q*C8Y~(#_~"_o?"mjw& GX4A8J*`0E}V,?V_zp|+!n_~C>ҴF0ؤAN7MrwV}'#ڔkJ[2_JsCo=W("^#t bdSaBye~Uĸneym{tVHou?"?~%ahOy#Eq  \+ōhMpc>ـ_?uGFZ{I3U~'1  ?BU'g o [6-:˜D&NqG xjPݦU`+9~ٿԱ]"kJ[o}6nJ֧TvP">ٛ 6;e ȺUOϬ_~69L_B{nD֥C >-j{ƺ孕5Ȓ =]aQđ.D`z^_8<<O_GWxχBQEg8ukk'9&>_AEMPEM\GӅQ@WCztVp;\__tۘ/&iu4Y219FN(_a|^xKM.q@Ԗź='d:0ЏJ֦^ JtݧucjΝJqéűjV6qhZ/BBI"B?P}Sv^Z iOK$QBKܰP %?(5/:'5vѦ#K68NO@ i/Y@k{Kko59R4`}мgqaMF=צԥUs|/><,m*K;f!4+e VOLq.W-^s.|Cx6[M K^AF3!|޽3]"/gAJ If̿SA䷖ou.l^sǦ:}3S墿1n`νlzWl|f>VsW|2꧝tHre$q{kZsO= yN%)!#*57N_$iؼ=ǧ$|av]˻69׊1yoXRERC΍z.zr+难es[[^\Z[ [!Vw8ns:ޥ㕷 .ϕfPʆN:⒩_#rƓ>OӿexSVxyT<%p9FqDZ>Rk:]DTHʣ2cv_s.c4ח,8j ӯJšJhW\-D7 ̧ zWKtմmVx)4>iȇ8X ۏ5 |3~Z-7&q L(AǹQRa# cx##`Nk)TuJdž5MA4vVUe88xZPPXơYYv=mo%ff+ERhʖl1L:X|4їRoC.*Hl~,|8EWZ֤q926Pb\s_J]-2l./  =3U5_ h6fskYV Β;RX?æk/'R s`Src<9YЮRE]؎"ăxqKm콯d6BK$$w;B/N=k ^ b+٢VkbJy a{U#'f֝yi8[IV,C|jm<<<"eZP@7jR Pw JωF "[7Svb@9Y3]Pm%mr3}~4vvDrgCÁsG w)$8>.+,G (X׀KWzT^v?v>~81ޛck?\I6~uW&ֿ߯KOT4gc&]zGie^rIŠhL'?Zs mAm( spkHImJH|ݙ :UMekW1Mmq'*88pj߀Yv7I9Mїu=^$cU ˢ2|+_IIʤɷ.>`zrA /î\HQcx_('hrqWc/C5-r2%򅳓8'޺;\i)ҭa"iJ8ZQjVCz1|t |N0.oA*-$MtF\7쩤\o^xfE}U!j}|[BqjW8ӼKS No*F=%vֆ__dž<GSM"yXBul 6\z^^4e[Kx<,qUynd)T;cҒճѪN>n⹥Q+ƕ$ѵ䷿g{Ϋ)NzT'iR1#~'8ƾȳsζ{[@/0W#Q.f%q> [[52vILQ[T) Anq_)x{G]MPq "QXT*[_{ɦ}6j̑2~d(bLD׿Z~K 5 i~I{-bYPcR -wc҅>vz(>Mu_ؤm_ αJu!T68EV{v:PG5؆zrzQ]|5Q%rv*8^Y.oRyd}$ 8f$j?hph|-2XۈYSm_'k2O+O՚k\FNh +zW?HVic_{D-:2B Iv^:ھHCBit͠1w޻(ycGRᤁw?$>u_\~tF6GοK*?^SbAhRp牕+;i#]L_Ũj&ž9${{kh̒K h܀+gTW՞ > ˊI.,/%w1ccޱOy_YH.\ʒ0+0-89 b}*j)rAZUm"y YֹH𽬶[k d!hSm$YM.MR/.mDLbn-{mN(tHդb c8';{ן,Mӵx3_tC0s$I 9'?| |SF^孟R8$vH]pU@pF}k3P:adr 8xOX5YteTq0O=.e*M >4 \{ hK漡e)XnM'׽]ݵ3Mc%>s 8"Mi^|A54@avO3.##ֿex[wm5B-HchUHN yZG_"VWl5NNʋ%do Uq |SZsw>#h#I}9VTz^0CH 9 B&3}+]2 t!Ԡ2%q3Y)ŹnɣVTfxФaX*ՑNn`8|U*O@Ұ!tyDH .#'^hU>etwc`W..__/ ~|UK~ץYY!9AqOh rN1RnH@ T95d?VmϰS`?d)=~jˇ  "BϧCXvsJkV6qkAa_ܼ=nf.e&lJJ/ ]\"Gqr$Cԃ"?&9!x!(\)TF=9#:Daʵ4] I E`d;Wec|_=N<6K8Onqk< _Z\Y'vIkbu8{YҒ6lGl;k+uHm #  zdf\),Qձ>W?o}ֶdxgkRu.1ҍa*uaW`*&L = #JϘݖQmA˘Y.rEcAMKhU2km}=jw!Ikc[۾*jEo!-h^ytYnJ#pTm(ՑcVeOڀP"u)l~>#d䬚 'N?:z&?:TQEtZZt?É}NIOuP}ST0}SWEP%d+WBP<͘`&I#NWq}2FT +|8XZxrO?65hm5זLӭwp$yGpN@Pƻo hċӴ'pcI[x?s_Y"g=:7WJyX#[z=:<ֲگZڿ^J%5E=<x 5j({dz+_0|6U W(l? \GFqG:Ra80$T2v#9q^M'w/C"J_̏ [ʾXw,(sCѮGˤY\HJV,@ESla$G?{w4Yꗻ-X\7ӚySgZ$9?:ux^JQwm\m?jR5mz8kIYKqO `񆏣YYΝt[ieT3\LJmui4{5h$ #5xWwgq|іb{Hr/I#5tg+V`;9w [ NKm=GU?i-`ɣfW zW]Y/fw=[qiw?Sè7يXu+i5{fghE Aȧū_ۢ'Qβ>cǨZSRląckU[i;JL RC,2Uc]CPbf & 3t_W/%>F'' bGy O@,xTJ͝ \,Zn#R7ƀxZڗ"(_xЇ\VCuݵsTv;죎_uY1"PY)FI瓁W:βJEi8*Tޢ[Yyv?gS%![\t=4ݙI(lKe;ʑdBr=BRgyZV$^oݶ~r`Ϝ`1ߚkY-0匲 עBg?2 r9o 駞~m~wF[VLA~UIH?|F:Ym?kY&rlmF6k'vM?)E'0.JpFqǿ^? o>Kee + 1dPrG8zoOD-[3RHJLUuGKky0eaPYS?),@f"=uW5Ԋ:{^ Q4z093^vڞ A521 ;Q>S5|CTؤD!oZ]E\9o vUK}I2Ǩ&fD#sngN :pjYx:O iO|"s#p 093ZnqSۼRFB1Ҳ)6 -cUl%X-fKuPNt-!i3:Hʼ[MK\~P _A}GS^j2 @1J5tQ'ŜVwM<0ڹP)t-Rl.e)[%ŒF{?ҹMCX~'to'hP I\6><-^iwpnA*ΰ]ÃڰGNg4=@a:|Leeˑ:t8VOb]x~[馔qu.$8¤c۟r)a0\ s$W1 ssʌz+7śZ.mI 18|ﯦȓ_]Gl*"cR O|k蟎[4/H༱1KGNpSAxU֘}ir]Hclk#lskԩU.xq] 5`_0`bTsJ>HV6ƤGzt7| &6ga\%^A'Vl3WE< ۭV]˸%Y8~XlR:Sktm%-[vz;g}UgXĵq)0N[k ;H7OW76o.N$;]~!XˤXZ2.@U<8eebv׋^NiVg)]Cq[:2vC^WC^i5VILzC;;嘶O`_; |R[PN$mJc@|nx~-xL,g-[ۇ|GL몝.I-:2m{} :т/K 5M?DΝi!e*ږa{t+%͕!lg[YPUnOP|KMyV٦.I]22/cҼ^gF_DkqgRE@$ w!#/u Nb **Fƞw{ ?\^W[rOtvcz(-Slc S cG?RU/qJ՝ ٬'`2IҹrV5 ,է/Ow\?Z5IgRp y=K]R/h!*W K̼w炮ڼ8 6G>/TZK\bRG)3Ar+R:yf >W ҽ#\/!g]'CA%ca[2\eφ}Q}JH"u dCmSҽUz <[kBbژܡAr =pQ&1;E3Es5W Zn.deĒVE#9Ep-[6_ؿo34L~dϿZ n*?[ƿg#Xs|WO<6?)LC89Si`r8&E`>}_< VInd kc ;A2HlS 1>"LPՋLHPlBO>͉HބImtX֖f:ReU`r'?AU]03KĽ~{sjoIX#3^{O "Ne+7=>VkVMIS+J+~'0ϷH{f&9XEk#[V9H ˩ ɯj},Զcc%e%B39߄4$o;nH;{sPז'Bp~z=\a%kn)VX0?NCHĖ'$wx/dQlgi+w5@ ױzV#Wr&p3^oX-' Jѕ5[6?mz \qX[ʴ=•p:qXqSR6b=1^s=cǰ$FN]+2J֬TԊ O4OCG]=}^?zb*z(ֺ]sCtZ>$ڧ:>詪>詫p(I8A$דx7=PGt`vs*lv?55[bY- $n<Hּ+K&#H:oKͥձ>_08%4f#Nj9m6j׶z\`)ԭ4`GR IҬgJ&z}A f#>;BfS[΋p.&u`.!{s+^ P{FP+>Úu++{Rrb8c:p? Sʴ&&ӻM_t? rNe3^RSn)5eٽ[O^1=&(pè9/q#mC^lgjq+—cK0$19Rp'=+]~#<x?rdX7VjI7m5IfԪPdMgNkX3triZvP7  -Q6?,n돛pQTAd%s ] ;Jُ~I*0O+ ˛aa295_-ՍP2|ϰ3eܥݬCy* ʘpVkRQIJus>KoF C`Ʃh[[ȒȤ1V݌p2+C mCF+k8"? Ao~?myvGdqOC:|cz/C, 'e3f>X^A JRHC>O F=W:bATubŹ1C[^?٦gx0, ǥyѭ9OkF*B12{nakkks;|GΩ-;FТ9a)bS=>n 9^+{e Oir7˸u0you+&ՅɖpH$c]8jqRp}i]]-R-'Z[ GM˲Frl @5ӥ"Hc^XOLJ?:^ZZZJ+PHeD7pn>+٥)OyvT02 ⷂ];ڦmg{ce/'knHCtbϋ֐]|?[Tk%O#v *jH7s/ޛl$УXs5G|CY/{J{-ʽGVqmS]IltIm$z@KVխm@yB!Y1#؈UQ:v=3@Լ]Vӝ!?܀\?-#slVNO5ŬG}beFqׯe^fdjڝltVPdZRu#(Zz3PO9|CCI$e7,TzK.k+v c\yh2x'sMtZ޿>{]F/q8?*kƸ˟ G]IJMj7Aݒ&&xg ArS5+J'1UoOmٌEo;;n}~lvWRk[1$DШ.U]_8F}wukޗj1ֺfjPxzFX\c]RQi¤z^Ykhn A2X+xQKin2vn Cx}o ִM Γn ,0ǀ8#bZfOĽcPt4 )$AG>{WZ^=#G-Ew42G+9N=ՆsbKjf@IjsF#a0!9<׊- SY՞gGhu9 1m0V=A 剦լ=[QSj- K[ +LP6@9bkF$4<-Uyn08kҼo4M58o4d(!y6 'r3^-|^ҼMuA44s }仢7Pe9sTǧ_biWYI;.=s^R{y6w7k87[z4ܞs w[ô16;$D玹=ӏ*b+g(İ9D>|r@˕'͎1Tcu 5V+xHd&a3t*x~#m:Ŀj۸(UO6_uj)岂JôeS8駏2ZNm*SsH7ʟ(1\8q^\qt𵓩ȯdJOqxNѓ2`y#8Hk.eGhnl>2{c޹]?,a̷>]̑*5Đ uyfOn_[\C݊V'.τ5tmHkVIHs"6ѣv|9K-.}V8X~*+ۙ+c{) Ɂwʁ^o/+|@mE|e?޵j+x~.ncv#X=y{by ToMտ.!$_I-YAR Z/C'(.~0}}帆#42, [.m:T$ P~356iΟm=ȿcNL{/~0ng7Zh 2r _Wz+Ժݞ5IGyhNug>jwF]X8Sc^X8]Vi!A !<UtM1  ] `gfXѤu=N\\Xۘxa1z*}q*Jo{Gx6+h<ɼNOCv2ŵU{)2ʌy8>Ǐj&SOܘeLK}d*sUӤj\5՜ m4*%`yTjK|&5Ƴgqg:Q(Ef aAm'XWG٤p(X }xxf_I#dFY7B0r܃kn.eյKYEuqSОx54Z?4ۼYi.v$\" y8pGgwg"FmF@զ̏[$6* 5[i6pD| ׹N6W5+ចߴ7`~`z>;|Eh]BQQHp@#%ʸ 1'3~7}B`mhp? WV}N[ևS/m;ڞ6/xO!,2u&,^KؑqJ/_nxg m*YQg RGo+@ڡqn+I9bHt A0mBckΣKh:헊~Ѩh&jAsu2D\oqfT\iZި.ij1'\84hN\8 ˇiL3ZnOS[RM=6+ T`0 ޢZ$TovniXf JI>ys]$>-ԘIMsC; rdbq\Aj S#ҮBGt|B?]=}#^?zb*z(ֺ]sCtZ>$ڧ:>詪>詫p(CEO ^sEynгcǁ}_W/w@lu'9(3 cW "$^Rb+| z=z:wJ j5UݟGf K-jN5H bkݗ؎+ɭ/)"GU^۫^%5:x \QٟN.c*b<_IK(WvNOw~wfԧțm(_SҼ}:Yhԡnqk`D2\v ^ +""FU^}` 웎3f{nl]ҳӫԞ`Xd939j)7>n8'c6 Tg`$k(RH;C+?N4v.'F=׎_"|"ܞks~<PK)<*K4yVԩ 5{Z"2<7 -ӎVg׭[Yvg!۞}Nfzi;XWT5E7<Derh"ޙ v ICXڽ" Id $z5 -VuiIly{&̑#=3%nxG횔iYx>w(^ݞW;.fL˟nK$v3zWf4 ZyZc m@~G\4?>,{{HbXR?ڇ#!,3ڹKf{b dy2@h9Z)S{ӂYjZ`MB+[b] 'ӊ *Mf]^AMu!FтUH?cX_O3]ʂnwmm `UmIcQ($b@9ҳm%>S|v#g}ZGiM ؙly{pgFGQUWTZUUNn"<۲q zb^ мg>^ᏘӐMq>o-JH;yʬpzx%+%+[ۦ@8< vǮkx3ŵƋ?6<&02JK'ە0ק~p~Ooڴg+\T,zϵ~>^m2D(r0bR0{kT|z\* p߯j1^$㈩nɜfC`3KE<~U ?<5 D59K4^T9n"EiejzkRxE*;%&A)'N4}\k^uMRmS]iY;`̪IQ A_]8m:uD~kS8SWY'S3=5.c;NÒ@K ix~$e5YcY-22#95Ղ.Y=nS펹/mbЩcץA4,|G[[:XQj/| gѴ{9cF6:!K 3cD S-.!Y$8? _t,!h>a}aR&0>ے99:joTD Nyo;Hb]w/nZJ+uEolF+ ub/>>x ᶭ<{-+})SYӴԾ%X-ӧ-5*2oO"VdѮ|7u^h4(_AN>9MgǞf@k8oa{ŋlq z7CH|M{-ym|CEji!% vVQEuM˥r,c-dk7QsRQջ P]ЍCedd/9i}qn# HA ~EriιMzzkpc;HeDh; uIV&Eox d!A6C \6|{xSk28eU\rTFHڴcoC}''3)qb8Tj ]Z20X00;WJU8 m˖y,&x؆.ͷx0%le3ƬQ)ktuYsp8?^U.~S$4W*zcҽE1j),7 j@'k'Rp?d-GZitۼ" ;%dA+_wPywkn U?|d\W05/-Z’d%S +<;}-3ԯozVw_=QJ4u>Ec$N6~\FbBN/]aWvlqOb/+A> 6W*\iN$W&rt=zgkOCw>_~!''(m 6=s_ Adnhss󺁏'F|]eWC"0*\ּMRIkhz;-S׾u <* _h0r8J|/VCTm2JpqiŹڻY1<_9Q/z-zfܭ?~%so k7TDE7RO_*ϋؠ9{AU?~XXx§Lmg2razJ@xar:`|U#tcT鬵(x#:̊ȒGY9$:|wx^/GTAOVω~%IkIk3ɭ]ǥRg!!OAោW.D׭5ermb0AϨR-׵%w/3;umgw-ٺe;Ԃ0A]OD_wWi5r|ܜrT?ֹo ~Ж>:/Au f8_8휟z߷O|Y]V0hw #Gr%O^GqW:ғ=?3S啗_G|yo_idsB$y%c砮/?[k};#YIN@/3Zx˰4$yD/6NI9j[OVrVKzȬ%x8GN+JU%o/cmGW|YHnei'ٕBJTUz4e"o#~2sO8V7@{ˆt#g {Wj׹yGHQUn3z衅WV['#˭7{48[K0u%PVI | IoQni;[5q%ą<̠,I8nyXKfU;2}1W%/t񺆵jxme7{ UvA VKdCäQy̪3# 8{WʴKSIxบBɼr#2Fr8"Do8_ ú/K7 y<[0>l %^HϛUWcȏJ* Tq]S2"8W.iwiLZ.l#ByNrpqs[L7z;\X?X*}e??xXiz u$Gh$;'=:: x7P֬~BpX˞WiVQD#+A ks&ln3L.ŌvEf]Ez$%15>3xɃ,Vdnl x'YӫUisxUz5sW3IZ v,]{UFcjXq֭\uɠxCK+* |PJH|(SaEU:KծhuC\8T~U5C5qNQEy6$-p@d'W gtWkn1&#־R}4NLI|qXסFڊ~};YNzxK[lMougK_١I|>OV^ʯr;HS3~|]@ٶ[!1lhnN1֡/L3۷4K1ODXB@1^SMXOg;BD d3 M7}̡UFP*rZj~`]4WJ[" r2}S[Ahv$*j ikm.ŻsBr99A#޷&.tDUtL8|k]:ptݙE$aRA>Pq\݆_Ehmh3N y՜Qvh^'mCe{w-v6'lZ3ξE&F6<^w/;d؁%dXpsޭ0>#MNmYYDK4@o.x զRx~Kشk±@C^s mơѵVW'-#tf0y,܂Ć1Uu-3MԦKYQ60bHzX4{5 t,n'3n?rͯ{CVChG{_Xj1cV>_41q{1Kg)evGsYּIҒ`$$lu[έ_iwVycؐF? +uh,'դ4;SZrFr5M;Zs~K2AhW ;>n??|KymALe=bwOyծY' &y*Wk^Z|韴l4šej8gYPBK~bwrk=QxPEn]x lx&.x,ɁBFV s=kҮ,FQxa9܄f%gZ6Vu " #n{ΰ(r ;]qW58&Kժ& /)밌VY&FPvb13@ >R25 hql㞇sL*3H+9d!9V*1ֹ~o=U]?BZd1!O_ǒ*~9k(tK+mL 9RV}>_a?f p.1.nt%5W0~=baky FHgҾ?n֟ |Q"/f/ڗ1ѵI]*-*L>S#u1~< f!<+]b$6z3x-#.4|cMxV[t1q'y9Pve?)n1>\7%i4S4JXTR7d~dFzzV薶jS<3Np6ÃY+ѿz%̣88WuR:tvO!Iml^!`JNJ W+Um#։ SϪ\A])R0`pplkZ^׊Ii8 ql:'k-|'x5xŵ%CyOZx-+]\JVrvrF;~tgvEC⇉ χF_6Tϕv>l{~/37lu}zKvCqDu1Fg7*ǽpW~-njOVYIzeo kk-δFUXmt+w%OUc*6Η\$,R7p]weAk<]PXO{ܲ0Q1Hb+'S\ Kr۽BKPyqQkZm=]@T/ (,% S]T:#̫WKh!R[09.K'[݌8b763=CP緋{XMgVU?XW$n`.On5RGهn, 2ͣ' X8cpv~al_ui$pNzt[FPXIAʞ][+u*>y(_}4CR25m17yG=Loͩ׊ma]ё. pImպVwH\ p~e 9Lw?z|B5;=+ɍ<~l*FZIϠ:Y\SmwQ)<; A.}E|灵\& i۷<~J X' B Iih' nkkpAs Ѹt z9WگxX?1VVmU3~i(KS\,CKH">TV[9rc9V}΋a}o[·~m7TL[[*BzGz"x~(.-mW1(ɰ #0s>7=_#JrASﭭ.DL泋Yqq^:*+G9+h!{9r0TpAtVIj|:H e{|ehu?Jƿy`jyiWC^8;]nKw Xrr_xѤMp:F81$qQE$A;EU}O:V\RM*ӏNh(5OsQ:~|;Zi m1]ͫXA))WtOuj~@>#~ ṻoH8K~WnO9EnrqV%bkӔUO?t9QO݊)sؐ1979=?*֤RQp| Yx  "Mq r:קMZ#i1&n4(iB)JGT^HT(?Xory֮asb0֠kAos_6ŧ9}[Jԭ^TaH'.G?߿J8|(SaEU:KծhuC\8T~U5C5qNQEyơAi]?mm FP2kmj7-%R6-XTy!$#޽qx WR/jLh:8qokds&gg!03kḣ 0Y:*S~A:у;ArnRQ)s5mBn!G0^$ۆp}1P/GQ+^-䰺%yS?^ k#X۔Wh<|Qa$ěWb>=G^|\Nj뭝Cٯ0Xu%qr9Kk|qٟ >$xl)u -"7FA^V@>#*^~ZCII]R(D:dkxF, ;Wn~-Ν,_j\PrbG2PN{_kb0]ZN'giN9>IJ)ߑwl ({2ۏ]3PX;;7'kxf`XVLqbo|Y6LH9R?`G)?iaI Xc=01y،~vJG͡mYp=24B񼻋HpIwy_O}%X0HEI %hooJxayk un, rX+|Vw|GxOUl(Y\+iEzVnVZK>SJѵIȊ[o.=k>4}Q\ݏ\M1ˆV){=+o%um.|;1J8zG~??Mhv>i=5ko<ߵw%Y#X䐂N~|8ҮMl$?$cZ@RU[(猞Sb\!Hkaoc 'S޹偭}#q4>T:3Y)xouIiuXÓlf;WqƓ5įy1cUJ5-MFinF@RG}T ``T+ZK V [ۘ8l Q elsC:V.Vr<Y {}o{Fq bUpp>. ~]?ZK̫\}냑|ad-lmDrS(N}#Dyơw*f̱Z:nsl>__1*Sץ=ex fxzTz!Cu"%)8VoAc!io}& >Fa$B +\`q01q- eo,y'W4浦TqTcҮ +$qy;BX`<*8?HMoxSIZm0Fd7|޽?ك}.H7B]JVePUNp >.E617tbR岝X)>2x,m7 GsvdHYpp#XhZM.,b9d)wК_;D Zd6ygp}֓RuվM M7xҽadN&X7R -n"챲ZwK̦"&?νOtF+zôFn{usڃtxcF\j|Uh|z|ϙͺ) 4 ƪT 2[5i}n#dz}M". G'rl1}x@3f  P"k/ZM>%|~`f%$  s޾Zhxca=8nC KIo4*>A=OjkN?ke>;[(޵ok J[xe%8Pvosu5g#aVC#Pum-:DaXTP^>npy85Oɮ?fBҘU)½?|C,q?e\S@:{8 "G)aޤd3\,ٚ,U$xf˻ui˕5[!e4gTAZgkؠ]ӎ 'Y[Ś1-eU;On1Η\Ep2"T,ӟ]tw1*}֝_E\ȾTQ}v^OZ[!yD{p6Dg9:~ JSRJ;0鎕_:q& 嗎t;T~?LFHz滾%hI3۷/ d.OsY:Zmv.d'=+?`vt$w?gwOU=YbYl0=*ZZwr>WѤڱZ4}Z|%.B96u*=}1o6[7M=~Joi`K*>[Outt"%~z}ǿ׶GbA-9f䡺КϯM|9_MHl)m#I2SڧN#9gzjr72<K(:NGtS椊FHRA)>>b2-:} W'n &hEg6Zo^R5itτZ6 IG8+}aW2O9YPZA$j&Q$׋N1PpБ!IIϦHוFu!~gQCIOwIՌ^t ,4vfns5x/OXYEŨ{F #_0+[$גi7̈́5Ȍ>GWU|c֩SFڏo,6Twɬ#Bv)ֆݝsAžaM>܌a;Wۿ|gҼ'ejk壈/ʯ3_ kORmIk:/mxqЋEԒ}y:ju 8'?u3Wqď׋/g̹nuS[0 UgMakKfsM#vMu$皛|#<MhĻPWF}MDO\|| 5,8\Ut#C  \WY+gWֽH|(*zm_Zƈ"] \Ik/ pzQmSm6kեqN_=_8z GKxMJ92]iR{WO⼏dV5iByfW-9V!b0ҳٮ֮-gG,lU#5Gޱ~ķ+9{~& ; fvMPKh[C|Ü WxsGl`ӭ h!R?Gxl"<%ܽ Z=[4>?AZ]+6>WVcNt92Q^AEPEPEPEPEPT.O;Vw!rKM*/2Hy3&ɾ$`kZy EOP፭p YTǩ.ymWS'>*9Nc1&hg,_mQgv:a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_}G%?a 3EŇ 3EŇ_ 1<%:qrPŇ,G,__{՛ihU 1FV#hܿ?a 3EŇ 3EŇ_o7c84}mX?~1a)41a)56ɾi$Vq%8W|2,ks{>/X?~1a)41a)5oeZ\iouEVPJpA{\}?4_ XJ4_ XJ~{}G_}mX?~1a)41a)5mQ?a 3EŇ" tX ]ƣ$W_}G`Ňp-op }(E[_B}G_ި;; go2j_]Y5v .%{_}O}O>%A%yirt&v*-zW<s_]{_}Dy` ԓkx3/p>Em?Hcφnh߶kzv&'W_ykG67I EmQ?h }_"'}EjgO >!Iխgqs7]ː "fA Ookz-k-L5&绾O,G,_iV3Ǧw,j} 5 mYl_Xpin_e/Y/,?%?ƷZݮit_V]^%2ܿ"(FN=O_Jִ|BX-Aⲩ]ԶG i^ݿůο|j_8zLkTnwsfc:e`Ӿ&p JS(3"ҿv{4A7cODS6:ϗor6>N*kA][jz}ĖV҉!}F5wO#|B 茹f_N{V $U OmѮxTO?趒'YIJP#В>쟵-̶er "U :КPT?eO+ Cv/͖3NX+2\ `9TT'/wN:-QRR勶-\|h<7koXSe0Y][G̎ 9'GZ_}k_Q/_b!'=_IWRgeZ/{sݮzO|3>%͍՜g4 =u>Wʟ1iǫ$2Ckpá~f}W}+3\ = u F2IҔT~y~o+E|QEQEQEQEQE!@ k׮ s]mZY7>.^":aCmy}~oOit]kz,5G'>}ދoOis[t}~G}\]kz,5G'>}ދoOis[t}~G}\]kz,5G'>}ދoOis[t}~G}\]kz,5G'>}ދoOisRK)o?^t]@mRvt(Ei`P|I8]>k>$J|Q:ccu4moᰗs>-|,<Mgvڅ䍨K$QF dHbAAI ?3z?~]e-Ķdh<آEAcFlc>+$ԦLb#IpZ |MM-J./".Im"7P8{J}[kB?TC׭x'Uj_ -Bs$1԰,wp*4R5%uq =c/cZ̀FWB$mP\%/=H.ȵ!pD辁AYd񾟪^jV-ՠU[! Cv8RoO"_|@'OXfi xR+rC'L!E]dޏ`>Y?i>X.k}O}O kuz>E]dޏ`>Y?i>X.k}O}O kuz>E]dޏ`dHS$}qYi>X.{G9æM6Im'zUO]Vך||, pyv:5g/41c+7T޲!$䎄#R·E4]\jM,2a7Q+(u ([GS]DYo-xnu n*Gn5YlC"I2r>:V4#,kIːzp1@׵ g9BB]ݟ]~O ).E쪗fJ?觰r<| '4Nm* 9< zW^6ضv>1W־C4UҊM[tgY~bf2oD2ݯ2A5VxU^Ro*ޓi˼ɛ.y cp? Sܙ^"JThœIb!կN ,4MƑnna ġzZ_` !W0n.떚= ٣_&3\t f_~I5VԭŜWrbj]k?56➿?EXkиm3`D:@ۑ ~5xXgN4&Mk^~tl pYڂ|¾/m/i,FvOK G} }+\)<En1v}UpfI"Y^( ((((=)iJϽo}ƮIs?d_?a햡>HwPg]xNORj0W?j_2k<ο -<^i<rAk5h>nM_]J-#2^߬0ƒ;@&iyaE5|#f)Ə` Կe/׼[QoucGA^P]4-/S˕Y9l]G V_I S{/|#f)Ə` Կe/?Y'*mEӮ GX j2 I' V` Կe/  ]]kmU*}f+ 哴W7Ƶh%7hX[jI$~`&?60KG)+ qҟ(\+5/K4|#f)ƾnzjZ^ X<ۇDH$gl?NlYS̕m22SЀGzV_` Կe/  +/?$eLǂi:L|ORKx#n$2z,___+5/K5xnYQ=_nMJ'0xO(Ο!m085‹0W?j_2h G@KR}C@PXႾ3RG0W?j_2k@Pe?|  ?Ⴞ3R^deNjs%ӮVF\e] =';+n;[J-s$|L"%4B__+5/K5Тҟ[5I{ J:* ($ U@PXႾ3RG0W?j_2k@Pe?`>^ G@KR__COQ‹0W?j_2h G@KR}! ÞҥɣiY/5 A` 0UeI\7LS$2I4{Q $ +5/K4|#f)ƽş l4$iPYkMR,[`Umw /Ϣk?='Z9t[Cq2*l*@#+3  ?Ⴞ3R_O{ Y#t`Ynr=8oH:_-v]t=nnaTm^%bRHiX G@KR__p𿊾x\Em+_#WwAsp&4b)  M'^ Ѯ5}m6|KQ[x"WtfP2y$[K[+5/K4|#f)ƾ| i-ׅ|eo[hY&x hߊt ~u[Qա4ZH<ۇD^G3:A4m__+5/K5Ν-3Umu3osGeYC)@#Y'( /|#f)Ə` Կe/?Y'+5O`,lA;Qi71HBEI9$R  ?Ⴞ3R_P7,KGdX -a?Z߫=ؒHĪAJ`A`>z G@KR__񯤵h ^V[ wۙ'FY?*'w~<m㵕a^Cqh5 F]Nr.|  ?Ⴞ3R_Bxb/ x\5F}P}/PH*3F~I:H5•{+5/K4|#f)ƾ u ?ToYԵeBj#()zw@:`>j G@KR__tT}bH&{yfI` 3k/)LjGӣ'MFn0W?j_2h G@KR~n)ǃ17K7A7O\0W?j_2h G@KR~n)i.JS7?9(c+5/K4|#f)ƿ74%7qߟi.J9" G@KR__|v#Cw ?ট!;G sw+5/K4|#f)ƿ9??9'<}sNJ9__+5/K5?_yu95  ?Ⴞ3R_0C/ki.J9__+5/K5?_4%(x:䣐w?HႾ3RG0W?j_2kޛW cՆ4%>F.ck+5/K4|#f)ƿ:c|tf4NR`q/J~͇:?CႾ3RG0W?j_2kYO:ޟW YӍoO`ڱW?CႾ3RG0W?j_2kcsOrU|qƷzSj½Я` Կe/  |s)4=*4ҕ4~|#f)Ə` Կe/q6Jg 𾳯Z1rQ 8$si+0>ԓC4m隮[f*\rMKmbyISWWC_ЍRӑCB0*i6V~if}'>sKjqH"+M|Ea+/co8oy$gs+qoeAL0snϨt%fLfgFK񖓪j'N%^c%U)Zmh ^%JE[iJ6nS?/)ZEhH=QGuldV\r:#־pZӯGQJtm̵[mkyF3џ8g_ x 2nU$[1wϗ'nYX=>WƌW:YHFpwMԯEWYEPEPEPEPHzR{Mp$]5x5K5[&?ӿh<b%=x>Vh+at q+I|*5!{6rp4*3VFŽIx=K{/ϑ~<.ϊ_{^a56cr LsRxƦ?/_L:Ity,y<0 ܥF>w]^+Z > \]ژa.b|ą*&ğ;8o 1DW#宥chFS1s(W5oOX.7/jח pnmvF#@B{GL=wXOti..l0wlsbׯ-sJ>S+sᶫo_6ikeөZ+ mHĈʅf(CaI 2Tueuo;;K/Z^e^g 3[֧≭@24dCq$3gKyf[.7pZkԴS7P !&F$0H-𗆵 aY,2-*SccYhX\-HhO&u鑜0Fy3nݿ&1W P񯃭EK|wtSY'YSwrosڼO)Ci4jl+-ͤpF-䘬2y><wkojp\jkKuu֡xB<myH9űEO dj%̈́61uO) ں TErVv*~I\ yugž'H-wxZ8Yٔ\\%,wG#L?'Yx/M&y5_0X̬L )]ED+Vi Ol%p#r7f>/l꺎7|Av)5_O [DLQ.2Ŋbrp0}=5_p=]OgߘEۙWkmwS{{9i|;."u(w9Dit=[F!161ʳ^`<\sY GÚߍ|I'Nu7hK"oQ`#p.}Gl? _qye3y@H˻<)7;aɤ#ܧtZ%mUcCտ $e;;3>˯ϭx>7*ӭ4+S-FEo,-¨Y\|9m|m ȚLlo4WI/'y#5?DP+԰r3Iqk}S|A!8f^T >YnE27.;bx 2+KF xo=}~ _c~䚬 L7㵊 r{g|?ֽtzd/rLI1 F՘zU[gRln cz Rݝؔ_Ϗi_|:i:^iedn)f[5.ˇgnV&|E~ww ;ƱK GIܢ{ cXHcgյka[,Ku*v_/tq*#7k>&4,Od2F3,k!]0YUPF@Ȥ~_M^-z~ ?cΉ7W?Τ6D,^F^8QѤ8%zmێ?L4ۧQE{ tTV {~*ji$Q cYWΊ n Ysyľ$Ҽ!kZfe3WiwZ NO<W^"4koV ]5'>tpyf2‘@~6_:~z| ^hiEܼ0>"s1D>?|Ik6ΪuZQ;E$JV`B]{oiזĽk,E(J%X8=OTg;z&OvWGɾ4FO?{ዝ Z{Fȑ<̠ dbAu?'hڧ~$Fzxv]VY.g2#˄yR3pI2I- 8tsx:Gt*uMó6]$qU+7H 3D֛?&FU,Egϒ,$z;ZFgeqxOMg:Ϫ]4ByUfBa+Яil۫&|I&'),s_lƛk{jKDD!e`%X8=8.D/~uw?MEiBs'C5 f GN3M$(Y|b'f9{/GNI&N'/6&YSןZJ9 }mzڣtƲ2=kDޮ5E7pW5E8$aA$65&5&Ou͑9À{{Qb1NBkkincif$h2XL~Vq1<Տي+ߋcKE46Ä 6=¾r c }¤ڗ*4OV|ѨK}nck?V#xF# FA$srkסEЎ< rqֱC|,srpTsCm+ :S IɝwO8O\OiFYT|s̮~j=֭װ;O\fe=^ GTum-j^c ? {w1vAjۋث]*(.+C*Xk*)VbxT,{bhݕVȯBHWVqPҧ^$|c ։mUbobŸG{e=K[oEǚ\_-݇sL^c-Vmo"N\fkQM#@pYjɠlm#7cvB_[unnx2\s`VzjvkX<ޙ NK{Y1dtry8^:_̢]{M?ß /Ռz~WC_ЍzyD?uֈ5I47,E'9Wz$V>(~тW ƱQu kP?J~&xV/rOJwߕkSzUH _dž~&x;ŷtkY"FGxV#[88~mь[(9׊rVi%`i&eI0b^I//ꗉHlyfO'R28[YicMIU{>*E(^ժ|NtHiy #(uXwv ,NWmB51K#oP#

LBٞ͐$?V8bjZj/cR2k=-!f#$rz$1k;"Ȳ!z?S]Vs&QYaX3c͸EH @ OU]2+ fX$s$=v+30P;`1XҒꨚ5xnI0kyx#/皟sw<~:ϩ5`DڴAa%N@ 1x?.G[O]ϩhn>tl?^?s;t -=JO:+NHkf<.10vǽtr U&57Oh26Щ`{¾mGwnSҾ<\:{Q ?Şq85KXol3dƼ>WByJJ>txO iwaVCxӯ|]4d&i2#= oLjI;}Kz}ƹ[=[I&Gzr /+.aizݫ`:\sӲ?{ }{+ -9QFNO9>}YԸ%󴒤UN~'^]moH 8w"ŽA9湏i g|Fϩ=>婇mkn5?hMxv9dLmةpr~-1*RI 6EV6E ʃϿ/Qּa}{ hfZU9- HW.,<IhӵK{{14m]8\csY`®2C(kΣ.?jn : rz<|MY|q|S|4Љ%su&0#j)p ^#$.ٛSf(C̄) ;ds}ySm)ɏ[GiC$wgjR=b ʩI? "Zֶq.UI$|p9xT Ձ3)YfĜ, ^dm)&ÞI?ue>$[>ȯ{W_x  dݍI=`y":Eck29U wIkq%V67;POY!Ւ$cMh[r=&h5u渍JGsyamAl9;@ڹ-SǺ|RlMcN`h=)5QW R@ٱ.+<*5j}kìëLllDq$2ZZE{ wRb+"]F_hYZ􃴐J q׸i'!T@G־4Ÿ +)<{s%yJv;}Mk6e/"0tVk7>1m/:g5/&m2]iV=K;%W;2Htg?7'XK@$D"y9#v9E%){˱<g٭3AX^Nx4T/?{kfZl1k۞E}dvGgi\̟tH<[vkKcծT+ VN׏q]5|KO>.5I,.-|/hBR;H.IRL]Tꗯ]Ϡ|K4b&Ki$Ub7$ ޯ-h׍ +[+N𶯦MhrA=6UNd% 1 y?ƹ`KӢ%ׅ4{VTe&s5̋5Enfi,zdK]kAgֵbrYcMhfӄRK e*X p7d֍Cwg <'ZnG<4͇.ve6OǞkԭ>+Zeł闺Ŝzb^YeI$lም4AELn\ |9-nAsu[ocoo$]ۡ {js;^W>ѵ/:s{\\ےXC+A}dž5x_MҼWy>l|+ftM.Jg2Hڼ7QF'C /;s[:#[ ֵq*UOFlʱɉ4&LHH׼ׯa_EW>vH{/nu{ [>wFY0Hnk_iơZF"~#_Kh̊\i;jѭ[0YxXInP$ qIj_1gowGuo?¡hn)5/ anXHϧ8hŘ Nx=ަXaۃ[TZQ'$=OҼ⎍>'Ypyhlm?G^q]9P=ldPן{Jxu8˅j5\Y^~FTh9=q*2:k//0I4ƇL{3!k{/x9͢kI?y: '}R~ 팁kV]vp]<4>4^ۛ·W7^[[F6Cn[Rb@R2q^1e/ED blOC3+>NĶRh6muf.#(ۆO;_|f»wqs]]GV>mua$J|HXn(Lnu=OSbcPmCgx81 A @΍hvP$,͈a 0uO.+byl296|HmЍZ*>SתSDS5/7ZLRO-n&L^GC-JْWܑs^wWuژT m lq:O$AffT%*8ZtCQh~DGt>^4O4ClyR08 )q= pD7DPFCںf_-\~aYw:kP1j]ZnrJ9Mm$y c>W?CӼ+g IBYcvvv+jXy!8ni_QPOJURwd2ma~^x5?Vy⿂>4 ۭ686coLUf*q!H(էCjǂv2l,4N5Ki[-ͥ \]a a=Q۰=lR,u}^HA(h`,ۏ 8㹓zWf_#l2,8 =G{t{sKAoZhhqkYRIaed2y-yr3_y&)5> *92<= MAy&g=Lnoy&1tdm<[+412 ahXVKuwF9 uߴEOTxHJԲ^>i l9 6)xijp uuK ط[ݔQf _|!cfX Y ~)|>+Y˹1s[Q8eGOAg{_e^cm/&E9cYcz۴{_J 6n\͜Ǚq“}v|o`y|E[L.nlťѲ]k,>FB*ė^(hKd̀s_3|E*UjU!,2$Q:r=k> kkH}9Or@I%od%ZO+FpGE $2 ]89'8UX{jKu~7񖡯Symlwk+S}'I |- *<'VϠM^zN[["RF韁;)U.G<ʲa*z׎|RkmbC-m?|'N]uBO%Q FJ_de4\ nI0iSݬS\jg4YP1Fe\W?|'G'hS4ed)xc90. >1cN*/SOg8%p$9?+kJ .B 0곴lgj0&P,Q n>nHG&Qy9`9g#򯜟%YϡxVl[d ;eIϡZ&X_bom m:Ncn:=1z5O ^ t"3gX)B5#kv/S4-A!eb,}+?ŚHѲ Uki$ xOl{T:$/όƊ趹t0e ?Qnp|]z[cIcc020+v+ŀU&K^]J3 3Ԝֲ8)]eU&kXސI$ я1v8LV$oqZӾͻWSң, hW``YO^d-ڬ;# 7ƺ4~b,;|àXLo+k*',ATd> >^ߝf2VvrȀ wxI`|ԒAE82yRbYWG,꯫s={cx|5H̐.\𜎹u@nDk|6cilxkUլi/ec[Ԑ٘`d w>\3+C`Ց_W씼*{Eg\(F\Ʊe6qD[C%o1v#,NA,zR ѦlwD />_ yzka)&? HyaIq~ok0 | znWR5UaZ9ݗ@f|M?b;IQ$2J-s7wnPҲ|L$(@Ns mWE{{oo4f+UCA(9^8Pn*ha+?NOs!;V5_=yŝMjm7Jjhiq rF:_>_ /,4߭`3nΛ1dv`Fð?g/7~ NǶz]%[DY6(lr氡rU۷kVif7V8Ѣլ7F,r9#vHe# x EnC+sc;~x1i-&.$"GY(,Abj 1^Oֹ]0G5WR2+`NѸŶa ;U1388on>s~#|.hy8!#3 %BErXKfa{@g܏Y{{i7*M_(ӟ,=/^,ֺ͵$Hʊ7c N}Eu`1>'fX~st|&y}s$vڬ9,&AX`_?IRQ㤻Ijdy!0JZu15E8I:QM?8MV/ݹ0#񯪡Kv+N8kw9~7^/(MӴV4JXyH5&6<8";_P~?[<>gR׵KW&ȑ"ߵ"݂089˚/SXӭ.^wTVfU'K7(CJ>]X:֣fu'jR]bx>NQ\:u>еCߡܷ+\C2F_Qw&#@P>׳TkT]FjqR75K` zWAObLB3Tq߭z._RC10 dU׾UvG?.hL\ <9" ఑y;y+ oveWwQ…W $υ> V׶5yImx As[_~0xxXj`5ifE28A(iPs2kU+[P9<_={ij:'~ݍȭX'!r98 TAw<#|V]wA>ɩjSmIR춠7J7}8l6ed%3`}xJ;(Jq]\et<b;ל.E8Lv%h{Hj*RgOs$eټ6g'8ɿz^>Q>zqE{cES$((((/vׅV'ϱ: }+U-m%Kw^kFӼCC冩johJ֗}{^Q _~ ;Uҵ V)IF-ng7?: ) 30_>!Yzd]^'HM]ҡ$A-!eN;Q~K _&C yHooidzќGCSEKkwڣizmt 2ix'КO>/YK{a=՜ B@fHgclpP ^S‡T#֒ ݠ?޻ b2'#Ws?AhCix!H4|g@1\^1š cY -" R}mBxiһPW7cW~ݗ-W&xg~"t2AⲗN7 ;n 5v0$n`Oo_k^ M.M3P(jr_呌F9\IW=TgrI?U.&i"uM6pA)ƾTԾ(xƿ u h_qãb(Q@U+W ;[='t?WO{߸v5lĄ?_ʽ't?W+ _7琨9cxO" >b$2pQ+1KJ x!"{K4:}U6F0crj+0>%@sϯk/nV^91 G0t=B𭆍𼗺Dqxg\Pۘ:Y(p޸wOUkFꌡ,Hu#^WW/l*`ٽ^3j|GMhZuރhLs_߿WԠM)RzzVT_ݩ߉~!iz-K[; 1?^uh[?-՗*Tc*?~y:>%_[Eq8ʱ!=c_h|l'Nq 72nOSj<=Ǒ w,*5S{q-[#0~ SI:Gs_A2Er?VXw޿e =a~3+oYgL.'B.~uʃ@|gƄxVmo,FADfp;>f? ~FR.,5m 7cp')[bT1ٗ~y1FH77|9dB/յskxhڅ2XJcs!!j%(<|'hW Ѽ~ڄwKDX2ȭ@>j[$*H"\OInH$d%[;?o 0nt=޻5boBΧIa, ZoZ+M\Ep"Wq<sk#h)SVc_Lo|OIV)~[l!O9pi<֓Bt~X@aAv} 5.[b0[=VF']5GpToVA#y֥/xMWM˽[4X\0}}לX0kw!ůmJ6oQ'̊x+*G8lo[j 5뇏?4n>Ƽd!/QwcOuEM.Q+˯k<DŽs3Y~0q; r ztR'2:p1kQxwŜ] >9 ?ι+IHnZaȀӌ Ӭ,Kg>@~Pz'8UƽhiveO̟e^u} %Ac5ϟR|%XeQb2'2ğXGľPzxa]ds$p0}+?g|EWzjWK}#=r3)iy;4[$V~@7 w'{p+OL (^GjR1,m ן!t<]Uԣ`=}8Wɿțdbφ> oe"N2k{qWioD3#b;檧-|pveU|Gӌ~HǏw-~M~UiЋ$!mbW~ O?c.pԚoH{oƩ -{E"%wA1޲5-YlI4̿1VoMA ۋ{ЉmďEeqk:x'!{;Ee6)1I{_~~'q]wM.d]X,; @8 ^ũƺw&5_62+;t+qq]]S#n~9]  u#2|с!V tcv յKRvyut2`2;»M[R.!e4%Qh'zH|KbdY&l茝RnHE{+g~& 8O4'O xeVԬcCnHGΠ#VֿK*M HBB g#,,5 ˋo[W]4]f dc#E"٘%[Y~z}txįYxWEuNqmB+s@A*'4]5Wfc >P5Ե]n2$t.LPm`:_cQEAt<H>s2QO /*+[QW?ݿJ?ݿJ諟ߥߥScϻ~cϻ~N{>Q{>P:*=G=@&s FYEnLW2O8ea_|?j6K}^1s=q$I#(컰: zK췚;N..t$ϩfRIog< -4߄ Bp_#]K4iG(TsZ~GW7*M&&q2"Xdy_Cƛ46gǾ3F;_ nj!\^`\@w#r8>A-,m 8mڹRdԼQ$~M3NZ8CH2dݞ?ji~rC! ,{qx$r e\TQ8=OX܎$(i vzv+9TZk<l[9-ҏQFm=Nt|;ិO:YHS&" ¬#t1Re%$.rz gھqq+o>&sy8 m'8^:4!};h+;h]rPeH>f7vS=F8QK{akugS@W(\Ȏ,s_ҼI5*ŽN N߷xp_𗊇d6Wsnkoq+)?.w$5A5XVoB/>W^sޫٳ>8zI,m9qtK" 'p6>?`֭{H.cmR"gkFN$yVSw;be;-Z`Iih|ȋ7&Fdײc>]T[6З1-&46LB$0.E`cJ:-kAv-Y>dc=IWa#~_xcK׭x-JM2DIY 6w.:0k>7GuKzc6>xQ+dR\\e:iԔ]|-g\Gw Pţ`Nt#Gjn5b$@ect5C Z]xSҟWM=ҭ%3*uʈc}o]DIʹG_=*VPwL]m-sVdIbdc=A )spkoZ"/u K(mgqslC7w ddWĚÈ>Rd-?^ wdsm չuakϯB <SV>hZPSRWaH;~7Z/VxwºeS2@">J,fsk?~&/tVh}ͻ1*N?.!lvM'E%9f:fZQkZWGV9l޳u&}vҺ2Bׅ?߅mK&jF4{Qkp9e]˹5P%Gz~ :wv3elgWnno/巄A3~X߹5 ?gcS'a"6ߋhl{0 lN27Wɟ~Msi{xrO&k p7t6J(yuD.l |Ɗn8ܤx\4yuvHS;h;,2[ƹV?0SKťkw㍴NˍV<.BU[H20e9+ Gѭͩj:,? 4n˵`lxQӣ-b6}*Rhbnh8bpܜҫ.Oy(RH].O,OakZZ ֑{6T<{׌W݋;xc`Ñ w#i^5Y4q2= pA9˞ZNԪW/ I+۰λ'?u=q~W~2kO[Ug%v!󝿈NOQyA$:1)=irZ {q9=:2Ŀw5-kت|GO<8Tk4dKnÔ9#jU 2t"[F3^lq}aEyΓ; `ren(9I澱1w5h:سo[:B}?;G>*RӼPto-ÒA]͕%@8-_e+a&90cM=-3HۀFAJ-_"yI8cr֮JESPاz-]=J[kH/+}I$ w&%71J'~s#`cBwHq3{ O>$Mmtb>h&i@,r*φ X:%I aKG $}'&cci|Oo%v|yM؍/D8!/2kmಷ:F9$n Wؗ%!˱CHoF+rbU-C3c'}ޜ^rЌZ埿QogK+cXʜ? h6_ ijQ%H{_F8YQEY!EPEPEPEPEPEPEPEPEPEP\>-Z&2N9]y޳f+`d惞ȆRcxK&ެ}²%C1}KVԲ,':gO~FnZ8l\tkk=n#˴l>Q(mݭӮk VRvlLƽfX|PT1` J rA=ҳw$gǓ9ʂrqGE4}7T]NPx NXH$aUxV eH&c''i+U Pw)MT5_ }#xdfEd8]/ {컘R6 t5Դ9^Z2a<.9{yG<2g&MXw];OS b=u4|"mс=+twi)tl_PfFn. 1ح9a mi:cIhQFѩm<&pyVVt獵|ugo5ks%F!'y K3k3T<JċuK8t7( !ǖ!rNR~?֓5) Vc1vkɆ-znbKHEIN\t&0ﴘOn=9"3oɪE8m|].q6#61[GzQ5-[#xϋ5;iZ]gig8UTr njQnk?wS5_O{nA噱,qn#FjA9h|64f[[r~5c sYүt Gppj;V7zu$EO*rz`WԮzuꐾ-̶V) (B8@^$:wM'E,}8CH,n 귾S !20R3\d}kċ!$Akjd*G%ٰ? `}=dɊrMGF4n<׈_ d<4NYr?Úmowk2\[LHUyt~B'3.>[q̑_ɪx9Nxip,{:dFPq_`IP6с^3[IѵMvxċa$fP Ifs,p8UiI^ztm*Po!5#Q$a~5F۟ &<MzOfkKO(͇%ID3'0ağזSjU炴(}F[˘9ͽ_'y˂v_@QtgU'}7rwY,#tUKEmIv3>!>x|UifooE\9̓Tē;V vH-HI d6h5?n xĥIK$%NF9eRjdc7W7k5m9;|[]aoyA P17;qgYnuMǧOJ'(;,O'<]Û[?浫+V48X]0M7F`T(u-=<Ium4ai!~JU٪sV4caʟQ^>)((((((((((+>oi,Z[$%̄'$h4BFG4[E v0C~,@υMD8Ua_!5+W <N?J(NwS-6Dr^=n4={{ mxA+bL hU>jyat %̑(GQ/qĀE@QGn^UoAsIܕ>|7x'E^{O_В с=qhE/aK܇. =)~Z -g@|E C_i1cI4uǤb:5B-1E^—'i4|tiHuֱ邢)ʾ]޹&Uwew;o2;QMEGdC-69I䆙' >}wċr0T,wR"㪆(˒=Oį j׭.n4Jos7B̙~FA$W :YPkeYOEHFqѬ=χA Kc˯*Ej9̨/?`|RzA058$OW2E}#ny0-g.cH$op-NsڬNRxV6frgNxcEZi;oC0c\dF{G}tQ\VFE$0nQt][\mGC%čͨ{ }ȥV?~ x;C ,tx][x7IWnmh[S_rM&N@4us˟񢊵F\ϹSRNZoq )TysgЩq^ Yo&7\.v%.Xd p(N,R{'{VXӡ,*Wk ?$sy5|B2-_ oe<…)یgVSB: Sᶇ/GrZkHw*ư<#b0wE j'!FiXēE)QV0Oq([idN‚Qd޸n'..R| (*d 팚(J1Oc~ iPAụu[@dɚKCxn䘘cT%Jg4Q\~•Ⱦt˹;!<54P.xr@FO&w/o1:n@(!Urovn+n$5uN>VL!^ո?cK@tQXN); Qю0>.Icnǡ(C}'4-fOZ?!'yl>kL@gEJkCuj[y4ѿƧ^U5_r2r  K|WO ]]3:$+&7RFy QEiMA}țiO5Շl䵌]=w( I|fo1gŋn+`X{f+/tImx'º7ti!(tHVfXbFrHUEfotoxx-18.01.1/images/adjust-HSL.jpg0000644000175000017500000007340113222767271015554 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 297 359 0 C     C    C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&se䚡y*_-VnhoRA"LF:|m0j.tBҡy>}OjهbܿCF3)Z1}eZy2:d}j)u#H$SYkPQ+Nc.2 )޽vY҅O)ceqܱl'z^tUTsp//'{~% ̏fdЎ:E6V)@ww`@HZi,ie"6c>lv+ퟁ#>*Yêj=0_^Jnare6ǰ[*-,"$ VBW~.]kN[moF}9zgZ#uJccO( 'n5#v5}RSōcOd֡E66G0X G0X F6Bl _k,P4(VhNBA7n}?s8\ 2yn>Rd$r7O:*T!G5\DԪ'fLu*zw=u4١9]Z'\J~}*s.ubU{ .GB}qڴ>}kKpG1"F4h:Guo9uU.g`ʙت GSk o^osc{>$S\IX&rՕcx|>r0G?wbK| jM-HZI]SNI 2s|Gi⸒9bTI\"y`?/|R?Q8?ȣ:<~xJ t F56_6Fڧq[wៅַN pk@qһ`zs^8?ȣpK:<xV}KWХ0jw"TTUU=]Ɓx_Btm2mZdgB"bY IMt?cx|>r0Fol>8?ȣpG#tfZ_cx|>r0Fol>8?ȣpG#tfVn䳴(2is(S: }ǭy|}r,R<2G$Ur0AQ9|*亰š\G/G(V_ժ+X}oGqj,|cZNnt$n;2028%bZ]˿l>}֗1"}֗1"}֗1"V).[Gr% n[[`gF|m 2y䵎 F5 񃟼A# k?<)ⓦ_维6s DB꒺Ί} _<þ =i:<603G#y8A/ mw`Mb\}$~غrdw#y ufD$k־xk]SxlK>.1^#_%ngQBC%<'o6YO?鯦F)l\7$a.PFgR=v_ĶOA >9զZ?5X(iO):xv_|%ؚ\ZETv*U.I?ľ~_zkVQ%M$#xDg* *t/ g:6qaOCXߛ-U`wSII.MO|DKI3Xk@9^uFdCZÓèo汓ƚ4w[wUt0$(AOŞ:ּ4 Դ2G 34\T' |-=:K/:Ljnqn,縻h&?٣;FXr1/t-A֗=>ËύtXtLȶWov. mM<<N>?xlN_b>!n1lw?xwXbKG ] Ozyz[9b pʰ={A[.s=ΣlGqosLVR y+;7h[޿U\?^H?Ixv!;lm߻BEw> Y]k\mm0K5EIU8pA>0>&j735Uۈe;;=yHjKrz?|1h~'j3iOﭮl'g[HcXᐏ к]|SKNM_sh^-n|%7Jҩ U r;be̶?}Fh]-ъm f<$5.<+;ڞ 5.E2e^N5[T<[|"yHtҵ/j: ow,*F#l lM.![L/|zMBy<$ll.X+gw'*g_z╶ƱF|IcY5YiVlLÎVKߖ9<7Z JtwUo4Ds,wVɜrEn↝;JEޕuu}) sȒMQo8MQia'*RH紓P{7yeIv:[k @5='ȴ{JZA j ,354X[BNwZyΝ^5-WObeE,P:FOVoh/ ׈4˫M:LznZ]{OeEWşRQEQEQEQEQEQ^C/ƫ^l!4袓KLTrL劂@F>֮W=RK{RZ Y$`$er8W%[h xėBjouoo4 L(&7ldf n+վ.WCtڄCDYu>plDgc<>x.GI$.ő3nlg.3Ѩj?Z?OZ-äK 1Ks"BVX1N2z^C) dSh_MΉe 7I"8uO*I؟, YQk ]K3[rG I<0׎o,Ind1 $R0UA3Oӭ4(mam1h=>*GR_XCtKq$pd`s9ɭx~:W#XfGgpEOQ-l'ׁS'ndPIySKkUEDa1&Ec#tXZ^C{ͨ=E IQ$^hP a y*iv05;lm.o-`hrA/>(_9Nj4x<1&Y|$!]h]ݍW8J_WEܛF$m.\6 x$ \Rp/ڏ>;qj6ik&F(`od $[T$飉nTjЧ^Ѩr̞*G$G3'rI/%WI~j%O~˦Nn.cbbZ-ҒQ#R9ɧ]Ŭ]No 84/hva6 eyl ;^eNʰ8fy3'rI/%Wmៈ<{6wY]^-$<)_! rBp;Fmw@7]uIZ'#udUѕAO/<"=QfHـ>__jR4U OYiԳj:b9;W}}rxV +5{l-+nA: xW!tWOj.-X vv%2@kij1I/z>qq"[.Z;l E1 |Exy9wx:EMJL/o.asqD0s!/_Ӿg\Kik-:\][\Udkl;_נj+;hڎڝ1k=EWlաHΧ "18> 45 lKu5"=Bn#*1 "GQ~ve#y-5 K{۹ l2O$q2ۇ`@rkԑTWBdЊvQE󇏾 \vҭCX.nYv@rgk !O Es [k[Z=i%ť4 -1\Kx4!ī 3佀,,X<&("TRV_Ϟ-v sLM̒jm܅h@!# pNz_ |0|+.#~omY|EO^؍/K&VR`Ib 3o|#q5MWv+ek mM0F쌜♬.|c(/b=ZI śJ"h} o+UBU)5>oOܗտbj^mXkVVk4zi|0G$QB342Foܲ?JU'jHp^Yχ $xu'Mnڌq d zOý+–$ݟ4}YTYe̦\!^'*E±:7ۡjZ o𶹭jzu{m܉ 4sIL\Fyxz7OvR!g,280)[N^~ ~LoW{hُƖZ-t#SRѴ Mg,6 R2OM: 8"X(@XɲTR9Lh!D7݉ 2u־o߄(t:Kk:5Ex$澴#4&?/I;s4ZM滫-χK'0Ka%NFy>!־'MRxLbW1(}n0iܓSF{Rдw׿ %Ȭ5V:_O+Im|+, .I&❋nkďC촿jriSxA6,k]B9 fa*ch/Uo iw/+}y./WGwu[{{>팃V3ߕ#f[on[m8ݿo q[q~U«d4Q6r_SMzz>n_~?VOX['suy&P?3PpΉ$9 o>k`H;dWF*vi3^D= F꧐r⧢C0` KxaiposxK`eYH Pž_ 6:mwma0a,WKJןJ I0IPS85Lu@gj:_ Tv xs]5XԵlC E[(ui_tOWҵ-Vmj#Mm-kp/|2lPH uC>]YfBbtB 9kǑN}XȨPp6}ӪW>9P珬(k 7M>|Mj"X/l.,.l~֑2I, > 29 BVT:+@WR;呂06 fhŽ]?fͥͯɣIX5;ycW 4+ E}$]cc;c{ '5y7f |=𷈴8<&zW{ȒY;6psﱦ.!٬m6&{{] Fn7洁t^xGWP:EƋf{dcvmRVnV_/\4*Hef cT-s '&KbwQmuQ462bDIeA*҂v".]WgxRS4Y,D,Mk# os'b?g"}R+E9lQ>B<ʱ:##$l˒kEKgoO8_<3 mOSmR[mzV" <2H껇"R{W3_&wuJյ"jzBj,i+)w.:׳M?xRĞ9V BT+d, UOa8i7:m嶫k3TY㷸8 !BTxoLi~b}~s zGLyͳyRIFw(Ẉ 0HcfBّ ؀k x]0ur֫GQP/|vBSg<#tѝ(7aXT^\+(ü1@{O);G<;›N<#u{|b(ü1@{O);G<;›N<#>_='~ <;›Nü1@{O);_W(?Q_b(ü1@{O);G<;›N<#>_='~ <;›Nü1@{O);_W(?Q_b(ü1@{O);G<;›N<#>_='~ <;›Nü1@{O);_W(?Q_b(ü1@{O);Zt_^'<ciڴ&~y<EH\YPF0_L}+'-:OmtʌIF j'W )4. Iʥק8!!% qUE:+`-5@KFH?Ue'ke|k19GE9Ho.}{fE͆k2O-y@l78y;J[NV#zb _NQ _NWo?*6/G@ZmbD2NXr [ CwwzOBe¦g;K rsI-կ{}_qZ/<^~'-Ƒen{-&9\f7tQIA W^ fn 8ڻ8q,r I@\dd0z^% ZxD{$'ϜݒrK5_j׺^:Oq)U@NDP4PQRjZ_ZC*FE5vw7t“??Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(,oտY+hA(Ѓ|7U(o?Y+hA(Ѓ|7U(o?Y+hA)VeP͖8FUb(_'QJi\h> ׭GQ,V]# -Ñq+ĞxDŽeX}Y!b1Т^ >7nmn/둉"bTV`pC \~SzzčZީsIn0`#p(Z-?Oy]⯄_ umPr);w6R̦% 6X5M+NƅNy4i~"}GV. |UbbJ/g<7uc5:+{bh-H7(B9Tr?v j0:/lV!K0u$h " ownߍ㪿Vh/YiCO $țO%a";tRxPH=+ 7e5k=J=u2' ;H mtB9, mcg5=emwm ņ\ZLƱ n" W##h9xc΍kΙu#."(@BT F~^>s߇ZNJGNץ_%."@?6XUFWmp95|gG ]sSnԭksnd0r*[.Y=4x^o-5(^iNNXlw|99jߋgXQԭ/Ѯ [-N4Q'7TBS(ÏZMx &z5[Mq6,WIwv_hp淼?&ZxrYx158%e@Y7}/] :,Zڄ[[8G$n`;~EZ~C_z5ɧĭu,cc4%$iiM |Ί(,(((((((((((((((_7t[L IyG=@X5l#L䅊$qO@KBrtӵ?/Ic5*\kۯb%:cw_՟@KBrO >Wf])?'uM_?(Ŀ/'Qh̻&+SN?)?'uMYĿ/'Q_OiwLVX4SN_O?пE2?A5=G_ҭԮ/ v}A5~ xBxRd?hb=r~ײ <-Rȣ' =8ox_Z,X.^19yu<יR^{?tb?5\sۘg%wuk#vzY`i |ln]:_&]?᧋ЍWa/_be._F.yT?rܘݫ]S]B徲İ9OO_P [W |MLbGqj+_܏jk:z.<CO³.<64 L-+Lϫ IW.qݏjk:z/5q/qT, ̸y\, i0)_1׈^J:54 Z[ 'B|6h_X?½,|mZ3L S5 ^B|8`cW?§O~+gW)?¾sU3?x]=u{ig񎺱j׀SSo)g{b} oz߁/TɎf$;uLk:&5j ^K 6|M:_& QOG&ԗ9rg#o5ENk9xdMsW>"8;| FiS5jyoPOKL~:|Af{з|Ynk6_?+W§kЩO+)-~3N'LsZ6|h|Mnk:<Cs5msǫ^ ]2o&)(Q |`ψmH<^:xPƒn?7R??u׍PO.WPJ[#fMG,˨VL[7P?Aɿjc4ΪJV43j_נ~~[Cþ;y@|*.λl~#6tl*O1Ҹmk$^Ha{{HE!cc \x_ʰE6kdF ~м[W'ſM6c à{cPPbA=Y45qv#;vg7_mwǶٞ(I[6+Gio4}CKt-KOA$7VC,R  G>{]jGq<E4iw.Hz׆xI[ߍ fݭ$- %Ƌ$w$ CִAWZ<9N&bHQ;UF1OOK~~пMfK/t]v. Q-B*wFY<z/x_ #=EzvMhzΙ5CH)R@VwuٷUnu$T5Il% Tp8J\vzH6öZΖ6L."Br'h8??_ ?&cx~./oECnٴ%i \n1g|W 7o?x]}OWo,Fm+4o6UH !$W/пM>7RhtҷW6'R+>qI]'[T>"tn5[[ 4Kok6>f=Who~ .uJ1%6U,<˕4]۩JiE3A9BQ4?ϧ _G??_ ?&`fcXΏO ?_ >ß/G K(cXΏO?inIa:>?I,пM`hZ^\i^yqkwyumv@! Kɂ` eOi' \DѴZj(혴Y;p $W5li`#%11Sºv&=$TR2}+My}[.fJP[.vc2W+:пMs/kS/jx|X"s/hß/Xڞ/)HS/??_ ?&A9BQ5a?<_>R,?@s/hß/Xڞ/)HS/??_ ?&A9BQ5a?<_>R,?@s/hß/Xڞ/)HS/??_ ?&c:Omi!j}jx|X"iZ%m͖طY0ecPaEP\}]#JHӵTZim#'x 5W|3χ.{mgInڕ^l(AbU Sеi`}SV$6H,Ifg8*Ox^?ӓOGX#VW%q,FyO 4txS"xc7I}t5O6ikvk['[c[dd gÍSm~f(XG^] 9o7G4mEU>}+˿>m~f(aCm}b\ޅx{TC-ޙr=d;X 烒yiφ6ߙI?i|YnBp?@'ZXFƳ_Sm7ZF{̋`OxwPa:ۍM>'P{唞I9sRx+6[XZ_6bv8C !ۍ,zO^ ӼA 1X~ɪ€M`XbAc9nXxTѭ.g !PG21Ir V%zW/H j+'+o+o j+'+o+o j+'+o+o j+'+o+o j+'+o+o j+'+oM6xZYpA' :( _>5¾Ս;iV}bVXX.ʜWW-Pmi5-.5nlYaUV 2P]Ov.ҬtߴO*35DBXY@Ʊi_ּ1}eݜV^3MTxB'8F`]va/ zKd5{q(.eTsO+c8p;Յn- }ף]Epwvs0]Hga[_ķ6ZTs^\,Hm,%H2,2`2jNᆿbOyaV8r>Eܝ3^m\_i2$79F9VZ[~"fH{yH;"+12֏/g6/?uG/Ǿ:ﵝnÖ~$tUo718#){g ޏ}cӦ/na"28avv|mN]y4C{՞-C2c5O$]חMxQfSMp2N%X/^9/Ë_ھ-? hzM̭͢u,2Y5|*܌ay5X~${Ӽ »Wj+_?gjȾx2ڿ%Q |Pqq-+=imm%(5U$s_趑_}m2\p*7aZEWhz<_,.e-ӡ1&Q'*χ.m|FsEr Z8^S',b$;x$o :?D?iu(G .tTP;Q]?!K9]C_r@tWEEs4Q@ :?D?iu(G .tTP;Q]ʚUwKܞu}vVQEW/g+džQ\ =OIG񮯤v[X ¢ Wˏ>J_Z+uh565{)fU\,[Ⱦ7dWi!-5_zV @q8q[Z4M^E~}ŪH$. ,sBvi)M][w>i|-{OΗk&cy Уc$ĩ?5cx.?<9c'FLSU1]KkΔ³T =3g%fki4:KnHd8ڤ`W<LrI^,7/y'p*b*.F0 ߂Wqm%ſ>G_χqٯHo&f rF*F#"xx(+咗.AEr2ŏ-ڏy1:w<'[M ZKi`^0j$P}I6^ֵ[\Za,U- rgy;&_yχ~ŏÊfP/Uִ Ǚq EϦMUѼ_FLլQ>%lo gQſ?K+8]-_FP/vxI<[C$»(%oJ_F-rmw~fmuPEPU 4 ˙V9誣$BW:N!\MIbE C2es7fjFX-bfHͮ1l.eF7OKUńi#F7um`Iqps %`q?xۑ@_jV :{> eqŎ,"+t+7o/uoڇ7~nbUyI)F VH\_4/2OGzKk KWݎa><:a0\\ɬY[[=;C*$['uamxMҴ/Ci-$P6IʡB=pAL߳ι7IéXTB[Y`2bd k|ě txTSC]~t,Z1fZP E£l c# mF;-Gyj[dYYT$&`0:k]xN_ [$a6MpLe9nedfy[(5{=L,̌hX ~lva/&UQxSzܚ:P^Y/<頒hyw #C+}1ڟ_:?uv]u2lTJNIsH {۪h5ڗ~zm+k|Tb剹w+߁Z% O7t0=vm"PwBdJ_-Wqj>KuKgq?jW1ѹII#aTzy85KFVW=3I$v,pI$}3׷S:ߺ_ן ??IjG5OZ"֟2,??ᗾЙc}IWewf^|4%-ן ??IjZ ?L^YBe'Eן ??IjG5OZ"֟2,??ᗾЙc}IQ|ilgx&g5OZ" y)UӧQW೶T_M6Z4IyāIE[@39'j| ߈v1Xi㶌H$J* ɭO gB{ &?[T_.w67Y ZyETw{e`ʅ?PqUmVm<"o{3Egn+vy^ Eh@۶g(ݶW?Z4Pw?mVm<"o{3Egn+vy^ Eh@۶g(ݶW?Z4Pw?mVm<"o{3Egn+vy^ EhMGQ]>8 3J"oN(o{3n+ڇ oj,jo{3n+ڇ oj,jo{3n+sxk(^Clm"R(Vl֝i<#(/#d؞N}}Vݝ,0˧GХUѭ?zVt[].Xoj,jбO?@mQm<"oj,jбO?@mQm<"a}Z\Y\m!1"2v\z^ko̐Zzc)DdL8=Gn+vy^ EV?X{PcU~,n+vy^ EV?X{PcU~,n+ìA<t{IT~$ڇ gO {+:g!K.pH(̧(F(((((((((((@uor<\>I q㿈/<55֪I!!"p0sϦ8<=@hrA׼FED+ d1HЈм.0fb?◎;Cl㳃M+Z%7"O=kYb&.n=hiI⋋ahQ<,7HcЯR_SmݪG<`qI&&Z^ <}s͉MFeŝ[IJyLzבkDּ1&Y wZ\p2H QOz߉)}=>355⦭ >Nm'W$BD(:5'h2'Wzuޗ IxlؙB ]s6FͮD薚|mq%$K3V%H 5x$e Yjf;y%! ܉ip  -~k|}*Gu8 k(5,,-&Z5`#>[ 䌌>5x3A#/!q`%TECr+ȼ3s~UkkZIo0D{Ic界U$c?$ :yfZVem`h^X˘XnV p-oowkq]],}; *F}{{d0β[DI*=$7qI$I,ԧ5?ѭMDg\wW8-3 OAV!X9@2p_u~)_ˮ'[%̇ ƒ1"O>+KMZJ {?K-n-#֍JmoN9oOkܷlFm|Oeسp]7hQU؅wL9 e1sto^ ii\jWBgQ爸0O1|d“sN>7#w6ofk{i/ZGW'$HԃА;O>ؼck7o {D:i:oj^l|/6ss,9! #[v GF=?uCšmgSkVMn #NH8נ|_1q|ysW|+klKjJ n%h⌈s7*Oo3gL*VmN((((((((((((+Z=ޫoj&Ʒ ʍe 1kr?Z' fdx|O+8ϖ2-3='ա&LSa dY]6Sӂ4KwXB285Ep2-3?Z' f@GDbϖwP+UU:qK 1L;+9bȿ1L-ΐE8S"^N2*; h#_o1GGDb(h1Q/?c7?Z' f}UVq#WH,K2WtPEPfotoxx-18.01.1/images/stuck-pixels.jpg0000644000175000017500000003536013222767271016273 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 161 274 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DPoHEo|+|jZ/J*lt~ɯ4kkBLd{gD%h+NJ]uG𥽽B.ltFfnGLU8$~63~}G+|#w+;kZ\]DOp .A{V/Z.uK ޟoONLl Ua M[oIw@ D~QI?'+n| au#E46X,]0Y]*e?ZZn}p-X-ewY~P#ԟl>| l7Z |?ɫŁƄFNF$y~N=+qKH!/ݭ3JcJ8vy[v?83x@F-zoŏ^ {r4& CA@Vlx fc 'h~>xPuKK5464ȅMKǨI= ^+^*\l}'?DV?ڽ+ȐHiળ$}&t?siw!!%71EXM|<3DקGXtdmV1or_մ?R򤷼$ Wk$NW0a58Ɋ RzĊ[ۅ,e(aO9 -|7 sKK$kɅdGR:t\UNIjZީhu,!r !4Q ( پc$qFim[T WM/\mVTЯ4F@zRvvm  -yD(P;< Ÿ5_Ie[ʅb/`<}k#+_GU#ܬOi>sC*q9krGO~ø z?`_Z=.~{_??0xwß}G; +_GU#~oWw>Վ=B N$@zzv1TV> f`JoW:Ѽ(cNp}I?`_Z=??0x0k J\Ȑ̡LzfWOEW ?`_Z=??0x0GOu"8Ytk4,toqhfGӊ?U#sC*˯q5Ujz>8 ;a;_{7?=a9vbYOE~øwVO#r>/^>4[>?ּ/[.sIwZ!eB'n9i~'o[9^h}Ձzү$Ѽ5sycѨ?iimt'S~#q!6SIfic 8&9'-Skwm W>__M/ıɥ_~2Y8aFތ;үi^ >$閍5uzu` uv>7j7^ƶ)UAIcrJz}ӿoߩvq{x:cC4 .k^AwR)يdl`Dduz+ 2xO--k[,8*(}f}E|Z[|{~ Ë4~\\@b*ZA+9g* iqF\ڏ~7-YZ=WOg-i BfxJM7FU/@f t"p7v[?Ox3~"__^8y녙QJk%)k6s>ݭVH2BB6 ƚ7v)m1FF cc՗;qiɤHXг]|@SO5uw k_t/{#83!3_PKw,R.V/0ryF}+vTx'A$SE#!#y7Kǖkiz7F{kY>]Ǒp8#5?.)V=OF5Y|D+/9/+Zo\Rv?.x%|b<&|W4G/99\W]_(W jMЗVU&s4I#6ч!0#dPQE!q>Mh~(&<jwƨ5D[Co' `Ry]lqx[EgHYRVKdEK6zIɨ\+NEe(}e;.P׾(3ºxBݿeo(`qڻ#v rk0"AM$&T EciOmiig&]Q0RGDŽEP$Xz#G-ޓg=ķ?dVIO.zJ| ^IxQIc\\Pql6X z4~w ߱Sh: wv.;[H!Hk~KqjUm& ޠ084ۏ kz',&В&B#|ʹ8 d( =uƍYc-O< LnǶkV)QE(ak_Euh@' W cn›:yC|6[EFYZN)JCe&㟛hp)&Vw;>xc,o>e~> V%W[pq$m *%$ʍFxuž0յcðk"tLc<֒e ,{o Oa[0,&>b b&|k[XvV(mxIY@$ z>lZuM|Dn 1=NYfgi~iKkPƣ UU |o2<+XYۇ"6.%qǿZލUIeVס,/ڋ9?Qe?a~_)/XTWW!>cY~|{ NOYƏX_ r5}r?e?e,/ڋ9?Qe?a~_)/XTQϘ?UGǿj/E,hQЧ' ,c_aQG!>c}V__S_EB(}E\aY~|{ NOYƽsռekwxDq@ZCnex0#~KqӁ^X":ZkQ$sy1?s#R t曨^IyMu5`qpD g U+3ը4ϊ~&<6.м>˨]OZwG#Y# ΣIhK· [:Eޮz|, ^wvdK_'_SZ+uB?fZb2jqeݥ7J!ݿzGOx*s·K~ϩ4Z=haV m"fWW7gokExǭ×*([;};}.cÁAp6_j[|Hzoy4MۖPw1 d|K8~$xcS}j5qe=E$2CuȐ?W/ 5Gύ?moV{0Tko!{>#宔It_q qF?x3\7 -i<ڄښiڭ՜O(cHdR _?W/ 5Gύ?dpΞ%6_jx$kƭ.o[H59"ۙso*pn}v~O? Y3@%^Kw9'VgnXQeƨaTVoύ?RJTycҢ|_|l1~>6_j4?W/ 5Gύ? *+7QeƨJTycҢ|_|l1~>6_j4?W/ 5Gύ? *+7QeƨJէxsqK>WQ@QEQEQE\ޙuemi֖pɧI+"rp+7ׇx/v4KZiCRd8ū,/U  g?Q/$OkKD^M{q$߃^w[jKI &IcxSNNzO ӳ]C/ׅ59Υë,]v 'wg]l{'S%X 2=+_~!ƳGjZU w&a,vky.ah]2j ng13F$5-l{g?\ ȼo7G\w WstI/G|[YVuƻ@l%Y ĬddK4}?Q/$׋x_⾣x/RI>!D!ֿK> {i4ѩnӃn`l|EZ 4-,95+YoR<3:.$WMv+OkKDRϨAͥnBpCHgϗ?MkK間}_+YAxA;d zk,K}n]U H!ʑ0&oˍ3O{kx$٧ $i@U1)sqG i(j{-wϰ[La#2qOZ񟆿ޗ VIk6^ia[J]4iX6qdO? (i43koZEmvGsJ莡3 3OkKD^Q_//6wxt66#`A}\9q=/!֗&xZ_ %;PSjZ%G} #uQn)21[ZևckhGNMUM)iO;3G,hW_dK4aVsismc OW-GC~ߨm)@2,F,t |m/K)4Y4dl0a (2zBWhzΙ|i{KReV( ~UO x[FPC5h((lqw~em՝A$W\ ipA"qt߆m牵?j:|2A:gT@.Fͳ!x@5 mhlg;o3Nb8 ye|@eݮ#GmHUw5i~Lo$SlǶ("[=ëV+cC>Um7x[u?텖'%N{-\ #[9ii46G&dJs+[4홵򼝧s-ۓĚ/e nqI!my&QPhf+@T'ds5R/Y5h!qnGq”$pGʲ:=K[ mi qI5-WPaJ-N+;m DtqT#.[i۹p/ `b@\rO$ ϭvfEݛoSxɄDQ۩텑y/L{N?^_c.H k77 q>i|Ɖ _,p@9#kL`?g-Ui5{+ύ#TSqS E߶)WV*m ZlJ̔aQ g@A!PfC[ BR vQOȉH{omw0 #q<7Ea^J樺"-++kjŊ)ui͠NB# BFV 9KajSf|7=3cЭO?XȤ+ݧ$ vk\-9싘8yd\mq.4[%U!EQ HU\X+MEb,%f,wM`N*ئ=\*ݽk砠jh6Lb#DZ6|DV\֠bZ8.W}ski;cd(=qd( `ki Ec\_NوWԄY<ų-+^8!Fm,'K^(ԔYkMRN@ȧDFUz}b[/; GL-Lկ1d>UkX)7# "ۿlk]+/7СHCE?Kvk /)Z]=YLTTc,yn3]IF/=ef܍LyJIac.=CxAcB<[ vvv̡)mʌ8ȑ[RIp jHM BϯN DzRlnv8vيU+Ǩ8T8JCtN"I5kmٜQ =5؊"x> g ()$*k%^VGB6.(:5ID8(>sۮ1Q[A)Y@DE#"z)`MBwljF D!sp B\/m:G`'8YWcgHyyK + b5R&_Oˁ8Auk>v ?졖ۆjrp)bRrЪe(@E`cF7*(ZT$afcu.̚څH7y>Bx陷p9xm)h,Nh9ϦPjI_Ҧ ct?? 8MMyc+醍;AR,;m:k-B.} Dg.Cpq_İQѪqa=aw5p$B|w۩fiG-R,Lqï jBڴŦثupG,.gQcwmfvLWo=DzL8Wh%q{vぎRti yEm0lddfc>u6'1dHwAGV}xLKׁhQcc⏌bIMo [;w,\:f-ݕG_="(r p-.afaՄH 6 ;`R 8'&-aT8v.-#}ҦqX|WWdddV1 #XYiJo!#1v^C5 1L4btw+܌> m3K p-9P'#j&%gT!hʇ (Mnpy˦ax`!-JÚ`EIX,5j oQSZ0B{Chhbٯ?8F!t+E4"C5j$)p(-zO^↡Cc?caSbXDXgc 7Qx*bN\9w;]t'8ޞ&̥_6}j>4<ћ4a P?_ i:(&W|l\fꂨ+9|<}78?՝bD8 ex.yYm5GYj?,TA)5`9[@׊r[nRu[蚄԰Xy䡰 2B1Sy:8=jl (@qvYgLE̬, ⩔gE>k蚃]s14ꦥYEJZDJVV8Efp0-KHVq,BS{=:w#=czQH(aw&>+ۈh u(8 ;ka;0+d`w%z30}xCk@͹H<!LooZ&+r͔4W4PLH(4ƃ\-[?)Ƞe7SQSbAdV \Lɠi 3ѹ+E͑Myq ckĠx{e ̷A}Xg,ߪD?MF jX:yzAR*tlmgp65>> \y}5,-?dBl"CWKՆ<{ ;3ChjDiyxx}XJ?0VvvvtSqrZ:zO4,_ز;Oz7!W %2zehZ4×$gؘ.9DyTn_fxj$6Tjצ+M{[/6ypӇ!12Oϛ &^OTVr Sk `0+ WJ˚lBh Z !԰%r͖CjUn [=cpSN HM[Gp6a1_LBɜD ?{_dCOĿ/g߾ѱ-ၗ(q/_HT{4OP4~™S{Ќj͞j!lqkY0ԜEcۣ;-lqd2"`^w(eӻ@fv.ܨ ςa% w@NO!, HLzI1s(t%\=_LwVs`SIOPiRH gdX9Bsd8Vo"KҥK8tL&\QguhDh(B[x3-#YW::%sDt؎jZEѼuW i>_1%tͷE;xοW?EkF8"=Σ_ĤgV׆#jƍr ҺͩGm|6χѴ17i}GkfIJ> c>KMI1M(՘k(!CV Hlيh&)֫-i beli"erMKj'Þfm B vA|<ދ];‘ɞC? t L{g|d KC.Ok (%&i[KFZP~YEZ6RYgE d1~I4q4&m C]ԁ0KS!u TH!QDR J6N\K V4Xii*(>@XFM]7ai{V4:h R`S MeI+ipVJvUҮ *Dz:[ƒ PAX70¢\Yd͂.֒ K qn*b2JwȚy{8P>=]Z{&!+ykF} p=&:i_=KKOݻz(Rvnۻ;n]CrFܤxMo|vU/wU08G#PFX4Y8ҙZ n`Nd4/JIȬF\Ɩ4 j"?ôN̅|V9yӺslK spudOMvcM, C>!PAX/2t }5-C9w}]߲8a8˘u%%ci5ִtݭn46sSô&.#\LXKhp9Ɔ@Y ƢJ#G#И< '6fl#P\Ԃs8f',1#G#`6p2SqA9NX p8f'Fd4^oI=,㮜N50\EV_9sw$|S!7l_I׬rĦK9Z& Qq,[]q4aG z|t8´q-< ܐ#PJ^8 ReNУ'BMjmؘl̲6wƦ=qT1&cn/[( 3+ ߉h\q~ '(OmIx}UhgK:*mf:Gػ$IaŇcpFq mBļ0-xx{m9Xl̲w&]b[g@Klز g7χ*;S5~=SΤݕi`3gb'ֶ xMBdhk$|i04E^@LWN8u-b7kfѮ45욯߆+ON$cy( 'iOÈ^B%׽m3t< լby&ǎ_bh;k͞OEMӌJMHh?)iHwxRamߥ+4GG 5)eHR7Tuz;COvcOohG56Evl<κ200PmǞx0|`_!EkG2FcggMǔѢU[{zW8_4.d3Ĥ*8Y&OX:MX(-Aܶ8{1KFA JOO?B&L_Wln͆\Cp;)Y^Eu)viδ3.88*7uѪ|ϞzbzeP+OoF}Mn&QA)'=gBB^.n_8TR)R$,_BGg}>Lo?&>Y -E|HOoi$m&T9olžWfu]?]c{{{jQrH@ER;EqnPSͯe>O?a9aYaNMlRKЩ+[Qڨ.ۯ6dڿ//^khk/mǾ#1l, ]ةK_2B+UкUh%T;WfkJdʹ nXf9F EH+/H[vrSaO)9ËoVNh&> >x=?\X ڌT%myoꦻF~ nH#-lPr|ceaΰ& +%`Z]cpJ+', ,{ <}igdp3*&>GCBQRRV/#l+yhK+%ukqy|&<~@׎ΐQs1xSXM;U5W)&8 %!2e:Z{K$-]3e]261KMjڿp<6EYر0*?,-~t/*yQm3Z -u4.;9yYip*# t}=f`F^6~ݼvHGR0upaۏDS)3gC2 n: dZ9kix}>;a{j7{aEM7D&m_<ۺc55>\\ݞY?6|."X;D p9=9^=˦tì>.1־2ɩR)9m'qx|3X|~{^!}]csp~B||<[xȢݭs$0不pG3dfBl Z!}#ƍ;> yx+S|BN5B-FYbwMP3u֬]!̞_t,[: gV%5=>j,HOC<̣S413Cm4,^brI <¬ZRlv"+8<, 4(W!`KS-2sl5(L< 9?c-6cdM7'KՔkgEmłg^FD(RA6})'z΍'5,^8AIaBJR~m{hqy@H#'2Xyyea¼oi `mV?;k":nSo<֢sܵ|n~ŹG 0-o~ʛE`gFXۓ˘Uyhl_Ő㋍ !6o/끳76?/ tÑ-<x7/uq?*2=3}H`u lW '2g| z3/yZCc :ش~Au<=oR:++<9vv-:8SKӴ Ky~O›+WcOYaiSFWxC(% z^~UUg&H&`/+y7#M6i1#XtuD3ꕡj}jQOaفTaNjr& ]wi"Ħ#vi+7AD+w6 5#j=goO 1rAQkF ;{&{PWmn~a0ΓdL,o/6^ȣƱ0]"Tg&ݤ K2%;y½]<?<~tV6J jgRd (}a"ctyU逞?!Z_}p*Jh=j!>+pgX$~|!Fz9O٭ZS]yX`M瑁Sy8u&r۞!jA+ܺWye`UW5z*O_w\G=ydL>jmyۂN[5{Ky߿QgbP25N/E6i²nAn"|-MA:TɆnVл_{oԁtRZV5yL05G%9BM΀U1tr({ j[VOȣ,%S=Cm:ץiB+#rTjף $1sP]CGς ;CKz\NۡmA>ӷk-v NyxvڏݠJ߯z[ۥMvڱӶ9\[Vi=^nڱc6o=[ n]++-{έ4Rn-,׮X1-ݴNЦeW.xϟk;:9:C⮍5D{Yr?]:OMZJ|sږAZ++Cf~ڗ6h v/գ'%mpQt._n[Up %KۣCk>0FK6R;mZm*WXW'mZ0`1c:;h»jc*BkJy O(?ց*e2о Z,$gSM%t/TA5!S YfývɣIw7$Qn uhz%fx f|UG!Ф;\G0$dsN/_(C6< G#P;> / LE2hpɒ%h߶- ^!]7G# h?;wxW`Ig dU,~#DE5Qb@BG#p`ܓj4[e4&?pNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpNX l=G#`:LNJpqϳp88 ǎ8kXhcGHKK5o^ê"#/Z-q5] nܸggg\zbqtT{X J r%o&r9rwwGxx8ۇ>[Vкu+4k bMqª~# "Dgacc#͛ GRR4j5VZ%#*GGGI ?OϟVH G@]3Sll,^y5 _ ܼ;"''&ľY;<=[9a#PCpª~L(--:KKe-CII) 9T)QBXJtm˖߄a$Ⱦ}q9Q pe$NN8q4cqzO>nt' 'I6 `&719a5;:2`² Pf B8f5F#aqY`5F,I_/_D[ )`:X}z)sj0aA?# vR-lm>#FJ%B H*Ÿ?!H)e`ODNl}gDeoo/Y#=KKV#[Bjf|" .{'ŽkFJC$ytaY20BR0aM. dȀc4Ka9:$pqXp:T& %2a‚'5a0udHhI 0Z G%Dlɇ5-u*_Ȅ]2vfdAXԹsf̘)[XX@)ZTjC(Lc5[%5Mҩ,1` ujvD"Hlvj)b" GY0`DOp1,O&}izPs$::;w*-똵#a}B'q8v$j9O#heik 6!5d4^f-uh.L<"""묖Ě*/^wt &KIb#Fn±93{]ԉF72¤ο9INN&ꥤb%B n۶ QЇȆ@5ֿӳgOsMD<3)9YX4AA˖7RF0l^@@3a}3s>K$Z:Y4<fMXl+!@4 h^07R5 t a~9A Ft#\ o .G#Pؐ=[/Ȉ.y&Ħ~"RgXRsZCuZ0Śh Dmaly P+a>}?plܸ=&MT% 6۶mñcDŽ5Z:a1'f[f6nێ 6 22e~#hx]tQ2uj cDDlϔlbb)%w?s@$FגoM ̠22a{Xg6!PZ2*6φٖ=/+B;bY?SHH*Ր>x))|ZE߽TwOdku'T&ad5j`?dC$Gڻ]Q *Z)=CMߝ,ϛVAAƌG@L?*jO>}mV8;W,Kh΃|$!jˏӶ$*䄒'^>dϏk*a;.^^ձoqX`p+c6ފp "-HբH{狱bv_/Ny'/`̸ ÈoX*oEm$GawmϽb 2 {W={Fz(o.z??̘9(Y1dt$ⰸꂭYd|FIDAT_̓\"}萞՗+q(thE#6 JJ<~69Alc '»vαk,i {%t}l!/{§U}qwuƋ?^VOޚe5+oj1*VŌ )up&''GRWA"Kˑsg5~merԗr%hxHK Ok_NCgx X-tMG>oCXZ׿^Ipth Î9vIm X_Xt G{ׯ~ň~=SDh۞KwV1NNT];"(( CDH\ML2٢YJĶp'B;a!hTr >9yib>&7'J}id ɓOB.u ,݅aG77=:wn~m'(UUP jAJhXSKxBBBBD0p6> &MiU N e,`Avgȋ&5m6p&/) 옝cXv+/;qnyH&+rJC:@~eQOJ"̍?!Q,W(5 1-1& J ٱY`< 6XE:$ţA ˚Hȝ5غæ9XQ#hЛı:7n a192#5J"PyX"7{@@.]^z#(:N8lQIpSymWNń p~|c篻0C2#&e'ye H /h=GG1q8F -JX=욄_ ֪q=(3݇ SF7-k+"'c؈XI+߶q#`6`,GGV%DM:olʊ.o]2q R,Of‘_`( Vb|T$^CE XBJ/g-))f!_YY20hZߦ#p5,VyOv>cXF*F?wiji;`Gg$\޶'sKiּ-|\ 5MM-z39\v\2Ӱ/[ Cߋ\l2l W`Kohl SʉKL{(NA,#h>V‡s{qK<>v4 ~ nly9dTpp]w:Q kZxyѥiwXY-I!$H'@B񯯁DdS؇u߻XDJRrJƵLuSj@^ >c`ذa$Q6:gY`o7o IǏH-)7n\ęAj${^wW࡮]{ cٔn޽nP{KN8{ޝjyDvm{-_Ag7$Pw9:/ª/Bv 6͂/eڷ-A`p4Eva}9m\/8Iⰸƃ_]Ƹ @5;Apw4dܻhף˒QGc5H<222ZξA>jMN S='OmsC/ofV>=B*ߑ~GxNk<{"[N*7{gbދO`嗕m>cz~dh(˅M[(**'~4! vvvK M 6sKVC(r 'aQ8(DC"} tn9nJv4'B;&,&4mPSM5 Y?;oe%qhC(9ˮX]Z }4募iʡٱ)NtºGT {Mp8NXr!G#ppº]}=G=g.n9~G#p0:qt\!G#GW_)i U '8Ɗc\j4V\"PTTѤQ2|s8MT,X,\E@SAVS$׃#`p²#s9MNXMŒ\ U4Nkh*q=Fc)kýNX5]IЩ)Rev6u'CذaOl'xB8oo4L0c ڵk]@aw瞝+)`А:5%]j]}ʗԆ6.L2boXxIVkL:5%]j+w+aZ-;+`;… ]#ضFwn`XQB; MVcb5v.?W]}]=~lGdMU դPQ L8Y.ƬFSSҥ&#kuԕVm554Zw_c䳢6Y[`qNMI 3p mНJS]*`}2Lh u/ņV)SPmR.4:+6%]åy:ƨLѕٓVu`7g ـ`_Oo'O}>XkTa7ZRgGW4%IR3yk#,tzϘa4( kn;ԥL cE.0g$L4!i5LyWR; qۙ++IPZ؃FߨUdžp"~v x9h3 =˦=Tx.0^yqVon#ȢN:&gm҇tn^d:q=0N3>t)O5B 1{b$ZyKK/tJˉX^(uѕIkXw^M"JxaQBVH)U:q+ǝ ;+|}(_J"1wcx 눨煹S),ϷX^m]sq7_L"]NrJO"uÖ]boDmgAz qr`;@v{{xu(5_jnW}lUwɫѥ?일~cPT}c1lS{E oQm?}]ɏѩO#Qڟ|Te+{ jucǕST^5Yuv+ @YH)(F~U[Uh&lܫ{H :_O^wZTvl~ٹ]9an)X>lNx{78qW za}@c&E3G{؆'C 8`'aC%Z{=Υ1Vjala@cj(oSetKbc[AqrxhP:!Cxyd =~kײA?Uyt9x4$ %e^}=؋԰u ǺoPu;"L|z,8;-PriO cx mJrۡ >Wue6 =+|ZYfՋ?s}lauz F~B=*lݏbRK_?dRr_cxU%EDHla%vy=~!Y'X:_DBFjtb,?g"=Ocg®tԛš*&&4#X j%D7_+XdPeoޘ_*cM;եo.$Y%к .ۏ-ǷӋa#Lv56* ݝMAJ z҅]Z6ޛlgzg{s=3dia IڵVn|ww<^ߵߤ ?Āp'AnbI@.Ih~ ج8_teAY-v_8juz,yg^_Wۑ5c [1iP6<%&lY_ela&qfຓfMkϞAJ&ލ;L1S֫0&$#S<–ʝ'+)NPfUNl4  ,7Y@0SfĘ&aw=LYKO7) ̭d&+"DQkp'PV1"t 1Y4@ڃ.hH'id+WWd1Ci1>-Vϯ)uT0)oL}$\b":ZuRd]!̈́]m>۷]SUS31RTWUfs>@װ`Njb8XVn aܜe:7ǎg#8oF8cҤh!Ͽi`D>Ěue'`EX!'gzaӬؓ9E{[ȗ&T`-*TtgV`\Xɘ-qD_kF7 d~949ANJ__J#Y76:f01>7;]lQ(J,{нG\vU3+<& |Vr.ԓ|729B(5"ag:A穇>-|t),~Y g KHz'\$9e9)HVT}gȔ~j)V>_ZX6'4Y]x.8hhxک#y:{mM*]H.GPC'h|(*ĤX,A ZIPcR%*9 /X?iΞ_}* mAh V(1@8D[dēo– K(OZRPXaE6L K8~%n 8W,F-bDv],q-vG.+Pŕ8@"ť`dvPj=p㡕X/e KmD˺zTXtRaw profile type APP1xeQ[ =`qhBJX;~£s竅BL!q ٶiİs 278 300 j]LIENDB`fotoxx-18.01.1/images/up.png0000644000175000017500000000140013222767271014254 0ustar micomicoPNG  IHDRw=bKGD pHYsHHFk>{IDATHOOAƟwvۦ? 괊SIO 5w+%;x51^ 1Fћb"BbAm).mwgC:,7y2w~;dƣgfF9F叵pp:{{(MJOŀÀ<3r]!՗ TT}8׍1bjlxP_[R݁l,].>׍ D"X/[m6 ՗/](bC= ڟj[Ƅ 627ʞqݸ׶׍8S};vub;JyBU\7Ə4$݉dZhBT ^pȴ4A zE2AiH`ôYCŬzE ھ-XZ!\7[ FŬ4k 4;ՒS!,v\ۖ- dgҧe.JfI;hQ[@eqv>UI Ԭ:`Y5!j{"oGzTXtRaw profile type APP1xuR]n ~=6*M?&|پ^~o? R & tQ[19,vhݑw؛w0pΙ5b~cUIΎI癅o\9cbl_&3K!AVCd2vBg"@2zm 514 815 0 is<IENDB`fotoxx-18.01.1/images/dots.jpg0000644000175000017500000002315313222767271014606 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 111 170 0 C     C   d" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}Px^={ĺ6<gi&WpFOo ž ~_GӮ Mژ oEsrn_/8/DQ _|:kEu|ar6a'hEf'qEi|EU 6Nt]7^n VhU2:䌜1Sd;mhDQ  ]֠QHEWRJ..oKBXJm8 7 /t]JMTKȥtIc<;O[u@3MBaO{mǁ/^=Z^[xws6^dHsZ3EAnN<^-4}sǺ 6IQ KdsN1bA9Z.]2?DQ x"?|E֬KM7N񍧇l8[Eoǖ2Y WᏇ|%rB#n5I!teʫ±'N*][^o?~}G+)h̻<]0c%ym$KdQp{Xnn.8[O@>Rw-c.3Ÿ@Xdk+ bxso`O >W6Y[BDtۆ䂧=A_SMG(<Hs Sަ@'.m(=ة;GA;o0LHl8 }~ hg.qkl,`1y2`_Z=??0x\n5;;cOC3KķW ak9!FڒHI" +şk&mV-236oz{U#sC*q>Ծ!X֏VY]!K =l*cq`V7%]OMhm"ϖ ;=O}sC*q9kY0~/xxzXM8a.! 0hylKίHpM1!#۱*j}sC*q9k(]Mw>{}Q%"o Xfk'e( 99|Ysq<dZԤۀc˜26> ۿ??0xwßn- i%p2p2OMM_/d.|]? Y\ۅ4R.-D|~w3>\VwvK^56ΫtoC&4nwgW;AV* 71ew39iGPzyʂM3tQԚ ׇ ƚ氺,M 6A-)1 X8d)x`ŽmIʚ0$3_V$9_0xoGG@ckJxmd7 )[Y$R6er -8ir[7~#[ٟr͗# -o=w4Q`8oVW9k'A ms ܗAwqeEvTrUs:^^gOW=0/E((+xW5 XlNe) .цm<^@ڥ' 7]#_G$Gl!?Ɓ<'-{^|ay [\ <9{ }o]'^V}s[\!V2e@FTgj+%?+e 4KVhN?Z W7 .A[/O]#_E?mx-pUxoP-bPxã!T?%?+e 4EfKVhH'JH' .A[/O **e3-,,!\V+yPƻ۠|[goT0$hAvόK્~M6[e4&{f ehnJG m?%T[g>z o?fXNφtvYu+5KZ"dFOn-Ŀ iew}OAw6E,AL˹de)gfJi%SeȪjQ%ޙs` aFOrZc|DAki_Kv]rC?y\F:/j$:֐orl884GVUrPUz*XMg[g>z o?V[|"Qoi_:7E6i^ĥe@7:s?Kyƨ|4?>|#UZzv-n$s`0 ӊվ!oAAJӦ2$";b2a@{S-=gjiݮ>|r ][Io "bsk5ι[t:|ă8=cNy 1X\qi -_xn'P %3w>?-=gjtF-/QĢ+xuݎ$X7 W׬/\mk9)$\ȈįG@v@W=?Kyƨ|ď \>%%ԬKkt_ l.k7VZ~_^Z&I!u JxKlY-?Kyƪį z&&-ۍB=B&jGSU ( iZpMZ_SGRw0G;tR۹tSJpARzXbNo3sll/,,R̭VbGn)t7}{.`6kcO}65fY$*v x KP$W is3ټ1*HIj3k0xW`i,E7pRUI(OTߟ&Ís~6iG~koukwZ@⽫,GKjH0Q좴6^m^9VdE~Ґv5-ME\FMqnV4<i5[}¶Gnf$kʮ5=o?|]ku\14m$fRY\99?/*QЩVzxw>xľ,}#[++㲶4I涒dx2Qď9'm'CЩVz|+kM{iϦr`Ihޝl`k&Ҵ#Rm5.|9i#Z{{{^ɓKXP+,r ,]jX5tK{I4HcaFTu9j kǩlS},Vׄ5+duKk-/LEʅ O*7HIp#''fx>j:=mXB<8'N{ڼGBZ>?/*Q~ ~O 600 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((S!" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?^)2jldPz A~Oj& ӔTP4Rl͢i4ɢrjP8Sf͢eK3nlHv*3 a\[|!q{v4[.Nr;vΥ[FrI *d[DV]EPVh!1HHJb)qP1њ*(J5EJRЀR6ҁR1p*0[kOƔ"QYEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP)E9W5-Ig]Af34Rle+Jn)hM M9S ׊\"新rk>>]+{Kۈymv'k/܅]G/yŜyNa_2x>$ztwB[ PONxlD\\ڌ.jRjq`n$,NN@=[ xY6d4-#`@,JW]%w}+,2oInC-!jTZz JTtu SvP٢ObXT*1S%KB)آjՐ7ҠTX* Š(((((((((((((((((((@%=)Ws(pQQH !.i cH)vai1Hi2i2hORo&ӗKoWs̘X/Gݧ!Ъ +#\ԎI{`nԖʑʲ9%[Cp@?3%֦&K{k8!DFxOO{?"jq a94TT@>]uoV60*F&!mdmaxvA+-c9J bs8=k[Gb2D!7pXc}_=g0oCf]7BO>i0a c~kW-,=4cuxG^ѭ5&"HD24]( pOCk#Zz+5ȊyvAÎ%~^ +-rˀa쬊8kqk|M7e<1Fx>T+QJO\vz2P =ĉO1Nl (8S@RKL WC4ഋRe%pUh"ul1ETQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ն&"pfi4bXM&1M QN*51Z>-x/EKERgV>gФLUݎx0j٦YؐFG|\ ǍQ$]]>^7l~NO~RI] ]0n$’"LW5j$:)'_TfnϗqDGJ#ħf G^zxwN(m02D" r;s>֩iDyv$_(đltJ9u+]mglƾw0QJP[[;%fs͸24ϛ<þ VwWWpq5sK#HȠ0܆=GCOQ}xĚF2F t&zKy.VA2H"WI3 2*6^G VqU%-5˩f{e)\~.erٱnxV4MX`pV8$s=85\l'=O6t 6?XmB&3#w&=kw+&@SSVZ,;8RSI|P .HJ((((((((((((((((((((PJkC(ȭba$fSA&\BZqii(Ni S fB^aHV{shn}q^dXy%`,z:v|Q,&kQG'?Aڰ%; Yuyc7ڕC=DD1pG#98`DD$ G Cp@5|AZ WdhW淴ɳ)\*`HAזX̪Y'@xraLItԙE= V*\*8&b_,cn`3s^cu::GHA'ndk) wT=}^W&Qʼn+,m)R0 Sa)E%fz3NR⤀j RRkB'+3`((((((((((((((((((( HG+&_m6X4mzr&r~X%>ӫtmu uNQIJ#BGٷ2RNM!uKr2"Ȅ!W|ljHfXZD [pX䞼b_,m;wkmM-~@`b>]081VF_Xkq]({gRcyaInQ+XQُ'GiˬP=B5ڱ=BJ˛u"Vw*sr4ኈSlx'ZvEB !u"V%r>~b}^sqF_r2zzi72 S81W;yeoaw4xgcb[;a\;lI 󌠩Qݬ*HZ6>uPKo&F$#O{KGn*(Iג{_-"E>e1s.9ϯcӊ3%qëNq}.𒫰p>༾:Z3u!(Z5&#' Qn9=z|NaR\Kqr @>M%Pg"w{ .BrYxܟn2(#B2"dLÇnN08$OC[GLKxP 1K"'83U]9)c-KgxWҭR/3ҕ4Z=o>)qXS jɍ5#>zqoTТ[J8<p==Z# xR-P4(MaEISQԐu5/b-QPjQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEb4ջG"dÚ6LY'I]A&{xJHTb omZ|J_cz4RT 1;INHji>K;9wk;m|K=Iski×Qrw|7+=K}Mvȸ1g9?6N>q4 _'TV2{^/@ ^}~KKh緊`<q?6x{I]0c}6p(I$+RxhltҚў+t儖1b)!cd(9V =~5ֹ@,nȇ"+C{IsR<Ѵm?%./4Z\c,wss<{M*KtA,I8 _1[} o![h#4{#bs}ڽ3tg{^s%0FąBg!Tp=Z{,1[(mQAB9Ad4{>i\ܘ`NnE٦|hr)6cbWpw>H;yQ:`V-+O#$0m _ ²6o-*zW7*KSMÍO< ŤrN; h]1pc6#@ʩ' zz,YZfxEdr=ʼ+㗎!\ibMͳA3 , (7 .{RTa6ϙdJ2$.8?Qdi"mUA[wQM\a$X(VV?0o`9nj\Aeo6.[K`.gbbQ ~f\YıTk|8VS\9b:tżqbČ'YN(VG[Z˨GkgjfrJR?˷ֻ6{y4P˶HdE錌qN+>in-֘bb%7(INAR0@G.FϞ⠞>'8A+]O'EŚ^ZG/).,=*զ:Z'(L2ʞpdS=+֮.HCßz[ǧzM3AV8يBzfqyM~WH!Y"aC]g,KIIIe |A5q]B b]T͆0S0`~"xPhţnZl\;mIG#5sji.٩xoIMa5[_"%Qr%o&݋sϵ2o0,EO೭1#á^K/DU4!zcZM6IdĢ\?w6ΤN䞇yb(M~L#I6 xow»^Lv $+lu "]N &uy$uUR'٭gK#Uy9xx2i?/9k 15{"mW 08[ ]r-:|n;7:γ}9lm6KJ " 2نϯ95- HTb');n=k|iyNl-464r(d@| }N3麭v2=7c[;=PV?D=McxZW|x9=p3ZSuIhɩ3PEPuMSUj{~J[ ɨ(((((((((((((((((((e%;='{y Kg%wTtOW[kpYXy#~~zg_65r= ~UZO/4 Z،&g x;[H>Ξ: xž*{}Z^L[Z6?@W1Fu{%7\ڭ[!|e x5>̭BX0dǼF9l _>&x1$8 sP*Jk+4y<_ٱR^I坑. j#U' '<\; ෿բ4@IS*;F)U)rs౬ ^Lͤ n24,bĂ89ӚG|%c>(4Oκ/t Dw$g)PֿrIJxn9- QZyed`;[Gq׮i$}W𵩤[8v1f3{ 8EՄ} Y-~ۅID 'yHx-m)i%O = oRd4JvM6MjHl`o$72+w7!idk}A|#lhY >U۸ w bi˯j7G`>QۚҢ;_3d*,@$jU5x+h$7P@#r:`9jj@jH >T <xjÎQEAQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEh̲0r3S~"ΡYXmpqtN2G?@ʱ=}jCzbGsmcЂN26$#A׮b]V#SsqEg ʹ3 3լn0r@G v(MΔw8muıƬJ [Qx*M7 'OtuVLs[7mkZH褬u.!=iPn+rNՂOMNB%ٙmv8K(GCE³o' w ;ڴAj^&xl`xbUFY m뚁䮠1g])&~]2R}"zo88>Ԧnx#FQ]zIpB8=ioԮ#)܀?:J[hxDlʠ%\SoMIQW|asmM".č!JBHt_7hcҵY4r r:8%޵>kxD~n?̊kڲaUddg2rҫRj*=mu/Z _VbZ Xz=jl쥂! g瑚9 d9? |[qkkmf}Lnx=M,FP7&gZv[)Y 9#/#ұucJa>`Ŕz g G- %)(QGif1fJ]Ţi`Y#fVcccߩ=8>Ũk=}ǃ^ihZE6UiN$+u=TzJKLnִcy7͍0yIW>ApG~Ҷw]CsnH%@):3RCZkOSLʈ*1^]g/njGt#?z Vܴ,e_\0l@7 ^>nihΚu.q˥Aqz֞TwN4˞q\\ح/Ubg͝㓻ڽ`1;&BΥpu3ƛBʏ:qҼ;).1 U8=[֖Z|Ϛ2ҿ9hFv(|̣Ǹ$վfrJܭEռEn$3 ;wF0N1=kS]DV(e#G#`O3:qu V٭$5 +cJ- `/"& fbp$RTiדQG^.V"|km''rr6^zeq-ŔQ/2e;p2 Ry?wVק:mn Kn6rN>ls5 JYfRJ*%nrx'xx*ק-MiɨTҭndǑE:?,膽%W> ar a+|ͩ|IQZhvʒ4pV'$a 9v5^jMX&Zxkҹ‘(UJeB.;wfcQ54(1ߝc}:W +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?Go΋ +?SZ431j(O-jhcxJ/I[zO,{X]=e!2L8yb{ 6K,y(pbNx#S|JR\I%H0"' Px>`W朒_u~`ҵ;HedZ|m&d &Đ 鳽 w^-}[[Tռ-r-板x|+:`Bn뷶qل$U; :8qK ƽ2EtFZ&q=+t[&Yr8[Xyǯ $8Sº{β\#60u?;!itok7^4YT8<+]}Bחbh%i;c^^ԹV7Sriyvg'b=B4L ؛glᘌ Y7S)#1{:W뺍6julUnLܕb1>EBe 7kۉ7:^a( >Q89>RMc6GPuf8:Jut2"kg4:JkK<UG$ :oxCHw6$y LY᜞A]jnsPg)X}Wm'd/5+=XZK|rϰ*OIi+3C#nx$[ݐɅ-ͧ*c,1Xij7$Eg1a J '#NϙltV'#GQ5fFQ4;i?/ k5IPL +<jV]i^@8$A[ZѮS`q@uNL{[C{ WI$08|2~Zh{Vqqc úƩrmϒđ>*2`{>8kk_k嘯ts z]''ΌHKƮc*{eOPׁ|`u t[2\#+SpF}UTyQv6Aomksld@բu;Ֆy=POC^[A}SaGN_Cl-?U\r90078B)x@Cl$@ _Ɯ%ZleӦIwd9WUSk>,!neI rma%M cQzFSmRjv(AM `OhFv㩥]3ÚxVIIҼS^"`jɷ\Ѥi$"oB>^Q#߶MI]0q3&XNG+ʓ"noӋiX֠Zw(5}}[kNr9?u.H!5݂r>!{C_5MĜ~5^ʱ0pHm5&ΊZ-#Nz?g\~u6okEkHrԒ1v^eυ?OB\J!s^}q, I8WKߛVRV]OOm6QtCs15HٸP~}*FX[ܕA@; %)%bSvVBfS6j#8Yv=/%_E17; /S%OuZFhvDRZ KaF<*sXog%3hц,`JHˌ=难iIs89TFց-έKgwkuٞG1). xcj}TAEel~qtl~qtbKΖ\w$Mál``t@?.@?.[ ɽ5h@?.@?.Z++`>`> Z++`>`> Z++`>`> Z++`>`> Z++`>`> Z++`>`> ZiVEN5,.8Ok?JESd$-IbO- 4rsҵ"ϥЬ֑ysj.:#7AL'ydwWƻL3jrꚫonfXƸ'O8>2tﮧj:Muo;+XtU~md ۸8Xz]G_kaVt?) @b/~ }cmB O*FFMY`5Q.w!wi5獬 3gy8^{:PoԱD_ 8{g}okazFw2K<.I<'~iC3鶭)tB7N kb-X px?>jJOñX H-#%$ uɛD,]d2:BU$ {ۻok4 6!܃G:VKb[rwehlm#[xHPf0O}k5WifY#71;I& b[Q[SVY8 ɸq8nƬP?ex?ex?MEC[Q[TP?ex?ex?MEC[Q[TP?ex?ex?MEC[Q[TP?ex?ex?MEC[Q[TPi Qh}U@vOҠ֟[)v1Z<7}BVɐ.{~=++y* FOzfk.b$++HYo4ۻ NI4˄I^6Ru|>**\N3ݭFJExbBXLFui.Ĝt[+}ȶ6ϰ6qon}Y!Pt1l!n?7۽zG [p]>W$}ីYs_蚄 HYli 3FT9%d\W6w:je5V?ʮNX9~tܮcŃUw/#m+IJ<;]\x-f6SKp\ 9R8$9/_bQ_/`,tg j4&p5Z'̨x=kiҧZqW |:ƁOp{⾎o-0?unϘ|=kI4e?c1R.>_3??Y۽XD}d6];B2 z_B[H/Uc!v$}J+ 8Y$^ t_{9Ww0:k'׬*%s!eE8\n;]ʕ4rӊZ3ޓqx;T5;Fw.<=k̾0kW]{U uB LǒH@`W˯I=ȏy IcoE{{3A|HӦqi˝#gueN"+E($h))~aΡKBU-b П5[xcҬhw2ii 3H1'.GR+Mg=Sv$-"32<#sZݚe.$!hYGOnoߊ|ԙsikvm!1 U#,xJN~eNtL0CRFPla3QJsnn7mlʬ@$d N t]Iyi ɍH$M˃5TD;p+>HӸMb;\wbj:Rb!gci=Si60,'6+O!uۼX"nFGq?Vl&D,H% vEN+9*?*m ?pTT}ZZWf;F3kđNM+.D,2OVN׬imw,(Xt'i=_8;\ڟfsf#8y~Ox!zh o<$5iAT믞Svј?.WV/nneEb6_-F12~Ps$ǭ[Ub\}đ[".d%coG~\g  ߋ–;e'c<{ԟ GIC16h c9KXX˭l_*SOÑ^iz]DibFxD%R Һ\K>ŧYʐE* X(#<\O nV;YQ=qY%(X?y?+LU8ɭn6=s~ʼŷ*ڶLǼIFW\oTu5=?8 DwztrI[[[h%}70 8#u4ɒ1߸x ^Yz֐uT1{mnc OPIR*A/{1GO#u$^^,zᝣbb[ǃA:LOԮ+*YPPv+4$% (UQT޷M/"iQ_Ӹ' |un/y-@Sz/oOZj*O Y#;1Zңf5><^WvIB0'P}U6>D?WG_ ۢ1>ϪQ}W~(?¶ OwA}U(]Pg+n>?WG_ ۢ1>ϪQ}W~(?¶ OwA}U(2-A.<;?N1zⶬ֟AS?JES;x"3I+H4j@D89$tRf(6D $6)4yu=n $±YO< υzUݮŲ9" cN}JVYͪ]FOn/Q{!(%c$=L+"@i*,FӴt5xVX't^0A ?.5{mt ,pt8?aWYRPM>dbZ]xV1¬zy`U7N,Pۍnv$EV;H c-]F@7X׼ƍoU3gz{蚾,zupf0=2+/QZG#aK-m6H`kWEK'?'¥W5 &(}ߓ3ݯ=x-![o}؝}סVRRfI.dY8TBpyNɩٶc\弃h ;ktq$@ל]5/zJ^9_zxg?K;պht*~%%nfdR4b2.@X~7I{粊z Ƈ Am}+Q|z ?хt= w 4˙\,˃8?{~Zbް(rl>.uɮEKt2Ďz+=*|x]_I|OG (V?!vr&<+mBk6R;*`ƫԿh]<Fo~dH]@H C(*AGzno{7e& K 1A3i+ wy?ƢZkz>^M游y#N Hߎ׶R|tѵ'WdGgA4L:J|#ƙÒ9Z{L.ZXͼXDA*ڻ~)ǝv#y[nXpC-{6 5ȌAī0A#+u'=އcq-Q=ۮ+aך[sJ+e!6T0::\myom|X5ctVq}ݧ%H#Һ_wek)Z U(*8ڴ)n}ؘՒIy*A>z^y F BATRi^tڸ;qFK W|F}F.mvB*0qщrobiCI>^}-90 '$W|#kωl`9V( 9 xSf\fS cbI jIM/h_[ ^ZG$ovb(8$~_A!p@#H=9.c'.1I68cZҡVkp"ɦ]y27`*PᰭyƳsqR9_Fan!-+,!V1#ڻCcrOh-unvHw;Onxvn7 ^#?˛Y 4-#YH w=F+7уUdJИ-Eg_2$r˞=c(j~(eiב_EvxGu/c$1mnwO{?q+-zʒ 8 9hO)Vgї?UIsQEPEPEPEPEPEPEPEPEPEPU1. ~Ufj MZu ML7nb*FHP$@T:_4NK{Ye[{A<A ^Hxf YK 6v J@$ |FoCyݭ,ncYI$U /|Ǣuy5斗1Gizkjm$ `H@ۜu㓊t! hm%Xu+A=:okjzFXaRI@?J%}&;iY ۦ-q#Zݰ)뼿1`<ϰE8ǍUNky-4"'TSX6/J 44?Psy4ol$ 6:zR[oM-m&|;CJb4)9ɧAym,^O(2THq Ӡ\:OVчHpWY徟x^9di&'#՛ ,y2k5,ػ0v6XgI8=zfGdҙ(VOҨ߶,zg)n(MCNW6r#cdUzVӚ ysopzJ6>N'һG7ul"`p `䞤I˯xm7e! RFe$GFܮ=+\wRU%ct3 2Gyn<ש|1ib& ې/#Myi_dޤ<VyARX8F5KTlt:þ!f,&o+k;v`|?~zMoKw35F\ۖ_xO̍nҸTvj9 ιi'MoJtuHa2-ßٺ|wX+ #L,ߍ¾'Ú+\DL&ND.1pqSG:'OjzE!t̐9ؑ.MZI| chģ I~֎;<;3d/ ۳<5oوڼ*s$rHo `>۷S6h𡴃-)Rf[Up;:Tx]C;>9X; ;nA9MaYLϛ)rInyϭJ<伒MUı&[vۻ wڰ弦M8'xgHVw#Pn I#kZ]^{K-hdQک;_ Ec8g''W5xb6 ci0}U.jiS)8X<_c{iyp@ج(FWcPc#א@eƈ7N%s,.Ėɭ,n+tV^I?6UO?/WSkyY#,S/˩>Urծ\K!c@{jl<1tq@@ú6\JlZv/|-mkk]N;pF YyP?݋P2Zc%Q/.1#?ɻ ˠZOk,fF@ۿ͒ۃ<Zvgq&'U4x0y#<0^ԧYqqw8hLFNyR}Øy>7qddjxPTp9؃ Q(d sx7:tG5j嬧ʶG{m^vyi1E ~UQG yZ2ܼ.h"IXPTAѾxOyxݧJT*@3qamן4V;U+Q$ୢ< Vl$`F2-Gy+U0ŅnI%$ڛbw(='A"̎܃ }߅f΄L^vf%F?ٸQEWAԆ?Tq!,4KI.jxlת-O8} _51; GB0RG@{7٧ŲXn@j0 n'~h:tѡ2o0G=#g3Uֵ]*41čsh w# s0r乶eO *OUrGIWytTDEDEGEIIQIPtTDEDEGEIIQIPtTDEDEGEIIQIPtTDEDEGEIIQIPtTDEDEGEIIQIPu^}W?QV|?]3=$#SuHmHАDAx \׽StգZYn F5C5,'IAFGc{2xiQZYmq3"c#y}IΚ 1("" LKkEVgt@\$^Q@ \AØ陫&[r_şV?Ae-݂ʚ}N:e5i 3cZPC ᙳ1V._kD,橮t]h󟤏__GW^^nΚ51'01/rC2LVTT8#m|#oAw?#l\aU} JӴPc{tHpBgjθ;bR>Cfp(TZ•W ӉB7q9hmmU9h4ZT]]5kQ J_f(1⚦yڃX؈H7I.rq2v40Ԍ8_fbf(dC,.SU(L" ^CP=ӦvWr%uP潪Ubc`pϛ* _<^az4GyExcSiAA0XFFD,[ 'bdr>l>&̸c$Dt/!pUBЀ&J>&`eoP2G뺪y^-Ao# r DDp7lE 'fASuD $8A 16TU~uz]NL3`7"Ѡ*I x'(KuCC@CqHuxH"҉v(:Aͮ*̐9jDX<$y 6 (wCjOq8!8hbK̽{{ St Ɋp rsGnO+l]o^C4 ];[Gw kCOn WN ŋ@ODkι뺪*9ŝO>c /NK&׽rN=qD6z:%i7xlI)ㄈ(/,YwlmeM˫T3%cĨE"| |p.ڴªVbՒ}Q;mD_ƍp8IIg%;?m$9;8~bcӧN8Ǘ)8W,.-181@>|imȥo^{-<ɾD"!=@\Boo&O6d;1 ?oSѣp݋:[$L8v-ֿ .|׆Ւd$n~yl=rq9h;T*?7CAeX:4MȊDwcFb XKDu,[ >=a }#.{v=>| &cxsWs&^o8mx#T pg`%%%CU-naFeAete֗1vAW>~6+W"HT644>7UR%o|doE}ս[P܏NV(!,~CpTjkmٳg9o}zD"Q ?Mu`U+Y87AZq4>d:N!{`G~՚=^:h$EQ|>PUUX9&k9 @03BƐ<иQid'۲17dX}MD8Q)HWc5g?qT__H8UQ 16l6|B0N7}񦦚Ɔ d'QrYi8wH<Ɯdq6~=Yw=@d_ OE}8QIgg5{Yqq l۾xa]4CoQrn '?mth(j|>g.qO+jWUyM$߯môiͽ#0QYyK.ݷT,rޏa!0?7-OCiĮij;w!##3O)WF clS4M3m[_8Im8%"ė4Rnxbx#h]{grK@~*eD"g01nhZ"/KX4p|!'"Qɸ4cD*>; F.3BDt]Ne>edTWʿDi<&0o,0B"#$mAÙry~-;-)$^=}Sj3uRm>, yw3ZOR _4H9d%,Y*Gro\~UoU<+aLH&>Vxt)?׏/-xbJpp&&+JqӓM(> . #4G3Ƶ@np/xo1ƀN[T 1M-ݻ ޖAdr$3_skaGFBig`cY@%@ºpA'6Е81b„od,pȳc*Ɋ7<86x;-w Ŕ?Bl 1pԮ lU7`i5sW / Im0 фm0 фm0 фm0 фm~* ]rzzTXtRaw profile type APP1xR[ )zc ǡVVUQ*Dv$c<_ޯtwSw|(I /q \gq6:ꍼh#ǫDURg\*q`i:323crS+a/P3a+æD<&7}"M8U}hRIZZ-vd7K [(b~RVh?yݛu2m,|Dxx~iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-18.01.1/images/batch-convert-raw.jpg0000644000175000017500000007704413222767271017173 0ustar micomicoJFIF$ExifMM*bj(1ri%gnome-screenshot0231+Ơ0100Fotoxx:paint|resize|sharpen| Fotoxx:paint|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 257 545 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?-j1s_5=y运Z>}h jz/(S!Y_j>}5|_Q槢B}hQ,j运OEe}G0XS!G /W[e IΟNfڊ}F|U˻y<5iZݕay&t͙h6K*O)'߉N ;Z运 [ y..d^YHUQI_04asDZSxZRוּCss:E5u[?q 9W^61x: > k=6'I#I!rA'v_+GO;iEu $et B ;OEx?$J,!$< ay=X?7xW~3ּ)VenEgH1qӭSvv^n5=y运CC\~;Q'<<޷io1P+.]_=n~W>DU&TF%ups\y运OE|/Ğg:lZໍ_J2jD3yem =qUL{r1*WW^y运OE|ŏ _IHӵB ^cwL[Ή 6ܮ3d]:nmfTL-q6fسO<8c[~֗ <_Wch?~"\k>5;/ɥj4)j#XU$I5~Пu x푿Ե+\x\4l2$yh_#OEjz/+>1-|Om5mf=Foo0 r~U^P7ؤ._"W,}槢B)%8ԻI=?kMSTğ t*_^[OeU.[3P\1 {M{MV+-KM\\?G RKB!TcSw+Tv>mu h-dA%V K槢BC/<ekYtٯR%FTr2€ /yG::=-otgo&6`E==jJSv~W>("(f(Iu H  xC⛛oFseu+'u\^ GX}hA4}2-Q|hݶ,YDU,x${;| FϚ <_W~;xŞijNiT֬Hg9l."u5`;>5S!G cX<[ \5h60im&RFdX : |6vOt;M>CܖXgm/k`w438Ԟjz/+Ox? EOTqz6tQK,[^5 #`'bf6"{H|x&OoosS!G QG֎aS!G QG֎`槢B5=Gڏbk`R.ssA ŬֱXČsW州Kple/vbqS^Z6F eGf˜ݣ݀Ec xZiLqăs;`=sT_K+D~_{r9I9}sM xc]}@ݟ5#;|J W6qi Y+9|y G_[²Qn|iirsLⱵImb.ӻr?|{ڮsƗ,nڌAGc=+3/8+_>(T)Z17-"i*IetѸ5u_ |cNqn, Dޣ'E[Z庯̥ߠ̿V<.]t[FpsS}k3:\J`QW% sDaֳ>/G_~e(sDaֳ>/G_~e(sDaֳ>/G_~e(sDaֳ>/G_~e(sDaֳ>/G_~e(sDaֳ>/G_~e(sDuC}jz,"MN3`7#|qSx/G_~e)H|9[ Լ3i2OxvTL7Brf|@)z'.S[HshK/'$k+̿e_L\F^INJ]v-:[Eh#峔v'9 A ּ?6]bFT)- Mte_?2Kr9&𞩨xO@ڨ{ѠB/G_~e(xm>\W2iڎqn5KSwDĿ =v<5ß [?%Gf,LT@|3/QW% $~Coh^0׼;O~᮴#FK2#8a}N`xs~+ORϫ6%e>byJ1W% >/K[28|9shL:^d}pۇm̫8 O0^moK-aԻTr8 =ٮ?2K}_Ÿ$7<P1KYdH18u²t ƿյiiw71Ax" `$Mv?e_?2K%k~+ZX.u{#کbHmҁ3{Zl_ihqԫ!Ă~r {//QW% |b|3?ݖ-*6AJrX$X<im0 ŘLc0Q , XG(ƣ*_~e(+̿4M?Z>}k3?2K}_—$O֏Z+̿e_AO֏Z+̿e_AkĒIWVgB2yb׮q;lZ_/QW% |4ISkQGI'w9'99{:upH11ĮI$Ov?e_?2KrH9X00swd/#Hb|6r@#z_~e(+̿qb4qQ9|nxyZOs [x c7odP=W[swT?e_rf^?¶*t9:,U]KQKĐ(OZR3^EK!,nѲNN2+^9OX'wqsMCm#B (@3\fz>wy(מ=5rrc97'`\ bmqu(v7g/qu*ܮ>z]~GWV:'q6lmuhAyaKOr~ ݣ_tW>e#Dy}V"Tծ- ˸2"4ĭ"B44'gґDoo,/Y+Gt[ml~{{gfiy<0V;nwM?E`m-́N(>'%vB\GӾ7|=f[c^^EJpCcF xo|ShW,jZt (3O?~m;|dX'_:n tFi%#%ܐ<6Vr[zRZ'k]v$5.=׼m,5ukA㺸SE\F08͜Ox_:oAvV25Ԗ,5TC*8$Ro4}/>s>Ok: Ĭ|ٕnXc 2sZ%,!Y&zF{{{oa1̻e Pf [5V؞mϰ _Am Z\K k3'EO3*20+ٮk1K>ܛƶ{o5|J;Yvz}S)QEQEQEQEQEQEQEQEQEQEQEQEQEQEQE 1$H6n2Ktf X{@kxBZiv3rֺ {x`HaE$U`W%/G7DugR:TcJ__*EYUVcJ__*oKQp+QU]5,LhYus»;j0 p0e*YXt Afo p˻$70GL,ֽPEPEPEPEPEP/)kO^uok4i'eNpjcZ((((((((((((((((((^XAo5nⱸT,y(PP88=p/ jݮt25a#++ r2 h;ѼYíeuBu-ށ⫉(u/!`2H*U9enWR3"񧃼Mi: dJT'#J򤙠cN{?xơm?Bmc",v{цN`)ͧZ_xiIMKQVC@UB iԕdwx,l? ǫ6ny>lb9;A=A[<nO/[7Y\h œlgmk&o|Iu3 1%7ʠ<8d2k>#K[kk-"[荞.xkuw,kȨQ6 F>/jz ssua5ƏC;suI&RW$3exY=ֺ7 'tpψun5#"6i W*No|;ø7qv^&d<jG5_ xC]'S+AwkyGTrs{ 5kxcS =x 1twWz,|S&,9LfH-Z@?xnu}CG+bX_|RH%5ZحiZ$6m։ge$a)#f"O5 D)ǡ_INoqi h4E[6{;9PA,Ҷi),J6EYY/úL- Ne=J5{mGF,w^kVi"ӌ`I>6xֺݞxj[ӵk}8M6&W2! m6br@&[_ [!aiCۙbI DPH,p`y\ӼCk֓6oX{IRƒr8"EsF\؅{jr~/n&qMGg$i$)!sVH5 h=&xK@mefwhUUsZ.+exPR_Pխbԭ#$FxLRG+ gI~jǂ?g/?5>>G&Kw9Mm5:k߈8P0$ǀE/'ҿiOjKѨ&g*яFȓp{߃/#i{)Q:cpѫ$o*:d9?g&|gdɧeXwIWŹ4$rd #eέ{s'uA\ZA6FAH$I&MW~x\ag}=Q%$!d.0GƯxCHڕ}26oe2 =kY$I_oymijI.LR,K1;xS$}U eui2k>,?/$ӮVSKcE)0\9l]/qiyi˭k6wWVۋ. H|00c&RF8߉^')ūi!ZHlvG_H~?a^n/-|556iF$qHI$dS#mQ1f]-G/>uyM2b!b=r-7#u}?2iqxrQomEi1RՑ37Wt$#3W|Sm*RtmRXD7HQTy222.N$W} eOuknSCUB{ȫhCf?4o׿}ǫU,($+'|cëij]dr0H)X OU7k?¬ѿ^Mj H. jzjC%yk(&30rOJ,'*5?VhVLJ|WxCNi?ζ&7ޣpa*{ZXCf?4o׿}ǫkOZYuu$:fghj<6; &@𽏆_\n[7H@kZ)3{Uiu@G7_i7]֑IYCK LV4ݵ#q87836Ə36Ʒ~|R+ɣ`,YPŎGx O{%9̈=JIc˿?x\ҋ$ơY̋0ʒ}OZi?nmϦDnq 83|5o}ڦsmSI 66FfHeer= Up?#ih?#ikӥqdv$cѺԼ} K>_\jwlsGs16zlg4V€<L_L_^DZ+oG=[ߥ 36Ə36ƽ{|?m~(?{Kcϕ_!?#ih?#ik׿/Qc>V€<L_L_^DZ+oG=[ߥ 36Ə36ƽ{|?m~(?{Kcϕ_!?#ih?#ik׿/Qc>V€<L_L_^DZ+oG=[ߥ ."eD6`K,4YZB,ڊF`?楽p5 XF} H(4mkkGxC֦TkӼ-}{ *KM `so4k-WL{{:%$l28)_?zw?5dE?4ZӉv_̏x~(#0ߥ .J` "n;*jVUP=~LZ~`5wuN\^ úd7+Y+@˒"c8!F_RBW@V>W7WƗ(mN5smC WkZLg,zRm] >,jgkIn/-c9aTSZ^o>P5qOe. /DA_( 8f[-ُtO p|9tn.3#,I}E$}zב|gexjkh֕i)u\{mY&m-,K5Ƌo{_6_ͦj#~'n8t|ӋM@و?y+~i:W|0xMm)4MP[kn|^ee23ܱH>E0*k?OS ]sW=^'kvK%semmyɦʎ|j 6?kxwzj_'T;=5/m^[}Z'𯄭+5Z]55o[jv6bY`cP|=#'HOv2K5MK4[8m*,_;=5/?kxwzj_'T=]~s/[RoVVpxnm,Jbs,[\fV\OmDj4rTȱo * C(?sԿOyƫ\<=AֵmNRЦN7P_"Gc ģ2M{o +kr7@ Dž|7y[7peU Diݱr?<==5/?lx{zj_'TvsZ ?;=5/e}ԿOyƨP_Er;=5/?kwzj_'T\-MKjZ ?uW! _ÿR=禥{5@}ԿOyƨP_Er;=5/?kwzj_'T\-MKjZ ?uW! _ÿR=禥{5@}ԿOyƨP_Er;=5/?kwzj_'TiUsTZ~0T8ofMDVRU# T|];XOɒEiDD,:KVo/<6c\f@Y 2=j*]+k6G&yiB$Ȓ4EI]g9ǵEv#&?Ԁ)0u)t#t7Zbuy5 _qvg֭p$lC8I2Ud*T𯉯~ xAĽJVbNt Ī@ws_CI^`K_8_ú7kGݺčgú?_SZG/^ǒsyoim\;<% HGHbk?gg6Gnc)e1]]˶ۣU?68ϧ7ŋWck_kȤ⭥_2ĒDCs:Uoj۬ C$Z"4-4!%RI@A Ҷ)({Z+-__uTJPtga$QʕC0sv: \+tό=I42ÒB󺅊 ɭ9c+m|Ǟ+iC-|/x]֯Եy[آG02§Nוuz>cu"hWOSh$fKU+pd;R'g|QSėI]*+%OK$LX"l rpֿ/}dzQ_/iZ>3mKu-汩鰛Oj*VB@9!HAi-wŸ /)Ӽ9]s\}q}y4ւbWrI,p97}]Ey_ s\߄|=07%ٞ)%Pc`N1 oQ5[gñ:ޞuYVɧnn"\FTJ(~,_c( ֿhnY{+8u-i}#cGkOVs~6dkY*)@ORwWA>nVt cVl<}kPۥ~#.氶{※G,ɿpާO#&jz:*-yŜwچ, )岼-ݺ$@ qU"yMQHR_ci̗2AXWRE[^cjWơoŝ,ZY'w\3OzW5_Ap%ދ ' ]r>aȠ pxL|I5w^t+!7*dcМv֚ߊ+^NA)^(d2۱Z^io^ת{mh><:.dǩ,;Ck>b9I؟ZD~_|C ǩ]k]%ƌeIo^>OěmZ[S-5kmnmKEk!E+ȪQ5\/{imYTv Fč{5fZkZrl829(`4hb(#u-?\ͅuy*48h/C]o|ejDž?4PwӦ$$Ѥb {{z_.V>a𜺧|?5=&O5{ۻmNe^qBe̡$….Ae3׉,o?Ece ,Ytsiw*T%m{:ֵωl8>'5+hvp΁'c x|c )SZ=}mWPm@X9d`*x=@4|R𽯈#M}Y/Vub4y qAϛ``x7ᮡix=WJ[xq(TrTX^?a=5o>a:-uM*=V:ոԣ͈֯TF7(F ]Ŷi_5MB:mu[c U ?h8\ c}ϧ/Oo(tK\,"#+JFh08E;^{_Vtb+?I$c"I g8!'k.IX(Fq|>_[M^imݼq[o܌eLrrOa+[ĺo}YoOH]K. JI<. 1-KPy}~Ɩ~-k6Ɠ,"o*;_0F7yl~li/{ Od}UEP07Z/m7QQc+*o%v<:oO ɤxĶZUW[ngAvG˛b9a)[o/+/M5hZn-M'+,߿y#wO3y126I? <szm*[`c0[8`qxh_Y_בSLoc_V[;T(h6 n,ͣ.K-JB#Uvƈp#x;So~9.N0oȎc=}y}-ZMMn-uxz5ռZ>1mp1yV"p}x_g{gOv}WZufe#ns9'(q[KQ@&M_Zw֗=̈́#n (*p8Ab|uV4}$(ygm#a gG뢢8ZM[ŵ;y 26ܤOB$T~o\6k8յy#{U#UUPQqdz(+Ru&:mއ e==vc鑭:jo QHΧ}@&|.MZdS ˅ƹ$IE+xvkk+-VW2b/g'I1|)..&_QX2-J觺,f^ ށw*:.j6Ќ)WF* ׂ>i UFka}{]rFJU ]qYZ7Yx]uuw}F;v@JKnRJRssz#[ :i7j^1\Bc2 :FEF1e<(w_`j_Š(fj~.5M8Ro,rW z9[hD]#i?pm8 {cCE+@4kX-٦*$YJ jh[U][A*>eɄ11;B+l]syY^^_A|t4WOuɤ^^wKBqcsq]%@wgMiWbNմV;92MGu*C3/Y8o bP72sw1Pܪ* WOE @*NxNnn"{RP<ZV 3 z85 Wn%`2U@P?XRͿl$_xnΣyäj̟gE,~Ҹ'v0Gf<7?J𾯢Ư+WpK`T)\d{ 絿[-%0@шgP!ABdmek 4Q@x.}xn~tf?l/v6oc_M:4λi(tMQI,q(EcA ץBa5}Š(a^]{@6mfеoዏ__ (d(ϛiDc;5t7:kpkVBTx`5tf"Q|q֖@h6 ?R7׬5+;oZg7LrmfG݊ ;?SO XY,m=M#O$~_?rnIݐ׈dnW+Ȅ0m{9,PFݠU[?ٿ6hG4/ZWGm- _hhh.JH#>bu:3Gإ˿@dvTy%ʷ&d4!~eψZĝ MJ6KK;kimf\1$kˮ>xSe}x9<9v.tS5VH2:9+x_x`i7VwO<acn;c8UI8MukhE/;G6_3 \xݒvy+t+=sϩ;Msci^C F<P))ɵ@KD {1GsGXH#tkfX8;wG,E `3~ x>-~#+;;]OlmfHKLK(p8xgINjz^ݴcB  5iuO"Gm_jiY=;2]73=pWci-_nxo:z~h`:AQ7٢24xISf0Kn(KuYQ\b3^=oz-|Maexxi$Co/BXE`A(4Q*@)#gwV}<_\]O$ZE!+y oq+dfpp><:w}-n]^]N%j$PHxJoj4?NŤ:F4,cw!eQ v ȯ>5{.+]ManDg:ţ}qr9 Ao>%xki_jRZF (29"|] R {H[y"H~LMT8/7Pkrƍx#Yٔr nfxc➵i|.zA]\+3@ϵ'G/t{R|CWX;c4Z?1 LYBۇJf:x?|?-$K-=dFRUPͼ9VuO%ֿzQHߌ<5/%?e-p B ld׌7^0 ; L,K2B0bzQxA ]xg5gD3 n,XNA<|?x{=Ək 2Iґ 3ܻ3BQ;Ob[n|Qt"X#PEI+i`a>C@p%I>^yη[O?XuK[{& qBdeٖFg&VW5ԧjIn e cg5|0&]vuIy..eGs6$j.;Yh/QVmitb-Kgl~, 7 x &{;9ol$xT,y+$Wxv|;᫘l|5{jQ ko). hRZ$ @a,CٴXq^uz-5+;aͺ;x)-"B@`*o.)lkii/^i#쒙TE nE{/Qwעb#0H,lඌ5.ʼn`d{+QHaEPEPEPEPEPEPEPEPEPEJKkZMe8_jE`Kt߄ .↷Be` qV.F 48trd2$qHp:Ҹ:K¶(_]hW۴Kk[\*+#<29gKQ$z_0vMf`،F0:Z+-jhsh<ǨGBȍ#6U؀xUbx&ڭ+&YgWBnj*%]l%mFhc[O$3klSF7YXA<{W=񟏼W-?PU4Q_E,eAr#V,lQib(/)!E@$dѰnps_Q χ??lUIվ+kv򙮭&Foxx2P{ ctM?qڶM.-2{] PKǨ\v4QciHI_ß6?*N|9A/c⫭Cv_xO&H)M38+/h~[㷅z^ͦϬZj[*{I*[r,1WAy?wmGwL ZmJ@ǃ=:j+vQxJ[Ť.ݠeP7?xR4Ym2-cMJ.dʆSq@nM-i"\b6(qV#s]>3gF}u "8fp0sh>0FxUaZj}do u2+Ȯ >!yOo uQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@r4ψx:ķ:m3[\4/GC_Yφ4yښ]ϵ@mdP6\O'[^AZ\$ IJsXҺh׺M*zw և¬6uM**OQG1:xcHWSVn4Z^T{FTh\bl&z*i_ jZHHX{KMa#NVTa7N,ex?xOmgܶwzN%*Šlh#޲uG=J(M]( DŽ[k~+Wh5/dR8P-|/.7 v5( Pqt'(9*~ּ;;wJյ]2+_1Mȵ)wH e'rk*(ZmhS濇_?_{VY[| wHQcSpRb=:l\|,E#gIU\1,OZފ_[[].qiis :mŮ"!ë^?Jj? _u@.gI6"Y\0oSҭ(EY~^ :D5F#ˎ񣱶 8L2Ӄ^yχ~0Z5mEZnOnZj^m# X*q 2×6iNbRn^Qi  ⽲7_1G,oJba3'jS'PHdTZ&7R~zwT+^ ծDQR+^!LK`5ҴK ^,th`EvaYcH OxF}YjWzEH=`-bFBs]Jסރ?<𞥫xBx_[IyV}tcp=:5z:|{i| {iVZvy-;G Ur)0NM}-Eol^GȒ|n /Ffפ~ܬ /ۜW|*o,:~a`Gy $e"vPs/'=kO5r};[|9SXԴ-&MwZS{-6,fo}2{ ˶mG~(y?+L,[Ar48qr<)ʹsW}146&H؉gy@*"8#~: Ŀҿe/J"9/xI{lcYjngxi+**8c1u|K@=+R?bzW [DEmohzMy0@J__2V58C!S *2z( ( ( ( ( ( ( ( ( ( ( KU,B\Uo#V/;ZPտօjeoh5o7㵡Eg<[l hQ@F?_v#V/;ZPտօjeoh5o7㵡Eg<[l hQ@F?_v#V/;ZPտօjeoh5o7㵡Eg<[l hQ@F?_v#V/;ZPտօjeoh5o7㵡Eg<[l hQ@F?_v#V/;ZPտօjeoh5o7㵡Eg<[l hQ@F?_v#V/;ZPտօjeoh5o7㵡Eg<[l  .9& d+zuzWտEO :I=uz.?#V/;Guz??0_'\ ~F?_v#V/;W??0_'OT"=Z5P7RdBz(տ\SY$ܴ h(ެ>nsj"k$Oտjeoj"k$EO :I=տEO :I=uz.haԖU2ڼy-YIqUhu, WNcwqօ0 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (1ۭx`A[j@>^ Ex4k;;m~SQ>֚'7 B$#H=d_<SX÷Qcbۣ rs|iZDI R Rdk@#\Ġ :u:֗{nE ɂ/& s'@v0FIp.Kw|n`aW %mjh`ӵY\M'0Umoqg' kºW)xoCXt+V)iY ߰MX,svG(6FוxoV?׉R;}ZOK]Fkq ،9,A'+ӵ&0JEgl[?-]^R7 ҵiTcx$3OR]𶘇C/_h6O+iuʳT1^GKY񮙬j-4m/V^H7W1]Oo ʮ$F nkCV'^P1jv7o=mlBF48i\7ݍn+^\fw6yt^}i[[Л;YZfWΟ]x&5>I v'gf+!9D@P:H7SV̈N]FڮMmۖEHJ*! x.mFf컻f-ؔBG1|Q8ծ帊 43,%:) u9h~CǠ]qZuGjÐrlQE ((((((((((((((((((((((iڵ[]! \t ?WZ/ .P E֋K9E?]h4º}/iu( WZ/ .Z|i7isW 2gkOw4r1w QE,7612cBG+_r)º}/iu(u_QE+ WZ/ ._K]( =öqȭ.73t'Ң`fotoxx-18.01.1/images/zoom+.png0000644000175000017500000000333213222767271014675 0ustar micomicoPNG  IHDR szzIDATxipU{ݝ$lAB G8)K!@ CP$(rDAAP RA$+"䢐>²l633, 􇯪y{= !I?|@MQF Aio24(wd$qt&@WRX(h :?h5>oUB9~ y3`#סR36Mѥܸ¤7:Z6>(n$hYp2;VulyC4>c,#gp)1}hdԀ, cB),Z֢;25cA 2n{ 03ܗÓmGSsw}z@4%@ Vdkahr/_?]K1ӡ^njآN&~}`tAH()?:&i<;Hٟ*gp;#nmtN Bwal(_DA퐊{ .Sgyy 0lep Soj>D Azs:@ø:w$orsG끟Zzt3®pT{ߗT{g(]swEvB$;/\pzרV|'sHJJ"W|1pʨA*8':Q:tl tH6(:k:qT1vIm@LfgVUw] C-`8!>n7GE:$g?}6I1,#e+ ]S@9>7ɬ  \QD?P.,d1]g- .y=jdWйm\AlkRF$I_ N4afEI/By)JѼo;htQxS2VkjDK #B4Mn k) #`0FΑn 5w']QP r/T.^H^fyf6 H5FqZW/N1hU*IENDB`fotoxx-18.01.1/images/adjust-britedist2.jpg0000644000175000017500000004562213222767271017205 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231%Ơ0100Fotoxx:retouch_combo|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 237 316 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?յɵː'8#DVIRrJɟQW.vcY_m3,9o~3Xkbʲj7.#$@;v較jٽGmW*J\>dЍS'܅ywB- Σ7\wM=Ɖ!]|6I%Z6[:t%MZIrɱ2s1k~Jj+Ŭknoq{ngno/Rw 28 Mβi*r{ш؟xT4Nm5 3Kk"iI#3ݙ#=2(wAk'6'O5״ooKcFtд85[hp71ީ?O$K'5jEOJZBH"o.PC¡-Ikdx;^Fg>$q]BHKFQs-G =>LUذҵidᥒ6yZ0VnU/F/^&oxR? &tnRRw,Cu2WQI/t<#ZsOfu[x<vʰېuĮfSHM=.?.o.=.?.o._\E|!hͫx>Ǩ_3:}@>uuH }FǪA[ortlOqsuV?|k$i#9wC;z\\]z\\]Uv-lOqstl_qsuW~tyO΋o =n.o.{ ~~t]/g}\+d~!b=KM'i^g?:a LhZg$r[FAl]~۵枷s! ʨqe4IA8wdϵ?7%mDHݕ!##.J!ծ>o>>}-,b8 wwVV.Ywxm-hHףHk=ZQѴ(/jNmٌߚ[zh~*6[>mܶ޲ڇ\פyZ?yZ?r|<_Kux}:dU'NˆȇnG+$浼s3hjuuks^- ߱|# O+Gޏ+Gޗ,{>|zdDAQgww rI'ٰE>qfO zTK 3i7#I֗HTbZcZa$ F~֑ԉIIhr_W? ,=޾v_:~_++6s}D2zb It5ckd3X^3δe/8G37/x@džoY~-/bݧ>Dql&0l~%Z;V·ZFa7P5ѤU]H8 ȯO X|*5bMYi%\^ymz؊--Υ$NxQEWxϨ$Vnj|A]XivzQ?Fdq 94>"k/ 5Ҽ Ls\`Vk~%@9*:+؜U)묧}(u>\YHo'hՔh)ϟu'I<-Zbi&T7LNҬO$g5+%0tj^tԒqnWgQEQ@Q@Q@TW7P\JD$G8&۲%{7O >+3{ʸcRB1#5N"7c;X:;5+Sa$=N[҅lM)B3ٴQZhQEQEQEVO\5E-Zd|!KCjZ?~s.^è^? |O˖悽sS?λg|/͓z$ú'mo5S i {=ίg!l#dMTۓJ$wDӍ~bRm^M6I99'&n]AK2vjm}Gf:8f89Rp_|GZgᏊ^\|{Gu,u;cD3h B\vN[G0<o'ǖnrtZy/v֎F¢Ҕh'oRInC◇~xFxK=.)[;4vX <'~:>.íMgq{y EYu9|TD+^_c.'2 I|y91f$rI_O*LEIo&%%^8m*[xنO?ƾ22O& I NvWw(q4pm0]v8j_?ǟ2k>GH,eI] °FW ΃;'q،¢VI$]RI/$4T4xCouꍥiWkem-3+0EHxOk ?iM\OxG%&{EԎ& 'E`FцیFjòoㆱ㾵n*.,oY+[K w[6r0䎇9럴d?..ƱuVʋ(Sqs VsBOyV]=^(ζ:M;ڜ,Y6O*υE7'7kZ=my@M|@~r.{\:=κ_']Zܴ팗dykQoia/5mmցT$J]Twd`/ Yx? ?43K V/5x6&&!R6y]1~5~|;h)Ӽ_h׺vqu^0,26;F }.FW^^i^[uV4!Z%yB6WK4W?E~jO)>*i g|Qqޅ[i%6![R(R1b<uEuHkK), k HbVx]<=9qi(jC \0m[wJ-oE||| SCiz-'+Ap"alNqȯ|]>){Khα[MdU`&n%9 ?[V/ZmR_^ s׷ռ% >Rx0jc'q 2-׊¿N4}{+=ik[bq)C'''~y4^z+ѥ)G>5j[f2!#Qc\ b#cD?#XZa}r.՚DvFܢ_~? )aUܹuB22~lfV V~*h4h]d[9`܁8|uoZj>!$G[T6UBU#sMi֞$se$S%۪ Yǰ$Aⶥ-fE[Sާ9=2N.QzvEd֭\_a}n#W?ק$SԒ^Ĥ0"=㏂>(4:G=@#- Rڼ`W>'fkybopmŸgtcaK8=K*u՗Nv]fC+m_VS-4vqU{ʢo CD&iKBΡ Åb#Ke+OKwrф"kA9fG19e#$tT5fz;ӞQ% :M2-;( |O? nx|"z :&71=O 9-5V"^:b[ K딞1eeFȶkh #{vuSə:/ٷ{v9oItO8ĞOH(OHѩRÒNM}C1zᕬeq(B8ЂA:w^=G$W[΁xmum6TW7,t`kᯄ>1>=^8#xitywh7Be%|w*O}^E^'_ޛm<6,M&X2pP{uBzT.k孽w^)8JJR]brVI4ևXéX^[{{hR8 GZ~C77LnQH.n* =z׈~ Ke௎#ŧ]) ,Œ^u^&u5-~,cėF̖!3@g\ p̝43[Y~w# `n* )fߖׇ?ivן|3i_4yF}srjz,:]!s]׉53J/_i:lknі8nFUGLێ,Yp8mi_>$Zkc2-B^Xx2Ydrze8 Gw}YxS ͌JtmF# ̓W*XՔCrjսՒN'9 GhѥSWRf[vWkw4|}CHg.R J9 W<=u%Εë#y#sآ& F6K7B*F.mmmQEwEPU-2YPu)-bH_FVji#OZiE?t/2ݣ[Dc n. n/>e?HrRJ2ET0.@ۚf o~ ƺqXM<"k )VDd|+G8b8kfX\.vSOzJ+~IyŻ/^ſ |OӮf{d[ZC ~U_.|"4_ c +z_7ׇ r2 j782{ ]OIռF%H>YQ纀sf> nm+E áj]kZ-9%Y  c vz1mR&3v-0X\RAIt_+C9~&|:Y|Dկ={I4M[]g,J,1.Y1>6q[GF2TcG/ 7-h5?_ tB}'1,2D.$fM_[AvG H#5T ? l$N4"2j]+[E(km <\(AAyn];)>'{!Cvωt>cQ] F.!Np3?ho7/΋ZS+m .>=kz+YfESϧʼsk䤥o=[rߝx_6W7}WLo4Z:4,DJI8SV-|kOGÚ=|G.-<;<_fBu.m y9;W;M>%x=wBק}cwaOBjQeb9lIׁ\?|V"K+#yqiמLi ]Y³|ӚqWԝEyJk%!sBN\nZo!hZi:7:D}.AnȍC);G`I^c` SXa+ 22wzz/ay5[ۚ%{ǛI-UUNTRM}ގ>,c폋~ h Aq3Y j3M!f5\+3Ao| Xs%ډ WQ+_tW<9SY6t^+t$#Cةʼ5jNrrVr9ݚvH_Eo隇u]#_Ʒ+SPē+yYn/KF1_DWuKkĚGt0I{_s H% CqZOKxzo{DuZisdaT 1N9]Vx~X4WPoVoxnV'$q$ko*>S{;֑}JƤWz+-[ԼzOs%vh_<B2}ŮAu,+uQ2{կD/|JOi'!5|'I <FՂӐU;iMA$~#|gi~w}W?x:،c?};M{.z D_S= Grߴ7q~iut|sP"`zۇ^koItO8dO x+m wIJ7WWɧ^dF,[m`~QW{riuV|6=ʊ((((((((((((+$ZG/-,G-ytV&V qW:4kN&DώgW?ž觳Ot=E,? 0@|!g\I4Xἴ썤λv1Hkѫļ4K/9 Wrj -v~brzc{ӎcjumhIGbtϖT WcIs..-*dp4lK `gr߳xGQEggcxjg3J!0$,@t+\hχ>%~)X]ؔVpia`LʸA#k|E;UMaF PJU^CO'1ȸru&G$Q/%$2Wsڡ**[Ef#guM6y&7pqj`[wHPw GSg᷊I?ۿ¾JƺZ$*-O#_c%wv{ώOLK}:q*Iz W=C6*IwEG|WZm|j>jS$W,u~k=ZiM$o+li";á] ̶ﵽSzu_0|]$QGhWSJȠ̶]vjW?x:RyiƽEg%{z $ú'mիho&6?o^\fER(((((((((((((gO Hx%I`eEmK+|sbhWj/׌My Ls+sf_N[caaN6?7qV(EPEPEPEPEPEPEPEPEPEPEPEPEPEPT>55r" #d=@3~’,حPEگu߱ςY?cKM7[ypbf +w~<:tho&6?o^^SC1zj(@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEb]>F[Vni]>F[Vn09oItO8׫Wcn|n|KA u+ZGFU""r9݀Cj4O?A/R(0/O> ?Meœ?h?ahh4|zx> =:0DŽſp?hh<"zxg8|j/ 44W=?EwTW>+h6p$(;E=>h:6=rEe#8=:^t߲TtP2IЬ@;mڵ-~كGG\REOxx}4;1O}4;1EblWP/OCXq'= @[1E"-~ fοnS|%N, ~Vہjہ~KúHhb?g/MK\`8-HGiXpiX=8[`3Ҿ<"4pG%CY I ?8(ځZq}m/4_[|:-Kz.nV?H`M2&!,?ոVq_IZJ(>m# 5zJMz.zVW M:-(}#p<8[jF8݉H];VRJYioE-VQ]Ha5kz_C5z.Unx%8_#+t:xQKk/Ghڵ-Jdž/GOj#ڊV>Y.mmZy5U`;O1OV~J#^3t"-z[]BMltiyR3|ʪ"l Ü f:-s[K! Y?ؖ%@kKĞ^Qu8TƗyO3'%:NLQ:>㻽iR^+kKMv[4YT'dI,pŹ5fZm VW-4V(%`ckgKWw\t\Hfx1mfnh`sn 5W\mleK%EU ~M #'`i5-rYC4wl;w"<,ek> x?]hv:^f.mP2ʭ1VĠNc2@+R)aZӢK* hrň‡#,+1PON6{+{붃@4*Y`@dz3i+uU-cA ڭ6R p 8Hg|V<=KE-gKXpГGῈOxV e@f'n3 ӎu_FQ$n[s{'/OKkTfydf{SZ VTk"!0(f$J4Oc⨡fmEu}BEm=H`ߜZ Pԧ񖽦jZK(R4ڠ}֤[e!GԯืZ6n`s0;g=_A{y)k_n4ۉ,ӆoa3#8bdP)u>%7YX "50!,4h𙈐/4e pW44 * ZO2YK3w$-,M (Š(-w)]b]GCƘ~? _QE Q/J(_}Ҋ(bo>Gؿ(Q/J(_}Ҋ(bo>Gؿ(Q/J(_}Ҋ(bo>Gؿ(Q/J(_}Ҋ(bo>Gؿ(VCD G")?fotoxx-18.01.1/images/directed-blur2.jpg0000644000175000017500000003614013222767271016444 0ustar micomicoJFIFExifMM*V^(if%02310100Fotoxx:trim_rotate|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 600 1000 0 C     C   qM" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?%jaڻFӹM= bi 7)Nt|vmvcMf{AjqNJ쿳}:o4]YvgM3ڟdsyiٝ3f?8M:F;Wi؃C{QS>]AGg48~9Z_kgt4=ZOOҍ#wj?i{Qw߳աe[?y3g'_ͦ#̪w`$nu!^_EhGf>vك҃{G=? _s]4 )#枺?]Z(J%0)'VOZ:ӏ]|P'qSBX}v j8fTgE+5e ]HLds  #H 5ǧ[*:?4sJoX?s4iLh8tӿٮij !y;[SFG' v8CJTиs'y]K oQf:Ktsڜ,=J|v1#){pT}ں3dEhXt>еE7tb]` 9!r(:yW*QeO0>M6]6^Խ^93A_C]ҏnc+'445Ea'(n}ړKA`/؇+l[zD%?Zp:/pVWAm;YkPQms|Ұ.Yu{d~P9gIBq?aJޔ.zE9ǥ^L2EmdA"5V+ ː+*룄sfNMW/TI0Vߋqɇk5O#y6} VY|X_7a]^ o62?Zed)KO/ǯ$%ױע–W4#p|r;שƹ;\1yj>[42}7se F uE6* }9GҷIL X]#ȞyiM0>Pf Ǩk,嶌}fqS k|0X 3oshY>͚ ghw{8+z(ORv4jQljnk^>>ڵ=ԋyTg?>6Y^@!>>mt e qFA%fgM_b[E`=)<) aQC=883ژЏjs]]\$y%dk䏉P-64ipN}<\"p[JxL<ږu] X>~WG-W~'h fA"Ʃ|M^ghSRjOpCn{7~O~Ma1bU0A"}A2t>cM<"$rĖ0TAp+30TQO@+}գյ{אR!BI,yR$>\` y|xRIxgu-:<+)f>x᯼Aw8݌fHcw/#꿲'PZN5]k=7 pz=OU5:M#!=עVxW躊m'-xVl++>?t<ʸ_ShsҺk9'?*uisjlR:χN|OS(pĨ_4ZuݴOqy3A *#.$Տ#943yd&eSU.~&ZkROҚ<+'CޜH^cq\(vP'm{O?bjW> b UմrZ]$g`2q'9~z½2]*k4ǟ*Na=Rx|w5m^z~dM}=JHП// i^jPOL1w1~j06+FۺLA3~%#\o >CWQ#0s5ᱎj5/K?_e65˱=Ji5GRTɖn8iYoy2&8!XϽx5]rz{viJD3.| dRp;F:{}\cJQ[[Χ:[>l39,I9d9'}񟍾"L5e4}rTN:|#||I]^ZYaf$r:) ^Ik>8?0Ǟ88?5xI GϽy7OMIS5Ӽotp쎣xcjTI{l$=ƺ{4ׂ [Bܱ4WGp=yo#knmk&4M"7<2Ѻ2}HI ?_w^!,2+LؐM2)@6RЭnn<5}:F@&4>w;dVxERtIE-3ݍ1lz|Zu9,rMWl msO:>*No+9.绋Wh+ly#\HzAxuqJ=Ù2 pz(Wt8WW)ݧpҩ=KҰZ0m?6>{Ғ̖!jiϒLC#Ekگg(k߈,}wqd<# UVA.>s N Kœs⼷Rx-%[5~mp;gd|^ץ-,5a sӚat?Ҥ6M*c8Zk8PrۭM5_]+oA \mSF=+\'bF? T4d۟tڱc'RѣL6tL_\o V۹O5j0Ųaf1Ryu>b@ьV<~}+3Z4O ?tm<}^qMs8^)rxsHqa#a_x_=\\m>fxnȚ9m#{G'^io5[^Dn#ޖuSMV*is"!cinդ<$p }^#e[K.QtVon8ǯlWWZu[f}H7߻:=sװ McvWtGGe8xZj5o7$i|)/)c"d${W'oD]{+ le }f(8$ݧخPq5}%dy7~ h>;E Icus|\zcӭ|σiME !qnBzmNx|]o|&4`&o>\G;}׊{/IVIVh 8 v?ν&2Z_[>Gҫ5 M=zi&>V6Eo<{#9?(_J*/5OO[ 獥 "aUN{>xzPnLi1Ww³˥.M(*) єWauW$uy:X5۳3Wig@8,a xLӣ-oK5;ePV@H,޺-OBWYe3L&B]XǨ#nwG͎CleRMXz|ﴍ$R 伵6ӡk P9fMG>oU_VӴhV#4q)?17ھe^̋wa,2mf%-9Zq_f௡\ $ᭁ`\/WR_T~39(Ն K"K.s9ihN}+ xRf.lg{aBU1`0'w kėpi3d) m+sǵx ,~iWzxk'^0_`GpYA* S$XiJJi> #WN1*޺ohK cŽU5&imdMo{< )?}cSփ My$p (m,OP3, >Y b\rǜ)9 zf]ENRrդݞ6,TE*:p.1؎jy}F)A:*}2}WV[خir H sÌV/-e-UI{vv?2S=Ӳr[Oi:p7Q1'q$=y}Kšw;K=L؞X{b1qJ>²!Ν WuXoKc@VnFH957A}gwzIp݆>bA^9rkVSkGw1n^H="+{4L~b>5uPٍ.[;Fz 8'IxۻM> VyD grѿ/u cF$Yzqϧ_UnwyiGZnjvNh SEybIǭ}2Oj,6$N}A]kLZʑ1رy:WsE{4v'+U[[L;" L sq^i,F'i?F@BNr?4 cR߻\xxtY&T6A^gȷIij^+x-8B?8[Aj)Uǰx^=w2kj(__?Qgۧ_Vķ6{Rӈ6@go[-MO\vLw!k.R!?}Gi 5 rbWđX*[-(M) Ozo E#LR zXTjahg%;&{3-Rƞ/[84*e Xzv[j^(b=ʘq+P=1Z?[x~oCo't8CLse_鶑I xT~5uTĪ(z.n^|D [ *$rI@ tLG2>q\^7񥧌-6l1as趷wZmU6D]ߕ+*ҍ50ETr_Кyd}qqwn(4Hg$XAۂ8xM-ݛnY}@%{jz3]Os1d8<Ҍ%wQv[_#F4L 04co v&ncabxMouk'U%HzpCE?J!F*98l𪤚Hz8y5M?7R2[7hQst/g|/ÂmoR򌕱83@Tyz ח:/#:E7p<#޸ wͨQmQln, <T(rjGVRqv~ xVmL\g3"zǏa-~4mMN z]+}? '0\~ʼDΖsŚ`@XA }Xyw|aQIM5i"Ez.2~Nⷚ4X]t.V$Ϛ[֛̅Ԍc>ޟOzS5Fu#9{L7LF;a.Ku<|~̶Z[gU򄜌5D#r˰ey< $\S$k3$%yJOGuGR+=sT]ѲpJ/~ j!AҝRTg5x.gu w~5'<9Rvɛ?MhB24ʥ9J4iI"h0w 犭⏋ D+g/pHd\ho3w75>kRIďO=q#zc_R<ه;ZiR^Ĥf1Ai ӽwIOqNU929=kv@}s^w W8Mv3HH=zvv/-Pkk(k$ =>ב \ (6GjI?zO٤O0z \G `'9QX El?3W~ҧ+{XPg?w5N09NkxWmZ+&E8T3~u3ӼX~ weQ@j⛍WƵ<22 yq/;O^vZyft&C9)aKJk[뇎t_ i%ҞgKk"{ߕ[Ю@B|dVe$t8#yerܼ_n?r\Q#Jռe+[[mYdvN| +͕8r;F:k2RS}it$r~e#9>$uD&d8>$#Z/%[IH#8\Ԟhi>Դ Y2vu+Gs[]ErDe A)SejdhZbe#Y` ?9kA$IPh ]N9@?^JKȊX܋m$j6T Br7Z6*)FN;Z֠ m퓃Lr},+qP]|j_Lg7K)yu4p4.z\54JS5W̤.Gα#a&As =Ei Kfryռ| (~\e{\|Y4m]:nV]ŀl{*i,x^?@oFWVG4њVVoЍc9wE569I&R$#uυ縶_ZL3HyN9޸ۿңʷk ڏXvP?SEEN~5_O_` ?(;3Ԏuo*9~EvfkG%(g3?}$vnԏQEr-ŹTǭ?ZC([Ksٴ/fN?fotoxx-18.01.1/images/batch-add-change-metadata.jpg0000644000175000017500000014711413222767271020451 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 383 654 0 C     C   YM" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&KdUU,G$iVSyo"g(oRA"|ć#zW^7}EΟ!24mOAASڼ,vaGw/|(ьJVmwj^d G֢X̒;kIoEEŸB@϶s^C;S"HWhf =.!l$&/*Gq-Gy]xLRUR?1,{(]ȋarkccU[i-'`XG:H# yx|T(c/ӥoFG,C[E+^n\,lyru!_jj,tĚ4WFK c7]֣Ήqw,S$N,Is˭}'Wy|{//oKx>Tk<4&0@YHC IE/EKOx3EJ -"0`NvJO2m}slk-J6x.QX4LX s}1SLI>#vR Ӿ0(潗<#+}2=i v;ԣ{Xi)Jl3%->goOf{_Utլ&A#ުN#+~7>=O[úij7(IUi]b 2:Xx_ > |YF4-#!xli&xfc8+l}f@hF0ުN#=qnlrǎ1޶H!cg%1D%$[+9lnl4VO> 3a[? ׈<[]jXA%~ q"OXsO( ~"LG?|Y/nt6WmZHeVx!KWgw|:;-KE#D\C3/;fn{ͥc껽FK"BTzjH432*~(7%ix[F#vf8pfG1/^=qjapd3#|m<+_q쿐O+O:׌,(|T-/7?n՚%hw2lHV> }w"Xj7֚=ƜX#HŔp:&ݿB9?z9ǜ_Q'Wz>G0X G0X G0X Ҏ`.D`Jb^cj2\jX+a?Q+**5tC 0sMſx/v 6iYh&{E'{]mȣ6?VQPG-\DԪ'fLu*$𹕤PZ)W>ҺGmȭ9su碷wZEdV "aI Z }x|91#sw:=Rs{k|X6w }*l.&1F.u,CɯR }x|9s/_k- AfC;mM(fs{o:[._[ԺxTν$h;{Gmȣu7Wt8tE%lU[$K矕3qZtgƚ_l4eD$-e'mȣ6?Q:yŸZֻ]h\QI.fh."ÿJ0Pz -,4i}ʤK#&o|aŢkEBEm2х*G*@5x|>od FWWx|>oxK+]1mGFTg73IqJkm)wxSL$RNM!x.v*n潿6?Q(i9>ië ZK&9!)1hvyx>aXxnK [- ۥv={Wq( rΎ_ '+FtxNIxRq <ysG^h_k5fMIJ- #-*a^( r0G-.ow%0I,O0ѻZΟ5K k;EeEjWq3ZFHdqׯx|>oO%ۤ(QI@r]9zmȣ6?QÝv)l`E9b(LB(((WŭO]TR*v޸V`~a+kǀ>!^(mtηíͨ/$BhVO6Hd]_AKfNi6sx\CɚHl5?"8fAP2;hk {Lo϶^ӴR_Үm'Vw.Y\i w0xVG/m+\RW_,|" &Aov2zO.^/ZNVe:Vy<--y'fkty]bd pf%F_ԕz[𷏴i7WZ}Cg2,:;tnיxGo<)iUmm-Px!{ ʹ>G>hvm q`yՐUr˼)o7Zeu SOY[^(.,o%Ռs#2b;3|?GvxĹJ31\ˍ* S\hW_,Z%բKT؇OAYWmkI x@K_RI ɦ;ȅ8o++zםM?xSQCV4-BOr-ro0eU]Ċ-m_>:}TX٬lRC$+cNj\jz\m^5}I!w؏ MXl]O[ B?x{R[4I$[Wʴdf1dR |r&NL]%410Z#m[K[Z _u(8|(-JX.D6򄋔!+#潮 Pꮡ 0A5.[Ǻw ]mM*Oҭ/f6tkj%vs^2K+A9Ulr dgRv_GԒ(0(((+O<-1:+8}O~*c.|5L+4Q4c%Cr@mZ<_mc=:;/Q3̱:9`d$6ATaѬ!-2QQ]\ĒG 85GK\j!7#ʼo;ŚW> x U. g!/(G-n$չ Nj诃>5xT=oG}e5'jVutU乖M6b0WϏ-KGM΋ygeiv-'T34EӖ՛oԮW>g|'N7LR 5da,yE|k+ޕ~-F:e|1tZ{Z8N>Z?i%K5=ON߈'I,oE+`'#=)54oct?~&ukMJ{5#Hb"a5_j6NO}x\m/u Hit4q&Sr´a/:mRcx⼽!o}5QN3ɕek mY?Ԙ>}ExO_5+3i׍cl5mB)rP.C'7>[YZoW^##mhԭ.DׁEkW3W.[PԖxǜІ d hb}H:/l4]3iiֳj40mI+N֪O1|RI8EKTuOXG˨i0\^izƖye G K22Еn&{>u "K4&33JA!=XN=]L]#'b7 Kp$ǘPxCęuw[Ϣow}'KtPVfvė% )Ahe'"+Z|q7<~>u[˫{d;QAv>Sb~ymuk&2ETQEQEQEQEQEQEQEWi๳c5lܱu $ix"]xn+#qF*3~ "k2t;xi $P@$W^ ⯄Z't׈d0~H$ul")&bq 4֊kMoo>?<xWNӴ OrD}q|g$=;LU-o-&9^Q Z*Nɣ[h>2ѵ,:uΟ 00Dځi]F v4Ohז(jZ]G}{y1Fšxbi#;Ń z~_}5"?޺exqsWX*K:o63y7쏢j|Ikjz{uM?SkXf8΢Zy#ݓ}ީw9L?^HEng`!D^_1MN#q(bWAE!)EFQs)`L!{X>:( CÒ2Z#fLÌֈ- u]†( 3۱ۻ=;nh-sWH*ݢ0t:o6 Mg7k$D|7Fܳ?jlp@.AhÓ1ajz߆5(T_Om:ױQR3/>"|=lZ咐Vki"R:a):Li űfDd'*(cè#ռ)X$݁ C?6? m<'5+A$ h+h>%^|:.ʊz|`qQ|B{]~/qqm#mbxLzײQHu`j%fOl'+|Ƕi~is9{Yc. ~$ײQ@ë2qq3 VR((((((((ϯ.R*|}c8=|t^Gsĺ{x4dKpe?.U^\5eu20o@{k.xFOxU8WY_55$wPr7t4֟~R=KAxom#Z,|J#Qmge C$-ʁpMr~ \{{ma &{lincfx5Ssyggz-ص+_ )T]s\41%IUvD~$A x^9eu_iF`2M*4ra Ȅ2Rz[~&*:}_>)_&_PmsEw!"I&˻ @Ul׳xǛ<1j/׼%YEmHiq׎(T*>^qV'H?> zGEQj+i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i2+ Ǥt_Uxc=#5i26mM#+(O*'H|MƗ^^o3}*_j:(5i2ϐiC?5}dFD딿9Ϳh㻰h&%D`9VpAGPkf4/zZ*((((((#K^zgpKFdQt1P <ֽ| W:/N}chizBKp90+-&Ve-xƺCx'xNO7=*iqff޻0W5'/?}E|5px"Vd%ޣwqw6;B+X$[39լ-tv;K G "4%lldRݒ}m>#V/Z/{F4X絿d;f[?=on#4A-oZ$h7$3H pwx#1&8T<B2Zݿgέh5XaxGY}^{{ iL,ؗ=wm~_z/]jڙn䀑~*fU\*3bB*J ( ( ( ( ( (2񶟩Ɩ!ēq +*k%[v2"+Wfl`-ov0zXþ2<+^ymfV&1]GI^,E{e&YUPK[=4ICXѶpTA}ZXt/zVsr꺢K%Hm `WWȺ_h36sĚm~#}[=$۫ϔ-loR7u_kNh4}^{1k+HRN0F{ y_>B['M>6[BGq9),^ES$`wF/i(5wzƜof.ddȊN0͞N+xXo5/$jvyRx1tpvW|ݏΰq}]O6ݓx62;xBA@lr"0i}*^4mB+wl`c+e{6xH+:yAU R'㨮ky6K-G"HWp9ylB__c[U^OOs4n_ ZjXs.VCG'(sn^i,iF-hye$mbNӽ{˟K:Z셭 PłX$?=ROI}7u2* ˂i,ҊWx{~%y2A&h4+`ױ>`[%~FBξzڣOmF Dۇv>^QxrŚޗS^)| (&6o 5K7WO_{rK:tWZmUMb͐qf*1_7r_?WaO>y+W_"boG(C'>|E} W E~*ޏQN7}?_9?UAg/o71_7rz?/G_?W8+oboG(+W_"휿~?q4W~*ޏQ W E9_dhUA_9?rϧ<<_*c|ɼ, "NKE ;/0z7n'_0i)H"Wν7.|ai__xn~ky`:y7K;43ۋ8Ox'Iѿgx*ğukr'yVY]5G#IrJ twpWM-{X/4$QO`ӿR_=^3^2,/w×mco:KNk'-; ѣ k` LxosPtxvc&[H]mOG/4$QO`ӿR^?q_4NJ;?e׼j6֗ܽäGD% Qώ?kOV^/^-n kiCQ&op5N,8u0}C/4$QO`ӿR]sDd\W#8>⦪j ;p/ rU_Z]dp3__@2u$ȼm}~]3|&76,}RW7wňg;B/$j ԒIt- q.6tf;HW{E08}HϬi 6tV$OJ>ct2 hQJp-[t(Β[ySH<054A$WuE'_0i)H"(/!>"NKE ;/QE  w _('_0i)H, O`ӿRG!>"NKEwTQ`8_B|EC?  w _+/4$W%k#>'O+|9[IԴ\"f6ܼWb$jY`O~y66Wq:E5I@$Y+t^~֭~i~f3t;c63돴uOkdִc՗F`O@?*>E~).׵k;]]nc7s$;9,^M8qZP]blz&?K(a/!>"NKE ;/QE?Z>wmzX-@AKe R9Mj=cJ`m?G@^򥅄1zW rI}>mJ%"($hv)M®i ~W!'/~zޕwxRW:m?в]8i !ەwupC񭧉u?Z/[MkO.P2ml X&~ =-QvoE=#eaqfI!c:RN3_Fhhiod* یTY>}?v#O>^G)/=W>"u8jqx/:Ʋr#a oS_WQE HqoX(((((((*Ahzԍ մK"0N@ ֯VwjX7ywlޅwc#8q@ҜAq%z7"&\^sJ A_+ѿf4/z:}SGԧ򭯯5{yg0{pkN ?dxw{>GSxڈ3yvcFGĞizԵ%{dwڷ,G@+Ӿ$~ڦZ[QS̱@4DQݘ,K)$c9]8cT5Jf𕗆6"Z֠-RE0OkxB5qw;/tm'Ze6.$'fW,b\)$ 8V)/ЛG)/ЛMht$MG? |sLgE$R$.Z%i;ܞ)71~jvwy>dZs{ hdYwc8f߄7fMEٳ_uKUy>mZ^xlCKnܮ+P@’ 'G’ 'G RU . hԲ?rBnQBnQ=@?,GWk%tO(%tO)jYƏK/' K&Q K&PYe?p?,\)/ЛG)/Л@gWhԲ?rBnQBnQuږ__R~5’ 'G’ 'GjYƏK/' K&Q K&PYe?p?,\)/ЛG)/Л@gWhԲ?rBnQBnQuږ__R~5’ 'G’ 'GjYƼ__7O.J2}!ǭwBnQy犬-oL;*F>`Ii0=3ƾϏ%[Z&ǫYy75eRo\;9|7x[GtMiScmE,@N'vKߩ e!-HoFIY]5;1/xMb+inm \k0F#$NR^7D(´[o+t iM5­\IYZY Ff9wc8WIe?p?x77f#<-6rvt yDO)RUN>HW9h/#_^XkHZ^(b8i!Rn=7ږ__R~5’ 'G’ 'G?,GWk%tO(%tO('S'n!Ѵ2~o[icH"!n3w@\l潚%J`\:^XJ? ||>/h%^4$loYZ=> Tzͣ -)9:93_<|yh>YkQ AWݮc_D!P]ԋK~c&,Ő]b O|3WxԭuK->#Kז]IJ$x`dAo?~"ho5=G°7+[[\ɠG!FWȓtL/UȼhگOᫍrY'QS$07)T߫?Eŵ˿E_ϥIЯo,s< {Sn6VYHr3fhKZ|/SY&Ika'0L(ʍ$o뭿1/KG^g~(j~ SZxr]ޥNpmT$Axd`͹z9QM5O`) (((((+7:k+9D76 B;! I9Y&[ެ^ͤi׊_+ѿf4/zSG=l߳OrG= ww^ռ[:xEg6֠GNHsI|w;c>m7Nsci{5rHgFIlcв8/U6gBO/V?QxIO|[ )yizI6{r`J8 Aז9;[tW >V:kuĴv1yMxwmnvJܤ:C3Gs bXq/o.j"IRE ,1qbn7Qm>hYeY(*=[[}b[qJկ뒑R>o7'V*|Њw|RV\?71e]<{ѾSn-nOOp`а1uI)H$M;^>v$Uz XHiRM|Gtph_L$> K䬥֌<$6܌cp[~#|-۠|Rּk;6:iVX.͗KDQZKu]xVM7Mh $R. m_>>񦁥7ōCW _! ↣m{5Lj?Q1y\ o$[mHUDՈ|5/~i ?Ε'EΞMg&ʎtnTn8ryfMu]诐x"◊uKL`E3 eXf$h?/~jSҡĝXX$F|LvUoyǯTM/7>⢾Eioo>t:w5YtO8Id[Ͳ&pn/i(>5-x'7'M(tN;~ z5QL(((|MwwaZO [yhzҬj͠oU1+YZKr#c |YJiOR9`iOR磡=OlI"|zI"|z(((((((((((((((+|{"on**ȿ[۟ ܇J((EPEPEPEPEPkTi>U=cJ`m?GʖG]}ko!iԴ4z,IJ!!LPapH_룿>חK'ÿ W4w6/jxsQwd ;;3^xJ]SGV CqZ[$C[E|/?ox˧WSxwOt[ zm2K3uih þKNx[QO ^#G-XS]t|_) Bv7Gďcg\_jivF Hc22rFw< wM# "4\lL%x;r@N+Ͼ1[p|C'+'@B+$I]c\͆+)[yn(:_>>ѵ}jKZZxd}D>\ bAM]~)x7:?dOmw @. :c8?\1Svw\dmO Mټ=ox$2KEy Eh#;>+|wZMzu0qMCkvGbIXk9Abp⭷G3wKh 4xnZ+wm9ʷ0w1ޱt}Z֛]E%3٥s);)Exo),ሴzņl|n%Y|셾]8_^ !׼7-85]V_[i[\̇l*ꪥp ↭+y?CMF/(Ɔ,caQ2cT(hQBa䞱OO4" 0:((((*ǡhz,m,Vv\:/V^:ŝϨɧm#ܣ&%^|QJq%z7"&\^sJiOR磡=OF5%m Q;cIC}meF7gttaA =S|$>O _=vt8oGdž]??1Oǫ}?<5aA =]X?1OǨ xkhpaA =G~#c_.WsE xk>tz(7~#c_.Q EtzGdž]?,  }?<5w4Q`8oGdž]??1Oǫ}?<5aA =]X?1OǨ xkhpaA =G~#c_.WsE xk>tz(7~#c_.Q Etz/Iע;IgHپbHv=s=?A&t?w_Á3'OGdž]?t7~#c_.Q EtzGdž]?,  }?<5w4Q`8oGdž]??1Oǫ}?<5aA =]X$W5˛K-uцf9[OXOrtִ E;X˿y'O^8Nh1h^=u(t\i̊G;>ى``557ӯD.$P>s\ß٣EMڴ.u}376z55`bEd|*ت\%FoތN kS4+Mh|nӣ3 hT:c;BiLjo}TLD nM%"dp> Mu~ῃ𿎯[_:(|Ml,G3&)9湋GN7 Լ_LvM=ɧ-nJ:ml?+K_zw퍡j,2x[[<,oZծyp`(bRw p +7ƿ^>xS6v5C6fA~[sa<+_z:%ӼOci{dl4m-ڗ(%f/ .24kK|wm?> `ҳ t' ?Oº]y'i*/ԾڕΣpnk禳9H?R|7=5]`wW WY禳9@•uw(+PyEp|7=5]?Jozk?:Q\)_ MgWrRwW WY禳9@•uw(+PyEp|7=5]?Jozk?:Q\)_ MgWrRwW WY禳9@•uw(+PyEp|7=5]?Jozk?:Q\)_ MgWrRwW WY禳9@xwH"禳9\Co{Fȱ!3JҸbG園ܱjX:Q\,|;4#InbXY'IM+S?R|7=5]`wW WY禳9@•uw(+PyEp|7=5]?Jozk?:Q\)_ MgWrRR?}\ _x[cd-oa U̓&kIK8OL+vxϵ| |9>#Ix'L]G\_ I.aڋ7k}*{ .C:(.Wrmq:Tq~7<48^,T}I}g,Ѵ4im)>kw10<#"TnooqeN:~wzjze^4Kw@}+vÄot<en8ҭ2Fkgv M^ײb[-K}[&sY|U}9{Mw [\PnQ"+ٗ>Kw;Χ|!HM^KHȀ*,` txPw?*xCO:~btUӯ J'*"q s9Eд='Ah4]>NFa E;@=M]!/{k +OAx%|?W4Y|CnW Fy2cpP xO'75iZ"{94g+`9Zz;3r>#MӾiwwꖚRǧQr8wLF2FGs %C(1F%Q@Q@Q@Q@Q@Q@Q@gxI:M0KYmݻoBFqY&<7YJ!%,Wc#G"<cUDo?J_~\?X7_տ#wVZ]~ȟ'u?/:E>ݵjZG½·*r~yDž$ ?@u'(o ?OQp:+o ?OQ cuW# cDž$ ?E먮GDž$ ?G- I"Q\- I"[$E[$E<-A'I.]Er?<-A'I?lx[O\lx[O'(u'(o ?OQp:+o ?OQ cuW# cDž$ ?E먮GDž$ ?G- I"Q\- I"[$E[$E<-A'I.]^+sW'+Ηl@ew%x<;T3Z֯/1B,-sUY)6$d]7tϏھԵtw㘅ٚ )tȅN̨𨘌 q!BfVKɽ~ ^ }ii^Int\“[#Œer9Q?-{'_ .mcHChV[;) xKI/% _-LW'_㾵|o<{?u2jzWo28kwU Df-$im!v9fE$JK_&5l$|&/>[jv:IcsjԥK`ķ&} ^)m:x|*u!e߃t=3J՚aiP"_2ų1_`Kh}Ѵ ]kF}fc+09T#ejzjiVy_b[}*8Y'bb9UR泍ӯ{l5~g;u;>;ҵ/ ,VdIlAN&QcwiEn\<;Zj2Եa]>Nt1-7LF3p6j+ڋ~5^O-|=wXi%/d gS^ZwbEKYlgUWi/1PpCݵD%euW <7wmlu+-㱆1tpZl2~e$gϏ|Wt/PԬL|?eM-A}˃4;be#E=FTY~X<|/SSӯ[xN)&'yVY#Sq^cZ~5u|Gsi,_McY&Y|>iX,3q k~ V=>I+N=Iq=OKfWF8^kMp_u/I}HC% -[$q)@b vi_chuOxt<5Z鳛%fu-X#h#)$PzX|Gḵd o3ͷ$>kr}mm_FMկOc( x-JI>.Aln-|'pkIg8/"^LеBT# GG2 Iru/_0|ni_LM ð2kFKuYɅ۸Hr޻_ڃ-?z:=OUuH-dF9VFWVX ~"鸖^OþΝk^njSH&k|ˈTT!|li~Қ~Gh MrYYi JZ_2dTVVrv 0o KZ 7aٌ׊~\|,5h^3uVBZ7O _=vu|$>O _=vt((((((((((((((+|{"on**ȿ[۟ ܇J((EPEPEPEPEPkTi>U=cJ`m?Gʖ'P6P(Uoζfܫ}1\g75/Z:qiF/gWPخ9Pwi7d`qm!k0b28M|^&{Ꮙ3UN ^AAԴ z5YxX8u$VmlO_e4nǞ4axR[{6d2A%2FZOOE6A>?./߈zڭΟ=|TZl\p%+ ®I._ ~ ]ǧxOtie<Hy9|z|IDCt0u;Ak6Ri 곴}&Ĉ]rkyKv?1k,u^=wYs^,z|:{oL"Pb2sxIO Y[C_Yi7_Q\:>|;O| j+tc[-quٶZP%*߼pp%k[~7?۷^|smk-ڵ",DHrwbT].Z=|=NWⶓxJΡcϢSGIgfm PF u$mq!/ɬxJ_Eg_k>ԖiZfܞ2ҩڜpsOğxQ}=3O׈$ƙ=Բ\D0qTo'ÌxOߴ;\ׅtm Eѯe4\{qVYmAԩ# n_NѣѵoهOQ-`[ⷈi~-S2|-1]6]_s KqXI٧O2vǒ+H;|P$ C^M1dn7V`#J4u%)(+jEwWEM}Y{OtZ8yьoc 'd,ǒr7xKx@$_YEH~!q^)~:N]|3i ƱM+EUJea2'F+~)xğAcy` kxo4ͺm晫&XLrM)b5;>B~5g'φ zu1Xռo73̘+_h!dž===$kKT(FFyTՙiKm5h-@KǙnc0e8#3է|Gk>)f'@XN>~v=S'N調׹6\?Af2vPLyVX*uo$ +&r3zTw #olg;OW|ͬ[ڬ38E UlFE`1>%YYmA˩?f(Eϖ&i],àPMݾZ$OjhװyvLpA(gghkkm\JCBω)_;U'a]OK΀8ω)_;Q#= r蘒sG zn|HOBܟb(B?۟?Sпw'>$ЧO1Gď/+8ω)_;Q#= r蘒sG zn|HOBܟb(B?۟?Sпw'>$ЧO1Gď/+8ω)_;Q#= r蘒sG zn|HOBܟb(B?۟?Sпw'>$ЧO1Gď/+8ω)_;Q#= r蘒sG zn|HOBܟb(B?p'k!(+s ±U=;7ǿ/VvH""#$`ua#= rJ)n|HOBܟbω)_;W}Ep?۟?Sпw'sG zQ@ď/(B?wP#= r?>$ЧO1]n|HOBܟbω)_;W}EyƱuW\N`<;:pw^O|>ޭ<G$4avU9Ҿ/|)| 4мA79ԓ18EXa}NIF ||'q L<^kK+{Aa>%`JcmV?뾾Ze{xCEaqcLr9e? _J~Oi,m ᜨzs_/OJN{wiNWt3"\ipOQq[xng/~k_kMD65y!`U̡Ip't/4ϥl~>-z IⱍG|%S33I!Z߉4iwoRo7k'4-㍚E2(A!azg |x[IۄbivF h p8e|5>xxSVˋM=[L}Q0HVwa)8Ic_MUŠ(0((((:ŕϩ"ɧGm#ܣ&%^gz\zZ*i[H,ʹ.yWQ%ox/?.?5m_WQ%ox/?.?'WS볯(7{ȼM(ږOx%{2N2yMtx7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7PoEqx7Q ?vW ?x7Po^+sWw ?p>& |FyZ;7^IУlT(=vIķËbxrs I ?0;z+o{CljjV5夢?KsXQ[/ #2/rykt<@}u.wa]qj~~Z {/D|lqKm9#\\1A!́hRǁޅ{ eڟV~SQ9(]5/.sӒ5ڷE$S}'CO.-`3^4j/$1@cH)Yb)s0|SUn,G!_^i͛ΚtW2:O6o6>^=3H^c,!7!x7]OJχ#-m]N$LdGil`DB s__x 766pgeO1 6R=P0(((((((::}čݴHT2W;ĚI;iQ JWvW8]jڸI[ ˏkcUD?J_~\?KOg^g~|95}j4WI׳A t/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFhkg?iѿ>/*YFk|{"on*YFExWm͌{ot-$IGc_ApGQRaҊį !*(Շ?OYF`tW5 34G,ӣ|_U0:Z+tog?iѿ>/*:Z+tog?iѿ>/*:Z+tog?iѿ>/*:Z+tog?iѿ>/*9cJ`m?G}f^]鷶E~}"n^###q[,5_'K9K[yg.U! c T3ៅ#񎣯Mi-Υ}<#&ߥ>bw1"G&E~َX$(Yܓ&վ`AHaڸ ]|{#~?BkMI^4pc Q؀0ˇ;w5 2rOSi'ύ>#YjVhTӵwе[k;I23m7R+mm$ll`nd x0Եm* *Kk+;rBF$H{o1e}k+4X!gʮ5P U]3r}x7P𝾋9ƯO]!?#|ەf%L; Ư7i!_KKyfKxD?m~v'~g_ZS&negR>mpEh~w4WZ^&jPإh"tWIUؒw`q薟I@sOž Ŗ67-ot5FLڦU$>Q/g^ o #ip[{x!2Hۣ$*~xZ]Aq^_Y[WU($ s{;?_&~iO33ů|C~K.hmCM 7F$H\WßS&'mHMKY\{VBcW]7n(C +:ĚEg\jPk|c{$d` gyM&źgeo亷medhɔ)H ( ( ( 5֧ZO&Xq]2#G"7noV%.gvc<}u<BNO?%ox/?.?kjyVZ]i:cjJd%v _N+oH]W;D?+|w#zG?F:hL F: t Ѣ3?e7.+FoH]Qޑ@/O(;#v_G#zG?´h F: t Ѣ3?e7.+FoH]Qޑ@/O(;#v_G#zG?´h F: t Ѣ3?e7.+FoH]Qޑ@/O(;#v_G#zG?´h F: t Ѣ3?e5?d,C`3ӒO=M{xo_ERG4t ??e:QL F: t Ѣ t ??eEg7.(oH]Vޑ@/O#v_Z4Pw#zG?F:h@c}i^hc*z h=cJ`m?Gʖ.{{D K2)[jms:rd|+ƟZk޽΅g-jϛ'ɪ) *0 _Zi:BLolmʻg5GT;Rݟm\SBƉrѐg^_;j4E/rwΌ#ݴNX.ڏ3d~/6\n"-_˜|ğ߉_SֹVd15v!x.\<|;}]W zNm:*>S#Kqߥ U>oT՟,|C/ dB}ZZ[;f Rvس3N9M_GmX[WT:(nY l,) GؔP^o:V/ ~̚^xOUZO Ny#_Jq ]cC-k!,:rkMJmz+{2)UíPcs5;_`Z+ CIn*0 8 {EwUx[ZOj2ԵX|=s=י#5н*^\k@c )uo+hmSZܿu4K_AuUS0gq Dp83>#ZAEWS&M{7~!$Ss*~<|G[Iyj1WV\ndL|L<Dkk^ޝ)šx!(.5K=9|Zb1pik6ІBb($zu'5-Re;;zibZn6|#k&S\_2}#Gk5hQRH7l_2E<3 yboCk,?dGn̻*p<mo%[[t5kgǞ0O.xVaWz{C6e,6 --(?ֺ#I՞lzƚoVԪT ,@X5wBoO|W=G-O; T4np*l̓KԿ\>!5z֥j=zRep̰lX)a噉?gJ/%mo>`)QEkۭ7z݌~uT%F'$iVw5cxwU!3YZr#'!lgq@ w!]_L?J_~\?X7g:%yVZ]OmCԵv[,dyU'YD?+| ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5,wP ?N5MC.-ef奴*@~$<5=?A&o?/C)_j#^~DVMcB\%blFō[ٔ>Mދx}oMִ;Mʖi%#I$7PjX3?"7~'Miԗwş[{K?s =0Wu>_.tY-u6cz*Syv?>6XO,[*v+NT緗l&POtœp>[ݿ:ğc??I?V?.Ϥw.K{mlSlzl_fRu}s[izmN2ُW͟$Q OȟEv}-|oR1osk$b_2LR|qH"Un26=C۲G3?"7~'@]K'gۿ󳞸>qE[{;[8SH逖{| OȟE$PgW9$[MPkN)qubE=Adlu{v^}{b^7dw^vsgٟ+fՏD(o'XO">qibK=쭜]O$\UOi4:rH6bkN)| OȟE$Pgzo˫dPGpN{yv}&|Qdk͟k8k،=kg՗D+~2MWg.BgCW6$yn5Giʫ F|nJ 1m=2u?k"4Zʺ21#rRb='WS볯(MKGh4v6\Mr-YZG Ԛx-[zm^G?rտPm`H.6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJ//>y%i E*Șǔy8 >~>6_j?W/ 5U~ '7_J ^~>6_j?W/ 5U~ '7_J ^~>6_j?W/ 5U~ '{wInu-:!wDR8Mjc<_|l1I^k =ĢPJ) H'22qT~ 'ןύ?U_z_ > 'ןύ?U_z_ 䪂WE04v/,c3qy ?W/ 5Gύ?}Bu =:H YTDBLCC?oK<@_|l1~>6_j?O/%Q?oK<@_|l1~>6_j?O/%Q?oK<@_|l1hJ k[[Gh&xd{wRX+gߠ((((((((((((((((((((((((((t};9t/u]Eqײ>6M}ȗCKx,ԧ-6YrʋB0n w: OxNtӦz[E%xnQ1GAî+uW>X̒^7J VWNeMkkp YLUfc9&;_|Qk%u/ ]Gc_|Kk OPVխy%ԩUy<omM;D_GfJ}+e 3aRxXSx>,|{ *k4-J>VXv]$To1M%~7_Nk?hvΓ^Κo2\ mv1PHlx^(GTGխ-di<*y-8wWͥn-R-Tjǯ|C4_ TG4W:Kh/ {]ddOĞƽćevO:זj|+(^m,u6 t>V'.ǎv]O O:u{/ xa|E?uiԖ&]efFۓ0NQhcZ$Ů]ZڮfX4%nNtz!xj kziv}JB wy fKDdvI|մ_ZzNE>Mfk4ߘ W-t0P=fꕻ[ڧasy\}Ltx m9 O# G^wz7-?ZN͂Xf2a[%:y~ 6=cԵ8Ey.!heR:A%FҭEkiw( cUio@ΓVm.?y^m"|ƚ.B)3K)6,K98^'CImrCz:xY_[XoPqsQԾ}wljx;GmbC/}pZt!*V"e|hwZx9-?OŇ.fTH1gslOʢ3egW:4.tpai^#0ԲH<G aOYG//mVoKr˃{o/z?:.۽w@oBEK$QdHmG̀MsVfbmgn?! ?2#wo–F)aizu͵cķ7{y&VNmi u2l<_hTW~ݣH<$i$C C;;_/6Do? {ǃ5;+洒[I S+<X q^˨cqns2*;2{~tn[jQEHŠ((((((((((((((((((((((((( E\$NSa̠.:08n@g+cC6?[gEq mhlf;o3]¶?3k!"wVZ =l@3vPcI[{Y%bI mt klf;o3]¶?3k[vtP ͭvfi }OQ[zCty{BBFQں(+6#nIYC#}+[vtP ͭvfVmo5@g+cC6?[gErWoẗWԵܑ]<[R5]EPfotoxx-18.01.1/images/mosaic.jpg0000644000175000017500000015712413222767271015116 0ustar micomicoJFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?+Sº.}zH2|#FXa88wWCf\lt3&_Gֹ#wQ <W_rG֏Z]?:>&8QGրCωuu(|M9^q]oi>mdiz]֭u t7yhppX3Mkv.o]?wTƟK%ƥtMzƢ e279*@;> ?[4YZ\ڵԚDqݽ̌*EPK=H❘]?:>& ~>'@ţ[jq\[:̡FwYH~lt5߇ß6ysoi/SRk9#UbV,9MQ[y <W_r]?:hm4fXV4- }FRL҇0޹z'W;^o 5ާvibd!+qpjBnя|M9G.o]ྗOKj~s&%VgL,ayV60KI16C[MM+nCωuu(|M9Kυ) ·wƷ&,Mc@1$tI==k[c'di[ӼAOMam*,#G aITq[dU5ggZy7G7_ۦS%Y Ȗ1uGZjWl{c~4=޳#rث9-YXiq9h1){|b /\mV ϳI\݂ ^7zw>V3mZֿg@axuj횥yU2(e@ֵo_XjGh?vN^fOX.?Z[ԃ➩o*3lI$9X} 畾!^ջR'` C/aRndTcêz\]\iȱb}.ȶ59l{UvPQ|t6c6w oyЬiu1`;~FxSMH77195-͖ŖMN)Œaoҩ_j2~TpBnȣ'OK⌭g+qB:ep'@8k{/YMv8瓁\:lhW2`2}ԚU7r5x&Tm(SКylk;Vf|aߍkQ6 o39 \np*̩-pěr]Y`Œ{WtB+)K"Ưݫ獠iJ7mV;+Yf/.w犴|CcBww&wk@!돮:W=?ớ)n` ۼ(Uyq~ҝ('QO)_ĺULBO@Oosz`#kbi6 ~ vΦI. 4o"NEUDƛ6;,[#*9$pzjh;s^wC U/k\NzFc\ @xz;`fIּٕZ?z^2[ۼ+FOAOUml,LC2~\/< xGK=QG^s#[= W#ܓJ&WaNAm\Uy,^Q =99mspg{ 2HI@ąBwݳw|tQ MΣ)a^=Yt[mcn<J_Fs>oPAwoku3i!F@ws ¾-i?[8jq%v[:[G5Z̐h{[[z v;[/#ryg!ۿřxeXr=m].Z6._ !q~5jso<dyITa'O{ufZ2'c۷5>VhmE|ÁN|̪ctav~|"A\V$ڻQQilkZx]ʎ}rsC~zmSXe}j8Yg^O9SA{NkwJ"kx UJ_m MLPnƥI,51.E$?[If\CLbq,\8c篵A}ol͈8f'izS@!K=VʠӸ2Zm-/=s#* G9W.gD/5>k;<%Z8HnFkF܁uu:Kg<% ˥aQ[R8lJW7nv9uޠyP;MTޥugq,@cGjk!9B 1vk ?78홛hڔۏ<Ԛd[(T5Z>S*3:~чVmݗ>_uw->|1BQksͩ_Ec}/'T+ȹ#:T-UHR}Ȑ^L Bd54^^yE\5X 2 ұVGKj).5{&yg*\8R}ȩPG-qLb~^r+3f]u1Ē[ >j'ܛA-df28>Bz>XSE '\*&VwI'Y#IdVAY:qҾX?.~(fATo "._"X́Qj:ki/Ro Lß=U:jg!O׾s><,DVڵQ`݃޼5hkZ]Wl+n卹Lqw>.4wjezrv14{'KuYFn:q\ף^!7SQ:X~=NO>-];;~u;}*շ)OW~1^-vjRpIǨ,xVIfH<< _ SV𶇫-fObRzgyۖ6uKw3imLͱ-N!G< r=fLm]">"VܾS.2G\BԼ ]ZޠYL*1-oۏ<+ Ø^fga98'Z4ɽN7h{~+k~,_RWku1ȮQ余W/v?eqz%2j;kLI]GRĆٜ^}L{QŒbm/S)Qxg^-Z '#agS9>4(l(.s=;O(V'֏ZSQd(ôS,%~\'֏ZSQd(ôS,%~0_Onx+ 2FF )vEO(i4'f|YB|3+PѮiC[Fl (/ŀ =OZ4[iDF-園%_a`AoξhY?J?' G&Ϙt73jC Oj7jGEmgA8ZKCm2jQ Nrq_HôS,%~vEg׆t]WfRP䅆pWi$5oMe,#um6]*qmaG'#;E?QhY?J?;6$Kշ/,]^ŬtI<:#}6-|W~;iâ`f׆؀rva@A_KôS,%~vEz?'m:akihp_& wo$vrnNzոhzVеOJFէmӴ`KbhU'?;E?QhY?J?oQik1AU>]QѼ9 ~)u3ϹӼ/'٥Qr.%dH%ǘaq_bôS,%~vE)*]FfXiH6sVeW$ǻ CQd(ôS,%~|x%Fje*m.<UǙ*ϪKw;mq<' GQd(9_4Ol>'i1IXDD=W@>(vwz~0j P$qLm!O5H$(rNN~`?Wt7j^"j@|}kn5[鏟]w =98>rxkx&MHN~vT'@k࢟{)xkI|8 ii~3MznŽG* _A őX3D&XyH7R7WыkK#X!&_F; usw_S)ԣKKU~Hc4:lH¼O1g,G?ڹKh V{V@'8HmҬG޴$񷇦,NA*HhFbFAO:tI)[ƭC@ѭ^4j7-#m95<31}^ +\Yyi "1マ׾{]:\+rL_(S OVu'aKKۆ_7ˑn9q~ls*+_:ڦ-!(Ւ@Y«`S~5<;<\F!_,/v.$&p1=Ie=t^FAkǟҴ}k6t;hQEr1B#)5H<$*;ϭz~7ў' #:Ɓ^,"5RYP10H .zwIbM .nL~fG$_@~ƌeԁMB+ZY xX.#" Wkt'9TZUr*+Ӓ]Mm>ZI08|m2a4f@'P@`{cEo<6uGZ{mYf9P@~i3\MLӘ (%xO%:nn7}S#N6 ֮Ze,AKh 4/cEyʷ+?)c8Qǽfyi% $rM?hnNRv PL4hhԂر2kugYEs%rϊ.t-.k_숢6M0}khwJӴCm`1{X.}{q$djQ ±r:SZqtmŽ]ų3F0xgZtݭ֜uKݺyg>aN?zcgo9_)i:὎IWR<#$s[oh7rm<^l+r斊RBkğDŽ<'Kj6>,[Ź>=Jx3C$p203']oBG?Ty2ԁ?FΗNGS𚭥ާq[Hﴛo2O-̍v'݌0+;|nt'W-R; 7"P|΀c_٭mkGKkM&("R^JHhߌ%Ўk^v>io4:l1")$"\l,"esb?V>$n|k]O+fpǂ#}9677:!xohG^ ϲz |8aVxg{B.n9CԣM>S9 rpye΁kEK%Zlх0ԔۑFS_7xUWk:iòݕFHd ؄WGZt.?iV16d\ƭc aʜͯR|t}(`Z9SL ~F)>(Oh 6I|*vk5̓KiKm.I8'ֽ{𗌼s}¶7wH5.I4 2E)_0X`/ȵO< xKI 14k"~bsRzWq ú-֝;3io,r#t)]XU"4ƫo<0o ž"Zޣ<B˓}3*AU>HT4ŜW*x@,>/ǂYյh)3\N' ֩Y"nӷ#Bޙ? Ai74SmF WVRHB./پG]./EFqbڊ'!f|݃cO[I}F;`I#wD ` |+K|W,<_xţPCqy~mMrgA,[ЃhvҬ_T¼7r?_DEK|d}϶6|K85h+%4饗Bцg l#w|a>*G*_\CًgF$xyBX6 ?᷇'{{ 6^J:-ɰ[jh\ ]u7G Jm#)*yw_>M*v  {ĵV*g⍖4<`Jw_ib;o-eJ`W+YöW^2!e}WIŬ5/WĶ'Xb]ep#/̸ၪݼUkZ>m}=ͮ5Ž .%6HUw=m>x>E Xdn#Ͻ]/|D{_~񦹤i7]f]*jPhQ:<2Fqy }~u&b%A(U$7(>U47k#BޙhZƛYu[xU7{Jz #DMR Ap}5<]GzAaOֽC\Cx糟WKqTtP\]d-Nz ƭaඹwkI5ƞEWq\7+1VnV{05gc5g13DcASӷ^8H|S|/dZVeo-ÉݼdY$ү-odv>$( A}e->QVo-=j}l-#0,~7z!.[JoS.֍7y|]7d-ue!h&[1Z=tm&vC,$ؽyp{vjIa$]QhebAkn8@xRFEmn֩1ա(F`9>k8T_PpCn\dO˝Y22vV^5-i:"d`B^=`-%ƿ!eY%0ʄ$c=kľ&|!}&}6){ + _.Ty j7~"Rgu y vYn BW0p”i9OahPkuf<1#)9\nTҮ H Y ,}3Vj 7vPbe3ʒ1Oj%Iּ?iڶ{ ?6(,%Bv8QkǨ 3 0+3Rx{|5mR#4Z598k[-{I"VE17* ד^omGݺ%6|˃]:wkKt2 T+! sSV4{[HK{x, @8'tI+wӧJItT֫Un1)SAIkd%ihjмu|csO`'&O4͖PMr\[ı\ $׃Wmoۀֺ_2ˈ#".rOPxXyUO1k+T(|.Hg&gX]t֮5s0<¾T9Y^eBK>Rhk]įBvޜ"/ḥ[ZI+G8{~E53f ָ[?1yɳa\$切/sR595-7S UuSaI#vO9)CmMDQ1tN'ǒL 33tWo|2ihI7!}Z~sN7W^y0ƚ`| NQ$>ӅǓ-bB n$'W;_F+&O/p3yEl؈e\ԣsNKO<+AŨŪ\k̤o8W'^=ȍJZ<+@\^Y͵+'rwDB#AT` I9SK3-Hi ^ RQ1XBR-rI w'eգXLvL=&0V5SvܹMpNg|7 Z)n# ,l鎧[<[g_7ȶ~U#$. =3_8xj}~LӴ{MBUH*ʀ _YbWK:w1ޱȘ9xtӧES Uk{YHsgco :P@8f'k^/年gB>]w> k>Bw0= y yes-V9÷y''>WS qJG?x?x{-ZR`ڽo7RZmsXxbZk-Vɸo=E%<" Q^,I (9<ڕZ1o тRB̊>5 OWǭhz7n}L6yneF}Frܱ6G*0еRwik\ݩ]KxiH t>6Ӯ5k6WZ}ǐ7;FFO$>&?׿$/o jB+bUT,]q5KV]7_B2;XYG,)U :XM?N$ו7{7W^{7 4_Tg٣`K(XHk.`hlC*pFFA} YНIn&K= k`ϝacxF7F2H$VL񕿅_Lӆ=VU77ِ΃n084\ t[QH# ;VO h &~t_Ώ9Y:/4I|?Mr_i~']mVY"R}JY- "'jdoaA _+h/C j#So pczߧnyq5m֨Eh+0ȀXwY'kj*)/,c4θ;_?QQNK :XMek^)-ΓkUH.'b"AZQp:A}5zTݟ,gҽC- ƫVӬZG589n+jkƣIjq]]FsNOu;ښAhܝZ` \@}_lm}"7dݝ+=sڽޣn. -n#Kx`N81֭nG-_3$M߲X^G3HbW` œ`/|TSxR2Iy&NQwb]991r:ti$6#+yd=~GV1MnUQWPM~G9;GO=fN[ǵKyn'JK#'=/hv:\֣n/AkiTǯJZ?*<܇3:KI5i#yYPD8܀y"_)'˧tz_W%S..)8cdDqdqkk,6\RZUalN-qKN&q;JIX jUGeA8ž(<:WƕZ,|gNB?\}Yss͞ @ 2A7}۲M̧~ƱuU{F[g0b;Y;TN{Wm>hqfVWY'9b3b{Ҿ wѫ֫xظQ1?hašrs':HjP3Ġ,rPGii =oq5|%^#Xxf#q8&]s~tE}r?fORV%oC] uf"d 9'? =B]YT\\\]|ƶ;-F4bi/k}[>y+CW連 Ң#+p>ti'h[BX)W.qNzV炼Y>]/Y5}IR7v#7?lJƗ70' ޔ^י=^jSȂ<*qUV*А֦ԧDʡ?+3B fBKbpEO<~fuC:[T"ʥۦGp}a:1y~^O]ia4,~H#Ƹx\p1]<[4վISyt}Kd3DE7_^-ƲGo1ֵ+wWtp?Z]k㇉luIJnU`䂾ֱ_,Nep=X՚iUM!m?8{F~oo3=e%f=@*Ekn6Ԓ푢y{=k<' je]bGYnw.=2? |/@:-y8qu*]OѰ8U[ }=v yVKz(kxŃWNFIp1( v[zsFFa.Oȑ蘿ʑ7pFE{֎5 ;9tTTH-#9=kWEOM!q5 j iKxz]nb>[׀sЎ+fGPa8~Ԅ1ғY/-f_1KȼΛR`sJr9k~G=B!!VgdrS. eש$vJ_iui!$pA\㓏zı_5Ūj0?uC.5f1fxذ$ewq_L˳7/ hrǯ+c-8UO$M3x XhSxw~NmR@Λh -ï Hڹ̯˃wDͣݱY̖ZO ]gfCfR\ ʂ0@#҂ l$osu=V/JG]Kң&yJKXYr3W`g8֖_נnl}/?zWֿ͟ yJ`o ^E,oB:!|"n%Zh_͇[Þ&ck{*9g$͗V?ݹdQG~%rxK-SNuΟ{⫦x 6v 9@>%ٮA #%TFT(Mx(}[[ܛiok>z_#x&n~$?.ִ9u.=ޘ!ؿ$[P$7mg?6ɥ;JmpYLVr,"dds]-~Tk_Էzevȥ]υyWydk**ZVo .אwo#xpd|#+@͵m粳Z@_ZoL NHq\ٔ䨵4М*e\02+9ArR5Hc,Xqj+@V3}Y$ G8RrsZΥi}c[ǼQF(xGJ%ִ4l@Fcx8%|f'DM2fh;J~izn6}rgC\g{WN<߼yn5YE+hRY|l-OdmQAgֺ__IcH3JFk<9g\xwIkG sfr/]旨i3qin"}hnXG x8'vOzujGOtoK3i"}KT?KZO]Ιwi w x ؽz^ KQOn]Ǒ\㞇~wFUֻem" c&ݖ4q(T|q]Jr揘5$Ǎ(K9d5x.$X:%QI8隵~'Mu0x N91MӮ ue!.\yc~EKltPFM^Y6;\qw]kMZMWVh(˖U[!-΅js\Oh7(nX𝾯ĶU0mb0 W'=[N5DmVMErD[Ip&&۵G2=m|OjE:Y&U2abN89׊?|AiMG =>fN;UGQq`695W.])SmMYik[X0iQ~dChc/㠯oSK?=~dh:ԓ@p3#y 88ŃE!g']\=8ug8si.~Ӥ LPZ$Kh!d;5̀<8!u=^%L$`ƣ<;VpwMuOC$MXv+_ktXu4EGw"1q] Kj,]\] v F+mc'cڼ#? ľ$t iDZR./NkCumnJ -P$Ǧ_^*̌4 f)-?b^>2HV2DԤ#>o\Vw}kC\6BN)~:5͇«oCxkUX$q$mfOT;sPRj6s$:}֩o+>X CV5)_R紹er$#`7}:N4ҫ:r䄴;m.v {K(,SZ:9c=Ӽ.;MBE$aG$WVxsB;]2ߞϯumh4ր[F֯6{b^ܩ\7 ߣ|7 r\2N6F,2eKUMO[s 8 \gڴTyqŭu]'Nܓ^].wW?SGZ}3h&Dm-)8\(*#+k^u%Ǜ{sy>|WQmU4)meMѨVU+G#=1Rxk}?GSI ܣq U1'0pM';{]}:ӧ*@1 QxUǃf5[՜G$l6G^SM1ԊtvѲyiNBsTo|Kj~47c"Ie-ty_:~9&Ktm)>v|t$:mѺ.緳 weR+קjs!N3,Sd;FK,t0z!# EQEQEQEQEQE >6X-uRlsol,~HĒ&<ھ?3_6rIocP^2e#AN1Rg5xs{MΕ w2ڬBdAܡr+|Wꖚ,V& >IA;;b,r{{X,HZ/JHxOw%X @xYҴP׶p;~dA@'99M^FxjJoڦjiXKGĐ:Uu AIo#짿0I &>[&ArIRXiʃN863,v8 y/侵[>)H%?.n{OwWvf$:ew%Hde$dbE+Ӄ{h;(Nn h4wAY#%◣5kׇn9z+[y+sVq=OjGwƷsq\0, |_66ׄ]; 2HqEqQ$览4qVϡuGB(-$ ~n'ך<5?!3}&fg*@c 㫇Z=Giv_No362'AkRt1OvGGhdӬbhekô/͂2q/-4Hu]Eq?!a y-Z7cB9.ͣY(Y ׷_y!Լe$ͣo2y2~Gǜq^>ir|MEcA}ye Yr7A$|xKhunfHhB0x[M/,]n|? |:kk5+Qbwj .۾Ww,~{]=$׼M<tM ;㎹CveeNpUZ! SŒI@{퇄t]ZYXEo!"@#[{Sթ5ԙ*.ڜ3K`|uFϭM%ˉ" tF8XnKmvkPWHHc;fx"'|CvZ4a#ppBogu=OuWѵN׾}Z6I@FWK_c5͛E,i q_}!rIkw^iRiL#X3Y#H3W HO7RyOg\'m n>;||mGi-bOK!UOҽ᷂[xPn4i;tbM݆~^:Fֵ&(-Bu w5<'ݽܞZ-ȘL220{bTJt?Ш`g5s;;;5K{bl`d cGl)|Z-rT3sLW7xGz&UXϗoIIl8\x:)Oxv÷*;ZVXpW5*p4r5=;@gz=!¨rI^p}jiE 0?45{mn[l*JC=idԏ!I+(-UZNmEITVGG^1{kA7[=mxBvj gWDgA׎a׮o#AgU_e[{7ƹ^=][G0a- zקGtڛ|xIVU}?xk۸$# nHSv{u^dn貝`_|-:-!ʲ nHڼoi<#j\_CQll O9,W7VܽBX+#} eMrϣ{Gn '&&4ofT]󢎓}B8Iٗ'-JMrmSD)eTQWkpc Ys$rג+VB`2g9%N`yxU qy-XbYwR;S<񑏖3;sP_˟ G{imu SU ?Jw> xMMC_kDU El#|c0oT[XYhY]zLIY"7 J\]-?+||O{_uMsR%ͬZKSp0rno0HRYq\-j:fDmo4Y'9+Ak˻X ,Ưm ܱ^^J8^/4(݂Ox[࿌t{έoVVznѵGsۥFG>\շvqB6]kZ%xͽ,HNΡgcmn"# @ձx-.owRPB]_ڊ/N 8@/ XO?G Z,?_'ފ?Կ)a?j_|~z( R R#-K/AK뷢8@/ XO?G Z,?_'ފ5ᫍ{n/c2H`1*p](?3_:|eO1XjzSDwڕK#޾?3_9|``u,O%hԙ$ՙ1ٷ,8PELv\G t8~0-ΣKim@pW Nqn} }Ys&kmku)PaW wdsyƯ-kȗq^E.׀;:1tbhߙvVovH$yl FJ$*4nv,ZKbH v=PymCڿ"]OIżгyr++lc`kxnW!`~$6v_,שRMlqrVnmmw2yJH g y_?hͷdw8''WW6sG-<~g)""OoT.c2Z~HS!K psװxBm3H夙R[gt4 +B'n sZUaU^ua︳?hn AeMUxѽ:Z^GЭ Le $wB; [xDq!SنG4' tYfa RbE)1 `2ت6XWw^O=RU- i;Cx885gMi*? RzgR]?U6Y_>QKƧg[ϫQVK;A1T:nzqC-27"hIA}_Zx~ko`^bט2v: Wxf=}͑$d\8⽪UJZwkLu(nIʀw#}+9i-m&" x\p.nRQ-5 N\1\Ye8Ct^ku)-JiUc[Uk o jFb2WVo|t_PtNM#` qz?|EE /썈xz/_V/mWIf$c}_B{kq^4[;xV# Q\A ?^Z~ ^Ƕ־r"gy=7ozޯuiuf1D̉jߕs;$y*z:^ܳ`Fi2r@#X//?7[k6cidKh! 01XMZ(폟+N{`u 5*)7Iཇ6߅^-oy$6KHZHDj GӖdAD Y!aMqm'>Tѵ N5 sG+q{&]nx.XĺUޑxT"۝x',t7M+ Wk^/7*LzW>,mohBqs+AlJ)b7-7 Drs+W6spR#Yx]JCwq ۟R0GP0J/4$fF3䅔=z}.c-N_>(ӤaUvUtGP3BHA\w漪n˟7eS''G_U?_/ړ[Xq&'R0NjlFYdMzz5tˍeWAufFp]7ƿ^ ύYɨ)nu 9Һ k"sٍv]ˀO~kҡRՕZŅj[]kLih5u =EzΉ6#‰ ?cا௥|ߥf#rF"g:W^og?κcxT^4)$}"L<6PeŸ0GJ4Ek(XĆ>~57q8^|Cm>]֣%t˧ּ}XE^.12e)AK}C%b<؜T1A ZX+3R0z޼VM8mc[fKhA" wO{VZY2I$"R?/$g¢3J^ӣ<<"ԼChl;r\U_tx? !8(@]cᙥmj=uYo{*1$nӶ9u$2dYv2V–Vq¤X;=#خPAꭢi5Am!2==)+1j+uĦ>=\ FlMB[au﷓aZ)T5b*qy?ڵPD6y:+ָC^Hhs7JNWj.c'Nzw$QGϰ߱C<SZ'*}A}>MOEA(h?5=ءP?PbyC<SS@}>MOEA(h?ǍOX0+ʌQF :&Ez_xPtWNQw~ g_>L/{gx'žau Rfm/ ,k$mzwV~--͵iߌ޲Ot{xO%U%EYH OȵZzףȴOPfi ӿ /&3zwEo"?>;[r*rbpkfoQZ՝VK"ibfU%ڑt\#$V6[llb5's 3@2fskMf2`H v1}kA}5F[o8Ph*"A#$Vл98+dNF j'Ttt^OhQy`@j2E=J&Q)elմD&y=']҅q л{y,YX[Q,jb0dvM.xuK[{/ .s"69;D!G~^q_;2j %mU%F,;O^wXemܙsF6l#O;W9 ueWHա!H9a9+T|+8_gOGNRfkˠxDIVMKa2Bx>E5B坣b.H={ξB75Mɒ8Ė>Wns<q3XWjDž*W7ī;t.8̠`NHjˈ5WURֺ߄[dI"im8NJL_yMu FA8sz/Zŷ+H1>^HW,zaFC~U z~i0jq/N|s};WMogx)+Ԯda}Lh˖qm;Gk[V[X2#ׯxR_ 8Fh7cdr++ Q:\bJ ҷӍc M4}NFM=!e$6on՛< h'6h8#\H 7PWOU[|pf9sI#Mϟ?m#2:^[v[y.wu{}54#k1Ca Ln~΋ 6 ם"x6O vۙcߑ]ƭ.l2R0@8ۚ/DEzՖ;sNs1QRR_+|jV}3S▣xcñwo# ';dskiwoڎ ͱiYYH9u^ ۯGTV5;|CgYt &;a9cȧ=ɯ]s&mf!l󜓎K7-Cd]C Fv:pKPƷ2dsѾtf ^1Ik,9tkۛcˈKpZZq@{9{CkK+Wc8 nz-sKjڤWva9vC6sˁTT/ď1δϟfQl Igc~ }!dN?^ ռ3x. ɵ?\s" v} /_`?5ԏogNp~D6k8TH.ٛUKޝ2|{i^leԟhXt(:9?{iҾ G[M505؅[=ǥy_čkZGѾ! vs88x'5DžK}#L`tI£C *Z$]om汵aeV[-WCOIVѠ1ޱo+}1u]J{NY&F%.@0xR+MdLE>Au[9{c~=*|nlz48*[0|'?):*AXВݜ z W|Gte{ Fo@s۩_IZ0*\ -0_S—tmawvVe H']a.hK`n:]ϖmW0>4N\jzeHm)dd )v"ῴY#prOk?golJPŮn[,He>)?!Tj!I _XxEPEPEPEPEPX~98n_dK jЩyeGRJ(>*EoWַa\!q=+o^uˋce[Hn+[ Bf64w`2 FuKm~&G1 `#>w¿4﷗]Yxžψ5kcIn9u(*EXĭ>7n*1RsgχĞ7:G`VVԵ{yծcD3̍4o{Qux t$>"Faˡo/g)wk:3Z7t1%RrkO/ѐW9ˤZ%Y-B[4 >}U4GP1_NEJ#h1Q4;y4GR(]P\y4'}uדq'-Ctf[ IuD|D$6/bZ|KqtU=; 3ZS04J@7n5Al[qlmЯO=)[FxNU&FoosF_h7_WZKKh.?wڮ dn1`ӥx|?6:Op;3hz z?zVp[o-%b,G8BTl{ >6~Dw`3hw?3^5J@2DRF]b8ZC}EUt8V×hʹB$V?2 ͰйKV5dvዢp _:Tf|sZ%(mdYHxč.A#'9I{cxkǐZkwڽWzd*#I&ew`ckV3i֩(䈞8ՁwWi tgi{a4dlU&r]T`+ž"OV. 5y0:x[SdUOM{if[vOG%.nHs^qskc[ٯyvլ"[b ǻ+O9\iV҃Qmkvʑ FWC[|vyyOkƽ_w-Xj˰xV0J~Ww}+A;y˙KY]8˸f zx5ytӤqNNc< M)j:3Tԣ9b׼=mfQư"JE`>`$}+&ut$t?x@a#MT0G4±*'3Yvz1,dnWf.6=#&'pj5n%s"+sێlMIR SQd._} }Nݖe8I6_'fJgl6 -\H kk5H/4Œd:X*NGN{due˿| vd\CH1´ʮ.Rmn\^C׉gLy`ou42]\" gW?Uf> nKIa?3P|Ӵ="ЍD<э>@S\n u+[{5f2xQAv>鐋{;%yWY'ƶ* %&-&6 PFdFbH7^WѯmC!"CCdLKe> f$YZ A:OB˦I<3-r7WM2M'Hk=Z8gin!{p,npz΍ggKEñB=ͳI,!';88!;; c0cڍyg;㧎o|3Di,ZdØ`@;TܨʤUȺ-Fi}w":ah7_gu)- ZZv3ú[Yjw o#eg1s wCsVWвC:Eڃ9_r7o/ԺJ8 jS+S'a#\ky5̩#82VU,%|$R6 #Ygxח?kIXwq^TiNnVnί*M!^M'=ţ48 u[=EQt{ $"O%&bRQXZtӠa%+q,YУHk٦Q0sjE诿9?#G"dttQ`9E?{'k/7O?#]Xsy~FEs=4/7OGE^o쟑y~F:("d=5E?{'h^o쟑,9?#G"dttQ`9E?{'k/7O?#]Xsy~FEUm8d*ι^J>̳,{f׵zy-Ğ,elC 9c߭ \M\[PX]*"!c$ LG#Y9o]Tf0 NzSV{epaj+:cumXAS;M4qNgcf OO}]?gUYx+:. G޼\r>}|&8L>vJˑO_>I4>[VG1-dBk=S)btˆʒ(־φ#@skk"!8(0~kh۝Y-`7LmgVoAӊEI颷Kw=8N YɞGsێH[p\}lgMRb@;rݱ^ti=CܣZVyOPR RC"\!B3`Ml;|aekȬ4XR5st~t9޽7Lնsl-2nHPv#:^v9FuZݡ,db JUo×ϫLe5~g+}Ӽ=x_0G@Xr:>߆~:%ďwO-EMipv$|}൸FѤ$j Amn-/KJi fzczT^=U~g,*SZu{o, kq, iJ8A]&Sfi)-Kgpx>渽8ϖ: 'kKM_O١)w"#.F@ZJ:+XQKE:uS:uݡRU'$ $tv|Y+]k~N]r{wh6bV~Y<`t՟W׿fjʫu<mϸ5YV 413ݵEck9P{gQ K#'!ÿ -ea A(l#:eƫ@^v_3_1x)VG\3Ǩ{z -N6$l㑓5L$@n^#j$RGں.N9㜞k6,xR=XmY5"KDX,Xq; Ooj/_Ncoasj>E+. o|gQu|Gawn2b,Po h ]YjVkiw;> !H\8ŴJn;Dvmf/zwkټq 6cĉ#u5? 3SI.g6ozby9B["0@ʞ+ (&quYc-ɵ%vq0ྜྷeQN K+6.Eʩ؋U/&9EG  pRkEnYimVUIǵs?¹V6jvW7 L%}s 1;i$a[kgCFP(l?3+եJ/ (^g2RdGGŹYGڤ)@35V-a7ėSڿR%Ss׷b+wUs]dc:zw=/~ӼS}@Xk$6,}x]~i7]ClXwG{=x x2źUū_],rK j~ˉmF3GũRR9Nom4]vEDD&Y p 8#}w_ |;h|x_Inb)œ?eӨB%p{7.|~wO+F,eD SbUSkU4' Fg~?|Rd]XuxQYxCZvFj`@<`]Ooh֑j>[-D2+8=c\/KkfF'FU=k+NTe)=Og i:ui@W;@T q9?tC8?*|O>;WFDb@q'jG,`)bzp>z8C!U,qץR2;+&9 ʅ 1P4r)CcqQʱ*{(guq8-Y3\e l'8>[8n+}m5b]>nm6Pbo7^g<͠l:cKWPJ)l[0<~ՆB@YGC);/Ee}aEWQEQEQEQEQEQEQEQEQEQEQEQEWA{=+}1פWxPx1Dh8ZMsl/WEM08;z*;>9ݰ#81bHáe2q8-|jidF(8Cxm4o%RAHh|9k^ǚBIas$EL,ɵrXZ.ؼ6a =I/g\nckI[cQD زdA-玧ҥfŕ,o,OrR.AQz}|7mw]DMg'@l`=k-xFo FҠǔXMN@Rp^nF597}Po:INZFBМ =b;'R3]-@:J+[B۴vtIIW<{lk|!in㸈6 `=˫Bו3I+]_q_Pq/^q^wJpc_IHn 2ZՄ.Sp:]>=>ky@+0pT}W&;8Tܯ|nWdxDi^6%[jm5]3^-5 %dd=QO|E<744uaC#jĈS+| <}), Ncu<ҽ5<F,/!g%ن49yGNxG;} /unBN<%dF u٠KgrLk>u?lCHM.TD>\#`ĂUp$ܞ+JyՒjĺ.GCo #^xk(N7 <"`Ux|kg]yu%f){gy yQ5.*n6M.>Rs[Gmt9Y%,iK! # kxZu*)nk\xJ='<]i 4HdUYdfG 0hwGQIdvn/ iͷ?Z[->fe۷15;YZ `ѤHU:|0,:;XZ>OC_aਮ(N/fhﷷ_|c9xv[jZڤڊm]6$~~JUcI%WV1C1 ־<9VڬMD^w,c1'y.Q1իis`V\# 'd?L#um2w188x.4]68?pors{'y$22]0?nYKV2|.L.*qog犆cur"Ir9osҿG?׾hЍֺ PA l}~sOYĐ%!6b:֖/5T+<=I>YKv!m^ b?xkO{ 4y%9b\έMF\:d\W?_k:e-oY %،MDc*ɾ]⒊]OY< VҮ xxI wn?OʽK.]p2(ld+ t~yg<"r#8W|+N{.^xuw r?Z1XXBs^*RRYLAogM|..#7||t·ڵOH^$>lL0 +g++ cŰFd`EnkZj6/X!C$rn=p;^Ubc)r{X2Ođ4`Io.4N.-Ryg?zWU7⻻;*Ġo|W;ivEuxVr"7 ]Ş q_?#1Nqܚ C8.fr%m$^n.Ͼ1߆zm"^@I`OzݭtֹYශ pE7qo 3H\ۊPc`JӒ Je55֞-濿QZF;x9'5fGQP2 [zU¬O$/i0[ O2;Zlkn[S Kxdu byLU(;xBY-nK+]PX~l99WWC֐wk-m |G3m$fYcL_1bbG?d$L=NY+ojnۚ|"ʿ@U@ߞy8F$;GOp&I@ 3\Q6rm@85Y920+Mm剮\6phRVjㄹd]JC,sኛM=+4X09|T9Q6&7m~~T vfx^8wj>]:NEIQEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ycMّ#9w<קKjS\snM˞e࿀=YmniP;hve$qZ-wVڋqG ɞ jۓ|'EM+UbK3+rg+}w9y% '* 9Q k>=lm Kh4Y)V6 .r k~ x21YYރ`g_~3xCȢvQ$[34RLp?Z'iǗxɷ֭O,/_[ )C2*a鎿N|+av{Py4sDQR& l>?I'7?f y2c O8?'mw<xoeycJ) %w{tOnhMٻ/C}O"Ig=qxψ rǧ6O7q rW:{A ߏ S /,ïj^gnn48eHlr215TڌK4 r$K=g#0D|Fy$%c#5.tSpO/e jƞ&[9 pqH?g߆'tK+KEurD4:߳ZZfd1HޠW_|.0 Թ QI .&Td_gR9/ug3yt'Ʀ8Y |V|Nqz^@?1\CZ*QΧ|zW-KN y A\LT_`fD~jJZ(ЌߺGa8 cum =HjZ't}D!dLNF]2 [}FOdܗݒE Tg{v˼Ԭۛ9[Jm|oA]׆?b}göx.7Ӧa3ZN$޽{Nվe*(pH}^.8tcbdGLu5Ϛ69|O zڹN.mJ1K2{<3 ͭ9D۔sӃ[F)y]ۓa w:g)56b ,>>&SHH8׌#޿EF#StϕRR߳BVtkq !gN,F3ux?f Wz&ɐ,Dě%Fy:O7~p{|Wu:-La 7ۜ=?P0rGP]ZtPU} m(?\WjIE{u,60W^~Ѿ t+/]-h{9jc@QZ~%w{ x:C)s y^sZN[[C5-zU7ʖAo4oF2+_¾ Ag>%ȶ!:>=}O$M5Ycij$mXXIj-o⍽p$1.ɸzg5P|B%])ly:G' w)7$ԭt D&W bN؈NȾ#"M#yZ cyp5/,hd܂yyv`%WR^>9Z9 N~nWKY#y5qT17ntzWG[VKxaTHaK|**_Q~]FFEiy9lꘇWmG[7pT7CD)Z0;s޻1 Je|Z]ؽmXgEkb?l ~~n'O֏K_uR#ʻqsM`r]6nj|ncU:*r--*}NQѯ"nzw/~- i+T-滔 ozUwmzQ#;;x?td"$[x"0t8n5:?$ѴN_.$2Jsrp;yw^ .lY],WS0, H% wLqMRKpǿT}/u= C ޡ-hl몖 s]m}3UgNǓi2ŭ^jcH[Z$F/ÒBm|( G5Tyɸ:_\β$.orOmzҝ y9/MAb1ppvx !O$$qkoַ&4cA#CA0pqtש[&ImiR(^=@@Ǯ|k)Za6d.>:}^qC0_F-^?"9a *_}H]slyRGD3m? ?Vi:kh)nf7t*N 159 300 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&+u;/Km*Ka52>d̓*9r@?*o^iy(8Z;=?S՚5i,C{(Uwo\I8 ;QgM״٦>|  !~߸t:dEFIH? qm?*kۆG_Tm'Mqk^Ԡ.XmP**N@$`}.zW#|ǎJ֯8,*|>xVu 9M a[$3(!šKPn{-,Čd`:*U V]'eށC<x=;}?DXR7HUDZtmt3Ц8bi&u4fc4A# :j^)|.)5$k (܄H8}sZoڕmhgnua _wW\F"DBQ_7~uFͩQWh+Ey,gONBէ VZ6Wsu 9[8P iװ\6؜v?|=V;#82fBqEN܌v֯/f2h$]m$S,:v=hI]ӋeEsz'oR;5Dm_tfK|tGEsz'oR;4\ޥ>:wIhԿN?ttW9ώ?/ӿ:O3@m_tfK|tGEsz'oR;4\/ӿ:O3ZM6B)!ulӸ QEt%O&k,N0ې;⴫z%7QWqA~Ӵ]x&VId[H#{zHMy Tzj+6P_FZNӌgKoM"]}3 rCH# 6N=Cj䖈*r0zqU|5Z[MooۤmmNTI8dvde%0HWW>"C84=SKvׂ]ұ9R@9'4e|;&K缫$tqNڝo"R:WR;A>Uy~(,ouKXM2"f\sPΝgtʹrw,6b`m""hVNa=7(Q͚tqtVW:QӚV]wBpzs@Vsk?I_ʐVQGؓI`B8#M;6PA^{TyzpxB=JonFN9< u x@0$p=`;VY HJ6`2@\gkOմSCz:_ѣΗz?j:(ks5WS켉JbL ~# 4 Kڌ&@Vb$@9&>EI=4yGGEI=4yGGTVP+x׍Td'c$Ҁ42 8>]fzuZs@Yu-jyWh?:ock+< iqc$v*FCdg;U_]-IsOүaah2l޺MCCӵ .^)r7,mG74OMA&#{ zŽfh1'Z#5\Idt0M-=wK%yxZ젳7XLnrr'8Vt x2;x~蝹=I u{Z Wv׳iwI Ud`X_tUUkѥ&oF.?Y4e|̜9kmkqoưISEf$ҭ:=^[kRB$e%.0x!i\;[0fu}_[S!dífn<ϑF?5g_zUYݱ{ -·˱l3`W_2^D iLbē?Ə};?t^kx357pӍW2T݂1˟WZ#"Um{Q@tջi!>a$խC*?rrV!|-ܶrqj?vݘ4gx& %0l~*ikx+$3g\Fwz6wWo}wym$\\K"lܞ` c8`dMbXrp9=Nu]@zX}LxN;dY G;t3nvŭ>\-x?,Tg[+LM$ؒ:5E1jy>?Ƅtw}9eKen/Z=5\BsXk֭-tt[:ambeb ItcjA.qjǎi4 m"Hn/:="\Q-BAK򫸢0k'I6|=|6O{c_M3v3 u+E g9AwSZ˟}VU=[r QY2G6'cOIyj:FZ[10NaS5Ou:!ҵI!8y"<9`*?^"*uKD&pj֧]XͫlmT<BLVХ֚UO5\D`۶+ZV-n,M8gb=mVRV7$q0Lwdg*{{JH-9<ӾcP7_di7ecc,FnVGvf,H9f,I?ZҜ.B:nMT <$^(QEQHW)z(lߐcV?~T͏=[lߐ ))z(տ!@~T͏=[lߐzʌAS6?oQ~Bf 6?oP=@z(տ!@3cVz(3cVz(3cVz(?oQ~BE3cVz(lߐcV?lߐcV?~T 6?oPlߐcV>(((((((((((+nI=Hjfu;ڬ.-:&P ?4ٗ??ٗ??r7ڧ-ﵻcMY.%/(th_ݐASxu,QѠ;>mBGw7‡:˟?ƁF4)q㻭.ѧtm<_FRA(6>QB\r7֏W.yyisomKIhX1 D!t i۠_i+yDr/fXH"o۹ ~t/{_O$Sc? :C$̹q4f\D߁5_{_ӭ|G-?/nn #_1?W,z{E$s@\."B=P*J@NG8#ʸ˭aëG#\;:0cP*WS9ߓE}l>(aEPEPEPEPEPEPEPEPEPEP cKa}6I"{w^g"k322R,#xDrI8d(+GQf6vP$!kTP7$dj> Ky!P.bX۬ΤZkq gZaԿ-._@iM3,VF@`>\ݐ3Wt{;'5CR,We0gUMGKZ4]$:yϭ˟QӨ/=yQԶ۬1n*9cĜOU{Kgpձ2/Yj_(r@%w*ַ~qmhJ0lr}ȫRO]/u/ GC qxcOM7Gw[m275r?x~1.umN85F Z?aԿ-._@XhQm7otQp9h>?٤?Gk; }̗3(*=pA( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-18.01.1/images/texture2.jpg0000644000175000017500000010104313222767271015412 0ustar micomicoJFIFExifMM*JR(iZ0230K0100Fotoxx:trim_rotate| Fotoxx:trim_rotate| Fotoxx:trim_rotate| Fotoxx:resize| 6http://ns.adobe.com/xap/1.0/ 600 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?lI83DpҫnMEfִ[k=POtPWnHcAgreRiS|&h1e'HZƐyQî1X>X!bA _HwdO,;C̞֕*R㦇=Dgc,E`-Y; * [=w)n2.p ^1Ѩ%JtݺIoPF-Qv}V[cq#Wi6 UYX%}3W+/z҃1+摌+򣁟wwZ|9XKN*]BTbf$<1O`:aѹVI ULQ>$}.>,z1Ͻurm=}ƣXxg'@{.M/~ZKyŠY2~zBͻIv$*2X׎٠"9 0G,dr{>JKvg9ٜюzG$K1æ<0i:MBF+-ۈOX*9o|PjRͽB8ޥ[m8In8G"ķ$ۅi068=6glkAi;W!ːz8ϩSSy#+1t`|*+Դ+vO$aG| `dtߵn)l(6 c~ӭ [f& `1$c#zfy.HQPN<ʫ}$-0q=zus׋)ΝƿjmE]P ϩ氫QA];#sz!PCzN1?^3xB$3"dx6o݃d+B;h tÌ3q g>:`v_VκO:' 9sVcjuࢳ۽?J^AO1pO,;"wbUC9t^0p\k4F256o2LAXj1jFNrqtZk &&H Mֹ%q:隆p~9M ed O ;H8H#ҩRjvns;G4vGyܜ+ub@z~լDV ^=1]m3v:uԿdEX,"h2r}+|S5r;^\[!%LBX`YZL2æ!=x[ȝ@_PcS$5G=8_z.l[@ABya~wjZR~Q#|lm[*2-g$GޫxVk"y,pV1{כy5N;_F+5Z9Myo0L9%~fKusgk9n$3 98RܢW.Ygޣں?Ö]3v`X}yߚ)Qz"-E-Z-4G\4p=@`8qZy ˹bZ?_Ju-[ñUK03[vu3@{P+:٭NƧ\ Oz۽lZeƗ$@~J)hrq8Hs2yobTpzzcQ> r_KH[h# p20G^;tv<%$.G$zW7kmkn>pq;$*8)L4zcGoA+H g'Ҋ 뫻Đ©$_@N`GEJr7J(d;֝@07FiL һp>GJO"Hd}1#Ƶ.L.@ߕ>{:׳^*`_C,LaIry^Rħú+YwIu..2o|$0cx#{7<=7+X%e9zK%R!i,X+SIsKQ;#Ⱆ]u5.Wu-oPP1g-^gGG(h?zʐCǥvEĹ]'smӼ$ֽ\ZgW$HSF\q1Xº,2'$˜ʰ etOSi9UHTIS\܇c}z~ b_ʤ5 x= r=iZy5B%3OGAO{}RٔK:c,q#ֲ㶾Lq,9i }^XMj@*ZƐ+0Px|M߳_Gkē^4Y2 B.oodɈc=8:E ~nTΊ4cuK KzqX'gIVsٳˆXGL:Ik&:j IbPF9*r}ZK+ug rX#qN9n(Kh @ 0`y~?ϭhMx-0edirO2d^a3\) IQϡ(ERkR!BA=q_ìHt;t^daH<+yl]g$wMzKxt|IPӒ+Io=ljZbk#Kx7*N/ƤG9w#喯crI([WM=L5XlF_CYfP9Y:qP)5ZլѬ7[RpT8>֠IϿM(|p3Mo X-A8SӐW=Ji֥i}$:dq"|ǻx g4~ĭ Mnr&w'm(UuB8'x#i,>7ZDp=(ko&=PsLt2xRٔpK`x(Z}}{Y+g_|XyK/=kˬu8đ/ <'OyxjV|%|! A'՛#$9*'ۥAO_ƺ]5H^8:rpVg6mƼqNiV2NFXw#5 21+wK bm[C4h5L Tƅe@&ỏ<ӧkv@1Z-^2Mj*G¹Ɲ"FzӢʓ 鄔6>Rqs'W![q^+DI.,.6xϧEXj*2nǾ3ZΕ7)I!!wan4 |/zaPb* =ti\< :J7d.crXdqWoE].7xhR[oJ_'JfY ~=G[j{J9&Eg5%iZ*A ƈ(w1:2>ϐ}'SӛdEcS'h_ ,zV/4.dQ[$p~"[As&ROT;o`yo-`+p c]2U= ezd՚d<~+ބi"z&St.j7WO="| }) +5GֹYʢ %N*I''s*>[o~;ӏso ub%ē*Iw8>V6_ܡFz׫EE5Rm#ƔueӦ̽uk,) o||>{}qNu iBrUpcij] .!{[TD@ϩ>}gЭ<2mʁ x޸{}vMwTeT:UvHxV^s5][Ar0w\wP%[ӭkTk5'vfwHU+FD4c|Nx7߽ea='ON.Jv睸$ҳ5/DZ&['P^&oApzwESWŒb[rNx*zb)i4u?f#75m\gbSy=\J#nO rc=;#Z­GX9sӟJ.E_[Js bb8bc-"5t24oXcgxѻo;F: ?xxFE%\ n#ʛIqyhpYtqWV4ZB FυVp1Clo&JlI^N|~s?zke3QǀdUہ>ֲӚ;B49cg<3k&(^BҜx^N}rK{)T|6 }{j PѴ;]\rǿr?J5MV˳7" ;(St=qW]Q} [7`O¹4zӉ%Np9`cy8ڧc&z$t0`/#=΃nc2:Rq}{}^-FȊ5-Oz$>;dCus,@ ;)&h FW`>Rtaӊ)toeom5,w17b;T4nD'09 y/) gc<ֵΏnZyR -U')"= Cw^4ݭcnFM}4S5䅾U˜8 ǟ㊹a;[(И['籫?RFGna4#$788>jOu(jVqK[3D5[=Ђuwkm,Τ)#FFE3$",c*zF!]aZt8(advO jQ͙ !?">:Ҋx ^fZ$*r:vmUsPInw>ny93\׎h3ƪ60zn'R#5G1GuyrŴ](I&x3:b;>³ {جFb\st$S$ I+3 Wkv ?ZcUcӔ-@ZKKoЊ|R7ѱkjɬ_ J͛ ?J.> %}Q+v]nG~:,UJn?3ֺ k;X)HfF{mYs\^P6Nyk4QԞnJܲ5t;k3neX}G#[-Tq?ڗBHg0.iTI>qj^jvVEHЉֶ4̑\0'>tb𭭜DIJTGZȟ0TUԃCNF~#KwU~Mu)b]++WV!ԎWHo'mw@iTQT>k* L& Vx?ֵ28]k4kKrZcT!ZPcXJTFUa*2~%Md@y$vCi[co#\#>^#ay׊xR`s^}_\bmR'Iu 5Y]UU8?:lNg8vR;H%O6yOZƺJ#̺-U+}skV wIiq#(0\cztCY˾'v31t7sSи'kU*g;-&rb]W5 =*Ѫ)9x+/QY?w0 63W| sNQT8+:8)oJ<=jr^Z̈́5݌YنuK\m)b Gc55hn#5,^V <?˓UV]ү翸>-qF ezvQSk[Xt:XepJ*~7=zu\BRd@O^kɼ!|3G,xYI`c Nݫ|&>(8*-hySLxVc8tY|K4:P$gR(vkT\۪I0ŷ~FOlgX )"L͖*dbs~ʴ0z|;i7:T0۔0@*:=O~+KVU+e>03jimb,lxU^;*Huק)IYDvGx$t'>".ߠYhnEx6f$cOzlo-na1'ɟ%w{ltեqFjZj kn$(¡IϠ$WG%^^[^絵Dfܻy`#1w+ 3iֲ`%sTGBxbrHy5iA %$FqOHǨNr+V5EH۱A־aMtPXrp7g=Bnt֎R퍐qWߊҥXtmZuDcl}Zr)_HЇ $k~cd%x 5j`9TeH xGoj9wK" 3+ Kidw9ggZҧ7;yM912>ӭsVQ܍6yUWP`~9v;sT4Wd{Ux#p'1~es^=TbzUũZ]x98+7RHӔBU>\OҦI=- eގ$Y-q>\|^3cWx7s@Ƞ:׽|4ǁyh(鞍*Q4¾lwIm'=?RVt)ZɍO/dsҡKo\#=?jφ<0FI/#ۨs(RL?Fofv 7`[]Jј;= >DS#Ң N r4Q_ u@-d^Q=>HG>:iJ#V&ecV6A85ۛgj %ddp+@3._3#٣\\4=s4A=c~̖A'wqXgI|tX]F3O5R 40%i,n$FCO yaԌQ@<z󥿎!\mcMT41 Ӟ=낽R=kkd.끟θsÉ-arI"5xKmtDԒ8Ib!\=)Ȝ&i3<ض4;Kc_XG$Mm \\wr^xkPƨѡp Qָ4#5sWc1uu)Cj#atVL?,M:5-Y8 ^GNJɴGDϕjKrzg$x\h\g?"zz ז$vȵ tdHr y݃@86`iYCX yA5xIo|DdOs[H̹ > Uf\iELj}3t\ӞKPWh?8Wld!zg*}M찦qUI#rK(fh?Ŀ yNu_|6Ȫ.8GN5z؟U bqdG& V;Z8.[9 iukg ̭$r`o<ة)4mA!c9b\CuvRŁ8 WSH.؆Ԕd {$hl\0\5+$9Ey߅-C!Uf={bRŗi/ș80.H֝YX G#GR^֑O>I7 Qz5-:&XFZ ,1q U|u$j0#A<<P1Bn n<əLOpĪ2PF:p+|]3IM]th%M¾BN {r==Z@]M NQAYBkFҏ+Ny “PC378&|d ƺeȮYFtE#ƎNuzxK+ ֺXErn\3ZƏJrZX:֦ivxdp 8'5jen ~ny[f}kMhCy5]|S}$jsUAifȞf6Q=l)BQzj`!Wȭ{`j *Zg[e vھvZ3 r>/ WNrWV 6{6Ą'9=\[E(ݲ~b9& ۲`I[wѫm8*Ev;Uٙdz]Fivd j6 }O14}\}zWKmAy蔈XqּKqqG]5efǷ]m>5=3c}bgq]Eȸl1dw$:{jeMݩ\I(>rqv5YQIJN۵DCfN]N4A7B c7ףQ^FbZj)\ |d-Ny}*ժ<1]$L pF >rPȢ *Ԑ[kRK 4g=@;c&[lF{󞇚uA-0ѫr$e?Z m>eA*5\yv&upFWIǨ|EIݜves{l4Αf8G`(ǧ>h7mo#,]`7!*bM+RK& i3Ȉ톅\zs^KgʤF';k.Pvg N;\Mci1KUJ̪@8sWo Ԭ=ʴx tp\Cqkg1XZ_&kBO;9Ǹ55?z%QY/l Kfi0.XG t8m>/A+[EChזJWV@ޱ,bB!HU5h:+9ePF`KW=mi")4L6Qj?%y@*$+-t)9+"jR:!H*@gNxIZ׬o=B&E$H;9W[1H@H^(&8}j֌JX fVNR;pyG\F+hv}GKsm6?cۧ\mOv@\o$Rќ-RtɎXX03G7`׾N0?CB[)VUy+`~^֮}$7qyڛT>V"w԰) tm_9oQGos^[}'.#oV뚼b[y9#?Z#), >L)N@p0G^=9]Yؒh}=7OQ:GSvyZVL[CPtۂX|mJiuU籑ɼgNGN%4RRڭ,{pI㞵)JWtF(-N[/41vʃm-ěαFml*q~ SIٶvn˽Tra$G`TIf8^#9Ss}i{-5x\Bpq=sMh.FaRZ6n3܁8Fcլ.dzQY92|Ϛ&w$q,q11JH|f^FWʞZ[$f\I@;hXtр'$d 4I FkV[?olHOA_q9d7x[%sṺ c(LXRArj4dI6{>IT|zkgd(>S-?y<*\\y)< '^[x,X:\+ (2J䱯C$wqF6ՙ(_*$YK,hQI+ju2N%Yϯ>lGf|)ڞ1SwJu}#hJy13}q/ " #`982NDyoh>VE8e8q8ø.9tx~Usi*EfP#:ϼ𵎭.G#k\%9TTPHtCʷ2yrGaɮgTt[m.6# Ii$aUP`pGFZUi-Sتu:ү$(|'?5zqҚg[ zm~Ơ^M$:D3^zENJFHŐݹtQl!Jn $*y ,8NUE۳[,~~Dn~b|̫<#2=~bG?[ӬQ$3; dtCEum"oB@R-:[BI(|`Nӎ?=Xݾ%wm#8*& +4pӂp:瞸Wis])! O5I,o,nd\ Xuy7RZB=mCd!rp=?|AO]E_5!d1B7qak-ΊRj:Sn/c{2.8;ʴey~8Y둏{XsYj6 Ia2H^ojp$;?x\?}EpNqG]*^EqocͥM)tݔ˴bxHu9ѮJYrI<+7Z>-(e'a%O~k廷t6PU)NFj4;EҮ'ִJY^@=+ΧoBmX.0 ǭY`-Y&IeG_t> ,&>V1b2sW沨⬶4 \GhnնmPW?VnOeȳIvB 3{JGl4M" s~5-TTQ( ᛞV5OWs+ ,@PbxQB浫^q$ Mκ+=YԢdQ&sSjď: k7R89>K!٘(mY7aʓW[mK¶zj L#Yc;JQ#T|>˙#v3<:Fa75hcX A p;8Ϲ#(ʵdr:Mmk3ZwF'qךj 4c>eLa8#ֺx}umn4͢TH{} ~e=rtW?tY-U<8M圉=||9kSY<#|Y9u<÷U|߾~E$sɫv^2,#${Ҽ)6#ф5O qadr[ qߨV{ɇ !YW <YZ ymK8Kl~q>bwla6 v{5Ii߻6\qg.nukv2^ !{-|)Jv$6r>rK9ƥ57Hm9sf5hPRQpG|ߥg&>6Jdzu/H.3:Ob܎GbG[m>EdTp W V'.:;k7O@z#>;Lԯ4{V.RMzu9G4ZhSsK$oFǏqҊܷ쬠1ohd@T|?w:qz+RwLTCq rO&zgڬJInبd#'^l  tA iF2*p1LLl|nCǗb.Xn9O`kys!Ozϋ.Y,fWguh;7nwmW4w3k)npX_zھ^Ǎ-Jkx3+9*09=09=$X"tI}?:.-ܳ=щ$`>i02$~y#x!,f$7v3:dUGnY2 Opp\4YນV%c8=~kMK{[]#`cy>ONo]!Y ГE,SyYreʧ<@u~R6bdI82I+_ddc?{#=;\pc 38 NxlA\Tmt;E8Y.ڝ:* [ v1S^x-##@:PW8#?O^kLW#HN?*MaD^~ݐ򧿲;wo\B0F2~koJ< q Ϊ*Vro@xyr89r?:1:Ym `c=SFtfv~օφ9`?Es<5*:;?4Vu)LlNOJUp2>3ް].]bǥ}d +حOxz#-VVYӭyN_Uml1ZE;`~rwRM+4䜞khZqPSr9W,C֨r,^;jr%HR?,bbr?Pxn0Ǵ9ÉNuV:%`g\3'W'!!b;Uܨ6L&gKX4fFa'nִ5HYq ڴqRE5$d=];0lG=ٟ+GIMgdpN3kҮE8YJ济35P:k;^]?L4,*kɾ;< ^ ZG3Gj Rԯ.arDYks÷IRq\ρ&PTGº3c؜>=I9 {#9:;K:&yIfT`~l*WN{`a!NY~n>$v\@A<>Xwoqt+C {{Uk 9~WRDhn5Vw1u] c?c&el;5$C+~"\mH,qJ7 ML<a[HYE :=X4:JLUAsmvbJqkPq+!6)KK8Qa8C|ZGunB&QXߡ֘ casnKFr)GMMe^$њ%7˴' n+FtʼᵏTDb"Vrw\{~Ihy܆6XyCM~1Nko a#szi^R[ v9$dQ˓jŠh]r:&#Q:NZo<[K(Ǔk|q,}k;L'w0{u4˦2 ~=?J^jv6iHmuDf%³\+VY+Yʜ>XĖD,ye QǥmK>MӨq@)Πe)0ށcP{S*e2*1Vq+[TaӾ? ^?(~3`=kX+-lr\AUda1Fp GVZ?gʵM*e HTYo ڛsZFߕiYFu*LR!mTsr@WRvG4|H־]#/6 aGּ h#mi R@8F׭oxeӴ_ΐ),ssn+E/Dl}*x5S,4&1R4Z8̈́B;z dgCuk~yR.nq*ο?ΰ-kdAXr\5xN.S4n1 7nT=~ڴsЅtK>Yl9u]Ce}?MQeې g=6Ieڳ CAz3=208zò[[gV q>PS6){;.Ofo12 8!r}r=; Xw,tAwylw3ӵ7EOIbf\CkRVF/FFO8*:^1ŒY>G{8c#s H^88=yU謍0ټidpN9ZB'#+×Rv_Nyb [i&8 wzZ"YQ E z^xkLɭ% qpTD~0GHeh_3cz^ cuXM vğƾg0)qG5bo"G4CA-UEWymJB;9Z;KSsj%F&[Vf~] _i5q;[t3!rxoÒǗJ\i)h.&)\H9oh3P&]"1cMNTۼ=8oޝ fEgkk ٤) Sj炙)(P0ݵx mf5b#` ŸѴOʌïPI56'#RWv/He^UObcI~W9 OoWH7,rկv:hj+B;YyOןC̃I'WRms]YH#8;OL0=}@Qt/UIrWvUɵAcA9UIH*GPhxY[MmmԼ#lrLV8(hV$g%)\Y 0sW[c\5n Ñ?v:fbL`qS(ԥ+a3VE#S!%'?GDB[ye W zs]dd}ݚN璹Mt#5_|4|ec|P1q^^_Nj %U\ d2q8\BH43Y[1+NLOA-?Gax\oNJt0#gc/|1%ѕрVd|/vO:%=~U珬ŧZ(,H[̽"Nv泌,ւss{g/. Wcnbzs֥[io3 A$g5z6FgAX+Ghj2\"2?wΌ4zN'u{Ix>9Z0==xn@@enpGUZ y <,G5>Uk!rN+K 8W@(0xV.L7p!*qFJfg,5+O4P3/'|K6m6Nbs[˦mimԁ4[K߀&TF>R!#ÝֲE,>'IsT=:Ssҹj,qGöv7{*oS'8-yOu':3d JtK8k/p2x^+uq*+OeXJ 9#pO9^kFqp-AOX.uʷZ֕E8q= MĮF*/5dY1柛y~QUY,dzW.-g R!F7pz]Pw9NS>I[o0!w.B +ɒ8éFHc׌o^jwũX!i72lO#-^g [Z6plR +c+H[R5`n]F28Q哝 v>ح NZz'e==yUœ ÜcZUY:Lj'>rc}{Io}unI)akͷ%ҟ0gg=њL}*{X-o^rIQrq}5 Nr:ug]ɇHS^žnފtGoɸcZ+GBלU0Ij\,Ldq|'i'99EՊl:)kݕ# r},b1E潎[9=QEq3sEhnpM_k3@Mr8+ PQ--K€7sOZеU‘Fh3Wm-q,Sc7=JԱ_B+ФE疕;˃ȩ ݐ(CbjCo2&H4k4Q]+sVIvIIAVR8#u/^}F@$}h#*nnAdVmD$(g/ͬ`zZ٣7F!(;0 8D,}Ҩ-ԢCEq-QR)9p~hxc}G_j(jj̚5̆h~\#$>Q]M{Pݗtl-V2'V烮Q#V+sGV8)/-X؈¼ga=mV"."~8?9;M<<- ' NF?Jmx;9I܏CӠERr-vT$ZW3)gY\2 1<(dugIlYԂ[· [Z>6qڰ&lQ]"Λ,hhE#4gޠqQEqfotoxx-18.01.1/images/graduated-blur.jpg0000644000175000017500000002717513222767271016547 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 147 195 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? { x<>}XjCSc?}'?©h񮩬RMGsq//UZZ~[&jgW0D5\ i ue>aOK;z~;C >}ZO [xÞtj֑jzQ6j<<ɓ 8gԾBZSյ9DiykwvV41L"l񁃚3;ãɾeZ0}8?캞x+kND{kvO}貺/F RQG =h tܼZk^zVf< $[[yaOI #"DWO_x21ּ2 K9/mT=GaῂukzGnu,]Fՠ@ij+f8lQ}mCz@ _$ Â}AW :9qlCI&R@),/-mmW5E(P=0F1N1cFR>7 ?t^?-U-?3^FƫhV7 ;toXI=zUpI=1R|xՆ? |+ai{knukO*4Kl>|ZIyi|ڄËx>},u_A-εqm YkyB%{clUtfzMiou<+>l1ox9ēY'oMlys[\ճܨɆx=Gb=4I'H!nCAsu~>m tKŜi=8S=k=FTKy.m6".`8t$8F%OؚM@D^Hx9>-iZ}}KvCۄ+Ą$|䞣r~ -.o fqUㅣ*WHr]<, :7,>sB ,dp=JQ\t];z$֗ &f0ǯxJk=VP1GaOpAߍIeIgdt,1DaN:5/;_2?>>>>%^XR40iֱU% #* 8C7ޯs)5[Ym`9'nUP> &fP񟅾7xw>-n>sA4/6sFEaبX>:o<^(vnkgvvy1_sø|C//|Mm 4]S||Ǐo[Y'f k+{UՃ+Iƛ`ݞE<;>!}m M#[[IPDz\z'< /|MmomhQ@n|[_kחڜSkX2u3Av'n?sM'wZ][I16V=q_tø|C//|Mm.[σ]kbDf#{~ZV6q ^L䶘dn +we74ø|C/f> ^0ٺdl!T8SQLJo,B/|Mmomk:V.WLڍzzJV]ώ #m_1R{9{ nu.g̸2}Womhp O3\8LmЍzَwTcvVI_O)*%8tgk##-|?W^y_žݭ<su٣)p’ܜs<|uVcjMxtqp|`~#=cAB=WG!H38Hdh)&$}&B*!EHgB8 \N0?,Jm${n8]>^wj#%l.G$6C7&w|ɒ68ݹ⼟?&OjI~D'c`Tg zTW dx̨fR* ${T\%k$C~;>"vڝYi7wvJdծxc, [M}*o~[\~{Դ렗w! K$1YJ1+F7q]ݏl> e[{j2RaVl玼AX~uOaA6.$Dd;BƥCI8/Fľ35DӵqOǤ{Ioƶvd|BWD)E)]E+pϋ5R;5ᛩ=\Kn$BbP+GzɚQE ( mG_55u\^C[lE<1:}]k<9lЁehEk4ѵ6Dd^OºfȷM 8$?+H\& 3qYT_#Ku&mջX$~f|]vfj?5mJE&MHlԮ!W Qe cHcϬ~ MouoSsP2;jZ"?_ yBɨܳanN+[{ggZ-WJuz$'Ic9ȭ-/tl)t0-[x_F1(+u< i4}:J* mm4[rY0!nIh 86)Fv31$:Ij\,oF!X/dalu(}X_ lѵ*C:ɝ>kd6A6խtωBHZ/Vg';#[.}/ϟK/"xoᶟi}}^F[8&$+U&@OǾ7Ooo+B<XR<\H7.d pd¤j3VJnr뜇'׎nڰ{#g %gWʺ?Mdx_R}Ə񝽆xci&,?mtWA$qNU[mC:Eύu 5IuaI<}~#|N7GZg5mby/mJ΋.eiWTu]?Ui{f;g#zkGW<%xÞ7<5a R[$܆f#ʌ+cG ]Zg64$ipP G(̥FGkDNK_wIO9_T~f~6Wv2oq΅H9$+k?,((([vkۍKf-Ėq 1\O`=+g8#P wfs>g1$o2o24DlUw6n8MZEDiVQ4R)n<:VG#P[&TkVkYŹ%s 2>V&4BK.K.!<+bpF$rQFG =b1i9X&Aj5TaTdMK]NgJlWnWi__dz`fZ_FHGVGXC+X gMlt{YV,kUQFG WSDּ?qԖr4[@mPJƬ3|0=;c3K"«3[@lz2=ER>*~t[=~O]4gte#>*@ 0FElAjG#Qp-Ak7[EU3}" /$JsSz2=Ehdz@-Q@ E&G9nmV+S%@@OW _m?A{hZe7}c/4ƈܞkV&hu qEZG(7|?&j/w>/oZ2r@ =Ev_74(Ao M G xkmƻkUMֻoi%o_:;T#I>R "i_J3E͢j2hz:tKoj1v,q}2>{%_+ Wl=*}3c?L /|?&kӬ֚}?r_jIb[%ۀqZ[hn X%A":pFA>9?J>[_o4Q^~f{@cZj60ifHC#j~ ѮwX Hcp>Z.d7'3V |3+ E}|ڕu)n nN98^`|?&jKּChnhښoNz꼧wE$n1S! @8 7* y `mQE+y۩xa5KkNn>u%FW6ip-/v^6p;sZbj]? ?J5t-wVKxwNsksw<7C[Ev6FVʌF~Yĺl,ӵʛpPnRx(.zolF֚GcU1O=U,Դ[AT BvpFAulF)?okyqk:r@MI%դc.NHZ^3Y=?RKkhmai\h G(nJdž?4 I4gQ/5CV=˥p 8sWcB4sCuvV6լd8!: 2FwM}? m]ٺOیS*fL(Nr_w7jY.F37~'X%M j-yK3Ïjgr4^,VX\<8qqyIn˳bmQJeeObN.1Pտu3oux~◇uts}{-4uBE"2+6X#"\O+`ekG''+3o%lFJۤh۪b>8nIXԢ*J ( ( ( ( ( ( ( ( ( (?fotoxx-18.01.1/images/rotate-left.png0000644000175000017500000000557013222767271016072 0ustar micomicoPNG  IHDR00WbKGD pHYs  tIME )b IDAThY{pTιw>BLHh@ʳ%X)hE´j(c#u2Uk[j* @$$KB6{w<3N;Ù=w~߷'vMQ;tS:џN#U`nPS4@NUL#X|ΠqCPO6)?MHUpUt}*j~EvK"M{⒳ *9Ue'xb7T^#;\j+eJQ *'DVh~B]5u*vy۪+-"|fy!Xyc@-%%PbO*/v\0Z ps#7|ߺK@y"~䒋jk oN9iO,9|ٛGὯ^@D%rHܠ@tFDnfˈbON3O8u~Gi<8h>{wD9n>d m\\OlB^O:0y[ W\Ҝx b&5嗅uc#y}ڜ5>}헬>5Veм .chkR"IEQJATBJ)*RIHް..jC[OP;ғJi[Fnܼ>-&_<*(7 J(Y 24y_޲B!o<X_i/X=tw1_UtΙelh;y `ژ؉6G*>8\8hqEheitxi0li>p:*8' 0P'Im+S1RRnҩ6"iAZ|XֳA [,˾7eM/Ç7tp'3AdHJI}*GY(+<U2Ʀ8bȢX3sFy`cY^Xz-dߋVȏzΩHeThñ;;<lI]nTT y&JXUa3`qm ET ْĵCn5e Ou!kYwG=N dR R%F&!72uV\qWzkɼ,O[qHvJblX-Ӡ`ZQ9\/o|*^K|"7E#`p/#YaҒnJ&9BN}? m!&0bnԈAJ|}sL儮緛x>R7>S?y푲]u2=̢gvXzAy$v+8v;`F$Q!X\6c^$StL(d*KH5]C&ž>FIQ.y!ɋA#?[V8o],ߣv>wP4C(({+W#7̝hcoA]kNF `-֪ؖ,ai 2T-s9zx^{y[xq'' *לZ"G~GP ?}m/et{#A4ğğ' EEIHR,N8,H憸߶ ΋@^[iYy`͵7nygF]D lbK%2EID;,^- /HgIϧz_G)ԋ5N!Qnks-E$:k&&5qzDcbst K̪ yZPR똪b!$\#",E͇QzD7-ЎEhAUpxM*]g#L1;tQ H9Ps||aqg=̆yTiaޮrLqR &̰q\[ $( k}-8+㱕EEvߣzE'{P&> usp6 WW$ެQxjBQ1퇳B޷NƭD0b{!יqQYC1F1ulBm W|CM+3O,z`iWH-SʆًVf{P3""d5R] ~t>~1jv)&J͡XрxN+YrE=iーt8aĶ 7K>?s9BE6= i?@ bA nt^*(m9 k3Kq ~5ƀ$1,U2ir8,ߕtrAL#6X4FLpz|aeqvݲ՛͒Όmg|A՛ǣ@9RL8`Fcm!K5ѾwEN-EV:Gf@:![8DY,ք&I457Ն ~I[>ɣyoq&k<#과ȰYˮ "%9/K+qݴ8Ub+  󠔂sl=g{v҄1'yx"}}JLNv]PAT' I&qX[޽ } g "IOT;NS P+NIENDB`fotoxx-18.01.1/images/open.png0000644000175000017500000000422213222767271014576 0ustar micomicoPNG  IHDR00WbKGD pHYs  tIME2qUitEXtCommentCreated with GIMPWIDAThY[]Wij!5P>ꥭ1)BH(>'}i *"*X1F/-i)NINڙdf~k{}gd4t9 =>N@*: A=~ PJj PA;zt繹֫yכn=Y¹7_vW.huNoYo_B ss}g酣cov?ĞA @bfpŧgo*oO>^8} WlHQWR@$?w~$%"T8 K(cNYTf3oL!_.$2<QaO2{  P@kKU02׎Has kxJPOM!ڄ(zeR̆k-G\ E3~O34+k,Ygsq::P@Taipeu׮P8RR\1$J;wl=/'{/R4Ip*0H-NxqFD Uu U)BE K8T{'_0 >!F D<רR8Q_X|Cߑ[iN#SSɘm)F久u.Ч31=?Oua3PAg([wG>⟯/"Ln oDZtP5ȲNow'gGOzwY?ɨ(8"pO MGņn~KDpAhf$BEF;x?p?Μ{i6*]mP)@eâ/^/ qD>+ăZX_oCffz:\>8ʖᘠlQ:9E?p 0 ֓DX-!oIc  ܨkBb`ؠ_0 H/_Z}G냦w}0 0XY];x<;3S'6) ʝzBDi+$_&쓰Kh jx`q0梊z#@&ߋ=_YAIENDB`fotoxx-18.01.1/images/viewM.png0000644000175000017500000002214313222767271014726 0ustar micomicoPNG  IHDRxx9d6 IDATx]yxŕkzzGݗ%%|&,17!k!\a $!Ha@@@µ@B Blc0|ȷ-[e}K3gzإzgz.iE+j{U5]XOϞ=v wmg /L@Xv ihlTpw=KbgKKsєWΗ;b W"| Hk &d,i0H#`^ɪv֘iqzkNR%W|,;&;yn\j=۝Q<,H"~"@R^`Q KPAth A_PDqmoBMYMV$Tܣ=YpqU4N To$'9K2r, : `UκO? {2'Gj]8po$ g)K>`C3LTp, n(%?qZU_~龜øh OS ^1̞}%9ߓ^;EKҼ7Pߨ .:{zcxoS* /4^p aTfe8ehS^<&8:p}70ே<Ȫ>^puޒ!F0 <0˒O$r4o5"D+f$d^uoXwtHW%Cd՛GU,CrM03CBllbQ* Tv|ݖ}C! MS>}Rl!O'ӗbJ5|fgٸLxZr8h⛾=~r?d࢘O\#g05_Gm*0l<5nt!_9y] =(q@E27@;& ~ MY۬(t2|VyX*Dbxn{9`"ɖ=`,C37\ry]h:ž$fZsXBQWBlga%[c,c>f/]Ӯ]Txc_ڽd9LU\]ZMT ! `@_[V/YYbdO^'c"ˡ49s\7@@۴FnP,43fiZ!8#ecn5ld&hO/7_V*d5p)5E(f[LjMu[\VbHu6[ qFIs&X '_W&˒&%J7ȏ9֟Ke8ڃ hÌĠY7 [T'-۷B&R{/x+,fŗT"[d7,2G\tH<:q%Kk07J('[pm12x{6ML i}W9u/_p!Nyfۭ~콵d1QqQ2Y`?B_q6Cp=Xm9\ƙor_hQ6+6Z{[֣04eTB2YVkY9>3NsY%s'm\uNq&3\Af <2b=E/F{y\SpOvbjZc0vw~t"L5-/uM;?4or#ZeL<‚6mCKcJ e-[R%ٶGsCdpm,]هG//hA*`?2VŐ;*bF1n|f^2-piyR \Z50暪?KŜ60{]EBky`V'/3E16li6~:{>?Kr’kOٸx0b1*%oR[ Q .HvWZ9`J V[2G2xܥ9{Gjh\Ljey%a1~~Wt<[:-vqj]Xr˖g%1%)"`*0 e O)"U%jl|3ԬCڀ. |r#j s9{9K=8rZ \-W:Vp,sV:Ⱥg!% `P%s\^ŠdK4tiV5Ub1˟745[ E |w_ez95QmN.pO\2+c9z>X-?)$e03W;ưe3fEfoM4̅]<S- Gxmc+QES?vLVө:׺Nۜfaf|)EOs1i=^﷠rxm!ri쪐y8>6#7榖lVtӥT몿YY<.Vy3 ZYWfؔZ sϟD/n,MU~\."MLrsOVUӞf(Ê֌!B/(7aFfkŎK@M?͖̈j.>^ȹY3ҚX8 7Ƞ Zr᠚N"[**P~GhbqEbAv >m{VoLG%?r."9k+o |Eٯ8e }Ҧ\%k=&{u,f`-6f-n+]9rzY`6hy@(wjn834c/3 Z \QDkA#@SAOTJWYV6_@<_jܩJ`<_/?9wtĆpK59{ݘ\f,h36&@{ "J&XЯgq^t]\ ^;#Oz@rۀX.`W%;ۿ&ޮ_0?A}.f3aȸ|m'~3AשA\p|Pة+g\~NOcN߲fv+l&q ZKtC #6en{hE`d7A2 dE9K[Z6P#fxιhFiHP1 % x4שOCFd突 m>xQ5| ( GbUS!m N1> ,d"tV]F 39ޭG) ߦR3RDVQ2'drG69"htp0 1ˁL%0.aMfTMfW:E.ƒyWLA3YOخbѵ >M27pbiY~TPqXd1ʆzSN1ʷWt̂}xbp.h\P s ɂGdxiiRd\H ly<댺3~B,Nj>κbb+RHxP_m4v\ oHMV>z)(QyG.xGl`sӀתXRMЖiG-!,Ytj3~8xҦӾ/{9_26j^Is4z9@,Wʖ l.DHW @ŀu6pm$,bU95p9b_$QeLɪ[hɬ1\%hr|_&mٛC1^xY¶b^M xCrq&VppHhQzQCjKM"ݽ%l';Ų4jNVU[J&JK5ĀӯiyR 'k7Ve(+x*zONLp)'T$"pŃ3~8$&o h^"M`gТ4 EހOV d>= &%\gВ\67HעBN:d!pL̈́ÓnwxdТyH|z6THeHN}0y PTgpp^.Cl`juuM - Ѿ^pߺr*^Lt%l6T]\3cWWzYpI!i< Qt jhYE5z;4#$ lpPCt6d7JUֈAVld$?oUn3 CUw&)@P4ﬦu=TK"RSTdnzkYNj& ˊjDW!..awXAޱYv?+.]F;7 -B-nH&, }TZn?#0?' ,xwbap'azo6aTcRK~ᐚ,M hGɠf\IGP>>0퉺4ɂJ;,񮚊*m!i^ȹoEz^y;G)45K!AVGwog ѣ8>Xt$er_ գOQK:Eb1Ȃ0ttm ^w@*P/d~kV["i^ruR XdS-Z \}d4$NV|!5`1\#Y%,;6)#h2$'}&Z% ?=pt;Y~aƱQ,r7} sGЪ,-#?\S@F힋ͰI_m9ڪdNSa:`[ޓ %-%}Ѹ+-Vn__zo1nJ?z:uۜ]߻[s=,E&d(p̬A+(~㗠ݩ8.:;h}XlJp3L@yɏ> 3…kJWL]pK1hzs/7S\j"ɠ)cS|{8d/9]#, qjҙ2Ւ[35jSfMW7Ub|n{ZjYݻ69Q3Ւ}8H30R,7_s*@*Vpd_0)1nrSdAOz6l9L .(A"܆n\CX0_5p-]ar0f[@"~ɑM4J,S2Ud4sÑUb8p53Y(tn{܇4ͣԥ3^uQB$izwZ|cao+!R%wݷvN,$m$ Nzg̖,Ziq:&kHͶͫWI-SI.Q>']VDĝ<.zT=^}6v._ L wj3a䇑6R~G?^g)܍-R!h_t?VT2GJbF [6SZS8-挗-dw@jm_ݵ^,k:Q?GZ$:`us+֜P/uZ/i_ާI3~^sǑ7L5fdXL,W"Ymgޭubͺ,lρ_c5kjm$7 7].~\P^Z+>gOme4F,4U5K Qֆ5YBf_yD|js?S^91dfl5MS-zxiwD31%@zˣx,F*V5mߺ+ǠYaRd1~vArnpݱ OK`z#5˭.2]CU{m.%@*l~GӐ: S\S\rnQu;P3Q>|n7<ю)zBɊFSOo~ܙ;~ `6&rќm+zlYEGY .ΩtY.MV>bҿd j@ܸeHc̙b/ q ^IXla'uQ37?Yw@M&K~C[*3&ϥL*@1$m'Y1KFZٹؔko~ꡧR+>lٱRRBX&Ӕln('MM+:S~x{pe[wC[MpoANfĝ"G$"o2YV}аۼV+ %16OSމ2n7ޙo5GвQg0n"@j>AVm!mœ4 L$6gV@?*v6Ys_7x|qCn|Oi(RyLW>SFb݌Gr ay= l]}{>{'*U ,YDS(ru2 (HRҲҔfBϋ@+Mkvnz{o_eIDATIEH ?D=,8v΂quܨ S/"|e7گ")Cq&a cH3yO~ƫ]U|e! t l&\x팴)f[Hӧ u'FyEœ;w99n4YIq{MmMkR0A%cl䂑.^叼1j&2" gXʌPMP(.dP*4ClU\-U;ٵ+O}upUYٌ:*LgJ'͜uΤ-Y(e䎢i%lѨ<F3$ta%P PD! Fj:؀TWoiula}ZXO2ڏL  = w g,?Oa(zTXtRaw profile type APP1xeNI0 ?2ϫ.9 h9ĤYLq )9 C>К򥿊ekWņ/u ׅc5tg@+˿OjȚ(IR<6 8ɔs;C_lB 3Y+iTXtXML:com.adobe.xmp 512 512 =cIENDB`fotoxx-18.01.1/images/folder.png0000644000175000017500000002352213222767271015114 0ustar micomicoPNG  IHDRK .zTXtRaw profile type exifxڥk*Yb9A`?:}ѷ HL}¿8r.9HFkCl>mϨq&j'3X{ۃӥ%xjRkZFzxR95#A&&=u +r/1E+omRaX?Y!^EBKH/~:܉Vkcc!}Yu*"-ϱvZъh|FEÚåF눜fkҧ[9t}tO2ygocusjޛuo7;Fp*f e0t+[ngBKocS}=>$}#vQ0^s͂.tu1'}ͦl:uVmؘgK謝r30Xlet&:wYئpʢ,;Pu3\fўiډX1:{SedGaf~l,EB? ?FִĈ,:S^,zs]q}jUGm/&.ʾpq,PYvb,I}nVk:5 yný2s٣g=]SbVgjǪg|j'3Q.ޜ`S&5< tKY@[\\'s3jթ ]r${EZ=eIK2bm6\Ggc﫳S֔]bdž|kl_CSMR_CJ>ȵE(YU'`I|$!uHK\SKi ( 5 {>PV1I+1#zӇz,^O<.kn2~yL;g/1TF{,v}iZ "Sp2tޠ(>&S(_hhiVPZr'5cנ ##YgSDeտSf*1ʇA1@6NjLa{/_h"PsBIT|dIDATxٓǝ߿Y}sbp 9@ (Kki%˰+ \PRvb?_'i`G(n0WܥV+\ P880'WU*{fz1̜򗙿%@!B!B!B!nA$U]>rp R'z ,,@8?On|| iv]x ӟt!KS+ymf=|,W궐Τi ʕ+aٿ7k/ZyX+wD_r n6l)RR/\r"v'It:0 ! ɢ\.qT*%ބWIgNg6ͷk|7fA߽> R$%-'?ᓦ4t-Y( tDZ},-=FhKRižݓ뫚oT*6 _|+2PH5s΍BӗbD\](ݫ8A~cPT044Ru$8Xϯ?>/ ̹sgōn'@K,"J!JX,ڵ~K H)?FɘãTX_* VV3 |g>裛7e @JO`asܻw 7wM**v+ B__1"=5llsgƍpҶ^'OV!ɟ-Tܾ} U?Ç1>6H '18hTR8y{i^ET*RJܽ) "w;zRJ,--'wnR ,w.h;B 6ԛW^巅pCPss`e9IPݻ~/cް ,Oe^i#ѣX_%``N=S0ɜ5`apAYfaYa=4#}+bn%M7GkH5T N:)wϲ,:wkkhfͲRnHkev" zM@B eYŴ~>/] RbU2~]QxņRF8F/N= 1Vf=! EDD4֩Y[+A"lIPJЦh ߉|MNh P.w^^\n0O)qZU/y{B4Q:P[9YZ^F!~~8.^|LO&$nݾI'M-@R¿B_{7E'`.onnB*&+au%Rlj9}tSJEpM%=N[)Ǒ(*(+T=eCTvu|/6wތFKHsh(xd|bi._cO?&'b>}Gz)B_o}r]޽7ȒV|L $=GK̝{z>>9֍X.ťK!NV&&x'%ph42N\tğ]pb0=2 @uIDo~.~nodxδ|bˤS)a׮Q܍aݣ/&vH.,|sV#{l1Z޽f\t^83oc``@۲z=z4Ba泄4*ݻ/W166f$J.7o=%`;6>Ҷޣe `llGoUG 'OCu6}r9(f`Y^&\+P,mIZ.\8}v!pqݻ{v$I N/Sїwa&`ϞI/G '7ۏlfu*! !N8㍉;hHZLk|~ܺJ%%$IT ɤ1=}k_?<$<~3}ac۶b'xxt [C8v^X{3XX|̈$D C&}cbB&'ci?Uccc(x ! ! :͑!*BzGOc%i     i*:t"4*Bz*Bz*BzOJh  i@GIZ0T0T0-V DH+i$~&!U$I'6b,; a `MB"܋hBHX8~:6J!2,I~BD{Hs{ !ҡt2(ĉ4d-ljMRb" `BB ~v5o֐0wä_s4@{.qE~%@1!jk Rp9BBDd;Oޥ\$[AH{cpv Y]ȸ#U!qbk0p~ $B:nsB@J'L&[AW*?@H{e@ZD*,N@+Bd~(PR !F57`=U]Hz}(P _h-]+!!M+bAn!A7 M$s[0!$ >1!' !UHn0݀ @ dBY]PJ;, @Ijn%Q9 =H84LrSˀBl@n<#a(|?!e'FhR܌7di{/@C0u h3$w2HK\`P ?!Q 2>s@Ho7D gHNLh%R&' !q "C+v4!$i aW``BHRqCO(+OH+I`N)B<""]$~2!$L7 8݀^5G#$: 3%$K&?qSm6+  If(@ *6 dr 1ߖledےF+4)!.@Bewex@5-%Hճ> BD[}–@@ e=3!Q>@:a)X^eX,2F Flox<WOK+z $IE3 !4@ -P(!jv%{ÀhM@;Y㐴mFJ})~`+ӿaN@0鐁 2RmgOx'BWJ@W؉D*5XCzYu 1]U*Up phHN@BBu%[:a=MɢQ&LCxC @n0>`үW.mrPBP®z@>U? O@5:c5Pn:z0 'n@"SC Dނ+JOiטg+Q@wetK>(MdCVV@KՆ?ȼ]+i~@~J'$\c *;}xw)-O= @LU^*@uBvL5AW PD{~kUa *p']9K PSH8Kʽ蘜d #)eAl/=&۵DnbT-3"}KOHC0uZYw}ڞBH#M~]+MBW }4nPMfʠ |+@ˑ yK `yk4-Y:l&ջt4@U=5Ir$IlIlwZ2dlBD\tYPP?@G+5 i,ꂮ  ݶ0چ缜㏲rq[ aNHO*D]Mu9Wn#K+ QTP,b*B__ͣ y֞Q1%\94YסI@UH /٥؃R֭y%T*eT*8vur֝XH V\=~S%ȲDh[wq?7]G&3wc΃XzTC WTYeƩh۶bx-`>[o 7o )%>^ 'kk"E}tUp?|ᵵ5_5ygN9AұYGbmu f٪fg)AWJ@ؕECzÂ+¬ʟܺ]VTTpn꬞ަR?m? Tp"(G˩7,nU%هK !5X]Y?pN({2 "fk{KHTAܽ~}lTB" J+EUu^["FB4 oߺS :([i}}}K҅]6/h;Uz(bpșOgt_ -+5JY#;BrP77?( JKjN@);-pAv+W(񱱉 !\.ggg?Wcz5zho{byF_g+SJ@\s(C>U Uzu0ys *9$B.@`j+U$+yiK!Q W=}@ I#O+}V(J?BأeJjWWi~u. :6{ O) ,mf@u @M铀Qz.?5Z:@0+" Č)p|}ߦ~&-xj?եSF1Fd_5>uv9' F/UCZl `$B!B!B!B!B!B!B!B!B!B!B!B!B!BڏXp'5aIENDB`fotoxx-18.01.1/images/overlayTL.png0000644000175000017500000000164213222767271015561 0ustar micomicoPNG  IHDR@2cJCIDATh1 0_ApY 8&Ǿy}V'] f2!x>f@WNʀÝ IpӚX8)wZ3 'eNk` 8i,;2p5pRfNʀÝ Ip9"=]zTXtRaw profile type APP1xRmn STm̄J3O6xyNUr~gS+%/~AN@p>[c>ȻZ#r|ȯdQ%u#Ѧ F[3#03&6 v! 5C.7T_"f0'7Ã>NYQժ>4a)ҤJ-z`I̲$"'yDo}xNlYdK Q]iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-18.01.1/images/HDR.jpg0000644000175000017500000020105113222767271014245 0ustar micomicoJFIFHHExifMM*V^(if%HH023170100Fotoxx:trim_rotate| Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?O_(n渕#ˤX3s?l>|au^jl}'?DWqsW5F|=$~X`e'=Uv-HLJuM"O%Ae 2MkSJn6_wf/6c1$ 1=G*AzHG{^ ?u?J6>$0BkkIb3\<#úޣ+[E_> %aq*+??'Y_ j~ = xV-xG\ v]ZVVT\MlzP G/to: ۻ {eT#( $Vs$%]8DQ  |7ag?K+ |MZ:Qu2PIlmݞ*=u$|"}J]^ZJ7eԉ2,88D-DQ ??پ% RPҧ;}(e+ # |Ehڝδ)7#ʳo~_>>}G+4υ~ k-^$ПYy[xFp v_2]`]Xqs?t 3^!f,V4+V=*F!..|2v23Li{Bko9_DQ #%Okÿs0H4Җ#2:'ko_3TmM? YgPၥ`p8z9{i_x@D{7ßCS^LAu# I`W',qzW2zOMH7mi%Brp09+-qi$<11(35 $tO} h% xkxev(C*$]x|0oo<wZ|K׏չkrNl?%4/7 *7#Hw$V }z ׈~D_S\CUDaU?0l08No5p2\< @23>Mlm_/}G+|iš|_Yo[T&^dTo'j¯ xcI2kɪxB 9'U%TPPFt2)QXz}ӧZjZ}M;^i^QʽĞoaR}wF6̚זBJ .`RBO ӓFHэ6\9ydd(,k"d0U5jz-6[m ( CK[OO%L%ܰ*/ oϭvWWGvwkmmb1٠| ݺG'cw8 O.D;^9" }#"j:ZZl!?c؜^0{Ú5Ʃ,- Ĭ0 pWz^st 4?y|ϝȄ$$d+0Ƭcew'd}K,ss, }^}QMDW0,=T_NßAj7 \LP8#o|*/u#now!RDJjH8h`>#|sSy[✨O<gE#DžTpHMķN)\H};Ir=.ssP0k ۯjqRXFB7@ ?^Ԅcm8ߌ۟N) {㊙`$9^" H曜cN`@1Qa_J@0n'=n@[r9-tX#6DΊr~U`+wß(~5x[Ox|@dJ&h6tR #ɮÞ+|!&>[ʺ}#Ђ8 ־q9kø z,~_jM)&f770Z[[I`0ę8ŞѨu-#^4jC3fUfR#Js_iø z?`_Z={ ZK-:4VĖ޾yQDkr"0M99ZƏk:}RiגXX@ݲ5; +_GU#<[C'[k?UM;}g9 ]%Oj~%|Cq+t쮢(XD=\`_Z=??0x'M;u[j0ip@\%RCZo_\}C[i\mH_h&lkwßIy]ɍ ['<rbb_S ?}sC*q9kryY攵įǦ01[vQm/<%sߥ}sC*q9kY˽I^\ $Kn|=i}#`$۞W???0xwßt*,;N$u8=x=i[JE8"dzjn`SC B*je'}i9+5"eP1Re#^)1 1Ӷ)ž82TtCNx>NVܣn>PyF:&zdy  1M*qϮ) .F8❷) 1'H@'1RaO&x@Ͻ !Q{M9p#'aԝJri|Z𿉼_ax/Wf{ud(ݑ*@8# _*|4m~2Պm+RlP̲/⢽vOƶ&_l5S,e@'l@{F9J~Ou:i i| ]SHME>p$ uA+>8||0x5xsCv6sRM\6yl6HGG8+_?gq39_+-5K_5O}I!Ka|sm EzMb%k?z ??Sw]m@Zީ;.DT[tP'"V+_?gq3J~OuEb%k?z ??Sw]m@Zީ;.DT[tP'"V+_?gq3J~OuEb%k?z ??Sw]7;%OkPXS=,y>Úw㿍g xLsgn-Yŷ$y{!"V7~8yHl4}rQ+EA&}~~&o;;7FT,]x?s iDs$ʲ}ZLnc&<#i^?- -O[3vQ%Ȭ?JJ<`$Kqj:) ac9}@|O?]i|yhavVQYIҾ{MU_w?)|/?4OOk~[$U}3?x$oMNj.sfAth2Ÿo([$ r2p*TUh{wDY ӴAռc 1@i;sduI>V^U"N@?RykOjWZPhv3fPgݵSxEg>4w]^L'SW:쥦v"c1h)rկN'8ω  /ƫ6n;=1W'8VE W^oaE6^NsAםP!OZe3Cׇ7O}ӴAA G*r\F{WQ Y/Hܯ= ixAqcstuJЫ 0YT<Mh]y(@~¢`A9TR+e/:?9h] yez'Gw-K/(~'c-  tz`W\N_[\ U=h1|I Mzv6s旜M/޾ێ;'F3b1J#@TdiT`8N*}rzv)Ld'4m9inOpi@=w ֜OҚri=?£Q)ʤzu;e\`up6U@8H#'CT#r;q/}5&rqҔ p#NqSơ'Z-ίe MY6X-M[1yWGpc+K IedpI} 8pO88JrPj6e<}NdnϷzi~}i "'>ZS/=vr0Ji`z}3RUC!$s֥9)@ JU$3H]}GZ+&G|ؿt>0$tem"qRƪOID?ǷGzېq1H)ǭ.vcS'9@:})Ҥ-Hl}M7=N(19_MHpғkz`Fwݗ-iVmeKZU QEQEr>K?]0{MB2a`C*d\i~ygF?<3Um3S;mn.b;beNtP'H׼A/n#cA _#U4QH((w?.E5z/o:$ M^?ࣧ3c~[$p}%s?I|QK|Br:sPf:9H{O\wH }30^ڮY_^on2np QSAcjJ+4qZZ!>cRw}(W46[kY` =3X1wf=Iɧ]Oz-&mA[VM:Ȑ/nS$d}{_ xL~KfeMoK,ls{O-TX?cKjOA8ȣ?&_|CiB\pKrn'?*\uiGVrN/b=mV) $j7#ydnNzj=x_W7;3-\qNv5kOIe|&Q9X5 X>!j^%m]bN$}qTs+:RU nIru Q^#ǭaYK=jz \),'Ң6öjH8eKxqP^rRW6uwWWOEH=iQbSRrѿ* 3}W~v}ࢿeJ~3*G8\S$t#<[ abHƤT= )A14ckG`ݺ49S@j3'Ӝd ?FWݗ-iVmeKZT(((( w߆5.!y+4Ϥj! ,r\=j?ay) c_@Ԥ]1㙭$mC%~eΫJ__|PSL ڣUFuA2HH<БL$>0muf@5xzײiX]C{es ͻT#!RQ@Q@c7/COǴ+ +F mÛdzPu:ֱ?;( #^;ixwL4U"+keŒ'1$ĒI$MmQH((((w?.E5z/o:$ M^?ࣉE|$V|PW}G})HGA?o^6p~5|}ͪE$IH7#:gJ+!# 5"&GL< o'&LVҭYߕd, }@#[+g GCMpLg9޴̷pDs >_Z_Xl]+aI-EI)Dd_jt ^iO`\{ ;O 񭲿-lH=n~um9Y}c/<#C^KʱHq3(heQ ~?4oEeƜryl l. bI9ZitO~..adVzrî9k⦵-揧둥yBi*; ׳~̾ Ӽ!a:FsBkuQxm1!ay~i7B`#k)Fm͋) 6Ҫ;OoCSOG(0I;2۔+dR,qSjѴ^O\6sI=܁"=ُESM"2]ʜw0ShO']}*~١$Huv~Irr5+WiL,YCyLGͶ?"+4onm,o5ItD2 qg~ⵟ6M$ onR]Q_2mpWP}q޿}?ࢿeJyJԀ럥($篡yHP"1Ӂ~s1J3pH?y +2s)F:zN=N\`ԻsIx,xSqZPGOҤ1G<"8FHL|?J\.V0ARJ`yRq֓iNp*o, (GPxU\$ I91ȠV<ZyCCL$9U#'*7 BޟsֆQ, dz?xő-ɷEs^Pj IM9[ |].>m5QLl|C:Oa+_GxiѴ諮_ʷ r`C9k;χ/|QxH08ʼn9\SA-̖א=p̥Y~V5L"bUxJF z1 }1X3D3`1Q㚛 8:@wdcR2EH0)?)jǂ03ɩ#RzC[#ӟ^B9Cb8*}3izn]VhIv_>wT&ƓV`%ʩÒId 'wվ)k Td76ͱ ~ÑЏ ~В5o#TG7u!Z2*Cӭ:h(m$q 8>g|8;RoBO>PF<<®pGQEQEQEQEQEQEsw?)|/?y'']j_ ǴM l~_I 5/jз]F`].r@fiWGs8@+t"2D9?R?& b0}x[麷=[SfR{֍[t'}`sr,phh [G,$p8.6}~ueZ\iڥK[C^n'. ;'p 7Lcwun?\[ +JG^ko6b-$Ng<ܩBc Q? }x&/l T'# UJotnz[Ҝof,}qS+KM>ٴ AV|6$uX' fY67_$b?2KS)YDǵNp{ҿλmq ktܥx\cnv Q$͔Юs|)cRyIYjx3c'*<, $FQ$ۓS;Z\J}>uSzoy(Ӭ|Do4so$ܽ) <7eqj"k˨/p6*`>)Sm&$_bKGE}Dnn$XR\̢pޡ~*ۄcDiρ]|\uyL4\ig,l ;xk xC/b֗sw.Y)'vH:W4x_W|UjRiZھ$hv<x0pΚַ}+.yKs&X`^0WRqn?&vG&zq'T$t'$(FÌ{fMkr[aUwF<ҿWńx ;Q *9m@$yPAzk//mWT=2+Ĭ(YB'hxge{>[_ό7?gOimqj~<7J;vmcUԮ3'> xO-jCuTY7:WκÍsJLXAnksg~?:jP{Tj淇 QQk3`כnU+9~~kY2lGb2# Xi-ƣ]A,y$-kVw ]f/5° 0>b 3q+%8w'(mvL)8+E䶿.5s6Bc`xm!'*yh{ۇ#_׭L-eiJO@Ojq٭*<O#;+-Kv4XY%1ڂ^>|--R|K*TVrq FDb,r={H9oW?W~O(Ҹ+4|s_JE:pPRYUK2 N 򎽺ҸsOǧ<+:0m_#9= 4ЬH rjD]Є܀Ry!yV\݆m88Sy,pӦ2Hlb0x8jRq)ʀ/YU? Gip8R25 #5" =QT'ڥX0 v4cs"!֚|>ƥ^6I?\S,aY ַ|!KLYN $k5[vD^V8$(O]cY'o +%RN'ȴWg77-vgb^?^m%.}:sXW햓#9syUniq/aҦ<>Ի w3 /#`Cڭ$c}joZh!q#ԚGqlqOr1ӯ=6W1P1r 36O< `t94B(.~6y:zsьI<ԌО{S['y5c،5ʓf"0yDY4?LPpgR<0 v@#Ҝ|r“ӧjo?5/ZҬ iQEQEQEQEQEQQ\EiO"C jYvTҼb_>4Ht߅rGx{̒B  $7SaZ~Z~$|cE\ƗZún<̚C[ I)+\KVvs ĚDq.7BO˱Op|uAe _]0Uy}'SNA8%>7g$s>_;ǔٕa/=Z>Yl=ՇU`x*@ Enחx\g$gH|-H/D @GԬٱ+|!)H 4QYAR ʜLԩQEQEQEQEQEG{覯E{MU_w?)|/?4oU~#羏S"4BŬR'?c=o^-,TSk2+Vpsqz c⏡H+{k%=V7:rMicX$;gCq3k? v7h$ۙRR<*(9/qP`n676}tVrcXFNQo_=ꚶig-PC4)g=[$ mS(#1$0n ck7@[Uf3::}^ִ k C3BYF8t4jfܟK-Ʀ0N Ҡ>'wg @6铚Ҋ;Ku2FzRv9iMƔsck$qG`C9y4%,sұՔpӊo]比Ϙ+?ady".~)Hr#\Ϗ+xٖ}nGMs1^ưKIɱK˻sI-djOXrCѾ-k>w4I:ܶ#.ӊ괏wjB]6HY22OA/tWMprT /8灏°/V飞OJ%N3!^;4>=Faڭ!;G n 連̒ʬ3jʱUTV#jU>]UL=BJryһSƷAqkM2Xf,Tc8${JSW:4V+d$sd\ݝ H r$-Q:Tq s;|Tg_+jM/渲ԃЈ`8c2{: O_jϕ !ܪ{W38VLockak3?qN{~4NQاZ#{ݰ& ݭJ(h%_1$P;xe9k?ioFĴu7IgKߘB|֍znO[βr|Tndb]N0 :b*x[㖽~]SAIXM[$:`׫˩|O ^xR5-R/]\OEg־Y'+?e6E{KaTQ7}1w%_UP>$\$}Cҿ c`?R`ȅ9M]r:GUFֻ3ETVw8JADYqX}bq2r˒{ĹUl>E~v\3ڕQor2Ss*J?W~w2_Tpמqt Wz 8t_z$9!8= G.h,kÿ?g?G&m2;u:7{@o6v$p+Vom٦yn-bE"ypw\wWGg_"&{^Lv8'׊)~ &)[E,G9?^v/PF@2x۴o+%ʌmz=]W?WJqR| /푕z_ n\9mP܁c>-\}?+/d|e '͞NN3}s_ ܬ HmmcFHuQƴRk[ *1n:tFOjvNsM*ASӌcO^A}O$sF@cJab=siSҐ5eKZUy!7֕QEQEQEQ\׏%x[f&vBӔY.J'dh298kÿ ZNvO^yXF0wK!Xr?i&llkv8P& F^ &kyn5;ͦY%7̣f8^rv(W_E|Y}$Zd`r ep"g$*GUt%QEQE/t :d8s:2Aɬ>"| 2O3+/[-r}~|O-*[ |J6 ŬpM xNWU^}߂z5xuÞ,E{G4yr@Ó2k}Ox+%^-uJ.<;/2cQLj|Hĝo *\sepdeXX:RH H((($ M^ξ#=?SW_=|*h)H+'p>:*|!x[ﵹfʈ,I$&s^"WſT(/9#?ߋ&~'M~&a?1%~~&M~=ǩ'~ÒC7i'o(5 HyEſ@4_䏇Q YWX68}6Ro-dSy5qջ3qңkC/^!swo3Eeϗ=8-˟ <ۂcI!7ɪCCAR%I]۞gPDV$Hg'.W'}VѢ[oet/ηR Y#EXFFH5o j<5֡[BĒYr}_W`~UVv\sRG^Ԃ>^dc_Z,CV@jj޼p@9s_=|/x6q܉s;c~)kxPxYөO]t D/G]^hnxNyos~ H 8.ccԭfm\úͰ.7w+MG kj\]BpQgξGxwSf򌮶vĂd# lWjsC]m%;w9/v7dyQ_;C1J0ʨ))cvsҭO :čnA PI89v{5TȬp=LF9ǽL|܌SᶒyA2UsOҎs~VK_ ͩmL#ꃎGqOYߴnk!b3<kÞ ,U29>?6ק?PG }@9GVt[^Un9P c=s1[> Ks`":B[qs?̮ev8R2 `5q2]]yX{8S/4WÖw{]VI<iYQݸکxLԴ29#Y#_랸$w3܉.aDt#d=sǵKy$*E nl_C*08 49#A8 u r$zҕ =zTIt޼sAV!sA\KgO3c%QK~8+6O'#XӔ?|;k;k?趚흴x̉daHs6w9h<C2GbF@޻Nt:՝-yPePIw_ V=VgAӭɒ&* SM-{>%}Z[4|SWN/OY/̞nCCwQY.~KI?/?Z}}ǫ|;&hNf5O:]G2[kO?73e[iQA OG 736@W}{+/~_?_WKH? ZњL in++R~ʪ?#xW񏉼[xMY.t[kˤym,VN<0OoѦ _Q KozgůvFҾ4zE夁dW;pj䏗憬 _Q KozZ) _Q KozZ(ַ=G+/~_hk?Z}}ǫ9W_k>¿[_Q KozZ(ַ=G+/~_ki6w/ɧiӯdEBvT_]As_Q Kozg^iG7*iV:v&?t,1o޵FNqF3]ĸVMKKdep4;XH& w):zmcV|9T!Z>3L&QR4U JF;T3TL= 5)^i}h9KEc~כ2+e7_lTmw?dZBAI$[&5BIj^OZKp ,W▅n\:y\,[GϞIb3jn>4漘iwqy[rpz>'/㍆$d0\A>K>Ѣ-eG'*qsEޗqT֖WiE˜e!#ב)]=-?-ww/Q2c?1MwY]/Zf]"w_ DaQGj]*CSL"^|Fg2OmwRNp+IIFWzA:i* U҅e!3P={Tz;+yl$mЈH/\%! |8GSTѴrD&H t~ed[>t: %_6.@}Iʍ$\BMHFZ$pU$X89Vj6jehgmG-K*~V:Z#yWOu&D;vv#Z#7>8PA>\h?SVHZx&ѯmvyCRwH I*uO2ΞmՐL s $[f(UULJ'󪺾$,h@!I21Y7es%c"еgt{nn幹{+P1O$5"CsX|g }WۍJeX">Ifßm=Njt !%qּX1m;3y~ַ7$*lL+yIF%O~5wNI㸌p*E:u kUZHXr}2M:2hL nWs$5 >x\KQjvoM};X5Hdhշ {z"bq9)tdv9]i-, NWx!zN59Z^ wEUrGU98(-Ms6esӟ+VRY$Kg^n585O^]3mc0 9|z)Z﹭ lIexgV -w9'vH֒P}B#rdv$`湭F%rKFv`r=q?u K?*1JGקnya֩_q۾9$g;؜\2"%`9nkZDoQx7L,홬`S#^8<A }pvwӡW.J1r~JExjoZ< `LT9tx4 }4PXv NGE/׬]:o!T6I3s*-y%Xl*HC. ?> % FCo<|+ZSgwr5r0V<NrrZg;xR+-EP:O]xn 2C+@x6oo7u #KnqyC->=Z ㏌ w:L׳xG`%pT9 vP^/>om;EBѯfѵBuDrqOcavPOc^' ڶwW"} r\F ebW6Qx>~1WoE6zVw0~Ő[7NԬZǞ`ef $_TMڤW6փilH ꡊdAvehڞڦJ4ڮ=jz_w~ Xxrx2_M-^]ϩAW%ks\-آiQazڽ'#O& xC?'jZ'lh `?֟'G𝭷¿l|;ƫ6}zSԞ=<x֍/5CK/d):"CFHs)i _WG*R+^BLjFt*[[kFJ mi:t$/-7,p@8˷0r20*pA EZ^l6>{Eǽ1GJs1QU$¹Y"=i*Fۊ̧9z}1@K?-iK2c'G@PG?aC(C ~-c9\Sh7}j7[cc|\ם7'Ѯǥʣq }垥(ԐJͷ|O#ا Fy^12<@mƙOȺl-g:`P->f,DZsuYXo 19Zt]<;L`I̻x ]q d kX{O=vsUdžm=9?=kf({ q}wH|۷sRMwszׄu],ZCh#4L#_g~bUO#ƒ$n^u~"I%JJmDƩi\)eې3یsOA*QQvUkx,QCtZ#. ?Z j'k젾,Qa3gq]^YMyCGu[+6ԣy%wQU/i$:ƑuẽKyV6~[D !wTýO]~'k v휟^:i3*]6Gڪcν%ſ$&A5?Ozf:Lp̊ZOٞῇo<]ck,^4Oڬ71>˕~s^jJr9#8T|s6Q$2xfjѢ-d3v?C4i'2ܢݴ$P3BmgiBI.7"bh9ʏm'*G~u)ӧ0F[i=:~5%$~$M \z]a B x;s{C W:^bjbw_!J\rdcjѴծB 7ZcE+rm8w>-f7spx^j1ƍvkV#Ak+٬hrI Oaxt+zr{W(߱B{:~iN/ o *vOxWEnK`Bv׃J^5OA]hXA۸[1i^OG]/D_@~PN=q]Gݵ~Ƀ\׿5Mw0 uG\ԠS%!좷9dVs_~}XYhwVk{+ɮ% *@?:`WQBvWW IJEjYkPG< rەA|;vNG>O&{%wI[wm8614O9UF`95'ky~jO-x_ǟt_ xJƅ[ SMQۓMEI8O>+?c'k x{9m .1gsD%b2yj5)z_x?W?|[I6gTӮ/IŢM qr1׵/x-hRxoU&细S [ىluu]U}NHԞ5sr3'M^.>ExzϡY3B߁O]D`X*ZUS*)hn |I/<]–p_lfiJ)dI ҽJcӭKXUS 1UMš'!Gxm@qd ӛrZ(((((((7W9]oC>4!sg.$2Gb+ƴ麕BqbI(} z~Wn3liҵ'q.7q'Ȭ@Ōbbn 0GƴtE$vAj *Ðp<+ UWM4}Rt"c]I2P+S #2s3La]h6=ݿd->n}`V}2 ˮg_YcԵ5- [OXlctُÒrN3㋨jE%yf`r G2#(;>P'iA MRX_GN\ѻ<)FVAb~Ks ߕxW|,+VYAr쟴κjZGmH[kFO aWq\YUŴ6^b9.n3p$'xCx!ՠ^AGr#,Hݯ`[g{TҖMEF g?~^8oy&ːcr]d @*}r&1\ '_hP|)X^Y#v=kmsYm6`uȮ*O6Fk>QX]_:^emgnܬ9Sq^oI@8Nx/:LwWXUہ_1d{;)^ǩ&`P[ZVA\ypH𸈺I=N+"1c,2\iV"c!d#mpx4[sIڪv>suڢ+VJkf/ݤlUf?jم1J%Iȩ_RdHYĭ:s_EMhE9xV!Ԟ_Q)Zsmago5ERk,(dx138ϹFf%uQ:Qj.^x/]'[N):in"HW^0=H^kxGGD5q!Q6ߘbꑌzy/.GC} xbmJY`2ij+LHU{Je)?CrJ*6k|Mm1n,$kn HrttZ_"puot kzJ /ח"P6_f!GRKE` *t =kտhj>Җ"t`T A;9nkɫ 4=XTʛ͝12<;|ĆHūFmd㻏7c=Ixm'_% 'ֹK;"]#mA_/%c9N}O?xjcUͽ yA ~<v:qk~϶:Jj3 ّWx||#nds^Ӯ~ޕ麰-nGY$g?ƼM= Llʬp1>W}:[Cu?+:qp};h(/WֵH̨*X&8eD>[Fkƚ5m ]<r3#Qt&Y,LW y~iA5:?GT]KF0ȼpéfBwr3 m=#u~6vP%Ģ|Hijc: (78'MDZc%RUDqIrzfkvڷt Yi=Gm,葉ErQ+֓Di.bѡXdLf񑜞"xL./dӮURJ)IψT$u`V<4FZhr޲݁#:\,BD3!51yst&id2̎nFud zc ;.rKs ^şy>о|'pCd~:tv||LQi7I lu`9\tOz?=b !*^ Ā6=5ƶ!Sq(J קQZtdF<*+yc`ڬso1 +|eXas9i"ŞҖv}Qj3&W@{8<~58GڱT2c:kB$,7<2CkU $4=n𖱪|;nmMlX]dHBOp5MkupӶ,!2=G0+Oৈt/Lڇ, Jg 1|ZyXj:ŹH'RK tM7N?fM5ݽ"'ʁJڇSK{;a3yu ۥykOp}IH(B?.Q Iܲ: a<8DZ5_G|iڍ)6&n&%P7}8qj GV-la0Ed\Њ|S>bp/q?O qױxCT֯<#Gg=UvHZLAWZ*-w>,c '`̻yQ'$>P[I#ێϳCzuH^q. d́О@^OL+.xAwQi`xInQoFtڂMʵXӨ1ommڳ훒W`ONz⽃↻iះZGlţhuhʫo$۠,qKcvlم#`0ֽW-/ЯXc2#}sF@'fP|qJnߩ IܳiyXۭől$hb1Ӽ68Ah%?f3O(gZ >&ivm7Pіc=kBmfbfHpFGZ ލyje%uס 8㇮POWnMgU@u|3Ǻ%4]b3NԓK ceo*HxF䑌n;@_[y_(Ѽ+ k5mVՠo>c-L \hn NN;^g#R!-$,ΘMviwnyP xÒMK$S +x񆣯K^难V4:jܺ4.d&M*|=+~>3uic$\s#$p\sCMQK3N> _'~aoR#^YbwImHr7dO9?4 šYzNӖH2{ K=qMZN=RT Kֱ4>sD_ۘQ gpf9 E{&Rw1|K6,THbI*!Fln ;R5u^L?UcM}6vw/cNIuhVxl DW2cw0-]l17Cc?? eymnzLiмeo x;wMǏn7_j=ht e՛ʞ6e ֖L=W:lQtn# o-A@8 ]=/~} W6ۦs1:~y"OHJnюz`׊h!1_VNK-;GtT+rryom߈`Xnu{KmVg*\d)ɅTMr,P(,C $>cZ6Y׌2*&U}<ic~okxJ? i:ѿooZUټE,x~be8obN{|hN^wPWZǡ!E .g2$k'ﳶ5}Hv>?{GM|gFzi>ڛjH ZT?γDqݰ*¯u?i6^*6hΕ&imi,[^t"V i ppm/Ǻ3<̯#KL|n 3Yznԟε) (((+{MUWG??nW||+vsҼjCh76Ln@A_hGh4VKXґ,h8?y>%µW%;pΤ6ݟABUh$Āgdzh' 9hr-Me)[Fk_ccLl]Y]]D ݷKꡗֱ ̸mcc}kՌ>+۷jۨ~n#ҪGm׻%WLz?>N'؅-߬7c|u<¼z7}K;{( ^GexɎ[WhG8zb}gHټ׌OQWޞ$!.n@Lm"ͽ ow6D˪$JM݁>y< iep>pH}e@+H.IR4ӓuR)4li i}6M W_ ix#۳9\w?gZ[h-s^"J;p/QNJt_P[i$专Rp0N3]/s|%hh}Z#.>Ղ&Pڠ(UzW&(9b%_ |I6SIkPy+1ܤ2ÐGcW4h:,2 mn9e%fdxI]#WG+l9aoZ.꛾]uɦ[JG=ӔZ{dB}ɯ+=,&gEqds^<1O%4d}j }jKB!wok:ᶢ{VћRiwlpFP18s|ڒg*S(FnÃ-S .3#֝{@I#q8@m}xWy+}}R̃M[T7R$1{⾋fMCNMj~"  0s8o(m߱me6PI.' ~Cz# -DV쥕NyNI40ilVP$2I2.vsԺS@Dӯ ]y i?boH 33{'5։}-Xӌ[b8W;z_~km[{pðXޢV"W4`cߞ<%S} &ܒבKlW5:eL"(6g8qsR]u4ab'r*F:}+X-:As͕"]9?Nݫoxva)[IOx5 0kBi+JVV[K;L#߉FGbBNj[iWR/S9ftHz =mo 6ܗinKD='Zu)o*K7hJm?ƼXU rI_N}Ӷ>B 9"?zu!F2"X*AeebTFrq%iF]<ΒTsWu<}$lH?1Hjr|u@:R xG}_@b"\񞼊c^-J6L=ͼ퓂c>;z[iwז-'bSqg޾^Or|n*#?͋w.ɾK0d#w&cѹ7;rJW;}5TYY =W[p؄%f1ƮԾ zAhV7ѭG$oy$)՝EBۃ ^ >?u> ooz ;Gl}?vr9\"XTuhMHG/V +?<6 /pA_G~6cx!]GQcZ'T<;sOi$MqI2brc$؛qkofiqq-2;Y8^9s+Ct+CS~DOsgw_~&;OZޟ]O #,B9C)x62$ |Oޡ3[a#Oyfi iO!B*ČdsӽqNoKnBJo[[ \qY #H(Px }G|jo5$Ign!y#d+2)\g5(G({ ֶUɚ ݀;TʅI#+٢4GS ,y=p3f<]<=+u C_o6<J&X\Z\]N"A u*%ClQ^dLٯ>2D5Cb-v=<׷t"ᖫd M$kυh֥;=V_{&q ݻФ'1̎8/DWy/^5A+/s?|W:ѯ//˻V6RBIVR SЀx5)ޒO.Tz-5(B'pG?үBLUCnzu#~(u_rk[_ml (2@=k|miltQ 7 >XhBG88 >4Fд{eSZK$SToU GBEt> 't[ Lrf.Ivi gbI.X$stS𕧆tW--ͬy?(vbg1W*߳׀Da< [4R 7:2 9F08ME-念u.$f2fw`ZR袀9oKk]ۄ͍v7L\7zz-X4dbTHkU5'Ԭ G]D#}U%p47a>W<=5[I-n-P``ea9<4"OOlm7핾d=wB=????O̗XwKl4Y44Ѥ(H؉Qp5{n{FG&8d B] zO&?xk-qKҮmilㅈ%@4 ݮSs}:{l_fmVZL@y^c[˹hW1[]:<"3t85τ.zڧYdQ] JJTH7™TE|B??U@V ݗ.IX&=+Kj++SVܚO..ia|a3\?mD8->\Aぎπ?G )L̑AP%5PXiZkm0DATQEQEQEWG7S@~E: )_omX~$ܨ+|`dq+,V+-A[έ3JAt]Yi$8c:Ws; " jZhjb[ITBq z^ ֆKq@XEKLmu5_4g@/!,\d.Fۂ;^Qnh:M^^Z`7q$MGe{4gk5s CR5Ep3C[o&@OK(GSD!m% o'@gE)Tg&zKxL[Uv+P$ Gp28^-OxSIӼ!/n..Q&dȗbzǥpG(d_;^2Fφ,'ȳy3++r Su >N [M0@oG茾F@8#!NzeAx>mK iuK$(Oz|Gh/nYs:sU:TB2q0z.BO}iXmcdxdrA" ~< W{KMfVҴȵ;¦@@\s$[X٨tũwVG5mhkm=FX5@L13>A<Ϩ](c;H9= u\qqԍW$u}Z+uXݚ b"«J9 vsm#R{ -Bdfnw`XgdgG_YBZ>7usx40v898ڍͽsmm 3 g2\eӃO=jjÙw~dEiSu.-cK'Om",n!bM^Zf8x0Yi}!M<ԗn߻$rVUe$tS#~>6_ NxuM7TY 3{ؼĮcw޼E~MD~Z i3Lb#v搔Wx/RBMc᥄ MhxUsP3r9"k9nXC3 Im>-;}.jT$曖ǓSFlS|TZKk2[[u~8nzmjsTUN[LNIN ŹNOO5O}kpJ^gmDe@zE~K_-umd(S *V &sߍcEF|pQJumڤzeMe<ͮD{Pq$9+;x[独l5nkU2HA #'ċq W~ojW3kcړM) .0ryFhfYysf kh1 \ 17&86t-~gHy$ْ۟A5ݿM]I\o0 tO=%{]cEȁv+qr'e'O<ߴ׾4|6⨵/<[sc$PiqF8RfVҞ8U2+%~QYb9c$ҡs]mO߀T]BmbK8c ¿ Sv"N dO^$e`Y 1pwGz{W;_ta{%rjW+)g?q㵙r#v< e|;i⳪ikHnnnyI#,dxdf&e.ZrÞ+aJ3I4 CD𗍢7iWsZ0; 8gꞻ8/ <_I\"#El_xWi{3~gO'Y*>!QѣYn&w.r^(&eW\9\=+8қJ2Z=t3*#xOU=kİ\x-Hl"%9f> Uc=ڌ4ah]ǀ2FsǭxBl,oE+y紕cWˎ15gFRFT9 XL`8BvH31\m^u/ڍީ3=3Ox~0ޑ>>nR Vv1/sI?4,^i4MNc2h.I0zes޽Zx4uThԺM׭m Su[-U6w ޾:5|oa4 =SdA ;q6ķ^Wi][Fc<0=4X;ܞumjhQ!Ŷb^@xxnkYA9+еvԴ.gFwmȸ IiCV՛^?z[c՗",&'Xj~%|2֭4WȳY kĿ<VPY޶`8'8^3xgUҴ QwD'k'qLSU&h{Oӣ'!Kuo1$brv$}矅 %b̳],"px]՗ZW,[ aڼJ%sтUTZC/Wt 6i6Q 995^n|i aV2I=RsԴ8 ċ"G>~т˥(kuL͸f2p@Z/j7u6{|"1]^ 2:fu_W:twmV\YH*<8ɭ)TI&oR:Uuπz|gǏM$Ӣm[&MP (Cs6mṽT/o汆tIAX)=V;Kh>2.m+MF=ЖUx(ӓ[ "ݎ 9G8'@#^_!8 >v53{|AʺxkI5))#at#E4Uo?W_ uOM3zj9|3,q, RO /. ^n_ Ǵ^]]+R]ڕ@.5o٦2s;^? 'E(/-“H;#e|oS(jmkv9 ۮg2X(c'3˯xSViz>ڢi@$(,@[|H)M5=yl.mφT#:~Ue5xc434+A`1+?e !eM?eU=rD_&OF Gj^1񇎴k#ROqSBęYd*B`sۜ|D# Նx7K-%"K$ HTXPVrH*tOmmV𵽽1,їFgVÀ@'5g9Ӕ)Gz*7kkFk[xn8lG,CxȬS|B@=2pkK?x %;[&(C[yh]`OBǡGB/+!'0/$Tvv~ 8W#_Z[pM&F浺mRfEñ z3I_o>;ͻBgbU_6%}2PI7p*38<>GַHv[ꒈF| w~|ZxS񖳫_"2ݺ0p0Ez}M_VCogK Fv R)A]S9M]H xx[l4:h!I`̗#|Y'ß xZCk}KP0wr2H<:և5 m_׉Og7 ~D]ik2Ėcq{rƧ/ݵקcM}ۿ ~!j4-[Ttk~Li<(q9]O|Cgw<wRX#*W'=5 X]sNfa/!Uc" rQHؠI~*9dD4m1=,kէ5*o.~iUQ5MkU=ߌGPԵ&mYgmP+޿)"u_÷ztWEm6@H_+[{k)om-|ˆ^yu9'0| ƾ,g}&5mi$k}ܴ%p1'zqElt.ပ5b|_?TdxgSLgٔuVÍG$~N|IRL+(U.0~  ?j=GᆃkKG7)o[C}q0*),A @>clxK|? nthZ3%/ \8)$}7iKfS珃P] sH|e951[hEn/C+:6#1NH]_úρgt= m:[I- {Uc$TKm6Z˙XWR2pAWԌݻ(} T_^?7Bkvd%I$%Ē_=O=~Fhѣ% ߈>q$ւA %D3\6o?g ;sKE/"G(O1bFNONߥ{ <ΛO+U_3fG[ٛ+i+'rs8|O{ᯎ\xH!T*> qFXWm-Ake`tllK;$_.XB` Hfv㏃gO-7Uc,% AlA6mÃԭ^+׳}:+iғs(=HH ҡ+oGl#$Ars~C 5a }hApHm|okjE9_Sn$]$oq$c>j[ va$b@5WS; l|JWOxb 2PB>̉q5^?7c-׭r1N[T毵bifw#] Lvf`sgcrCt,mԝyi-@Cz<;O'|}vDw:(ˤ^g^&u!j룀]hq(8Z!(S:u9rqD xEK;NWqy]L"KXWz?yZ}rYOkpp}|C0xK.]-~l|c Jo F{ld6.v<_G_|DP_jXCm!TOA3 jΫi>vFFvwUkamw|Hh<]]y2ZͧH yY hWNnD#VϘdW_ :sc F //`I}S UP5s||ִ/M1 䎧d}+ Zj#_b.e1'_ W8.4oQ477< I$m$QK(=NR>N.5*DR,H9APFG ,ĥz$NޟwuVcks.iH gxb2? lnךյ¡ih PV0't*q r2doOI!Cv.7\>T*#]|di.Zȑ$RxA,"؎esǘNOs}+#MO:AN+gǞ#,lφ&'X7Qȫn@댷]圮~ M+mcϵh:*)< jp9jk!F%ݗ6L3׼y:j%jw,@+I1#(<]YϐKyrFN?1ӣ)IZַZcM)'{ M;OKֶ6 $+I$MX|:io 1}9ǵ·'wg:֊O? (c ( ( ( ( ( ( ( ( ʹ7?iqorA4bDM*r=0zеZR-6VWG)FA#`QO:4yk3 [ZnS: Œ`81qgǿsS5o.z{}ZN$^jx^8FVPYH2p zFoxv F3úEZ~MA?$Yy<O{M'NúU0^I+|и'9%8M[O-6;xo_E{_Mxt5Ȭc$Y 0k5(HK tٙgE!KY7䂹;W;GCCƥS;pc]yx`LtmZiZ6vZGR0vb~_o6GĺA*J=?7|[cضiFĨ\y"URG8ҭ0HReYF\W|)=D5-wgX`{[)'Ѭ"[,.uy$n%G=[xcU屵H q[Mj־%] -7:OPKLoDb6![#ksx xsH6dM+T&9hAi'%NAHsAķ:Ekx"0OHYk$?YYIX /to(^ ^5yNH=w[wIԮT(O;=5O z>|20Kgiʩi6($O(omjPm'/1g 3n5e6rN7{PXF$g]a/Z-վ$Yp$1\--o~]EP.Ȳ܏|i]áx/M"! g>HAߏy޹WnA[89_{4X]4B0 嚧Kݽqs;fF$?ᅫ jbiSnjAijCg=&x 2#:~6ּ&~>^ kpd1wmZtSTosQ$W7Ly&iKR9k}rw6-.fԆr˳̕K@$^%Xx!^uQJFJǸyڮnsCu[BIaFԫFUGB5ʓzH.E.vh%;. *qڽ+ou?;,Sy!R9lX^=/$^/c~UƌRrt?i8>dҨkI9 ѕ߯%gҨk]mȕo8fX 9F-f+^Vw;ɸU!5K'Pl 8l&y]\ bq6e`\w p`pAQI.I=gۉ'J3A(oU"(7򲟢ӄL߳ ө,:m)oN*xĖJkB|?};}>E+;󅄟ZYuy4qX?ĹVmSI-e}JUȍ*%Ry#jTlE%#O;V鴂ђzddc5GdWD|cki[4W*rYBM"$Ӎ{¬T]¿Hxoj1s2K9\rAkEe2Tɯj,0g'bDz<4#MFMh|S%J;sp?1P|D,0CzCp2 Ӏ MkXi^-mH41 >TO[hb%hGN+5/1[r}|P@9aM(Brݛ6kpZYsKu$gһ ~پּRBcTU;c#7i=ee76x_ܾ?Ԭ n0Z1?Rz׎& wL%vǞH`xպ{H\=?OKiiNO3$37siʶi*=}iM >W~q+5$~JQVG/)xHtX?3\yJS%N:65 gfz#9ci/X 9&K:O߉5i^II-a E|wFIe( (1+e!#uvSZ+hN;֡W[2|[G%hb^}ƏKEĽ?ؗ_,{<?/>EXx5{LԮ^ÚmÜ[BRFMQ` GЩG*=M (?4*kOUOk2t9IbYO dQEVS\YG.VOTz GQE?Q*hQ CSG8?Š(*=M ?Q*hQEDӴ; a${u~)hgp <{(~qԟNϊ>=:ׇ"uk;c iBz y%KUh1IEk%kv_x;gm=[ +|w:b*!I{fA&W\yZ?$ }}Jry(D7&~ Qu6<yq+a@T[T/>!ym<7zMw cY|'JF z!#Z$Ɖ[( z.p (ʯq6KcDpx)zR0?џaDgQN<#5?I<%)RAqT(xџ?"$O$O(T;زI/Epf #7ƣ=?QS>vFIQ E|q=5:b)!s23F?<)C|r@Gg?#E>T+NFN1]o%Jn;RK]FVrI4* #89P]gω_7|5&uxRf#![2TH_VWǫX[wn2p:_>(.3pط'5@Ubg5$'tVmΏ Ⱥo5#w./$,9;QEBue-dP}{#|7s~N_\ۑ=EV+c.g{%?j 2:rtڝ5N2gI4ѓ)rG^}P~,)bX0o-)#Uoa_kTLsKQqQE&՛2x,Uܽ kZq#c53}*(U[H"|~ё_>]J,d }Y3EP]=~:@D:;=CzW|8RB2sPT{(8KU?g.hKSiFH'\"[1ߧJƿiUX]|1҂6{=V$\)?melZ~ߴݒOzYt#3QEhz/?`'TRŌyb4DR,NoEqOovzT{| GQE.T=>>[-XuN¼UbFH4h9P\r>hkBB]oN7}T,ޜp;z(Gc'in*z|~GQ2cO<E'Rh_|sq`=sV 7+|g!qs3$Ο(9z`<H?Ӈצ'OבsQO3C7 [tE/ÑfqEr&w[[dc4*9?#kzRH9;VnC`u"q?(¹fotoxx-18.01.1/images/setmaprange.png0000644000175000017500000001513713222767271016152 0ustar micomicoPNG  IHDR@@iq pHYsgR!iCCPicmxc``2ptqre``+) rwRR` ``\\yy 5F}Yd<^\PTQJjq2]^Rgd$e@좐 g ͗a_ ' v@t0l-bVep/,L(Q0200PpLOJU,.I-VK/*/J,IM ! A!ahiiI P<@X(v!ɥEePa>Œ9 KX Lz00OE2030ïPo#aIDATx pղO&A5 A .007^* , *JDK-,%↗+d3$&gL6HU7v>w/f?Y<ӧ+ğ~cbb TVS*sg=* .B>K0Oaa9qwXSJ СMiӦiܸq9gHTXe=矽&333c%$LM׮]})))Yfqqq3QB+=}_~1[da뮰j &I&:!l#ܿnlߞc|Wܐy7O/fְT%e;U 3=OSZyyygi7_r9ra믿"RBc0c#xbL||KMnW뮻f24̙cM62c﮻¤ֻE* GuG8{3s 'ӀlNͨQLv;TW;]h4V ojժblܰaJpj,Ȇ #Nsr 155;R}?}VBqy>sF`e#gVs8vXs-(\^( ʀvIL%NY>]T۴jʐgX/Bk3MF*G`(Vn&ez#v %zA>"]a߳Ǧ6pz #mL2Us`m1FҡCp55e[#Z0ݿj^o߾j1/4_|QM#(<#'c .TZ a |CRR#I|)xv+߮abpB%[Aa QrD$fTb زeuj4BFGf 9s>W'jV {(ÉN+D*6%q/Qfz0Qp tƌ6󕁂䔔 }O<k&젡tޣǵgϞa>L1cGCEvr_PP )xzz_diX,$U@Hp76mJ:wf@q^`@> 1 cGL%ρsL˖-$?Vpj(`jsqC;38dhK%iT4j&/0n*Kpr+"Xmݚ!2+Al^S|a`ЀyP&5k[/ɹbpVrrۡc8)&#Eu=Ç5袋B@i1'&By{! {INwӧ*G)5F%7n h%d38f͊((_R 0A[®Z*R-Zby]ՏF׭ĔN-UО1L#RKv\6a@lRf mHYϭ$''!D5Pp͈ ^< ULU&& ! WV=*-R)m#Ps#)Cs s(F7 `1k94ai=0)tƟ`" >=ׄ{e]o'C"ųv 6o?Z"`0vu-+NL(ZϗH{íGL U4]+UVάQ+8r+۶mUs͛7e#R󑿩*Q H +/*(  >UB^R q.x|w8؞롹&%%ɯ~-kwJ;wjG{ZWBuh + &eE)i9qPc䬍B1+}x  Nf o&OeiOȋX4*R!B!fZdok`k)Bʁ:QP!$aY{?5(lѢce˖J9Gk:΅7"׊-Z {"+4}-K\xfǎ@=K:{}Lʘ1Oh 7+M^ufRRRLpl.W؞ 3-,CRD Q.Q! @NJ, 326$J,΃zlXYe)xt8'&bZONj*.@i` x 'WoA)?H5|~}}lȼrbMTW۶̿x"mM'6):@,blSKAA 14a8a([^ mLG&uK.]!E"H ȊeH'94MrJ=l r2w~P'jɢ\+D ْ"'3@A|!2]S,{L$ \(gfeM͇~ %; xx9OQ(mJkF ^MFzv2Fhw@+2_>D̔גp&Oxe0TMѣG-}]-oaޱ³l&MٰJ[/>}z&y>ql̲Q 4/ >rW. EɁ޴i:DշZJyl](2Rx,!OHHTŶ{.}̒02 KRnV^׻D*T׬Y@r8q#{nrng wiH>c`.]:ƍ AwV&`_58tBѣw>BF""3@f{Z=cĴ?^~ρTr [qm2yT|k'߮][J>t>@"PnNuOSLMtڵ$x_0ph3\r!UwYiN#¼+zge1۷#4$׏~x \g&J[M*K8R>#eS{ {kdqO°_kJS!zA+|~;8HfpY3 ={R#G;Q 0U^Xy@2~AVHL";*|8Medžj!<(DAt_y:4BZ~X]Y! q<|'di>L.}KA٘ Ɖsl$8AbbC)z}?X)sݶ0Ndb^-tٗ7&' Plj Aә¥0W$=ꦹN>o64IHy.Yj 13Zqۮ=`;*ľ7vNn.U_G~#!#~BvR?A(U$KdY\Z#wRz~?^ 1JreXIfMM*V^(if%HHH0230Ƞ01002015:06:03 10:17:00Fotoxx:paste| Fotoxx:paste|paste| Fotoxx:paste|paste|resize| Fotoxx:adjust_RGB| Fotoxx:paint_clone|paste| Fotoxx:paint_transp|NED>1iTXtXML:com.adobe.xmp 195 195 0 n`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-18.01.1/images/gradients.jpg0000644000175000017500000004253413222767271015621 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 334 300 0 C     C   -" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}XjGt+Yk]u>{ KǓ p$Ԋj7v9DQ BS5T6Vŷt۽S h]~ xWKs!.tZIZ@,ވ2Oں x[OxsQմ A76({dxȯ(zrm@~_yW(c@Na> <wo~]k-P׬RYJ@9𿇼߅7z)Kj2ً+"hW_2BG~(j]A;xdzr&7 s#I o x,|KoUO_H NaAPܝfY'_)M7EڞiZma]X\Zǿ3Ɔ6 vv Y_/?G<'7Z׆Sj|C{2ܕe%x#ka<*\Z;dд;rQl K"x9_?aOl>|zmkZ[Y{N,@1¹_ҩޥY՝x@MOS7:EB]n5fj:gpѧU}ֽd-xdD(1~^zsYytZmvrD2~%a6*p%!=}F)ZW6{wF !e=A5`&mG5{.tgK|d.rktm^ 7b=GftW]Gkklrge!ݽNsZ~#A7UmesvhcD-#]­F=֯o=4wtK*pAհ?kbm}R^|渟adm.'1kZZH@I.!]r><}K0)oD]>}xoiŞkD6(&ENebx rs}hI ?j/? ?j/,i>}?5xm??5xm?asZ>}kwKbwKb0Ol[Zׂon;asuk%JZ$A+pH#8&y0Ŀi(y0Ŀi( CfӵYao9meVTS@i>7x_]a`I !3FR`zWٟ?5xm??5xm? 7'mWA隟­[2xTV7o-%%$IJ6A7¨|TjYSwtu U[EȊ@(]@a}aCW6QaCW6Pv $_>)>,ZTWYTV?:X18+$GKLiy|64,*  b 7ı8Ϡmmuo τwxV["!nĢ*}]%SRPmloع g#'?aü_ ü_ [тvCoEi֖2KlG7>7%$Ñ$  @}յgAAGV'>I:ͷ0S Td B#'mmW>uK˹C,9;MA_ü_ ü_ l6??ra2X?`t̐_oü_ ü_ q}BHוE'֟OTEgOݸ ?j/? ?j/,ZZCXkA >a nj/,8>lcr\c#}aCW6QaCW6V4*g^[Z8P|I'`}YEyU_\4'Þ:e]x7SUGy'wE/ODkH0O jnO+li߄DI=,H']W~%xGƅZ(iڌ7`pAǜ@ )h(((((&/ڴ7ÿ ?6Co%*7>𭏄gqŮk:6w0BCϖM2"2>SҦ_>߬7 yv~&€=jVhϿH$ @Epq[xƾ#.>.6;=+O>IKN'r^Id\38i Xh&wk>%C<~.P2@nGCWYzFA56c(XU$aPz(><=c>r#dgu$(LO|XC;NӢa>X_ ҽf4f&_GSO YC ōZٓٹԴ)w+RREaK]T5TP|w^-ޙ? jŮos=a;x8cx ~((((CVr:x;k#ɯ&u 8'󮾊5|9Vm|-顎ݣrȁ:nRűc|Lׇb4aBIC3zோ:KF4ψVnYa<qo^QMR[~ תL#HY mqm9%(+[A~ZL;v!ƒop@IףVn#|ΕeX6m}nDѕ u+  uyl?mBDn}r%šΒ̤mo VtHoh_cx }IaHYwNkX{V1O'p}i&"xӓu=Y`^3n%/0(֨g! &4ćK}#j+7ÿ ?6*b~O kJo97ZҠ(((((((((((((:`@7|?'|&?(]hpIL*ý<ŞH#M^ w+w`81Z(Ϗ>3>_OG%7Z6{6Ӵ;{HC^VmN '䂁7MǵkzTׇtkkNI#m"%[JoRU6bF/_i*sڏ;[RơMp(۲1;?M(fi4MZL8/#Vcb¹ޥCV~m?_5jWAE!#z Z7ժߛO1]7ժߛO1G#z ZQ@#z Z7ժߛO1]7ժߛO1G#z ZW+冑y$[^jvy{XV(ヴ(CKW?Կjͧ0߆CjX}yM-+2xUHx7Onk/tiNM r6y8޹s@y?jzg+SK˱ΒfIPNN~VG!xkF=^5TOI~WO *mXIT?Zg{qs'mNo{f-ֱqw c1 ]vy*m 7-;k* Cg .!m{bgVpv]O :_]< wxM:OuJT-Zh)#k6e!a=s^E]]Zy(((((+>(x{ZW |Aވ(Kﴛ &Bǽr |GTK;k? ~jv jVnWMȍ 1 bMFV8*1."2V*FAg^MxwImRi1[˔Qy9J)E[[šoi]gGwŵK"mV,6rFbq^E_ [QE|kTMn[χvpjk ,HHb̨x4>eQ_8obԼg]CD@xRI\5hċ*R5~/|J𦱫M}mK qéq ̆ qg n7VOoGҴWɟ5{QW:ׇ5yMBɠ4C@|29mcR|AQ/- Go6Hڣc1(DuCnWO 5Xkif6]X0 +hYHoa3]m+R͸Ek r%DNq\+lKG6_O%uoA_?6_O%uoA/' _h"/?  ?藟տ_>G}E|{ y][hD.4ga>s#*+lKG6_O%uoA>aU=[V$ʁ021' bHI$_$eD^Vu3#_.xlJ?h JMqYեyZީLҞ*Irzz.a[<+ 7syw${G.? ]Ii-ճW6w)h/0#H>?/΅/ 6kğO@,o2RNL/ssc쨮SK~j _u)Q\/'?Ox ]&Ö2$1坊Q~K_BQ0OVȧr^M5Գ4ȁܔ]p CxL4G{6;^I.S,ȑm|M7ޙ++ MGYdo.-$18.wq9v5PcZ;#>o 1mJ?z(ۓI+ ) ((((+-Πl: NhB[P&=c˸𾦗. >ϻ#Tumy}OMiϸi;Q@Q@Q@Q@s5O-[y.洸r4.#`a#5@Vk:tmB̪d^PNrIu{êCom+'f8Y4,0AUxF=" .$sj:֡pq$w6qaEkWj=]/VX]xg41bbeVTFw dEyΉ|9yq k>B h* ioM5jٽȆ8]ѣA"2ća A;=-O_4iZ⫟6_Z\Csݿ.$ kྤ}+[!{ۘETҭ-P``r8<z^Eⶩ+2?; wT5kn5[[KՒ*$!Xo,5? ~ q6zU-ԐiLy*ir@i:WQM~?=>x}g𧆡}]bIe"x9 ׿Z̞0߇|ȴ[Olsm;WQE iXJƮspלF? _׶~!Ю ͪ߫kѴo >eV $0}R/mf\Ѷ=?vǬ d-|L~R>&Y[j,ܚEcw3]DX,qAUAse8c>߇?NdNK'8yWd~LASܱ`w |'$`˭hÐF6GF''b[ZI`TԊYsgD|zD|cAwwez[9}.Q{ +?<|J*|g_\-Ge J~UQ{V (Š((((y um ʩʉUkHUP֑@/O#V_@TVo#ZG?FY:iQYi t ??eEf5*(kHUP֑@/O#V_@TVo#ZG?FY:iQYi t ??eEf5*(kHUP֑@/O#V_@TVo#ZG?³ADQŰwnP㿶?G߀?hҍk,@$h4e [t ^&(|8Z[pAh;oo4m7ƀ;+NrWpB;MtAK1Q(¢( %Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@rZf4{_J]Xx/孍mbZkεiV3V=?ަMI<=Jo/]-uDӯ=.{{kF]F{WhK=_󯒴jxIi]iHIdWgG^O5cen^Muxzf+;|_ҼuYIYziOiڝ0p uW埉-ou4 mGŸjZbKe󧶏xFipH5x6I6i=D !e."yR3cB@tRW?M}/΁18?_fa65u+Y[++4 -H 7m X^xUj: S}KXɀ)Ka'W`22_}>~>=Q-Ɲ-Ջ<ɞ0ᇧW<H^m{W;2 ;W#A#+(}= 0) (((((((((+ϵTv.b.cIb(*v d؊(Sʸ}7^q])&*UsW@k tį]ĪJ.Vu۠ݥ24P0-/4M„.}7s\h'H"uL~ץQ@o fsa3 3]#W?EzMtxc#J)&U*4t)sW@g.q#XGO$@xgPZ(fotoxx-18.01.1/images/prev_next.png0000644000175000017500000000336413222767271015655 0ustar micomicoPNG  IHDR22?bKGD pHYs  tIME:8tIDATh]]Wk{|g: 6&ĦmJ+J* BAQRmV}!>Q)J_DLHcjX[51d&Τ{ˇ}Ν|8s=g_^kCmFmFdt4Z^ig)F%%Л9к[&vB9:7@'a27?qNF%"q((S3\fy4z&уm=Nlm$V6~#c|zv:7Y"ߏƽh-b?[ ._?"ԙ!+!CdDYZC[BO>ry`/ 2!rbB4OĀe!OaYe]Ma1@v fN; V"{[+ n,%Ȓ)Fq{yC}z;jkrzO6~xn`;Bb0XKOĎfOXCMW,bY%ʆl*=S !3\A@P^x7cvduXvQz|!>Park (!"@pS\RsSA8"X8N1 (c-R.au">cHroǟfSϻ:K)bˤA(E|HYklAf:]C(GM a@Nt9C qј] $E-8+&FE𳈿|_Jw P0Hᕟo?4|ah/)W=W%&kMZX&M1D d"QT BgNܱxmR[Qn? !g6H=|8e3)o8F_t5Ĩ/;co -2 P.+LUT:hD }L}!3f\zYV5aM un{0}1q,u)%A&5X5be7h6h6xgU꼯݋IENDB`fotoxx-18.01.1/images/add-text.jpg0000644000175000017500000011347313222767271015354 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 380 413 0 C     C   Vt" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?|D|{ꚝܗ4YbRI8ZF+Bm#ϊ4T8n3\JmVf98!1n {mȵ-V|g[d\"ᶎcھwPRh}nM1Ri5Yi3\Okq- msm}zOREtØ#@}71>tu! KkHYa`0Mw#X:>Y-Cr'=I$}X Vy嘹aW8 nnZTd<@z{ilS6R(ԓ?t_?/A=+@9 ϵGbzVnG_1Ǫ^r~;ZTu:F:g#ȹB_3:|2|W]W:̚6ZO4 0D9lc +^ n>|?~|Č+n[o$U->8ֶ~ cѵ$ȆH`h_lZ%Kѵ;~#Y.?>}Gۏ+<;_POӹ}ڊj[y JȲtc]޳ឍxa񗗡\b˳˺RPb?o7;z~|O{zv >}Xj^X DQ Wjm'?DV'ڽWo?}'?±>Gڽn>|?z>EqOn>|O{z,ۏ(qOb}ޏ{`6}Gۏ+^}ދ >}Xj^X DP/H#h#QXj^XyIOh HZ7r싀OA+Wb7!i++m[ȑވ $Q~:|.E:|.EωPFn`s<^[7S QŽOCs[5o{ gQ5Ԛ~*e'xD(=_Ο wΟ wtS 0PC"&Vퟓ.wFu DϹ4^&'8 ?gOO;gOO;´1;YA*@o) ;PQgOO;gOO;ŽPGw]^@/&WCyN``;g񞏩j6> 4ybb TP8kw/w/?1ˏF$h y @ۂDѤB6PYe>'#x=MKء[x0EH" 7A޿U?>? ?>? \yZ-etKj#MCA,GL7ߎ;>]KNHHnl\6wɴٺS_Ο wΟ w;7vviucŗR7:W^dQQ̧gB(^с7O"Ol"Kxbe)vO9lM~:|.E:|.E|޽5Ƈdѽad9BӜ|doƿxKMnKH xYwL~g,} w/w/};l~S\$m+l`ܡ%[ qҴ~0xQS[3>Ŭ^gl'%:fE99/J~іtm޲iq79sK[.JgOO;gOO;—.o)/&-/.{ThY: c}kP{F$]p8{Ww3't_G3't_G Z>}kw/w/W?#}hQgOO;gOO;ŽPGڏ~:|.E:|.ErZ>}kw/w/.~G}_?Ο wΟ ws;G֏Zqt]Dÿ.(t]Dÿ.( j>}3't_G3't_G(\QGֿ\>? ?>? 9Bwڏj> ]Q ]Q?#}hQgOO;gOO;ŽPGڏ~:|.E:|.Erȷ ]QG(\Z((sռGk^M il$Tg.f>AgKÞ4Uy5 EeMfk`npٓʕ6w`qkhr>!;ci6jj KZ29۞q:ᕽwE]jk].I 'J@H7~ qMl%][O_OFKi'ѡfC[]H#Q"K\~r$?('WJkgZ c%h֌ C2/9<~ZGd ZouYA5٘ "[ oIH’v*o~W/u+gm>a~uIin~")K2u~1G$T>_ hZ7Z6]Zs*,?xgpA | i_5MLСKOl%Kc9'޾RIfK V}exZxg$ճ5̭ VlsmZN=%;2}yۯ-ɓɗ2;pk /״*4##F*Hx tb ڇ,o,4bwQm5DB5I#LCIx FZ;H{kpg`t63 yYi]]l򝱬Ҫ>'muK 蚔6:4ͮĠwTNvd*'{|>|jNkIn>#yj~Ɋx3Ghzxr \D#)'+_MEis|K< xF:g-ݮ$o8Hs ;+f_iWl*-0x_%Y/ 5 /P6k*[He,+<1WeyFŮ\t>קN ]q"Th9UӜ;j⟧sl:>E˝2 < ~3Ɗ Aj{rpbޜpڅ63~w*ws̬9_(|SaO7? ]{zmkz_ge>AT͌!V5-BnO:ׇgf[/ -$e^t+ènzgo_?ޜ,~_hPG#s2 ê:_xWqh:'Z<m˥Ѯ Y{lLHpʁ2dE`Mw|/__ >w 6ձ[X8R8^=ߕ-?wo_*=wM'x WHYYgR#'?TZE8C"CC_xF_ u ZxFL׭ٯȆ5PMˡWc oy~~)G&yMyy'rB, py.gk[^ @۾-.Sc* ɲF eXdeFEjEi$1[I4ik2T{̚_X5ǂŲ3=w ʹap+ 5M> >u5-Rk {a !ef#*O_5oϲ-u>f,E2ԃ6 Lym%;HUSВ|O>͵<;FCkD MMݬq6.yFU7 o ^t/ EqϩivZ)wqn^"̄I12o;K*]3K{{Y⹅쐸u?B*+Vek"+d5YWM_#6Ųa!i߇]ĩb|WMOZ~ ig[[xnLϴnTg<iƞc?[Mcf v[,Ьx; j(i ?tA}-?͟d[Y"5P+ҸP 5 R*'\ʧ^~a+~/x" Zt V~ސڽ;$S+Pp x z_0mč%ΡnmBlV8[٠^K)*Y:Eb.^7S}i!sރ; ) s篖ǰ EQLzn^k%{9.%㲠?fd*Kݝ5B/Xgk=[ޟkhQ څ%uIdE` Kmk>:iN\gx^=8״e5GΥSєgr9xoEj~41@eDi5Mʫoo5`6(dWGI(MV=~MFlŜv"&ВF!H4e!nk_ws:(((`3$SRK@Q@z0?"F TuQ@A%p=Osef@][qR<3ңH89(U ~#"OenJEPKKY)svn)`֠ U/[{ Vg75MBOik-ozXP*Fk{hu2E-`2d4/7# 2Bjχ<7GFt[t6wmnQw1fbI&GVi,4M$̇}m0?ʘU/[{ Q {?j@]糉!G Jv=ѯQn^x# ('>!Jl یy( dB[DӢ$yX Ė8[P[ [,;f*OC袀9?)UQU;%b631$⺘bH"HPUQO<Š(EPEPEPEPEPEPEPEP7?J>H s-M2x"_-dNq}w{52~"Ay- KKw]%`C!6vPƫA 7|I?<}l3$ S} @H^Gsx}<]y:?ЖON7ďg3ZvMKձ13n[MoXԉnr6&}G_\M(T2/׳hu qg$On. C:DF@Qsx77 Y[oF17Ҩ,00xkZJZ4]^~}v+aWUM]"p2< Tz?* SO_n9i^>h+#4\$nqcOhe]27W\fv;KF>Fʜ~x"ZLz6ŢZ gF$;!+A#4_zAoG }>(еIuMt5IV]|*0@_\Gt]iZ-ޯyac\ď(G2 G(`xg/}"*P},1<$&, 7^ F-u5\K6h˘ct@N瓛Mh[>|G?GZ5ҴEt`YGR-@=9g|s/ ɨWd\Z0"_,w8~>yjza L-3Wm 眎++Y]zmnW[kyķndr+푉,>^%hZh+_~*|gJ=c[M?UCHA*bE@ qm%-|qmJ[O 35a$O\^XȢn co -?~ ,+'AevQ "~ ,+Xmt7[h3%NVSL]``05 ?렯^,??QXȢy, vQ +ex|>X"o?ex[_wGa1"_-A/G, vW(,??Q`<W;e(o߲X"o?ex[_wGa1"_-A/Ko "k$B#f'Mza1"o^S:;F2>h@{K?›Kk by&d3i*v :}}zuOth |AB'K*IV[kx NdbF}X|bԼC}xOTT}N5i"D[yc*(JGߧm9AscSveޑ%ݭA ;HC9x*)&-Iy5UHB.P $knk^"~& FHVp nhi=ƚ|{/mkÞ%𿄵8uMkO!oyfdfҼ㿆/ڌԡ|i# #w!i_*G(?b' 0IB6_7W?=IjQ_zG s6ZZwumJH-)[L$?ŷbμsǞOxo[E>Yq:{t͛My2Ų G?QO`ϕrVWwn|s<3AᏂDž.#M5t{[D3 +IQvp\滏/}EP'1h&'1h&Ш`<ŢoJŢoJB-U-UzX='1h&'1h&Шy!>.E4G!>.E4^E w Z/ ? w Z/ *({ObM7%QObM7%WQEB|]Ci*B|]Ci* ,_MT_MUTQ`<ŢoJŢoJB-U-UzX='1h&'1h&Шx t[h_`'031/+ wu#/{K$XLp<:}!^G慬I^is/a~fҤr/J^˰"@?.7" 6n5A&- d_J) +̼Cnź<9mc:5l 9%(bXث;r zmoqA|SiV-{Ntt2n8?-{/x_>C!H+`: Mq[KUPpyh{_ƴxν:MCYsh_je5~Q2+y{Ы2+m'1Z~i>-m`Tm;O1GO65 ߈Ŭ٬~VfWXXb?l?+o3E+~Ŭ٬~Vfk6kzu?b?l?+o3Gص5p=:k6kZ mhEyص5-cc4\NZ mhCf[.)pvZhz`GK:Woʼn<:ڲ4RrI26sΎ4}S0X,˖-1X玄}_W) D̊ jʰ#kӵv iB~~,~2|^>,3`n4wa \ŰF?wkWy פi_Bk[hM NB[+¿*E|Go-V> i>-ặ0[4{lUw ,0ver0ܻ6:Loljgd")׮>̖Ul /b Rm FEZ_Q7˿un i 0>(]%TU=5c<O|??'߅zc V^Esuy0Frq£1f!@+/IN֚}',aeLi.fMo5"a~vmMCUWN=CEQsab$(*T.,a_>['qXi Ӵs@;}[D(m,DAW^U#8?օxb$]_-Ɵ6}1]$"-UwvK_-ϻS#"T7+ϟIm7aj h:,t+Si nڂ$bфcɆeGT>5|_4K\.Nu%().zUz<ޟ7[??M (O|??ꨠg+ &p§G \EuTP+ &p§G \EuTP+ &p§G \EuTP+ &p§G \EuTP+ &p§G \EuTP+ &p§G \EuTP+ &p§G \EuTPi&v^kڭdAi ĀxTԚԨ&ğCSԀW-owE׵/vX[7m!Y 멯>j^5ᖭ4!ԠHC%L|C#;IauH}_ů km f;îy1Hc3*lx]}|7pX͆Kkjzׄn[-rl[#1p/тW7'ENAyystt-(9 $yӵ6F 9{[7:<|+h^LRԬlGוڭީ%u$* _\*y1ݷq]o@3$' XycZ-`N-T<3}I 5J04yGmwv;0jo;k 4),-/̘W_@~C]}_y`n-OP(bP/&T`]bxGAԿz۬QEQEs^5:rX2J#1F8TڹI x F??zeVN>G9G'~1+(2>&[Ev\T',P5xVy}0}6#v -aǦ փA ?:#XSh?o1-E1-uF?°(ouF?N|V\ N|Q փA փA ?:#XQp7:#G'Zc[+.'Zc[(A`QEAh?o (h?o1-E1-uF?°(ouF?N|V\ j^! ͳ={$o8^AQH4I*vE ?Z PBA\S#m!W,`nj`1QH:*(~W~Cs1+e ;uAh?o (h?o1-EwOMJT$c*s2ۢ@QEQEQEQEQEQEsu7u5[k][.gR8deTdڹo&xhvҵ%C6ˀ!ʁR ~7Wû#K mZ;]BK[\G xB֞+m[M6w%/bF丶܌6|1z='i^o jz$6r=#\J"T@D"֍߆Oǎ4(T'YoL!YA= ^Uߌ|SobRu)|3qZ ,46G_>I$|mg;Af@g>ٴeSZA[0|, Gt[}ѷ9Ж=2M Yf cb9\:dV&rW8Eg< *7ķ/xck9,x!n]{vUQr8'^]>ZiThlfو2eϜfC؞Ƣ:>>/ԩitY|sд*:<2jriqyzܬ"dbcl[0HO5WnhڜiZ躾Zt[Yi ?Dyl ޚ ~'C[}Z"+8Hxo"%?gdegW yԾ JO5 xJh,5+S wF{UfT, e#r ~6y> xEWSUuvf=Ccpk2м)ibSDk=#kvQ2HU_y 7]z^ZI+jD[k_/"65@dy_iXvUJ|F~,;x.Kt)9-̌agf~:x+ox{J7^ZhAh#K˵ּ෍tkL\^%ҼMs8- H֍%E]1Gm]ʖ[^_/SrlPby"|no^ksi:Ex~L5 A)ͬdR ;:/iߋz/.onn.[hgc}1"<%t%w3xow~)zZZ?aC|MqiCrH-'S")#)kB[z/?_<'+<--7_KV 9V_(vǮ+#}bu >7}׼@5&98s|*KaETQEQEQEQEQEQEQEQEV?Se :%66{pϵhsYXH##T3$6cVbA'h$xӵ; "x|OMPR2źsk 4gM}x?^']_5+X/O.5wey5 x9F-]k${oz5>0Nk?jEw M4N2F"u}#Cᛝ?Jmf5s%$Vsʇ.Z{8LO֩K[SWϏz'(} kk |2ĺ-e-Y$73&< _Wc h,ԒĚ4ZKgIVXR0ʼW%weZEvu>|?P_Soyi:川UOYOC]o Q4iE5 1D<*Tuw+2kß ?& 10ǫHơUT_NtQH((LC <B=¿|)O,}rI@@% #[7¨<5vSU@+覐)urHJS'miYjVA0^DH9k*k k.+[(bcTDGWkWu]J= b=/X:%ޭeX5sq1EY 21J^ß ?&`v}gϏz')T=Ets_G XHKHPNER((((((((i'xu롧-Өe"*ۘc<&/G{u6,6+on# ؚw*;7,v?uQMhUc;_CuNP}FO.8m]I "+i ŜvpKiMg-@`&vp= -!RX3{**m ntƌ'!qyjO#$0\ u[WlJl^f8JqPֆm hw@=7Wy3+X'|6OMWx9i$tuz;xMtFE<s<|N1YPԜpx>DQu [O,й|N kN09sG唧F-Egs/O^24DM^QZGm6IvULOQ\Nc;_=4d45 ȬH̄pkԼ=;FGiZWz\&how@I?%+ce.ofVzUotDuW% ${I)Nu=O:O]W&RYY܋Ryu0m-̒&QH((xYÿ<=V>hm y%TgcPI<k;\|/%Fu[aӧH#;xV(0~ dk|h,~oh hU${dV&R[_㻯 i>+:- DM_y.qfe-{frmSr_O J?7$ӴTזkA_W?>J7$h6Zi.G+K,n۾F`3߆4QE ( ( ( ( ( ( ( cY[]e/u+`ʶ888"W֯_ k;1z ЍeʆPZ,$N`]Ϯj}:k⵵ 3H f<s_2Y|Wnje剿$Ez7Ns4i$nF2A$3.?isxJ[;;anЇHgt3;. nWUx\^#Ʒw-<=4%]F+VY7dt*1:5i5}/Hzz.duEioWm#Ff'?wgC jm.úG&^Ohuyp渗dsȸvۂHW¨r޽¨ ?7]mo- *Ho5n#RBÇq*o ͚4b(tDYX*@Ty՟HkvtG=KS mgAX{Իk 0c|/m/ zw&X:Ee" wWfgÚE>jVoqm2%VJ `,'XϨYX- Ppv I Yr_W{<6^'5-Cxh|owq/5([#hPr]95xC{'Bdžu}OWӼׁ)"QVÿ )@ӖmA5Y`[ej"F0dۏ9Q]c;2+;cH[-^mdKmm5+ɚ)cn*.%#lhXNӻ 󿇚Zn/N{;VWJ0NFQq^ WFMk?E|P|Uw|\doE!0+@0 l >-qb>Q;/'y9`c`isIE_toֿOsQ WFMk?E~ZZ|Z(CAW'Ń/ _RO߈^3afPl/֝@:Wy|u{&H#n49g^*kO [ Z5ª+c±]6?FZϮ" :mܭ;Yh%΄FU[ns͏#V&LK+u,hX"j.p&Mw+Z&;Xg18x88$խ/Cҵ߱ K{Y$ؤm8jOcwC~lV79 =w5aNI]ܽ^y*:s$jHP;Pg}(f(EPEPEPEPEPEPEPEPEPEP\wĿM?M[,zz~n/)b*36 'yi}c̗;H<՘G"1GQ")', ՖmNg,zIvS-@4u=}_dΣ["K aPF |K<M~cO&EW'>'/4y)?_?>X{xh gw߄:z n>POe8~$]HAS+Gk~M`Cqpiedsߴ6?W;&I!WyoA s ?ՆMҴC\piq,p8[Y(զ[o5ZHC.Z ;؁ YKw h&o֗sr\ }4U_G)[W) c?7_1ǂoпcTW^QO3tO  a⫎MMմqm6I0dt7GAw¾|]_BҼE<],VX$P~e`Ay$~VSOŭ OmE3i cCRL"/p19$yC'`Dw/#D#6hȲT0wgp^z-`ϔiO [ xzf=NJ; lb w=F;<' /M}6~ |mR L ww\)[hD{@<\gG_l|}x#^t}P[.]-"Y2}$8 =8Od &"W!GA_m|,_~;O_9 G tUϯʫa껻\ӠRҭXFH䉆zz w1_?\_z6rogWKG h.w1_?]w1_?Y,&kњ(I`2d=kk<)zu?.i>&6ZMW,/c+2PpHgCUAoZW/O^"&֑S,m@BNIu$ :G ktoA_:މwO hlqhblQ<.2@݀p6OK=o!*Əx? ͷu?  K{wxfiFÂ0+8ީGw1_?Y'Դ$]\ڳBU-H'zy?}a]KKfc%x_GSR-QB̍_n|np3?W4K뚾|?YoXmӬ qpTbT31I<)|AĽ @_e}=2¾!?#NhZmf $,2;j]?/֟>;_]/cGt~كYxa4}s%5bմo\Ve Y )1r ^TՄcGt~跒,<$dO}k|5 %KER((((((( ־kziWY3_idڢIdnco,¶0p {X:X{O mV}m:MJIR$ciR F89YB]]6yYqq +RBqَk)ߛc8_^- -b-:+fp,xH$㯀,φ|G{>ZQll(v ϚF⾁o{//]ɡYiD$POeC>kCKE $!O.R=1l5~|;[ 3xD  h:&V[E|?M :P/izv/=cZY⪪$V_G+ Э&ĕ`-#MpFT[Km5camZGkgoC z C')QEQEQEQEQEQEQE^;UP#ə?R3U->,c'&("b?<[qÚD>伶 ?J-k=o>.[/|*Դ{MV>tn Xaxr{? ?EӰ-k=o>.4{Gk;/LӮơgin1GIP2 _j@t?Y#Ҭo!f{y.tƟm}cwvW1Ce $*BsV? ?E?-?Yx[ltJ.`ƒlR#}V!sG@^?Gw\/&4 ^Ҭ-4u}%dbGYk?4hy _{t`Z[;McC$p$EfPʹ"ʩc2"h?-1xf)fRQD 26`x+o Z)JѿR2+@_?T ?El^.&u0ڝ>=et?T ?E&H4=6eF+,Ue h`Z[z|]dxS>FbZOnKA0k2^/ŎomO{(D z꿰-k=o>.2ΧiZNq{ȱ^–nw*ԩGi4 ?Xx-44#{Qe*zϨ4S-fbv,W$cEUn`Z[z|]b7}OTtsywkE\$I&xZx@-/B-ైd|`Z[z|]Q;_.hYV>׬tY}=R)g:|{-F.0?Z?-k=o>.76\M4u mBᦉs4G-IJBr=C0h?GcO~.0*23Z>4STl<; p8# pGqOz|]!sG? ?E?-֛:Nai 쐬BH3ႀ̈yz{gixQ $ĈJ3^{c?-מ8?mJ6ZH=@[)9ux?,=E _{t`Z[?xk"h;_.hH WBF %9r?-tKP4[K 8Z{䰋lq%v)AwPv̖PK7~ߣ0($l:2E#S~y'MGHyxdhOB5l>k6~#>.7mW)wŷ1EAY3.@ ?O[hgӭUԵS[>ff E8Nj"Ƃ"@, 㚹4XimQڱʮ ҝ_k'otv?zk|l"ii`u00P^4k7QmQ&~Ydd!3)m$B^7LՈ5tտ!>t [jO'_A&td3j{F@>AJ44ꚕxރ=tZ]7P{8&n U8B#&&EV'^,TvqYd>Tg9ʦ0yu{KxY$Χoknoo$0 ?pl%*)ro|=U5[OJEֶZh2I,J$vD O7ĺlYGђ^VԬMuo0]GGU  {t2uϛ]y D-;g<ڒO6edIeWXHy0ׁKx_ ڔQM㗃Ij}Nq%-f30&ʮ6k :6emJi[-EeH y q _h4(?8Y\_FM6N30D%$>1N5u/׭=y5ynGW?n_N&K#mkQ؋Ux\~LנxXWAqlY]> p5]0\fǼsw\{RZ;c.9&^ַ)uRrb #hmy=9v䏎,/>u=,^3馑%$34*fWD$Jt犴o Mwx_4-*Q@u0q*7-^3z/tQ ߞ~|'Dmhħ\}{֝O[=͡9,|Z3( .?xm-M5+]ן uqD֛}8/`] FcnhYs>yj/.[Tm v=>`HSτ$%bhx)-o[],x߁M[2s-zo }ċ喢M[_i3NT;$<NϋJzω%Қ餸Yf+ie1FTd~엯|&(t$1Fzp8qQx4C?jsc"E1GD ڷt"5CVC.3n$X͌*$Dpz2KCi N_2zѼAc ^C}ISLGd^?¯xX HYN,Z%cZ#|1>CXMCq\ yd;6;ʥBc~4BѼMu:7\ç>$KYDxp 7S^/|%=\Krry M"?:I|oice8#:.>sGj~oKm c?=25ZlDуTa4 \V+|WplVzcEnWVgˠ4cr:\o) r(}ǂ|00p]:+O d)w^eE53fڰ_uCx9?/o_R<7f@3zUOX|Rf +ekzٍ-;ys UR`¬e&ن4#3I { utX֔o-)Cɮ{soj:d@9sC);S{1-3f *6! W׹:y߁ y/Zm4^o1|:6(LK_6K%kf F^EZtR<@U[E;?v6W434TS`BH3b=4;ԳC FѣMiC2}.3%Lkz٪lGcLt?}Ɯomk7O79 $0M>ґpT#]ct̜S0%`ai`$VEӟ$ujJЇƣ;=#IxŵҐtLA.͛g= X;qjnǏGǔfCNDA?ƮZ49R$5g'ҸLc=$$ǭA&Iۨ)2ZږNiflޢ.<4"$Bj*@ MaX:t]][b&ǀٺRr86M q9 bp4O֡ōa| *|:xVKֺ^D"+'ʩvO p4j1{ݙM2a ~RNתC'dVwgNZVŗ .Qkm5Y7QK7wF<6入B ApåB{\4=M^'JU\7ys{{?#g$KE g1:@&"&""&A,HG_@znхS"+Ԁfhf +\Λz^'1 A>e[v\c.ph=O"@R@*c4$LӀer(`<In 8X0mQsWme'K\;[0뎎F_}%aQqhX]bO ۆMr֤z#Bvu O`|VnēH+tÚ-͟wݬRpbz;c5tؤ!bόӚ #[/~7j\d!;(0b!XDZ*jR*|^ ' ۣբvnG"b)% CK)kK )%@s3LBQ/ BPj!x5kۼIENDB`fotoxx-18.01.1/images/add-transparency.jpg0000644000175000017500000005776513222767271017114 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 265 359 0 C     C   C" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Eռ} WT摝#,JI!KHмCcsWz$yG\+rI*O5RXe( Ρ{pM{׈|e>Mm*aKFupW*8Ojv:XYB#yJ6O ƝJ!egMq=Ķ5-H&c~jOR^Et,U>`s]E.4AHmGc 0;Ε6izeKj8&<'$]x,BPdslr¹^ɫ}q5͍[^ZʌgǨG&f׺[2(ܜkixr{V{ rk yǰ;_D#MUXooܟGΑk.PTa.3OF E 55 x#IAhR[;dFIJ (:z>I >}^MqW^_}kCmVB C2ɪ(gԦ<h*Euy Ҭpy8~}}@2mwg MhzG MTX=FG##ҽ/!}'˫jzeeǔ$1Y+1Ugod&] >}^+?_]oxAt#XJԭhՁ(Byx+G_ ̷ ccu6mRNR{EJ qԥ}?n>|?:/ ma=[~+խw۬J!OYwl#9+_/z?t}+R[K>]FJC n]^ׯ3}Gۏ+n>v?bOi-;taFT9C0RMnm'?DV'ڽW;qOn>|O{z,ۏ(qOb}ޏ{`6}Gۏ+^}ދ >}Xj^X DQ Wjm'?DV'ڽWo?)!^6NC A"GڽDx_74M6\jn~pKHq#$p|ǚ*de`tھhބ$9F~=|0} W\ \Tk2sTqo?ּKy ߵ^ڈ ?+ҠFRhI > WG3'\?MoN)AB]:bjJi9I+kZ.6FuO4衖D&&8 JfzaD_+ &15ZEo)I9#I늡__ ?xkp4=|0}?'gjӴ+6I$DK irmW1M rKPKWNNz9a1Ѵj#-kQʼ%-AԏgO5?ើ> i'K[~[_I55=l>m~lhL(BF,ێ,hzv%0nfm+,`v P)i WG3'\?M% _V|=Ӵ]y_4%ϟCȮ n8=+ǿ"mOi=$֖['|L9Vl#= &gO5|/=Cz櫧V7zZE6g.]T1%lpzb|{4;gijjVwQ^&޳C ;>gO5?ើ> h?\>*x[!~Қjz6hT0ɮ_zǏVG-kVŴh٦i:bHlg8$Ww3'\?M_ ?xkp4( k6c8"S#k ǖ'c\Wڏ~=|0} WRed7+~E__ ?xkp4=|0}ȿZ>}k &gO59B_ڏj> WG3'\?Ms/G֏ZtzaD_+ &PGڏ~=|0} WG(\QGֿ]?ើ> hzaD_+.~E__ ?xkp4=|0}?"}hQOgO5?ើ> h twh׏gO5( \zxsFr]kem+4q4r@'NW }|yW:~ucag RXZܖ 8y:քzVP@>oo<[~e\ǦEli-9=k파4o:D }IcRSx߄|3.{6w4ln>{W_,錴`n ?_o |1Z]ƲtE#g%7#7_6σwi[8p1$߳^y}xH|Cm-dž0\ "(,L>_4~i+ȳWm?"~Ɖs^U楩0ʨ ˤgsg#W{^%ִ ǩ'bn$Iѕ PCkmS֍3r7u|=:Z>=߇/_NA2 2:J*2 6r:"4]qK>w'F5ơdZig@fr TkdK W⌷_]׏|-Ϡ:R<hؒ+;= p𗍵oO':gJ⹰)Hxd2.Cg.>ߏNci+xo,M95$l!)q2&1pNy_~xT~%"ɡjZ=izX&I̡Wn$nU#>;j42m@;Eͼ0Jȯ+\"d%ⲼeCFI'~aie<^O4K'>Q%.σچEEssh$V6:ErvɂCl<,7Ago,YPhȷe$R_ZAHJ&|7 ˉwI pj$Jf׭)^Qk S R&l噤c&"̄Anֿ dG~T}#QФUc6V*sH8eb [| r <g᥎!˘syq8Up=k _QE# ( ( ( ( ( ( ( ( ( ( ( KU4Dsɖ2aVuOW㕡\YmˠYip]Iq BKy0;*@N?v r'T?9\7m߉e2@LRc+''T~!k+/Y4X v%wgh7=?_Qg+)|?G_E6[ C r[T,x. ]qQC-|Og9ԡ[y[ghIg(uOW><\xêMh<,d]@uv}qgUi5;^j K#`Mz;~;'T?9Gݟ_|I| ;JRGvҲ)gE^pK@|To%ZNxnd0RDuR+GBA g(uOWua{xDF:PѬuH؅ 29SZ|#j%4VM. u*׃g҅7cuOW}O=FYI 2I$V c:ƏMuku沵I&c!e#lsсEI)Z8JC8g|9˧,tZj3[ʹQAR7o9^SaS)[4UWxVvJ˳=/?~@M+(?c/O=SHdϢU`iK $IʧEuN?v rvM!_|T96g|&֕$cޡU֥Z'T?9Gݟ +3S~N?v r( 'T?9Gݟ (??_Qg+BuOWy:+Т3S~N?v r( 'T?9Gݟ (??_Qg+BuOWy:+Т3S~>*nm^<ʖ줏cPEP^u i=|gx5]")f1%pAF{sz-f|Bg*}w˷y;߈>!zݭ"48%Ou,Ɇ:u4ַ⏀ Ѵ5;neFC)o1z\_ |% $~Ylbo5P@U6+ic[u,'xrO<1!&N\&P`k|81/0{F{OEy}Ɨmeul@i'1li RQM64'M~LWÞ Ya+>'K X$92Ϋd~S+:#5淧xo c[vZ\kkpW={6 #$T~ ~u#-x[< cJve\唐",6Co|K閧hj$A$v] 'E9{׿QErYkG>ɠOšhidHJ_d2a023j᷍-Q<.,,uagIr[] چڦzhKu#@;3F3q _MǫBR.\FJҔcgQ^ _MǨjo / o؟L/NOAjxt5 s՞&ۨBld>A^|V6j'8=> SKt-Q\GPQEQEQEQEQEQEQEQEQEQEWxO|y ;k 2(oa$ʒ)Ju1^_6x׼y^;8ufGk۾w;¿3(Uo^`7~>=|RltOz~soK%2j,)Q#;j `acIY,đ;q,,rI6/4d~,eOhZEA5ŝna2>aЌQ~ KԤm2 _x%^_)`_|e7x9pS6iM=R55}3@-ok[`)RRdX*m38;OO|K좿+&=U#!x&EwU%H ¿"~|qxb]ÚeݬZfI  яX*:|1]|iQ?xFGX "YN@[ۧm+/ j|/{EjI)A 9 1ٻ;_ oE}%qnnLe>/+ϕ~;W|UafFwpjFqj4~[nݴa g-O^I9%*"ȈSmDyAsßƋqƕMnH ޣp2ObjωӴYL^8T݃?Crqi46 ;6G3p63 '!珴_w!ec_@p+rF2zf'օMӼ-։hc*dTː]2FOSĉYէL;4$F' qTSOׂMCRt.=-` iHm7;IrmGQx-gYYmveb>|$ý+X^pRV .HFb>00xe.z~D|W~*kQ6wx͹FU߼: 'ž8ӛhQ-g>MV17dxʅV|H w&[ᆵJeʴ*ѵeR8$ Pkǵo>5KGxG3xxY\mLƒ2lm*c/#{ǿ^xo î7o;H =~Qsr++']ƙɨy:|}&Hm%DEibPF&By*q $] xfJjbK8Xê8bdISA~CO=nCҵ3/!cm][pl124d{{Jߎ}<[i7ږïQekY,~f  C;aSq8G🈵MPY4H-M yd&O跲-꡷5O3}}{|4MRO լtqދݷAsyk//;AX=:k{torD&&3d}f*2@Ax7NM{ΏQxmaKfoI|dm|@\мVN]zT1mȱ<  _ _^2lmΡlK[X! J/[fi94Ԅ䒿]3X#U /Y4 *-(в!`\j~v$WSkھǕqg [Ҭ`-.Q ?, |mŗ? |V񖳡KL4],nu 10l>ߊgi/Vik?d-rM6qʟ_ߵ7caڐ]Jm5K=GOsFo*32m7qng^34J:{w&qw"+ii"gWzrifxiR©nc8gši:g4]V/9f@pч*1]3mM[~zomqwV7\m1+UUIa4~VK$ nV32,^;׊KiI)Tn P2)gz2#`+ n)JM۵k 7PA4$ȤDB0* rAAJGY]HeaGB+Caf-JokRZ:K;lU%E ?$KhfW 0 cjm!&h+ xßR |33%Z^%GtLq|{*}hWȟ ~.D>$xv5 gMM.ztW"Xu#BX8ٺM4,k%;AvfFBw*!T՝o%I5u_ +x:!k+^o.oMmag#9kc'i)x[_Z,km^PneIهpΏ>vZo$Q _:"Wa7*+x]֛|'BH?>}w Ŕ0 Aj/A}w ʊ7#?nuE лu7(϶]:>MOۯiGvZo$Q _:"Wa7*+x]֛_C uΕqK5೅Xg%YrݜTK\*8j1=|Qa}^H$qMj&E|j|@~&-/3[;=?NQ;.S(O԰%6U]7Sō$Fڵ&徑tqD-3G!p$jS47K=cO闚/CWou C_7/nб:o uh8TLWо > R]SOL'ۗ[;֮\WQR0"Eﵭv|Ijw&h#hdX H>5V| {6?:Ok:浦Z${Y-1xXm1?<^1m||ko֭@nG} lWlޘ61}_F{'GOΝ]?}²WO̺6ql't.i6G4Bʣ WO"tjz>4KE43 cmRcm=F{'G}dP:7Qĺ7yB&2}',rHe;XGu M ԯ4K[4 72+qF9NH>m=F{'BawZsL~xK.ivxV_pA>⼃^8t_BEui#6XNXCq֏ =K7Kxcĭ|7&oS~eBMHp/- Ú֝[6#AIa{\*v쑀yp2p=nyjmK_Y׉4-Btm˥hFQ+˸:n◈K{X5 k.k}j&Ih%]H>mkS]Ex_~'Q.|=ͤ!8͹FuP[ 4l2O \!5[j%[z Hkgw[yq@63WW]zw.=zսn4]bXeI{Dȭ6.G'<y^^eoQ3IQ,e y{ 5/mÚ +T/{{xԠCI,kwJd|vR; ((((((((((((((T"xǺccip.y%,Jx0Hu Ub AA(㖋+Gx ި'c}M`j ?|'tmJ4Y3m#)P\ת2NASƏmDxFmul("x1}wYw? b9[IkZ-YnfY<ɶ9@14R$;1~ZΝ|/uZO}ܯOwndď$Jb Xjm[1jjz]k7 H"c`T0G9(=BuGױM{kF;gh-$ͿL p1o߳]/eu>/m^OC ڭѬdQ\aG4Pm_W_ Oٿ^ՏA&o[}I ոDm(ܕ/0WOYx4:o?, M RH@a>qO@E.wzw O?SjpJ4Y KvS6~nWQE6|Ē[QHaEPEPEPEPEPEPEPEs>#,3N-~vn956K^'F0D]rpmGB{@ݒtQEQEQEQEQE_¹gfu.w%ƛ ;5Nw|_CV.#Zj=3\X'R2fڬ| nthumoMK{饊Mf#oA v&Ҽ|IuPg#4bq5a=x M{4Yf3\˩]K,o)RdtbpKmsK_iyfKvyYkW'MR}Gfӯ;o!8¿ |C,&ͭ<75#܋۸$"<\[*WCfc|≯to6QV;I *8xϔ0AAɮ:_s·9K>MI..f&7̙`sw{][NqnMn/]KC^$΋G`7.-&VTbWP@nXgL p56i5WSk""l2ڤ0*17o x{ZttҵDR0@%ؐCw(n޿K\5?ǭjjڅM/+$s y N>Bq|Vv:15%wI#%Vr: ck?<|;Hdx[dTp񒈨BF1w>g|zS/(&泪蚫躍%}v J$Ϝ>*ПZ]jzMB-u; n=2'錃Wk&uDhb*._3xzj| [K:"Q [K:"W4UrMj_/"| [K:"Q [K:"W4Q_?7$V?'տ]3%տ]3%}sE\>#Aj_/"| [K:"Q [K:"W4Q_?7$V?'տ]3%տ]3%}sE\>#Aj_/"| [K:"Q [K:"W4Q_?7$V?'տ]3%տ]3%}sE\>#Aj_/"| [K:"S&>)2Ww0| :@㨯)CV`ޓj?7$5YlqXЫH\,OrIڦygvg[R0klƷixC= WP.x :zͩxb4{i.ˉ%O9ʢ`3O~3t/jXOp*0p='Z}+XmA1 DE - 8_NO5G'Z}:?7 ?j j(T\/U ͻZ!1F@ZѢ ( k1\x?y5ܶvmƥo3ڤr+ԫɾ.4~bndHfHs.]̠dB {3[/\k{mB"e!!'@}pɞ/|k-5c.mme -0\f}85wN]VWH͑#G1g^O~6?#:kcuC+ t}NYʚIR<6rX$^kCgm.<'սx = TX0pZ=iq2nAjxnxOFaHKR9#ԕ%g`qR<Ȗ]5zL Oj.b U'*C 񮫠èxC\xk:kL/SM6il|8mt$2(OdWI6Sx+>ԼEhQ/Ү-PnLp svկl'_ϩ<itwk}s8PeC<| ĝsI׋ +[w{H5kWBH1=}XSt~W"sqiJYEP¿]?~(E?"~K!''֪ϙW2($Q G.~}NC&CU?̿_H֊+g]C w(YEP¿]>K!!_/$}kE| G.~,Q_ESAɿO/>JYEP¿]?~(E?"}jKZ_%,Q_E?WGD?oD>S|%䏭kPxh7.ngBU>Q_ O`~hYEP¿]úi5 xTUfxQy*# $fd Hz(FemJJҦoѳ?Xη$EyojتDi&-vWv.#rW^X3;WI^kx:o WSC`db3 ]z:Կ'sK.A/ qEpj_G ڗiwqEpj_G ڗiwqEpj_G ڗiwqEpj_G ڗiwqEpj_G ڗiwqEpj_]>izL6W u2̮ā9 ( * 8D*9T紌oKytEgb[[ zl]hQYؖfķ7@V%ٿ?-o=o?6o.KytEgb[[ zl]hQYؖfķ7@V%ٿ?-o=o?6o.KytEgb[[ zl]hQYؖfķ7@O KO=o}heA*A?ؖfķ7@_ŷe׌X0OX!f\t&g:;p&Oř1bO$Iķ7G%ٿТ-o=o?6o.4(Kytb[[ +?zl]ؖfBķ7G%ٿТ-o=o?6o.4(Kytb[[ +?zl]>"%YKrJhQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@cxn5hGQ\;am^ @GwG.|1~J}H>A5hgG[jzV:u HSX ^T.<5Lxɵ^Lºi+{rg͌k|iZ힡{>ԯ_BF.DfYN?v"'F MjMa=?R#(G/i_Wʚ_K-7:r~n,nMٗP rᅙ5,j2xD4.jXK!|V-N!oG2j$SehƒF7Yy٧diGTpx]">{J8¾r>!|97ĖZMz"hR* )9|G֞"-|ghũ"k(bGWy2>9fڳӛG_o+ ? W7XYCcĺΫ_xVэ5ֱАa >8'k?^!о &nKbo"b$i61;2ᱴez7mr[KV.i.ȏq=aPN;=ȫ6ֺ_mthl&W!ex¶8v+[h+2{ ۘ4,RHR|$`#1ͼM>o+vtQEAAEPEPEPEPEPEPEPEPEPEPEPEPEP\'K-:S [GvV\2º (|Yۃqu? fH$rA855ώ#} CuhP""!0 cڽZ|q gQѤyJ$c8F=dO'& ! Z%CGzxOW/c)e*BTdpN"o='Gazul~RXw63 355 282 0 C     C   @" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Eռ} WT摞8b HE8i:(P rI*\Mm Vf983n{pM{߈tp01UJcnny8k|w_ ?~ -ZYx˥OknU&GYSV;H^H#$N_ds-ޯ#}Gۏ+t?9.,DKO#FF}+c^/t/kv^YK0`$^Zp +M-Pf/ۏ(qO} | ZNq xɠِ2iLo >漵|%{? o-w׷6k- ik\Ѳol'<{=?4Jwqn>|?꺧/[vOo'Oդ{W#F-@K @2?׾X>_bX!Gj2]&_p<ҷ_~64y'ۏ+_]]qpm'{9)I\8q\>Դ<%Ai7KsM̯ Jd{GiECjۘ:m<"/⾭٥H9>W3<=g x{cPFn݁yl[oמ!U쁙FDX}{N|Y{GxY R ?o|[ӣ 0PC"&VퟓZ.wCr3:9jM/0euxXP{W3<=g x{kasTM)c$$#IUCGֿ[~Ё8-/)rk-XJ,Qvުgq=mi|g-퍂iPKH| o3<=g x{;? _7ǿ4C$9ɷa=~8-/(q[B_Pgu@՞-u ̟yo&-b 6yyނ>si~m}e HdvAs[+T?~Ё8-/) V[e+[krx=0Ğh 0YW_$񵟋xv[޼pFՈ ~]ko|G3<=’n~冩'׆m=}ƍmy6 %q33K&b$ ZG7 kiPޕk̮[a\0ko|G3<=Ž]~a㗎|+XL t&17d/99X޵mMu9:d #|!l\$,YeE1 j6I/8hO]E&C]ЁQ£|}9Nߡ* ~ UIk"Ѯ.cѯWT1:;|/A{xo& nR0Ltcʫ6C\~5WIznM@ (ߘv|9f֙ữiƫ-y%̾TR+K̢F 9[ό^q}kcM%Mpfs(H #,T`sU IhZv6j׶dXʨQQ7He¾6Ҿ"kj^icKׯXH^9cx8,>ar>|>ҭեsKR'6Iif(W fso.WOOo?qxσkSZ֡kUuy HYgbRUr:U| Z֥rq$60gP'N2Tm3㿉#T6sXirs13:r z /⍜cTe֝so%0 O$Wr nN_$uڧ-M;VշOn׍:[ۉ?>tXI"Ѿ@I^仺%ɬ[hFXMBh@*(ѐAx wAx:jxAc/Io煖12 Tgq췬VN>[fwIf֢ ʈX2KeS8wnE~GM~q,:֗m%P%eyQꛁ<k~jZ|je6qGqi^l<@LQ9dB0r85u?|ķ6kwm!hdfU8$1^:,X'KZK1qlw-,UHE)]s[[%'{_C5Kmq5Bif)bj=ky.yvao7^ ssA^Z xľ L/li:̶]o4 PT)%-x>=/HIIw[%嶛s5:Bܤf(@wZj3vR/4Q4年"/ 8\.8`k,?dgSMZx[ܺ|jzڠX->ȉL[ / >>ȓXƗ>%I%H[UkWq2X܊jW_~^]?Etu\ɬ666~е Q]O- _R}G]g5HB4PVM`G1Fl;F AR;l9W]OhGdz}}Swk=x7a跗\j'R{.qs,Ǐ0RJP^նA[?46ڍdC19G/j_%<%᛭Rn\4m1sdSOdZi"_Beĺ^>2;nevDɏԞ@9:_62xR|?\݉EǛr A;Y>OZS÷G綞W)'6e!u K& ă Qk#j6)usrNu]>Ys>^ݬI R_JL6:z ]U{9b I y6yf='gDx"@5c{Ǧ7W=ݽyxBCd薖rOZ\iv֒Csq,SKn=ȄE7${zV/_ zLLHiH}yIaѫiPw~߉hS]64v^XOhJ,eE;:1o+xZU_YC-֙$ Tm[iUXk?%&waET#X"i6>Γi7_ ,Dãv@@c}Bw m4x&TԖO"dYyT =̻CaFO?}Sךw]t};MuH,wQn-۵~6;[ Gjj"u!kß޷-K_&h-hDnleggMLr%\>jy Wan5[dR`{Kx˹;#cIk_O7o͝{̷嵵2PƳAa oqq3ƬT8*dMOx?75ηv}N$%~!˹@5xk~ri) Ю 6YvwnC_oZi׌9Ӽ=J'][Bi@+s?܎Tbӭ!/8-m-åGG(&*/ϴ)i kK ҵiZf5NGIbPKE;9ځ S#V-*LHM-f[mX 41yv2] F+~# gprgJl>ȟejį6M'l+şxkGn,u+۝fH"yīnĒ@RK`f7Fk+8;CiM5kyQ,]ѸuyV~>%<.[!Ws@gw<. e%Ydnp}Ÿ5'ǖ^.u:UVSEhA$IzU4JrqO ?|]k^~5 hck;V9Q/$789V?wGЬ.#tkq1Osk|p5}pw 5'&oZ\. 8s<pN8N:6ghx_X,[t]V9.R'6G'ǧ3^oKy7h+ |[.eGϠGe G4 va1"7Ҁ:)~Z5펌txSu'Oh-$ܼXQ!fL)9==UM/_[~ 'NM\o,ul.|HC $X #:$ci:6w-IalPy9%BF űw_&t'B՟VDIL6v/Ns>%5ųhhrOi-8۷c9=^--i˥h^#uiڅݒGoo#[p%Va`2jM*]O7aS6 eӎݩ3l fp:kwvG/v!QmZ)nN%|L>kz4zq,qUrR'c^:vo%^Zڗ?T4nF6^NyIQ&b ,L4p4IQ.u?2<30#Tܰ$\K)GIV' YϮ{5Ucˮڭk-qvʬ2cǵY/эSb-I(AztYϮ{5Zq^Bl#<= di>3ۻ=3Znۋ{KHNHêTϥ\ћz^&NER((("`:` sx3YW&]1uEk"8_(F.;Q_i>{n&6eǗooaF ~k/ǩYxjK١ҭ14lRXi^u[15j!eRݔa"J9ms_}q5M+IJ~*Յ͂iD *̷rZ1ff!BU9^D_/qs^V>Z SâšM;c,WqOk2D G8\oU/|s < *Mh,)yQe#=訶wV,>{kh|;{+ >I-q2GMHM0vy`d!:Ox:ŻGYG=N>dI]DR,aτ><\)?Ed܆_ V?dC5 iu3u$n.e:Le^HugmX\25 ot*=[Am++,3|G,$|郅m]<φ!oEum4"g(;A [vv|IY6}I5EG%P *Ͻry)潢?: #Ԯ~V\(Z| dnOwwp~(:xkPƥ S}\GwYMkQ"\HP3GxgZ>xJǃ+Jd;x[=[t_ΑщpB⾗^:J$K&WR0A KW4m?N充k Y$q  'v]}' _^x_}>[]f6/2G#U?s{НU(3z-73ju]$ҭ\)EXےū[Pu cǺiauaX/|[j/\ڈ +q-&>"C'jg ڟYm[LVi^2ޕk;\ivnmj:Kޠl,E}-nЫN*]K;=$ Ⱦq,J sGJn\JQEIAEPEPEPE|Yk~(W׆}?E, |{8~Bw?x֢nǤQ^;{Xƥ[[>D-: v.IS7=Ϧ/G[{"r.mfƝe|lv]cب1xs]E"&-o!2zmGh:_HM'9$1E5K,2i#D} r2m{a=b'kwMKKREyi e9^@軉w %~Һv#~&<_kkZw&<w\N} TN-IgWxm {X,UT83Bwʼn- 閾#Җ+{Cmneϛ> @, ȇO覣t/u FGE$&jc5xPᆟU m+/;HJ~QЇ=2:?ǟOEi:"~/b-;(R!5\rpIѾoC{&jc4}&jc5.[Z4YSmOϯ0Ҵ6_QH-dq ' a/d>&x:W|'OhkKh5< n ,i:lW)@ܮy_ko~|XgFc_ 2,&iZΕeB`885oό|)m{M:Fi]m+H@P k #w)jWa'̓FQP0ito`奮ηAVRWr0q˯'57wWj%qytgwrl,Pz[N^ ៍wm;?IH4X.19$i19Aa&$6:}Υ۸}9mO)4)3Y70M6%Ɍk>cXm.]>l.u+mXI &GU,mҼ_L:՞Y/RDvcizu>AwM; 4$G?L"hG?L"k?h>*i|V|FY-=6rb4}.id`0 NG5O^wVB\|?k[İv#.UU~Q+0+OG}&jc4}&jc5ʌqKEyUֱC-8 ijZ2;\aث\GTQE+ƤkA=wž$𽕏- [F#pcz\Ƹ; 5SZ= Gk Gn_8p#:,0YWDn5 1db6:])%+[|5cyz>ݙ;K(61J ʓ_] .\AxT͊T..ʞ`l1Ag(c?g۩>~{Eςu t`ne-r@+@\tu G EE> !Ž $!D{[f~<?覭ieݍ|^ 6N?Kfkhϸgx7_;ǖzV褶_IujDw l`S"jWk6kU?~ҼGhx+b[Lj[X'1?chmSZV 4I'=Rk.n/&TWg ȨX|Z mhCf[woQwm_ [E;+q "N#fb|"?o}W=|U0 Ս6-+N>EI {P)7qh@r~!x]\%R!qch  u'f O_ tס1ͩ^0eIl2y~V`-=EJͻ Z̳]m^xLmKSa:#CyFc|Ӵ!$$}:[-? $6yw>#m˩[ξއ2$Liy.IOs9*wr<[~xEZx[I_OP+߳LU $>ӌ{ص5-cc4?ſͱ]At}G-<߲y<ϱT(K1嘒zWk~Ŭ٬~Vfk6k{/-?'c*ʗG1-jG*L vF VQH|I߁] M55.~ɧDbq8 t%׾2x fxJ4*#㻙vT-g.9I->E5նiN,lqG+:*b`J]d4Kχ3-':uM٩*GO-e VvmZE'gմBOA|G{_kz~?u]C6m~r<(y䀸@7n_%h!ڍզ]gf]_GXZ TgqPs|Ix@>%Kbʰ$,H;&}.[|]75⃜9NCoڋrH&綆)Z o.tȭcur;B#\0*rÚ4iֿhW~B71*o#keyI~0!(%4rxmſgi;ϴ2)x='gh4{9<_|>Դh=zhFhTd}%` $e7p ZE'f3Zfsrcu8m 7R:TxM׭Y.5[S,G| K_Pt75Mg]kk;q01-KlKnb)>,|4]&wz"esy _d+xD'o+;3j[;sM+_((((*(ma1#}=I'֥+jB8r'o$ie%KxqQ=Z@E#CI"6*2XmMFIڊIE@Vb!1xb$zM#%H}OEVM])*6:t4,mH0 o/TS+h-dC*t=Hro=UXwDm9ҹ>񭅔 Zwq^CwʑI]%J:MɭZT msq33 T NA[hTF#䚱E0 (Q@Q@Q@Q@Q@Q@9}5|}Jqwiɩ .^FN?yX2&x {Yu{M'ZI[fhD,y\W|}Aj{o}`X闲&SdV&d>Դf ~a6kE~χQH@c׳KIĚ [ 6ԙ>S Wq>L|ǹFmuqk-|oa}^j]]cM#b:U|׷DE8?O<$]x?"4-eO O4"xXZFw8ڠ/֣~?+(inN&b\1u#٦=4ou9VPnJQ-;Y^@APHsy_]KߍnᯋA)NJF6 7.7 7>e,030S}r/+yi_[d,-#͑Y ksj wyN Ixܕ(#ަo4ZKK^\t b6 QӺV_ֲOmUmJu=:ͥ2$@vacP<+wDƩ)Y׮<$4Q}{($7^Pl1J7==Ǘim F?'źgkpJ)F.BפKG²|/-g[EᣝDyIm~Kb[[x]0&[rrHaOdMwxׁ|9^_ x[YӢι4DeT rlZ/ki4 {x^k1WQ_M.۫t0mI@_4>򤒣/٫[]oC׬MNCu>)ZKKD(cF .R|\n ;HIEH e`,2BY_ io=cIMDX*S^(C +ۨOMZK'үhLwv V^FA<^soڀA4MSZƣxX1GX]EF 9`-Dݭ~WϚ;]9]KG^jzͭZ"L 2"+uM':L}7SKy(#;0YΥ%ء'/'gtֶ%@1+ʪ,O-E|7X///W>/i: RUBsݣpTrëBiuԇ+ѿo'#Q?7QV^-5[]þ7xFmŐ_7t^iF0`rǷo?7toR/ZUVP : oD!LA+(+?#<[Z iGmP(m@0?mDC_ԟG1o"/W"U>*~œd_פ-m_hx#J/ 3Kt-Xgz_h_2<\m9fB64Y _5M6S[S;iֿ//˕|\ddai:cN[=gyg_I5m'3_uu3A/j!Լ-Lq:8c$O$hdW/q>>5%Q]Ir$xOWsh^||kJ{*7bb?\ƀ,Y,|[utjX,SrA#5`QE (5_Eȳ\dJ#"_6[^(b@UlxD[ثkk;RJ.j2F:W^~;:G*WRۢ"Gl2p%k>~!Š}C_H_Z_6KHG-[7?j^_yvҥ⺿׼{?^"'FRʂp!<}Co +?KFz_4_u_͟@X9H "3JIsVHfׯOPf +)YYyFCn/xþ#izUMtKݫA2AP&}S^_1ǎ όmtjq]Q"Xs*rH +~=3m/x.mxzN6«7G%͞[epFG c4=(b_l 5zŷF|ҭۙݨ.z:m#'turXVlj<8}5%PA^kq#jRtiyy[EPfa>ݿ@ZV\_U3_Ͷ}uQWW\ÃMԟ]e2(!ƥEm]'?Zc _}事I-Q|E`?G^R1_| ]#w:~v3 ` .肙52mZ#ׇ|cZx||3|O{-mgqorLnKb v:xZ>Z^ nnKy>_FF#ҿf_߇#_OA-ึW/ZIG%˓ \K沶#\n_g>z_ .e[ß'c`'Sφ'e<%GCc'\um+Iχ6W#hNggO&)gUˀlد(bwkWnu"7axEы\t7bb?Y(EPEPEPEPEPEPEk]Znmn W+t1PI*p99h H]%3ۮ&ekH(H\' twإ[5V^7 p[}HAسIU;\~cR 2xoVI]GdcQ?M=!cHԾ&&MhdWʙ](pX ?+C9o^7QHoT Îupʂj /_8*}m~m?^?ͧ_#ϵɢxǺ楢kzGڒF!HDhIP9cI%>#G-4yJ6$F#q^EBB|!ru|g?i^("V+:X+9;v*:ԣQ]\vfJgik?j]CK^־oSJꈒu+fD14wC[r~MjѾǒS?S߻O3G%3?5tQ`颛w~kg2|%d5Y0]Zo,咳;~\akYse'%ֺ mc [9D׳0*w9hq7wrҲG_o7KoUV:<1orknn|_5dQ'1*c3vnͱ8թRe&֫fOؚ8FBL_9xk?εC|+o_(>A珣#i_@x#J/χfikXKDѠ(v,Hr[v>lc$IY}AwiolXmfǯWMB/BGaa*t! n<'ğ{ux}Ú|EA ^2_5Z'P6e>+w}J?)Ni;EG%$ts9w6SJuӿ(]};lxEы\?z?eGֱ`Ga;!fR8cZ:?/1k"][Rrl?>!Oχ>'B,emE #Yc+[,<ü; տNSϾ}IG%:Ԕ\cO#4_?3U* g5TދdQR I$jz@QE ( ( l#;DPK3=M:kּwO0–#m.e56j@v[h2i7`s#YA:].nYb}UԐG5r5DԴ fӭO>i= n$/K O$xq_~ }Gö7~&b1XnJ.zwP'%V|]{úNǷW]jQ$sKp2d+#ÿ?&ώOKoXubiG!q5E=\|Ck`5<61^+_ (l~iVWۭRo/3]۸g8?[W~/i:y ྒྷ|//-h5tb #֗mpVm3@> &p+]?SH׿0(5[m&d}JZ`V8с}~ꪍOm|O-~)&rBoWG*ЛWUE<_ |!sxWD)4t(|xCB?54];Vhtb;wtxEы\?z?e.yt]P_T՟OWCl<3i-]ߑxYo+񽟆hNծ<9z}͸c40W 6[#*kOō3U׎IKIod>Z-kvr4|1e|2M[Q -^O%߈.n4ˈm1(t&y"Ht^o/~Չy_;W>O|???S#"(( ]GҖ[@\Xڤ!sh~|0mÿ \\xKC]*I%M¤%rI<׊?esY_pcUѵSؕlZ $޾Z{_hմ? ?u,lM:Ke,V!`b|o+ǀ|K:X4W=_Xri2&/+);A-}mkm^8v?-Ρ鎣'ʃ!lf3OI/OX+X@m$ P7Ta-tw{e=H! x}|%`4֗5uS>yo3T7+u//Ĵ֖śXj-V$/iPxZ?d6˸_~)'y":x!!{ѭOMhWo;a }L6la@  tF?_v#W/;@Wayc#6W[{}2hK1.$Ef~3:}F nC#_ F?_vO𖉤뚎eXjƷ[Ora_hd_ei=\˰*qߟ®yY<_ >Y?6.c+Xj=\G%+#pj(_/*jeohW(_/*?BE~1W־F?_v#W/;Edž'kn14ofu<ѿxZ_1Z卭YaR ۼ^Ҧ1'3_M :ɽGUz<_ F?_v{B?oQ#W/;GYg}iX5fPI'l5RV׊;[R{Ya]X#*N57yY{B?oQ#W/;GkɽGUzY7(VGp'Xu.sF .d6xݸ:R4=&šXk#qjQ#U$fPqRY<_ kɽGUzY7(VGp5dУO=G,u_iǫ#jeohYxºƑyo%Ieb!JN2{ Ҭ#W/;W,J&tyq2)U'qQE ( ( ( ( ( 硆\{Ʊ}"\I CfpW$Izk۶ރjWs]K#*1$`PJi]3_ŗ#QwC>|Y5|9OOjd]HRKxNߗ&91WOwFx@SW:xwڮig疵ӦX,ؒ7XJ-u][%;/o>Z, o<}~(M"1MSm2 Mm.'XC|h{ |]-s[ꚝ&-d3*eY eݸUz*I[K UVLQ dyq !c>Y)q{XDqϷJCKR_%߀/4i.!gms :BeK)H3.gֿ/F}kj_[Ϟ=Ѯ7٭u fnu O/ȘA]1D9 UgS浹IdYʎ2$0o"Q7k -GV^ԯKGjc"*vB3d׎|YR—piW*> GLMe^fe&nP5_&lΓs_ZAuj>w҈I $pUAld^C|Cy<Mf{믳U0#y PsP`]ϭ_MAzg/iޠte)#e,~P=6֒\O Z,Pxh{~9X}Bfio󮤏x\RT89_|u|Y53_ŗ#Wx[w&i4 k_Xk7ֺs=ɐxd % 5r6MAo#V-X._O{3E "\DG'bC}.{Vo{izڕᵉ6Ilc(Sa1x?ԁ K*2'oIpլ (C ( ( ( ( ( _[Oφ rmpdd/ 8>Z< k7].$hw``'8?Nu,3_Iu\ 5aA% C W7Gi-;_'ЭWN$/ @W± .[<{qg)j\`+v?6x0-tMg\(4ˇD:&]d++]?Xy_Us;ԕ ;פ+k-v67-o2KW*C*=+4*EKK(t$X䜒I$IMb]|9necV. 눔9'j8'Tp6)]KZTk cSQ0fHُeRJOTڮs=ŕ:7+KmNs]++]?Xy_S_ |3ؿKu#uk|KoqL*GFsO?<i,FQ\O<\Lw<1%ڻV>W4-Í)&kT09k9Q2o2\Gx::luX85lxHG(@u22qצjUe%^uT +c}K9H/>mlP?imlcfLI2,[$}*_ *ǂ k";eKءYe.YQ'E9'ֻ?V>W4±V H Germany 8 1672 2178 1 5 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((l" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?mFU{P8$[9*t.8}*pxzzl>nZD89}E&e,Fl3횻^/kq,%6sf[))VRIuلXAy%a|T$F~a4-p;>4NmǙheOE\K}oRr|3=cj1 a{?2?"ہf/i4wQ)=x&_K]8Cv﹑W?$ZПPgg'\^[^xfkCjL*9'iGVOd@^?:V&ҙf.$T9M gwEwun6M޽ kkMđ  g֩2lΛ6~\GK5۴W)v\AtCҫV usw%o R20G5ewʢ89@<}UaW`3n^$HHHJyCJr\)Bq M>C4D r cb(Al?T5f3RYKv ( q*P|隂7)ȸQq6"ť.!u2قO_aM+]I%N'(0P%Mo-ë,e}!K@$acYxZEխ(F=XKN.EƔ:8ɡ'3Z3a&Bb{h +)}+yճH+G>ޕj0äܬE”d `օ;dV{Eg9#KZ02<^@tgÀH}3\,oWVRCk)bIcLֺ-7ZQ225IH< КY[ŝĖ[6z)qLt3M1~3O]B[e1:H0DVwxG:tv=D8HEApq4ss:8PE<ֲ&@H>T:r v*y2)ҳ%;{(çALdwH];P>u{u b sӨϥ^业iSh@dMlݡ&"IB1$8{ֆ5ơu$^x0<:QfNAe֯yx kapu5ņ%["[A{ž!4% cHv/@R:dOeK xkoE硆ä1jtF9"(<sg"Kj"sV֣g&m |tg8>.n6$l\z?*ጔ!{jΙSv;kBI肌+ jn.mTۀkάY51X{5sBؗ!p0y5%a!YO6,u ZJJ#8,n{ onE͕mTcycb9EjƗL2q^cWni E·b*qљ֣%3[]7E{[xUY'"M޷=ԍ[_~rs^˭hU GVcݾc9;υ\n!m{w;nohyPJodwmQpn%w8P0J7pȖGb+4r|6SRdr`Vnk灲~W`pH1C&Q p;ש›m TG+nrG\՛_ tP'Z6,O_M.mPD<7eqKkRp^U'a=~,ݪ2ZOo4yx #~41-Kyd "(Y09f6\2˅`#Jo"tɞ1!̹^bkXMVm[2eJK 4Ffb[K[d0LB z}ikg&N33x4R[4׊M%;9<8 d6װ6,Jd }0}Dv2Gy3X[`!>gq4E(@A۞"Y@[>[9RjY͝DʥBJWAky7C=LpxeA2I,%X"[2Vf>s(~nhKkYnbFUyOoJ4'޼T\K t֬?OCquq$oiq|=p+o9NEܨ@_0 {w^Bs{}-^IX`Nq݇oj]-E*.ZԼyjwwesd]V5(n;VD ,esHH!xH  EZP3.'¼ړt6xdVYPH;]9.a2RO*-6\ly3[ כV:lH@/ IʸeOvln.[ke3/z8O#q~+.ɍmUtz۲, $Da+l:d*sxKU6|`'SNH;r5Ӡ0XՕkMqY'P#x9I9[2x9dwIyC\Y~bPyF 5'$Qz-%VvuYRv&%D0#eb7W]i’;DLj4e RINuʘxY,-|e6/\‘q=>jO'eW=Hu\wi\ ; }EFrғZV*f {˄V,3g5Ϭ'ZA[-g!8K5 TG8d0I ƭj6Z>a|2ɰ!;n[#0+湶_վgTcʴ+x &u[nD/(#9$J7w1ȍ >judq~-z|>{HHmѬK؂xߥiUu$n !s+VRO>)!1A'\E Ix<¹H/t؞":'z^\,NdwV}偓̋|}1G]2B]BsioY Rz}J]jY\,.^7 ϵux{F"h;m,{W:R֧nյݚImgm=B\@r3muq&K yk/KͶ#`<\ r1p}jj`|~D"1 {dׯxTO:)=44uH$#3>7p?>_}>+H\s z8jhc8f́(heP0tzf|km2VEdI"O 9V*s\Y.涑o$n#=(XpJ)ܞsOz'H/%(aqo;F$u*˷X@p1tuJdq#6M|8]QLT'P[;Ai!<|^=OҼO3G J~iel(s2j>pq:XFO\I?Cw:cqXH?³E P'Ӟ'ify_,pRk#7޶4;vPo .f({g˼FATh\iЫ$Vw -p3שfGJ94X.Acz5ͬvJ cAmpͤC^y讪s҈n/.{in @rA= $cu=N2?^ֳmfe٤S#>^)Kh.eW|Ieœ5xA&B|'jAN.oA8}yp4 Bc70ǟAy.P?S08'@+xchGQWq>Ra"Ъ޿y4t cTEyJ3w[Mk$<(HAnhc=u9*iڵ]M+ZNAI#GrIYt%ʬ̘]HTLjȼ$ I,ҹ[rÜLHHW%Ztt!N[3_oyF$5h$J8*8渣޿fA0(q󪷲jF)?||~N~JOVZc-*U^HJ%׵SNq#76'YngUQ[w"E>qؐF 9ampUe"KJxۦyN;d>'.,F,}k*qvbM ~K Gq/ek:9>dq {xiVƿCU os V8bC[v>-H9ZVz>*xA*G>/Eg}ϕ=TW={Zu' [۳¡}ZXV:n>c<$1( LRDEq"Ð qA\2Hc8bΖQy2@*ç?c .wGMsv%1֩OݤN2:#tsR2$!6=)׭wx1tx_ozGFίϩYH$|*s]tۖv"Gv\n. Ǒ[՛iYDL'ESp+(ӏ$ Z}w.ynJxFnl{ִҮ\Gkڥ%g N=SI-|UdF$_ufqlOpA*g^Ԛf%A?:Kyv`PA A̶Z*Y2urF=9kK&׺Mhйʣ #ˤ.&/3^𥝕+tY뱇bY^\A#JC1|{ORDW.aOhiB4qܵʄf39 Ob1TSP ~3%ҧGr}^XvJ 4`b\ȭ{Su Z3!9?(@;tv U28tBpt?OE-r3o(H$z g3yڛgaju3 $HX^oU{/e[{d@O jUUې9U&)Tx?~xR"6裧]ݯKhDod"~+е4vhqƨ__y9Ln#Mkx'&Xdk&XSNO5V{dS#"dX]YVC-:':ҲV8 9P3[\ q\$ݮ 8O<\*Mܣ x=qUnͼuso*3^LE&ۅ]s҆; 53 e ֋gwS#hiҽeN}3EuiY3* eaJP0$Z~p[%%fx'*¥$3Q6cp)I%8ztkZ-HHN1 {򤶊FL$#CVu-Xķ jA?(֕"Ng8縯 ZaZ&$7PڰLe鞧]ѮՄs`cQX30nQwϥhZikw43"wnXKj#Jlb@o[8 º.kXI5b[r2hrr9#"kHQ[weΣ,G4.=h l%`,`͸y]rr>i3OhaIa92~B/om-%CdzsJNdq 12W|n ьKj z`:~I5#00zR^w$I;V]sӷVJռ=-qڳ89Ǯ+t ,=վfRXپ"ދ[\Z]q,i18^k<]P*跗7PRgaˆ/=?Sbci t'{bq\8[MAtCok7CfFokkewz![#iV 2`n1޼cǏ{l{f)ߒ=;cw<{>,g3\8}AH9ۻϽo$PƟfi@^iuo>J\ RE#ͪ*Ek|$(L$Zi-fٻs,H`A#'wҢ[]\0 JLg.kQJ1+jKo.c,:U)x$VqOo֬4x5ʘ'x=8=u)6EݻŲEt; 6ֺ>+l9ν0ۥqvI4h^D Cuv' fmA*ԉhlm,#'C*1_o^ӒF9؅Te)SwK>I%G@V2d{lRe_К̴<ò:G>p:ͫElt%;OyV,\2ebK1>àHYћm .99/$Q8ԒH# ~M,iM`LC.GksvlpbDT*H##8\-I SmVḊJ++8sִ= HCf݊HxuȮSſ? \\ݼW7LZ{q>k|EԑxwG\J7ͳo[>;KolQd9~) kq^]cEдKRHlsw'=x'uL?yk6dH6P׭c s0 [Hx#w^Ē>9XRǷr EDf/;hK[Y"}ǯVEo{ i,ARJ}1Yv%V̨88=2*z3m$w$m*3ƣ z(Ѣ,>8}C#6U=Ƕs`YȕLlzkz|]i(/)?Vꮤt"d?rKѱ 0F\v>J,gp`ed=*ԷҤhsr 椶ehT.CQֵ*V;Xb1ϥe91SG'όs3zиvimuezSZ˧0c%$,svkR6i!7 b)ՍcJOCY#X :RPi^dT .Ԋ| .MF9/#y>gU>>UoE.yf2z0*>*z~[H2gFp>'wлL3B̡t`Nҵ__- X'vx!%L,V=A!ʃqȪUp̎\339v^6Lk2hpl1ҾzӮMBAbB=v 'xC\x)7Zug޶I'urnV{+: q.+UPޮ-By)%\U>RL9v뚷o%x'Ei Ny!_ v)֛kbtt-`O^-V{4zeUH0ka$)`d 'i?ָ"RӤcV ~y]ᶺK*yC1L>٬veUIcE gqӽy׈PX-Pz󴞞⽢k"}1ͼλqx/fӬ̈@'펞gt|iugW XxΓ0C{|rIt[YV\v3ٙ!s@;VӮgkkvBʯLdv҂Zz64;5cϗɴ.GY22@z5n8芶9@Zaա[Xd>=++;؁f?򬤡4&`C$x-uO+Y3<`Gd^E}=%WF9]B0X ZRWɚJD(H\=s{9ܵi+2d5,{Os[roG8aO%\paqIjۧeGq]#x܈I^։v]%>fV^?NY0S6a#1ɮW[D:da#ŒΥ!FdR 8+ŧ;r;h|>92)Q<Ic~y6n~=A>m;ʾcv\p k\.n.`UV6IzO W7/5͂I9}3ޱa4-|5{us2Yr'dϥxY&9bl1 ):|5)g6qzҌn*œ\hmIaF\Ƥ8`[!91躣%w_-9+*e-u?(l5]pΫs3WL՛+Vz욥41]EMVxHj6W=i?k;p`9@U =ͽI,8*a S3hf̯ژ'ds!cZMȖ'L%G=֍_Eq˛{+*8;=)5=/u[H^6~WIxSԍZ6M.MGO2n<՘'=:YWf9WO0"Gq>Ybzu6,0[$pw2Oq9IԌVIWwGՋ{xzެӶ:B G]I4W$t0~kbKL8R[r#+ⶆkmm {:U+ V[\^H ]]!.*H$D=y8 1ߚ`̫պ3D2E1mxVAW sYKy,xJZPN T5eTp8LSlF R _޸*Mv{; ?j\ DJ!k<M26^1k q4^-6inG\#ڗH -1$y?f^lq2wy׵ja J \wgm==v]p(ʤv}k۵Q' :b{HvrÞ#X\$,%MuДlDӹGn'HdrFxS 7MFFO9ݥ[`A') ;Oګg{tWc0 N0CFb CX(*HQ@T1MZ",DM~Uw}MJ6;șzq5ŷMǑDŽ#iqZj3!֨tV?>YQsg؞Iciw :N0G^/."&Kig-7q k6c#l."*^N-{k<$S\D8WޥGyďa}6(9P ,?$=+[)- Ϳ$d` q,5Eئ>f<=yJ [C6>_=` 29zxm-en7'FӚ9]#mm{ ۟ǥexjTsʵmؕ4Ӵ3qһV,*{]GienElRBY Ir-Q,'% .O1Ynk"9 $FA!y^nY:h6+D1gp+%dh$ON>|KqC*ʠ"Id.3Z=ԔvvUo8xXg9Tfs5+η?/(رL`wΩYL9$N$fmZXKsO.(" 31֖ojmnwIt#ֆk[Kii17Zf[_>mř+ Ƹ)s.3ZۤgOh< mr$s^O$j#t E|wg+$F ~ǥ{NfO24YX#5e4{3+ƾ=fؼ:m*͜^0ʹ?>dqҼLx^Ef?.WҾa'R2$s%Alӑ񗀠"[4/o'+$fUF(5Cbvtq8ٙ%SbsįT2iyf#&0èہךti9Ys↺6-GB@ 'ӥ`j׷K7vhtv0Ց.ocl2(9s[{H.VEE8)T\Ԩm^Inŏ[-+,`46 'ָ7i\$S:JOx:c:2bh Rǜ0'_J5S\$N;B_B-mXc{6HlJt'OEıBO9בֹv!0g99jמ54)+ >Y=u*M.z=·k>-vy>OתgKH8bxcy$P7*18Xa=Zc-#er.xGTee&U ddEbg8U5[-W&ivЏ~Ez4Av|É\S'UvGf6#gz{أGށ[TnLa#?ʘ]wmnB̐+,SwZ%ԳD$Fٷ tvg gDQh),rogdTH|+մӭWpYހxԟ1׌}+μGxrj Ied\E{уKlX^'yoۘyS)L|:YFy辸yvA:y! ]8>JxmCP4K  \pN%]1ֶ."U 282^'E]li֧L  ' 9ޠ WM梚h2pA'1UE2$2r80OQBcʬ9+A=\YhJ1V㨪r^fW1 ib~+ kfhYWg;ZCk%D|h>Ep\Dg j)Ԯ O.KE%G$6Ga_c$ 6ܮ8QN:Sӵ0Dk"Fm(+ͯѓC9>?[s*,Gu zޮ[j6IXF׀+;[ hu(<e%o{Y/쌍Bv?¹ _>#-J/"ZElbSO* QՆN3q42ā x{\֗l%eVؘԐߧR+eHSsenf-RKy Uo^'F\:R3Vf] 59T6;4A`` 9+TQKJK񯟩K:TUxtC_dr3U<HUL0x ۹-cM؋+@9;e܃ $[ךꥍZS1t"G|< /?y$EIV;HVjg<.*oô ^{>y!4`KӐz8b%!`\-gۨB X}Nuu +3 N5juВ)UNFՐ{;UkbZ#S = _[jt7RMebi~W{~W:ޣk,e~. O~iv$leU*99ҩC%SG[Ov\.GkןC8ZovB*3ӿo=ý&&$;6U  ) M5R3yTW>6R 9꾥KIw-57֢bdFNсϡ]mKLuفӭx͎Ѷ$$rI h JZV2ZȲ'?ҏΤ)s %ΛYdok0\o<4"HQycMm7X֡s\,~f3qF*5śLDJ͐rLfZ.ut8I!$1Eq *%f F2 )$͵:Zخi$fqR#OtvͶaJHRYau,%`$X_lxmMſF`bIh\z,{pOm ‰`ld4yRIRh\Guk ,8mkXH9H<JaP0Z=H&}i,涛RbX7]nir 6!!q}kχկXHr@\(oJ7w8?rvF}OaWV one%p 1tUl\3h\iI}QzmNWm {rU[ x_ڤY]ixppNqKQi3yhr0z!#MS3ku*z=֒8=LrU :Vԑ6"wo#ȏp}ǐH-$\dpU[^kbI熜`v=+(fﶺI,AGlYU[V*O'=W]5n\M|5 ʟQ]uy4]&$kpbm#_9[ǨL\SF$\v]];KVejW|WВTZm>'CM%;ı| $y:Wu$lEidfVaA^+DӵkhIJ,ӌVlP-X;B€sǧ)rC.w’t -ZQ YzƧUnwG.W >8MlV65 kOgA,U~۞}$#[ȯbNqnk!^[_̈F0J?5&K[Kbu9Pjve4wH#8T~9Mn"\G8q?xcQѯ%Keۂs׾bٓts`N|tjMӧӵ;}7Q8Q+iDk}An1Zvzcm9gƙbŔ1 GP c^JIQٝV>a' tZ}桫]Dꖐ%;k9?|פ]As\3ætƣIuۼ_ogh >:ݵ[܄vsAWy@'R^eyҢ] =bPA-YK뷷U9X9 >B6ΰ,̤_B=63 {U ob r9GV l$kN*,[6]VFq3]$ʉ1%;Fv3\ӟOKq-lq].s*/ج0I#9Ry8T<6 r:t]*iveCǧ~]a{+c$Z}Y!RLׯR:fԯis{m֥ 85~k1F"iq!Azt/\EeWc5-ͼ_)9_Ԏܭ/,M 6W=+֧SFqJϨWVxaXG[zޟ&fn4Q;j^ټ-$Sk[<(P˓D]BD2:KPQ1P+P;QdA#c%-r)WoOC{Mg%Te8gt;S"2#"p@W8o\蛢xP\`t䁌74NN[<,,!['9ϖH3c׭ghI461\2᱂`㡮ZkYX즱. R?2cӍ76d`9Һ-r=J0N.#Tc`z0 (\"2がQ^ujm3E(4{Q,qȳp:zWiydOҺΞ^CwK.k$Vh$=W|GiwEut Gqk2ynZ@ydzF = | GO?Jtvhއ]F},D7e[Xj7۟s޶m[bPzӚQ9o+"[UfVen[\7$P&sgMrwm|ǽ^x݀`++9:!5:4M&陧 lg'ҞC FqzH1>6{Oq<F=ӫ}$n=C>#{͜i%EI`Ju V xc2(1?+=G^ Z=: XD҆[=3^ɯx~HE,8!^>.hUt-F+=ZJٷV"m:ɴ'H|rea}ihw188v[61]/#D tT_igk{y 1mY;~GZi?lǠVt=5.[c!@δThj6~! IbKd8={]ޢ nP T{WMm?i-_(pIǿ^}* jvqX.,i~դaM_so-4F?5dϿ|`|uhigy차q \W[t]KNI`G8!N?O^h^,׾b s20R2IQ߆:RWwGzIlH!kX&{) x .-ƇxiL.T~6W1[۳ fb!prG~[%u)7:#^h{Xnyn=޼Mͫ*4^ IW}oCf)']d-`WcyF ;Ǯyv =\}KSCL&Mk$\qe gQA?CCz2Ouđx'Yx[kADym6fGӖ e9};[,lV@A {f(e)-*0-=#G $o)l$d)afЗQ,-dHN^+al6\m?O7) H"L6BM"bJc`.&&wL# ^%OO [un~S8З {FX@P `C[aZ aG!pGNVN.gc)3uHqTƛVsxq=JX=:Imkdc޹z\ƶy4yA_^FlVɪ*ꖱ1J$G|d,ݤ`ث+Чvws c5X8Kufhiڅf8 ;5eeWe!UH_F?^qaMPs zpGA隃^&@dlSzZ*U-=kIpv#Sƙ>xw+]%;—eFO8F}0 2ÚJܦJ/崾Gcw+,:s7tV!sjt> H 1s^TVҋ8(7@#b3b׻VCFg”_۵^'EQmp3T^&HlUI=?WH~̗j6O#DSkSm^I$u bgg΀'?ZU[i,3(#g>}q 2nr n5y7ҬrTJ?TR4 `B)^B' `kv yBkm#q$5&i Uk@ǿQE)Y]=P ޺E%mp#s@==U cC ZcȑY1r-o%LR$$Ӿ12 W0UDfSG㞕a#71cǭV4-j̒B`F7)}UBOY B2OT鸻3OSҠiF$\q]-Bf"POrܧךhJ)| u, ˦e$d_ NPVQOXMkÓH-KϘpONTxKZ(f̸RJ;KY2G&@}kFGԵ8ʶ`T719eJM} "NU[yrNX;|љPz)uxrM57qӸ Sy3L)#T8Io,pe&OkBXhdVv`)?SZ-H*c@o\Vab7v /aX$4>{/)QZ Rn-Ŷd<1玟jvЗ{& 7-w?yu[K[ {[YO%3~u l3}4a0J mXɯnR6߯7&Նz;uk1{~uv2R걵3O+)UNcPDfq$*X ZM̸lJ3۳dJS[[;pu[l젝۪wgyj嵝bUV!䝘 Ȟi! ou;|ɫ:xnh obGmzPy!Xu䎟@)o hԗx҉TMi6Ǩ*_ilm}$ g氅 CGC[:V.$_8ҹN;$S!Y!v||]Ε@ӵ) ̉͏Z51m7fLFzV'UwA`L٥%s4MUda$ GI!_A!B˸?5rd+!떡ۈdtcU9Q5[+=`3q\)kQ=!ɞavFY;`z{W m- mfvO'<85eWj;_Kk?!D+d#I'W#D44%V_+{O|NZ^m滰qǴd=ѥh{Rg@!'gW3>Gs6Xu;mxޜIY7%aBF]DZiD[E8?#m 秡mCBRH_$u=M|VF`A*Ԯ;)ԱCxza-qЎ(5+I_%9"Cwo!K om# rsxMoЫ^$xy)c2o8ۚstEtU}̢Ls\rjlY@X qޑoL`ʻ:ڡ`[۟^ %\HqǷNۯjWwiMGk܃vdsӵ1e@&{yG(LI-0;[> 6"p0$o^& 0kBkYi0evP JaXz ZvV}kK၍9ZNkTon0^#kI1L[Eim${kd\C]4|?w*[/*L;qr+VԦj@3.) V/nH ?-&[N{f?/lf 'Nn,ʬS=f`sNGNrǿZ{fZb9=Wv[9,2 IzO-1*C0"+x !@3E/,"f)#[Drbq<򦻆``erL V89P d\=H<*0Nhuxqpf22[:Ӯ#K2_˚d1"vHFvAϡnڼ)osݼklN6e̗:ۛnT(cVº|Ӌ CPޒ1O՝ŲD*0c$0z'R"qg9NId.wR"zgɯ="F8!ϭ}Z O_=;@ O'e$K Id{כjSV:EA.=XS" vۻ9j]1T֤cHDZY%czqI[C 歵aKU->;*H|RM|¨.EU鸍m½OG(|]\4qGotMT4SJ y,\~μbhT-$H<&qLH% N˥E]%W+Ea2Ep9_JSQ vR.|I[i%EܮBq?#[X_1 5ia0\"+or9[s^Z^i- f>s=u,K+$pņ#s+^;4QDY%_a~5حE,˱$.hl_Iu[Q7,nN=k%ƕ%QFU)tM/˒0A_6zc";Fq2"k4>DA@NᏥ-?oHo13FP&um;xn7 Â;6 1 2:Z33?Y-pE5rQٖȯ K{Me l$?,냜WeֆdڷdLcyLoio2I9klΣ{#G׷7fcF6*S[]CQom-D;~ֹc],asFs.iro@̬rG= ]ySo#g|w~.-\HqAOA2%6rCq{[uIH߭vզТK3<Wռ{:OkbM "}ٕw:tj[dRAͻZܰ0^N?ZѮLC 0!@A'EolkcNlƾ&tN6*j\^ ]7^Q18=:W=ULS];R^4$Rqڒ`Im$wi n0B^?ZsM6 ,3[ھJ) {J{itfQ%vs޽Fi<<2Ȧ7|q :LpM߯Oֱ74Ķk4>ͮbhcv =|(M8lό>M(!HӴvq]I~~K"4Pqf|qa& ͡v[; m|>aa I׋Pw0<7t"+Ek]?'zY$wElăiS5M,qu g?[{e%ǝpwݟ4r35}XS`X 8ҡmnxnhs| BhQ;oe`rH/n|ɖ636%j-1tu9{zU[c-Ef($:qV6k$\T#e'#׉Qڼ:5Z+(|V?ֲUSEf>)Z{VQG}n$&8nv+iw1=ź+*nFї}섴y<Î?S\ޫcKYQOUc//!GDlN'ӦҼI&x+«ԮeiA#8F@Tpdcڻ!b";Fɋ1@`O[X\*#9H9}ssf<΍匮G[SM̨d8r#D˛n{}S o*jSUeuyqw ƛ OڛƗ5V'fsgFDgenq0o{d†$=ᤒOl=*$9/_IB14MfKG+y!q%x̑:;^k񥌭|e# n9 n8>"zuGxNM6I4o%YI Ü?0H\\A!>gb=y=bJi$rQZ3+^VRx.x\u_u+OVX l-EYXʩ?j@F&Tn%F҈5»MCO9k6}w!cQ}=̑a1 `^1dl7I;pW8UŻ;\BehaFS \ߎ6pٙYP8 غS1ݞ7}1Vone48!9\~<}+|tSA#x&cV vpNC``y$W" RX ;&<::\:vDvJ2X#p¹{Z2F7\Y30 0O|@nn^K㛈!PnII/ITڼڜLH AsZZuϝYF1 d{7!uF:)ܰDz81 Mzes[{F1fcwFC( zӭVO~cHƟ)1bބqsҍ*XmIEi 2N}Πft7sߵgFi #MdiNgM] 3s[v9QO7xJpi}牾桵x2FA݃.etgydYvFF\n^3W-M6`2`#X B?sբ *L N}kTgc3iuuC7X9O Zn|C$k}~s=ji0yB#'ƻ[=rYX"{,:'z0U ۊnudv?+teDe%#":ɒd8 8=M]O<DŽ%Q.C$HF)#Ϛ^xAJ% zO sCٶ ||m+3dҌlS-^2$Fْ'mmիm,D/ 拀R#חϮT2 R:89Cc8RX(Sn޴.Z3 anx#HRg 2oQΩi,.9Vfe.'5bkFۚ1zgҒR)`nwuJ)l5F.f@yɤtbTۆ_ނg8*t2A+ Kmî{$:1,ʜE=:։8OKˤB迾i s< #rZ"H1}j]B BiI:k]-sav IhXl0 '䑌LFj m[aYeJMb4IjerH 9ЃY!ċ!ʓ<*ᤓiSgD$ξ@q#%ƍif+K H  cb1Hc.vkEd)K C<{]yRlrZ=V5 Y i2F _]3F"gy #^-wiVV<)R::|,#.#ь3Tz6~]xzF3TyW@?|GԞ+Ş%cy?Uai[[CrWf0=w擧,Pqߥi4ew čCpN9QZY@F$gu\Ȍ Duך+4p6=D`IqFn7nR>ڴVى#y,t$S$zG(i~SNsμ_e:m̋G 'gM2Mm40csEy;تQC;yNb9Ujx1(84Q\RRv9Uӆ6+rB>=T)ӱ&DQHa1}+FN6Esq4M}"]Ro=VuYY?htsD>T@@([DC@-ER?k##^wkDn!::ԑp'(q}jHVkksyc!3^6J&J\G?VةMioq$L4j7։ut<;*alpj(fgj^C+B <~,U~ԥ},m,%;>PE*1SE/8pO&&ͼ%g$EM)9=EQY^cs]g5d[KDnj(JP*Q~PJD)vĂD۸l^6B`vs{sijR / Nh&G$W7ɹC3uۈ.%x+34QT 6 Ѵ'ڴ4ZkxkQ)e`=TN4*zj=r!v1n| >&?ENb)QzcN-WNW$+`1.ǘUnQ] -[Y-hn_o`Hgoy~Apv4QRwgfotoxx-18.01.1/images/undo_redo.png0000644000175000017500000000743213222767271015621 0ustar micomicoPNG  IHDR00WzTXtRaw profile type exifxڝVY( )h _w%&l d-\}A,zE^0iE-q_w=.Ou}M>i|=xsC֤,H j?װ˧3^˚!Q}U>jr`O]Eߘ@H5:;y:躾Ru"u'8OOa}ZY}!guuKhWGaCyaa%g: Mvc>zۻ6<W죙ULz;m$*\P M7D3yh°;#.PHm1˘mˍrN?`emC$`yڣU{ Ӏ c=7(ںJZ+UKn$yimtgVda[B Z56Supr8?LDDe21$l8ST5xH#c +zjB@j[j;yw<͎~@XJhCDYw̌g<^VEi鰮۫  hu 8t;ͰTW$a ج %Z5[]hCXWJMmHÒq!Ih s³ ྇ީ1b[LyT.: 努71*UxJXzpJ yʺ@)-0tW`w:X<)XkUHvEBz%BSf}A-6щ}U)sBIT|d DIDATh[as39]Iۡ4) WAEmsRXJ[ԛ)yji"5yЦnCpB0`cw~?3s>}8iRO33vava_/з{5(b!@wo%8~ss*U]H!{?g&}R ($t&59㴝bf~%$yT60]},WƒM'RltCTzO-{ X-#.,/mwCM^ܡש5^zP%6اsDka̫Z?"D@1uX އ1i-j~W^U\|S}|$اL48~W;c߁1‹Ԝk/z`_|LS*>W5^_רuA8<|տ2syv!f3RNKIGRXͥ7xg`{e QXŸJ '>p'_JڸvM:#;$P% 2bt]"}?5v9$Y0aW*ǞVl|1:E`@0 PJJT2e8/Ȫ꡽K|#@)zi %8-0HLd1!Zv }uǼԡG[=ѷ dԛ~ݹaQ U䓨x$ւ ySgv򬉗 mGJq6vfM=o#a'>4T .ޅ?imZ,R3oF9`%áȝU88tBD@YbtSԭ/w e)/x,t.mMp.! 2cLkOEyѫ6B}:E-C9}3VsGôgPȯwxͰlXEe%Z0m1~^bP^z_ 9Ћ >2-X׸\bXjalfBT%wgI++l?c"ccW 2JcHG`p1[PndM39wy|@Oj:huZb!ƴ$!ݰː!i.a14 sCl"` g'3%$s ;؋Cf=XϐFnvǍ\4$rI0=1#Gy~#4| W9J\r\8mYIӱix.ŀxy@>ӳ84x¸N6w {} p2rYҙ);O̐ ~JXfG8,f.S:#ɸz~eߘ YH91 m=^,sE@΋K6ӟtIdВyrTM#v4QNdRY)bHy2BELj?KKXuIď|Y.>n%f&MA5ش\~8N2no\~Qcj#! j/cDSUe̜6<'x՟.Rw*#_3w`~bj$s/ҶMk],ķ#R>љrD>D;8F>]UT_E 4LLfٷzY;"_{ISk:?`F sדڼUBM}Eɘя[dx4y|#4('MV(%.o63Or6zu^ WzO]*7Ba>0Kſ%Aɗ% e}ZgIE-Sݳ6$k\~N 31o|BȫsP)O)98ph֬eY;5h;g{LV7HQn4s;)'5Eŭ~`F9Myp2 t;r S[g¤WW=ravavhIENDB`fotoxx-18.01.1/images/overlayBR.png0000644000175000017500000000160313222767271015542 0ustar micomicoPNG  IHDR@2cJCIDATh 0Uځ&"r{]:vhmP @F 40BX5 @8hq `@ k#q\!,a @qC -[0ǚXӞj'4;X|Apb2XԽlzTXtRaw profile type APP1xePI y94KjPI#y>?:kY3 BYa "HnJC(޿cd$rZ1ɥȒiW]ņ. u=}XPD3+rWǦ*埈~!qS],%KR L5 0Q>u:BiTXtXML:com.adobe.xmp 50 64 ?&}IENDB`fotoxx-18.01.1/images/zoom-.png0000644000175000017500000000310713222767271014677 0ustar micomicoPNG  IHDR szzIDATx{PTe}ݻ +GRS@LSfuL1,|#)Mkr4%DrԜALFCA1Q^罻{;Ҳ0*$3GwιAl)3C2eb:,(g$i2NF$* G@oPPܡ4X}>)|ЙCueG:؜?ja.ҾTc娀ЯG8- #hdTwQҰ mjcKOqό K#^##/8K XFT;H7BhCҬor6hwO &xks? "#ANCSPKCKfF9X#c{L5het\U:n(L Cs%6TIr u[ $a|?GmMFu±oAojCUіxcUG$cNs漮aw;U[g3P uUۃb1qzK:BAklSC5!SBavQ J&mg!lNq>}kvok$F>+s1!@wz)~JO$Ia65>ze83!VO+בs+P~e޵ԌY,G@ۦm*s(A4i6i4J3 C`OR%B9VBa5b,Z*| b/tD]]$Z<<<___Fq,:].+ IENDB`fotoxx-18.01.1/images/fotoxx-views.jpg0000644000175000017500000032400313222767271016315 0ustar micomicoJFIFHHExifMM*bj(1ri%NHHgnome-screenshot0230ґg01002015:06:05 13:58:55Fotoxx:write_text|write_text|write_text| Fotoxx:tonemap|resize| Fotoxx:trim_rotate|resize|trim_rotate|NE:http://ns.adobe.com/xap/1.0/ 949 1434 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((;Q" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?OAK"7֟%IEvA`@CyF6qXFW!AH<{+YF!8]. '$6eP|îy,t#_=)B۶m;Npk'U{`Av69qU}BQ2c"gvl7ch G8tZs?SYԚUfR_]3 }J+ci 9,i=:%J '*Ӟ=^k{ra=['>+Y$E;vG';P+b4 ~L1~sk\=odLmzr*mi>B@A 2p2Fr( $j2ɡR2@BêȬXyd`Ui\ \ O>q-ޡ7Lm\3OlQq#=pAk/GW_%b8 9*;vUUD;nnw d{QqX R!PpNzSJgɻK˘\J>f}:M={7IxL ;W'y$g8U$pyX2ޝLL u#nܓ֤ѭ/u4/ %#I8'= )HrTOI^PO>4,eR2@<_-ZGYW`nG!rj)`dyIV5(*8-[]@"wfG,$n[@"K`0k3gqV7XB+cv c@ڹ# g`ܬ"[-̀a sTq%婊PICF@= Im$ғoUn#p+]<)MMB1qyg>jļO*x* (n ( {ch7sFMld_u hiEöE6En7Qpp~fp~naciHPяƙ~&$xm[ɳoVgZ KH:REPӥJEEME">x=iQEQEQEQEQEQEQEQEQERъJuPEPEP%D-Yeػ҄Ob@5^hW^oiu{-8N;UG<*=OIdT{ۑoeEOc.xR\4s\\m,<vg\"QT]H@p8>4BH@]c)1VoKi~u h:JR{m11ʒK/IU9=+F?5v0_eycP1G"/e! 60Hj{XdI+H'Ӛo %Ԓ/ٜ_ZQdIWnJy-NztP>{`FiU<7z E'PYy~Y~R,iE(gEv3G(sᱰKyxa f#'Vtڞ N1EixF}kJjʲ1ls)Ź6iH#'D9i# 0Ed-Gqw$(0nH/Ӷkݒ8$rF#gOrZ 3GO-2O^(bڴֹ9e{E")8qY7Cmq+=lؑUn<5=po'ٕ!>bl3Z:}֡uq%As3``u9b~FWSKgIXvv1{XE޽gzڶSpG\`9R srRܨaNmEI=ޛo=hPbl2(qPIMEyRFZ zۯΟ4 vgeBmNx^Ƽ֖D=l@XӯQӤ7W nDC=LdJo;]rTfڭ"=GӤƐKtByV++m 瞧Y{>;h$7B3[0=$pjK)t۩o2ޅ2*3ƚ2IpdK9~vۀk4M[,媄m~G(sn#77R$yh_u' ͋Hr3ڹ| ,E?gi0;p~~ vYFs}qG(sv}q5 h]jKK[[X FA[*MzO0iof{GO>ʱ S}99|{1%ҹxYAUA#Ew:Bd̮cb8#H ڝY<-]Ckr["+H8T`&Qsݴw/$2 Њm,WlqbOؾٻ#4=*MF e٢irTס0?.k7YH1XnmG<'+i6숀Ŗ݈oLѷMR9GŸ(_ٱY?JOؿ笿hl>l4Ggf=d(͋zVFG/sٱY?J_ؿ笟_OK9MzP4؀@=8Fȡ6?'Wj6oΎ_ E笟ٱg"WWoΏ,~tr2(f=d(͋zUa9@d,(͋|.ggwĖ֝=E;b8hi-QkgE=e(lC;dqNM7}'ho1pOji-Y.'$nddgE͐T :!\~'0HXF1Z]-n [F:/'ZpG<-2 xV\A,YVH$I+NrPKV Î%QSusc}4:ռ]wju.f6Mi /Vݵzz\ɚ}^_Y?J?bd ]>C^xvp],%B}c95c?J?~xAhVa#7J4=NRadl޸X Kدg>^kh76/'Gl_OҴUhH_R}tkźQ1eǨdg.Gl_Oҗ6/'Zo8A$RV>*S(f=d(͋zU~tl>9ٱY?J?b~a} b~ّmoڴ6o΂6GCގ_ `V}dp>LDR/AJh((Q0zTPF;QEQEQEQEQEQEQEP(Q@Q@Q@Q@5·$(ZQ_8xK5gku9E G־n<%y. kfC۝ۍ+ZWW.4ścҸqr̾[QRT15m[_үJ6ljtB~֞vh`}Aqi0ɠX㟭3-S`}pw[?R!&2x(]۞CiTь2]t#㠠24W`}'} qf3߽;4r»@c'yY3qV&Լ=OZrN1drKqYW,Ѭʑи/#ouA$T`p=g<Μ9lԈ|߉2xnIX~ҦzZ("9B$t8wʃaћ=p[Jsɨry8r}Jdor~zT(#u0K1w΂rTvg܀+0|&~ߓVߡ#Q>qG*Ϲ]=@Ʀ wi̤Qʻ3G`3 #ŷj(LG*ϹǶM& |oR玸vg 7g(BQʻ3 42voOdƠ0GPQ¬e5 Wb*XZq{:g[yM޼}'&O_-{qgF-aFx\`;˵Z_xR+&B3"E${ׯSB4nӼ]Tm{׷XKhe+#Y]nM7hf3#KŭAɭ~#HD$y"@9g4gk6Zwt->Ytm}vUFS'{Ɣ9=}z<$m/ 2ALbKs>Onx^Q=^Jօgeϡ^u ]q?=χq;M4Ñ!d-ՓigbVƈДSO9g- ź*hp87t}xi?j,m!˔y]CD_7eޞS1" ֗k,*nJ:ixGFToGh'Ӟ lw k"Q:a%m.>XmgЭMȤ|PךIc7:pYNd]>VnQ{c-幹][3ؾH+rzjVΟᴖg7Tܭ6 ƿ08Z%mvo/kx?/V]+ÖȰ:ԆQ1@bPETTPRSQ0z@ E.(%RPQK1@ EQ\Q(PEPEPEPEP' xnkf1cu V_|v#l̑-/)tq;H*ߋ"UfP<݅$ys-[ZZMgNX_.JT0v~kD"ߩh#b{< UmhuSž'> tۻQjD.Q2VoQ-&֠"Y$:ҳk]cĐ60A8J3Fr&Ʃw9:^i9⩑W4#`TIi&T(SI$nyz})0}3JPH8|dwb;'8"ch $,6s g^*pbq V@G ҕxlA,zcځ 1N}H3Oz0!aB`OLq*3ׁIF(.;HTE*I\ .OϦY5Dgd!VZo@c6)7 z_ھ -ԓ3fl k%ۼ#Cg׊,<$A"d}\m>-@`yK͞w_S5kKin?/}It>4W:f߽vg׊;}y)Kb>sJH)&:]1={N 饱a'j6\*A4?cHx^y.'8Tm1z )iO@ }iP%x($$g<.cPݩ i`)#F" ҔF)~h+@ Rsb4lqQʃ'v 79cF0 fE(cF)X֥'BI֍; L{ԅ)-ri#酻OAoHwZv('$Pv88I5_xb/ҵn+{)Pt!f{@f{@f{@fw&jD|O5_uhB`n4&}ϽG&}ϽG&}ϽG T{@f}L3Q5H)j0s@ EPEPEQ@}7< =zywǺ]',K(_ i378brr7p=+4QjZʷrnb?ҔSԵW:+MO#xiSͣ'[ OC/"-C&$O{s]տtM Q[Ȓ*r("UU3՘.wyUO]JM{PQu֓JE`T1dJXasHXd;Pxy| si֔LsS;bqQҀ`ryJ niE &1ٌp S1Ҁ@'SLMy8Q3;#fњ?y#5i K15dNFEEd,kM¦sn|唟6L=;;ȌG_baY生ɭO G::ެ l-RLcCy.RVp-;/-6V׆c]h"sy!zE,SWëZ;F0m̀P~'ZY6y$''5̦O6 7-{4dqt`ˏ2ԣ3'{;vqM|Y7UV ֭\F$AxQv.Vfț4{|w2>3)İ41m>O0}Ss)?x&pxHNEbpJVK$w1"]#1^`p.GM Bk蔸kѣpWL㶶IB sv.矨6zy]zuKpcby@aS}<' ݳx݌g8sGץ>p9<i1?ky|5LO Ca 4vjFn|r=@^aQw8K8?en9ŴkMZs[]yv2=HozJfPݠh#I9 4O5f_[AUaIV$eBTdg=~]u3KG65 ^$B@Xc ^iWxiEL{p~}~]ꋹ옯{Ns4.1ͼעvao5?U[ad Qv. i٧ys>뙮Y~ajH8)eiDH9}~]ꋹy٩'owt5|$Nlnټn=)kxt}~]ꋹ4s*ǼٚRyסv#&F" w{: |t}~]Qg`A?5:_7K}Qw<$GYF5hoΏG`<ђ~qӧi55ioΏG>0h1\߳J܅ק}:>?_`y-f$~z_-jR9="9ԊԎS~|OV?a=닝}iw֥}M.hab/7GwSFhab/7GwSFhab/7GwSFhab/7GwSFhab/7GwSFhab/;O[ ӷSFha`/)ϭ.G3 ܪ J(߀|,9>KDY)ORmpjI$Tbۦ5?Y;HnedD KqT쯭;\<2 mo 5{i,wJz'Vz=M;b$ܠE#O`W3u0Ź L Ҵ<1KOK:|4p@ dA˕=M548UOpdiV"hfzgzzIkVXm0FB7mZ)#b${h.cO\,J`I不<6$sӓThqH6Z($;xʾGB9ucoKO N[Ibt1y(R1uG, 1ۜ9m:K\\G)`]prLWQ4%[ܑ_zioKOUb9bo@ppcG_ƣKN Ge\@:<+#ӧ?U̞ o%Ns6ornˌ*G[ cO:dޒvg k  = |)9[EXE掜\l3,Zs#L` Iv>~G?⨰]hŦV$?dfҋE,LPFc);u?doKOU $o G#2h 2KO0ĈPmBB5uF rcO3q[LeKuC*]xV)I(HG _ f@N1~~M;WQǩZ5,S"9RHKg>Ĵ%2ƒI3\OerO pGcjfƐCa^) ùZuSuGzqdKYZ[ aA'1/;mi~Al"+qs"tFlgB=őQd!(|ɕ tAm$h7OZwm Ix@ ,#Unt,lOCV ᇽue)Qk⟕ VD\E"G#1π$}; k⟕f)Q`L([f1Tf1T([;H.m@c~2 ۦmZF>f&$\% ֬7@Bo'X]UcB~vge<:5(Ceyb87z`fCm#Iqϔty5H< $sJ&|u0ӷ"<*][t,aOU3<9uk:b Y"ъ-NY ʩjb[?f\?=6I[۽F.W]`˖]%dzǭk|P)=Q/9#h#]j^/  mǃ]y00yڏCu5G㯭v߆*Z+a p9mcx$TO^_᤾4_ ~l+oaGC,j Bjj.+i(9֍kIk⟕W)swc?*>oo[nȩ r$eW]ϵ7܏7x2̻9 D{x7?uiMfWQQE s; B $®hL$еYedvfDhIg\lM#Ѵr-hx?N$?-!\M$28@O#<&6]m;2I Q" ^ 6; \B҈!jry}qw 9 %,}+SkugV?!lտaj{ص`C-˃or ,t'aêjErnL6(Va P~9*u5w@$J|иyuKnl#-+|<5m]Tc1 ̺|0. !*\ޑ=zM*%̏p"Pc$rS&V(([C}Dm2^-Oo ѺiFW++ޛaA'ٽErbf*xԏcx#şX!aƽC4RA"wY21r~ Qi">n;+mC(|(K2 MuwsN?5\xO$j |(n'vpzqkJ׊v/ɫ&+GĚ|m-9 q=V22iFFsNB9 \\qcwos0GgLqSϹ0jUweƜ:!veT^}kob'!l3?hxvR.R4q.#7ɯ( aA-ϘVrkx:aKsдveYc$eF d?tW5j:vZ Y,cʯd|x\-$-2aW8-OSH넆۵O\sx#7]onc#qYD3F5jR Ǧ@ܰ#U4[F`ij9i8ih>/؆ǵ䜜 Ӗi>zm{Fn3v$l'qݛA%NдV{QHkFiggvra-=73o'<%̓[ .d|4 ,Gn^[>i#`[xf q^u0* DV|yINi( c+%*,qhZt~TyiK>o>_@e2#88/ fIJGpRGtԴ64s6[;[ BλQLi6"b>i^5-I58H.TvUAc8^hȆ6Rfl$ԣ|r~RXh7x;R/.`WqN5 }'5B3)]rG)g0@R0NsoI-t+($ńO3}k?TvR6(cFFڒH>$*4|/ҘnlAqa{W3s^!XoB?{ݓYZx_PS(Lyei*rϰ1ǵѹ ӼZ ֚5WrB$`*8nt::ܮ@ GE)0dNNO8k"*wdpc>%^~q}D(v#5_KgR"D'?5ciVq#D3ڬYěwm/=~Gsi"#besH|}+kJNYIT}OᷧFó[&\=*Q-J;[8>G<Z]kE(I >\`RYz_ ^~>CH<$Ãx?ا^OTw_Cɕ^s>ѵ=?]Px^C$7m'ңSO]_ \yJ*;ghQYT [ o,]ExKIeAԵ@#h0@)tz7wXyP%mesdg,E;IDo$ !$2sM: Kg1˰8lz2ڭJ#T`N#=b iZs]k對#@ TI>qNYxI ):GѴy,\cY;P־Df@e~3S|gh<⹽mmj7Rk5Xđltn ޗs.AE= 1`sjx{QmlάFUݷ)>TJZ{pb\|:i-`PI j@ \T^ u+rG$+Hcu=J "#i~szkI$(`N=jo.?<74MF;)Z&vV`gB*gһIh\yqtR#;q"sJ(Ʈym~>z%s?:/#Yoyhf=7Pgc狖>n|;P7f<).?_.?\7.H=ih4>Q\q=Y7 xU*}*8Q@ (3Ht%7W0;v@2IX\qx5ܹ;Q+t#8B=G֭_ x-ni6o<<zK@&H>e/R;+uqkuSGi! M=G>\j 095CG-? ivɪ^Co\L@݀y׃Bko'x}+ob7؝חo u rXDZ^I/yRmVN h]@ ᘞz]ưψ8b3vF>׵rڦ߇~.I*H)'N5Ԭ4K5`;l⬁Q컩]\5đܠ )S<=c~:!^iDnnǓ lʌey?4Y[I$#^9b2yk/O)Fk[g7%Y37˵V&;Z[귒]:p>8+GE̒ lk n=Mt^[Bu]: s4Hlm[STel,76љ.7OAHNoby1dBG_q:VdE>YVXX86cӍWҥu-{qZ8B+$gjYVz}ťvs4Az%En*,iPFx<9z (dʒ.Kz}wV vR)~?OjogMRљ\]$ #dnWjuhʻr8@y`ّU:?k֚w<%'ٯ" "{9 :~ \N2XI,ogҮ$11#ֺ_j+K!X ѬNvy;|eSjJ]>i6݈0$(J\z,U:.բ6mxXt eI6vMIj[0d\;[x7ZG'ӳ0vW?jH[ >gB!ORy03W4 Og.CmϖP 8Cc8W+Q'=b#8?'ҿ̓WC>#EֶɀGl׈G㾣M&Gu:Jٺ0/7o=M 8dYXJ"&`lO۟ҟaF$HາKi%d]cm\r?Ҙc?,Z?h=^wbzu7LuxI}\[T3 xoV<8// wƣoIYd&"0ǧӭy_&L&8-@p%~Rk<[sB&왽\NgT:U,j{ۯuC>Č aWw6nZuK;X Уֺ6 [zROKjMOÚŞyfʖVFDPUt[z6Z}r(_);u$\NZ߳5Z:<4Y2}R{b>5MJ`ʡr6ro#wd_B(%|B L#U||HXӮ,gU88>5}cs?BMfúխͳG$K(˧,F۷m1L`T6^l!{kU)坦VQ2n9U1IUz?O -?SY-߲5, i#r9=F=kq>?wKH,lovp@EO=9sz|F %w1?uF}\󋄬c%%sq[#kڄLp0X W@U S{פu !V&Iqj]\UGR: /|1u6iEa }6@=+$[B0֔d1o-ߙ)Xx&LF34wPHTm;`ԑ?YmMﱵɃi>bC'{ p񆷀| ?XaOe{>LmII$2$9bNrA*{Z4-*|cU>0hcx\PXe)g4ɴ;C3 0=+n2<ךkGYYgΫňJ$)Dr z9-QFz#[ޣSj՘I9ێr%.ilQlnmedp $ި82nzU]cwm [BN?:/\RI4b0WLڏzgjKcʮp9 T jx/K5 'rpc{-?YzGV{{ՄF psL ,,{`͌vWx;ΊOB:,BI2LlKHN>[zVYkŤ[#>I `̘5'ƮW$p$|dvkSAnlt @47%s\ WϋBiVg|zDV34&݂U}YuadO&ntu [-,i%>R6"Mɵ$yzkG0"=͏#aܪ,=~GhRV}Q9^ìCs2D\ԍp ,.n5/kQi m.\pIlc9tkm)xG9sLZ'Ŀ(]Bo2çnb]Y;W~pֲ}St񍬡bd1I ר.a=`<7w-t}([\ܤRlOoQ,*䏥c;?FuXUzW'fs4r k}*^>az b(Q@Q@W"3G!==+iUeP{ZozAs ¹nn$ms*̹?@i`¼},G4M7ClG9}>hlI3/P|_\@H㚚 =]i<+. jas敎oin}lE$0Z6?'^Ox]>lKWppxڼsQ{$u>cw'#[N5+wen yk< H'C^eJm)XݟV{q-$*O hܤi\ZY2Zz_ NCq}&냄8])/cy~; ǘhJl##[8@L-cRixd#8<n/zOk J^H8 X>!G:My e#QH/6+y.oD$+ [ZEgB(zw,WNV_ [g^yCX#TLvF>r>af XKk%>k0xgn~f20ϦjZ%嗃"}R1Wm}-| 1>vƲqz3a{>ykR )#$ _I||+I<|_k 1b$穯yV\h$ǵxt+RvH׶2Ka?,lU;~Q^c&a x:@H8KO }f!,l@N7C@>i>x8Wp|+)wqqƗ?yqa׃][ \Zr "y "&X,#E}ǔDN'ېg I\=ooj򛴊K9LNw3\ ?Zg-'XOhVA|;d냏©xinaPZ#ϖU?qS1fI $e y?T,(+n⡶;Oq@tZ42+QnwlUFvox^2 Fb$[SkH[+(cr!XtGDh.PjXvvZvirH]|戏>j_WgL_C.YvV̨$2L%dòW7u< ɲZ2L6J?ŧ[k:,YoFY:aWڸxΛ:oI! I,+2 gc: cLYlA}ѮA,?tF>4+&ObdO:c=OvXj k)-,IbrrΥX`:vĀL<,m#XXW={Vfyi8 qOR+eéktBAlו'GbM$/1Hp_½f| #LJOIs$#pKmmJTՓ %垡9UUR]85fU4:_R;'5 z-Rx[+YvF9?½SV][.hÌ~5qFK}N)>Y;;Ki`(AuvuRʼn},7o.S`Ev#CAM;&=<<z~<Լ;[ʖ;;utp(;$_Sjdp%ƧnӢd]*+ؼJ/s[6HQ+= ӚUٌ`e"( U8}oKkzmq[4'Ry$q]03E:Sṑ&K{0Gp:y LCo+RҜ(O1n5/|<@C٫C9?ot¥*QzO:4H0hH뵛5~KM*,K|ԩiV_ ~Ʃa*q$UM?ᄖd&)]zǰ"l|I^1ZV3ϖZ67p<WED?: M;(?K&8QmQ*(\15l|54QA*) 1-03t[ԭaOpz0W:XW͑/RKcg8j麆ZIcalDbiwUXaʕ*QzJiN҅}ҹxrz#]M8?pu#J3nҹvci:56"P檃گ=sz2Z"+9f;tS۟ƻԲƿ.z.=*/ ^qL zHvqgi[s}xôׯ/nu{O ѣ8g^Ă8mmn5"-.'B8Xr=CZ.Ep6(5*7+/GEtkB 7 K2/pch^L43zw  e1xR;[+E- z`q3X ?.#h SGG+=Rݼ:U(tt_1rJkl5oxFh>nCtkzPica"0qA-Я1 3[Di>tqƸ(Js6cI)`nfo=+Rɵ{׿װiUY!2N̯zZ%ѕ2 [W~qڬ"20\kX=g/,tDbe9Pgxn!Yh2WIQ;O8w zw6n <0vQ֧HU*+X9#hd˯zxm|+}Mxn[j>R5ϝn GG=?Q$_SYA8,yzzmbyƗCk{Շ ~=?} [NsI3(Zt]Y,t*ߘUyb-_IM=O/hknfqI[fw'{/W:uH-OJQSRp8zrgvZ4$Ԭb, Fg3oj]X愒rxǽ_sl%LZ. V=t@;_=/ .sZYX٤8 h> 1/Vmj_k%`)]`q1J3dScṼG~,m=:&KVt2xcÐ6$+:\e*!3xJBx r;uNE\` ?rcU{jzOɬEw-un01^MxC-0l=sߊwm7+)cFn9VM4Ao(gq\&W6nMMPVn 1' *^yyKKwNz_6f?:,lCLHrִ䦮g(WOj_i mt+q<&9';oVϯ5v?56U9}T:XZ4' O:U`] Q^i&;Xhx'KQgYpNGoQقa$fH\g qտNU9nC뚇4f\QjADVkž 8]6v55|=&W p,:yԑ[Q0 I^< 3\-(\@f{rkk}b <:rȇ眊W7zEC&+zxG[h}"`R0Qg7SŠiRα|lnOU|OڽեjoavT2x\Wiy4iD(ȋEhճEQ[rY#,)š_AcS匌V^[d#( w9=d \4y_<{s&i4:`UXҹ>#=:͕d'8*Gz2XVbi!. `=8k#Ǐk3éxo,9Y#'pѸZ.]۶L黜hjE&l@b ֨I3a&_553h_웑$E^q-q h9>s>Zggа޷N;7ֺ _ǩ8 w ȷyIW^fZGV4L8qϊMIG]{ɣYb[C '9yk-5bn]qɭYZ#;[y-muX]Zglk̀qRߏ&LII0MCWy4[WPd@yeUڛ;V]A< @ʏ/FC9)sd6SPNXgZin$RƬsʜDIDVfFCwwQV՞Q%p,PGy.m kҾEk:a?\2Ιg)rbOGn/37_dlf`9spԷ`dhxkR3b02C`}MaM@~u>ki,ID3hZ:A a\^I:nYtNWV; = #:֙_iz!,nÎ8FOH{v[]8,(1Tau[\E'W%İ=kQWJ4Q5>~VoH 9|a +|w# W>?wkM].ޕp3b>az J0(Q@Q@4?VzHt`ͷ Jq>#i/}vݫN3xϿ5=((m7[5i 7s^`\^Ycn;b Zn?lcEs_~NGZUKkx#n'EiGk6IK htW`pM?DbvU*s#/ݵ}_- vH>-Tx=W_ִ ̫I{[ZMqsZ*&򼍣'Gx^[)$WX`@z}kKtYamNMKs>{Vi j6[*Dv@b+[I񶗡G#1w=tk:Žw6NoxK\N2U]>I -cT= zz[iitjKrXĕ3}*\ CNi_+%\8->+ (N~K%ݤr<0(CD.: =k_Pխ.YPr |H7U #ң6?;q _\L5 k]OQԥˉr<2I+Vzý3Ke HwX`玵&?YC)F|h.~iv u>򮖗Lj^VEWd#<;)RĐ}k4%Mu2 yz jwzY};)fuQ'_Msqm$7h nTP EHɨ*ME~--k?g@,aӚ.}Mg3LGEn|n-2/9`|tq:Ff545ٝK@U^Uc& +s~ [/]E'\}8/%؛| *?)a;V k%7}9ۊtO6-%*%mpO3nrYlzTm,o5x;{wOt4h/~P(MrZ#q±owy&#?PMc/kd,6hFtmHWXf` H/ KR)m<=Mb>bgK%Q+Y $s!POj^CֈbX.A'FCkT:fb\a5r;;bi̖y46h1AS}$ע($Y8仃δGcЗP&ۡ$%D6zvKR9hG'G+MBDW:܈`$LZa=>썝 yixVGH(-r8a3i I'81ڶ+ɴu+iPc$rN}k//5auZH0vo|FxnP7P6z<=}Z>>,8 .BcvWdȖ͆mfBEG(;Wl-:tZvK39<7n1Nuq%D\lo;=>^x&( $ b e-[g:E8$3Gq BUA=:Ujɫ-I $JJB!=ֹD'ye1GtS.1߆iu__K ;Y-+GZ&+ㆀΜՏjRUT+\zTi݋ `19 97w.XWmewUI?9E9 6?u88՛zE7kSxt6[ J1.V RܐyO+s]x/V(Y 6nҲI-uo 1y*%iVҟkcsV2'YT`G$y5b{_VՏF5-]M X h&hWkp [&YԪ3+} onnfݾu$zVf C׷lrSo'n3T.kͨk~0mosI<ٷh玧ŤV-t`̣kO$XSG;P;N:?CڼC@<Z{k;bRT}< 0bXǧXwI%iʸ1J鿑pG|\ΕKq kY`GZ:,F9lo1:%Flx|C6Ѵ( v=NAXl[Ime[FbgvAk l5,o$;8/Ooi "+б (X;q?:dKҥ I'͎WTƷ3]hx2x$* o渝Dꚋ{gGVh~]),, iDUl O_;?Ss9{;cmbV8>k)x7kyþ ocl?'W;/縏z}n1b?!j'O:,ܸ]uH=s|D%F;zqsvVujj]->`L<,hUFRT=)3Fƙ}6ye+簮äPn YӴ=ҍFKbMZ_Nd'e=# סk?e&IDU{NxW7x|=}pA3{EGmKY_Q,$'Rr_?>tUԼEψo'xNk%a9:Wtym&ǧZqOJfs"&O>zw.4SMg- p}A#Мd/ЫN$hYEOō c*|5z e]JKxdxY<үGqs].osa9QNJtsaz n\ŠQE6u]pkώ4}h$b$Y;mo9$3^Ƿ JŨhڤwvL5=6sXWV\^6!+sZ.Eq&ati-ɾi$Tpk2i) Wl(l6q'q>%K{M6wtVdrGx_Cm"9eebE3S9k 3bkćޫhՂ#Olf]q< %Qڽȵ >QYn"$q/!XJo#e" >`Z+ [U_1kK6tEcۖms =2kݞ4^dT [ܱkwSch2aH;,|!XO5L\=YM:_-lrˎHG~oVs0<`` Rjl9t]OĞ.ein1p $1Z6:y՞ xƞ۹Wͪ\갮~xź/+$kQy,,Z5U5LArw^YGj naG\Υ`s̭S͔Z񦡬 aG8Mo*"8gB'ڮ G^:V:5ѪDrɌgr炮I4]A+noA]uabF,O$~C$VG[je};?AxI=jL;_iw>`[Iwc0;.pd;wú#gcoڀ&=g|;0h\q=* y?G|+ r6q?ʮhho xvg 0=#=Iz"G+2?ӭx'nH }=1x$?$SI'u[q1{/cI,{vesdԒ~}kl)l[@vbOКG+,iVS6p68=}kback=K4&0r~H>I({XXvxX>"ZZhV~fAXEWi+$/c6.2\ǖN:砨/<=_iEԪat?rskں=oK3נgXt5 vs4g,u?ZZ8A7t}#BAsM+ s <`Yk:7r$k#>𞙤GbtE3HA|Z][7\]4,IGZJ+:2Gp6&Pv)AdX㢽} S͟o@'^@lB;?R(tb` }{W-e|.ے__k¿QH_UU3Re;ԏk^S<)o LXGLee7#8c4UIRd`#ɫܗ[-GprsJ{J70tWe7R2 cj&m/SDf09ZzݟnngHnFVpא* > ̗U%U$N3֗#9#b/3T{3*^$)xJ?1R4J@a?#ǵbxjF(f@&V/#Uާ'c{+ \\$g lofOb BһKcMOL" Gfv!xmf5I8=@>Pl:qgwb7FYڗ>Zb/t1&r[`F9E7Kvf>L$p{{Es?+iދNYpFy_P|[X|'[V|.p]W : g+p; IW=<1fy4o>֛1@}iwSL?4o>Q~_0b7/5(I7'ަZ]ĞIiۤᗕ ْ@}}*+ڭC$vQRN1] h-)&eޟb 2EzgY\نlC"1f؋+ĭe3֧ : {dۭT0+;ִ-,4]7ME[۲1Ȝp:n}+Vål1oH?oơn.T; "R7WV_#ؠcEhrIn8#漊å3ɖq13LV w~xJ'^WsewAYOZ qZZ֨jdn~;c֬!l?¶|CtbxG/imWsTV;[˸c|CС+v}+Kvx;u(FX ^ ukp=sc^(KTk"8H.dFrv_Vf]P_ߟMv5[U4,&?ć$1rSoޥ*Wsݯ0n#.<0{5x>⮂t15apsyV+Kytɭ<6>Ԍhlsr'SOp)Z?3\kG㊷`TSdQMO(ُfzgk76-Ӆ?53wJv;_<;Q1f _=ӟZW9VP|vuN:;C4LGm5Z\EuxSkoV{6um>Q["A8Vvooܼ3ul ?ʷ%U.b#g\H?ڬizlg̼7Q[3C瑓^Ekq6CġI%`@X\5jrA!B Rf5I5[KfŮ4YYFGa:vmkss(1v\"֫]X:ˬW1 g>K>̉R};ctyi5߇ggUi^8rT.8Si7swa˒>fb02.Ok~( lD.жx~]Ҹ[ߴ`cmHnI/T,;Tc%%$WxƃU77Z$?SMw-̐ .rv$X3K y9& Uω.xmIhWgrmսy>9b5!)etJa3?&32dŒC#95ͧ]IwcdV΃᛫v'=Oa\SKoĨ]S'UŦYbikl{%Dp?L E⫝r$IK+c U'WoCvPW.gfo!S%FOxMԤ:M#)=rq~5GooM$8r,nxWඛuE#۫21(# 1oFĥFylk=f8QfR2v~;=H'RmTTWM]NBӠ |3}}kݴ}J-Z"1J]=x9u|o4?6_\"_e=^"]*'USɍQqխxjxZ*+Yd R´HOz2Eѯ5ag"[s}pEw ՟YkVq?gw^?*uq˿ <+ķ7:އqB Q7 cZׂcw#59b| !yq֍_΄qOn|B=F`ǐir_;8}+ ֍w z =g]퟈t1+}?Mލ>/xO\կޑ*.?ʹ|'ql j]jc; $vj>/M`[zku.T<[w|C7{f ƷjOCMhsՍC &:ƍ杂_ < 3źE5- F>U;QpH1k s޼5~o"}iZA&"_ݜsΥOu-COZ~LW[Kg ,V6ؠ)A⹭sĺmῸy# ;X]nοƞ"c,w8gSyM9e9#=β4rP;1Gq^ X47I!gTUH9/X|> QpzxSMe`}GP/z*lHmdV v= >FZD@+u5bs-Y/MLmfI09$dI^ <n#!w SmcDEWN38ѯwAv>ҝ9JӓZwVLgE3ޢWm")g 6BwgWuk: g<[o]S4Ѵt`Jufsڗri-̊ɶH]H #*ޙO{l/eoE6Ԇw0j:Fnxb/K+3<k:1ɹl8$vSїnRڪu|%IJaHFr}k+p1"˜>ihnKkȼE!<#C>pI-J0=0Kby>OjP.&'<˺MX,kEaʱھ1Gu k$RD͟.FؠϟE?$4c}Jl@x3;6dOyt/dϗ%ՄU58g}//3/~_x_nѠ<>F|Ξ&@/1AWvmmeX:UYvL:%ص|mj7y)f j Y$K_DIh_N v+=}, uO~#][ &?qKk% $Wb8M{sDSeIvӭrEc4+GKI,so+g[h[F0}:&Fjq)U[kk8Vp$;P`f2(Ȧf1dS3FhdS3FhdS3FhdS3FhdS3FhdS3Kp5h~ |;j}IWp>'aU*+ŢIg2ąHVkGsY~xTb5pgMQ^m0`u6zl0I[~x8".S  m r%wӇUΗ@5uޒPЌut5H=dg{sμ갓nE-,˸v{>~MϨFfjS7O(s4Z>\eWAu+=WVwis<;U6c/ʹ50Ԝqs2[YU㞠UnrGew[̉9 'K߽RJ懞`s/Ø`چ+Uٟ?k2%=?kuͅ4G6*Gb$0j4h]d<@L ^_dVf OnGS^&gi&Q`۽xL'jn:^YG? `qzpXF.Os֠5#viy<삻ȥU =;W-4|osY+*Li!Hxk.?jxzh- }ʽ7Zdž-ɣh2`Te:#޴xRS%aөʞ3[CC(UܩwFy'WI|/dW{uT#l0ӭ|*KbXe;3}wiEf/S?hDCi"%`U*0;5}}*)dm-͝ z23Ӛw-j uybh'pL}ho@hS[1mWٱ\!iƞR.=I''ֻxt&RfpE=N1}y q<(c>x ׶?\9jizO%lneeox4 ;du$?¤|3XgOṴn7wlTaT r:r~P՛^*6?b>Pp~cO}Fi.lggdSZw*Dd vH*akl?!Х$sMzI#%Do8+X{)6~}ul1%Dro›NSk3@(((((4lה~ұG5S{o10 Pwwy7\Ü~>״K&@ Ǎ*F*ExLgv5zF~\z]\ tTNcg%y>*2ϐt Z[cI(ϚCy[Yzp?!}lt#SLҜer3}8̗@Qg:nm`[\6Htm2yD{=_goeok-V̍# @f6bUz!p?J\ώk,:IDi b[^65Xs0B0H_\. g<@Jt/_¥Zb"87^ygYSةW>pFr?}ºn#N£MFK=49nϮqPmJGVK6 );u%ugu$G! 6 $}jf+.b_D }xE QGgl %n)8sQ>͂ʖBKy>ʷ 4\ RG X}DF4X+X[$ϦwMA ?J|LZ[o5el vz( S"z7{_::-nwUz7{_::-nwUz7{_::-nwUz7{_::-nwUz7{_::-nwUz7{_::-nwUz7{_::-nwUzK xi_O yq8<¯зi}TWö3UyԹ";lnՙޓ|--v2O*7uAvrw>NIU >o Uu>wu]) x#VܟU'*E?O*:jែl;i03Ũg\xv9UyYR^E Z#!ǩ[_$⫪jvr YrU5/zXTtw/E+0) W!h@P8tχZme|ѿ#~&V2L T{PŠQ@)&;jAWQs,vHGEZQ ]?U*)O$c6+/x1ZDNJn֨k,g͛g[c"w׋FON{~5*:~n? [b#78s=񆛬j?mlaX.D8t.G#]׫j0edB_ƽ:qƫ4fh f=:V74g!%*dS_Ogq{g/# q]vͰ6ͨc{(d r% l"ZeI"EF,ɞ:p5ؑʶu%<6\4$Uu|>ku;qpʦ0O=jUx:ovIgOr:VC;A֢c&,ylOzZɮiѯe+khg*W4*H$+A<$:nRҹKbtqyÂ\rznQҏp0&ե 6o\l9'Oҳ~'kqO<7Ly-Wkk$ ُY!<ו1M\N9lu$Оm|;ܗr\FH0 sߵt^ ]H4#d}mϒmY!f QƑ@Kª\Μ2JJ\W4~wo›ZAEPEPEPEPFh1* d (+6jhT<PQ?|~u㹚8ظ,KzUWY61.3T:6\iwWu8?:a#h,W87ɱ,-JGrOR?ΏD)bo}Q7~XVO`  .#ډWn]ȶb4;[Tdl-@+\?Q??:?=>kI[K\CQ??:?4kkka~U5 ~clm?OΏH?:M.)a[kbV5h vgIY^ECQҋj'G|~u}8 ?“?i~W(|~tj']٬?Qk+\CQ??:?k~W(5ߕ .!ډ_k+}|mOΏD5ߕ >a>6‹j'G|~u|mf_E?Q??:a>6Xύ_rډuf_G٬?Qp9DOκXύ_k~W(|~tj']٬?Qk+\CQ??:?k~W(5ߕ .!ډ_k+}|mOΏD5ߕ >a>6‹j'G|~u|mf_E?Q??:a>6Xύ_rډuf_G٬?Qp9DOκXύ_k~W(|~tj']٬?Qk+\CQ??:?k~W(5ߕ .!ډ_k+}|mOΏD5ߕ >a>6‹j'G|~u|m?'٬OK _@|~tj']w٬/Qk \GQ??:??k~(5Z߅ .%rڊ ;ufoL:p4N;ǩǏTZiaX>/Qp R?? }O}_8o4Zߕ (@q(y<=n\7L2U3On@F^۞*准UXY|U:V)5uRWI=AupOF2:p[ֿ|Um sz&hj1O3pL3j6~iLu u 9>O{8FU*\v}NQm抧E( F|8S\ $9Xo'$I|H]ϒZ2" #:տڐn|E9Ly'u'cyThFr1=2Zy<=Ʉu"(GLzqԊ z$ K*MYyGT%2B!c(93? F;BxMs G`?WlSMu ImmbcIݵy=ozʎ7ER%ˢJ6Drx(B+Z^Q/g!;;&oK%5o"3 @nYw ۑKر5ypKqH*4-ฆ g'{G8Qǭ2K2O-QFDP&pA ZIDo4}<'NMP,q -l`&Kx_P|R 8QԞX$CZ٢}*oq»tM`>B Ǭj>a_Aoky>gAeZg 1\4LZ}v'8luug+ոQ^n[m{NU`xlOֽoϣM#dr_s58 ya.dx]5 kbsNL K- sCPo!| 5۞w潒b=;ŷw:Y-;|rM}6{m1\%'hRYXE?8@Rj^:WPyVy&hT>bC^3V2)@)s( xl5f-N$-XrxbKOKtW]73>N0U8_J|Eqo6pѫJȨz&(D\o xvJyx$PKXFVs(GOu$7)$:0rk|9pdr'n#z^xn[ jj[G E"aVlsçry$h`XsqUi g9,2C&r`\Gr+E$s=uK|Gl yjۃɧ"($It QuG^\.rс5Ѧ9F%\| A'5ET.rp*iXN1K!9#ϣW66jYIR*ڏhVkC+ƶ}lIoy g0H9cSλ4 {# tc˸+' ^판HoV;=Ǡ"\9!Y5i̲1KxJotmufYѴrXT; qsߟGY~{;UvDRNʱ4ݍO?ޏ?޲%dh8 dw-{a,ӕ :q@~P/yyG8۾RQi ]wV}b>>Х"L>GT~98@.FҐ}Za<ȤQi ^=֗u9@' V}b>>GqG?V}b>>?CAQi ]M.\d{>0 z|kkk>sC NLHC\[Ĩ *:ʵ*فtkͪ$v(񕦅zsfZZat׷N^Ci:hx%a:y4uk6ڜd(_7?.~;JԒoC)/կy뚆>}on]GҮZ]Awn&9ad ׌Kwaa/ Mn4bs oSw:I6VR[6%\c o<+-.uSjμ#ӼMbxS(j&~l4k5'-S/56;{hg~jaY1A$R:jv-ϱ\iqv8m>qdru $-MsZe^|Aas,Ixq?!RrA_Hm4kWS1 Vm0z ۲ɬkH$ ?*ír[IK?r0kKqV0vVp?y(_Euz4zN.nUE@=M;o%Xڝp(QtX=r5ve9FFJgqEƱY^>c r:qּoZ-3Lf8Ps^EሼDL%6@L#0? 񻤱lB@j얬Qcc6[Ob0'qf|_GA?3ۤ. FJ7J+;HV6X63A+^8u;@.k IIi(sx ` U<y]OJa˔^{ yIT4KʾpsVsi L8'i2:|7A#EsmtLXe'剟k/>xZDsd>EKqQïpic)Н?`^=YuY ߼]1[*(UQUDIcĆW21ȨS7C[ X&&V{4 둹`ZxM9lI?ykdVevX0rE4 Iw`]{J_c 1moW$\wr_Cݬ2pαd32>9½di=J3L3`(nNXs#S[_+ $W7?f9hݾF#ngT<1KcOe[Th``|3noK-UrLޫǸ.F%ľfd`UK9)[-Cq".`$'?ioEB: ?uҘ|A䱕rF3v]=9O{uBҩHv@ی8\Ρq~_[2#FY :ZtX*Q"|Q1\ΤJ 4<{6:lU2쌔UgƇVa}9v3V@)y=zUy[bQ9Ź$jqC{bml$$)I9ȬA^>\q:9sWKwB[+X4FF uFhr.x:>6ٚ_GNFcf&8b 3Wqŭx#Ixd 70 _ xV o9F@㓝qbɡ9x=;Tm2K&Lɐ2zk1bcI. nƃh r8caikj/G~m }D Q&c*rUTuVSp,&D%xRA ŰXhNN@dp<5/C+ة)[]Y>Hl⧶pZ)p??Y:KynH I ci: q}#ڶ0!{EƟVix6KhWuXQXs<"m1gVƪIgYr:uRj)9u)sc9ӋQ-yy{sIY7:y^Mg\17 *m1e)W(J5[X=b$c'iX9RA&{[e>X|)l{zSiXI%fl8^Ee-s!lkgcڜN]@$Ҏ.9خlB<`7>Ƶa}!]d|Y^zT{C2W n'Wkr Dm$c(WY)BJZ%7B:$w MC> X7ܵ!Ȯdp˟0c7UNV].bO,ZJOc񽌷2EmsbŋɸA)sҷ\:iҰi,-yccԏNۉ'ӌIf8}`~5ۏ'~NH)7|Fh4})x%8J?ҏ"J3 .9w});R=:@<}@=CnzTsGz>_q>}hF=N~sFA_;Hf/meUA>4 3QR.pwGCf`G𦶣b-" ƌzV_VsUbLĶ\:L'$٢P??\1ϵ'>+a9hԬq08?Ju+ҩ'+#BqKbG@u "ATT)lYAtR~$Qh迴>j?YZw ɹBT=pjAA:jSsU#wGTk@h7`c=5QX.Vm֤rpM_D>syiu1T<6 F=ӱ6.ڦQ1JX22)]Ěld#ȂX|o? \.=`<3P~UQu}Ks,xEqsw=LA# 6 1\Z.t*Y⽇ĐLǚ2g0&˺*kYj4[r^ޛ:XB5)`|bOJK@?k"і#*"hVkio䗽˲;ψ|Aka1u]GLP*]pzW,8fls^uMDUHÿ]o[DW{V8HP^[ur$VX@FI ?Ëp(oWz[E@M'']hB0 meLWEz #1=Mz k>B-ϑ3?@6 דZn۠v`@ķpuzVtKkf۝UrYӢlViT#xZBs4;K.4o+ld5Yܦ\䕖bFCs׋uۿ[5%A$z׿G?*~Pf՘٬ nGT15xמ59pVǖi|3J!<mH"C{-O Vgcj+ NRTwkG#md.n6c9uIZ˂DKxtۏ^ȱ 0;OpW# A>Piڍ\jۂkX{ rAdi0Mp*?v~0i7$`1䚌JpQ+ZMGP&Qr}4VIbNy)Ì'u?óow*p[܎vWfk] !qc@A$^)_׭u`el$BZ$ozۄ56Vfr=jWG=JJ[]5F ÞHM%Ӧأ b%"PUAR? F\N֪Onirj9;F.mҰʂ52بNO"bo6Ah,{r3\^.=*yCp=ku փ57^?ͥn$˂:T# YWw}ğ*JȪ6Z m2#sqgEeJ7*Peu驄9cZ|пHIC p S.WVD+\u?lC+ eM_t *Um'rzjW) Wy3 .ެ R}'G{6NZ޳n!Bz: onDDB2|N.֮{M:6x[F [2֐1华~SiEtSc}kR r^ID (6~^Zfbu.m<|Ƶ] ;+EVUlSR)ŵhz^}*6rm %/s=8-}i_/'w"Zh ncT :#[Nit}f&;{+ nr ٧e3yn[P@j\ FJ{m69 ^[X%(r{bg>oCBܳ.ET-`s]7ѴcpU"Yn HYT*H#Sc֯l^]<ëŹ^6P`x9dc M6[(n6B r&=Z^8I*JAVӌ{Drhv],kVBܘsԁRvrrCk}L$10RU_ڱp֐7hesŜh$ 6WSM ['op; Lɝ3ڦѬlmE9Qp}M-Au'q*sIɑ9$.]Xj>Oė9IIJF{֓\9sy.A:NDadO3;RO:`3n_5!BʀqTY̮ ӸZ]̙8ڗz*MDW%\ZO*&۱[\\z, hqM;Ysjey7'*5D[q#/o@m1nK_6{[c-ֵgx U$|? $*$66ײo9$k*QљΔf&~}#?ZźNÛww>xL|ÂyZ^5j\=:(`h:}sF;);붓c^;n}}҃G\AӶ2OL0g<zF9 Lb`G֎}x˒rR)S¼dn2=6 @n1J, PVuits.ӝd~ In58Q46ris:Q=F)V@gG}irZxA}FOmMUŽQǡqi8ڀ#cW4hҀ sA ~`gE'}q@/N&QKz-GU8 d2\ƻ>#򠼺HWB$L?jDo5ŜM'gQ+v\$RԍH;4y7Eia[w6f>u8YM{CyQ8B mbE]\WDpH>Zr%Gߋ7[Z^US*:_ \ZBGء Ü+TIgwϑ2p6\%r\۽k b˂I;&=[28G6s=lOƾT٢mEUdSke3Z[EMWUiPf`;m{&)Yg98)Î[#;jz}:]P^YsPkk3e34fy xMzܒ*"d*JI>]\>RPu=q.Tge.%~- Yc:p+n7sxnJq'$Vg]74-'`u:0=Fko[T9f#ZǃWM]6r1\]޴pEr${>ֺV+K4bwL4YFrWvz:WuI`Y=6ּWfIfV)n /*Hi5=$Z\@!kdÌp܀{T{c׵-ZN]Kq%b(B!+ӣ4TJsMCy+G28UHH-2X=ۻqM9LS`®j.3ACX:Y%XHY SYB\w{:Jy~H m_Zi)TnF:Cv8-4Ij3~9Օ]I@!9YU0b]jhojB~`p;o G>\(͈$,4u@~2 BGԖ7W2 >SŌ޷%8+1ګjvI{ +2 ꧵k9a- .B=iLE?wl~*KgP z5p/'|O4cdt'M0]!ܫ:J+F#6K:V X,S)VYr34J?L:m_a{𽐅i6v}1? #8+b.# -Ll Hf[m٪ieq h`䢅#򡤚£BEq{i_VO2Eqwv'P$BiH_FuE \qӳЮd2<1=iR`vR#ցG/ѿ1I٠9яL{b1@ހA?4C&=^=EO%ОP⋫ HlKYYbwD֧<7mh[X# ;mJהzg+yORffdܵә )1SxFRSzuM6sr?z cצA-HBrXۂH\goy~_咺谹$ ⧻5Osi]ܛ/,Jv?wzfa G3ƛGU/6-ey {wC+Z4#\ZCyǚ~vqlW^2 4\eס_ͷ>1iW OQ^uz6 u<K\1nJ+No!{d Q~P1zgGT`ҪQr^K#]J#lHbT{koᡸ1;L0IRWDEZe]Z e\ 뽰͜(+ؼ,+H̡y q o?<FZ5_&=ys?^2WS%ex̯%ʈr?:^'1 .WܓJ Pk H]6h:’>kjPkWq\2KRK=MB/S|&gE*WHͧyS^cd Rl<AsǴDp kA " ֶ_?7kHiE7wrɹNB[Ht:%6"9${ե8SJim5w8on؀!'-.P/|2y0r>y5eM{zѺ,/Kگx};ZEBR:n<֐{-rE82~7$iy-JFc,{Wk2y:E܅A?T 쮠GmF2v gBT'WmL_FAP[j54`-&v\r~nN+ pfZUMXX/q#GA*rj7BTY~`@(iytc$屃\̺ʼn8CTZi2 /ެnahl#P߼?96O34& B퓜T~?pjG]S'jľޛC6i#'ҵK#˂(NqiV+^VIt:=S/H|;g~b]GmxKOOyC3f']A-6V $lp%qBkh>Te1)Fç^(,7qHU+@J#`3KmhYN;4BCʦ>,E" e<k\j[vŴ*w5fWEn\Vf%$Lg`kĢF?x%UPg=1ۈǘN:C H#3̸Kgәm5| z5SQam6_\ YfvPJ|9(LZj.L#d9ќ9lɔ]mHJHp* 2wbHx`PN/9/BUEj 1V(P?JH*N8+ױFG4(9?)Jqr2=?t3}r(>QQ1@8c> h9FG(؆8422#Қqr>ϭ?ZAiyأ@Gz^ݨ4z?J]q@8S@ A94ϭ(q@Z([ү2 V <j+Pkբq_Hgaw.ARzF*;הzgm[Gvvq%BF8^2IÚ%t4¼sVbzZ:ƅ`:8_OK9@U`Ogzi ceZ$OAI۞䓞zk:{X +www}.qN6H'{Wa$Zlgus7Ӿ+5OCx`XN vVP\c'YZ+4Q#sUaky.fo)Td ` h)6uw^qDV ƍ$k/-D`K[k1#1y8q]ߊh)4a;CM6(ǥ[,pEא0n'd(jbxK6v|G##Q1s]eϊc|8^g+Ko8"22T Qx2IH`|f9ٗ:K˫Ꚕ-nb6<ʪK bwpfg'hݶlIV@0'X CRЋb\a[y'uA HցHJI$yiyf-4{2s$ rH mG/[;,`V=^ +  8;:|6E w})UPJ,MyΟ].x5;y&yHn{`zZ]#L| B0=Iܳkv#dnfgۑ-#T6r@k #0pw7[4aq@<֫[xJKyt#@rww|L ?W.ԓW=^i/ HX܂=+=PS崞Wxf]+Ld%MDa~6 RÚD.& Kyv;?z ia d5O \F 7&oYL7ƻ {k?\75GM`tìD$eћ⚴7z-tArX&1^ƚOfb+y7PBL@=G[Үu3# O˶McNjmpFeH ˺Xg1g> C4~)\lqeStlR~k5Cqb%{<5Xo*:~K|MdF*7(Q 5/)1C->fOMn2^b 2 XrXQFJ*{=I4p%J8`;W;)TRV>oN{Ҁ 5f3=T$ q3aE\Mx(!uS(,N7J5?薖qLV%Hޱk2Ʒ8&y%ӠɍRI9ҫ\fumxݑߞ?:u2ky Cu U9=GqFLFMiG W srp+ԣV@"ˑ'YD0(QdEf2VB3YFבO}CSFtf<,Oa2Y8ai8+64wB+!EO#i2[<~sQ9<6Sd`u]0O9]*M?O(YuX5^&hn>sZ-o"ؠ'͜zkh$QF# lqMy,&En >oyi躞qq:Khrv*8W XyS$TXqvIo&NAfzfecx 9nb\gca#qX;dFMC$RwG\Uyg 㠣@='hFG_J'@Tғ@<#}p);ZS٣y4u@'}h9̠{103@3':GѳhGG~$RJ3Nhb$mOTkn2,pƻ۠+Ѣf [ٻ6@dqYUm"7n['8 Te4{tOWu׮/OSeb]\f{⺯Eh},JI+ƮtrII ׳x-}:=!խ0ME;'"H^Bx- ջ omV2a#wqKY†InAgvw/ _T*ˎGڡ jmZԾ"\ش ; FJKpy+^O5?L.u2Ǘ=Rͨ6p<~t(g؊` TwntȐlV!T< sҲl[ͭZw*Od&W jx[w]0A$~60 KϹ_T8!И}b^-^ g%܌k]Id9@L]ECXjRuYwOSs_dһ8|GsPD)9p. J@B#qlk\y07 rrRsJgt5hmbbM!##J' &,ȰB Vdoq_ҹ. icv1Moxm 8aOtB VVn:#RXIDTڸ?$ur4}<g?C]VIe TZ:iʜ^$na0h##(=O#GTv9dfqxj4g1N=J?htȨ.@z4dcά8;; Nɋ2KYe# *(l d"jf$ѧ<&_?ʮiZChPuؠ}Jr6SMhD4).֚Uy+3N)bmcM2BUJp=ylzBLӥai=Ś tT9p VFYID:Tw1BHmBH@Sԛm]+ᱎ23L䓚;K?JA<8KԚ2z>6>g" з\a@=zӶ &,  $ԍWMO7^sgmEԦ!hp瞜uzU neE19-f5um?JˏMMmJ opC{)b{b}{@f,\19S5Kwos$G82~\t5RO,%n. $؂zҥ^hs [D[\ka?OOƠ7ҡh5@@,J̹ GI|,QSb >s|A\\h6N )K 66(s[^-ة%֒,G.圩{nR:5vc⤆l!JMcyq N==xnHAl􌓕zV-2wI##I;=dʮ:*mtC>="3F ;^F?Zc*ncp#O|Vf[BGz !>_O|V2,r춉 4m2hCG' ғX H'(A@ tf9W*k;_J Xo`j2iEuAp$N0UE46<[Rx%{i+K%Lc"4XB]| 3 Ehv7I44<G8丂Mo25# ørcUiT|k:y#0 s|>SSrfB1q#eDJz9@jSRfN6ِX0;m}+YbNI$ ȡ-.sQǟ z4+GSzsjJHpVACU89vQ-n?I!Q&x^{  -JrGGv@OA Q@ w=5uhrGFUwpҾEzZ-Z3Wumm&->dC]x5jWG>Jٞ `^# 8_Ug<Ҹ;ٮTQ^.|Mkk4aDݬd!ʰ/ˎs"kk$ݴH&JNNy}vU7zS᷂ΙcWjVo$|ehx-JiYE@hS|Aq 7gggXszq#׃T(_ /AS[᷂xgL>3|C4) q0N1.sai%KIc"||ï9!av qwp{NFD31ϊ%5i<38؊GVǦ[;B@1;grxcBm6xna'VFt ~Kn`lIոyֲE?vU:[k=2 X(Q*(|XzFўfaoGaU2AhcE]fh"ۆlZ5WK&YtŸhW2uһ2SdeЃSÕ-{pz6i͌psM?{n=[s:t&;fys?ȥEh9qi=/8鑚3L{y?^;§g AYΚlʋ%IhnX#?j۬$Q$2\^(-oU}ڼIϕjqJ/I*EX \(9͜&HM OFt ݙ?JD{hܼ#?.zr3CE w4ńe,q%IO/ROA\R:X8rEX,71jEi[X!r*N5H?t,j^-eh1G\B-ZDe1OlϦ;dg[Ro2,Daܾx8գq U S')j6y(̅exǡ/ՊK啔Y;Xd[gx|\~yh^`ϓp Pfje(*/- :h-ȯPָU{VfLF<ÀTyr7[5J]:ɮD6FEmFۻnJF/8S6;R.")1nN)x(zRB<`,xCTPPFy?9#iBgrS##55)?o=) I24+Ķsޅ!4bsb;ꋒݙBx?UkpeLeRVGz[[Y>P`_5xh#s/ >ZQzʍ֦ޱejڃIJОZٍ@5?j =kC<ڰHm£ʼn0C}+RoZnZ' Y9vf2hh[U˽d2,Eg{O bn NYDMs+̓$\UxIxN*@Kۑ] r{8 duJԒ[0t-iP$qOhpGtd`{~t1s})"3o?{wX@} ǽ/J9E'piu ߅.sQw*G&1┌ Ir֏=9cGi?yk3;C&'n޾swQ2nJ=5h?ҳSm3=F"'Yb>ƹpBzubhѲ>x1o cש|yVO6(X 77[m"U9uK=o#:ʻE*+|V9UQ黶|&`VY!xn|?4[$C<}o3|@7h:&!Vm61ȱw_~o#| ]2IEoh4sjecb=O^6Ѵ8s'ScQhRk+eP98)@>'hWOOdG0[ .mM7H~$UmU"I|^k0C8ϧlץTе}Lw_5y$5Db}U`64p/&z%ntw?ᗾ"#Ʊ[<]G~*]xqc=9&DIݵG=Ln*dӬpD>t,Q@N쵫hu :x7t53"T3ͼ+\Kyo -`qog@wrcZZixk}lr1׍T_6]g>lۉ*{4G%m6f5͜Ef z^R]omޑ%cwo `ip:uɢutӴwW H# Vfztqn;MOЭJ`dg8Xּúirɬh)^j6o̲L7#SF'l6{~wo›\@QEQEQEP{QA@ jV(5k_q맻k־"̗i gog4د&{d M8cs*3+KXo|oir#u=Գ^=M܍?Z BmA[ZźEW*2})o]ޏMƛ&k˪Y6k$M S#]|C\Z1(́{WG;q%ԖJ}FAȄ61dj߇=ޛcw{DjZ|)ȅ@F+OɞiҾ7[jBs>c{qQJ? FO@@ѷSKs@:41(Ǩ41ir1~~=2;SNG#xfR'9:=M.2>>ԬzpI֔>l w~*mtc$y&?]h>s>ڪwZdRL=}?ӵ՘֣mĢFH1?ƋHiy Sia%A>?Fy>dJ{13yI?Ƶ8q\u:#'s* .\rެ4I>%wgUyn,OO:7]m_CV4iT1 ~lH@xԏJEl`b.qDD\YbCeEM- W#m39lcM[BNsWTf,>P}ͥԑt*e=sml?}${W?QZL/+mkÚ:̱ܶ$|>ֲl izw%_M;v+y˟ s/U#cV܉.HSҧY'%1jS ff책)rw뺢ӼA Qn0ΒFCkZMwI)=$X2QmHm?X}#ޚ$ Q[J_i 619_vIT9ilf/5>6M4<C?V4<[H˓$;T1_YEny"bIa1RvKH&,-QTh95pVPV-`n<Uө}R\xp jYTYq??NιfY_<(MD=dMr /s0̝7Ҵeˁ=;$Bt zcگ2`0SއTHd&X&KU+cQ4c:JqK4w-*EUTtSKh_.>_N`zzVt#8nCgzd-+-KI0@j KA"_ڑ OA[5 ղ^.˻x_^ⴅIf'/}5% ܮjM.]x3-ĺU얫7ngǷ\d1NژO ;KR4:e6XD BE.n\|_ Cv+nQ'$ݭ=e,$ /̊x^f R(bq[.9 YP:|ڼ4%^=9SSݤ$3A9+Qڗ?+Bu i~gN~ ^}Aw@+HsG@K@>d@E.~&͡=T_ZT Ùe6u8k3įwo9m8}fQ5 Zh7pbݔ>T+n{ 6u!x*}< E|V\],Li>}sv^ {/p\1.Vv$9VTӌ]mhf7wdp% ;Z9FCo@kOcpW^ iXu`׬1׹:{21[CL=>_j[ˢm>+ˍާ]s-vġVi $VjĺÅwS =k,}2vhNǠ'Ҽ7wjb*|>gºmeddd)= ԯ'̠:NX$ݟB$\n X6#{yF?'V $pFO\g[YcP5IۆOf- z9 hv4g5'Ti.clhz*#3Ā+f>ctZEgin$cٰ=+en&ym7m}=*H/`RHREUA\PS$rF  E]_ޓ5H1<`uU9kÉ7a~rֺٍϙVDH$t`:Tv2T׭\|Aǩ5^G"4,wxrT28B p8(TOX~.ү%\xݷF0ԣw1}ǮcZ@ kOu#WXLWi7X]xWE?R~HوSݒ;5P32v6sVD0*s\ޛ'ټ@*d5|ǜsIПzq1B1c{ 0qڀP@Ǩ>P2E@G;KI3UXTBrxtCTiWtڒCqo{jRly<9ZNnwcs^YO!|ĕazDfvB9uCv:Һ'}1-B&a0!d)8#-$S/şKAM,[Myl#4 \zuU>:LMH8 ljkq"B$e 匀 s#wn&K#e͸'{1CD{o]H>9OZ.6̄o?ΰI{tp1"?ghZ](vQ>'kߣF  w1l6 ؊o7O? |ٰ\omgYl.l6BA;[#p]2[Y|NzE>~ڊ5]:W4ǟ#I wP 7ڕImVSh՟O5:jGs^Լ3gtN*&BnǡPаrL,; 5+v}a#>\H#=;hݶ]@SMvW'o;w㥽oko,2݉>ݽXIm*&bSWָB廁-o:PKi ñ$0llG{sU!ƩKȈʪUՁ@W$1rÃڷ7Z鹑~RKcsڲ}g65P j~>$l+]9`sh~tַ.Q$V% ]=X2_̫kqMi\{ Ph5P^JHR't7mǓ@']2_F5KĂ0|\awLH=iG.e{B$ga;Ku(..&P **qODèiײ$+~g wo6F:" :J}f{t!g*]N&=#<+Iw$k |j {"O XA" {zɼ|/oKkBg!b.6q׭vZccm-Q Kg89 hҼ\Gu2ABz =YR[yS#t?^h6<| G\nn Hll3'+No"VW+NP}+6e0!.OWu9Ǯ(<,q<q5ǟo'&;~=kOu#ZNӶq7[$ޘoy=h<6푐G^nT2Pzj G;PN)LzRy=z zƽ5=jAӆ}F)2})?AiNh \{>o֓8q.;5#A+HfYSXq1,"dZH>ø)O^7pirsڔqJNP:PdC\'֎֔PzH:S\`Pi7QJ :Ud9ZF885Vkqʟ4VW~a5ن $ 1ܥt-[V[׵,;yAnzd>ʵ>6ԮlG=*E:'HRt¼cxz=燴\(C G㎵cRBQœev14?njvz>MC4a 6ߌX<=L^]^gȕnYFY펵WzKcY#0} H"cvy5vgqPNszv#=j +v'DF-W7RLFk7+3\~1~YG㷻 4{䚞ve'o[6Vq^Ite!S'ݜ{Wq𽴶"#Yq<{΄jA[WmN?vL^ejHǖ{\M:ϖZCm }٭)6>⹘6pNII‘v]ǚJRD$P$y gEOo3щiԖGr:hgAkTX`~8y1І;.⼛|;p2O54ɳxP }-Vas>o=y$B7+*®H9H&Syw T1ՑrZ?+u`pj\'rG6n-F2pE<jfM I;=)ӕ{Lg]ڥӜG!&X\I:8\g7  Wp-0@:7Yc w398E+rzR7b6x5D$[_GbP)[ͬICz.ʃL `4tYbjY%-lwp?A[X懠+I:(?G_j1֞euuȌ`9P+o?Rs%cZ)aVb6{7 UDa{֕ս?g9^*9eM~܁>=) 9+bKƓ8=L )xE~ͧ?Ұ۩ r{s#Zqq+H9c`V-6"tg(7=ƭ)2( 9׎=gC55YM8Iʘ'zM6erp~N&e*w:j>uB|`nVM^)3P7AsȮmo ت)5RX@=kҥQr KS<-Ny2[x^ܨ/XTUUz Il[??M9 W1~`Vk?k:\GQmGCv~+2j껏ݬq+o> ǘcG8vd+<̐2 gw~h[>#H5V!ퟠ-XnYXGre5N}V;u(Y嵐 5wJT]=eK?"݋J#b$ ߨ5%2Kq@ҔV[%{sWO@-uA]$Lf뷱\XkZG~;ԺV/+&7vqk% TejZˤyߴdX|E$jזNsuYS`$8 Йdw\G*͙TRoMIIg0G 䟮j)/򿑬[sb}IAA*nxN pTTJPmtk(Egfs[z[\%M)ZM:7,ˮJ)r##{9dD~wW2ٲ. )TzlJIٜ_ZJcOn'?l˭tj>-kV. #ܘj#AϒsޟBqZm.ևq$")+v48WĦEWc{5M?F)ҡ1BqUOTҲX@"BĀO$`>Q'k ({+D?3GO0#qzҬ5a=*-1сy.-T))MKyٮ9i늓Si9N=f<}-Ki4hJ0nZE+\>;/.IA-^w..]<ʾz,[kF'Ӯ+^ {gB@d6?*[Sʎ2YO׮lӶ,g3اy^=ZhbrOu? 5Z{U#cӃe[2&v'񪺎eE3(%F KU'U#b2cF0XpjխGmsoHFrq5cmγt??—1ߪ*rDԱ+*k{r+7I/s wI d 'J)m"fInf,yq?θӃAl~l-?]7xZyntvey#ҦQfGRP.zZҮK_a*xx ~zRm4giZC3VKr' <;~YiYH u?U<1 7^)֭cZ|Iq+FE$u?*XNpW$r5:ۣi0YkμY{81|Go}LIK0#q=84Q,$$iO\&= ֏Vju"dP*/ ƹڒb)&DUGk>*i+tmPH2uoj=>G8|)b>rN=|A$ +mu{)aFOҼsg.bT|6blz|5MJit"u6O"72j~Q>DXFp-kigj,2~QԁzXN_hʼn|kTiTԜKEe[-kNNfd95 ׏l๎ REC}mokkemDd1 vH ˩n5@ ۂWo\R;&21{{d|**2kxbt2iPDnܹlП^\;V3fHdy;dNk#O{ e 6w9A[f7 wǸ]^rhj;+&h6w14h06-sXUA1)xQi3|<{}L9Uh !f瞝R]TuXbcjދ H,:ckƑsSj:d]|ݖ.;K;Vy$뚿$tEێ2S MlfepQB=?2Ha) Ǟ ?/,O ȷ*VkJWUmKC:Vmۓ=98u MǯZC~-zqg?/?JBO5@!l=zةҸ=>aHx<ƔTL Nz}rGj i? _`{P1ߊ\9LgGҌcP‘KS@ JO'"}:;HxBz}i}=h#G ('~Tzy4lV-5( \MZ:DBдb4E'8T׊mmC{2jpv GF;/cVQv*PںmВ62\L[β NֳAۏjXPۛ9(Eyĵ6n,8YEw取[6(X5f99]t%]`5E1DXzS&wԮبh7'*%<.`8>ZԿg5&Q駩dVWT,ܟʦ6Z*cERh$ lGI0|ֆuZ߆P-5<';JoJ:T,J]NB Ta@^ %ϐ@y皊фaXm'$ӻeHcid 6i4`^;T[BxE^7b nfaSIRJdVY%i|@ SAEܚ@+Ri21ܬ=hL3BpUECcekb,v?Xrw*9\szc)Yo/wqXZGb]@5)/[XW03l%ݤ0.95*)$j^op qUPVeK=4, ;j=R|.(OV.3@t2{XGt`}J+D(r~_&Pxfhh}b->m2M.q:nM?S^Tװ9X'n$<|Kߕ'T,Ec5 7KV="x՞l'5K.:ϗuip@?^r!$NyHG>ă}5lG{ֺpT$)56KYav+F' >Tze`$B_ˍ'ߚs =A`V1ڊ=dc}8+]@mFh-N lҧHcuV87Ӿ_]_R6lWz?c5'i[eCՍE6pLOƽR(UKU _bW=ꮡ&$Ų̦@v1nn)л\y\U(+׭*s뇇,BFX8~N[ o[kx @P%?5:,[8fv!;},1F xG _sڷ|WjoYZmʈǯ!@l. I W>IKXՎwRH^Ky]]N}HOZkB^ɚ6o\ֻ͗6G50i6hq3"u#gH7K8 (n3tٔ*kzOVҶGpHx tWm,.[٬]Ѓ1Ѡg͟+ZKomyb%pMs%vhzL{KQKB%*15[.#uVOƞ[ 5ZGs;̒ZܙcO]K2Nffbǯ5+ұ\K G—c.W@*f`H3@Tvs\] bPFЎfj VtRY;g6G]iq<ͼ7 Av[} x6;L,%qٸl143*_9z/lʘZ`t4㠭|VkJ|/"ՇҦP֛Kހ)7=֌zE+u9R;b8yqzNj0GEۚB=TqT({}_4dRIP*ߥh C#ސuR= 4;}GN9ƒ鏥 ^Թ`E.} 49GPΓҎ={}@ c@Ҕ0X! KGƃM)@H }iNqb`LB{ 8{ҩ$}G?JL` B}y}E#(td|# t׌Ri4 B=9Hϥ[fHi GJWy[Ef8ڼ3]5l+YX;pF1U]ۣHTXZW24{=iV(fdužU"ogV^(m8Y__¥ep`dG =;ޢec?YU#7kdfJ,F7m# <ȨI-'Z,cZ8̿0D Пj : 4(XOq?юzP=įbI'R@ى.6).pp)bvIB\ҀDp<2o㠧0(B2\px`Tdir*k+[y20L\J$᧍61[r 'Yh[,/iN.n“6aRC1?aWQV$ ȣ*"C򎂔TN'I1+ǽp%nϺ9ldiWi]ґٳLxdRŠJ|R-J\)d-X/Vb j.hPJueuCсדRMh֙_k4[35V/t2[i!犚i(j<1oY7e>Xdka=g5Ҽ)39#'?*ʐ{WMmYDoaW[˭RU 2dUW3ml\\4 mX+.\ׯ̖$N0JV_6O54o(F3ku"."fY 328#&ƶ|)qvQ|?kNޤVgiZ~st~a<}u9 6r+"8ZX}/R|fTrKגKe$x8rHWkD=rpv%Kqn.FFyJ>MARe{,GF/24S#ld=+ϵ}vq%pKiN1|`I?J*1բXŧڽݘW^Os'I˜l0篭u ofbn9>5]yL[H<8DۜWZvZ|xiIg=|P= fRG4q@>迭&_j{9bsAX} x&J@(bt#1Kr ' jC!M8;GB?4zI@P`{ޓ>R &(pkvDl!![늉- 'ѢDz HD[l6ȶp77JZO023޼9նH|rN*#4g=(ěpqg4_)r]'V{BH;x=*b['NKtk?9a'Vm0E9|GvPKie4?b6qd"8֔4pEʪ"* ~s$5$EfYEnG,X7iҹ?@,+kI>@ Y]FcaoAXF0ltmb{yUUod$?$٬(|@a`/P$ZijwvP"9cGoqIxO5!![ 56mce}4;Trs[H(隺h)%ąŮ -̻6F+=yqh=Wtpf\J'񦭖R[g|ǚŅ̫oiFuITߜc5D[XTv]q hZim%mJ?%O4Y˧ۘʪcfF̓xu:m{cIBQVJ8 _E QT`/Ҡo3X`Il xi`TK".qiLIdv^Kx~R$G>קej'ޚx<gz0}WGM.8E0ZF hHW8T#-Ԛ+煊Kc!~3 7^*r('=rJxH7sEZEuj|3<B.GS>1j[R$ɾ'gN .bb,'ⴉJKٻ}(d&x:m=+&mcOp#kHLWin085ǡ|J$Ѻrֺ䯪֜t{v<֯TG5hlֺٮvhj֌M3u1 R*I榜Uk^g7*}(ĥnBxz~S? q+/|iGoqpvL0=O'/h}|9:ШsҹOeY,ffp@$Ca/ԣź)1WLW2ctυJhKx2N@q mS ŞO%nJ_jz˘\I`qu+k_I Y twtW5j}JQ28Xʢ㟦}n&/$u֩OWEƛe!#i- לRRIXeڝ?8~,d;c zܱط vO8`e@b`?rrmlވ1MD +ϦM}i:N`m GFHy3pŔ*v洱46q9=(%q?ϖ@Gq@_f ߑe!7?1_<+kVks<nq}IZwOg(Rsa銤Җ>m-0U>_•IZWAc>Fbi` AZk܉%6MsɥYl9my߀VC5z589X3P]OfSΨ2ڴ-9`Ѹ"c(@r6vQ{߂zcdmRqנ?h#Z^ӑ:ۛ _[ǘиsڊXDBOkvV4n2~^O]|Agt|K;Eu|g#Lw GGR "ZU9o\e΁rmD83zӡW!U j͹>\9۩;®J+2T8=a:Jf,kzIh繳wNƑvA沂\k \GшWN: {M+\ x -/t,t2:G.񓞕$xUVl{@UzVGo5y9#IgѬ1LO:]-/|<@5S]W3ՄH%p\+.cts5MTWqCRlMXr1r?y޻k)q$FHmًqҷ"״HWR8rEgJ.B8`JOeRK[Fnx8u n٦\f9.^)ŨFne\9>$"DEl1Ӿ;5m) nQnP[w83Udv&g( iqE'/=h1Kb '~G>~;O.p7֎qF}]rxu.xH33KI@eFxF@iré $giR:Rq>G/O_ƐҎOip}!(sH>J~cGFsQg)s(Zj㱧O)8V; u;M'>AH`0ÐCƢKksV=sQqF3բ7JP$,g\wҫe0aKzԗVܣn$&ߘU{$)|ur@W_ :!SBͬaʃkAYccJsъiH*M{1*8d}$֏ќb r=\P)PyC{P{`98R4[O4|v.ف4^Op֓4y=} /:2 #C:9#GANSd8u< RRş'^O!ۡ&ũ|JӔZh 9zӼ4yl;P_`\c,Jd!kn!n|A~9 &~(RGZBð 荬s/[$Ptbԧ-nXerǏJqt G]){G>V:Ç_\Cϵ 4]3 㚗MH漹 V+W#~+K<]6?{7-PR=2 0e4hXeF~y#`_^dPm#zI˺>u쭱Jtmb{["|DVD`-;IԖe cϗ\~*ev*>/N^{BoqT5"XF,J 5xRWs?ڹ ޶J|wnXӴ]> XXC*AnSs] xV񍤋A5Rġ0gJ |9a ^a:R'| j~ mͤrDH9_SxNS/֑tU,SʆbơKч:72EUH9ui Y0=4-6Zpo%Wqf,OV}>v8'@f'nz58  U|O\ T<]<%>h<H,F@H}Tn7P 8>׉֦6$NH>\o+\[Y1s5B ]܌"=(II=6t\n$rWh|aFyK`OSҦF.G^8$sVW1˩ov"Pl 9,+g_Dm\*XnSJ&JV(M78wNk> GCI8eƹFtƥݏuyȱ`v~'X-֙绞EH9q {{&YL º["˷i 'kkR/:ռsXH[;A<\VjwVipUMknnq;WU X&D kt !sDLpp$e:*jYpJgKͲ%J<0؂kǑIdMim,[xRx/ΙYX$<~UG,I պ>rU1u+:[{FbK13ߥG("),b}o\_4&k]3–I?"\xcq:0nW4rJ[u#ZUo?o֕tSN D=pq#u<W1L('֌GP#'րFzRN06`(y(踠9sJ:)8ڌ~c@Kr3G$xPvh(>JBqgP:px>w84c3G{? h/rf=R؁@ b{qRcZQLFsF=J9Q`{_'=iq|RaA"tA4g?ŏŒw Qu ǵTioз\,` x)g)J*J̨] Vvyhp>0\$d+?j8&~caO JϽfg9ԣqjt,h8u0kJՈ'jryHuya[Ig4Jv|8K:.|sWqrWi;oLWRVg|'mQo}"GYx/QL+#(;HzǬ {eF8~U4pI#Mn֣r{`Y" @A5{IUSɨ 57TƩ,bFXG =(+i*r'<8FT T[!'$ Ajߕw"dSv*IG 6 U;FLnG=pNEXhb=@Iem!%20g[UbLz5/'{ԍ NiY0=I5gPOE. )lj2}(BzLOfJ=!(HןjR>覎crGn>NCFgvp:cOҮ3Ԋe^lD +T5UxT&Sv<7>W:y 5-HڡhQ̛I?Ѥ$IqHmǥs&Kh ?]/ǥg\v9_:tgXE| =(8sL};>?+8ҝ2tmSx]Gǥg 6!,"ٱϼ?ҏJ3| OWQaGǥsّwӥC>߱zPmǥ9&ҡxU.t6 C肻GBln([Oܞӣ{dEU㝣)׷/y> )fHz C$v O%(zאZ|D +q;+9ϗEՅ:mEnANj/{,6`rx=t42C$4RDxۿ`Ix>ÖImgw!p#۞+~ѤϹي `(c1nNw*!S 5~Ac56qeEF1GZ/i~-M YXc^17}nI&eYTLE%tjMiөirxVSmX, fpOZotQR7^WԵ;{8h#@rI>ջ(ťX>%L0,lX׊_ɣ|W_5 #Fv(*@=tߎn&=b,JGjrNhޗ^~v To\n9Vd d1C9 {W|; y5,M$V#oБרE4-2;kKYa\^\n ,0aOl;^Ѽv2x'&xZ֧ P QpJuF{K94}B*BIp2O4Es| QrL_ Zy%`yGEǽq[AK$۟ιg:{}:,Vlt22Ar}JQkQaYkV>FA?Ʃ]z(?Vl.uz[Ĉ6s3Z 4ho@{{NEp#I(ϦP_>~x>1(vZ3 qN;9(ӓ@~&Pph"I<IQ@Jpޗhq<ޔJ;u!~Tq(U o6N[+֙ywol0l}? "~nVm8 Eyit~%%[#&L7/Z>#n1jwmݓ=si* 0'{}_&ZJ>Fva><񤥰cEsEMegXu~ăkJ[[{9P\K&Bѿ?Rx^vԵ[SxZi,6DiB?(ПmXbǭ<0kP2l{Ul#Ng7!ֺA54ZE$~݌z DHθ'',;K&7ƒJ7< RHjA(ԛ1>Š*ƥki)+y҈db<:l-ėHd lQa 0u~4oSzd|M?i&$=sddFOJ>iyE0@34ucnZ^G?'׊րCKL3׭_ΐԑHܐHb=igz)rti^jZ1^ab%Z6ԴP2-mhѶ"FږP m.ڗP"-mhd[i b*7iqTW*ht:ŕ:$̝A<==e%7fO2Id2qvG ~\N>4ryЂF7W* Uqߊӂ%5>?zooskzuƭ &n."+ȣAlc 8#Q^{id۸i Ao qϩھC`L9mz$OhI~5VIGOsig[M@\A,r*[n޹\ ]OV#6) p+?$߳G$Oh_2<u) ~lf:Qk2ƫ.eM{$Oi%߳NuRwk9e )uocSB,#nI~ĺOQN5%fy 6bI9ST)^J0'vI~D#U:ҞRJ#zj9?yGJ H/N>=dg4qҀƐu0qJ3؆'hғ AI@y꿕4`uP9Ͻn{J~EFJ9 4^A&}=)x=#ހ $t89oB=A(?884Fs=z}h 䟥))s!8SG=}(3FH@^4`ڗx*:dL (ԇZf~=(1`z( 袊L(((QNKI@0(QE )E%* Sp5bqkOcJp*Q@fښl=n_JFQ@_`ҷ6J6Jaڏ6J6JͣҍҀ0=)?ǥomGa}ڐ{VQG0DxҢGs76>xF⻛\wgFЍmGVӿoֽ%8u g;zLCRMzqb@ϫq)'P @'*F@׊QhRp}(@4i9ϨGi@Hx'zԸ8'h<N*6t9jތzs8  9 (= GC'^9yAG"7~w /^'=J3QIGP(J(~! qӏ&N)OL$J>r(i E+sHsh<Ґ=('ּ)ǒV:|(Y1aڳ'5[])ﲏ8*Ł*SC1,䓓Pul}rXg=:x^֒A To+W^r0YNU_ZTF 6P,_(''ޙ) 2TeQkrcb,sH5:rYOB9p'QE-DnJi?Xu½jufV{L%qU"CG\k/-"Er> Xq%xɤ4>DA) mM8Xs玵z:uv]b}r+F%7+3|yk hiRE"pxV qU|(vC9vS&sN)]V;< 0PGT5Ѷ[7T 0:sm-kf.n6Tn ޳m.'uIf.O-3Kڱ#_I+i#\3Ҹ9gO17W~ H}Tt=q4˧,B%`;-h.gdfotoxx-18.01.1/images/print1.png0000644000175000017500000013214613222767271015061 0ustar micomicoPNG  IHDRb4 IDATx]`G/'!,hqJTz)-zxArq@big#ofݛ7H(< EH$Ӈ.p8}\V?ZV]]ZILJJdɒG88NϚws8Gb2 F~NNFQpp7o~>44b9tp8ScFrz_{ȑWp&t $9Git:]],555^P5 G#b\.Wl6hOHz9G#psq&t4G#HM'3]/G#80$> 69G#E]ahjaNH/UWQRsnzx.JjL_ބF N8˥b Z$ɈC+9ZIׄxah"tZ>;pG s7bbFinlEPG!yRpyQʎBB0 'g/22z"00RiՎ v6`c@||=G#pLV2#]_7e@PZ%v`DfhB9TTT@d". A, 9#:^NGW4A B (m5$bt@c6צ&̻ۼ3d}g޻v3t#Bjw"ܮNȅh4 &9h#~dD y;$I10ɈG#` Me<' 4v{@F :p8Vg7|mV'T0 Sln5~t6#шqn?>~kTdoAS*]F]0&>u#N'fŮs1ϱnw J%d3*Q`uᅇoBH+x}2Ԋp٭q#ZWGY\&Z-J1P۰ĥe8g }((XƁQYݟ.{K& ;`n)\:"zșs3puaܾb+|muH P]3#U)g¤cP]^Bs+(+ö 09Xۯ=jcʷ¸'cbۿ+YHRCaS!R^W8 vdD6cq/R "Tn ~GK)1T{1\=(\'!0oRmBނ$y*݆3߁&jD?Wo kPoF"4}`{0vk; ߛ]L2zuCZJVąjȣ `[Jۖyĥ%H7 /&b揟b΢<\EՏQ\c@1W`e`Xz,Meػe &NᡧBrw?boah#@O lB-KF@?-4W5kH*i$1C5v_%-JT ɈUصjZu.~;GFsm6̝[Q,At6z&!;d:܈ Z Q?~i6 W 4cǒv7Us(gX}L09n?a7!ؑk. e͸}:.zeoހumH~5.`0t{/ -`q9|=l W"r(ގj, ?{V/ي%0Ʒ/`{b`hj5EWJ(4 =M,F' ح´mv}];y{p5]/`Jq\"-1CaمGy}|X4k*$1gs-bنZm.$@uJmDWY@Ή9_ 3v6&( Svۏcӟfcm=/ L=B%3O^}/~5yaדm`2Eqcѓ>$ î}XPXRaW-l7jjj +O];۳ݤe:go(؜]w7,&}er6dt4&Ej!MօґώeMqp= }w_$xKp!-v+hAE>zDpZIHH2kl3C3xO;3cOE:zqX5v Ӓ0;5hilMx־of8C>`uXԬǒis&6rەUbCf|8k7 Ū|1n{q ƔDpFA6l+']bN\TpkP[^& q ]a|TWWbƭgDODK$6a 477 -L 44#%< DUW' {o]o`㺹Ƚ' yĴ.{9ˊ& Y`TOā.7:1nnZD PC0p~Ru ~L9gYN5WX)A\W:(Cc~ KwjQ]UUqɈOH˅/MWXH$A׾-s$.1GAnvl\7c mԅ'kPX5gDE`Qnؐe}*)qb|d3g_#į޹Akc1"܊s )7}' F\Ϙ4Ry%S@^\&-.su!qoPEf-)Mb `7ҤOt ÅnWk=֟O=I~qoaܿЬ1c;Aa=]\b.6l6qǓWR^qzJ50ٚўL.Vys( H(⼒oFF^:?4S p!D*uXy+l礼$s1 #Td VjVGw(5 ߗT*HB4 񿞡Y~f\W Yl^i.oui: ᐭcP{%v<*D) .d%7_H|Ϙ@czTI`N &+h{!WʲR(C#¹/Ú*+kJPIs\&%z2-z{[;;RB!H7Ut.&dgă7B ;lc16疢 _<OHDM̞'[Ăuo]|fY/=[eqk`QĦ *+E[e=Bfo˓^ɼ]NBQBj7VS&vd!Uh@LbԣT %+1o]ԺF`Nzm3PՍZ;}ӓܵ:n,KC}E%L=qB8 ,{ />S4~+8ѷp.;nv,j1ǰ8$ƅ#N:WL  ~מ 3ʅ8>.|)oV#d>(^5 g#%`4IZ1qXcM) a|_-f>:B!j@~EFߏg_# 3_mƼM(0J &i7wAD.̱(_ŗẞ0Pd\sG܅ {n w_ vF0!Uގ=R#kPj#͕ڡފH"b<"yg?" xw^$$姑BCJ Pd7>/ޝFĜAtb5)/Oq 2ARb+?Yރ0z#{e|_Gfظ~VӆrAGC<4*>L~Ihh(_`PQktDR%MV)|pU ׉n}K"cõNRWm w2"JQ hر#{O.R1r@:tJ  ~x;JT^] iނ' #7975t 5VFp5cmǸ ӰvRl/ÙI)Omzx{esNDB`W PiG$#.I-N4HhĮӤEt轳)TpaTj|M!dj ˪4p$'ctsW=Ч Gf%2쳺]7PqeEFxZe?ٵ,\9:a#` a3gĨQ0CĴrcOV%i f#(Bt\. :5)&ЀF~p xY?Lx1qjNNLnRaOYv}]=LOzg@,&ԐBt %]f}Y9aIDFR47ѽ{ơAo)X^{ަTKzm14'/|BB=hsy"&m{OV`Ҝp d]:j-fBUNFιK0$$MxPzȑ!=]ӕcv(U,oc!C2kj:V洞?q/z(9؄ K5@b FYౙy(.W3j[stsDqQSA#.Z\îxUs;di@ Uy4I].}ǡd91C!M`wpd`2o>@WR}/TH R^]Ci%XW Hz H@= Cv}z)znRgw+L%6+>DDکfDń[ا͆\M6KNg`|vq@11J;@ ѶTs:;Z|0xp8  Ί1#eGڋ&#b Z 6;윪-'zSS"#Ίg;p:@D`n"$NW 5[XJg 9Ro PSDa#M[Hf8?piXrӢ@G/3UOF#_Io:֦63kllDII1,f,*f.¸~QN64]G?. Ti1" AK~K[ᏯFtx5~p8SN *G#''PG#8#:5rG#ppFt@jG#pjʩr8q" A)^#pN R{G# X`jt&mr8G Flߘ1c6#p8c/h}/fi7p8 ZMyp8Bo!yG#A3"&p8E33 ?o#p8#G#Q8#:9G3"p8E33 ?o#p8#G#Q8#:9G@!8X,Xtg99H$T*BL&1Έ:ɭl2o_Z-b.\t95Gt"laR!f|Ry&u)"9@Arl6c=`l^}踡:q9CI5goSp8H$ֱgf+v&@$G#i`VN:R:V#yc"c^8@gG;utϛ33Oǎ & 8,_8+c6d/&CIG 5#)zhm6[1a ƒ/O+N#,DJ3ʉPt/j \ l:}=l"134;}>n$(ƒsaL"Fss3|:&S/>̄ (hGl8$*}' ӿ[wEO4nnD"G#p"\v>sFtt|Nն ME(*V[\4@NVH IDATD"1D CiǴ~wbL χIAll(v3n>> c_($[vYG DWcmpFnjg9#jS~fB$Y)܍yu7VEH!~MJ\|%U`خ)zd]X>=㇓ 7&MgS^N.*7h>KORǹz H]i8qɍP}^/ ; =w #Kqbw_ Aѩppn^F֝+"/}MAb6sfNüYh!:S9yG-,t̪©AeAS]ʰ^蚙 5PҞQ11Td褂 Hq @?S?7oĞ*.< t]yBV݈~`NL"I5i2̴WbVCM)֓OpilDL0ucԣquw%(:Duf2PEv0EF&8W}3z[]; }]1W*B٘gmwceL*Qqإo-Goy8 ѝĩr8F%'3cc{JksbF]NTulBqÒ8'Z+M.@A#o\,B|p*a١ 英Ceؔ]qyZRA߅tz}Ыʂ=߹IwɕH=7C"am!RE".J`2^pĬ(Vj#6@NPȤjUPznO-.sag1F#4) ZHjD1F" G#,xd޽"oWln@ζl4;-X2)lld Dp&iAlbcP[]} VofRgABs_ny2z c/ )r G,QBEsVRY-x&}PMBbs ϥx'Ps06J S.>r;1H [LqPkG#p8-9Ks ^|1_մ"4txqI5>|9$F8zV`?aЧΫFeif?]r%^aD{nMu{d-xCy(*,_84,J(p҄o{U_xKQRea"gLY P*Sdp\.0 7;OAs%_/;biotMEn! *Aj*-WY}zϸϿѧP>녃Q; DF->/-T5&@Mda-Ll&INld؊لɢ lBc~`<5$=Na= BnpznlV D)LLCT<{WGfAv8qZ&dJaw0>Qֲo5LQdAVa5*wohFR㭝&#ޞnEpu!l0+ѯ zZl/ņ_bߎ->{J{6%<سs/$7# ]ZeU0Ѹ)*Jʡn)۷&̉~՛ 2ޚ jlܺ]±jΫ.+=^,_({sJ&B,*//kk&l3^a3d\;&Sfhϸȣ *> &UxO0,QiS&+WႻ0]5>ibS|1E)Po[LCkƬ/'ٷ!9;(dOt-2F I"GPd6'Dk4Y;/aʗ?q9|g*^X E Ä> $`mJSnA~CKf@}w˅ cNb>(BpO-1u !rIF|]J6Ѱed994n4Hf($7UW"ажJ/$%j)UbaE,!3nRR(޳=#^KV2\0)UCAEk:ɶMai@}}|)\ĉZ:gsՃ: (dh"Fd"\Cו%:հBG R9rY@^صB!ZH3+:!՜وpTY41!&;= qǽ#'p(ħoo벗c_`6l5ᾗeR)ص{':k|OG`]n~l-Erp)^gD''>s5k<>Wy_AJ1Mxw=6 ?7V!qUw+aS᝟_ k~ \?muOAnF*AEߖ,{.2ݮ)+ja5󢴺pBRB#Ƥxy,߰LsW?bB#%:71>nFU}phnq~PwڃG/T|9s.K{pN~{GE?(XX3LCUSUr G[0#fg:8+H-2 Ɵs"h z\}X %^I*P?YOk5^Z8dBFH [2tE8G Tj#@Ǟ|^`ʧÓZhe䲵;O3ptԲ02֬Zנbzl_˞~PCcnk翯ú}l"U)?G}]'3ϵ$3AD4۰rFT61p@Z,pxu8I{m\~⫯Q^Qqpnbz[oC" ^ZV-W<$%o- gv~;?`uyuw_~ƌ%`8>wܑwmnlʭAה$Js J{d[<9 /W> 2R{V:Э[78*`DL 0k7}zBc^ _iCS#&R<_6y Ż mNoØDPL"+gW"x˶"4`E#,.{|蓙j9=N݅+dQ0dIȊW$/ţ#Q{l-%(bSx2喐Oב߬W7 dлQ/^gRz $ 1Qe*9[0ؾb v^xPJq9m! %EEn .""kpED hDD R, }Gu!Dc7^}}2>hߍ\rl7Q B#Jd221ٗ 'o A'i!C(aGCHIRk-2Ce((FU=oQ:cd (j.( ѱa-&xKQR\BJ/ D`X4ҺhVm.DA}iQs $?X>~pnyeHBH 亏hAG(fLSr6T(Mt @Ltt vmہf JTqC8tKpsޅу4"5PjC0 0{L#؂<~DzڗEZt-sfl__2|jhoL!!O9!u 7_C6$ KDU10KPSPcS#uc"+1{Ԙ}#9&;n:{Wqf,s.fUa@Ru6'RH5dobU \CELт˖X}{)wQ%P$ Hl_/~_RJ N21t!Pkə`|F*"}W*ڂ=1E~XLTDA]MHqM1k~a?N-zqTС%+N%Lk $={y RRӳ,!g`AMI`3+ zrd2:3,ZeuRL?B^sf`$'G >ބr2h^GH}E> cͦH|`\L6,D1סK$@ˆgb/E(B$X3#b0_( #iD^yyEPfJ3ޘZ Ka`y2"( N`5"#)ܾ:jM@ jrPPU⭋Pn4a@ڟ ;HWx-8ȑ vVD^*P:hB]Ɔ?VpNCG㌨-%d== tӝjNrݵd%\䏥 ]'98@~Hlv]1Nֲs%rSC> ILNNrB+~T8H_?R;ing!<쫅əβSV| r-"H: ӑ_ ^XT??brM̱4oׁu,H!ya@C :#ԐJRYR?d?|d !s[fr1"ES}! HwMd;y"-_%s~u40kkiCXb'ϡ؁&EޖuW6RP??h2QCD&"ZmWHI0y1@M }E"SȤ+*1֚ OIFSUY XHm14C 6]/LD(%sUPWCGZh?fK?RPpS'6VAw$A=RDžSnB$ nnjۃˊZC71`5-!016C&s5*@5#a(+1Hfs%~/!XϒAډAWTCCm3|O9DHbDb4w&h5 c6XGJY+1D^OL cujv):{6<}UwNݔ\E21Nj˱8Uz 8 SQ+ɻuqeյؽznLL-+R^A(u&^f!?cn'G3Yb BQDQB'/ÓwEyj7x~/`"]l,$]pI dp˱qSe6v ?AB /"m/f2xJL}o^I+'_q% s&>zU|bn M?`a꽧D 뙏O&܃)-zLrWbZTSݎ_9_)|!!,Ow^/?~M&kE?1}nb1Ej^.h<33K>h{aAk.J>M]Iq7_"0Ic:s^oОD8!71oZGAv g'o?L+b9]PPgEExz|zi蛄^wHBhᡞz8{ּpNw|v얹ѱ1:e5fϞE !kkS[]‘HPYM4( RjtT~.G3rBJf=-)Qn Ϙa Yk(>> ZIRb"rB4މySrˠ t,4Q}3P]f}4"6R2`H{^Y;cjQJt}Y0×r+R# 5h0[ɄIz2 5]ˁQA*FJgoءQdIIŧtUl7b`j5!@ z2VF.$]5PoCo)!a=-Q%1רGID2 &su?'ee 99HHKL2aS;~ jRrGeR&HANN£hFR,SN>:(&Ltчcc8pN!ygDܹѧOfOBNUݤ7"#9eeYfElҥ8ÈVP̾0r %7!K=!i&uRGKR93 'FJ );'&zbeE K"f=bRhci!(iI2!&{%E`DTA˞FZ%K\dDS.KB^EeEeYYdo]!~2Ka%)WN>bf@{C`l>X7Tk4NbP"Q&v 2%Kx裔":f#XVaj)?gHʑ3MK-ʹG̙]Ѐ٪5>ɯs9yy#⪹ S:qDl:FXmf7o6&+LF<"lm>-i~$};ajr \铁B39WNjIi`.3ĞqC{9[NI ["tX9Č9 }wOL%/3@[ٱѱ:Y%"on[3{ Nwx{؜|m6tڕ,391myJe3Έ{Daԩ8kxEhapN|b1bH2L"nNEUf12ɩpNlߛ-4%u*Q}pQ:Sr8Έ#t8#ω#k{Dg2|Z9 !o~~ttߛ3d;YY[)ӖiSp8f)LYxXbH^ ѥKeL${J]yo2G߁ ;;fJPKpFtHz2d(,sd28IG#pz`T*`!BᎴQG:uYgc",#:rriE3Qpb.$;uc,g@1G@EgL2H(K :ɮlG#8q=#r8X:w&eA\$%"T,uk0pP$SX:Pu+6⟀/z0$ֈe釸E(Mu rPVgDC>Q I4FpׅXplAIyondoZOҒC6XFyFv|$x-G_.;ĈR$7QXE+Q^m˜[WcbzJ]ov;1|jNJZy3kBģl3І,O~5pj|(5T5qc`dZ8PVaw>w{~Gb!_|`OSQpfq}W9ZCSt4t$dhD?>1;zι9Rv9"c qPjlʡq {hfBވHFѪ| ֣4ݓv?1&R)6ێ*#R;M0[ԯ~@Nqأ&::5zzD䕩ºA4vB軕Y4@$f5wner:!,Aч8JpF ԍTV*b[:z1LM3 eV+sʊp?. 7 ".֮>acpΎ Yz4(_Ӑy{ &3CEr8\ǹP7o 4tj.vmSGaދhIi~uT*ED^&jX?~զO:Bk"L±qɄr(5@NR?VG;R]=.逦(68[ t l?UX#V'f̝/I2Μ>D(MNBE1 %ܭq5طsPMȨ6006AfyA9jr@P b8j(fOC}M܋O yJ =BjN.>G F$\UИGq|HejoV2sZ@*Wr9 љ" 災J8'+u(*fpuǥ;qu󧑙nآpކHæxs`_'`֬yHWA–U q;`SbVX5p{L叭\)Eo5kcnD[жmNKqML53#tvmnVE 9YiH(DџPҼxZY6Qrq̲obkLR-QQ^*55CZA8w;tWZ"FF`Ӗ!=.9kt#-j@׽'>*6|>5 6Z_?{:pGtIjF;h8*PJ4FͺC0"{औ mbH{kt +m*VgcA!eDk|e0oiם85r8x#ĈWYBU_|yv"̻ڶ>PB;s$"f 8!15ܠR6nF(p _g}lC\1ޝ|MɀTo^퇐6In3V'+DOnݺH [G'HahLtЮ)Wptmzhֶ ltu[ ZjPrWs"=/7.C̝HtF9De f[S\iדqE\;}aI!CiI>nG ϡ)5 ttT+4OI٫aWӐ*^ +R)-$-/W(5GncYhn!_MF| CZB3;8s6 7;=#S0Ê `1 X&LȣycQ@gd"'Sǰy8_M@w{T|y48|'S$x9x2$Z))ak$A1ݹk7M~2Y9TPDO -xLFZ(#;]eF(WD@DE `gk7 sܳolF:d}}2״?#Uיq2w艶.$$«B*2޿C&Cm[ *4;dԡKMaA---rVAnv6 Z׮llҼ<b0r,$@OWѮ4cjZM8FDN߉Mĩ ..<_^%Hs8︈|fdErjt0lԻQpf0sm;8T%r.#eDY5W%Skt&цA): \(/!t|>+} C}kB6%'( YEhINk]:sR[4᠅cgՠAc@RL14n/zzx__1JضcMг{7:Y}f/Ә5y ^?|s'D:2yHH̃52h4BƎK,LM v `*o2sRyIo\|3DvY'ž('1ut A`SgXi2i#WXttBoˍ>IvaLb(--wm\ H,J]&Hsoԙij򡤥 -Mrv@>E^GKa\"OECz{="0) F„k2CChRCEfrG0,Z takmT\,$֖Rppi5.]X9$ihԷ `۠J)X:('Im$`/(!uhj((.DS[Ǟ:hL }oM]<}+pa_iyx]t 9%Miߡ߂8P:7aRP,^Fn-ވ/F$#.YA$MRЇOg¸AgO_~V!&":{B'WL ۖ-\L,l}5#ՉSDϼ~'1pmK'Fׁ)0TSG᫜|[ïCCk]䤥#8.W›`Pţi|w?ș{A"O "^gGDD^.$&OK2Z4I)99wUC]S\<I;R mPX" $"MOA~1l-qOf|u+ᡰTV44h1&-KigRN#M h.d4OJL)jȨJ$RR=J6iEdc$WbheUyZ@O[Al1JJX~*ګF TY hXqFX;cvtБp5XU<.;m <Վ %1fpe4xبV-=ԇ**σfBet/`yB mr,dBJhW%(3[VF 2[ʬ.D*4RQ }ɨV=񧈀@=E ..'5SZ Sk႒*$dà-$#mf zjT-`դmIPb5*'Ӓ֪Ǐ~݁*5/U{#oR6!ԝLͪ^x_r9f`1D>KT?1yITWRUs>$STD#3EVcYi5ǷUPYJJ2<SDFQ M~]<$yv##aV/ 3+q7QdZV؈*I,#PVmmQm  ++KZe[]Rեh1bxaA/>XD@D@D@D8DDDD^("#z8DDDD^("#z8DDDD^( fFbU>iڹ=@GeqOei\(NsR5}X:7-Mt="=%}!PŠ @>0_7S!EJK S#`h ҢJE}%aB}zu^=|񺈀ˍ(T3Um!Y}^ Ƒ3QWw&_a (WEi=l^>\@M,e8{!g.߂2m*ǣwF&fB0ʐ!99ֵ>c]"n!K*Z DD^-D ;Vאzڤt IDATUj?-wr?S3( 4,:8d]:NQXX.FM]#ަz(Aw^:AQt׺$+PO6­ZrL1˅Ȉ^h(':ZT-_-;]6zFh9 V]xíE(j{BMv:a*ERHuM VTZ|6-9l)\3ۿ7M+ʋ߶@\z!ЭOЃ~/s.cΡ3- x?t}XW@D@DU@@Tͽ^j~Jm$Zh{NJz3 '7eQJSG1A<`]8y=Ӡ2eE`PfJl]1ŀac'R!+T"QKKSpy,] ^ݷz!6= $ݾy ֢tByu,v>n$CM{7oAr&Zګco[X{RC񇈀KȈ^pk頌KC*Q\eUJ^ǤxhnޙaԨqy6Č9I4돟!nLկ`jَ$$*ecOI'  O#7GłS.hղ& DP՗ -8O&{?5( @>osnFsg~TdDOD\("PUs/A>)auUX6jcbdEy h@]E>(͉˜W$8{&Ͷ`hj>E%7pzqAJ׸qxEd{bcլ 4P}F$Ο܏7D>Z4Cr^ {ld_NC]?oχoӷ!, GVBL-ɭXO*7;؃PĜ-]"V_D@D<U#ς -+.V%%5wCrK׎;x5wK}JO>OplD˰X~'#|Cg] eZ[Hpp)X#lŠ>KѾWh4a*'|ٯjl {7 yIMDT؉NEH;7Cn^>6y QH_Ob2 '@VnB{o%g{6MS;JpMj ɖd P}⁈+*P9""" Q5WzL+Ȉ^#" " "PQ}1""""#z:Tl@}C@dDbP9""" շ+" " "! |:zsrssQTT|ZtIC]FY1{yyBg [2=MbF:" 2:V߲_ HAJA{<؍Im())xgC= ztLZSOƲP{ > EvA07B?)xMDy"WO۽{7VX cG!q%$$VdFT͢"Č3`bb5 RӧO#u ,,,L1$"w# ї8hLݺ<)QV5j{DT~fIWe,M4u& ~#!eD@^^:1(,,@ff ~_B;TJݠ'?̈os AY8r)Ǝ{r ȶ$ڬ>IR S### TirpËC)ԩh&Vuz^g"5![s,R<X[1555AzHZҳԃ Ke&(++ 7oDDD8,--CCCǘTyvA½vvČHZӿ@~~Μ9#L J}BYmիWɉoS@ͺUנǼ HfFzճ4SS#22$ؑl \CB. cUh2O? TVkwУ=8* Y>l,a4037G&8Tm㿘jYiIRJ)ꢤ@SaϰcATmpqi HAZN`ݺ5wCמc'4d_RW6ץu` Z4o)AeYYٵ ۔J*TЪx#I{asu7^δFIUP>W0m33S(A_WCK)?d.?Pϋ.6 3ƈSyn^(/3 AvIsxmB= '"P3ǤK0n/hosXf+3Q#ǡ+ J~kyN W΃F7;`Tr+>@s/߽?m9"ThQ֥tc`BH9To7X3X~ejj=vE+{dDǠ0:sI[ z_۱|V5Cぞτ#q O@01iU'c3Z9PEq9݌s\IAq[w)'WQ&1*HȎ\jʐ ohd F0ݎr m1`HOvPɾ,ƨicOkQbb(hu%~OVo&G`?}V'J§ǠwнCsXj9sw|D̹3Ѷ+1LbH/IMzd vd9~(z[L1Xe.ەX\[277ԂEEDG> zX2sgIk!'iir$LKd D;/3 O@MKV!,DZj6$f4v4q^I&O^C^Sp5A %cNb{(*SF>#0.!!elٴ bQ p5ӭMJe,[ ͻtFfRTUM`>l;pZ5l":wNo_C'1tBw[s|#N%m̏L*Ye}'_gbμo`Qa`n~Խ3l0|{ >$)#p]hc;#DeOuR͕gܕX84 C&čd~Q"XJO&#_AP[]^'1gJ7İo\CQ4 sB7x<rĺ*pL Gb¹%ø QbI?׃pl*NgS|'R `tU |xv\`''g,%y%Af|[0+$y+\F6n(&ˊ2 IuoL/a#Fn8s+fqKW 4G" B_wd\ =/.-q|Z:7ń /l]#`h =U%\~nS ODLQd ?䗣s.bfĶ$/zyu3愿~s.$J¾l~qy F^L7Y}LHĖR}Z+nAHżo@=om ]r?VR!<@9ޟ0Ef뿝ѥ(ĭ ;Z{`Yur ƴ0\/2 1bw_pd:$Yt?| >q#zcǢO1k/(ւ߆+wq MMݛ#,[Bb$ty)CN̩cqCFCLDD& 1`HCDTX҂8mMV$ {Q`d:QZVFfaR 5pu0$=Q4y=^:X$P^VPmz ͔wGbn!JfYI O~[i!!HuoXa=ãha vډ+h٢ԍ`lGdӅ2Pȡ6ȸy1)ٰBWa:THMXVf2S$塝5y_ʠ!AC[(Wwl *d$9x`-܂sqy\m+ sy\H+~ċLܗ--KϚ TKx pgm VɅH%о#9J<2fs  ~ .N=(7tD3;%Xw[SDMh3ONѿRD'fê-t5p+RE?5nÈ0FdT8Ν p0vjjjB=Q)PBVQ+:wpgqS&^XFB C7H* q#24)1@s3Ӆs6).UuUaG=ʜhxcX$=; hH7bւ_isú_7[`ad$;7vE~L0fKvkkNT4J<'.DAjC(MBliW4Rz?y%pt4yԟ0ß2y*I ~}6yzň_J1"%-X ?LQxH5ܸzSǏBf4zy\+!nNߍO?/ؿ=Luj Qpe>2#~.bcdžX2co,t0rGeFq?4Dqa ՐUdy+_?mA5d})&u'yqaSӇ RG4|.1 (55 Yd'?X=ؠg#m؇wm5<m )%E EQ2eW<VWB̵ӈOǰa6\ l~A!UA5b~bv~On%#޳4ڨS qy/HA[fP׮]v]P>} &&dҬߺuw8~L+/ǝT{$7Zi`ŴZicݚ]U#UեH%؛Ȱ| GyN lGҠ4HFIv oQa6TK$رRpj,{~ZMwWê?Hم1~'txb´wE.1nScHhhKDav:s:)1M I-g^QK]7FD1u{SeӐz4 s w0i MI 1 IC<,ƞ<&~ʥh5zMDs7g9qGN meC6mCƋ)^- "M7ddI[jpGh 2paHؔ/âI-$C}/Üf&>#fV],y[ R AfFI%y5?+< s1Rr1!==!!gBT;BidM S{Ϭg11\l:5- !1Iú?V\=ioaì)+ao=fyEȥr \op-ןY;đ *gZuj{ȕ~d{΁aHH鿶@MR*!=#gPcm?Op_6#~Ȍ~ o 6cH{yq~V6_svِ?óV+8yIx/^5I^LRD~y|-J1~@74 썃Q%Xؒ Z:@&\DB iH6=xعC4 a?w;rS3NyvT6TnX0e.m]i}W0o"  o~5_& !U]eTʪdn'P+;W cA a;fy-ڋL=&H2Yr ';z#n>xG/Цc 6Hi}ύȤ~eԂ_wc;Dgȍ:7{Rz?e&6rbw!HX}HJb}aιsg9''ga7^Kv&LŤfؽ{qDb:5^{S S7v@AMم#26}yT=z`vw>ߋݻ1`BH$x'^v&>wt-ZSX "/ª^R7Q /`$YJTJ#u[ )7{6_$Wy[w m~0t 򄋾,m^Wa_jw/sb[,XuҮ]{a[ژ%RvpZo"awk0滔VdŎzmCB֦?rxX[k; sv144OJÍ xs`  p񫴋Bsa G${@ ɱӶzZϵFdDU2 SSStڝfe$pT vvAD0ƄrC2;q]L6Aݘ⛎'32>e1"~CH"B^}V.8={V ~9OF fHKRrZ&s ?Y& 7"MLЗL2ğ'&L44yc3GDb53Q$~a= =yphvF17 -OJ yIJRM]KD7B˖-a!2~ 9@[6/(kR% Xh wb"#z*\Q̠[qe_.CrkWQ5c-Q0.'sd y:ocjE'#hѢ%},l~ʁn޼]v Rkɷza>]),u9WQQ{`:44 S<ż$ k[n 'ZZvfjkNA8sg)Bkw &Ŀ9ݻq!8:9X QFbaزuX2) ?bڭ(QDZ}!Ʋ2;ɬ{/`UPpŸcfnXu>̚{y?b4>W9&DƪǏ I?1B„HKK..n4UvRfYmj3&@t{&0`BM"1!9m njC+oN>NHSCP VzyT԰`ee!PNN>!+ETU{,;3ZP ,T!CRx(~?_,"Lih}L1TC R!Ll]Jg &=z /93Ijmlp]ݠyDN9b2}fB(pHɞa#%%%a]$y%PtyV)4Z#ROsۍOSXXO?_)w D \t~Xjݺ ͛+H:5E eEbXMCyڛG`Y;>9Ն-E ܻK/f%f -L( =HZ@7nE!.>T"D򴜙cDY5Jt5ͅTreU!K3'N,ČXy= o_˗3bRSX3/3.;_IJ˱~e=o)Ը)dIco`OsдRUroOn|;V@T&0R#sĀmaaѥp *iص+4nEe;EL8d$1T'Uh.hs0.eCl`9GЮU3|gzb("Rl_3 u7wc~'\lT%ȊqV"M' O!%yyxOAA>>Z1 雯%dO:(}XX-zz037,}4OVM6ak0sƗ |#G7QXi9uR=6Ҭ(,r"\-;ƞH#Ehud1F @ca5dXʻ7zR`Hȓ"&!=\b7KHyڵ#KM0m\MȬUq{W`h˜d5vfAsPR *x=4¡֝5Q$ a'6Kφt}|v, &V"wy?R)p7\iU 'ҎInLṼ7爆>|b(rQ VO?̓O4mE\Dr^IǚȽ/&G]^mDFO!冄ۇ zX]}(sgu.\|l+;$DvN`/"qgwrL5fzV"G< `jjJnf\pM(tܳ߮9_Epi|7 ڲo&ajxCbcB/% M`l=r .3?|+\K6axv8eX \KLƙ=?7?pǤ2.>Uct}?MM˰aqoc}{8 .l,fbeʈ8{Ӿۂ-Ñz`{6tg r^xZ8%/(UɡsΤU4L Zw$ݱʊNEd , јp* ݶ} >Kfc/M>,i_/mLָh<+e( fGv_}Cb] fYk*@|&>+nWl޲2r7BAQ~q_o gB?;:(Kw(&]|}f8|("S`V@l9z{~ki2FBjC?ūn\޷/ƢTV~z)/`ԡ>~1R|tDY`}8z`#yi!|sZ~q]N ^(3NY$݊6 N츐_XDԷѐ' VԔ% 1#wX-x*38y,#9RWg5=s*ʚMx@@HShQj8bzi Bt䏊QBilWjEF^bM ܽ\`_m;#6YX{8 }'F>&=EtQkis~ 40B9ꇷ䡈pc씈CV^[cu, N;Il:;Ät{ ^bcRbhS%{E򄁝Mx"$)3(+Ɓ6CvN~SH^us/!cKy<GՋXAѨl2ܸr l (pǣ4l8}W0-iL h, 70#n'a-`J&1n]QNsy$Q3 F􄃶9d z\+Oy(["F9vÙ)q{lv5LJfjژ<_WQ6ĻHe(!xN\|VSyvA퐖qhIXcҔ*RI$A51Xb)j3Ѩ&|{/ŗX& Xi ~u꧘wݽswgg̞?z_8vj1 5eň@SY J:,6V氶t:}| Cvl;/$g6-yq퉁t Y}6k n8Z EPo .q=)Ԃ0R2 CmF\Ju?ຽ6p_|T{ to>{RI?{W.]DƅbyfN5TTeiH: 䜟/32ĥW!j(^] L/p8JVǂ'SLIyԐvjƚJ57BC]5|q*f'DǣI'1^`jjCc'{˔ ^O+MMnBvn08`8 YxO ؉n']9c|s}kzh(PEAFǍP$M@|B)k Q!ʵlO\cQl#8:8c҇[1&B%.;Hk-Z H3Y'\RTRS-h06@R>L󇿗%"7Ҡeط+l SÊcI!* 6ЧGc`E؛Mp]#]=$ QpתAJV6c $8D=CS\! á!B˨;WᷔXlXA+=rRtPA@5NN8ZZ G߉Э)Ff Z?'?ߚ Q}zV=`ՂFjڰOA]o+¿X()*@J%:WXf a;rː~ ~5BxJwkF2.}IDAT+(WPA^*UӁq823n"[XX V 6Pu(zI<&.&>YoFJbb+:Ң) M9b1]t M)4aM 6uD!݂{DjZNy?N7,´O)T) W;x ">}RCmCi~z,5#R(ܻ4bgi\&T_bA)iאVZ^n"S;b'&Hy6G't geio/qƢ71O@ƝP44a/n,GtL,~ڹS?&a85E+F jF\)1ͣRYSt5 -6"o} i1+׍ELQ2yK r7ՀR6AJY3]C2ccь, 9)"yN4[x`򕙻1f &O mYT T9)5S܎; J kDw[e()9[|;n~cn9фD|ᲓAN 9 ļsĿ+='ZQ]~q53  Qe@m]{&ǤH#"k`KNO]A}%fСI$s-p-(9j d7U0>Vx!jfDj)PH(@9YNry#NƬ`DXz #"1khiVMiii|!9zUi8W>6DlwwD_4~ /cQ\]{;i. NűLns\\, &T U >;~#%gױr=(^wGNWiS.lEQ1Um(@_uFC4ek֭vN5[/3մs ׵ ߫q!E}_Y!K͊cٗl?D<\Q{zV'kɫ8{>\-Rms)SNN,"l^\FAH0E|B u-Br5h/GZNgYڵWw: ,X.-Ezp8g& ٹ;"xET]٭5H=Gݩ.k0u/PP@SOF>]' Ds6!t͎nPj(/!CK@fbֵ NsC]iYSg7JVV-?~Of#=9قphZem5Q QPY11KѶ2QKLN(z0%Ǔ g5˲k?r÷urvHlAj5>uj<ش1[bM2~X js'QѢ >QOVs9zuS#cB+5?F J޳hP"#N0>%6gbC\Ddnl^ 2G¾GrL0x?Nbz((FAb<;^\Plyg!ĂB] B(C׫{st&3'aBNl[-L-_m vmYOVaѻ+iM|&lY':X.$e^ƜÜbB6 SAQ3p zH.`ZhrEcOd"!l6%7WK UאxWhq@c|qT+ST*H?7_|vXGA8'Za Y~c7-} SUv+VǓK0]P\ȟ‘Sp%C6} .f$amuD$|{,_+s`[u+ĥ?@Qx>閇@NP,))\q'EL 3@9y/8vpL͍TNr=N`\-2{ LK FPA唟9;!_GчE;WQ^5aH*t5::Z'Yfpp6mIzYc(<<Օ &RN$!5J@?WOGIo+E\ƈɱk{8!ؒQ"ôٯ -Hcw[s4 {c1 M\ W}P$g+^B_ wmz08H%ԉp1ց9mkM8* 3}r&>&  BxR,kK*" Ġ#^7QQQ#a.Cm ; dra!MHHUhC[_~:L>p|?O6R~1)1|bTp8ja떭;Acy᭤[C[nAܼˉj5)~c8 U=A8AQ FiCr[1DӶH{q,,Ӟr9!T"c'aAj)vd{a+Þ38s2y셨3(&SWM؛Y74 G\)hX#a$3BO`{瞣wi;bΠpXc`wлw?"Sc}aJIJtlX`1 d<p6Dďq) S}aO7_o ~ G[*M:\}#؁m0t7-4 :r#)ݴ}V/9[,W묭Qs 8u k[ɪ# M(e|a&EsNYXذ{ 9_aGƖLwpqqv``v[疇mʆ(x[( D7!&]7($m=GNJF(jDb]g$:,oWAgGGG.z``iiEd`PTN-,D~l.>:v㽹Mpxs`:}ˆ+  E.y~98((XX, on3WW7Aas+1 j111%AD)'ZkĴWXXors{mJUrj + &J_D>Kk^'^ )3i@սpڑ$ ;m]}:xϱTʟ2RbBw'Y]Vѽ#: m})-ui{qS(dttD;Q@Dw_7֜}}m,Ȭ {, ›7,|4v>ha%i;*ڥ"QA) `'0آUn?Kϥנ̖]GĵנL)Fԙg&Ə:`ӑJLJKFԱȹsDž zDgN[eXIfMM*bj(1ri%HHgnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE 'U1iTXtXML:com.adobe.xmp 226 435 0 ;`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-18.01.1/images/choosemap.png0000644000175000017500000001503313222767271015615 0ustar micomicoPNG  IHDR@@% IDAThCٓ\yؿ{zzbr "i, mG)W*yrG;r8P&EX`}_v<4#EV/uoRɐ~R͗nbᣟ_=f?_.lɽzj叾:?~g$Zw潿V|kחF:5>fp$9noou>~'+(;UZam6T੕pG}TJ r]v\"8|R@J=hmWKkT<<<@9I&]_^Zy+3Ѡq3~/* ,6 w'SKfq}1:As.wgT(JD ^Sqέ7Kt>0.6si-/'fϝJMXI`h$5dAדbwAJ@š@T(DH>Bkn_Ϋ+_?3ugY<6ۅR4~ful.uX"nE"!)s=P*(\W)7r[`qa;ɭڬ˒ ! Qk7nll}/elׯv奵wNki*pz!yj1K(%ZZŖpUσdEj'RS|eNVxDt1q@ p |ݻ{'r<}ۻU, /K싧wA'h/M)7Q3 1?tuiBt[0x|,\4۴ߟZqE:ABH@~1^z|=rBQ<:q;V<ZjgOF2fCDdajhń()HMk}2fV`BzAS3MpXSUhC;)I*:4CO_+sS9r4pć0 mKF?vɣ~R``kwOpg3&p=a r{+HJVF;>E6躺 > i=!v(Pm򬈱?0&mS5ípB`D"Hzj%_kTJW¡vέվKOY(DĢ#)Lz{BM$%˽YXSH2I>*rA8TUk&*4 (Nv*װ``Z+'qr\(+RH'ϬNN$Xp4-c*l/Μ8J5$碻Pvif\J[2}?H)ƩQD@PPP"=t/4dX,d#hy#*n gˋٰ>ENE!PڵC9?YnoTN>}^z\Hm> #DfI>T|}RmS˵FsVkI7VΥOb #=GwϷff+d*]J $(m;`H@>JBp0p(nћmtHe$[g ^wzCLK#z vK7.=sl>Χ_x/>'cKUUBQR$HD&AHtcB  Ca%Zc)|bp|*@9x N/HzחN.f|&r[~/l&b7_1B}rKB$Ka 8J2Wv𙌾<(@L̤^2 t}3_4Vӽtf6FK+#{ܩ:oomV٩lz<åty!qIIٳ]BPD*o{r{\NɦvQu]rY:ꉡgts{ÙdeB B鐥\]&KG&{svq2t.M%R kM*uʹnoBؓJPbwm PLYienRK|H@BeDzCgf^}q> uV5U>p*w_Yxؾ=Ϸo,LNMdrx$1;6:٥Q9Ɠ4T$ jB3t*G]WS*PN?"dV3a }GATr# 1"zmTѬiq5kgek*}]IJmgB1D K3UiƓ6;qճυgM3Մuv6 Ch@hqOT#"E{TT9/L*5v&XNv @;)9 e:NP D0&B ,wߵSs"nɥMNt#_zV %@H)@JRDRr3E+0]]nFL3lb*B]C﫚u$4sƤBilU!8B|sm E?hU{҉=K %(I[0+5ΘJ[iQ25-Q]/f/@%@^]=:={bz1gd ŀpco~z?u+i+i;%cXZq)p"n:qP$;"5t=YoМb,9$(J,sB8!@vk/ N4z|ƘxT#qqNyT\&TU*r/KwBu.W{1#`Rpyg>:\鳓'bf4RwbڭX,5a؀b.T[}Zh}D!0qqn}P'9}$ R}O ;A,B*B(hmDk@%%pнwQ,хRh:pmJ* S#83q^/^$JKp*%" Q .qsBе 3x!K4w>(K{F-ѥYs~>q2)" 9eS AGD>q?Ob/~p;ӫo}3{t %xtlPn\,ؖFWSI:9 XX,8L\ƉχL~5*w/,d\.~xp<*;WnMN2r 9WH\c@rN`[[6|87 @.XQÈ \AP/M(h wNr텙l׿W,9WjJLLdrpxn90S'޽I^MM( s}Pb0eg}wE 5UpQB42YEA"XfŸ*!UA=*c#oRso.LOp0DFJDpGuhK8ak7tH諪}b]o~.O5VvRcDž7({LjAZOQi'hjgq0 q{ѵ{aA+5nqFf j=z{o_)>Ҩ(Q;0 A8)@h !޶Z$M+c};Τ{=K EqB8"` sW!\v̒;*<woNqI=93#~nKӧ^]\$RJ5~R2Gny;M?segQA- uBf*iOːQwm0ո\2(έvЭ'V&l뚇GhvoSO.(30*.A /(&):A8x}v׹ݿvWv'3s8^;SF^{;'O:92i$0w^?c'grR7HT0 s拓ي>׮onl坡KΥϬNNi#=GգVV*J\3pLڽ'A==:9WFs h_*ѣff&|jUq쑌ytn<LQ' 7ϯmV\e~u|!|e_ RoP7WazTXtRaw profile type APP1xmQ[ =CPUvWǚG$#c{< !ܶy_.?zl@ŒfUp+HblZAZ b/twd'!ՔPp:?y{ p;r] CaмHPu#썻F*cνXJ(h21_oѢ/:N "QY+P0*h?^#¡!p@"VGX2iTXtXML:com.adobe.xmp 1018 1018 0 9IENDB`fotoxx-18.01.1/images/paint-transparency.jpg0000644000175000017500000004405113222767271017457 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 257 222 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}XjCSc?}'?°. ApA|__ h<=LxZ 'RX;$r~ڴ\ZGgέ{ImI*SaԒ1$MH?zFO[ɨi3G}GMlE]\Ix;99C]/ |iTd"Ԛ~]*kX }^xg¾q vw7r>8Ѣk Vw JLjA$4mz;Þ#AhL[ R+0 1眀>B*rIuO6_eoDQ >|{*U𾛣 ʹ-b#W܎9 ^G9 |&𿊼0\khhy1WW qQ/?K_U^l>|?V )SF[q o ł[y, XA 6HsbI /u?_gweY:LhfgR_$yju_dG >}^O~xwNbKS/ӘcU1`FnC|zu֣ep|Wkf]b[c*b _jI_o%6oDQ M3᷁gǗqͭlB+MR o-YnDqE6]s ROxNu]K^gM2fxd$t>l>|?ڿ¯ x_~6Su{xoHvZRtG{lRNX1ڳ_/5 kobV[y2Ka9J7ĶsS&%N[Ĥ'csM{[{+3Dkv~4+.n- ߼F2[>_X5 \\<YH6 ؃Εrn`k>0Dt$8G%Oar&R7%՝֚6*B&]|A[KrU}}Mvۄ+ć H=GAsQ. O$V΍-<_xwP3R*+n/[ 9 P^W/Ldj-bCl "çN3ߜ Y҄oʮsp1X+TqmFm~~i]N5*`(H[~9M9YkQ~#?PRHϑ-援Zq|=m(q|=WMsܤF kxZuue8vę؁cU q޾q|=Q{C?.|kx񾹡KqX-@u a eqБT_>-Pk`Nu F^|T$s@=5; ^(bwQwh>5>=xA5F oy.5)>nPDZ2@a\M-?4,Ѿp@<_/1x?_bG(fwџ|XE&|EŪ;qP:IjZ&:dPMmRXZ(ĈM"ga_føm(q|=Rͭ[|UE,:m4HA챏$|0z޴SO.}+@ԵfÖrnGբFQ]U܎+wQ/1xtw'K[$|Tz[kj]Hj67F낞cB.r=jA_{'tc 3z/o1G; ^(bEω<=g^ 2Yw܂.2rwcapW;Vrs^+;=X-%)Uc `}g/o1G; ^(b^Ŷ?g]u z{%+S0|O +I, }w+ig4V &ŽVvv)H]p("J+wů {84MM..R &DWTS qG5i ֩[Q(1T w֐\Xm {~ /snM'C7%2]Dc:J1i2D|d<6rjw/$K]6m:F WU_80B(88\TOIEtnAcTcF%Kpx~K6i=KMj+=ͰwZdSLdEG s6!||I^E.d{D #gCQ%t~.:][nU{xPo֭cO _zѤlB!|05?s[;NIsq NTFgtDn`J x AFM ;<3wgvfvf$bI$kj>[.̞]4ύkZּ_wo˛cF c/d  Uc:}&W5nMtqx; U"A "3 VRq}E%}Jj i=մY>tCQt ^XX5|6<_jD/72]} |F᫤$Ia86 l@:m*>E`G O;w[y` "I+ AHiV]>Fn{n%y^GVgf8xtz_ xO-i$捥Yg}iW\Q-k7Ye3#ˢ5\|@ƽ9eaǽ[K;Ѕ&ʱ1B| io[]??~Ӯ<%?5C Ƒd|+Ť֫a1/p\e[pϽ|/4-&\-${)oH7Af%%vݰ׺V?ԅ}}ևh~oSuxK M̖{LH6RtGh]Eޗi> N\ViŽ1$ȣN>о^xJ=żiv\ cbU11, F1[?e/iY-V)x,.wi-I>nYcOͷmK;T b4OFyK$L3경y2c´*[G.-n1Jm.diX]>R|]!0#krkj$!OK#?+݋qWG'Zv 'OAmnbVbYrI$n`*J ( ( (im'G|Ec/|M55$uf {v/R$|2)%QbʣڞEyo//L xMԦ{xncW'C7PoCF~#}Ѽ]]NAZ徟alg,(%x@P8(CO?+CQ_1xtxXj6cz֎%xxB#4Qv9#w?ƛe$VW~ ]dSGٯ|-ge$KiT2 +ߗ{_t*[W" 6@yk$w#?ܗ?u zMƛ~'O{ Gth)xnɾ֮>?źׇu HrTM+ RvFUre6j>3~)?uo.# fg] mbVvd)+('+JWo0krhf+w! 1BK:I>5vƬu"OAm:ͮV(3{5dw#Nkdh.L^c/C𭭭ƥ?|P Kx"SQ` _u~54n%H rGCX%)px84wV|7/C/:9X (=Эdi?lzP3ƽBzk*iǫ3éRis 'ELYP< ˁCNk9_gNQPk[Y& =L!` [y&K}#>[Znn!^bs|'XƽBz=s<Sx X V9-X@rG rzhbS-/TPQ}>qo,31ɉ$f" σGJ/! (EP_4[|-uCg{MO]?/[]&;˺[pr9!E@6.AV?g=Gš.d!Q}Y2O ײy|ø#Y=֊:[ߐzȹq̺륻IΝ2ɸ?$ >_RwxZԯWźd^>%K/7k%L~҇8``I m<&.UVukndRFmF#RJ)sh"uo-$',zY7,3v6㍻ssڸ{Vh_bᴛZR Wd"?"U+Hk.C>ESZI+/K~GV߳g5+V^#-i^hm-n핢?xHp0~Z KZ,=]KjF}4X=k(O g~Ͼ76%l|5y)lt sKjIbiB~Cи9!~>632c6R\3ddJ/_OұF_][2&8Hҁg/EeNj{ ,6i2']CMmP6^c^ҴmzSh(I R ;higuhu;T.U@'OVM4_)Y_p$pyGuRa 3LeSr{8Q@`hSG, |_2j$t0/ E6X~/Pӵ׍{oa8{@Š((*i,DA#4l+G#I膷<ߴ|/k1D@z/##@ldֺ_@-[ɮ3E "IX9 #+T4jVwz\#]ݜ(F̤5b۳OXAXz֟7V:y[3q5ݚJJw/ 9 f;zUվxFxz $ZyB$|8P<Ö NO\V?ڟ.~7.U%_ܸau%א}y^MO? kީ隷4/Q[epr0.r@ƒRE O{ *O[6hwoWxZhn~KCc [yS;6X@/ӼyYj:yg{,Qe ]c4G[_[ Z_mҿ >0Ow^Gk up)c>VGVT<OW|.knqXh~_kyVH2FN=q诿]ty8xQ hV|8xQ hT (@QEQEQYZ꺎m{ /pyX,f$~%4iĆSK<i^`M6H`ba1訠V#0U$ɬock6zŘbP˔8+Ys =c~(Ef$5KV#ҦH/aL 5U:7чiPEPT5_iL.Y''D,;zگxM"^Oԑo]@F0$GL 4goih5-ַhkZzL1_^@};<+⟂75KAj,fmFwM|7^vxZ7 ez?4go??tˍC*%Di$Fys]_*]{k_=u jR}BgҪ*XȤӽj6`F}:!w2\2)r7qREPEPEPο5/:=OJK{+,q F`Rم@wcϯ59gG 5o^xI^-зF7KDzG q}yh^h鶚wGMnš%^MKvC!X4cqS֊Z&>5&?>.:y  _ oMWڎ K-ew>-*#$*.9Mm*MN >()iJ"P81P&M}:.-=[N J?|N- /_[.DIhtk3pq;I$;ÿ.>q O~?5o ޕiyudw@*-)u@LF9hmt+P0xbznj-s[~S$v.Z$Cy$rˢj+dnQ<*p98-~,^{O7 |]a08Ȧj_<Gc&oX[5έbDmqRH<h_?;ú楧M[i:%p_14;<8Ҷ,|\]ϬΆdk ԡv[$~PpStc[vĺ-m]-F&hb^*~)1W_lmo;ۥ TpԜ Cc{O^ B WZ-mlY% Y4hZރN WīQ `Ғhi٦|]-GEt/K\zƟohZm8#Fb;3:bv]49 "@2Y2[Mps A9,ĶpOោ<)?|H%ak5ooݓiӒo9yC#$λ7 A5xW-ixTx+U:բmHnlnoޞKvRFk)];r+[V>HѼ|g;xM9m)>b3`NOcX xWúޔih7ZMyKth< 6Ӝ;^5kMլ&e`J @5)Y[)˛>j_K?~,>6c:eW7PA$U8s qbnfU}<~XtU7yFנv[5/xf>E e$ }˂4[ܐ@ߘ#S5'v-Ņ֗9dPB@*IH*mo_QE%Q@Q@Q@Q@Q@~40~c4F@GqU|_ iQ@F?_v#W/;ZTPo֕jeoh57㵥EfY<_ iQ@F?_v#W/;ZTPo-joD7skqtO b|I~;5iojeoj#?4k" cȴojeoj#?4k"Ժ4è^]~ HKE6U8)(jeokIgmoQӕo.m*;dؓzUGh+E ~F?_v#W/;U?]Gh+E ~F?_v#W/;U?]G{x\VM@itj?)#,٢xr]GIIg!Y% r2nl=Q@Q@Q@Q@Q@Q@<_g^'|;Ѽ_sR]F[׶ @pĶ1s׳0ely֥Gšέ-߈ZC\Ek3 ى*@9m_#@뿅.mwèީ'ٖy$?!ʗWQubw,<8`uM4fdᶟVƽs#kKa(., BƓBU 9Pȫ?|?wim/^+nMO9*qxcGwm7u_֚~'_#7w5)}YZ5gD?>Y>dvsm9- ~כX$85[{oԦBܶb]fOjr~՞cĚ> Kk+5{QzmVmMy+3ß ._&ou.m;g!CeB -_A4N..uedG B sjGj[Hյ{ 8]F*H>` CxH`konze2α|͉Їg9cԾ/AỸt \ w0EM.G֧a$ FOQs>xkĚ}Cu%֣kei,]MK$& :OJzi]'[?O/TӼ/ye?.x:2B*/:H0RBlVYl.4ۏ i<"'[ۨTpF \Z (㿎ig:z˨*, p<)^m4KYl =tĈN 126 431 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((s" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?6[ <#K/4[MCUKv M8P*q5#5vJ6ϱ':vsy0B~Rikqyhe}c|Tq/4l5xw@t=+# d"F5fkX@\Ѱwм9 3Bīsy) G4ͽ-*WQIJXf{i ZGȇ V?#H€#.޴ڣiZdv:l fV Q8EQ3Za6* ;KusÈ T0'_:MɶouU3>ҮXZ_Kurjȓ*c,@X8GlQKU/m-gHʁ#`|hR;SKg"c, u>RS8>WQl ZE=Fi((((((((((((zrzW7}ig|)NK^!y ǯ"H ұ)nt;}4o,֐[f |d=FG Z@yΥ ?[]nRF}[CR'YX#~㌨hռnƿd`-MC #Ƈ=IHm,\#Uq4[XZZ[8S8F֘y֏7 ׳-j{y-uw%eXEnJ 'vy RMJzWMfo(u$K3zשz\P8 wKd{2$B=C0?+ז:'y:DIǘObIfhu#B}.%ΟdѕYnJ8m CimaHS.1ҜІ٘"m( z)-,yTvy^)ԢCzրUL@3jk˝Z.ŮuMB 3oc JH;AʞNEztknVWcԐF 91@nu+f@FrsVMwñ^zA~׍FhIDVpZ56VqY޷Hc#@DK[+HQzp"o3<)kW2YMr\ Yg;_kk䅍F$"{/w$v7Q=*Ϗ>Z)$vҽoVbo*$όacNY+ }h)QZVvvQdZܖ,_t|<0o^-T֥[9 [ar?<'F~Qb]:Ha]6"( ǟ0? t6w6VL^XʯPO/v?{S9J(Q@Q@Q@Q@Q@Q@R((((RK&V>aMŲH@$վ+ މ41Op3HF($c-G#"kV$S# މ4/2އ5usu4cce"hc-Zq֐s@YYдZeLrk .*rd-{åiע cs=e}og A=i@Oud?ހ46o#cf2z?]V_L.immg=yCf~~wY=sKmk5|=xGzz%zF>+Gz?]V_@;hYwee?xzx]V_@\Fz{Y= x]v_@\hY^%zz?]V_H.immg=yzzA5eF>]V_A zFY]ٮ߫/GvY߫/G6~^Orz_G0%?띗#Kmk75e?wCf~ z?]V_@X撳Ɓy>,3K4oRӵ)㺒b;. #tҥggEޥ%3#HQ\]X^1@X>[Ҩb]ͬ߻O1N j2kzU/i)nfݧ.[ swC>~?$>i]\>\wccˍ=3= o_w\n:K@*NJڃ|/_l_Ij7y_wnyllAk> Кs#y&1rst:g' 3f<;=+A"21TIBXnޜ3}7Z-do^}5ZE՘9m^SH{纎KXVMQV_m~։d zR\8E%@98)" /x=ldx=Pk"5*K=>\׵hQ2yGI߾\qGH-Bo}m۸cӊ-Vgi<K -6EA[,V[iKo$b二^5i1xZ\[I /upČ Qӭ/5[ F6k0@ .T7_אI#jcm ouk0+=stcHL@ 0y(m+pz}.U[%[]]GK da"b9ۛ=xZvcjX YXOK$xK7pFGJsظVZM{&E8M1qu|'7;My 0H#)'g:2Ac""@M,< nvdq$ܟ (vG=ҤO.k\yԩ ʯ8݄׶w7I>#$NSqNiui"߈t?֮o-lEu;y9oJ~pQ??;DGs`y=p(7.oR[\-nd$G%r?2Zޣc\ӬxkUwkV(>`y'i9tM?QX/_PT2d7 ŲTxBLl^IP gM+󓑃CL:QX]*L/[bm3yTѱEaj^-h T#on,'77ڬF;$g1B.OMMnW<^T|%x% 6+Mu k VSkq\Vs\]+og;  Ӝ_(RQeXo;tu-"Px$2^w>%#t#G/xLC/1*5l@d>S'Zu+/<gwGCc$P% r 8.ݸA5>u }j dhlV@ qU/<^Iq2ݕ{h^zDʏ$(-=MF4mķ0ӤI3d/)bcbh#5k m46%&* vGRa'ro_\+LS:& (!R@t lo ŧbB8S ϝG:SŖ?OjBx-euaYVcGw5iMrgi2>ut. /mܞ)|Kemi%kn!p}r;Լ_G9mn%d~SN9Y%%g,Хe<ԴԟI,]..lg$$"XJP*w 7O!mڶ[E$BT@GR|)MEMhYvyY!ݺ\_h=ī=AR> 9mfv]FKrfQ Jef7 u -OL6v\n c\'Z͓DoonAJ?ȕP_Y!A(e-:X`"Px:1(uv/\Zn߯>f߯%~Zd?^d_z}͒d_z6K=S6K=/Oנ]Y[ݲ1?dƯ[%~l{'_?1(͓O"l{'Ѳ_Nbl?f[%~l{'Q86O k?_F߯ *fAc'Gk_XUd_z6K=酊ُA}_GQ?kd?^d_z_?1(͓O"l{'Ѳ_E_?1(͓O"l{G~lz~\,U͓O"?1*ɿFɿEa7jڳC2=hl{1fv2I;dݥ7Oפ}mwt.[ ֳ,8 *q^'Ѷ_F㽌Bmt؇:v?^X^\εtbgZ77 ?γctRS p=@ǃ޴=cz6z̟E\snqIbeFǗO/Q(`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEpm,PTI5=PF9MH [s k'/9s(+OH_7b=Aqu M\9c_ZmV-OG$m"RmʎޘvAm'EI}6[";]+P^c"nhŸ-rO fTCKm[qqj~iMSpe?}(t?I?">Ok_ F-5H淐 FsV> I|ma_Q[j %2#W4Ύ[mrӯT;g,}l~"J6O񬑟U`?/z[U.IT"aUrNxR$Xj]ZMegku:>njO{O $ɽ@[biml f#gڦnozDlf_a*P~E݁hgkҿY'$Q[Jd\w,M7VvwV2ŧjs&FֵL+i}5Vߝ Yu!&X(\=iVQՊ*=d_VJCES(((((((((((((((LϥŹ?A2fwPGuk-ۼTm88]7 oAǕlƬ{ $@&i_̖ytP1VxQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE Z((EJ(((E%Q@fotoxx-18.01.1/images/down.png0000644000175000017500000000146113222767271014606 0ustar micomicoPNG  IHDRw=bKGD pHYsHHFk>IDATHՕkQǿ.1I BH&EX +{k "),6 ,6 LzI.e۷cqw] `Y3o K09#f[^EnJHnzvf pԥbAȊ&©o:@$ԗhs\[$ tqKEtIQi u̶D|FВX(? d7Ȱxe0t$Q ?jUl15 5Puk f]6|#c#F#!s.yv pEU'' 0gf[z2-ADjF944}QvDs̶~]ٖOD%=.W*d9# 8vDwm}kSf[ V>BE!ʿw|-0M{lQW.D,@ hGݪ zlkv@&g~Hf8"9Ni3w͉GmE3AՏg/X=R0J 'lmsxb*,H3a5- Eț&o]@SOd5'ܫBJ-IŅᩏnY"z Lvg2?k}7W2AZѿlٶt>nlx=Cs 3`PggH03a ٙ0r i9uvf4:;3@F΀A #g 3`PggHoة .vzTXtRaw profile type APP1xePI y94KjPI#y>?:kY3 BYa "HnJC(޿cd$rZ1ɥȒiW]ņ. u=}XPD3+rWǦ*埈~!qS],%KR L5 0Q>u:BiTXtXML:com.adobe.xmp 50 64 ?&}IENDB`fotoxx-18.01.1/images/adjust-britedist.jpg0000644000175000017500000005765713222767271017136 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 265 300 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Dռ{ WT摝#,JI!KIAcshWz$yĊpX߆kIRkheb4QAΡ{pM{߈|gh ] Umb+%:" ^hƾwxYB#yJ6O J!egs5Ķ5MHfc~T|Z.ϨS鹈k㨥ݦxZ`L#,2鸎k i}h'H~SI$]x,Dq#Y+_<\ɫq5͕[ڽʌǨG&fךɷFCS {09 ϵG+=gz71z,W׎oxQ4iQ| fQ\ϽTa.3OB2Q}'?¶umr['5lMgq3$A:t$ޓ@ >}^ &.~#ZG{kã=\&בZk8m#̽3U5_?\jhȗwK(Hy* 7,֛+I`2mwe&~c$^a~bG?I \KWז~gHo!f;dq\䖌($1G_;[MBOnY#/$dO ccq|קlo;? |exN?83x×IwkyddhG >A$W@t C~}MR#;h2Y@þ\r\ywT囱DSvO˓Q~Z#~s>E/ۏ+ѵ/žkS.4K{1Y-,~{H,m9nqgf^~*s\@y`EHCdr[+~W]]oDQ AZWt|T_vqE Gny3+ =i!n/MrW6JhWLppI5N6mvN>qn>|?_o|+xEO K$xMJPuU'0#y8n/ gŠ'C?4/4hSat# 288$sڈǛE|<|걉&Hn<ߑ`7G/D}n}'Z_}/Ja 6¬/7mۛ ZO/G|,u+FU.-&D1$D; `̫5RNj}'?£VKI@18~5[Box276W֭rVXʄl8b:84 k2Q|+Xm 3$Nфd~blɿWK<?Wk 5e[դ"M"Y3WPvˍt:?|#=KŢ)u}?V>%A{cu }^Mǟ m2I> GڼLp_sa2me\)Œb|@mz<]uHY2u&R6ȧoO/._n>|Mwuy挧$rbV;ǭA+|jM/2@-9~8|-Q Z|Q{"8 !:db}3=8Y_[+k\\&6LI gbxg$_{]OR6õ2[uf9#$$_ @G8|-Q cXҿK_<6iȱU&Hc\Hƾ 4.(V |e}NS>Ё(p[B?g~n}-k5]WŷڤϨɥMgq9`Z[o, 2r<|\,[58l8R[^U,8Q?>Ё(p[B?U% >͟ZG6:͕Zk3Mqi%ϚhێvmU.x\m4ËX0Evs2~ozFp[B?o—/>om'㧍?{Eo.4i#΋xVI9xö4avvѹXH0ȱS cu G3>7%MSMH-$(R1V L>Ё(p[B?|ho|G,Z\j k$H|.«ɴ`'66Ю\Ltz@g8R $Gv.mA cڱg0'޿M>Ё(p[B?m~Vj_u_EI loD0*T6HcvҰ^ G3ɂ6 uf?[@ wWOſ ?=@iw'ǨoOPqEp~-]I?[@Q\?}z?.$wWOſ ?=@iw'ǨoOPqEp~-]I?[@Q\?}z?.$wWOſ ?=@ixwߕIkm+P("Ɵh׈4z}4G7aENuyE|c>2zO4XF,.txl!6H̏%1J:bŤk6sk^7zaK1X;pRÕWOw=g/t[VZ1[X{}B5gn,6297-)<^Ӽ0Z_6 xWrDD<%mgo@M5sب8oMb>񮘶2-vѢ42͹C*$dW/3xNJhZM [sؘPQ Ix/C( F>ķZ.i~*O,$xuA8"pqm| ߊqxxJM:)`'ţMű€Tyóuk,ZŖ/ -^-.C$rl'' AAHW`-y634?-!UZu-FHӮm-byErp^Xj^qٓDe>0GiѩWqo\ui䗫=i"Pjuy5w_PW1U'ngB91_s#4?}k_7b׺iY\^ΰ> h>/u;[M:;SJ_v_ꖱ]kQ:6J~# OV݈c֡SmN4xwV^NTrk@+> j=rMVM-/S!;,fHrRx1MopxεVԼ]44c>"i i*v1`s^G#𹿊9Z<>|и~s΢_ ݿa/mz-څP.nndX}Yvu)ƞi#U#Ar:EĒJ֚u|Wv%Ok05 o!dU_w8hVmoȫΛwGZ]MHތXdr2 ^PdmMp<:. Aut;(}#yt]~Hw_?G Gߢ0??O><:. Auuiiv' c '񩨠F?jJ,k3.`_CWV?}{SXVnY`8zM;4&տy7>" Cgna>Vjeo)&7:t19>Ls8lq#~|PYܽm$ +(|%f7|3^%5v\,#svJKK]s|o?n?b^PQ׮1uuq,Vxb$UA;s95'K-gM/º"7B=CHƛ k.c`V+]a <fUo1bxWHּ9.w{Wtm#S:rߧ>r>%[<_ct}!"yX̒i>dmr5[G4Wzx^/ KVL-Il8kAҤ)Ojj4[FK$/+#cHeSP*sw^)%[U  އv?⊵k>^t^'[ 5[QM!D#O$i!ny"c>:ӵoE^!W֩iV 0CPm5iZ'K_y-5!A#r*w? hkk@5xm>ޭCy[u5 '~;i[Fl(N-h*֯ylU_l&6wg=\$^SY,'Ә]Pq$.T Qr~bh>'kD㤦[͹RIpFvL68^Oov$<#4VэⲵIcP yx'kykm6a2CnnRS_?~'H-[Q6^jmi<2J[HX,r*k]/ izxYDYqbId `g䒻yTޙOM]-䵷#9q*3=p|}oA&k,|Cu_>5¡m"32I@x$㩧++ۿjkû Z<]k)/.,HYP;K;"xw|fEvz5ax)Χ(1<|Ǐ58 yXx'V&8f"XU!/F$#pU͟7Ki ?VmJ7ϧ4DIG]윀Y_?Lwh/-kv6.ts;[ 5ci7편R{V|kiUֵaibNY1ܤ=O+|Ok^Vk{t`2"Ha@KU#W&֭xu֣>kx,D[d~e|wwίiTȱK[dhrpW}2]x<+2Wj͕B{[bn`~?e#B/OoƯ[o=G>&SmY[{{]BkCqvF$)X)&F2pf-)#M| MҼ|-2Y#ܞPWYyr;~Xo޵o+uoi}hַOjUB=1^|շGDk"1Hpv)VzV ;M:Ʊfw AX&-C:ck-deYۜ:O4-tywPm?h?4 C?nO4-ugG!/ƀ+ywPm+ nY;E|?v@_ i_?[sJ۟7V4>h'Wu?<ҿ՟C_h C?nO4-ugG!/ƀ+ywPm+ nY;E|?v@_ i_?[sJ۟7V4>h'W n?m^èsneX3k7ZZ@‚8mT^zP8R>ִfk=߈|D7f;q+$W$@ݓWijwoCp'{PH=+O :O Ƨ? 鶚͑vMºEK0GqռAj|;_Z o Kq\Ub X m槃"P|j<xF [+졣q!f6Q\NDռq!4$Aw,FI<Ԑ3 5Uּ#_]Cь:@u8mxSTq͒&(`큁6zUi.9)WG(e!;VPUe<z/ i>/_ #3EHnb~A3Z~/./Gnca1ۢV5\/__ q=wTW z?_?\_@ee#uEp~/./G//Q\/__ q=wTW z?_?\_@ee#uEp~/./G//Q\/__ q=wTW z봘ӠK݅㎄C \Ɨ.vCa]^jvi%ə˖q$ '*t*?yaҼA!o)HQM;^ѵ/ c> 粰C\̍kfy:r!mĶ*_WƷ.5FeuK5̲>ƮYc!$gԧhnO[,q`/}z]ַmw%{yH/FЛ_?i?7hh;j^k-qqwvj坋J&H1Rh|?<)ڽn%,Bk]ZΛgg뺄Z}ԯ~j"3bPaRĐK]<yK`^i]JSts*,?(B·Shⱚ-N;Y1M$+(G(YYLH`Tv5uHN[;Mj$yB&IT*E!1j|Z%ZrͦD+Kc>i#T)$\h hz6Z ; BPA @ݞ(տ47,>#L$!MUf0s3ʻe+bq";'9|&`v5>kM5EB ߻|\}ݧ.6m;mU|o Z7mf-5Uk9m1 `P&@7ޮOC"O?m64uH6elyymKu+H hcia9\xJTd5I.n|hPtm/VE? Q1:I$FȮQ3S|L^JxSK`|][^ypD Q;ry⾣ӡ3]Ek$GiIR-7ǠwCi5fx~x<]go=VŶVmPecbd^-izeƱa-U.ψүO&Kkm)i$k[D# (,OyHyd;ޅ_73!/h<#Mixet25jDc0 λ@ߍSWxGß,/41 ОX.!?fȍHpΧϩy[K4GRIzdEt`!Bvw_կbj$WVť)qkqE,2.taR; -1|#v,~X?P5SRv`f_M<-?>4 ak]_ww1hi:UmqIo,@t[ #nN )͹+HE4 $jF ܽjrG0|"io S^joh@OQAQW){I3H<O &f_M<-(fyҴ}#[dH4}YEUa ֺ+ω,6E'&YCV TG HzF.9xeu=w:d yJ(;ϡqO aE 1? ,?B|?@K_(('u ۢ1? ,?B|?@K_(('u ۢ1? ,?B|?@K_(+^+XRcXAD{ +/[wŌiZ6g] wwsKsvo/֥t}ft>lYb`p}M JKWMw3=>}k?Z6^};ֿ3h6ʟ{x[9)|Kþо4kH4.6onr3dI&aA;T+aι/u|>ukT<ǧ*ӬN˘C`ۏgQ EٴY4hnZF3g2!Q3Td?[hp[C^h-fLŹ ;  o>z_juB,QT-R@reUʱrO ^Y|CAY)FX2+Fyhn[lom4mk9R%7p뤋<"]K 5h,29e8@9xUuv>WɓxPίkζsGi;Ub#  s|h r0'% 끦}+ƚGH/.%A,q+ *.)4/i!o{uILm}8ȖfuhBf/:EPCg~c1HJ͗ %;kkYjI\F;&t+Bp3:և#D@e閶u[(n

&ORCP =kQF$[6 2co𮯭OGi&0qk.CQx$ F[y~28_{3WWGx⾑#Ծ 5MS5B\-x ;X{W77OBMEl(L9Aiwa;y\?[~^@uC)A;s!3q5h|G|I>yqu>rϛldE5(9qoF-^ct .q.=vH$O6R He1ؼ1[]RJ5(-n$y]7 $[C$k=sZxM|<+5nX©h؂;W50YmCGORԍĶh6ўXMԥ/#{~~-ixwA=֣aI݄aCNLCB l EfKRGe$sJ愺-n$bI LL ?.^"H-#,@'o' L8¤ >]>]X3?? L8€+g^1tg^1uc L8C]>]X3?? L8€+g^1tg^1uc L8C]>]X3?? L8€+i^1u_ũCu*Uܻ+?iV0o,q¢ *崗*;9&juA7@ʻ]C UPuA7GUo?iQYW_ te]fuA7GUo?iQYW_ te]f"Il2(]ƱPʺ*7n4쫯 ?3{|AJʺ*7n4쫯 ?3{|AJʺ*7n4쫯 ?3{|AJʺ*7n4쫯 ?3{|AJʺ*7n4쫯 ?3{|AJʺ*7n4쫯 mJ+`o (((((((dcFUIr="Qu+I/#F2M#ɸʪ8[u|]K J˃ 8ŧy60\qG|n)Z]_{=}r=wO95wz/$Swzp+υ!)xgW|om,t)͵$1H0Ѱ_n6 ➗kirOp/䴶a(AvQ?.1B6p!^קF%{TZ2L뷻u|uqxFS >8]N4ce򥤄c(".QqWm5y %Ar G+UR_!Bi]HxRCsX$ >[Hd}pĖJI+*=*:ޡslQK&qkw eUphx@UaH<[{2j<2tum*TA5=RS6z查xwWF{Sk^HSwz}y|!> t3YIS\"c")1Ӈp)>~^z 9n`/ts%2DYN 7Mkku%T~%{Tiv𾯫XKG{ag5&MVd.XI$e`Ht gxW\UoE[kZ|wӞU*T</7$o9/,YkQ~sH 8Aקv>$-DEߓ+B`Ga@20pN77ėq ׉ЏКhA+-I{3(F;+qH )-]W=f i$KzADj;6$t */ |652յNZ~S0iⳒ xIr W?{ L*Dk/^{,3Xb&1Nn<psDj;%{T|%Kt?gc4 i09I˂cQolU|Y ׉k1<}v7gvg9n?x>КO: ybws޻oWoH?__#bi݊o7ʐOG8>̌>%}xB EM@`Xv}[0B~p+6pzu;ZFRFi ~:Z_4{*M_r,QiXxsI c6 ^p=+[?/&ج'Uoihn,S7Fcs*/E=B-e8l"H$s -SZx/ =[N),Vq) C p8n3\q)]i& +#&eydR@3]+ з _M/Bޑ14j+wsmsm 2$"[=;Wq -G+ з _M;XF[p[ٶ4ʑ%?3yf&:i qi鶳+,6/Ёؤfotoxx-18.01.1/images/batch-photo-date.jpg0000644000175000017500000016276513222767271016775 0ustar micomicoJFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 600 1000 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?.ioF|ZUd *lSzmKɺ[y\/W8ă@ZƋQ,mm0I>4=8z+c#˲ڙG;$d}SӠxD?ąT۝TY@\_!AVEU+zcv2WFqڽiK(H!Zpmsʨ,KЁO .2}UNWO:;6ڼm%)5 %Čv2@\Mp+W[:݀ lqqT<]ѤO> 6ߝAepqȰr(']Ǚw:,wtato4rUK?2~KfǺGA1{^j^i2I,o-`o69?w]~=͢]KFǦJR3G0X//QO¾|/PhE޽k$BYe|#w{V/_[_Դ\71ZEw\\ʢ0foq\?|};OK?2~KVjݧ/ hӟ>J`vd2^I^$/Z{*ZFx|:98+;`G˒pp)?~}__R̟|tվ|jwYx]i#ʳ)9  ߏ_u|(&KM,;%2O9=))_R5j~d(T'_h?oj!X3ɝGZѶNn^W#_[RG-ɋG__R̟|uUO{pO^ Zq q(!8*qEj蟴F|.SmR 鍝P,0`EbF3|~ZB__R̟y_0|H]^M*Ώ8HXa#2*A=-~ڏ籱𽦻XXxLr@EnMn'=2}hR恫_>T'Gڥ?% ?i{P݅w}i4sH2Je@Tq\K\kT'Gڥ?% ƋCZԴMV}{k%g *H#^Me*}cJt&kxK`q:4*u&3m\7R̟j~d+/pּ3.T֑nRhؒ'f ٌt.l[ٯ<o&v%0ؾ6\Sqҗ7/>T'Gڥ?% 7s߇>u,Z|:-F,69fߍ |ּoV+)o!3ḐUuTpNy(rnG__R̟x&SĶ~IqbtwLR jfH17[UP]mo@M~K=3Sx$Mۣ$,1,8+is诵K?2~K}__ u}o+\>!k[ؙBIdfeÅ$zsj B/V1񧇯X+4@7;AA]sR̟j~d+Ǿ|{>/jm+UH,gܬ8=b#gb6~//QO±}Mk>3 ?j~d(T'Xk>SG3 ?j~d) G+ghaccqy%Fj(?SԤ`lFZ gU'\m#O 3FoKmKR_9\z] ;&E >^A9=qJftGfϪ؊u]5vut>FwtE,@TYjcCzh+⹻^{jJYȲ^SMϖ$/A.@`T69мczznLѽΙqncrI$jFk>^qO³|Wa}vmm{3_s\U`ıBfٙdhpx>Եm CM#Ilny-Ķp!.Fpg Qǵ__R̟sZv-F )UuϸW\ Zo}kBaɨBĨx={t4rOK?2~K}__~~$%׋ OŢ驨G}1+ Ry8#ݷc7_xsM::'&mGfL_FAaZ}__R̟xNB_x{FоmmSOq `XFZB|͊sW ^'Ə j#Dt/<# HYS#–UvǶhr֗^v>T'Gڥ?% 򻿊^/u_ Cqɨص{7Ywa0ϭxo{ThN+ d~3`9[bڥ?% >//Woǻ|1ŭK2ݴnۗ QkUz>t/]GnFt vW.pJq(>//QO¾bIG/}Ehه8//QO¾Z?i燼/CR4=ψLԴaHSƒ7Iִ=B{;"ںU@ 1$!yq[;R̟j~d+|OG>մyu J]jGGj(.3ҧs[hmv-2NmY&}2{YG$r =%+R̟j~d+Oomsgľ|3gaqkzFf|f9|UMߊ>x/__N&qd+8sl˥7>T'Gڥ?% H֭^ῈQYtkcŋv`lgkW\7žlN_ZI{ke{g(yD g&`>T'Gڥ?% շ~KO g縶J//Wڇ]i[o<'G/5ኄ0ӝjKu^.VSK9#wpJ1M*:ڒnm{T'Gڥ?%  [A&Sij?qZmq學M¹$/Wh+]h]2O l7OK?2~KhY4s1XT'Gڥ?% Y4}9XT'Gڥ?% Y4}9XT'K%n[naeO.9r۱:sұ~}Mkx~c!8jZֹu뺂礣@Z?fA2:ctu'}1{)LT`q옿礟 `ዏ%O'j=?S> š/!EPY8$kԤY!ᑏ-PO?R}xɋzI&/'?Œ68XrS70k=vDddQa8U|Ieqo0], t۷tee;x8 z옿礟 ?b~c+G4NA&#KXmvPE##:\\H&( vq]X=IOOGL_Ohqcċ _i$ h}os\Ppr<GZmOenb82@#kݿb~c(ɋzIK!GOٵ`{}~^z:\`1(w7__ƴΣi +=6Ѣ5=j"DvVpR6,뎕1I?1d=$Q/ߖ_|\O]ggi_%U]%Q!|1.G\s|Oy/OәaQuᱎX1I?1d=$Q!ϵ߅-]{z-{N#e!\8\|Rnα *+k,|&q!߹>n9O옿礟 ?b~c(7:$xmU ol(=}i|<;xS[ k =2}-*?| ;+?b~c(ɋzIi_稹}moQ.|O ndP.0*(XTmQpYSa[ "} ?V. SH+b~c(ɋzI%]>hxV5ǡZCO?K39|\$a䞀gQa^06ЏB{%_0ʗv)oɋzI&/'?ŽWsS{᷊xO>2կWH`Ьy< wP q5koƝ<6ȅrQln1I?1d=$S吹>$ ^k7ԛ·~M$%cPWUwsU}j r7LQjJ/ 9],1'lps1I?1d=$R啭u7^O_q~!du;McI|&Uup=BmI7OĞӼ[:zI%k PȠmL`t+ɋzI&/'?‡=I-#gK _|CmOz=dWVDg>RO=}kξյcUvXAj7w~o34o?$&/'?옿礟 9d<_.K?Nʹbsґ&鐂͒fMe8H<xWР4Uζ]-bw?^GL_O>Y4O:Mx?|a4{[# 0 8^1I?1d=$RsDg?ڣSb~c(ɋzIAljOɋzI&/'?ŽY4F}>?&/'?옿礟 9d:R?AX/gџ~J}$Uvpf-c vH D9Lj屚k|aQ ;{u5ʆIypxw<@u-N k0]dB| Xk߿b~c(ɋzIyU 7},G XkY;jdl͹h%mN`Ŝ~ң&V6~c|ҵb~c(ɋzIWGD͑3JsIOk~x{ÒiGLҵxF]xRjPKr!*ÀxLɋzI&/'?ŽY4JnmbmϾYnc`+GГV:3n~ֶvS)`3r=w?1I?1d=$Q!Dٴx;U]cLW E 9q1G{gx_IyqewTdkYL|s1Ǿd=$QG,'k]|F>3Х6~$mbc G$(B ޤ}m'_ԼdeửYV6fȪpb1I?1d=$Q'[]O;G~C&֘*U\s8=3Ŀ  +i:Qmڽ$)$rĞy옿礟 ?b~c)xgټ7o~IY`4[C~|٦~Wo}3±JCLmɋzI&/'?ŽY^_Zx{>}RDk@j\S]T%|Y}l٦m5դ6mnUHǓ޽7&/'?옿礟 \9_gG~1կX nB@x6U9U~6#;_aB n kPYY JTb&/'?옿礟 9d.hQarI>-@׮XHnO9dx*us\Bmi> \h#T %7FA _Ad=$QG,|}KX5:󢻖O5:eX_wKwg_& h@FЈ:;,Ksҽ&/'?옿礟 9X!©3i|%#Lہ/fGcgG/$^^׼1mqg <2d*G;zgL_O1I?1d>tK~>mb;ȬGfvryk|17aMڧ9r.t[Khe@qOA^GL_O 2A/|ּu/#[]*[>UbJ.,|==Uͷ$]y5}6)*'}r9>GL_O%+[ 4~i1Eo\\Kï0%#>oW߆ l,޲ͬg--Sv x1I?1d=$Q C>ljGL_OhgT}L_O1I?1r93Q1I?1d=$Q ϶]Z'X1I?1Bmۂ 601Zd& OW'vu`ޛ˺aAɖ7_`:[Qq-83UbǠ9owZ?.?h+E,a4}?WEͨw kLN ~lO"ʑ0a_%Al ëѿh/ŪֺP0Ú,)pvu  1_x] 5kۨ-I %Z'p 223Lmxc0--/dk *Y!R$y{MlW X|y xJeɠ0c#0q*(2+ּwxw&x_OѓSUa5֢.$Pt%eʴ 6@H'xvֶVϣ(<;ͨx^]z; \}N9Hb2T=U]RuUl0HVpcFc  OK[_*ߙ=~9AKy-,7\ͦ5jw=6莙w%v%ƚxO`15- r৑|!Nv9}S(DZM SoCP-yimym4j({$a ǕII>Ohz 7'} &qu-Ր6\4oD9Y-ZM UB[VX{{.0V7o./ΛŢ$66+/fy]^u1lDtW &o1#hO˦hnN%էYnƢ}U[rRp9? h/xV6mFCյ18 e_[2`2!ϯhV |Z ' 99X[GyUn@2y O׊xz 4F[#pbY&0ˍP9Pp Y[_KU_>xs_\>Ѭ4^\X4F[HR3Ggp"uMX`zz)PXm&Xe2Fod\# \(@4K)~r{QRPQEQEQEQEkA]kC_o3U'Z_Ӵn-3' ~wZ?]oF qh:ڍ8.,07,i'_|( j*~=_ͨXYI}:}(Zsnʥ܇W̋vd ѮV?G#?4k"k_>. KmɦZ@@|D9 ճTxzl6[O3E&Eu.w!2Pp[%k_w5ii7vP:xVB;h8`#c^pZ/&%Zx_X㷝]G0nW,+[ Wh5OAԮl6"5x+sgs pA`dc;0gJgB0]Cc",?FWj{{Ӽagm7P۬f؞38ʕp 2Y0x:<=fM"%ж(#)P;N\W563ܨmKߏd4m3M#N+m}m$7X>Z|`᛿|1iZuEv F!bSiC(I[ L.~!'AKGW:֎"l'8R%{msĂ+>O3z|]sK=Τ:ro!k.ͶYF*9[;+ M*:6gN低zĩq|cےpdWx-Bjz}jZtk[kcgwYJ2݈~bѰr* MZֶĚiiΊ_C]Kl];Mz&s:4v>K4ʀW:֛4n x4D+m:Wɷx˕ʅzvv6OM;BKsPү5kBSxŏ?b}R;2'Ni,PXݘǝ+=k5ap) (((+s?[RB <,xel{[Qs4vX佷2*:Q"YE RzGӑo\[0.6lq ͥ 3$[nÖ%=k?^h?7M AY2o.جyd|>eeCmcMq \ZwQ|7Սog|Av]i) h,!V/:`mTABsWt¼o vG1OJn$IcT(uT($u3RxCz_t$MS޸S!2IObN;f?Wt¼o 3GKi,&)"dz'b낧Ii,ausw%wVmfhё\V`|NJWt¼o P8/ZOhԠK.gV29,u=z`xEF;I6P`6D#) ?<)nn4  `Y ˚E@~kzzjZjb݊#gvTǍ#K}jí+VrY_*g+'“iVssgi eZ9w[~^>jЦM*[NGl#K?&wBlRRo>>z7_jm:,g!Ғ|Ky\6ˏ=dEE Akռ Dy>uCQ(n;.&@d rk4#u rG~Ej19;G눵"5V)L[id(Hhq^E+;#~xOO$zڏjisR33ʖ),0@*|5^m^45I"Bc#`1J) u!!#P8#WKQt&8m*O3ךq5:{;HVK6!1LȻrtqֽVbmx岼x-nt+  YUQ@ȫ: kƵ\iGy]%,I<%1J>".G:x=4[XFkwow-j 4#5}"8ÍvqKrsM~s4Fı<.ERQEQEQEQEkA]kC_o3U;3>/ѥ<cx2[k> xCZ W}E̗nskora’Ãz_Bb;<卷2 &^_7ԁoxInlo-llu .BFB@r"ԾSUI]N o,IIUIXFO8C{Ϭ7G+ }fwbw|uGiiZ=}7Th]˺L 31I7mi?;xZ Oc#ʌN̜'^_7 BY.o.h=kŒ̷Oz^bb5*A\fxR]ծ$s%ٸh z?^_7 BY.o.1`[}N\Z!&Ioup;1 ȇ GdfkMV}p` DU;;OdBW;rKvo_++[̵dh"b(ZlL9J+k%m՛[VZ;-Bwq9Wu 48|GhxoHXاIhR2Q#oSWoxZ]Z;M5`%I/1%W8z ]G΁\P7wz"-,^u^@'wtSnw\ڕ}qw$W-!';0aһJ(QE(((+s?[7l;Vgq? ~RB&~n?^QH M/߿X+7Gq? ~b߿M/Պ(q? ~y7V( M/߿X+7Gq? ~b߿M/Պ(q? ~y7V*ɨ?cӬbq-ǔ݌}Ӛɸz<_קe0te0tgq? ~y7O?a?aM/߿?/k/ky7Gq? ~߿M/O@{??O@{?gq? ~y7O?a?aM/߿?/k/ky7Gq? ~߿M/O@{??O@{?gq? ~?@?IkjnneVEs.@3v{CgxzNb895t?V??|Crʓ\M/Պ*@߿M/Պ(q? ~y7V( M/߿X+7Gq? ~b߿M/Պ(q? ~y7V( M/߿X+7Gq? ~b߿M/WwQ]XZZyjp{!:V?O@{?`3ɸz<_קe0te0t&~n?^<_ףɸz_gG_g@ n?^&~~'= ?]Rմ>I㳶VT ƥ2QY\z ߿M/O@{??O@{?gq? ~y7O?a?aM/߿?/k/ky7Gq? ~߿M/O@{??O@{?rAʃ\x\d\>%'{@8nx"u ?JVx)h@CRBOicEi^kKإ 2Cqq]^!I 5֞K[H.!ߜ~tS`ǁ5A 7KZ+( 񆣥|$2]6m>D1%0/B" z/xPeƣY;/qO"m/×ڝrxmq~onu aQQ-cSK_xcQ-E^ZIHȑ,)+ \K7b\ /eAY]I U7t*O Êey{j~.]ص=/@Vx'%"F[ I%Vp [Ey#^ ]`,D-V0؅XBʼn$ǖ.C泷oc"1 xY" d)&It@NsIL{Ftsg'Č̡+)K7qxsĶmqgjzH!)3F^HȞTd 8q ?5%[6 T$ ^5+<<|dBTԿi{ Ǭ75}-.{AVmyPqU<Wx{ZAeTMvlSIH$Lu Ek3xQm|Meog-2Zܭ7 a17sY o.sW{y-+-,[[i GenNY$ջko_īĸ,l<9} m"$2/^~C;jViUfoIdxR,ҭtm$[[SQҫ9.Ĥm9w >0PxNM7U r8Iarʐ,p@^35Tr/? /4=VXK\gl.nn#d8g~w _7lwOϥ谽奰0UwDUAbrww%yz>/DXLwz w7qi qp eo(*Ht7uGX]k'ZӞ][]C29|CMW&_֫ O+Z}otKJuA<[4kgu.Hʶ"88hC!Hjvk%ۯbl,׺$Ѹ!X U5 C{ۣ{oi4֋ -[l?XwNFn|]|2ǥO"g9fFNGM %k~.ki7VoZ{ ˨uvi妁6%2D)2)3ǝw= A~! ɦ޶f4\`۬eVhCq[zf#~&|F,I6[i#6jwNJO||=-5 WYii2o+ A,(+iEܧ}moz5e$1Ld / ǭt5x+J_ Yx]O1D[19,H>R8<=w?aԣ*((((((((+?,]Jެȱu_5(dž!Y'f<5 ?\4x 4{hnQGcWS  yǎ>X>/,.2G-iFt8^++&Dэq9 š_ |?:xTn6V-a[lR[#Srǿr/rF[vIbCe&/xJV Z}IRȠ@*{|i}o Zj+RD֒ ,dcU\rŀ\(Ej+cmsm%9Ys4fp1s7% jXӿ4kk#:}#oiQR8杺_!_3S;_Ե+{4٬.-d)$L}F+пj;@}O@ηͩj3" <\Q3?f%s?jZؽ7isYIm$ "&UғaY~kcKح-u[+k:(wesA2V*lfM[hwFi[*>GϏ#G`Z?lxƿ۶<=:RYM0ڣ Ơn.k3<y5I<3iv2Rr \o;@5 0[|Ǟ?{o7O;~6sG xm.xO֡.goI$qIIVYɸgzuG]:9Uզ$(R)O#י>jv2^3:fumE\qQG)aIˮow=#\:lVV.cl*[ su5/M^iHx~umno*w:# 2H0"忇/M\?ľ.Ğ?gVmè9C3K$%QVIYɋ_Og~&?|A?e>h:mԺi47ףOO:y@Te(cc=xFxOKj/\" ܉"ߖҶҳ|O$(9#KOY[x ]@՞(M-XUbqFv}[T\<]J=?Úu4i Kcwns}KiuFC)VnA`H85eK^ӼMk^&{oo|,6曩>mۙ+,yt?[GC>&`j'MDvCC`yw6QAi Ԗﵟt-=}KETQEQEQEQEVw\_A5|2j- ?Mr ZK?5{[QQER(((((((imTYop|\ZPW#O['֭uwhv? :^4sHRϤi:Enc#>Sʫǘ ۜ ˟\[{K_]xKHD:"¶p4O, 1hy]Xv? :;]jc&uj VQ(|ҀbY>Q5KZ[<>3#O~ZMYuGTg lN;]?zxDxYéý5WZ.ZM?') wp_4o|&@I/9mA¿lr1š]Jz#O['c DQuF|Ie}ˍy~c}i, !za/b$hQxuy7ae&hl xMo ;Tz#O['׋x{y~$%P GiLPmRN*޵@xOw4^!O rq5Ĥ,MQvEv2nL &_[[ _A=oWvGc;^ |#jnh/^[)a>r$>jbqxAiBt+ zM&QEHQ@Q@Q@Q@Q@Q@Q@Q@=" Tۙ's$RZ*~d`z1zuwkVv? :;]ծ5ӴGN-JMa<ׁ<Hݷv? :;]KZ_ ]_&˦Vf7Ni> {r#jfKk6M]mGF]a˦W c#Jv;zoXIcٿzA=oWv~^ΝFdE̐$f0"<2:i:Mm=B \ 5ė1@G@ pdG*_,o]ZsؿzA=oWvz/ ŒAM4=~]ka&l,#oMY$ Gn)u]FO ^5, y]Z$9hSm ' E(/^;]?zy֫յsBi_;;A_Yv["DX,Ie>s/ɢ>&ggsj!k[x\/0[y1pe WA=oWvGc;^[<[꺝ֱqy%5/c|I=2}+Mk-M%n"Y.<Ȍ:)|V|=/#ry_#zA=oWv_ |OW'&}n{kߘ(IJYAB\ghxNq ?R\xdk VZ2vn +??iMEgSvn?T;O o7@Vϝ7?iMOtEgSvn?T;O o7@Vϝ7?iMOtEgSvn?T;O o7Fy?뿗Hÿ fmu(b Eb1km~xCLe:}9K'3IMkSdj:=#7WS\<;W5~>vn?T;O o7J9? %ƳA5k|> !ȥ >a׀Bo>]cW^궚[ABʹ"vFnxS|?)~>vn M e;7Qvn?T;O o7Oߑڿý[<9hNM>TԮ?K(HDk?$ y=6_D}6c9WP2G-Ư۽[h5>~>vn?T;O o7Kah]GrCmKCf-|&%35h3M0R|BXMn~<-]F\.|C)kq,28Qx1;JJEw~>vnjE5\E˧}ot$+-?v{Cg?C+;$o(`NM>2h?T;O o7Gϝ7BOtySТS|?)~>vn4(?T;O o7Gϝ7 +??iMBOtySТS|?)~>vn4(?T;O o7Gϝ7 +??iMBOtySЯ<|Xo x^@},_(p-Ē4ѳÅReb`~>vn?T;O o7GTFO|<_Ó飷DR$rD8́pe;AUc@'W~ UդX|<֯q4(X|i  {ϝ7?޿molyO_ ,tU%'9s"Gn1Rl5"ɬ [?NUbMl,q.W9<}iMOtOBN"Zt?nZk\G~ϻnȃpsU}=iSZnuj)$XϽdڃ >ێ9?iMv@O{G״j4y dhB FdyCN,mSCtY>[vif 1x[Zy@tl8zߟϝ7?3/xjWNt.HΓw. D3Rj?5/5@]Gy"X>MJNrWoiMOtHUkmf:Q_jwZݐLh"&VvG2I~ *o}:Cxv&h\G Nx?iM[K]E~.X𝾿 MOÖ>&XxF c/4SZ:L7evmp$JX_^iMOt++r<\9xp~ xHԸXG26mDIv A$\죦IGxV:FQP2+VHXC#4 A6{ϝ7? oKOĽCTuMZ]6-Fie+ {mȚP>hH {džkOu\  j{ $)k?5{[Qr OK{2;Gp*FO.Ht3nZ_&wxZl$-zIBw%OBԟ#]j x] [˙G[k=,bZI{H(z+Z#ר }CHE4-i_%Zi*-kk5l+Q5{Bn$̸-pHäh÷e du^GQ_:|^]ihmkFzϖX+;}ƿOjzfFj]o!1Xð^H|;Ey̟4#~55(M8]p1O? [JC/EZ t,7\nB>򌌖atzdbGɹCmq3 (((((((((5 ?\5t?V??Exy"m"i [NW?-q 0Tb2#mJ\j:JI{ȠE>W,*9AŖԤԼc{E 7Ⱥ%2of# tw~jI[7K\w'Y2O]#W9{h, *Iq\>Ԥ[ ]CKEPNXYiA4_xDZ.auZiʹ>^長e@0%TsBM聴=>!l5[_@'QE7B. JbmSeF-Xii,ʠE\1AeRXÜ _oݴՏZ+e֣j2^e_^ӴOK`@&ݑw gc_.7YuīO6wl#EA4Mfg"_5WϾi#U^h,Eңi#BWgܒ56 imF"fn潰XI3o#, hrU5go*IEyDuKKX4Go$]R3!Xrʤ?U,9$ﴯ$ }Hw5(C ( ( ( ( ( ( ( ( ai(?KķGҵ![h$JmiC!PXp;[RBصH9}t;o-#lݕ$׫:5ψ6vZ[@J-42. # z)i=U-5Go쿭ʯ薉q.o5<בV2Pr #tSXs!ZE->I XD4h=~OD]sk>/t۫m òh@X~\pbPU99@l)7UiJlpU *dq^E>!,a-Q2Z G[  p0kC/G6XQxH5Kv[I!$8 _%UfM.*pG%v_Н_bΞ&l|M`ޙwOlE#˷PBĀr=6~3(ӭu%D|>ilW?jK;b_GUq^ ]*tWe> j+w<[=_~1 g>iQL5֙Ir:w@HyK/fgM&_CX^o>n߰063 wd}O]񺿧jPj};G"H6VV*U Gj;y1%[<fxyiwMw:3K 3++08n/AԬbFhQx~Vt?pLUs2ȇo\g?S@}KE.'˽2-F٥dGl!բ17Sx{ηM7zSY~`)$\d8WQN*/٧VOï xK[45};P9 ct ̂s?MC]ѭx4iOBIK4 SնsZ)^_֣ȷhݷ{P^ç[4XJc@ 9$+7/?Uu ٢+}SW_nJTU +/?UuxNIfMBcR$eʢ($ ( ( (Zo8R4؀XQI ڀ.XϾ7G%v_ ]*tWe> lY0x{-C$ VmC:kZ (: =BO!hxkA]j"=o41I?5+%,8cvugWeI*@|/skEɦYG%ճ"Krp9ǯx'xT$ھɪi62Yiy\҉7,YyOq+?~PAX,t$[ʹbrH(9 *7-K]SKl?.s I-nb\fcTn^hE/ߑ>7oa  Q}4V`HAV_c#goYetL}x}-4fIh" e Ȩ(by'QcžɥqfeG*!q)-6gbVrvjHKFDr[42 [cssĂ%Ė |;2ʌX#>Q$^[_#S <BH]AAj$O9~7ګj7QO[Z={L74 o4rF v5xׅ4J{!nma]'MO)%IGi rG$^E'yzl5>?߉X ōc:E %]=òr@ea zpR^#DYG$>jz)wVѯ/B/faIcd' .b ǫŠX-Ԗ*Iܠn@5LU'E,]XŖ$!ԙHe#/G%v_EcWe> Ͼ7@4V:͘mxi7\:[ kIDַ1,HPEX((ZkezF6F33DP $MP}O]٢+}SW_nJTU +/?Uu+$.t۔PIe`HQ@nxg^?ְs?u OURBY: ϥé%YV>l Tpz⵫?i-7\׶%Å{ $EvՍ+SO4_ ڵl!`5F-HM&zXR^۰px K Fけּ7Q86tKjE-ɿIQ(]YKf3 /i'AʺEo(mLjXǜ;(sM_2SOm}-fI6m\˃+ǠO)G9x;G~ fV6p1adr=ExׇOEԼ3m-湥[7b˲)`im G 3Ȯ7cSY[t X K+r)F D+n IrSm&K__E%ŦmjI+\2qMs 7_ݗ|o;? Mx2xCZ~$$?gJZlatޱH* A## u1w|WH׎7B+]Uk6фYdTrV3RI { I?2[ɭwf}8T} )"FX!PYA 5uK^WԦHTv\Mo$/<&,2=%eowo;^ Ե hޝhڞoum3D$ڻѰ[hʰ rG|fvZwc2Zy>b9H5$pXds^UwAuu_ <]`W+_IuA[Gu467ur#zdZ+iEoujz~ܾTWfs >2R +K௉"α<,t2iu·!I*>[v 8'x>#G|m{?ިq_[%1AAj }ߴÔOaWݚB7<|Ȏ+k`'9iH#I_1x4 FŲhV:NQD|e"H.8˜_K4z][awn_(G'm je3ȱ[¢7/+%?6^NNP' x/C6Ŭ5{(`'(a>+?7ľ!O-Qi N_Gju2\{2- . c _UA1g"}L3chĊq.ֲ,9|Zm?ξ(g#g*կ!}djwi\څc t*QYA.+~/`Ox4[=YO`l/&{{yMYPe((dwo~[9֒94]^Cxp c =;?<6Uݏ[[۫CLj HLiXH-9ٍk| KjZKkz aj s5ިyoXbGFBsmc qO?WgxOuɦĖDgmZ0@.r@ܹ7`r9ډb{ck'ox7ĺ:iW厚Z]ާla>Fs?&8*6od6;%|ǡ]GÎԺuѼ!ۻ=Q^5H  (]OE~ |Hkτ>Nmk|1X"am _*%%&&Yp>}=:_~ĦQRQ2XXS?u__,S*@5}ZLfeopm&]9#@?x3Otg wT3E q^c|+-^(heP<uee??@/?/'ÿx/os,_![ila3YWqIVϱQkmm|-ώ^zp{Y]xQ;V;⸏~~j%i-m4SN`Q'9$e'I |@9Vk与X/_…N1\n_Zw=MQvCS{m;/ZV0hG8oK޿U\o;F&L\`3kSI ZcXL퐮Sx +)&}f ͤh0[Gln{[%h%T/lN2Gg{BfE⶜5itKPx%ӚO[ևIV ;+63(:nb{}KF"Ԣdy{<̲ȡ#fNkz?' CuK8[" D!Q"n_|STW|Ze+RW-ү. P\dO&&A2 ~eG`7w%\rR\ǟ21ZE@䍤cIkj7_ijqhY9&ȁd2 \0hEzyo!z@,- v Wdc[oo$ o絿7^mַ9Ch>}=mn.0Y陌ry TTU̜~ډ4ݑ?{[#}5CIj:Mim}o%Cu*Jp@8kzV)Z%j]ݔݛbKMͅHI'4uB-涾ѯ-KE`%lX+H Oa\Oc%-Ybv 3?xOZ@_c')/:[LխnFC$Q[NJ#bd-*H2z%SBNk75OS&Rc]iӪH$$rk?,]JVc_o3Z{Cg?C:ET^T+YF:8*x5,q$1qƊ0@+ ]}.;iu LhаV pq[#2iZާsd5Ү틤D$^@Q [I4-tzm7ǿm/⸺#Jy$H8݃ <5 :ŝm0w+Q+Ɓ"%ɣa9 j--wo@X䁴 ƋtK̑ΣXGǯ._YoIu=3I]Gi=U z7qyYw4.0-Fh I:geVN ?,@_c'ھC򈊆_MZ%hl<k$o&K|m=j(4mFTԗctUSW #l<ڶ!oAfRş<j:F^]i &w-r˖i#qA XrL֢ןWֺ+C:aO-[/坲1pryzwIT[5KLW}+{xb #4%# Ǎ'KiV,?nDH%L 쑊o@M3Ԫswg< #8q޼"Y.XhwMNcKMr\bХяiCJ,a]ZF,mTZs=rcbʅ]`coʦ IOEy^IKZ  6K2s-PZsJ!hpw SZjފ3-Fh IV<k$o&Kt#/ŝqVR[ %D,Rq^\?~s`Q\>9?ѵԾuu2;$rO$QB SiYƗ^$n$E5p:0pGYp[ū廾{x]*i#"vg`{ե<,멵;(X^wP;4(^xdC`ǁtw7LJK?%mO>0ۻk@'W$j(,kkɴ_H~ 3L<=j0[~ 6ȵe^KRBoBGFp|G-4wod2p\p9+Tå~xVhwXo+gҡdbc,#p~[= VԵ..=+~e|DcJ]sd?ſ|g-RSCk<-,Bqtr gx#6k0im4mn}Vh$#Fq^+ݨ_{uM WR *ȠGy?Oٖ+7[Gzb5s#wX(v;~͵M5C4_-ն9jAXƖD1!`X|=&>LIcqEzpښcE?5 4q%mGN/!?M{"Pmo5K>q\-č C@=q|-d:|v-) "L-p/J(Z;T4UofS߈|exx_EukY^Ot۳argnF WUc׊iiGHGhwr(kN+퉖-H'׼QBoqNlLR}p{tQ][YҢA4[Z_|EO<uh^^Eukprr$22_<}8ꖱF$ .4RǦP V?_n~:Fsku]%n$E QLA kk׿+/V?_|s"ԬM{"V_WmTS 23PN 1Eu{Cg?C+?r (#_e'R4 8ȅAf  y^&񗊛ZeNћOK6{c}.UX 8hhM]Y[ƷG<@:>*4t;fpk;g ޵<oKkQhisaY!# UK6vs|[O>~w?h_&{^Meu82W?*2"HrHq|#?Ckbѡ12 %خGq9xI໋_&WPih[7 <3Եjkh׿+/N˗/!uoݿͳ?M{"ښcE.1!oAfUUmV[ϴDAfl?T j֌VNJiV"G_+OוmEKy4^e弚a-WS,"0ZXr8ACm.5N[Mso ciO'W5 4EIZ6+_gyEpښcE?5 4\gy\?~s`jkjw:๖afV'pQp.WZYXZ-ӭwa`Koc~V7)Xgڊ>hg?x4tmmiRYc;" Y՗ylxgWht'xbZMmKF$HaO[ffdmFl\S/| z~Ѽ'K{ [6``_!G|9Et>>,\ҠM[OK6I{a|nLrq ۼA9qϽQE|͋Z? ~$x_%X;D3F]x-dv>DG8 Jꡯi1J8mw^J?:Wwd>j z{H+LH h" ȷ1-8+V?_- i;7?M{"ښcE.3w\_A5|2j- =q6WRUǭk&tX" ǩ [1u ђG̘zO؟.?&u OU@T;'ˏwtR؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}h;պ(؟.?&?\M[*}iE\TPOt?VwWuꌕ@du+?rt;'ˏwtTSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?G؟.?&@>q4}jSO?]7j AJ4oBGFӼGhfeLWq!dt9W93W]RBs:6 ױ[K -.`_D uh7~)voij^^ah(vsHأ̖F1m=?5B7Ȯ$g>~Լ?=ֿkqnsj3&7>麾m'ς^]jA:z+;Hqܪܱi_  ?k %PK lc7#8Ez~'- S'C3;\Hʟ( $ )5otitK$-⣮xXn,4y$n8^#ޙ-&],"aʬ4dUYr  OQ]~vy4xxZxw_.}Kr-k[ 'PDd:8y SGK7,k_j;\Myio%[%XXzkSI?ǬsX3 ktkXkc v>]x*?zq譩MjsF=WBQ8^H޳iKsXXKʛD9# 3q8RuoE2h~eIKoq̚P:b"E1 a߅>i wqkwLҤi[&5|Fyۧu]J|%G x V1K[TPr Yؓ _iř2"#Ċ_8V-i;knz?k 3Pb. ԵKNݖG"$pd,Q0 [x7>Qբa c,+!4&2@9SЂs~,uҼft R5+K)L%MF\mfYwp/o~$&M,҅¥ۺZPJ lI$f[[Z^d;1|-ЬLVuKxaVe]2L4-gψ+/ Z#BFK> !L(fF3^_ֱ4ҼEo%?,5=)5+ `& ++q\gf4𷅼I|z׆í$yKw)Q\(L2C4P[?Ž~FcV3I:nlH-Hh˕S8e)M 2hWkLI4,XK˃]0}}jIxxOo>&-PJaɘRU=U_xV}MojA,&Hm4|h!'ƿz*k?_֗+*MsdySq/f/|Kg۹G5k:~*\}PHRd)\Rx'uv:BEZͪh"`Zux=" 8[m䬸y7t C6]}/m><^ImCqiOql6ʳ)CCjj%7[F8i|ͅNR@ۃEK_x^\5kmKI*c{]@e_4FupQrn| ;[˩-Σp!m5af( p9#u8h[mo~E/ӓBnjrs}-I72[? Vo2NvҸK[m.e-u+;i-"Y*_jy 5>s3i0%s_Mt`b ā&%Oxi.Ú]?冗Dt&Y!gm)ee qX(+6u+tw;=/j[h5+Kr#YhLkGUP L_l|W^1iew$FY; W,񵶥⨮;{]B K) 3[qx괟k0[w]jnx#HHY+%C,&0pO#J_;]X<}+t]SQLZLڡ_Ȑf7/,A^7MxsW5Ho}43yҼ#4L' K٧WM7 \ phCN V l8ykZO^($^&݅p'~d<Ź€zו[C+7 N=//>nbfΜK`gBVMKW5,tMjIm딎gD @bE9U!wkm|%JwK#˧GdYkH\h\ϭk|Bm.ᰱ2YV0 *L犏FŭZZe ht̸,TlN+z.j -W[M^k$6qyMjda*ɽ6if-bmzsU֧<\ۤYaQbTn q4+u_o}?[*0ھz lwk7yRynC;|l6t i|Nxf#ԧT2ƅJ6bFq`~ʗ)Xi.PӠ:ݼHY)<#ܒIQ M1mF_Gk4oGkf6dv,qΛ@-[~O6> xZڗ4 iھc6-0>{T_ihV"P4|gktTs*Z[KFޘnX㴓AzelzV3-q4 ī21h~_IP%c4v7cd`i"AfE nM{MxW?fC[xO2jDW ,P3|A,T#j3'4ݭ勤QEIAEPEP[:4oBGFx6Vt~sA[7@Vmnٷ?!tТn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n۟ Тn+w|C͹ +?6?n}º0%JEPkt?V?? =BR ^ T95]ɢn+w|C͹B͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB͹6?n4(۟ ?n+w|CB<3/?\mnu~VX4-oBGFk?4QE((((((((((+[pӴ%@rAF03OoW9+ld[vI)]4F5*#zw?ksCD?4xWHm{|blj9"̻I>C2"/_1gbV~%Ιk6aT8,cIIvoՉ {#zw?ksޝ=OuzO{{($1MĎK< 6Jrkkj:GtNj:3K#s$Nhf(\].(݅v{n{#zw?ksd m$F3j:wV_$kךO_X&MApbpB8*3~>Zem?ڮxJ _"gO#l[?>)o k._@h`gop])rprpqo_iGkkeopmpy$I,gZƱFHW[3^8_.h>m*]I,ɦhpܭNv&g ~XoNڧ n~q⏃zߊocfۍc_nl&GH [p0 ?/?zƩm_RԴ ۝c(Ɉ|黕'TG#zw?ks׃Ǿ9o|A[G6{[Xd%?0eP>ݜi>'F_÷6 "qV҂ZK +qo3{.gPN\BҭayagM^U@I/{d,繖 ۄO8 .Cs_"PߺVj̛VS+?rև!Y'fQEHQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@z,Ica#9C`nyⴵcSGΡ+W_ <IP_iif`>h`}B<90Oyj?]/#z?ksV&›}WC۷t߇uSuKnX@ŽFNu2=`]>q5{,wqVo=?{j5oOڧ n?Ɵ!t/ji kooiS1u lKߊt<*3j~ ]:d[=˫ėJJ Pez[^TY:E^Nom\F%q8! W[_q-Mk%1j[ш}řzqdVoOڧ nWºOÿ뷺dZ<6?>hRG)eʅb6qFy6>{}&yixE"c)4xP93W}n] 9bH[o ?$h?-h+­aqdtciq{4&G"KY)O͒F}ʌ眖dyڈC ;Fv6NXNy&Q#AmG( БQYso[[K[)%eScQrns]~5kZmPc: 459ِ̟VC Ѩλ_o3Zz֒G4M 2G#Sl.o/ :,@oi{<?ƏK4E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?E^ĽG%?W=F꺍v5#;V  Ehֺ}nQ$CVȬ/R>'BuFrJFj-㴻ط ));v1Z+ج<;E{o;\Is'BuFp7uXiwM6|5( +) ?7|Ԗ#N|?ivGT;+ALrHUs qk#t/[5wJW7BLƙ\51>>QXLiiHVhcX }~SWx;ŗ{O/$ɒ@UTdWj Z:\.IU+ Iп]o3WuGkNP}6Dv) >FxqU>7K=O]*{x+)}ycl?Hg M|3BN&@f.uh5I?ͨ! r)B[; PxGai&}iv1nIп]o G?$_.h,r:Nx{O=++(=n[Hq}<3/?\J.ӥgLn1δ'ǦT]veqh 4-!HbF\O΀?fotoxx-18.01.1/images/bookmarks1.jpg0000644000175000017500000020716613222767271015716 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230-0100Fotoxx:retouch_combo|resize| Fotoxx:sharpen| 5http://ns.adobe.com/xap/1.0/ 308 676 C     C   d" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?=)?3\ƹ=>SZ)Yk9e@T;uW|uzE姇:}"]&X'XFW1E艻ڶ4ռFuOYZٗwgȝHOH56G&x^j[3CzU`yq]?XӼ{n}kߞְ>-BǓJeVhrzߎ~OQti:V&ngWL3"_woPyњw~|>>}k\FkGwx0mJx;{O͝95}vILi"k j`zvhhdI#7ua]|֝&{;H4f{;G Oocsh,?A4ג>GMAt*QS(&<# R@Y#3K."04V]Z#;N'yǽFʭ|WIa.7.l 3 $67!_';P>c&3o꺦hsks!q,_~(fL9IVV-3Z5 k"yk.-,r:7?uuy?Zz%qhkP{K6;gyQ\7" nx멲ռ/xHl&TeC֠4`Hd?Ͻ 3\.ss{[yGyoZD0Io('֯[V'4;KHs}m$'f%dq܊jx@ԴKŷ?vC[]7";Ʊ:Ց#YWfmC 0ٔ>7:3\ũڭwro- ƙD|6yj?~W::[+u3ve[g((ȮjO9m*4ԯW]ZK8M/}W)aZkk 4_پN5^5[e؍K}q֫COӢΣ{ n.!hmk3ZhR?)ӭzhy֧C; ;Vƒ[mpc˘y ǿuh֞8𽞛XONɪN0߽13^YmGI>o<+XkPͯH7]!Q?؉ƻ?<5MŅ͓ӹ')"Yv#3Ś^UuqFI]]vR H{o.1x6K#{G#@Ȭ ;?j.nS->ơ}m1캽WF;ucj߅{[5五Y'}6P>c&:|S|gs?K{O~=#ilWhzi뗷Yk7wZͨu \BvdW}ցis\tA2jQȊiva?f\]s?RMN\8rGszʰ& ;w˷@4fo}6/wA."Zzy/s'C΍}Im/Ơח<";.!= ;y5=c4=rQAo>s<4!UTUK9ti5K5X)d~]t@55-khql_ȓNMɳzWǏti`iPPheH#-iq߾24WWź7ÍSLm=G]ԯ-# zԶMd{i'1f?i=4;V[Œ3X1(zG] Cy5 Ѡx mgC0i:i6f9u?9 @߾9B3Kⵟ -Uny#ePwDO.ftL!m ]USKv-e-%荼2I(\g| OҼ={aoa&{m4F%fP]zg߳< ӭ=uA=Uy&tI.ݦ(yS܁|cS⎈+;PׅSRӬm ~eG8vوZOORWOG4,nޘZ7 =76Lp~?C=+/%ϋ}wN0 c1q~Լi7u;{5͏mv Yy޹_LW|y6ǭ 5ܺnm]IC?J|Q=Z/t[{JҵI-hDEyD|po,i2MzN?7uԳJ("NU P9_һ KyESZhzW|EiL\*yV@3\>$x_^1Gl<5{irY]Hظ7ۡy# %O#`t_:]G%cΣkv=ݼ0 0[ r"rzo/ j~ Nk:l^O,9 #K˱Yc{ÿ$]1NJ쵥*C>(|Ukki׶vBRZWv~b~˞)nu+S~}u[M4+bf9뎟MQP>mӿfOoDU4}V;Q`}[q3<)NΕi_z]5Y5cgGf_zZmBKM9fj& OeAu*YYYg$ѿ'ߣ- `tY~j)㯀ڕms5؛Sm{DO1Kk'{zKH්x$1(vU]hO1F()i]i7 fܶ+Vc%c,VZ?Pڕuo|soC!mGIIm@kZ/^+ԯ=;ZCxVkRX!Ay-Pp5\JzkhiSyuIB4}/ gAWwMKHh_SѮu-_U +f%/:e'ѹLJkߏ5 B Njv\:#}4<0$0u6wfoHմm6mf=>Uf? kq\h3hјI4q?m6Prڏ_F4w],R}Jͤ7aVtp Da"0NI&OxQ?_cӵ j0+ 6bmsCT .O8 kأH]|۳jnwqU/A]Xשּׂ|@K[P|Md-8F';9u4{ɬ;t 1GEvүh4?-6O'W \u-ЬLX)ǭAF_ķo  =QR)I,lΈ豪Č6mzngVWլMu}b#6Iٿ^<_?l&-JPաU;׳ #hC ?$fKo*IzGt|59 ז'+N;'ng ~T|Ei/n dd, <7 pas/*Ŀlu,5o\jzD7PeAfet ^|SyouVʖ2^W5\lM 34zq%M~^%fѵOx?^XΖU;_f͞ѯ7ط~ϺԖ>@Nn`mcO5%q溫47$~yZ|@ֵ@w_Ql?٬SǻI%r6k/':<-%1U³y.;Y]kA ɢզaR'MIߊ]h::]k+íjI -iI[U!w%rwoxX?-Oi>!9$.mA`sϷ]O<7UuiEyX[+e}GvcGMkToPwN|@ȬY#UN%v̛:{W/ŵ;Jt#$'ޖ| 4k^ ~]WLڥuy?oetjG"3:uiyu>K>O'K9,{w23X*|E:޽jfPN}Rd$$^NVfqR'q .2m s:gu}~xφK=CŞ$HoPE~e1e5:_=ςw-S]6g,u>"o$ucJKvv!aj~,Sqs`e*_)b? ^úeZYM Oң ŨLA̿?Z w_ֿO_о.O=Ig[Zi$i.;#5;uF=3LHWԮ0" |!㎸='Ykeq۝TQSAlJ£lzN:\V62B;n_+Z>'7ĚU#\v-x*{HKs^ۤMm:K3  m95Qk(m~OmĘnTl[gk_~͡gD̬ Ȭm&oqR#Ⱦ4|!:AўKni- ߾kh]3kǨxREb4F6٠RհwxO}^9϶=u-fѴcIlᱵUOy0B'OZ?j~M Yu{sZJ[4&]_o^<Ԥ@{Xg?xzm:VIr(GW7WELmeVvzy`֩(@o~t[-:4VeL+H_s%Y$_=^Ŷ.iitaYKhRCAK}?}~W'cH&dKk;7*ǟ,Jzܟ{em[Y㸄.r| Vq_(x?{-VӯgMYVx{م'$az#xG{OjzQ[_ljHn)L)dra (J4Oɩ~i&10ꍾDƷω:T^ ׼Aqeg d:MՅ&(c!J[-{ͳ^eY#ZUh1REYsEjjNъ-HŠ(b(X>Yu%4B3/Jo4?xW5M;GNL*M|yc/)eXFWoU ^x/uoxFf-ͮKm { We¿./-Jc%&2E*9G?ݩ.x9-HhoDpl1^kz\x:zu༚7:9'A2v;+#ZZ5OA MvlXC@y7dH g|ln5?znyP?5O)cx[Y<1k}m3@ VqlsfWvGj^"#7=_[&UGQmWi+C4|o&$vZ!x㳐eS2?Ȼg3~9bnan`>BܥÕ)m+m:ƹS|sصll=.C$1[Da*³!3 >>oS[o4MWVF[,=ͪ]\I|ha4O㔳5 _Z:&Yk-6f4DRV?:kܖz|7ZɥLcb.A)̬- w1ZZzXZj^^K5l'#t@0G^Qok>k]K|bPe,&(@Oهm{KxP焼a/}jS<#?Io#!2LL lc\+ȧz#Ƈ{^3< 4z+]Ӯbjv҄ f`1Y5Sm:M?c:U:W(Rܟ~1}L/eu]BFɑL,:|GIW<4xŗ2\è ~v$D]cFkFG<e=Ɵ 65;E_7[n~}}#:/nO}Bk&{t+v(#5" 5=յx|JHT[x- xo3M'}A&|z=xw|Q 2] 7O3PgɁ?@Q(O 2KoVtH~8K$/vSnBԚosxEKa3K%ė VCĜK1IWwxzg|1i{x}7e}G2ofGs$q7{.jH־x7m2d8U'#4ßV,kairsjſcyDlGj|?znǩϯ_^ bK+/uHGbTk:$ײ\4 Y-Α wQ#]%ãmoD?|c_-+B5=7|ˏ-"g!2j%_qxFm jw_jW:ߠa7y\"ڝ]'U7MOM[J RC*Ȳ+9'+F]k [V-}%c[:ZXKi8H;7:b^ Se.cxė5sy)KB6y?DC/f7Ɩ[wD6KHu&߇^ uT/sQY),RG*K3>Z* N+hzo+c K׉d۪ۣLY=EVUg_gKÿ6=Zxxl D6~ gO +;{BhgP]etu]gSxH5ƊM֛浩Mq+I2<$k\W[B 6R Z[W]ĺx‹ɦՃoVH>`p0/~YṼY նE=F)D[xaa?&|vfᯁ|5 rOa-et^\OͲfv[x(\*q\΅mKĿ59W~IkgskhLg+ w#zrz/>5;TկU7Z_=lvo? MS%;{? #L&ӡ.ͽޣsvA5ANb |" JWn S"7U-*`ӭx4O|45jZJx˨^Wl7~y[o({+~\j 4W֤R_:)d粬bO.W_P H2mc%cً+fJM5|.Aw>VJo=TB)v Dko@k%Yd̹i$iWuھ5-WPl:}ӏ:G>Tck?J{yO_[꭪iŅՄs/y!),a&_q?ŗVZYX=z['Kk!v3~,)?Ğ#5sj_麕nR&Xyc>sʟ1=g|aZźt]a|96mUkig$ss lI?ZZN1JD- KABbR@ Z(Ÿ 9'[PǭPXwK5/>L$!FVi={ſt 2[{Nuh]J]=Dh6?)~E& ?zuՖZ}Γg^_B mnxiwI -߻;+?L-t=R{i/t-QZ^,6߻o7F?J|m=3cOXihcy ' uFW??:G M?ƻO!.Um*CS$_<'&lfo>|V񿉵Pvk" :͔cͽĸ`unj>;Ҽwx^Kk{Y]_Ep|Ks}6z̕z^k{kZfnefdXcxH5+#{|5kvz[ UoǴKxܹlqbgKGdO7yrRxoZ?Ce=U"O5w_|[?ܙn7ֶr堂HVI7o%.qxwW5łvЕ>|2]+}k|Whh g-ԯyro%Y̌Ċ>qTֶ!=.t^|qjrx\*#ڿ|yg]xO FGlh7y_yBfštaDSDZ6z_5XtkZUu ?!F"?ʮH>ga7: Co"lX5;QX) at$^'AikUӴ5E /X[Kkob!ˆ'{pxhjsa).mgnU甁VDbDWEm/NԼyz[.MMoJG/8>er?+yEԴ-3NK7wFTf  +;COxS&ዽZ<Ӷuǝ_2UlxM>GIgCwZC;)/t1/fvxK Fa7qf~z$3⻯^)mu.o-oRYgfC"V7O5_VkMӬu?_Yk)-Eۉi#̲Dcʟˠ>4ڞ໘<䭅q4vxrEL|- plO3~f?ֱ,_ƚ߄OTծ,7yh谢Ek(kx=9w>'uT4Өɨ #4a'<1ͳuQ/!~@ǀ_xխ,-Kg_ b/'¿x;PtO[LEy_u4 d,^Gy$;W|`rZ@ k@]N0\%C$ #7Aᯇ'hL]Q|bŌ텍8 N '3B} {lDsYαBi;w.>!t1gO鍲"Gm1ooE,cMߑ+ox O [=š֭E5Ŏ+\s͚|xZjQ+N9ld ~gcGS-?|Ye{wxOԠsz=OioD}^մ2SX䵽76<*K >?|;Tkk%6[a?ѿ埥Aៅ4|7|// {fn$"9٥*Į"95Q%sy6zEljaaIci hM{x3Jh'B"m7:U{o~<Ķ_\iQ(5xmKI7UKVMyOY밽Ccs8g[vWYȯEٳ^ǐ|EmW63`wd%t#z3 o^jWR-R؉t)SKbIϸN\l_o~~g.XK7yl)nLmnQdB~n K*<'W՚-RV+GS䉾7!"֗~&]r] UѮ# i5 9C!~C~<֌e{隝#g֑bdw8$*_?#x79>2_tfVk8p=)._֥y#h"-R 2L9 7L*hoO;Yd.mUt5 F}֫|iI<047EH֓SM׮峷A4{|ᙕ)~>'Ҵ>X)LcYYY܉!g>cđ&:Qgj|% vDE|RV+lQ*|r.{YMӠ|QD9d'5_ _Aη'ٓO`fi8`Y"U2'5?/KxsמW&Oټ&|I介v4̑G[!/>?m74˛pd;d!v+5ivwvd6t]r-sezUHC{5k\ZٮaBeB>jO>' H%iYWB9~?is|.4MmSm5"+,&c@wSׅe;?h764[Ŷm$I"}Wm?_G]5V}5I_GBvDݿ6.𷍴xU]?^#jMi!Zo%=?oˠb/w,(%N|߭K\f-JRmuۣ4o1ݢB<+;x>]Nk..vEL~+^-Vw]𲋍]t|vv|D vΩ xJ{Ecu<'γFxuVۉ.",oDWϿ <oPM.?MvIn&3@$W|CA䵵޴ WXJ"mTdd>[~j6"iou}RQ[ݬDcȈߡ>~ K xc\ ͝䘲M2_<Y#*6L'OZu=JhV^֎4Mi>֌ImQ2K P}ҖI$n' QEQEx 9'[PǭPXwSŷ7g#__I56I87V`qm1k\þ7?u ?/URvn8?Γ~,E~f// :ak=Gم;9s]'sZ-ހD݇Լ7ǩ@H$/7? f~|3𯂮n/4翸#ukx#;v _%xXc\м,uY,}lϞе9Fcpxb|$GGZ?Rnuu5ln`-2&,`'W|F CVݧտgBCj&ݕ=ɥ=;-N&]V!ԛ2H6d$(SPE{mwJ-wPcu劯}~^5JU-e$MrG}~ӵ+]]ΟiogW%bUc!t6vºuZJԡk{Cx.JzT|ϙ4*Ko6kGsOc\[ !m|GFW|r-Ͷ4FkuD]̖шfσ\k v7PO& _w~~KZY[[sZ|TgU\]ZrL̊O<~@KO77<&t3MӮ-`5Y-o/|mfIgc9p ~ ~7^ VMKV5!d7f"Rշ~T;y%nO柪NkC%-gȥY<a t++8\y|=s]XIswIZ2c@䟻]M)S^iJZ ( ( (s/6x~?iz&gh/yIǤ ׾V3jZEѱSq~q<xkSJn4ٮc oQ4c9,7V-UmB_0Ea,1Jc1y _)8c\uM8o7Yh م&Y|%+5>֘Z\_o9k B?vS).ḁb`4&x ~/0gO:O.<1e}-3R H-!O, h|:T[mk\1ȪwduOCq[bK8uiKM,Hw}ù5𖟠hφdk)oO\2 @U Ɠ[MZJj`-qy@MF45h?/Q!xfUVFmwgد?䮯_wkQk7=庬1è]s'OYV6oʪ-c0Q!2*!> c~9[u_ {k4A+ 1iyvm|]b:|I,3L.$[=ۤ9J|[W)= x9,쵍d;\].*b,8m~ x_L5 ._%Ha_oY;GχPЬw:.kI,ɰ$gvmS(+OWo ZCX_xh2-mC~Ti\2NDs&xR?<?4%hXui#Yl7yLvxcT]K>millJoΑ6ʹ7O_M_Út5]3/hWw|zԾ \*ʺEmvd_, #oxwh擨LPk nhBXq#=VbZ=|YCk-NqCI%os^;Tkާm3$[F+؜'ʫ'|>~",e򣺹!IKP;lN:7zl,++ԭn J҉!p~Z~9NJ|?GP^+7kj'"a ;w><ڻ9; ϚeRM[OTMƓga'5KPg-DC"G(6}pS?123jFе:%3"W34n+96:N 6ԖRʊ;;XM\KDoxET}ѽx(WBܕ:_zeچ E&Kk*㑃nmS?i>"ٴiui-M<[04/q_ ~/^Mz}ލ<" J~}yݳo)ᙼNu$b[KȢHVf8g Tx%uڊeTqŸ0/ 5Ƨ⻏iދhW\Ga|׶ Y%1BO XO_A[;YcMC 'ͩ %՝z?BuFKftr$I"Vgx=j헞|@ P[o 76[H_k/fߤd)}Jun~ؑq2;oκ xb/Gw-E AxNq}~u]mu5e!y *DӶoG5б|Cm׈u[I4rLrsD}I[ݷn}CE49.};]}kPyn`6{sZ嗇tXҟ~q0MCnj*nt OGhB9(ll?|Sk=V`3tY㿋MR&}Ne+xeb&b[? rhC&s]'߮\5R6 ϕq$Ixx|s|!دm|;5QS.1@ţxOE庴B꺑*y_kԾhzŝW kMJ=Xl.Vڋ[59uɾK) _A ƝkC68oj3kAbElnfZ[8=?q׆`Mu-6Knw:8.']v ^iZ6څ(,m]*T/(ٲYH HM,$ yx}@MY!mjZUj~27!>gJ߉Zzt,ԭa6"w»! ߿Ֆ=+spZͼ_( M۾)ynR|s^mc&ahvδcxᑣXA(GucǯtcNӟO0%9`>ᯎ k|AcxuZV׷;b7}aoj<k7-;^.]N'%M=ۓ<>~̛X:8xzFI}o}ְ>q%vy>'k~U}yt]KM."Q6\lAiuk Bz#x~t͟7'Os>xv2qh7UmbZKߵS6475O} ԭ\}ЦE*O:8]wr17_ RҠkb""Po$uX*?hjzƽi6nXZjZޤAD7).SWw^Elj9nl EYe= u\V/LYaGyJm#VO:F͙̍xp'eīh^(ү&.;T:9}H>-OI6o*)5m&٤ +t_u՞i [Ů&n[w'(  BAoj~(ҿu ]jO[CSὮx5O Jy6_dm,?&{Zt 'O^`9%hd$Gӥo|G5OZxoOt뻍99m#k&1 n>vMϻG,_k SmcI'7Of-oVwZ_m'{#:7nc2q# |wjZ^,ѦִNQZF3mѯO(Ӧyaa!a"6o-}Ǿ ~ tLЖQnos43q.q_KR FsD6 *FX"" 2lF7$iVwWPlMl͐pkM~4x P_xp&3hy[ft"[^g˚gſ_?csI\Dڳjc U ?Kq+Wm'Z:.u4zZlo*'R̛|̓;s_L };ǺDi˩\i:M=#X>x/T4߈:.R5 In(y%ʇV#_?|At;?muӈ] AZ VC>ئm~CT߯Kdx'mg=.>TV oWj>5/h:ytsAۇ{5oh40*wpj7̈>-UAn|ĺ>>֡Ч]j f=,6ѱ\IZ/Q|O{yㆰ^;xzSIc㏊^"">=\- ZXKh8.sqk6ך-2actd VcݙGT7&< i^g{Mo`͞vYcOw?Qlsx7^񇈼!/ ^jZ~9ozL7жI2'A (|Td[ PWf]if4 /?>8sOd[rM_^$е5lh}^J`8I6* T $~#G+QJ:)W.=W%RF}ZB:9K Z%L^[Lyd=4os߉.ni^罐%h[>`~Һzi773W3Xh1Zy$WlP^ \"{^wZmyuh3H#'t\c䯈L欄 LjHZĽ*,o?Z#<?xwkmBUѡK7HoywJ#y)txI֬+ڂʑH6?xL48ڪZ+ O5w7b4) Dx-ğh &֣ߋ5T ӣKO,D=I1>-<)n+קO%_)%?wg,|5k>sYI^Ef/淐!8}~N+ؿ9:W=UG~[V2;9#},OC}ھk^ 5)-b@*/ɶ)x|Px+ f|Jcmj}8@wn#+~5!i^.IJM杣Y/tV;#:7`ݰꞤ-5_xQɣFmGNKn3*:\.;|&͟Vω>*͵8.?m957ͱ x/jO[Ok]5vo4+]^Zkt{ bX.mYb?xPQzl~5^YtU'~ƶُ*8r^ExMa}>(u6%Ԥ&btH K#{wl/$<[[mA)ff>kS~! v6ڼq#n@e {ĨnrkէglF#uH"MIt`~˿\MɇVEҡsn&\},?&O!r>׊gwnX]G_2Mx]|͙?OѴ*uáYx+cq61[c|Q_si].|0-oF77jG1>9.#P:=1^|Q?"ΡvjMRTtIG˽6=h]?ElĩkkSK_[[X@z*ᥦpiz/l4E,%HHnjNm 3'4{/ I{9&k\-L/ڑ#|ր W>+XKRbR@Q@pC %wux 1E?xJvMmwG5ΠS}nXC' Iֻ.xU}׷zx_Pcu|O l MtM7J#16rɣ_oߊϋږv|9/uԉ1yf|}m4'Z^iKι7%CȫfsI+4>>_rS߇Fan,Ii'-~V/~ ?5o亦oXZe̳э=ӗA>Og5B|4 ا4\9gUXfsU߃"|ic]RVu[)Cɽ{/Z M'iT,tImv>_2I4ݎKĿ2HO?WUzabHBYwmt޷|?_楡3!$+!ʷ5*>|ҽ!_x8Ia0<ͻSV].t>{i56(}|,6lR/~Uuڽg?$%.%3]"b`B ٸoR|HYOx~(kk+ ZP.4=hƆ ZU: ~&x^As}wDP D6$w3#/Di6x_k/ }_@<>u(S3Yym#'B6lQ^:5x3NИx~E59$y#<)$lQmS<ʇ`}/#r,$L]N]$I##C" 1O-1P{.ZBI% wV]­fLtZgŮgn׿mՆY]"\>zW>S~%Qn֚:7̋$ȈkXmo7uߋNw[M?CVT[g@[dͽ r]>gm/hyiso9`<#sn?_XF*zX>yW#nNjտ\FLf;g"0#~՗~^#Ӽi3Oɨ]x2é2v]]M'S>YY?'ܯ3֗?i\j{KWn}mQh\[l,wZk_|vkiPh:_d4PYUQo*zcĶҭu2Fq<";H@O @J? 4;Rk0ߢEkalz~.,|swͩ6RG #fFěe cUv-Itk{qݮ.@ۥdTw\O?->.[uMwN~YdJšO*8 h7㊵LmGƙiK-t58.Im̑6K!Rɰ9vO5; o3x\״ : Ri&kDa{DUGԹNqGO >/Ahm[^8Ṏ@vq@? ~΃ h^gѮ.F Gڙ$H~͏7Y7__ Xj λYiJ_%vBkZ`U;5hg?@,low:ڲAޒdzptG)*%;xhhv'&y2!/evIPk_mZ.m4_Yiˡܙq1B]Gx8|iqᙢnos-SqxX1W`y3ֆZ>2f|1{;emԷ("h|dt5%}<-A HL55 # :$(|`WuēUJn'PHIlŚFա5I!M-d9^;wlb>t85ߴ_.;'eethC$&ruNp=S]񗈴%me丌[ f/5W OhZO_Vm X3Cd[_y.4F^9=kԼGccuv:YhztɘD1o6Slk +|Gxw]Ƃ-M~,Vp>w?o>gC5{;i6JЫ2Cm1d5CS~$_;74X81ڪytv8OڏLԥRYXX>'y5- wYn.%P;0K'6'Wt_ie߈G%y2I$FTv'Zߩ*7z޽q z6Ty.2imm<ұOSqChV]e~pd<EcźZzC&\MmYfxXw_h> ~ Sz~4tqեdlؘ"y`;Sɭ|m=gš K]ՖxT;]6&HwQ>bjcåQwq\`G5Fg&wr|ּIggxsZ[+ƾњ&fp'>_|u[Ǎn9{mrM:}FKٚN<,{'4O1lt6J\]&űhI:O^\HOշ/xC]> Rxk[{Yyg6VGį5KEƛ ϬjH:啍o#?=k7KSAxP<jRK;FX1IxM9u_/w? ⟆ }-"}A5)fZr{rsqyk=4oIMVMNMXg'e}_'z2ԼyOU9ơ{iGG̈ahot9E^t3z>'j7æcj6%Ģ#) 帟p|䍿S$#_J]zt!{uf$ OF~rk7H#z~`[i"tޔB\BO]W4V/o "xCLt |V6l.eydٸ'OD/|+uxSy3TH2~oW_o<<9>X=M)h[rDf6OY^x5'O𦓠/.#Y%yXo*R>"tsyZl\Bh`P˳Ūi~5o"FHAzu5I5/%ot&WŚG h!֢K{#9G"1W1d$OXxe<7k7z ֣i'I[L[C1DbS+{uq6=6^\K bTVpwkMGy? |*λk`Z3J㻒x^NwS_zok_fFtE!|ryP2Excw յ]?ZkjQjn, a>䉲VcWze̺7ltu0Y]l.Vc(b)~P6ڧ+]/VHsN/|_-֟nZc:'Z '_Ibuw\P{ #0ꉝ#E+GFw1寮|O;5̫%AE[ [ǖOa\i^kQ¤s"M co=#wRn,~iW2ڻ.@զ0je[~`7hz485WG5wDM:?QO zffKD@ 糋LM|y> \'jGKiu=K:ٺ[I#˩s{FAiyvֱs)"󦫷zz>׾ :Nj[n-Jv<~Sx|4idT2#fXXDq<)+w Zi|j.tZO5^GhBۜܟݔLo![Qt5/_h FH7<}èc{}mgO[iEϪ\_0al#rv~~k_MK^5f$MPk|V6Ȏ~WB6c>M]sþ(\vN2\d%ПP[Hi9.&~]{W?};smxoNSGSE%7E&?57GUO)AZ^kii, llVi*4 :ZོB EQD@~!ڳ~К؟iB3FV$Yv>Ik[ n,t[3\DsF6HN~y1b>JgxRxK$z֫˹]ot9G_v˯ ִ='Na?> qט}kß<%ym2 KΓiu3s# w!Ad{Qg/kug≤muH5O6Z;cHcH jVp=+[\k[Ŧep(۽%ir>m Fmj5𙰳e[넸~ÔAߥav6>'hxPt ڄo2ݴi I u:Qw[Z"5ïE Q{MK#ȏ+JH(95]iP @ jn/1Zyu嚽ׄ?|zR eZ é\\uI4YZG:D"<? (|¾3U ڎa#/J-QEQEQEx 9'[PǭPXwMmYh_hthZ=^6PU+k{Q<(+Llgp~S,o= YYG_o[?agФU +|q>kxjMt-7H[yuRh* Tm H2sֻt}^ѡ}FO8Jbݸ.FZ/:TӼujzJiiq=fm1HΩ*w.cz;͏»k{KC5+YUHYs-r|h^)e5Vxlt_]ڋEw{NW`?w#WPӣo^e~D߹}7kˋ;I쭴)Ԓ`*( %x?xP4mJ-kό/Hu*ɿ{8cK}g ku(+t2ެZѴHltOLhNk>֖ڪˡ-弱8/ yUlX/tڗ/icU'n,cM]]>E]?mc4ЀJ:4R{؀_05_h"F|?sԖ홆{I$G N7?ZgcjQ%%7RY$XȎ#>buO,rjƙZt[/x]C\IQXXmޱ,;fI"tl)K׭;ZRg)Z&?ey]x@k;LD6 ~_޼ixvMKӗA4+`i)aGL|eO;O<=>yGۮ&)gTݸ ^iGIgCϭ_hxLVOwcfkx"gr/֪j>c{xL[F]JHxhlQ'nMHy~'μ>0M:Ǐ/ӮE,mIQ o!JkO J@n F)! W';}1>/Kپ̀ 35}shJ\x˅>S"EOs3P77|S[xTx>Qҵm8ͪ_66 >k]2wXܑ~9'x?o-Z^_ P/4]؅E#Z Bx5(eHb#*ԖtB,DeO?wQ^1>^.o[g'Ho/xIk; [j16=/ 3Dgm;72jD$ᘮ4ĚLsk iѵ{WʸjZvkdj_jZX[o-%|z"-y/xľ ծ-=*YEyeN2˃'޻ hơOwJ@",Z[h.ſACSu˛wHEI_XLU,I ~Dx{H^$Z{1uvӧtW֮IsZf,j/Ϛn(r㝟ii5 YM{XKMXiVFϲoJq{>, iVWPZ.d$8Hե˗tN~+m/VɊۉny $ 7&_O,4GMdڄos^4U(3O_Wɤ^x@pֶ9Ok!C_JIiKieNVq*ayF'͟5w? g5s42D,+|-ĺ,(oabNuDw;K9t$ (OWo:>mtta%?̢pgAZvuW4;>58FY$Rt3 m<ũ[Oqgs{hcP +۾~`GGm;íykycqFm.7{湯]zm"U5RFXۤQ$xFc< J);m¯ ]kje3\ɥZoysvҹKWLj.|=wӥ[-'WXcidl}'t<-㯄8ޘ#4K Q#C,:!-j|jvSjzl:qѼ?è[ ؁<ߙL|iW>եӵ R+u\>t" cuI4kM~kk \1=*/ jmw<:&hʜH<#\~30 >xGHВ+MZYnLh#D22Dw=/'xͣVVt9WqK݆}1]|- M[o>!՗Udk5nHfGKR Z@QEQEQEx 9'[PǭPXw{]ԼE8: Ð{{iF-Ŀq"]z5)SšOzLLo^5͜ϦiCxwKu}*II6ʮFr|0+lw;ڗ,|:塷+6mgN~TN|U^j#f6pi[FBj;ɏ'Y'X+oM>t)OkF`DJnw^{U?*k<[(;;=M,HfG"I>_fؠ!T58]}_X@S,șskZAk"׵I5}Z {NnۼB:#4u?8jo_oƛztKHhV s4-v$sYE [u7:=jHx!?>σw WRֵ=VO82aPI0uuAԅޟkw3β7~\Q멃?ĭkB񅟂[>, iQjw Ti-'g^PU>xj:~+x$I)K }9~ (No,#Z"G0wn#unvwn-^Rڼ;YUU@9P~zI֫.wa=ob쉤I=}>0xOsh>Ѵ%kGv"wm~nm7{'|L\li~|Jó>K{cw(}":tkXBk-^ɠ{;'Y _+hv*`bўYJ(iy"pd˝Ww34@Aw3g$n?qX E-c5ŔO3ew$U{~u,^|(Z^>$qammJObwvV_K<*4o jEa 𵤖J*pV >x|S;g/Q5N!GFXG:lT/koO \xjk)/ W[ī#NnJ7t]*oxRH5k׳nuiBeg3J.3,> ;J\_FzborW"4;}C~$5>ᮬ|At whZ/)fe([BMwi F#E%̱T#HRRQE _?x#įi$ҴQܤwLBe0w1ZM:|߼,O*oXW 1{},6Z=՝] V&9@ﻻ_=l p oE?஫Y4}JK={^u)//K5mϛ (޾XÊd}͔QKYa&[T&|g  T?h?&օZ3=+Q) ݞ7CѦ|sxTѵ-7ΛEXek#"hl4;T1a:GNiVZwa4Cfc-9yHdz(c?6+?E_UwV4:+m'm& z>MGk߂o-s\Mhm㳊k ɘ#ĮS.$? C"[8lc÷3Z5Sm_[+BsVϋh4%}s}QDb*#\K-^4ְ}yk渐ߕ?ph%z(Zt.e\Hd(ʐ*XJ>4Z7^SZ(>j]z=-t{F}s]osxni6v1䅄B|WTŽ(Ěgk؅mF-uQbTt&OxO>?4ۭ?PZ@4G%g~hwǜ5DC utNu:$<\ğro ΡkxYŹL$.-,gTR|֩uK}uss:oዦt^_Is~Z5,u- 6KUfE3_|эƢ=j>5mk=}/ )I7콡hz vzhi~$vL,B)X3毡k7~'Ե+WkvZ67Z6dKb&@'~?SfsHo%ohw6\$vöo)4l{q]'#^7czGM#P%yvo| 񦑥:C>kECnHe">֫x~&,|gX`M -8~|C6jbk[;@nWF :}|a9|Q)-- $B8zU+h tk䁭㾉e%h,kwL쵏C▁L3#t -]#ة _&Td=kGЕr_~O jewmW6VW5 )nexYG'rן)dLTծn`ccdbC_%aYm=n;y^X~|9m4׉emByr[÷ʶ"zc GlU%Qj-|Sx{_ Kִ.xm#ʚ -4cw6~pu/h :w,w4x@&l=qӴ\xJUK=f.,."JQ 3MUrcxDĶ6חZ5p]\^4N!Hij5|D<;ʹsx!+qeĭ͑Տ/|x}xS÷:ޮ[{˱)&rx~"+'Rur]fn*zpթ|0A]$]joϪSj<2/*$"8IKyk<K~"׼D)$i WCw`>OSYZ;[+A׏n.Vŋԉ ż*#͍w\|-O@~%dK&FPO&CR]8xoJլtO{7ټ_ɇ7~oWY{ǧ6_\o:f=I.h׾_rG;8ؿq?Eּr|G=RZ$3F,/PcwDZ1DGxgv>J[n]OpX Evlho]wNpjlYQM e;?hmB?W)ѧ:,-WvyqK2Gu,EХ4mE5Koi>ozW77൏ $\t7OH}Hпcо}u]7Mc&yj>MZ.+qr&#d%-qo<$dfq!/o0uWh2'j?$uH[J'T`O,NmVFU6zMIkanʢbaOVw%'{}ƯƯx k·:U@D6s]"u`Ǿ,eecꖗ6R;}gtʉIJWa/_/:ާޮɮğʘn&}76M:o|7E@=-ou=TJ~6MMkj6k,G2itpw=HxF5I !`)ݯޗ>SӦN|?jtdgE pTU_;}Om/>.Q6R)ql,[ɔel_χ^27Oeev;|Ȅ!oּ?4egY>׳붷udg!/)6G`4tʦt !>V?W_#|(5iūj7EīTcz>;&|E=/,w1ٯ88ȏdֽŸ|;IgNOVVWL6GݪiP:ᖲ$Lʕox=]o^97{od:ƍq.ZZ$vk)iXGe]?I^WK.M>%XMUe"*fOM2 H{{䯄m2/?k_yܼv0l@rl2csOoK7tH-ZM #K7OByǾHtMf/PCi$r"+tܫҭiVwckugz$+*UVI] ~/xv_w!Zfv4 C-8fi%Tcb RO"s?-ʩKsClyoۖնFڈiCP7d|y+NWѵ;Ėos^lnemʲuy\^'3}/En49tA/`LϺDDFu_ 7%+|%M/Fl,!i%HCn%䑤v,~;M_{ bamr+*zՙGi/oj:l"hfsM2"г+ف[|UxRff[eoxi>*f)Q7ش/Q-O{|3/+SL4˛Ȅ14RN~|OMcMmmhn#8S-|@<㼷е/K;F'^e$+mlfGy '3\ƯS,'JH4sɠ&ndV4eF}>HM vw7lzл.>5gxK?otݚz\jsCgmr7I7*Yiݒ DmKYu/>;[܆ж9|v"|G_ xMUMJ[C5XEVGd>nS_Ndx-񯡊ٌ2=Ǘߔ*zU/%2|MÏ1ʾ+CJw9Hq^:ቴ7Mfmt4ۛ=*,` .4 N=2j=+VeiBs q]:6"J۶4^hoz 4}] |mAtoUdmm,pfF$sw>c y)woKAAYEErݬhm Sy+ kiW~``$|ī|_3ziڅ໿5&h'1GȉG؈KS<!{M= j}.{˙Ȧx$ fW>!]Y.YFR]V{wpܯF_ ˤKp,SI"aT.5 TLbwͱcy~/M$(TzG RYlQ[k.G:zmRto'g߹>ttL~ 6)Ͷm:ψtkR, ,`|ѕ==͵ jW/l h4NW\L7ךUƗ3Yjڬqe/GqZɊ{5`0MK}x-kIK"0MsZ׃-4jڮft&(z^ ΎSXѯk761_QoËBX_vҾ/|5Ay46+n5 gy*N ų’L>j6R~'M/%A9!o#XO4Vcc*qZ^yo_\DYx'ziJ]IؔlOa5'k>Яt$}JH7*y_-GLޕ^-j5i"֯Hؿq*{7 |3CG,^V$qq3Rj>ͽ@C)75M7W:"^\]OƲ\] iϘ)oWLW_G敧|3uO+Ce2dg`9?.?N_ Ook5Imt{l2_;aʍgw~ ״/ZH9tff02+m"DX߈?OEo,MJKw!nf5}G'ӨxR߈ah%H'K/vhҵZD0O[Z=?l%uVo'lZ7z|API:\:v:n麂Kqs5X[BGZQ(s^<O߇.#.ta4E(ܲ1TA*L~ /_ <[^(]bJti,!Ƶ{W#._+c<@<]@.ӯ?JctF~7-S&v"-WAqsEYnܙ7vJmHn LI^G/zLj|xNxI#"~a;A5Qf˾2^^1/}Y_/io,v_iBgTr|2+:scPx?ėn.6 >l.79 y5,hҖMjZ&XHfKwe۾7M"*-%-PEPEPEPEPEP~Gi7E,kN›} ^%/MS_ 4}GN_Ke]rwUqH|#H ++<$WGX55q%z;OX"1{i"˕}d]~Rxsĺmr+QhlFWIO6c ב, ľƯ/5gTӧ/-ss3 (?j&g]󊷣X<3hYsogq5G+L#6K2֞,otLh!]?Q-yE?4&X/Yi|qo "X<,PϮI )X9$}ߟ?}:sGO"WžƝhz]ڄ/$Wq9Pq|SᯈnwȺ'5r 0y-t:C>|jkZs~rQ#x|`t봟'?1[Z֚V\IVa/'y*]p& >e-CZ]pjW2-#?Yh>(<)/|A4M\ms?$nm/,§%uĀ>p--zM= =4Q[2qB.};>@5xTFMIM2}WR [FI>OPǟෛ̯=𦱫 l0xC|j:ω5jjvM۬,pGwql|g|YaCWWӮ>Tc.o>xt]MjcM%Cy<>\:Nvs֭y[|@Ox@cjUJŖH""^o?:F[⾣px:/Wyk#YWŭc_WA<2Ymqw돷+0ԑmr|_0n kt :!OO+7u0xx^O:Fvٺ^]jKuO5rO/%ډ+8(~|7}*]{]5 F]J0]4;H@Iu@7>b<Cr^M3T4-M9KKľEr+Ҿ xU@mgiDjfh#et>^qw%~!˥ |Oݍݍ-F\ܬI*mcHtuSᇀu}/֟~Uu'GxYBf7fsT?mi!m\iէ}xh܉ pҔ|~g]{zơsz[kw~Սp=-o4,-)TTM7/5W\xƺd:Nܘ|Cw͜)KDe@1?>1n$5-b=.YyQOJ1ھ]NNC95mmY#y+b2n'jQיᇌ5Zvmᤴl$[>L4bG%iL|W>RoQ%[ۈc_hmoUmٯ~$|3=F|1kI۹gI$];K[}maHA\Xɪx.KRHbDhdHEq1O?ԟ?9<+j:-KZV yP6vTD{-G?zW?]] OdȲe?ud2x7_<u=yeɭzsޛf[1ϔsڢ5_ß< 2uM_Pmm2 27t?ECJ@#Oxs"}}+?zW ig%d-<^蹙GW},?j7:G5/qz6,{溿|-u]֞Z5bmKݽ:5XDA*r<<ֹ~c n59Z]G2K.2;- -n4YKh'?n^}NˢI>_IѼ w5WWM63VWS“ ewqSW'Ey>`I%ӭ!; O^W.ž2mv:v cS?ɘ?_?ܥ7*NM}sXAϧP±4{ʇk).MzmkZt ͣ_jwf#"K:o=zwj2 um,(%cfـ݈Wbb(((((((֒M&4#RxA$*7WmNs \_LvwPQγ%ܟz:7ȫ_,5Ox2i5Ś%.K^A0呦S_=L~Z\߾xUuYnu)^+F uoTeڤ3i{Y kyZA٥Zwu=~CG}Io -kY$r3Ҥ/5iK}kx|M>ֺl9eV213s#\\~$Z *>-/e4ح4t;oal:qVx^7񮞚؅97s\B2$NG2 k0C6ݽŴQDT.ԙf^ޫXzoI4oSTZU֖0ۛw+%|5/,.q.͍?؎?K_VD|76\x?T5Њ7bP5Z_x[]_T֡$1ZaZD\`oFֹtO~1kw7|(-S m݀]=0*_|m{(4H#A TgTPS;_ |wh7).; 6C$ʄwk*Dծu]M. S uF#Ps|".i#P;uq$R#aM$Wy6uA'm-uy-PmY.$8V3_CҵI5[oke ˏ69Q\'6S9'^n]k>kxmiP(Ʋec _#siu/Pl,u ^S1|L'5rtKv~%!ꯧ]M]&{BS'cq-8YR_ -/K/ >KOݭ1C6[Ec߹j7hx[E>ck x"2*2uj> C犯Nx }HťCe[{YKg7E6: j,;x (m#APWZbW)?^xT"X[5Tvf<;w[~{so5޼}jM/knq>^; xX{[xoIү-em~[ߔl{a]ŭPK}5f9Q%YQlUxңTghxNźj.!{{h"̷{1dT9p~Oϰn(驢 V i%E sy2c&]"~kC^W_5\tg>%gG1!VooKJ>_^j>3m[I2  <ixC֝z?|9jZE|<2wrݔGt]#[msCg|3l&uI)e,gd¶$E:_cO^x=2mzOSV{".7G}8/ִ\[t:=?Yưu}FN5 _]$~7 lr)Ø=*DžX+uX[,)!Bی1J".$Wm/N>9h5|Saym˩Nm4%'}7c̓? ?KGykz5~sz<*\HTohTC̷G|I%ĿjKCֲ*#?\ψ> ih4_wNٍ6y~\lPҳ>6^Y7?^5O$xp6y'|;o ;kY[gk[Ο3+X$nytVYh-c0)(CWc gNY4uIɼ[_ijk1aE{u!l^o9ym񭅝υ1 :M"*?<(>xkڗbkQeɨ4>I7;qS]σ|&h|6߈Mӵ]V{Ox.CYheTd?3g~/si?޽jE&0 n]N;o| Qrgqcmm"-y`W$ eξ&gO ZַP-R!T]f*|=_3sqMa6\٤G&ْCFW/2:MbKK쥒69Dyo$VD,|e+%] 4cJ?+k.?-Q%iRBO*=?+^'}ޝĶ3Z_G D[U#Z_2^-OJO:r;CW?.*>YԯƷipoahEJ7Ldq^E.brAREPEP\!? Im]pC %LzAE\MOi^ t_irXڸmV++YY@;'\4ou;LGG/ 4R aF_qUa|A&'t)ut56ݹ2N>tt<t=7.I&~ʊ>ui(sIyUjE 2Αfz[0&]mD1eJZ(wK V-+ht;#ʁq)#h:}uepn2~C̿>E02gƏr>c<$KXٟ;Koy؞B?~<1}~$ЉnćYoϻֺZ)@ym j Z5g۲HI U35go "J#H]=QEQEQEQEQEQEGV*K[]LX.T0hee%R4HՒ1/Zx#Þ ZN\߳]* mZs"Ydln%r$ѹ0QyU?Z&uK[S&6DmoӠia+s-Ywĺ_45Ph(I#H"US4h5O]Iwv$:%v-sƩc%{)+iIv)OJ/>÷z ^bosN-_&|R 'o/>6钛X.1vF'C/t[͵s4O,ę $?[R~xÖڕpjQ7Yi0Dq%TO1Ծ?X,4GzV:l ?w.`r<#:4K]Roi{sAnrZ>֟ ,|$k m!ݏ$ֽ?5?Ў,[63rk]-6KK7la6R ~l tZ4ox;Uzt y]5I$G_V]Zx'Þ掶z4E|0pq)/ֺ (ҾKEgt ],Yi eUqlUxOxC.:Dp VJ0sW5RPfvvkԺķa'xD78>Jaᾩjk=ΥYky\ nm9g|LQZ((((sO$C!A?ڀ&=hT|Sgrvw6zuf_qS| Z!/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/Ώ-kgU̟}Vֿ+u_̟}< Za/ΣDei&r@D͟}E%QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEA{-2M,) {+Gr Ou_ P_؏4U՚iIBM>ϯ?6*>ϯ?6*ߊt^ƺmxMim%frȥ3Χ<]n1EO# 2X}{Mrwgga|c{:geS-tk Q;d/ v^5 zZUH9ru85)M̨w_gg` ]=ڄPa)uo#oOXF5 GooJk\=m#d'G'\޽Dx{WeyxΒ5ҥyK$_9Nt\Cc4BY25?@j_"lT}_"lU<9@z,s>b%k vX+1dRVJ[@'Mi.nUտ굗Ž7S燭,5{.V,֖ӔIL2M/HﺹKv_iw_> Z(,ZJS֒ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (3+:#;^oB*i iO9J\ݦ{ַjC/S~DOנKYtmY!|b:OtgW]&V nD w?K6Z(=hzQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE-%PE4Q@hPFMP3EJ(?fotoxx-18.01.1/images/all-directories.jpg0000644000175000017500000003232213222767271016715 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:sharpen| 5http://ns.adobe.com/xap/1.0/ 237 300 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((," }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{Oޏj.ѺF,Mj^[q+Rw˷gsWxڿdK|#2p+voV ׺Z${PNH1?:{`|̈2{A^M\\IEO0h58h e:t{jeu (ܹk|q P*CdV眂:V^-%܏`$A~7go_AQyoɾ"i.>͋ѣw#z0 W=?o_隝܃R|Nڼ0way秽 ە_AW M5i+ߥZ E"26W#,HqT67;kż2،'hfʤEy_AQpPxQu4 /t{guzQ"oeCsuSxY )5)kg3fU@s4\v=K+?*24=-:ȴP2˖ Nsޫj/յh|CaeMm`'6eeI!F@87mXXLʼzx^[]CR<W sZ`fuح`gHR6CVg>y̯ʲ4k˫*PWr[q0Dw s̢[ʌGE_AUO22o+?*2eeW~Te}U<<.ʪyy\ y_AQT((c)VU A2S^?VD@R9lveC48Vʛ]8ZH4``ן³ug wXy|d #Gq;;׮JuYZGf<4<.W_h(q-brIrk$#p|oNd~~g?ӥAR,E.yjHCh?>O~3~8ӧ8U={zn4M>kۋne֮.E:y6 +ح  \ZYxBM,Ԩz>8<owGy^/$ifX_٭}$d ܜo!^h:kN*D,3mG~k?;סilckMAЬ%ycF-Z~gK;ף<owE GRg?fD^ggK;ף<owE]yy/y^=atE{{Կ{z?YGRg?fD^ggK;ף<owE]y |V ^140cZUk?VC1Zq*UݽbJm1ln5^Kgd/1fJU 9{'3#Q-&-it,:3gڏx>YWVNK?820u(du>x~K>dQ!dl6я)wӜsT5?jk IZ`;v7@7ʞ0jӴ.:yHC34hEu NNI?}lgId*@t#9nD =)|T O/NO/NO/NO/NO/NO/NO/NO/NO/NO/N3x`8p>u|i==uRiU3ZnFks5-٥i;@E,F'u$𧉭k"XcGjGǀ*Kk[5Qxm@nbp1kK `ӗH TRܻ}Zw!}kyb§~0h׵oðjBhpxH]B>'$8HcdM/0`I\KU,&m1_GId VƣG]e@͸i"G*6N034'fBj)Rՠh(έ+?S@ݧ5ig 'St C<~Xdr`bT a[:5]_LX{!d]Ub"D{S-ېrkzvr>a(Ē,{+NXrIIO|?;{T򠷷䌘9tb y;=y 0F{][[YZ۱T + >aY ї)y>g ~׸6D 5aHީp"dp hk:e]麔^um+HR r>!j.yqoqusveɶ6EUH#va  maYw7ף41[ͦ]w l* אs v7~Ү xo=N8RHW+xB a0 mu FX+'9f쓞s@s~*Djdtsl2.xyw5ڽ:N/QXndfV*0#sZQEQEQEQEQEQEQEQEQEQEu-*m:>[Tڈg'Z~nY?JVdp+7C^"c%rX2zOz\Vmpp# ѯ@;(y㥵,-.纵s%Wd5bxbHn#IaJ::V=EPG;D+ڪ|Mt4Ir5K M̐71g!d< =.+=2 Dp[#2rp9$5_Zx_M{l<77"!y A8Pۉ ֬Y]1u-B 8 KUeh6vW/\Phl/7x$ 09yh4߇Rh\[%v̮veQwׁII<`2`_Jwb:-{(((((((((((((XkA9=*hK iUg*1.vWOqaEbW |5:4H+̤ }FA}"=D!%6Xv~S5iyn]G­8D(A۝3:qZP7V!";ih=F;?8S]xKGK"J{EjiQjF {5C29L2ǭy捼Eꖰzńh~cU+F*MEVZԠ zw4ib~WYm #9p=_G״}iFtAY%1N5^wxVZFH a Kjs@EPEPEPEPEPEPEPEPEPeTCY/ϤOg*TZ~n(((y8J6%}SI4O}L}LoQكW*@<9*B d(}1m'\WѯumFk;S=ſmcr3e Ϸ' _Zs{kSڤ )8`Iy0A5f/yàR^%ᲖR,,#2slWmYGS\SxgVTm<;>vӧđݵY]r23sV+\4}j_7jWC o,V"_]،Qm ;kLWMvdxgId` ZU Dv7)(#%r]3I9$0 ( ( ( ( ( ( ( ( ( ( (/ϭ ?}&2{?V꥟tQEQEQE88{f:͢[hҞGSѨz*Qj)}='Jz5iOF >ҞGSѨz*Qj)}='Jz5iOF >ҞGSѨz*Qj)}=%S gէFF6HU1!p%x'ٛn§ 3Qf۟?>s|G}*z(7fo# ??Gٛn§ 3Qf۟?>s|G}*z(7fo# ??Gٛn§ 3Qf۟?>s|G}*z(7fo# ??Gٛn§ 3Qf۟?>s|G}*z(7fo# M6XeȰNge\sX3Qf۟?bP{Sz/j_ϳ7fo# CK`zK@V `5WIWHYAPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPT/):L'w^.$Wn("x #{qjuUmc,n$ju=^|g}ύ>g^ڏkqX|g?X˽c|Bj[El>mMf-:xYaloXW^R=A}Xwg(I/~o?rOy6?gSf^a}H~-ݽs_+(Aðg4-7#r<@vnO]9>ʕgloy>[s"~ۿ|ex`{Yu9BoAwKnZneWN?{q>Z;#_쾎hr*@ƄhYĥR9?\ n6cBVz]:vrHK:SiVbe2LJit6Sce[u`ڗX'tfaMXQyUi쑢-1ndֶZ-]Ur8F 1|cϾƔ;`k;1i#jLetd4tZvoߣ yC糳;t@gKE7˳-d^4}Vq\z}h\޴2F26OeB-O2W Գ)[2=vF7`lY?.sF=΀v94jHǺx3ExUMŊ*Cgk՞1PDuanN=&tq,Ƴqa\CR, \At6j艼YpBnc{ޥZg8QVy\zN\PK8TIpY9I9lETÙ҉gICO9lgLeC_3 [ï;Yb8Q]!5!+I[NLCo9[%dYoKbv?K3psZjEPZRcxP_!,SQܩځ>YM9d>cIQ;ezc<ϭmYIxQ™z2Fc8OVm9i 1I¨셞60Q :s}b`g)W0C+uK7$FRH$u"y~!*ߍRU#@Pj獂 E8)0͇jzCSЂ 0Ջ.L.nSeUkqzՠiGtDu$<@dtc(y\`6}6Bv9vC;b:?bxtUS y&hJ8Tk#`7шBeYýg\͍"ݣ1|"{(gH' } VÍQsI!VdcNktQ@ؠ] z&hߧF:l8 _BhP/Oi MON|?QflЄ l5XHpHYyBW9gi^sBIT|dIDATxy_u?~l$ErDJ D"M%WjX J4@Q@&v&nlN*$0)TZ,Œp[r?FJq:߼w={{y5v.T2V <eVWynj= ?k]y_ٌf@?Ja!zpDq-ZHWp+hT6cG(/@w#0C*?ln޸~Ͷ׭V=R,/>{^NCccSSSϋ35~',wͫoV[i^lsqdoeϞ:5ќzم<=h]ŵ__ve^nzV,'I.EHLLx)sߟ:;6x_9K3mmR]jCw={?q~;KV PZړ :M~3V?'`S4z|q޻ӿmI`:A*CS "HZak7iE5杔 zwn߻گwV=s&Ոj5s|Uw3I/Ÿ? 镞4_X>6=l\x+Td`:2GsG+43T+gal`UٽKyO35`슖(DO~ jd.\ ƶZvV$"+6l`kdȑ7CW+YcKK<<% ]h1ٱ !uZ!QH@j1)Pص} 4GqyR#}䑍RKΜ!40OS\b:):I¦]DÇ6֞o -pXwq~Q񔶁*2ɜ"rAf!3-:Yf%iaNMz:{ncM(y'>&)!YT\Yæӡ3>s]83r>WNbIoޱ_! T\'H}\!& J|SU!x1>CCܲ}#cW\XJd4_| }^ b@ ISr" 8d431[/;:~NAU9@7pC{ޭc3e+7b8F+.o+'" `2ρ72v=wߡFF7aOom{3/,_j5H*Y+f·Hɾb؊sk~.>ͮ|@<+w]pշOlݲeI?9J}`%Óe D(%A{7b" cݽ7.9n?[;\iZ=@{hP Gѥ,̂ |W6qahlغo>awaz>q϶(Yn-/9Kc pM HAR""@E/s) } q;dž8q =RZ߷fu(ťW_w.8&hQ鰺B T(=G &*_zU7[Gܑ{|DlOO`#-'D _uS DuK 2Hp*`pfrf" qg.NzO`fnksL b:N1 $=1Ġ"QiA*q3Gsd!Qz7lgYn߽R6z{&Dqj놵5XӈD7 /1B.16nVUq\h&YsVFQo?Q59!Zq+/E4Q2)J &`d-"JCklFvgA|\ Ν1MQ鄉vtRrTCvFsO('zKr!BG*<DDI:FVbj(ՉRZ+HumtFݓ5XX/ uvHf]q:1V3Qn@;TMEH#%tHhԡ>P:@ .5PBEt\ڮ*8+ (T9a*reqI1 AAKyO7F9LuV @6' *)i_nÜRMTt@)\o q>Yy"8 N0 JYFOYE&=1WۄCN*|tyt ʿʕl;s6-e0>`Ö6;JnBt ڂ)"qP.-}c Y)(PLC^4  /<nK9χ#hD̦^E1|lEt eS]~|Yxr A3 rՕN}Z m ET>/ӜD3T(.he/Z[)t^YAe ws[@{,Y(=Pֺ Յ_B,b 0"GlV.PL!ݕϧh`CoϮl$wR|(dwOXB^ƙ1WOZkG geRU-l͋ʟS0Ӭ̽Y*Q>nĨ炧f8 |Q}a'ߙJco߅{Ѝ6\4 h} &q?C>b$b9È]Kg6CXs3Ny5$S@>Hz薁JW5|pJ`7v["  EC_Uj70B yԝ;dp dNw)Ck=tVe_?LO=pK +ydW|;b=ḅH}.tT*"a u/Ht~'>?zg0B TG R$JE5"1rq'$O'[FΣ$ܪa.*) .40ݻcdu|iI9c/w$;>ԍnU^<\M-1@W䙓sd='3iQ=oyߞgڕkԹ W(4-^?W3<:'X#/ZMo[wa9J6ICJ228,`_lFjR)){\:9-y_n^3hN+W>X)a%'\rr%# )H]:&x駯W;>r|L9wI]V]_KCxӬ_USdQ F#!% Li N{ȅeirqZ(%/[~ :-.K>yZkM=JID.R lXq(û8 O +ܶSy8 ?|_ _a [8:~T%auڣ"Q@Gg,#y\J۶i/x癜dm^71C^8l_߲i˒cHu =%Sj'&]$DA'A Pf4MStc 80GLfM-'ޛ, (&I:LWJX/Z[ &Qԣ$tkp eO1L:F"@batzhn{ZT6 ^2.Xˉ2q? {t~KvTZU_F!EC5PJd@PAeDZ 8U`U"t]SKj 3re]GDQG ,d15%$@a3n@)JB5Wؘ.*H 79o}A-Ѥzo ^.wXɭRXGn-;!C@¬.k@(P\+-/UU0֑KZZ'4:EAZr[w- 0Nk1D̸8׊q 3'I&@f Q@a  7k=qz;C86Z;_zFD0QcR ֑V ?#cAG:}78^XIwb#R"Z(Ɛ -Ȓ3QcꋕxǿŰkȶ{oeb!P8G`\Pel+;F8_jLs~4񼃏D9 kɌ%+ YnrB.87yŘpa5' ؁&qwg=xlE(W}|N.~"gXau3;96$1_p p+HZe{\) [iv38o!wzާiQǹJwIENDB`fotoxx-18.01.1/images/flatten2.jpg0000644000175000017500000006563113222767271015363 0ustar micomicoJFIF,ExifMM*V^(if%02302Ƞ01002015:03:09 11:47:46Fotoxx:trim_rotate| Fotoxx:resize| Fotoxx:resize|NE http://ns.adobe.com/xap/1.0/ 8 2500 2000 2 2 0 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;t" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ocE/٠1"c)i #4?Qh?ȩ(fx|>%٠1"A8?R2w0@;(L6" #6( )|y&sLB}))5(9?T2ہƒ*Trco4,č gRRx4n̝R"t?L!I|M4H??KA<s@ !I|iȩvOzRȍDs"0ßI|wbOQN"b8WW689-i|h+D  /'xIE~4 ;Q*ʜFWa[};㚿jrpT=5i\9(W$!ެ%PQKzQf5pdw8 LD):=:Qp,>@13arzԥԎ\sJ%>ݩ樒Q!2zTEZ4 Ɲ9 XW, i|5['֌E3efEf>J'CBGjvBmS;z =iѠ,i3II@hBq'4XiOJJBh4SRn-y81# zZN `V߈R"ٺ+$ ^OnƱW-,~ObʂHt<׮5bʕav#nj=ݬ,\)iT$4f>C8\bڀzB.qԕ=+Rۚr`wJ5i ?1V$dDӜIA%i&^=iKaǗ'׊'ŸƭEG Gl1ABH!:p9Wf:n{"yuRoeL6` YI^#1é@r@řO#+{6rn:5‚kK ~iEdn 9p]œuoa\fXv2CgjpHϥ_e!s[F.QdRuMF %;L^(⋅⛚q7w M&iM'\,&7N{Swobnh-EZCIqIFi(ZJ(4RQJrnr810+ʑwx@[}}xRݴ˃o#[Gx`>%@m_f0T9FX oH.?3]GR6`8s]wY ?zʪ-X'rϔA1G qZ6wzϳ9=\O Nwgj%~48ةnc S?xcɋ@d8#֚{𬽓esYHۦ*{+Tع%Q략-ˌN*Q}.O4{6mKY.'8`(:283V+j,xb #&{hpFݔ3J 5$ɓ6Y rFjOoy}ƙɥϽkc;,@֓i 6zgޓ42sC3wɦJrO8<Wmlazk2i8)4mn]RZJ\|ϸXxѫ`Ҵr gF#L|ũ6kJ@*\ ]F'>ᴜpzT3[q)h1gސ+qN OG=!4 B1K|(#>arIFi3O,-CYWRDU~ʒ<~ɒ{%,%=xi)%0B13ɨ<k {jZ,Q2vX׬mV. ,~Nɼ}J8< $al'L3Wiz1&dM5?nP+c :p=> c#,W8HtWGPyrh9KOHcw :dWwne.$Rm8P}/r1.Ckzn<>$dEN0C1YwڅH9lcPw mj((5Cw* zgUSXVdIhcRW욄>+𮵭]^ kx%AzE]ŕ6r1Ac =C4-#Hh=j9yƕmWK`f#]5t 3=~bhp)Dq}>W0E+}*f<#n>p$𠖦եb^H75!h$Ӹ?|]{Jkj,aidV`X1$1"F ss뎝)X..Ada?Uv#I1|'ppzTuK09=*G ThCr:UK+nAWG@Q-$M!Q@V{`9\˾w j*nfg { *O+HE+IvHcQ:U;-ś ] 5sDc40^NԫrpI֐ 6 g4q i{5Y7<4}k]*Az]пj1Oz7ZS hⓊ 8OK,=jqir,)a%H89FC)o.'(\OP٬ IU<Յ%p͵B=JX$-38玴ﰚ]`UԌkۜ/*a8x#(YQ`GϽlb2x Tsޟ!LE&«G: ,  3wikǼw#i=i}n$zzwgcw lGd.J=)#(JBw/5.W ȶ&W#݌Nk]W#qTVw2}+߷},F>袧QOAN=jf .C.si 2"#m6JV*A$fȩ"nPx]AFn$hdn4;~GGgU9è8\bβ^0>vԖG_h3{@# E7Å>I{Vj$v~2}ՖsM9'ۊ}JДw'sd߁ `ӟxy7g\)`,14PK9,qZVF6*3~;CvIՐa⩫D/ H~ؼK ҫ8W.p}+LiYva5k vۀ}P"+.S-X#5s B{Em˯SQjE($W'4!b1r1ŲyeZ',oT\p4KdD#fGG{z +Dٜv?ZѼ@u"M Z`;|=+6M^3NqFgǠ+ؤXKUA%P~H ѺefǧY Ea';]gu5K%Ry1kגyOE濊72FqXZ⽭J0=kS+)bcjW!>/ع5HZ `J^$3?Hch3QE({nplKrzT-DU-G1RIJFA dPޡsMUk7Ÿ'|̆m#siI^95RrHjb0qަ̱[껏'54i< '?Z qO7k ?QI_7JWpP*G'&.;|?Ju)*IVeTңI3 ,l?–1R|r9m'JU0&8TdWfI= "X%Wv=,b$Uȯ$\8DZ,bBK[7\S@Rf i=볇E;2 ,u O1hq&I &Jso if nU!9<¾1Lz CzAq=;S Tl`Hv,I]ȊSJyK%Gj9 t9GW Z}Ib0aNc!~bs‚)9)m#>MM#<aFƢ{W@H#.d;ҹ- /3p'85GcO7.yƂ@QdP7&= At>F@WD^2= 7ZsHeE0bF|{ms|%xFcsҦQw0@' CcM|c𩥂UL8TiƤ*XwF13IHUs,FX*{[m7b" GyhY\qE Üy2E`%D8Q[.d/OVJZ ;rO${yK3|  `j䎼P@haڄI{UdR-n Sɹr(L5;_œS+F=K[B`A#֧hyMehڧHdnRd̳HspE5c8C&YiĶ~fp9XTirpʼn{WnPS4*\7qLE@tAYI9D`.i u)A _:xy1b0=@||4Siaqtg-ÕdRQ7wʺi~=)s9$r5~6?+x[JwJ^#ln4MN=WXքUK$8^Pxi,@(OD>V8*=jΏfbCGZH+KB|}H#5;ߎ)9diyjЪzb`khipmYm"v 1'j9eV5k#ϵP@MN ᥇#=>[[dT"D#o"\41Fp9=꬈F}֮X]lCl 5,vn 0i.C up*rcw#`Zܨ7cJL}PvnIkaHKm87;I3Nh-c֋Y,>q ٔ㏭GloGj/Q"匘_vq)6XHDfnVKLNґ|'HriY7#;3՞[kzݲ?J ۴b0}WrYp"ZIH<`M]m:lsr[3BQO^f=[JP=@[">CלӺv3Vb /epym l;T1ݗc$t8bRBxxjA+)Et-I1KTYuG)\ļPqQ40D\Oa صr9"_bJvc쎼R[c-ר$Eu@=i9D%2"v}`lLh;DžަH$aPy{N@*ImDFW+e`$QMVP+E}PK"֚@ǵ9HҐǨM_=S vdjF!@p!n;V!xI֐TE3`@Dp)I@ݩ<7Qf+ qI)WE34f&i3FhIvqɣ45=*\Ϋ[ݪ#9"L^w؎ Rhs%D?K~U ePh[zz`SeH ar$tu^\ qZn4"*QTT )^>۱ҡA5 8˷Hr@HjUpsNy0$Xcҟkk$n-;$3>ZÒ+ш  [_Fv@2+~"ˑВE[EN((ξDVi6f9~&3{9R0+=@&ÚZ n@'zԌy_h+~'ƘW',fy]JFusI4Mԙ)HE )ivpi"ۓڍDyRiXLњ1G4BNh$&hl\ωyE.G9XQ(yUݻXa$fC/ԨYqf6NqfJ̧$m1>Gy@X-M!7x q))x<@56:ף/2,gݏӏc[i6ŵ %Q߂HB25bKف^Ǹ z~Zh\mR_9EoB\Tܵc`/p>)򞕵Vkfӥ+'9Y*=ֽF/ ii f{RXOvH#6}3ᡁgTchm0?5~ 0ېUYoi~ Gn&F P(s֎>`}ѲϽ(U5֭#ㆣhmY1Ti}4OP3&Z_fp~f߳OP3jMh%h0!_j~!~zVϰX!!~bh"@ZٶDH߳M:]5WlR+\v]EMKgA1~;?El&k~M'EߓE@V,!1~mPߦ,eS:M PߦDz?_iV2AGZ_?Gj.2YF;ctauQ~Z}7M\`& id\"k L=gQDrsfB\PQdi߆+8{F[5єc'9+:=8RL ?űMiyHjyG4)Jx⠿eIcBʽxxsLb7k*[rj4ەH݈?vON)ؑ?gk&w59KitƸ>#GuMBV(Hiŗƒ3r1Ýժ˞;|3BJ)QK-RRLu} $~7#8-EH W2GXߎy\J)Z@X/ɪ`(#miw~;9/@yP4~4#J k)v+DCl:?)Gq8ۯ-$Sfֲ1Wʉfk!cf=]Z8%ca1kFSUNG"ftQk`G|S"(P8&5H&ii{4ꆳœ~TH@\\# }i6R֗1k8=SZ㺜؟PjFQNOѩrGDx{]Q<Ǔ[mv3 & 7ZMo⍠1I淭\ؾ4Z7/}hgjzHF1Lcǵ5)CtX=jRf:p]&{F ۱'fXٮmv 8^W^Xe*Rk a9p1lD΅M8ac`Ƿ&UYs"d-UmדZ[K8W^dlJ"QHVFFrR+°PƂhі2ok:lDCqOq]lZeԆ0&,+XGijڃ(܏zdV~w/퓜R]SNJi|9Üِٖ UPGٴXuvݔLk sp.P4iW5,є9P;JpiKGqP\8Ё]0V\t~4 JqNӊC",x$Tr\H;8O>Lq㺖,9 yL(u9ow4cL$y V31eo0X2jTV1fBGN}*e6Idf+pF=#K X,XI[j1o+pXF⑙sΊ`m+'cõ,[2?:XY[=3U"RR1=RʸH[\*[ؾn3Vx yl&<֑g(6" |ѩ6Ũ^ t7 l=uёE9£0Ƚ(lnJUAmïȼbFPgc')a6m-Q\To)\FOV8 OV_f}IKsdɜ$ddu)Xrkg!p[q튕l%޻(Cbhe`\seVB`~`[0YrOhV@k7^FRٙTg)`{(>fPi&pqlϮZR3M )_?jIt~[jxPDQS KPj\[_)O#"d~u4#9bi lF s{fӲ}qM[5 =6YB>VVeӁ0 pz.+/yF-Z(?h]lDvuU{ 2U`Ϸ5!\eG1P Ig`Y䌌8$nNrY}@C=P¥ JK5<@4Jz8$#AqQa~k/ѽ) >bvԺ#X\QVXySzԺT҃*uJEUFi2BaҝZ^͏%WژXzT` \& wI W?Q/?7ʎV xK{5x.+Ҩ1օC#vV.䞾]VJ':Q9)%C+NጵhEw:Iq+m5iL9a@귷B&ng˞c=;|SOZmEpHzGn1Tzzkyv4~n)jDb%.59TV5wGVY`Xh}%c p3}VhյS5liYyN?Ll a~Dž*):VVv6L{ʳ#K YN"1sհ2#V.R=OKfX'*[)鈙tdga*!oH:㊉?)KTᏭY$13)\!N @քJbkpa=mr ?:{B<,a.A,QU|IqLqJԋ3Ch$&CQW"71;6ƨͩg&G.Ekݴ6+_9^޹ܞ=jTcֻ nH ,qƫZ٣MЍ8֯Kf#31PzsKSXbt=_ۙ<5Kc85 u)knIOZ/j[i5 '\ƣ6Ōr6rk-oRg~si9J=S 5"4Raar3گ,7Nz֋S6hyS0'n89#ޘڊpNnc<~?a>Td拠Ď j ޕ\@ $ QȰăl#rI^ry߼SgL6*܇IpKwNSfmjjMx%1 ݀I=*̳2r~cP*2\SLVRnƉKO\fa W>D)¶OWwDHBrdS65Ƿ' I"Z̆f2ޟ>$ KctG8) yp?HRV%"0,o#Nj ug.^ a_]Gnbh *Dϼ9h)v4VFN@G5."zXӱ̬~sgiW}qd Ӿ dQ'*_w8۸fԥb0Y<梞xzmS;ulIOjQq݀5$@7bk[YbP ?L cT\ytao*7dU}WA!\(ɋo<6Ѭk?&mBWxooʏV=6eML5j6PܤT BI*{Dv_=#[5(\'9#?ִldK#R'bڛiShpD{\cZ'H'TVs:Vmms)@8tCmVY"f!`c)f1Wl O[$F׳e J>{3q#45.q,eDv9]δX(iFsϵ>fFVUrWblRF ؂q\UQk`rN:A28ϵs\]:*xFю<رkc0G"?6N_S_"jY薰ӢJY] u$7)KM;ngc -G0H.N=+<)ef<ZllM!Gzwˆ{kh{ܒ߭N<_-Gtw~(w@iIhx؉ r9?-[ibBCos2䊵qu2c3MY2C+ ? $Up=5b8e@Fǵ;N;y~UsIgN8n H0 V5dB3R4$39Sonӯ,ϧ5iu k \m\S1i<QW[r4I^訑%؄(eV T/泒XSy֠i|i4$1]p} T\,үK4g|~U"KH"R#"F#+)@rA;sVo.o$P q#W.,p\>Pyᲊ@RKs+9NIl:_^ypsJu;DJ 0yjޓmJ`J5ݚ Jj7ݸGoptI3 ## S\ו-or+50p'u-kDo`,\9Ϲ88x<ոtȧIl1i ($^4.if[ko/>TIգy p=1ɴ9qMo LqZ=P  |dTfk)YOuRNB@vR N:X5yqh-r}Q[ͽ{0Egl9VbH΍+vL[B[~PJ({O!T>UnqYR49_G`gh祄ZLMYka+LA8\(=ꭺkzMSA=F1TI̼u*S,1H\0=>tJ[gL"}W.Tֺu@>g_AThrcö7ƃΛWR Q(0Ng,LB1ݫ7_,Dglrs]T`*ܜW rkhg,Tr`ࢎe~UcBU-0Uީ|g.|l5[GȒqZfks"Ѭ\-Ÿnb9~Z+ !EL3хrsڎ'vFSG+M/Q޻,fjVj˹@`ŲTnfp3[7Px yP)y^<`@3S}QV<Jsam6][H҂$!m(:C9Rhūi/\~)ox$5uHkQWі4uᢐf24yj+sp]Z8x凂r RV>$e۾Sr)VvJ?LQpȬ49vA"GAUgvZ@Pr0~bERCTrxO%>jPt3))4_R 4q;.Ҋ|aخ˦pr+\[p$qfk$%7FSKO5A5#nJ~cꪛᏥM{~֥22 sV 2Ie˶qOZK.O<Wh.8cST2}B`U 79ks0g]*c{ꢛ!q ;؉mr>cAEwI%8uqT1 5U1HT;,H:~8Moc[)?~Z9t-=lVvIBy gv^<;8? ;dFbҺ)Tj;jrR[K#[]j$QX/bG\`}]- QuZ=QnF~Qw&YwRB T}6ܾ|TQW8`$Nn V% G?*mDcWcf0"WDmgڟ m;^~ـG[o򤐨ݗJ J=.$.}sCRax !e=c ې{qL]:JO:FܙcVn-Vcf P"@n% ģؚ>9cGj}] 㮵۵>tφT >\68[i:lj3f>38E.p'ݰ5Iӷykj}тoZ\G#o&ΪvS_Z| $z jq#cM{5)e _xCCk+N܊M;;4tUP @Aajs\A8O8U[)MydZsblx֛qft?minoWU@`G^z[[IW 'Rm "վMz7_z2InSڝww W sûVJdC 80?k2%d@V6az.# T.V|.H6spOqX\k[@ljo5^- O;[3BsڧG{)U⼁ FqY0fؓ IiZsBKS'ЁVi&?ߢ+C!EtavܞVNq !0OG9 \:cZd)'N*ܮ^9isTQDt٧Fub*ݻA=WwVȒ¨ 4̟͂>x =9,<L]YB ꊌw!x>e]EW-+j9 쐪N?LV1>kXߖ/p89I'PmN9co8O~5 b=B4^Gιjcn9#I(_JYrOڔ2˴򮨭rȄuv|̓I'76v>)[brq] .}irzRY'=HI2TdlXgUcޢM  kHuq'X{yOjbF4b .;7Qrq֢Kbp}Wa<ڥnO^5ϵ]m?`=x/?2<NykZK?w"O%blQ̂wX~f֭˦[jie_q@TjA~bF nEpDYbȔ+J#@:Y&R4Qpk7 B«3ނJZ]gIfaq:lnFcȩjsֲ`ӑD|yTpG [|nov')qQUĬfۻ(U楬hg[(mH>*$c|$V q$NztRܛԽ.oo ήCqH~l/ZeY|K.ֱ;;sLW{%w 7!= hǶb{`G@I&eR4ap:-r׀;$c_*-۟ƞʐ3$v?_O2Ϙ Q۽%Pf9T l9^u2;I8FuF%p5~Hna][+cj[UiJ$WŹ^SZ؜iOIpNְVldv)km\3LD`n浑H1*B>'T&3TQv%Mb}@Jp9%'_-GΆ? 1)i a oB? HzRRmUF{g֪Z1*5۸d܁Shwdb9UƥA35MyC*w=#vN;Up8'9LǭP7 i Tu(.grpr?wڀӰ֞vaYc4hnhTY}*A(敀k& g1882 >5?seW һiȜ*]h,&0q㋳*ZHηgshvI 7 HB#TT@?< W^U 38'ʥuɪL')ykhHc<~œhҙpiv&1K@c9P~^M ':R3OVdQ1o)15o⦩  Ɵn~iG'*+zT/pq|rꦈF}iV iM2X2右QHgѨXbnpH'fu)c֥1&0?UӸX,F?JlUj[~֞!.ywǡ+P|uR ~R{|p?8Y;9 c,1dȩVCqH-DeOП7,zz'i/5OvOޘ:vb21:>8=Z5.Vܹ<3-DFelT-GbڄqZ$8uF> ⱔtz%IR@Ru?:NN sނ*d;`pju!BZͦDs0TB##8W,L: /rr%l#7IP6}W=bvT>/6/Deh gbPֶaz%p=7zԉ'+@{EHeq4pR򫣚1G3$jO(UG4+c ~ul1 pHqY>뺤RJi tp!Q4SX mP1;.!┫cNBӸ#zzSBё3P`ՂN޵Nj҈Np*7 FV 䌚Qojb8Ё (E#oRQ ŽOP6e- nj>~EQƊGE0eᚰGMv}UgHp$(8QT#ǮIcrpLU.cU>b[ }j`e)]Y"f=\)P~.@WC҄&0ʧH%ܝjFv$_'52S !*P̮1횾UEw(lzjrɇãr01Uu]%#?R?L]^^;61ڇBU<bma$dO v-ET,2.Nζ-gk'Çb}kJ(V#>T0]B5++󛀿hu!k uV^%sfotoxx-18.01.1/images/color_fringes.jpg0000644000175000017500000004415613222767271016476 0ustar micomicoJFIFHHExifMM*JR(iZHH0230 0100Fotoxx:paste|trim|gamma|resize| http://ns.adobe.com/xap/1.0/ 1 2 3 0 8 217 664 1 2 2 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?4Qڌ ͔%\w'2>Jm*4*Оưu#;":Rr>ffHYnA<Ԇe+ȗ_Ʈ/J?\]:իXZ .y"qQOT-j-גjȨ-jp}9^h֖jspp~cY n5:[3.AhF0q7dShAց+PjFqMպOFBw"KTe+= &,\յY̧"[8"q]'pK#aT95hi!AR5@TCoQIO4 5OjΛcm?[VOr\qo`+ɮ??W>Gc@ɤ8Fj1\U } b)hin8.3XǽW<`TnPjAi[ 3DJ1E(EH (PbqN5Gq17t慽*{X5iy] "da Sګ4J3ibKFQP4eO n]HTUJ8fL[nXjpһD}MW=`y4K|ړ[%KT08 9ҫ(s~z;#-Z*0j:VЩю)h"ڊs򚚫1IQ cȩOAE51LmnA♶D*Ҡʞ{Liw85jH⩘l rr\fZ_ZQ+\ӳE/j8JqWdYpp?ljɹaZ趨{oZs]Wa0 kK4BvGJLzacKe1j$Vkf$Tf<|x1L<ҒsZ`"ɇJ{RVf=)YtKr¥KqWAOazbs,Hj9/֐-VS`#:bxA=*= aqoϗT0c (aE 4wP0AqK ğoI-.t]xciiO\V{_L?xk Mr2+hyٞjd)f :\uQ:yu.iؖ 7/ҶB;T*+Z#ҴJܐ*' M"TCT-t" N"jZj!5ӻ"ktaj+WC&@MR 7yObåWxzzToQYJ%9I*64N9֚Ѱ 7MfdR ً"uaP3U`ǥ(ls. [SQ1 SҨZnCuCLh9s\D3n)`Z"RzIE-6 i0)Iҥe ks_8OՃ)ox G iڲ4["ly1lj3\;(N(4J*(^@uv$uNрrzlna1(8[ʥ8W:8"eAnC[SK5 Vj ,-Ұ8u[1>ޕSO>5Ӫ IKc>B=u= tXc5<*H9ױy]cۣd8S˩jZ1*BNTU^^Oeir9NdR8b@嵑Bc ¥jF/jQ] +Ȭ{Ą 2SpjERx~GJ s֞&e=hhzOPд-MKިr(']ȱ& 1Y!z.{q8$sH5Unrpja2j&Ąk*`zU[n!KRg21 1Jzt3C{u_Բ'zzusc-.))q0N"*2iJbNOĂxy"PюRAG[:qg3k}v#ðu$qXs~U$U?)c)AMEOe[k8քwQ>VZH)09LRpOSjsY|! 1֙?]SOcZ_lI T ]%F< q`ʹQ[㚊QVspAc7-MW̔:GX H=89~;)CHO"qkb̒XqUde@=EfƤDGLϗW@tTȨ.#]պ ђ E0^1C*F<-ګIץosToT8ZnKGn+N Ue I,AR-4rj̖laRid%54rO&:sRAY{G&`ͣ\E3qU{ޔR9)*?*ףG!q֌8î*Ɖ`UcRc:q9.O:m={Kd2a\\G.V.xN+BP.3;VzZv̜ͭZjBԑC'GvǡQXOCuMYW7(1Tؙ5:WCR'W,  *ʨ JW+WcL[f&l9s1ǭY1)\26+I hGJ*n4o$bhzPՊNk?Es_7O#/|ES-~#?3\i0Ǧ4)A7bWab W\Wv"9JN'ҶfOB'X`C jP9==q\OL(T7 c9L@Nc'贸(S*"OC{KsWv?v0K& 9emn$> !1 [kOPQcgse*ȩqz͔јk)F׶Uu٢JqN;4+9+F-JN2}e(4CWpkɕ5khpG>̿LQ!5Gxf!8bYV$<>LMمq˚3$%짶WS\[YBWn?v*lg,pF2 cP3Y7G1y)TJ%/۩=KkAs1!e5hDZ^ѣv=oxzh-z2=Mht?FR)pyyo~_t SStqQI}rF \qH :zʢ;05uP{k5kAɬ_.y0"UK;RF$ӊWK$JU0Z'`Rr̓ҔRT(jX[W"-;-#%ޥ&6<|5BA@vG)(wsKF:U ,omڽh<1ޢa(Eny$-sT-]7Žk fus/a FI;|+J1#"M 1;g.ѷ5[wqҷ l9hڗ'ڹ;jx#+y!l8jVmLGSWw* ۛ_eY! V|֌V`ytnj7n*cf9FMknH$ ˚LIhs6V mɧߟ }|H@EDF*0H4@ybS*MpT9Qɝی55,,j%zVMSxP4'lKE֐^Ԧ;[iZZ.U}=Π .KurGzu3Ҏe^$QϽl[k&؛&0fO\'I_Fa lξ;%,,\!KbpH֞5]~4YojkzYnbQ\dX@VZ)Qr14 3Mx7SRf#J՛lZ`V6aL-GBPef>q+@f=t)+W[{ M+;$ 8ϭI%gpz渟˙HzۄU8eZKe ʎ}޽Hmޟ1%`(ڳ<= М NE]rn4Ɍ*yly ǽz(aV~o(YT{̉6[z+`UK{nv7+`TѴ\Rf:sSoсJ].WBWZg%}H;W=)#CHHj-CQ[WHdةUަO4R5nQ.o"QOߊiz| ̹?9{œV, c=˃ԌWK1mm\خ6O "g_`ֹ`3ֺҐ:Q3(Wx](##5 ).*[ڴ-WDPI5*9E}H|I`?m*H2}뛟î"Kc{nx-t3jJZu&c5Q4/R6aGv$Qc<dzנ i{!\&sN,ECg&6ÜT>#qL,{mމWiE;nhd&SdO M 'OjƵM*=M^-t})K'ֱl4i['*i,|ϵMf`QRjG[ 5s\v'4ǑȦ59&yōݺAAq-s]lHXzUw[Vm4tBkU 5n s|VeƉq̄V&%R{sI$hzrXV"'qhv'auOOF*Km,qRi3sso|B NnRsxVD,i3NsHkģ PyⓊPNyH#J+3CIP ]\~ʧCֽ-k.8柸U qxoz9,K%26@#, k7XL9fsS]1{p+cz00@YYicNWc[f1!"׊f\ϵgxz yT8A9tGo8zs#P]rHb'Yr='MtĩE4Anzzݠ)k_0Ui$Cs\i}vQp*wH>YV[#$AEyެߞUz4rZ[n^;ס›b4QH|)'=/ S[J1gjܟj;VF ǚ{؉kc7CM6;>^EaxuO5l]bOc65fIv)\滹xVz|R9S2d?8cWU f ^}238&[[ÏQe.K.JYg4w?fr/ݰ1XrjyU6j9%T#atWXO. 6Oԍ 8 ??rIZk+Hwɭ&"oC(luxklq=KA4q \9ɱDgbtn{bs#-)Z]!º*HͲaJ&&3V nLhcц:i[&sB˒1Z%L CWnNozk݉è2*Re,C#Iw9=jƀWQl ź7e-͠v#OUbZGcoKڐBHU6#RmQ~^ȣyW640ҰkMBh,k!L=iR&rjH*'s0Ri6rjsܟ(V5aRn\毙I3@%nb  '%/ڧ8.DE$9y2ɸ\ uP1\k$"\EkhrzF ywyrֽ As%( Է"RXA᫘`sk?_n%O2 q+%蕭]E XT^x>j܆ka_<Z?nApv;oXۇ4me\~"Ӓ0uӺW޷4 m ޵Z(&1 ع]D*88YeGzDY8w f'Үeڶ1ҤksIPoB#8Xz*,w܀bIHہPoԬOJa4'RaWA0 IV2F3Q54vtHzҞ4RyjNj6_A6hOj0Elr2 SRFr;($|U7¼%ej:UFNw/Ӿٶb*Y/95Q]JKP!vgU­S5E(ڰsmXzy~޵u2fif*{iAYɝxC%[wculS. HF${M86nn댃L$ә344WjsU?)V{Ujٗ5ΚK*iڹ?OԍYwXWSFO`ֹf* j, AGVMt`V:.k"YV3t; R`Aָ(%uquy.OSLBYT$cu偁\fx|uڥasZpv.@"MrTvS1iHTI[1Zc}mÀhOQVVp3ZLq`h,[=)Orq!=)|Ê7ikKba)s>&5y+ vlk1 5h1`r+]Ө>,P{W:Wfv/i U9A($IҶG29[FMfM^޶^*}} UesC8bNkro PYڥ ;p{ZS[2: 8Q'ps\ƃqLmҩ lw7Zכ`V,Z?ysM ҁ֥;3_E\CuF*) +KFjb8~֧nحppk̭]- KEG2֥LbH 7IAGz4QAXU>M]lz +޽,4oi/)MsfhwQNttkhk no9=L,F@W\~%8)~^F^r)CtzK)$V?ebB!lT{ \dw2r1Waծ#x2[Jy 3ij%Jq{685o0HpOQ2tc;ܕat0 '"CzT{^T*iţǥ(S\pӃT@>aj. 4Eҧ)xߣ tJ%5brZx@6nQ\οQYGfZ'c[p:qZkD c9;ɏ)VCj#KԲ-q.e;jMM"5׬qQf4gW{R+EVB@qZ\ 2 ͺ!pJ=+t⣐c659#4Ƶ<W(0Vs wNrSWgDj{g:19cUu?Zv8~6ӌ:k)E!qƣue8aDt:>wYH=8iMVJXEach+-oFiڰWAoj .B/5ʰu?e rxvн H5E1>"rPm ݻ3ږxbI(ۍ655|r#ZKs'FH) z߆rׇ]xIƲg-9YYNCs2wTBp9f&P9j:@<ʑnHn z70񪚽S( Ms!)=rjKEfpzu9+kR3Deԉ{ i ՗ hCu !Jך.-n, #qZcn]| R`/oBݲ;rZTA\~_%Ժ̓K'^})sVDsU5IvڰSf[J*짡3cN ;W5)I>.4>dcڽȪwv6oYO@긭Q27U@3S$aԎjZv5ӊsۻ8$AYSX!*JVdSD~V*V!›a:n;*qy9%GS:9 y = D*a@jhp|yAZ <3T{>yYpkPKWC}zط|zU  Y֜yLI!UMI)pri6sVЙXb 2jg2g5׊+f^;V5d*3ڒ #UtjIXH$Lt%F1YwY$sZ1YZb2+:Ajsq0t~Xis\lIo_cm?[V }mI@Ip_k''|B?RfUpmKp;J=>_N㞲E/jI1Wń3ɮ+;D~Y %sWÖ,A`9 i9ңh5;1ykYX^~h}а 0=Sai-䷚ݾICYmW^[abp{HJX}k)j\ѓm}kMzU].,[]+1! *?z*C!?!5jt,,G\Ն$fO4:b*z3=#k!zYOHlp]a湪#zjU50cr%0ۭ(HiVGrx]n-}7?EOQXF?`12]??p_\ŷ|ַT^^Geh? +PS`Q;Ԛ!p ~[ *T~xk ԋjvjҬTP_H+[ޤ5 k!>}|ZV3\o޻) ug\1BzS)\*YN(>u}iR<]УͰ+|cڼOZތZ"BR`db֨]gg]qC+7Y,wNmc+ܻf$Sޗ;]1 V:$Ո{}mNDʚg[& s\z'۽ yK?{|Gˍu9_'/}Nj2Q.uErnywuYV$R~YMdQ $ܑ^;G;;CE@{OT4.uo֝ՕT>7~=nkp[~>2"Ъ߫qg ?gOFop>#MPDpQ?ހt=wN n[^8kOs~]RflaK.k;ڛ4Y| cWaf0'N^ߘ7%~9\{=GjDD޽_vO~w/-?ɳ&RK+V Ky!|m}3ut>Ks<-ㆱV{-%MÉ;B(>zpFQ&d ˺։^gܻxSj]첐%^cHL}1nkEߥ^ms(SlEZm;:ևVktYLCgA} KfIf,WsHm*>t$k,8QrcvG$U} y*rQm5/j]/dX` ޶%sW]eDnlժ=`Y'Frؿ[Gi58:{ڣ0?N1fm>p؃56\tIs[>VGhFpFιDxWNh=fŤ a49HN|V}$@dN7O HWɼJ~`.݌UE0@`\sMVvT8H͹.E7 2]1Sԁ~AzacDTojЌjz?^&P:' C.es{tL LFc4R;h;\4}Kġr9P^E|+w2mPz[NX(̨W?'2ԠAi'Er=  [rMI@j5d="兠 ӠTmP% I LTu7Z>a@-PwH?~(G[zst#jT>#>ɉϭ@i-ka3ҕlVxFGq& iRSيRZJ72_V*NG4=Fp}!аWM@f)ͣvr/PGӉ|;?J]C/>;QTEH[k J'zQ:S(CBB7cB>:sBIT|dIDATxݚyuU}ۺuZڥBK$t d% ^C;88x 1N8&Ff1!zZ޷U=LL:;{u֭[e̹]C*2HX[n10>R[X udwWK9$Hngot~U =s푾 "WC_.N4.9m‘ "kny循phmP,vH>`H}0*eHL I;ǵHK4\_YexA+m2l rGFp '6?{*O+KKƑ1i4gVr~% "^X'6n꧘l1v'өw#J rq@D2I6׃ܷ[OSBبrR*,}+4nX) ܙ\q+׮|n:?U|"'h)q@DrCnw{{7?B7U*D,>ZA Gp"GJgص|ic=KN`W- ]'}d)'$J*#Fq`"F8g*ok\LkhaEx\*| s4P߸3l?Zok"Mwl:/=#O[s ;Iu;Z+Rɳ}JR!uBS`4q6J;ߺdy3" 0(4 OB2XI9 -Eu䲚qEČ]zL8=nӤNp PP/S^4-ap̒( hs"qOt 8̎ 6Ī^1xD*mĢE4n%{<Hë':;IZGj:ԑ$i#o~|_X=+,hߗ~{,QI2zZ8߻}NU> FDb݂Akȴa >G\M}qDD%ա3- qО$[=,rf?mfӲ Sb{rڜ3͋|o4KKg?2j"W- 2y >L82DrLjNhp]7޳ "o']Ib[@hZp6Bg4C7޼4c8K[cF+4G< [@Dt<}+5u f ~x_b Z+Q fFLDKYM3_&M,<ħO1M x!s^7+w沆i gSk8pEۅ#qJcB48F "0fW^Sl3kJ)w49a.xt{׬?핲YB`| QSxB݊3YP- PN J҆Vf1 rawt>$x'fR&{IP8c&Ѫj1?).;ZcI4dB@HDTT+^sO'gs$,<;䱥1VO?! O} ~bBBP)b6)3!XN}+޵?/-)' 8Rxe8Ź.|U콆m(ƟTӄ4q`dEa-Ŵe]_FuLDQkﭜxR.Aߠ| Fd2[+ q0m̶x-(7/ʖ"b0#焩eEl?nhM "Oۯ- A{ 5ok Ƈo>0;,l [cu_]'Z 6bK9JS(YV'$ߛ |ܱ ߸|{4f=7PyZ2d<XٰDxp~{1,j{[iP QI GyȌ7~<W7ukRL}͒xa=)F"Zl-pq^&[km{߾rө2^mϼ2Lwf͢i,Q< "(-\`-vul+-(@D!(@`gz(i߲ȼ]Cb{|̬2 <By5fjA~Ri:P4gqaaѼsLGB}Jh1Z4#{rYwtW(Y-UieϻgrpO `xX{ cP/?D{oߟ^q8—W}91L-8͡COxjLܡNuAgUa:t²Du5>hr?ul)-z?_v;-~6mmˡV0*L'{>L_܎YMheݴ F7NT @Tjۻr^r.g=ŧ=d">V2~hD6^&qbky[J@j>'"J,NXڶIaj&(7<4y+S;zyh81@+"K.#&cYMڼjZ(JM;fm KH66LXD$L;et,]:6֐5 Y޺'Go_{6(竏|4^^% ,jƿ [źV^LW8`7U(2'D *YZ:^+D$7PʥOKe IZsʀk "DI!cF#4W>d d<6ޱ~J|N~ѩ鹀ϥ6 Rxu~A1;J537R(UT@"E"lZ W,E;Z^1d .?5a7fxe;k`Ys@6N%JQQR%4\V>?4iSA!7FT1R% f%@rBa3[N& )R+0Ώ3Ԃ%?erz0(SdBXo`(.s!3ЦiH*Qh㣴Og;bvul>i'Vg)fK, ~Nq{R3+q7r#1O u"X(Rax_\yZ;4 LTT*"?*;1{tnNH,ϫ١hhֱir:p׀ A0~ Q;w|#Hc.$SU-"R+ '@[zEW8aI&>_ĈkĥXMHZG>*TaJPDs~?[_X_CVF j3 ԪgtzW*gPlJ7UsvZh4Ͼϥ7ix9Nw0z΃TB-zgX4s'&İk`) -O~@ψgg2>o몝숲gk TARG5L$ MymWOi(}cOm?X!hhl|h޿r7ck_(̾@܅ܷ |~i귳Ԁm/_lkgz:Q,XW,Q ͚?Zcb4So j(fiÃ,s'm؅IQ <љyI -Z^oۏ[mxw$u5w<ŝy o>-n95FёFìvFCFiњjP0Neȩ4Cd3%Tm?d'yGS<=NBQV dl%3C\Uܽ}5&ۖ3D`D(EDt Z$LWO=J_ouf8|6 DD)*q=fLhW;9*E*.uXKP [9p2m s+E8g^'ȏ^OƆՔpLzq~i;2W?E/ًݓ+q1U &x=aKqp4s}x E qbI}|mW>IÉM)>0ݕĉuD¾BdQQ;>/K`JdՉ^\sF1-Zsׁ0@cIJE;z |58gf4 QdZ4<:\F"Җv;7mvB DTSh!N8qđ#,6HdQֲfi N̓egoB8{βwnZq `L=8zJ_U9scW|%Ma*((*UG:F\д6VM=Mj!L!&B a,ı#Ixqvߢjuhʶּ?ܒżI)h&ɒq] gyyH0&3cjɨ*ITc @d9DY@,$Cv39}AWvڗzQf4^>xKggg{0K)UW_JgڻKj AhiFiHR!Qq I"8'VE%Y͚ ̙WZ*RCjyzW)GOo ?2 =lWOJ~7^ /d59M.PFډJ0ZE)r"+2xCZ @qߦVf,c@B)5 E+܊nX$s{F첽]l]#u9ohjAMaO2*`-rUCxbA@:5l[JL)jF 7TMeXB` 128 300 0 C     C   s" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?*PfobIg^xKy#foRA"|ć#zW>75:|!hdi4gւ͎>JهbܿCf1IJэS+-^Ṕu򌏭E.l[Y}$G x} 1>xZ %ivF]!#=2{{׮k7X}i1y~ԑll2-?ֺ𘥋.yaoS⹑0'@ }$Sn5]a5$RNHsV}we"6c>lv+埁gG%0_^:.e3]<#حUkWa($# + ִ%_mma$28I Fގ`+_)A7ǣS[F% qM185ho:Fov?~+ǭE}#ėwߛf%B `U~"xƂ%m,{kC2,;0V9)_o>HIc6_c|Y?wڞZ&?dK[IQPYDǵU@/Cxž'5_ jOӬmn{MpJN =@k_ >Jv?QG⇁,^X{K]Z҄Kj>8m/>,n%/gFQ4H )|TݿFMEixҬbm8jc|=#_MzKG>ռ}sc}[8ii8}dw+*5rc$ ,=Rֿi_| >ҿݏHxz7t}7zީos{m l"/06w{59㻏xE OkZk-ZlAVFRs x'0Oҿݏ}?+>8c%-d}XOmh JX5i oY 6)o[twTuH慥28Q1S99}.a\up (@:N=W㯂2e / kVFJ(ݳFKF|Cϋt$4KM 8_ZwԔҿݏ}+^}ޗ0m}(Jv?V/_ck+X^(cա&F𝻁?~"c\+ WWk5-FK+,RF.cps{@DȣV?W5|,LTkE4&c 7+Nm7I#4,ƴ h}`}=WK+(: T!G5\DԪ'g=m$i]sG l]M6h\L[9hes=+}xG|fs7MPXar:뎵z}xG|=.!_x{E}CTh).]v$1o<#E͇:<D|5:5 t[qe2SR_1xñf+[{t Ȫ>b"[G}Ýf~x O$-#ع#cvhClJ*x kÑǁ4;-\ZYMwryfEAӵ{oحȣV?Qݬnp_ 'Ï(cD9I8dO2o__Lm3^KB3oeeea'1_ EzحȣV?SpSH/|_^[[oE2_\A (^919gI=8Wԧ]YGm "{뗌,wFez9k׾o<#Eb"#]E̻['OsI2x3"\>P<]wZ=ηi-Eؾ1Oaш?Ѳ9Ύ+ |X752Wy%'q)<+Ytoglvvb"[G#7o(#xZWGdٜi O嗶1I oRZwx&#B$V}xG|=glO[GحȣٰF_=gj}xG|>o<#E͇:2P~d[Hl-sg(osz~v/!<%MV_xsC3kg}%jp eݞo2|K,zG- ~0|C62 8V&䞛Y խuu;{ow1]u -a[WKEdC-8"ኖ]ΑŃiooŅwozZveGn$vW~~;%Zn7k7eGX$Lh!"o,OÝGZI4Xm61t v᜖1pΎ_3W~[~Ӿ'F־ѧ^çMv/̐%a8_rkо!i6e$JBX$x%:a_/h 5~"gRú*OIiQΉqmY F` ŸoioO5CI$j+0 l}Ф+o~.zq *^ 𾽩x6Zͤ\ GSK)z*߃?hwZlj쯴ҟOof{|#0 3*l q8&>| Ÿw'񞕦5-byC  Am.Mu3fEaWmu-CdPƤΌ`A<+}7r뉥K%ƍ4MtX4O Yxqw qlE" ʁXl=G;OxEACxIG|啍,JpqCw鎜QKZ]?ʶ-'d6io^6"H/Șbȹ +œ[mi\꺂=hwS[²4leyF5?+dRoNz΢KB »dlg_ 5- O jZƗs[j7w>Lۛ%D.nl]z,?%'Ӵ%[F0?{@6$KaZbRjks5y.r3\&O)yr :^c~Zƃ6axe/ƣ5:$qyOGa# 0Xo+y=KǟR--Xt+ji}iKAX{skiw2E+2)؁=vj>5ƷrC6if]I#8,BrTM6_Wv (aEPEPEPEPEPEz6xE/,dG$ 1tTVM֗soO'/5Kv۸2zu{nj/iZ%Ιvlv˞u 7dJ>=ԯ$[J/%sa/r0]HV98dCҵXKu9.!o./]Mq4c6YN @=Ow i[ŧhג=3YDSo|ox~?Ha@&G_",Cv ^^%oBĚl-z'Nt[UE@rdpJc-iwt=ź\jAu |ek9G4-븮coPDŽ|!mOTB#m<I;q,qƬ͑U$yFMîUxnY&,o-^w2L0H#v2_6$4G֥,صkxu}.)EQ"y8']MǏ(t.dm*ӳߟn~ z vCĖZu-tFE@rO QebxG>M9>mgaqy$vpeu7(cX>hwsq %Aw=Şwuo;&&! f0*qgb'֟q+ ,ly1Kv#wܸWxd4Wi'rIe%bwknב4$yGnCte'h\~AEPEPEPEPEPEPEP^qYxLֵ;M"꺍Gz漚td;pr:^RѮԕ}(Z,JU1]G,bfcU^gdvswGNo Ũ.㸹xZ:Ib@/_+YFGqMGIMc 麝u˙4dn^JXe oڮ2MgM5xNA ]k1N-CDĺ[nfX[=!Ӧk7+,Fr\ ʞ+u| cx,Uyh-dJ0V( WUwk[+;[%?jY/vomk0|Aixo-ʬP; %~ϟ-"=Kvxcuh0f]ta_$D~6y S>N|i[[?%x-oHZ)dF){`v772|Wq'-_S[Rl[3 ¾aYJ6JFxJ*Ug$i.l$3ךS>(~-MGphv5h^fYP b9^xZ(z??XI$^_7 sJ/n5{ CfKl|D=7rj͗졯h0xr+]3Z_]IjZJD #ئ!ϐ ;8ʝaE4|*/O<E3+qaGė_Lm..nR; :|(\~Y^EP0((((((9TD$\~u>f]ϲu'U(u>y_Vh u'Q]ϲY+y_Gu>f]ϲu'U(u>y_Vh u'Q]ϲQUo5{+km%;Ycz I'Ұ136Nx^Ԃ GV؄r9u^u'Q]ϲ-ņksC j]IQ@$ޠ~ݯiڎzrX..XCѕ@y_Gu> n'Y MM#4dgޠNTieRH)fW4gκdjy_Y֗ZJ|C3k{_K/P wwzܿME]ϲu'UmF{"U,\\ms.\`] {T^'<#K\wrI ʍK9\ N(u>y_UYthQ$nF* XީxPx;o7]'ky_Gu>}STFOU\cZΡjYBJ['ڍb$vϐc[ xö&sn<2+9eIi ;J$`}Mmķ>"ҲOƌR QkEU/jm'/?Xtߴ[km?vp*Ron _|'oϥzOiVO ih" &D,ͼ[~&Vז =QCf!"3;Jg$k=zV]FW!f{LlYZ ]OcM+W>t xcBX 4d9Hq9oBM2yV;z^[BKd.vDWD}Aldq^ijV KѴJBq5UV֋]b)8otIT+[r*VioTty|]iYR/5v~%h_k{Kjvx m1FDv[\'ټ']]ͣV[>q;+<7cm^Oxj7,:D$ŧ@"}OyZ{ƾާ{rA>c2;Srگ S|_)5_6kWX쭯n)Eq"8ɮB=xZEG-nXaXB2 -&e9W%k-ƍiu7Nm+g2d#@27zb=Oͯ?'r+ͭ|N)}R苩` -9^3zψ4GH%u6B@+#r3ҹ_6lO[X|pj -HfUc4i}ῄ;OY$[r]A%@E>!L6uRF זs%>1Waƒa~xDӴmW^4eBe`kB@W}Ku뙙mLIJڃB3 M>լ/OSüw~%/rKchj7d@BHv[xWv]n,O[ioIfR~lۿM:D7>s4ܶKv!cw8\sY>ZV~ |mO^ -uK[ZԲ0x 'k[L;k2e{p麮WIu 4'4nrsNzWsCw*ߏQE"((((((((((((((((fotoxx-18.01.1/images/custom-widgets.png0000644000175000017500000010343113222767271016615 0ustar micomicoPNG  IHDRC IDATxU5gt( (VW]?_{ko;PD{AQ po s{<97'$d9% @@ j@@ @CE@@ T TW< @@ P@@ D 0@@ T?G)*#F{Gw.իW,*]ԯ__j׮-={,*H֭믿Wj8U=Jwe'pQ7]7o.q>}[d~#EhJ8DTzK.B+&-'Ld6h#СC99$:9Vg@ PQRTU̙#5k֬ Ц?䓕9 KAStgM@rKӦM\aVSO=UfϞ7sʥ^*Z2iРuQ’32k5G6lh^{mAc mr)ȟi᯻:sON8F^x 7ei,onݺ;w}*ܕJF_N<8q&+2'iͽP3?p"mwaW^}ZXY|n&K*k<Ozk`*X\|4'ͳORM6_Yf 6Ν; Pc=6.]/ AS](q gXXH8x\*hѢeŀ׻wo4}tKL}MP3J1#7x c}ѣGe]&&M'x{5ӎ-Z/?aL8e+kڵbx_uUW_|1;oVɏ>Ȅ|~zXC=T{wCE`a+w>_1$.9oV7| ]wyx&2F*7;L bG^}U}ݳ2\eG#<"?&0;Ց[`]+ |cC} 7n8Ywu-'Ocʵ^^V[ܘ 0r-6[qO~aAdM~O>D\!cӞ^=`*pMVI{i|-y+<DC_YTt:e/0gqFn?CœSŵow]wΚ NN5ު%w?Swe̙N~B~'\; @ns*;fZ\> UKEw$G:98tpL9+TKQdy+7ϋ.(S+$iK}\N\^U7>&L0.5^-uLǑo𝮶>;N5O'iuOϷ~ttTu:0tM7oYT>QT43<;e<=Wwښǘ6߲eKt ݐt"t›$e&##?Y7 x%`QgI&|n774Q"ժUÇU޷7VY(OV:;c̓S][ot="#4Qᗤ<86=QvOg]Bu*@A;Ƹpfl<7~j<# |M7'R@5h,=Tr3}I1&5-6A.pJشͼ'/z衴ƻ;T/>bpqy0dJI"q'ə363 L^ADɛW!^y^l4PybIX1-_Ҳ;[e׍B.W{F hv+ua ~MݢOin9S8.`W7h\h8]MgnԲ"`08=v18%0LB$< J'0ip |ioI% _lZVbPIrYg&5ηo&L?!1˚2(D%Γ9ts|Tҽ|@H;Nz<2`ͩ|']vEN;43s ZtjbeW> hD?VU6|sIkI䙤%ORJQYuP#^BYkjV.EnSg:N P /{]vv€jD  eO̢'^I!ǭj*8WAIS:L v}rzj)LJ“S#T}O?ԩiD 8g@yEIn׷urҦZ*7N #xN=\4xY:?YBd -\|uyyH5N'aNUHv%N7I1KpG[|@8]ep+tG.TK7/$ړzB_PȖuuli nU2 8RpNu+Ŋ4C~Qş4߲X1иG͜ҀOd4 †NNstNX6z(Kpm/#  J&(0y,M$DEc6K]>yi<ӬׯTsrc冣6AC@@xN:ɎVz΃tKsdO/ y ynT %5RfdOJY8l`a?W&^z46&)b_ҤʲJRc /z*Ԓʆ;+;ߌ~7.C@k.=Y@Fo 1uYFE6qLު g`8n#8Fy @f=Xj5"f/AYAъ]gj+˶']"&ZR/Go[դdnO^8TZW'oC=I8Ly✓eeʨKd/,i@)hР)58Uci%& AYnF[RC/B5v{,-~w^-3Q-M;Vo&~1U~ƻɱ^~Ϟ[x:ndļ\n:L`rm: yG.crr6L)?4B>O| Ys;%<~h?I]d}=7h2/8Mo_B&}]2x+WY-S6O6;CF>p2Ҹ}GiYz@@ J$22}0,Scl,?{6Div_q|8%%ͮpjeٌ3Q93/9vذai6Z~邝'ѣ=7|L!VZe#+i1 Wv[i>=Ah] 3>_vmgܲ%,ozN;dvC9P( ?dʧ/tv sϒ3,q\dQqSi|rOᖇ t]^|\,IzTLj3r#Dz!\q\z޲d~G'O=N/R6?|e2߳ΗKO?B)z^G&]v˰ɕL[!ONs9QLR'A:/5j!3E_M4wޤ)e~=MGKֳeԈ, `w,%"ќ=.qKrs]qc$"c'߿ߊk|έ/0We}1ՕW^YL !(qq>zE PMRp-bܸumYLsGڅ iݺ~vQ?Fπf_vp< m۶5tE\{G/ta>` 4n'ҹ^{ qysp~O#ؐ@fɇ}]v7ڎp6Y6(/ͳLj(uK9t׮\Yҷt[w:e2?#-U|.ʶ"ox%ys1Os^j{Yk5GˣLN(nE\c}}eZQgˤvңZyd6:m֗M-V_S6@e]?:r=Cx?'IXO6l깒]=o !%=iP]tt䥾.3\cY_:p՛Ɋ3dN66UԍI( jd##g}vNve*E=Bu \bE=<_4I$~e}^I' "zdmMrիi!6-%*B/4k,Ђ|ĉr}ɐ!C6#s#m 5:P\ ihĹ1Էo_*RH``MЏ‘og4fh!`GC> PcTIj6i& eU*ՕFꈛV_?:o ezYce Ԍἣ䛞[ȶ[o%uj"Xf̛|?2C-ysSǟRbΕ;!~./u̕Ԛ?Z&ĿJN2I|t|W,Gȶ$}Q٦I)VL$a,)1FRVډ'hl&സyq@? ĚjX"zM!#lz¬Ak׮L)7x4ʘxb9 m/ܹ.sn.m3pSz=aƂf yxz"\%foy\)7~2yd3(x6bksĵ7tK&ˬv*N60crz]wb{Gsαx^{ /& wwҧ7B'OtʄkGuByBAY%x9tfu7n={)Swq5+M=IO&M ?D`҆!N҅eS3q 32@M۶m>1(O}fE2oPW'W3=j>Ӛ,c"9"1R.xxz\4'd I_ ^qG9(R`[LZ-H,TS1h 8es:3?[FE{\a;N4zתwѢEWrR&4xR0^}Ueպ'NvW7 ~T)4j)S|BkYiN?9+ԗjcRmvC&L-UY%JVZ!\3i|3lǖҵciӮmD>U>;Ļ}V0_yy>1<ϡ˺׽ w4K6=ao|1S;ʫnhyU馛p(=b[lZ-N `TuO?t_lvQv?|ڏ6׻w ?q>|I'M+S5oܩ|ƟvB8OgNgN5eV ٣ƞi GiIgNʝΖj ЖFw|8N S3Kk0E|t7=w^g]m=vtSsgn6Z4Ov#mNs/ {un[M7>Sݻmcwĵ_|=enύ7qLiw]OxˍxYw}Np}CKvq]{w]z{\ÿ۞r["Ȓ8흫u{[o;xS{ܸ鄟|t뾭/ s/=w;qO)c!W$ Z|mwz농}߽M sݸkuVs]{ܑ6tu:̬i n*J: 3mY{(T@umڴhscNqƎv;Zꫯ:Ĝ^ }A. B}TT{nSH :X9]pO<]ˋnX6wba̩VS #.3= ԩ;_PoUIa|uTyan#?q׳)I诗EL=f<}+6NW_}{=ÉqϓNfN+R=K5>g/DL0!Q͈DxŶb`~_&z}IS IDATE:A57o=AEMV7rXXe-Q8yxJYu]*QKV UMp\|rʅw{V VkxLW_s>cf7ϖ-_O[u^d鰺tmά`'T7誔N? &O-hбS<]yy>o ÓߔOD/ĝR_i=Cmڇ[ty^ѰUVY.s)+ީ/(_yasOy*'#&mǕYl{(qd{¼΁ΐ%9F&n g)v% PPm%G_+p:->w[ Y!lEJO1{R6,/Yswjd݁s{/Y0?Diar$^}~hr&&r䶈B՚KSNY"s`l>< Qna. بo#҇|w)M0G)-ԩS-]q!ޛE G7Q~=jOJa;_<蓁?j7ʌ?J&.Kz|y"5J?|C21Cb|.s|ǩx;Et+I]O#W¼=";5im.Nj 47h I҅ON"[,6,f2p*c[$B ES4 йܢt>ό0FyAtWEӅOƩ?ifF *LޔYXY(L(OI҅a)Լ?l &a` <]|BO_u?]jJRJg1G뮻e-30facI8CS6TZ*5˘,lZdgC?>#hP8Y)+!0{f H+޽{y:$aŁmnh:pgy9U* lOL4!7#B;}33MO>dI|X9 C b3߬+|>Ͳ1Zҟ̄ G=+Ie SFcTxSI4c].*/c5W<1,5*Ӛ(Pyb3(dVJ3?=ǒRlI_WqJxR@@!9vޖѧ"vcDc20dI2~t\~@PDŽ4mLJE.mBULs'&,ףF{f`P)y@Y Ĕ i,=1FZ3qNIٲ)u9nDGHF(H,i_M=Eoe%[7㚂 L6 6ד̄~rd򚏢uB$  L*:SMgK78Їp/w|PMRJ- "h  EUr $jjcA8jKO;ҮNs@ݶ?rT NF*g=#Ώ ©Tskiw:H3_Hu9]vFKPLi:a<Bӧ&k7'' TSv&a ?G,/D9ݩPkXBBq_t>CԽCvZ$Y_I"^ޟ{҉C_o'i葐GWO!i{(OE1Gy" < }'zb[(N'$2i#  0cƿ,YhѢ"G_Tg$8-]Ҥl~qZV̰=ܤ2+.j1䑧7X T2mr * i`.x?'`ZB@@ ;|`/ϥJ@ቛ1'%P@ PzP]zLǀ@@`\k9t5;dGk~Ď4vܘ #?Jb,kEUKK ҊP}1UU%]I a "\˃@T& "QsVAn+%1r}ɤIJ. D햛mY5^B_zvq,Ջ o@` As?Nܨ]]|ꩧ #tM6ۿO7~\7 2_^崐N84hۛq x-q}0iým۶vu7}yPZje߹J37׬^wui/g,{*6$y~xӜ-w׮]9&?M41'7+Z^Hk!z~+T*e `?'yGv.*,rLf}Q9sˑhq<eǽ;<0lu>,>U̲$58-ؐ( dEC9*Lw,G}6U5i?}כo~T~tXꫯjժɓ'{g|B{gxg9'vC$gqF;/7p%\TC|ZDqP;N3a„rͣ ދoj6lc>sJ?3$!*^uU$;x#HQg}f|ot몄CGWS9sL=i7^Ty<*{e-#qM9f7 j'ӧOwݷ6`rA+T}Lw8GE4N :wU+\ `N@@`a8B?,pѮ.tuK+wޱ=;S ><9!qt@=Q4)UVY. gLT eT՘s04hPO[[1DԩoCnJW:/,G Q5*mA0IQBq<v}wj$婚q3Q-*SOɆn( cc#'NdœZ+ʼntdxRoǍ'Y0Uܽ2O` \5JR?f7?={ROO0qܼ{l:OzUCկ|U2 ŋ@/!@E?kHUknL`PԜ~s,[7o7l?H>'rCETf3%vМځ ԩSjo-䁿-ZNza6+G[!S5~PvW؄)IʓIjAM&@slj g՜mW73χL7mFfB^+3o>-d7:(6:I`Bʩ-L&&M:O>$P}$y~y$|e$8>NW?2t+kgbWfW梪Vf-HEw-  ir=E5A(ō [QMQlT/{|'AHF+ϵ~WKܳ^y.E\6z{!cƌ^+#o 8/u]Ww_+ &xP@&BhPiOL.뗎%_}*szJwq<*Lh6zal+I}n\8/aN LXܹsMn;vtb/婛-`J31SX[PO Ԙ@-/-ʼͽYk0N3E,'{!iˋmpIDV67'ȠVi 뭷©)Cr.\ ^U4cX2ln'6ubtsvifDeZ:j|Kā۵k~B24KÞ<wyg`|u=n df:]#v6Ba6B0SD!bJG/Z94hq).̎CpդО"o1IT$ƒ9e~ BLy ! L0`DK+b/!|&wq#I\s%DYQPQJB4oQhޙ$fr_\eV(]bB@+X@@ ~NPAˎ53NP-g>d;RwNPMnώsx9.b;B}6O?ԩN?TTT86?/[\8Uijj~Te2EΩ ?GO8oW<9%88ɳ\Xa Zoé0(ҤSxGO6I{^V+;?J[7UtrfAE5pHgDWgxӞ 8E.qg;#_],WCS JD3RX3s-˥s`bFO^j2/l>l2eTL Mu1h@bXvH6Nel&fa`( D[a4N d l( BuR@@h8A/+%5\#qYOf1_6X2rl(bS?E,@@  g@ h:Jw.KKU'P,fՊq A^(8 (,](I&B_^>o?e4`Gṣ:9͡^+ߊG/Rrzӣ {AHKP'휮mfKpf@3 12st1gFV;iz^N T<70 X z뭫||lfdQWk#pI߾}ر(PE3znU\bDڵ+8.t]*|=v3g1*NPs7>QHa@ :HU:!۷|cϹu-NkF-0,Q<ERE1 I-&AKs[ʤTiBu |K)z]#ײeKrmfqƉ 駟6@+_uB WZi%Do?{8UNVZv5WnNi?/n͑V4UyYzɇ޲6̮ܹsWsa 4]4n`¥3\{ݧO:_ZHϻ̈́v-:נ1  }WޤH?ߴxAT^1ڕLD4YehFi׊+?jx`g{1nʹzWwQJn_=f2j|DFms~8?|+yO7wjc+7زP(n̤Q+nwlvd׮r?ܩicY\N#ߪt} ?<쳤2^rYg>8>fxq;M<َtnj(/)S0[t]w%kq}=ZGJu{f[놜vi ܌e#h7ʀK~!8s&?Eh]09(qˤr6lꢶv4[$'3/jJN?~ 9Vj[t~ץ^jikls,'|"wol׸s²)[n}Sw-2o֦oX{Q^C =\ M6DGmXRuà{64G}dv7Z6?:m^vGh4'}Vf#`A=|9^sK*Koնnݺnfd-O>dz%wLE$Z&LJ!$ VOHgMROǘDi}I/i>+\Axu#|/iRϙdұcGӶ՗_Znmm3xAIOΤvD$&\NF[',_B;eM;=lu[n1.ucw6eB6C>LQ i/PksL6Sy9&n!k򀉌'ʘ4 Gj.ڑQ3M=saD(s&SQkjhao8n([||S3( dE@;f:Ϭy"d Zk^֒R-xbږ ) )լsN?aTs[̏ 0#<2DJ QJ頑]ox-:pTi|T͛¨d>IU-Xt=Q:Qx )R*Ȥ|g Z]) `+0R:Pw5)fRQ>eJm)@S:hTT3XxI/U Hk~TK`T6KħEڞ "O&OxВR&]ݖ;ׯ_J4^*oatIN8ᄔNZi~=pI:DTk*8W')Szœ0[o:Nd4-Si[Xϼ҉a#þN^SպZZU(k_ Im)ZQ`$_A=Nwwz+BWݔ [_ʔ oe#SQ`CyNwB,"lR;\^{-KhNCFg6,OV C{V5hѤd#4*hnќDI2~+6yZBi<OV NUBistΈ<#4>{X)2'j8β=zB 6Cl˼܂<Юz' /4p'}~-_{$Σq@;D6:'wNx¡UmB2L VrN4˨`9RhKD|6 Vu IaĊzY q{h|w_0bE"Qw0}7m˧)_no7E㩊Tԅ4 B''*XT "<ƚoղ7җT+l KLܫj0o((* jrؤ3[ZX$? .*TA|BN9 [Vҍi.*+J͒7e @va᯲fESMٰ/^!<JKЖyyTA{`&lD)?cʍ, ,H 1y!M?\7x `aހ0r?DL6Xץ{t#L!,u庳>ʔTXs3[(8V ٖip^GÐ?xcÒUE` 3,1(6CL ҈@y iBx'ծy{x _fLiLhs|󔍿 ȷeLti^;mFѺ+NLfhL1}u>ϛyEO&>p` o&a #h{؜c„s*}{hKܣc߶3eGGZvCMq̚h=Ջw@ # @'6-E[tpw&4 4aOnzx76E0YGٻۄol)/06ؤ/jIE˂~`҇I}Pm޾}6rBfclThDϬH]-/Z}H߅ƛPu L~' (KhJru>0RJ}4t!“NrY8p BE@&/hEtܹ҂p&SnCZZPlo7"L.쓦=M61ElWr҂6J;BmF&"?4tsa-ͅܨ'V 4͛!Dq\-zY Qҧ EwN_ CzQƤBqrl%ζmۚ0nPgbE}fsXA8΢kzz Fl})pFzI!e ۤief<~NkaZg7+`e@Pg7!y"_:a1K^4O!jNaBdrɤ |K  SՐ;6o7.b% ֊mBTޜTFD9)W;S?e PuBΨ}]B?m5;ҷ)FG}PUs&[*BHC@"p9r!KQb֍BmߢhXg7<*'~뀒)), !C&&±싀وAX ;pZ @K }nsMG:YRΕv!\&);ZE+BH'Bj N].-'nVLyDA69!:*1yC@F#8S0_Z0O݇~h y)x?i;č&4Sa !''e掓$TqLH#ELdvL+*"Qϵ8x&Up)'гZIn8]e6ܙm6#cH C d7xTQ;w&hvsu\Kq`~hԛl-m;#9EW {^0Dce{ďڮM+҆S)XnjvAuS;^;Sl ]WȖ|n>묳]R> eFE1Ǜo*`/ҧPtLfGv} ea^D? bA T>-mG6+bݠu]Qg}/ު_RT)lhђaV.K,;BgO' `",B]4g7 6 Q^l" N4jVR h}|FOT0|11jd|3M0T+SiRR&/+O~L. RnO&C_?9PL'P&XSvh$||j&Q3`A eLAw(Sx O҇vqe2LxM CY#xSGr|L0%aAXoNCs1 |>-" zF+B?]X٠9m]R0혶C`qmһ'_`P//'Vc&0suVO qhW !"43YJeeO(4hz9F\=ROCla ȩpm KM:6kǘ!(k]+vgmzs%eI2!\D]GLʨ[L6+Wx_+i[Ѿ)VPYE-W|U%Ъ@@ ОPyE`F π@@`#JwneMKeJiGL:+  jgͶ (\$/E2SlduVr ԋ IKuA^7d. hk1,Զ3Q@ht^`l-" dB#!P[@ @@ "@@ :# @@ P<A." @@ @3?@@ #1 !@@ :# @@ P<A." @@ @3?@@ #1 !@@ :# @@ P<A." @@ @_-DhѢegx@F ht@@ , zI(ƀ@@ *@t@@ , zI(ƀ@@ *@بX'$ԨQC4iZj_9HRߗ3t@TW) @@ XBBZp!Bx˭_@@ *%F~饗;\P)χzH_~y<$f<%Aie Qiٲ|QJy姟~[N^xᅒUY|\qDKǬKo_J ʿ>] -,1B/\r2xf*lq%|2ΗI馍K@ޫP+V-jժI=dV:W{eR,Y@ N%D͑߾VV]oRVjTK`)>?T3@Zjۇƽr#LFᄒtXq-)\`C?+d( T&Kzv[o%uԑO>/N?tYymחz*Ր!C~Gݮi֬L4IL{\/ȡ*M6 '/N6l(kז5XCnᆴJx Ɉ?f˛.|2oQAUxhPoW [VSkDJbSIni ,B0,O_[{nԨ r_e)N}7eƍ/dVr!Kޓ7\իg&m'w?Zj6H!w9#lҤriIΝeܹ;/>|5k~֯RVvX^l͛'t駟|#={c9:ɓ'\ iFFy3=V%N8&808>Zj9i׎mN\7Ωr-nvs}u-IL;.x9rYSQjo\nݜN5\nڴi_~nԨQi?*xw,nxI.僚ƻGN>jg_ӳ{W 7s0ȝp3q4s7nӷ3齩w+Np>bgi[n}~r5}u;m[+_> r;SŏqSgD[N9mEoCīo|Iwqמn[݆]><|zs<,s} xiktuh֮Z;OuVR3K5_լU5_c w1nu^{z햮Qnŵ7u-_^g}-v+5m| ?9H>p;M5=u\V]܀tQo{U%uv~M$} NW}phW ˗`ȯj@? ݠAM5j~tsS-/US{j}O /?ci9u[vkMDp)+C9B%){{ qF'|6;ֽ;裏Z* 6%#|\ѺWhJGXړ6ͷ?=-;/OѰE{Q\{x.dU7xCX6c9"Ƙ̨mbAE_{0ٲ(OD~gK^믿6ٯ VsLyײBr-3ȍ$nEڬPMfݺ~Ҳ"MKs5XN+J5l2byRw;R]&+t'{-Ԭ@VhPM&M(%?5R|q9o;iW6Mƾ1Bk~ҿcJn{![?_)CKxjRY/.I:Fdy ; *_|l\rf?iS7{rHwȉ[tf1!_)rfn[rSd<0<9uwo"?%wl9e6qW֔;U_oyv2kZmgT?4^Vg1|#_PCֿ7ڧ jd|J)[oٜ  PF _VI1GùiU4ݻwO{I“M2WWXD%:q1cj f$ve{1cD%|eU@ IDATzR6Z\ +w%{}=6_{7#D ?٨a%:]:3:&&hРAfAMFR5AZhM|^{&ܖU##뭷^:KyDa_Jw.mזjג&󮐛Ǿ+㺶u~\fOm Yu}]뵕NK uc0G2ULji [7+2h O٭dIuth<2v'}ZҬm'~;6giR ׳gJs2ʱطS7ڤowi0cC9w;)ݖDZL*}vM]|2Sn?oȃC?_"ONlYWR]E>!/;c=:FͤjlγaDQ0$W+ZYTD$.%y(η_ߣ {^} n,B5.a@tZ'&_|1CCd{"c;^m&y_~%ީF'L4|͘,!W[ER2?e =4J5\.Sy7^ZfSf]Ӽ~MFe_DVh#+j,͌7UTП OgicWV]4lY9Bqߚf;_&eMfHg>~+]J5e/]Aݗ2:ںdz1o2yfKf `@QZk2iL9gPlfҬ RupzX ߓbxI0|',O8RNxZv,5}y嵩d=+w}Wi0R=xFVHGd1eeVΚ|љ?nu<WQU:aJ*RH't1$ M[ ?s:ӧOgO{m6XbVϤ^܂ FT޲Vys9<;u$l6g%j[ٝ1R{w&!?`]1"ɘ/zw,Ϥ|{[4g>-,1j*9l`]*f.vh"#Z70%\bPtQnaZ0Z1SƎ78|# J,&Hꫯrmִxڵk( iXTʲJ9oZIӺ=uoevҪ~YيB1_O&~$y3֕MztuIɍ:Ր5ɼsd?srf~CJ7/fɼVMajuԆz_|f\-zrsɺ$jt?XF X|1 G2v5bZ'+-/H~쿚|UoQVmZM>KCOI5S5W2-/k5k"[ ~8B}A(4ISY|+w9ExD`DvE1w>whG9;P@` vl)&&z 7K[)ʜX9930uRL@oϊ~9u=|W81b\8 ><Iƕh=ɘDp{wpQlq>g-?0҅@DX3@6 "ː!Cl9Dz#GѨ0@;&?Fy"GΑ8M7h)_z饲m5՜mQQ@x 7[me삿O&͒?kt-vc[,-U &&l,u[C6h537{cFʈ'ei2a(y{Godw?DH: 5f~*~MzW|B:$ydkcC_ORjQgiVٵUsЍ /Lz/wc2ei{ CIɺjʑ7UAf#ag^->2l7'g {Ȏ^;+!O<-^qwqҨMU~4__4v,9/cEnI$TΧ.Qh@ rK맱6/U0a& )>-UCqswyc|$+yIQl{K2.${reQDQPsa3!JZ<| 9V^_PCt.3;pԯzNW1b`DQ,D:i24P'Nivu:wJeiy<^G78ߗMt7'm?2X]S]?NJ6 UO8%MqG$\r_d)_g,BjٲtN'?[UR]gRdLjˌq\[۩4u"gJ咾?[l&];7r˔GByqYt~o"l#UOsB"9հ jwg-+d⸥v<ٲE3&%JWUK/{ԓ~.p1I?S6l(# G=-Ϗ&+<)E+7.OSt$3Ô2*aI-T;\5O~>^jbabAY7]9z=YPNp7A7=jӨyÝFqc)  ^0:2,ăD>c=L5hoO4o5Iw[K4.}T_>$@$@!p    &@:;M'   .T GJ2DIgQM  "΢$@$@$@$@IFud "   "4* @dQBHHHH΢$@$@$@$@I3Ց/ԊHHHH Ш΢$@$@$@$@IFud "   "4* @dQBHHHH(,J$@$@$@$hTGfP+    ("@:: D& @FP9ȂHHHB"ꐰ '@< @HhTHHHHH<YHHHHB"@:$l,D$@$@$@$@ p,YVV]Zm"Ǐ/\*UJZ Zg={ҳgOٿ+WN&M$mڴ \S߸8^M0 :yTPA*3=I[D$@$$N? :!kBTgݺwiڴѣG=}Y GdI2}^ (K@@drw^2U| IJJVZW\(/1>\`$"!bp(Txx(Tȉ-mHy䪫Yf}'E뮻N ZJgѢEHBBdffϋ/\%)"uԑS^_~-ZȕW^.֭[y}'w}曲xbA}֭Spy衇XbRxq۷={߯,YR=t&0 :tP)[jՋǛKRJu֕iӦEj ,Pn\sj㣏>zA|'^tC~'p[G}D{ȑ#cɵ^+%JzJ^x馛JQF… =`Ԕ1c9M:ҠA%Zj7ZsE[Ccyiǚk}[p4uu:4-̋s}kQ:K2!mƑbƨA>7HQ9d˞ebR?sdK_-ӧO5|ү_?qK{Nv<2n8z%?*xE߾}{e#Dn>W^r7)S?t փk8p]3/TqveqV}Ν{0u&|<_+;wVZXA< O>Dy=@:g+ȇ~(VRoƼ祯~2=a ~z?! ~ِwSNvzk=k7^k2f`]oa=rd4}8]wGV5urcNyBэe@Fk˖-рY:޽[-}0rJea^_c{LK/1cY֭U5zpm֬06l0?7%zNoiSKC#exjlT4NgȒqidxIHA% -1G)J*@{F*7 5:Yv2ǎf `V357ʁ䭷RP[9,fs1rJ6͛ˈ#ԀѤI?lذEl6U'=;j&OիU_3 fQ0Ӎ+`u 7wڸqc5P{;nzөS'%oWvqNs+7{u@oqC!0 z޽Uv7w*Ǎ1kwo*tpu]v߿f}ּ:5^Kˣ˛G3z-gkSyZBg4MY9g.kyt-ьrtI4}-zуU#G %X_0cV ɘ1cAoUB18!+^XԔAދȓWT_9Sב65~VRnk{I Y35T_yZz5.)Y欑<;ڵ2Z_ҥKuzpW8իSpq:Fѣlt-A.%z0.,^g ,_Ν8Y5skY^00̀OcT4tF/'硰wt/!Ծ"n.[ns^E8 XNLi{=ײ%Rq1밦5+bvO~ޢڹ@fML"P"4mSW_ih߭ҮDd"[3Iek:fdd\P-vWb-`5c F!b# aϊ+.Hx8_T^vbH,'AK ~&C.\X1[}v9Mv`楼{MNNC`zo}vm`)ˮ^8`WOivuZ0mLy?bb:'׮69ț<g7QIZKRG9tb̚vy͎ B7t0 .jֶz /BT RǻvR%˛7ovM78/zie_a0ŽzЂϸh'n3gAsu(nn}J=(x /!'}E<N9ionLm''&)o8|:~he-GG@LTyZ~^J*yi&zu̙'l2y2)fN~!:,O5-!zȦ˻;W~T+SYL5+6Ifbg{cĥŗ.|x^ x,.O7ިZ_`>3|Z v.@{B Ůp-A Jtk\"QX-xNf۱p f]! m!CYYÇW Myhy Wcm! ;} v0emM7q.2u{mۦ{>coWƌ L~9 C#Pqҥ]ځF\j8NL;cKʷcgŹZk>Z(^u}Z}4"@ؕ3u9 L9ø~z7Kca`3N%xo^רQ?k{ |M}/P>vz[9W$MZב*IKe4mQOZ(iXxe]rW:RM֬5+W Bg}+;'?$#b : p qu~9oooyi7bdTN62eʨB/:}eK|3hgR.ZШ֞~%99YRSS/3رzd hT[D5 nlx+G::,/_|}ۍ/[vL'Pvm~ms |o߮ު@4F Әz4"lTB '°l xV&~ollVlyj^3 @/2~~XԌX|# @dA cZozZQH!@H0k,c`K~>DC$@$@$8Sbldž @x XmtJ#     @::M$   ]4s/ .TNfIHHHrgs/ 1l" @Q|)HHHH ШNfIHHHrK$@$@$@$@1@D򍜩K>eXIfMM*V^(if%HH0231k0100Fotoxx:trim_rotate| Fotoxx:trim_rotate|paste area|paint| Fotoxx:write_text| Fotoxx:paint| Fotoxx:add_text|NE€`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ2iTXtXML:com.adobe.xmp 800 1000 0 &6IENDB`fotoxx-18.01.1/images/unbend vert linear.png0000644000175000017500000001660713222767271017316 0ustar micomicoPNG  IHDR]ٹ.IDATx\ksm߼e18љ4(if֎D y]/>3_myyk6c<-? `Ś?jkitEY_z |?lbRnr|;Φ_/NW8d\慤֡-<:k6MX=c8F}2ߘY=+\hDԾ!~Wf7>d|JL;$vDAL*/#RDl$$2o CSu;?ZWNC׼z:YM]M ~YclOmc'yu^nɬtt3ϭ{}@xZVbZ'ߩWqک8o~ܙ{byDD }:^|D^D}>qH >o{,դ;4͆*mX7V,PtSģ 1#ВkA$!-? ګvMW&M/2F 'WUQe|$k5X^^z8a|pӅ=q?| >)_j)9,;6Kn / CrWј=w4. {sXOĴv.%s;iZI.1_1i)YyAYp}/VMHM@ݤP#~CO? I))W'f(Yc]r9&J!'!PZrV ^ܔgV㭨P.>@ $!$I)%v?Hq!Oư}k|mM9mm=bĽ .*^u`~Û.Ts_e4}(Wx~jT.\ &!>3f.ma\y\IKX傾#M 5Qda*;(ҟwJ3fs5h|!CtLe$V6uED@{ӝm'uF[Ϳ_ iEy+~ &zӬ"Vo <0RVL]$4l zJ5Oㄤc*| y*hC T*Jmq-JuZxNh{Ni  "pC1<8$p^Gܕ=n[!ⵙ?Rs?7Z?}sh7$7Kn4aK_H1؉X1gVzdUcFs| w$<#.UjQJ hh_gՎ_*%YodA!V60e.;"p$Inq5z# >!i$-6Xr~,' ^uofǐ(΃6cl18ȟ7Sk͐S<trR"JI{@n.ډl}o nbuw[S׽4/Tamd1q0 FKAp[Ԡ)]ys Csb:60L6z`Nrʪ QRꬂ87>$QƋss[@!ow^ +̡;Cbrߕ% e^m`/zoΡBPcȍJ)D(w =tÇ_c w>i wéjԏbߣW)EᶃJNle0撋:S ST( n#6L)OH@nn+[fM= N?K8=(-8Lv+x5Õ rlyx%a>q#(d' %VfIA֢1JgjQÿgpOO0T9q_(ODž둻cL95ܾxDy”a:M<z_}1PTTsT'@sW-;qpGU<\gqӃUl,~ztSTJ4rdUPFJ_2XJŸPU@/Kkq[V Oڻζnoѝ҇唨XF2_L{\c f|i)J˴MKvſ5[閅ŢыMWKq@jA=RF:;Bh4nqbHro y >6p{j1eZ  ^Nؠ+d-lGŸz^#O#?[X.vd}ʌ4jU :餵Ҧa+3\ҰP}.I֩߄u9Ģw1? u-`*e;*|ۇRus{ua ^oGy Y bJȘ1n8(b:Lu15pw6b]UFV;Xkp죐,3)> wtNNTm.*ag5Æ l9A|4Ra짚ػ6׊#$qNAxk9'(H*֦/\-.{jrt%DoGl`F%gIfH8y:&3bb_bdt! !%7uZ ĬҜ6Z+UهRъF'(v+0.%s6sj#?17bW͙ e4( +Y{ ZifXI&-7KJ<7-6ڌB):D9M'#8؝OOsf?+_{o^ܷi&/85BN޸} 0HGzPsd!tq;^-B{72 5W#t@;oTkQiU)Yz2 CWV|m1rճ/D xez2N阽z!A6[e w>o"gn}vRJPxgX$p_p&WdثGBD8-"-|׹ E}xz!/U%7AK9.ދcx'x3bXS sD19#p_%jy<2ˌDAd./8>(:;SϏZQo]Vp^Qz]NN.|FNwCQW$Uܞ>I^MݸDAE<@f̀+*{1} |c1 Y8n|߻ `}+̍Cvd_XS|*`?bsH;~M{h3̠|@;2̄C^OT!o+*P#:X!pTF |/ 5On)L<0icy6!mh ދBDx{{-  31]y w3.9OrhH[הA+~ZF~kd>3cfjVϪ[߹Ͼ7E8V05sH'C:Q̞؏YA;|xjE-Ѫ_K?ш yk~D7i*L["_c Z4rIvÓb ?ɌIYJ$ԉ"˔Y`;~\C~-7nQQ-p퍎cR䤉4aI{ũ[qžI6ύAC@|&S]m¦f wARLO9|MP?iD3`/mK{Y8{OJowG;ދ̪q@Q:aQwkԫHng.z}mLOwn>L?۔=nafzҿ_$\d rG[ ,̬ s*X£> ~?. _SOzg2B\bR 'f63qgT\̡PQІ@^#MZZeB&Hv4#Oh%'G^#ȏ>z8zVf?qh!ͯ3PN}~k(g B`duKKå*Wk{Zr-s8LAgJ,wwOOMqt2A'Q?NPXd1#v jY6CvX )&^} wރ?S$fPg| Wʥ:a߬!ky5)5\ Q&Q"${XBŎtH}%/CgC)5 @h!A3v=8kBqF{'9D5 _8I`G Q6i& DRU`'`lKOƔD%N֡_e0lo+H)F_A{N>e7s"IJ ڛ6휈xD[Ic)MW6U߬[R6]>pI/PY}A1[˜{ P,kkq|҄FxG7 Ƙ CM' 墣H_z_H̨D\9+ &AZ`!ɕ) R]*4b/{(P9:#mmG|rHLSnu]b \ q$fd_E%Z\7"q}]}ٹb Rp\NqabxL$n! Ft,T=&tUV`sd=56;pA1'Ew<(/x9ųgk4z=avm=#V3E,36yOi`; 8Uő,nl0M $'"6.]x$rʍ|x),fw>v~Nv͹yH.'D%]o!?9|~;}"9i)%j Ŭ /_^mf7Oرo~ޞ˿~{%y~?JlzTXtRaw profile type APP1xeP[ => Ĩ?\6'Y} ̽<˾彿(fi c] @ y٤۵n;ȗ7B],Mq:\|a8r 30f}lhFCn3X_K}$p+dE[$@ϺPy07[itiTXtXML:com.adobe.xmp 128 128 IENDB`fotoxx-18.01.1/images/update-albums.jpg0000644000175000017500000002752413222767271016406 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 117 244 0 C     C   i" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>މ|\i~\?G?Xj^X 7"7"lgY/Uº4\n5N'GmbmYc*nj! ~ozmY_Wx?G\pI Ԝֽ?Ze+kiz&:w#\-@I F91zt= d.,)$mIN흹0Ir__b[~64x[?@'Q^|*[gL5$֭<؜ɶH¤2ZnFFzGzH߁о,&-9=BI/258աv+]v9 qBh[v|tne7ܯcz'(cz'+ѼGúT-kWF2\H2T >ZAƽԮ=_5 r LTkߧbR浺_loDE#jY*JV i֙ͥͧڵ)hmbA}z -¾*->"EO9 g.y|A8}o-8`Ǟn;ȯs>߽[pXY27q߹$5o[~&me{a.ė0;*K:ŠXX+8>RoGo_].!]ϴ@ $'ڬkiKm:nԨ%5 xOþ>·iuw#9Gtyl nF>"NA!U%_$ g"nyj"HĊѲ.Ogۛ a껈,=5[U0u3^)ЭH 4%Ibo$$ץy97Qnm2JKIjmO0k6t3]Bч&4_,6O͏S_Dx^/\5 頞n!*#pyc<y4\2g)fse׍5.h*Ks,\hAwOO  ú~_Z럞?h>}w;Q?? ?/ Qf?<~}Z5o^=|//5_]ry,Xy1弁k1;Q?? ?/ Qgk|ּUZ6:=wMYZ8DBFswmi|4&}KGk~U A<+wOO  ú~_PNַCK7 hsZ-3=ؚSTv~7\{;]\CL{P"F*  NJo(?GA(X_|UZO&%'&8Ա>Y]KB:~iLh!S QI,X:GA(t4?21BMlOsVlM-O[.v X8z?x?( lk  F39? ?/ QiAdb-Ղ>%?e9"t4?21G;Q,ϸ_#G⿋uk}r zX˵55ğjPvjzUk㗎Imcox}F+㹷EtQ̞^ |}iAdbwOO  (m>o/x^ֿ2[ۨ2!UNNy7ta-v6_wú~_(?sK&yIgLÂ>^}bk i1NF~ú~_(?asQHE(E  $H?~UxgMցc}YX {/<:_rú~_(?qptutzX UY򽻦>GOKibZF lgz:'=kG(?';?2U*13g_.i?˱5`:&NӚ9yy$n 7#cޯ$'.>-ֵ[(P嵜s3ͨ[Τf' gjFa`<' ^\WFb;N\mw`sQ~іmAu]rč<=+%Ǟ5Ÿ5 Hͻ Ϯ7?F/׿h} NkgҋYXĮɺO:b[>28v_[G8? >g#ړ6Ƶv6|Ϯ7?F<;w?%/hGğd( adx^2YNO_AКMwHuG-aTӠEZHLHiY \ vw'i{?c?#Q}tOgt[4FT$TSe;iNd2#gK_[}!4*mjK$UmY@BZB|2oZ|C>' gj[MZ5hB)Ik;J `wF/c^PߴͅZci>-֑=[,I$`4rL+HDcW>8$6xv kڋ:+ǗZ/eH:mÄKI.gk̾Ė|'?Ao%i3 U동2Z\+A^.m"^!M15+m)Kx<ʄ؏2"e.x2o_sZj3'Ii~ǬX{l`w\^7c+ɴ_C"ҵGOԮ Ѽ;lk'$16UelqPi7NQcOa]YЭK-}iE"5b$bv_{E7?͕\x;6$Mu|<R[E l,4{FK`k7mcu{xcF&?^"^q$;ҬcuB:Cd]v>3^j ,Vẃ}VYp0>W|;yI1J!3dLy$M!a!'mAW 4!d@qryErR:b ͧ5 +gɴ mlIұ{4d6w²wֿS븭^^uGY'DUal >쟻]+-G}k_K]M~گ_ek=Iq4M;9>Эk)IrA}HA# $9f sC_b|5.W. Ką|$F9*sҋ[Gž]'W:ZzekmiuPNF8>;{=:}_Òhv6Z7^?xd72-(iqO7ϫk ;ɨ0\xs7Bcn0)Pa]3yO5Mo^9m[e\QMAry>a7>Z)%k[7&R˺֛jZ~㤲|6&F7gUKڋkx$\#;m ?^E>e^gΚ쯭]xvJpUPwkuFFUۜ'yL=Z=եu<Nbi0(K,Xdch;l \+/IcNJZV[׺6 r8( uOrg)Oq$1)~>vn@~>vn?S;_ o7W ~>vn?S;_ o7W ~>vn?S;_ o7W ~>vn?S;_ o7W ~>vn?S;_ o7W ~>vn?S;_ o7M׵6t2*46EI`XpY?tyS]_ }_Z?tyS]_ }_Z?tyS]_ o%ݼ#s >1qkM?u˧ɦ[bP6Ѽ!I)$ bn29".Am/kM?uWK.Am/kM?uWK.Am/j[l@EW;>nݣxJ0Bv![@Q@Q@Q@Q@Q@Q@4[cX\U0L-oG0cN7f Mlp2vv< GWQoXⱩʡtga̾ggmޅ'?:??7iZWt]CnRkfLO=nr .쪍ryotc=J}:\2Y|C#&Mx\Vu_^&kǎm&m`9- +S\՗ [k ٵ M{xD1'҅<ڧ 6E_?6m70](t/a) DHcbU-a$"a<.F%,z"x{Ҳ)Pj¯j(7.5Ijvoxms$JS/֥v2\6$RyfHO ۰1ET㚷 k:x./5.ocD̝[ښ?m[GT"6_ŚUu? ix-tιxfwic@6O#V{%.G#\tovuWԟ7YxRAoMbRɱ^GY:|a^]{\~2ֵoX]_躅֋weZ]ڼζBZw \|32w0WoQX.f Ho@pr2soW#mmv} .HӘFC,F1mǞcS9<{|o"ޗ:%ķv> yvY,mdw5Z_ Xf?4`u;gһz4SQIQRPQEQEQEQEQESUb% 22r= \6lϭoo1]¶o5¶o5@g+f[_1G+f[_1]¶o55ôYZ#`DF gdJHuPvCY}Y%#::t$W; ٿhWWgEqo%o%vtP ٿhWQ ٿhWWgEsc/MwJ=E9(fotoxx-18.01.1/images/batch-rename-tags.jpg0000644000175000017500000013243713222767271017125 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 380 609 0 C     C   V$" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?& KdTEXI1ҬEfv)-tb'Hr1ץ|c!QsF Iqh #{W>O1IJэS -RṔ׿#QKIDq EŸB@϶s^GֺZs$ef =.$Y[izW|/(H6W rŷ1'z^tUTspO/'{~- ďfTЎ:E$QL` @$l̝^>;ō"R#i61ܼalO.~e:+KG".e3]<o, ZNt:'Wbw#xMdr mjx̽3Gzp'Wy|sO; *w1ǣq5G|Y$X r EeAx?:7u\i)u]I7KDH?)c;R_+[]?/O(Bh:վڇ SSo8IHC k| ǿimo({e}͹H9<Տߴ_mIڵf dX,p[cTO=JRqks9?O+oڗz]?+ҵ xaMe;eIgEq3/i^5&ӆZ+Z(VUG0X G0X ݾx|>oo`aWCm=&Y8 ]E]&\ԺxTF$VS^( r>λW| .rZڶ^?u)̒Ғ}71E?^,Ri-WO;Xht0O6?Q(}Ý\v`t2Wݼlj|ǘ?F!ӭd#uklQ|Jdzmȣ6?QÝa}OmwIpv]yw `pe@>xF:)":>&dNx]ԊIty`0Crrsa1"G+[0OtyvC@O} }GO/^IMF13 ɦ|hYX/.$w1otg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tg}Z?a1"G#tb]~}(F"\~v 6A m,7EE$8~\oo M 8Fgr@`1?N#.񎃧iZ]ťSNQ" pHk7œRL_,X,dWLaeՠݒp_8B0Vej|? zsj}3jl$b6nNIo}b*J|g5? |J.%K/ fRIyNP>)߆=(ZYYeɥK:m4ggº2HUf4˟:Cӵk?j as ZƱo s.$%bQJI+E)᱃_ֶ1)'o_ҊO|.~"XS.k|w2C RK:U2&rNkmzxtˏAe4qz㽻f[9/ (QCد3Mw^Ӽ1Ojװ||ۛH_>+u+ߑx67wwd!ZO#p|:.:`W9xQ_գ4Yi:gw64Ѱ-;M33Vrܿ~yZ՜;cJK ]~9W[ -i \YlxJּ+ĺt ]O*rĢvbH{DO/MZI® Z_Q -i _!W8~ϪMv-|$6dպ@x<dž⎟ ^]^\|4 2g/=M ;,z B]>{w[b[5G!B֗® Z_W~7_d Ǘ=ÜZ rUKc{Ik躷fLd_w?Z?UkKT (Q® Z_Q -i ]E® Z_Q -i ]E® Z_Q -i ]E® Z_Tw? %K- 9*;Aw; bOrcZ8L&T$HKmo#b t5w%x-k?_`*I\eXޝ>Ċ&$@@Ge8\qBxMP[v۷9,l SbNMwśvpt+gҔ֬ |Cv [|_y4 Y CJRX(a؀Z s|4}:)`$Fc*UP7%m#A/56( 9l~8ԚhW t8-&]B 2ɵ fI[dHfUmG=>RoDH -@.)FRFU`pF>񧂴RiRh$"0dtde` _K )lԟU7z3=F- edtwW?O]<^-o_KţAd4^-fF !pL*w< [↣mctl'K. 2ĭ)k|';xC$;atxO[b+Xe@ i#xU;@(7yjm5)6(Y#|q 34jAE40h'–ɢ˟âY7:E퍏MxӽyąBq :_<}xhQާL23b)|Wn2gfF߻9ey0%ծKK5~$G 366Tm`A5zOFh5FKƊklX4*U730f=KJk|u<"_36kqeYL.w`e&mhi6nÚgkm. !<.1SPYh.ik%܀}HLi`cY~ m3k}Y5۹0*&琜# eM^_ Wo'P᫟%ǃ/ خ-66 y/xZ@i kitt pk@*)‘׭?<=Kⷼ7 4v_^ȃmb<qͿ %mzI5}vNo~ǎe>{?^C.tjWLdXCy>86xΛ*yg$d Yu6v@6r:2|yXk0٣*XK;wI;da`8Z_MNӣhRF^!8[__| -zqBFq4`]kpz>~4Os~%Mm{i<$Dꨥ>h63VشF (^zW~x>MT2iqZMpx9cgƣ'h}mh[ W=mWJWkSҠ `]12G/#!)[h/ŧADk)*iFdj@pBv:G_XK.uVqh&9$v1H s_#-wÚisqXR: ᔜ3.~cWu/l$q?uß3mhEuk(D,eAMa}Inhv& E,%2n%B\36P/~xoVcyCu4v6h] 3Ycpn<*+_V{Eyn/6&k?5۸&ta_jom|px_.Yj{o=x),l -IݑG Ö3ZM>+K{s_}gY#e,Bi!p 9!D׺mmkZ̐\\1%6X$φ.bk9u7Ct7\g9<`}D}yAx_K5߆5-+ZQHү, yfPw!3MIΉ_ڤ66i0{o .cG 0 qӊXQ*((zVlJOK* ( ( .66ɨ[D +>2@'-k6 f '5W]#_@:gXir().Dc_<M뷱[Mkl.mmmi[Ecyw~?%?+e 4֚st쬡lQVdA(n1g=i|8{;[FZ#GokrcrPp2\WM .A[/O]#_H _TMIo yB>sףNLh Ϭ]>Xs};S6$һ_It BҢ%?+e 4KVhZE:U6I-ݼv<E[9zIzkC*mi[UQ|bH' .A[/O_ &e!&/nB4|B䀣W>A%d{3! nXz1V?%?+e 4KVh[EXi[ g!`Nc9梓~-1m\oYR'%q9$T?%?+e 46kZlvem̻%dёqЊt{CO2("62)?%?+e 46F,lԋKqs"m8f HR*![⏈/8:b)3y!XcqZ_?%?+e 4'mC nx/]k"-繼גQme-ŽXsY:i? uxZZk֣C$]Jf $!eERvKVhH'![xcJmJ-u(HB+Wǂoпc\$Gl!?ƏIt B6ՒGI c?7_1ǂoпc\$Gl!?ƏIt B+?lx' 0TO  a⫛H' .A[/Ot<C~?lx' 0UKVhH'p:O[ B?U<C~%?+e 4KVh'-ߡ*mEvY?zIt B.o #O|L-7iܱEyo̰!\mVvT{?%?+e 4KVi1ڛk)4[T. Ƅ$( '\֋/^-ͭmfqa#ql bA=IIt B~k#u-B+;fT, clckAH\̱HZUd2=x5-''oG"RAOO|3Cg /#ѵzZMd[;6sFqf<w>kik}aM[kY [ c ('KƏO|3Ct>x[ش]wzĶ1vA G'K\/5;ľ4K-R Ebb{4 h?0iֵW$I.=:Y="rRa[o S@:xk HfFOn6 `2? d]!guh> &umC 棗 yu z܏yjLssF;\N ;1\= _G4{4 kmt]+{ >TG,PFC#o|=2mGIuM>u$(Hpι0sEqg /#?= _G5^+ԴO]gNf+?ɞqt?xG xrmox KheWnDp* j~W {kmRyy{ݹvN<O|3Cg /#G4:i? t V= _G4{4 kҿe9.(k `2? d^@/O'v_E_O|3Cg /#G4:i? t ,{  '^"0eaMãi? qfB`+LJªX)'tW__z]晭챸Ԣfe6#qĩ" 2%-Rxm"^[BU Bs__Ey/o K oPԵD-t{Ⱦȳ1yޡw3gJ_Uӭn;[-t{kd3J}ihh$iikovzVsya- Q&B8n8v_gzyo4H/..,1qcqº3^9;RM7A,yh4M տ1VzC* ?fbxg U?ޏ?ހ* ?fbxg U?ޏ?ހ* ?fbxg Uo:汩Z'3IpDq9y8'kKƚ柣h>*XVqF]#$f]p nyZ231Y릤EPEPEPEPEPXV1^FkJ߆?hCzRx>j\ ѝ{f|vzO_ ZWv;3 M77`MqsVO- ؼSi#7 `Nܦ%ۂ߻w ? ~~2׃,}4d! "_=aٰ@RE%:]Jov$B|}=|bmFdֿE˖Y2G2B03Af^ 𗌟RWv,-q(O"+Jȑp}~.G𮡦c[gTil/qڷ|x1t ,8qji}-K&cPNiӯ5vT-UH\ч¿7$dxV'`O7OЯf,Agi76":|H#ZLU =BҼMgy/7G[_薂(5- 3)xtI>dv >2xKFxKT::}X^#$/mp|#8WK [M{xgǀ5K}gB /u0,h 8G_ xi7`̚eyl&F-hՕq*}O'K2M']IAY23@4ݱ3Gn:HH}D%՜8MOݒvE|/U"V4J#@ mm$tmH+be;A(Q?r[||ͨGk5;@a ѡ;r?yM5&O"u' XG:W4TJ*I]YMt; ]~?/354o2X4+9*Ws`t,~#džl4^-&-sT]tO v%{V WՔUmܖ[(0((($ſ A5J=ap*I'7jzU, h@#T+| 4:ޥO on4MuqoyTTܿ!nx$1ψtE61JTLW, k׿e~we^mF\j y%am*k~%tZ}4i.nS upC1<)C֏YON[W½O|Q iwqqc"VX(еm4iZuJіAp#5R<1互Fr>ܾYxcOtk? .=(rg?%R<1SzpA:_mLJo^ȄG$H )S]v}o`vcc^Skz]owE݋J䖚LǦ:a 3Ÿ0ihYOG, fxS ?<)Cր:j+O4 Z騮gg?` 3Ÿ0ihYOG, c/;GvLE8뎝E6J߆?hO^κ0mei5K߱QQIٗ#[l|JVu u$kW$}W*B*}~vyW_ KZa%KӭAie H(@vKN@Եmhh.܁{ʎF ߅i֞0.oi.\sBYߌ?"B|6 m?"-t[_.JQ+^r6xGV7UZ^^p]4.ǖ5dػf*aKuFiY_}1 ih' XzSl@Q]v&im/lnYmruaAs_/7a߆_/7ߋ^3ook:e8=$B_3#迆Z>xcG?Qmnm4e#Ue(a1N9Soo W;yRLXCoP$s*I< w{4/jv>u K{crc*vWQJ>/⮉c_CMjNu-r$X)tq0 |_D:͵k~;k-Ph".6%6y0үe_߅e;]] K5+a*cQ$G2x6z4(Mfs&?@gOu]f{wkSP&9^x4Ŷ^*ҭexWMc-e&C٢)NعWIZ!fO+*uLuf!O^*~!C4{};:M嵞v`+u{Z1#G+l83R)n|G>+g^)#}7k{%e K+V.F|9G|AaGK*u#cQk~ xQWË4xMjVIYnZB0‰qlZ|wvaɪ^BKkxC@cfIFmnKHֲIG vU!o !F9XXH;A#P׾?(3/ЬྱҵRZ2(9=|Wx|iiJ|'ksIi.ԬcTN$#1cph>YNЭm!hkq^_?.Q?)g' QSG.MwoOK RkEGσ;yMW&e fUxG*v ?t_R\mKr"iwKAqqğg}zL׼w(-.-#m)!#qSһ}dO ; ڝk]A&h1%1qs[ O yoߢ=+Ǿ gw:qH-|U!s{F 1C?JU涚9fscb*̣=q'/x,jΗ=ג4p!Pʬ =*%~Vq2R~񏋼?AF5o v-Tfo=YTysR]~"O^{L/E;ZL[3`$۟?h*𞏢C%ڽڌ%ty/ڞ2HFI]gψ߇^Tgx맍7adg T6HZ?/#8{ᾝ׉4xt&2yq]~v0rk|7 gv:Ɨ>Dwv]3 n+x_nt_x5 ,nl^ Kߍ v#ma>ٍi$ʉ2{J9$o`>?[ϼ_Ii |Smm_^_ i0kz֜jZJ4\*\vݎqҼ ?s7:-m|y &wvse[C|MkAK"cR;3br^85|U?~l)&0Nx?eMn߉|;kV:qKKkF8ctkH"xnJ r|twGG֠~ jܖrFfn"h VڲHoYo̝T[O_iGѰ~+>SYEs4DcB|SF:]>kX6WW֜ھI6jP4q>|mGgῇtkIwe0=g^֮RD@$a$%sjXZX.=r3GȬ"e)sBZoFГ|}WmmO 3`U Ѡ !H'h#_$l4]Dk&mA"9.#l8>`%fh/ğx/ZWs_hl7)՘gIN 2vy?gxrJ|95j^Ț?K͓d7}fm? b$bz\ީK9Ѿ2Q D2 {̰<w`zc?\!MWNM_A|0 BJi H>?ƶ_4:CjM̑lIL Bo2|fayk4M{xмRt(_BU67j[x_&eγܱW,1ctE2wr+jJ߆?hFjTQE!EPEPEPEPEPEPEPEPEPEPEPEPI'7jzUw_O6o+_XEx?eikahOB#RW]E`)c0P˸c$q^טi?O|pefi™ ڭT`4u2C8j_.ے|q—0?ci:tpgi|ǀJzW{yY&}kN//"Q䥪a00]e÷qר>BzNx*zhzGo+-"rC~Fp ?d^ io/2rRGc l'%#ug&[ɯ/'틮\|1'u׆˫i$UESH\ <=Mkah/YYNc-n/>m.Fzb~ҵXMIm,#13̣p@Yd xs:g욼vI,&{dfVVrЂۂNzowm~?74IOx~^m&V-3zR^aQR" znx=nOi2._"K!6A'hGL1[!կ5VOK}V6c>YpkƟYIe,:e&$u)lL1!q܎A"\ܶ~wes_?;Y~4;I*K-vC Cٛp),ppK̗z=WF}sXat}ZMOQ;Y'|)]H0pPI KpYkoVEtHG`,9,Ns❿}?^KH.6 S4֛M{5_<+aof-5?iΣǮ(ԣT@אb`2$tWmG? 9Kյ/ kWϥ]slo*fS[\a g [kпe/c^}*^mo,ȓWçr eJ.0+q[Oo~w}&ir(CK<1?scUPeM{qO+ڻRkx.Kiiɹ g{x{YIݜqyWZ'lj-;r-~tq?yɹ d:Vo?xVKo7mtuԍ:f0<76)#s#nPr8j!WѤKdfT2Oǻ d#u*n7ÿ jӮ8/oVE\\~p2RKxb>ģj:mЕվx- 1x閺,N'gTHȓvf$ x> v>$o [a1x?.Ij R  hc:.ekei;F(<YG1Eݰ uc^!O&m6ZkwVzZ{l#XhAN"L8TF_EK?[O&+z ZCkZĉBeF$; P%|{M|Bc=CK¿ \m^w-3[HF|澗Í#.ɿL.n&"Hy7־%}FOj>t?iF8v۰`1Tݯnߍk{/GX~6]BO+|S8Z1-?fhnOk/=h< :ׅl4Q총Y!k[FɰDl`=kWB /jZw|Ax?ZKXEci͹f` _>#Ɨ.?k5eѵWͧ;>bpA ⓾[ |3>;xg\ֶ%B|$z` |g?:FU9P38trGC tGѬ-<#/L7MY s]͹*s11$;~BWGo~VEּdKy$B1k'y5oI WO1Zuf큀yz~x_>0_jI|ڪAx'$K*7M&AAa\t߱y]#IE-4QVE!f-9 qPmwM~1ڳWs =|g??~?nKGnKيSrpA9?jW{/mmx4HڎMZoA;Hn;KZ_+kzՆvmSuf,F8P +9oٓ^8mt6څ.4YE8 V|rYRIogo%}/o<u׈O^x<6鼒%HS6ц. 1\nm7-J躅֟I|Wkax; q4bkkWaܟQ٢X FO}>0N''<kox^e֖uk^"]! Dt(l Fy]m#{+2>;]ߏ iZ|?Z&ou#Ys"chDr^>;x=>1wyrZvN>y'أG"9|%~#׭foVZmӵXL` .DIIei߳֓*/ Zx0Ism"}R3’; WKdfTˏv@m9'6G~ -oh.|TǨ^5+8vV*?q_A %^glO|!?,m既E$Hz7 d9m%wڶ{CZxR+mĺ'RIuC\hlvl ;ܼxdG;vZP}dJ.^YIdWqIZW mIc c0$BQ%~kcn|sŚtskᗖ("9!fM]J߆?h▨{O{'-V8YdRP1zJ߆?h+rݯQEX((((((((((((+_O6o+_ƿ$-l VT%)?|=xK gu Zm1)$;q#ǁ#/K]uX<=s_pKM3 B".2qU'vA G|)>"\F׾1j]ނmq&Cw*@;z?<;|ftOkve&\Ml/[]I* Q€&>9'?iw>/‘xE0ˢ& Dhoe=׆ό> ovxwPCGB_eo/7yJV&r~;=^%׋>O A;U3vhǏ^>8\ivფ+)n+pʷ?x?'ijO>׊vd'ImŒI&!fl*1#,@EWD_>"x_lu{XgH~Vw}-gk$'?,Q.<}}ehV)F-v6Km$6Љ$־?(v~&k"QZe/By~wl i1xS݇<.EO ItB~%: k47P A?VPrrq_F'O#^8njfC&Sڊ2rNM d7ݿO~_M-G@d##5oTfuq~gw|@\;*"]}+焼_.axs:re Ҳye|);Nzg5%U[QWPҮm@\EZNI'R1lF@~Z]޿Dž>+Evl>#/,5.HdD3$#qav! s|xg]$EkcPuH!@)H:F%}Q/ҥZDbǙyܥ)w ΰuO=WԼu?Jm/{xp9¾8=h[[v^;tZ^i:em&]FUL|#">#xwI I`&o-ՍF,y.ѷ#9#G]!@|y#]Ʊiwż7.dV&pv~\{Vmڟ5kEVV@~G,ɲf19^Q}=9K,nPnp 5ÿl/}{kO ]Vl9PqY [şs^Cռg/Zu6TU1$_QY0Ҵ{7@5-;[iv6k}(~|:%$u~z&O~̟nj_x[DGMAGAWU4 }yRهp7n$߲w5=3[4^hkL=m丑Q!F*BMƗ񾟬^>W\k2P,s!\֫|9?o5 ;6e$ĚեķӗTy7Fs!:7{VЕ](Ko:XnC ֮4=2PfNv ;>%m}xRMS Yv ĻN_id y<{Y׼ ?zwV9Yp>^= j?{ |3Ho{jz5ݎ}; ©,3+ݯ~;|5un!RYYtݲ\ eosZ؋'&kxZ?xc2Ѓc8*ZN"^ww<]\^.u Nj i)XO ;`Wq |{e?Ƿ{x^hw`m5h9XDNN9 CKlhNv~5p>qk4Gv'$xK3 |zV CF%ms&ixw F4V ɭ5;3Q1NQvyhCr|N#MV_Ĉ=Clb{9ϴy~\+EGNR=;GYYm+fcg[hL6rp} 6r{)̕9WeskԚΕxɭ|Ex>צAAݳ*11@VQpsW?h?6k{i Cx^Uu Xu+d*щ$Y aq iּDxs]uck١xQoyjc* #S&Ig?ߵocZR\x쮺Pns.TaUC@RKmDG/~5:ߊ<{4T> o4 O`~r;#)R k<{oiaiU?2,+Stm*rFyJlgh▛֋sOiZZ5~<F?ո w^CP^je4QmQ@SvK ktө-;[/ gzheC[0XC)׌9)V1^FwT׼5sgso6݋Y;"8bnR36[V1^F_Zt=Z((((((((((((($ſ A5J=ap*I'7jzU, h@`k=Kk3g/MGIšxGzd];E4@J谖M˨@>w] M-,,yhU1G#3Đ9$Y ~(Gk94+ Ι}K$X4D gtVܴ[ş:ҿ ~&|A%·Q/nt>kh泷KL\3@A\my7|AԼkSI 4? [q[ |L;ON+~3aOOl:fxBn|=Xդx̔(|6?+˿||:_!F{Sk7Hn" v9']%KI&kYx🉬oy_mmn8;ndBc'mK1wZxj3A58y;󏗵8+5ov|2іmFV$LC%*-ON3Sn.O[Ϸ@9g\PWjZ)F]o͉Q$ytߍ^?'ѭ;Kׂ`>V2ź%#X |]xC⧁5o֯5R5mQjt Xyat? iO-մ}\KnHZAaȮ^2 p2#ڮ\_ m},S,qA+8GOk>$w]߷;঍gm}{ .$Q8`!ѥ˹ڻ8ߍ>xs^淩YGusb֌wۀ; G" uHyt+UsSX*22H'3^VwJo~ 6 n/y.];H$,\2ǿ |6'﮵ _hb\!e \W|M#wW^,mB^rƱ1Ǵwdi#|5Tj>\)mty`;XZ5g Y*VIM49&YOY/|$`ѼW֣nP]FnL[~y^~l 텦_Jl-Ğ,ᘼEhl) 2~ƽt>;|ӮToMciI*7yQlv$~ #΋"ȣ)?J^]ܽ֟toʾ|4|;5-2ZxMM,AcL1*9Ko |L|OFmov~cf> Kt :9w\mcqWF'aۜgg|ճ/ kmWI_0WvӚ*[5+~xWCcx%qv `wcGa:URΩ-~?M/iv )2\ ]G5ڴ>Z|H1i$3M%XRcVA*3doXN>.DKIoi4lX\|9ӛU!ֿ Ӟw*P7'מ%cv?.7"0+7xiwR[}Ě}0c>l*3H3RXU){&+ݏ.k.q.OmOQ\,0!C=j_&j^3Sx{Rᶱu33}崚W ) jk[|1|9-OċXCmq$3!YYnݫ'Τw wj #Z4KY4&7a[hZ-f2q cM[}Oy/Z5{;-)5[xdȭ6ѱ;#OM3tYF+i^9V647]2ɦ>X ^(d+b?jk8o.|/ǧw)RYDCk&LH[,_ ?ᵇi+4֯6k-GX孤)ڨI#G.}{7Ģ{i[22}o:g4+|{ MSPIbiYQYr+%]_ī]Dx4KktˋU#+jRhD;L jV)aIsӏ|Ykxi>#U[_4kei7pr_?|!4YΩ}3kq\"` j IXW~"օ˯i|Ut `doFFDϼi`w\/Mmϯ[GyѤBt<Ü[$ piun?s_p^ҿ[ex/tM: 2iqj KhvyBf[=k|ujyt>9`{+fK: rą셬iO3ZݗAj̍:X}4zIvOd$y^F'^wY^>uG/*.2J|(y7?nߊl zcm2Y1.ɻw eF|Q_ÚEźchM}r.6F7^v"^c@#c❮O#7EK}qa-q~qW5;/%Z\BzZ6\Dvڹ P&3;B-?y&t;F~آc!F:_~%x➏cO&ύ.OLiC13+iFk xB񌺼Z&mɤ^~\FGѯiMv> V3wx-p#:!O.5`31:W#мE\^H{ u?W|7G A 9OMc4: )Le'U!]@ 1ӗe&x~ ]'OBɧU>e @e,;_[GZiB/tiV)N FJR߉ Z$4]KU mO˾ёdOR/#k_&e{-v k kAx6t\͵tw[ +١xlƣZ^d>9P(# > rFY1rb\  }UA|K{Omӱ-;n^G([~~a+]cWׯv rmHDopȈe XvC_֑äNHDӤ:r8 "IoR7__IE/؊)*8R2s_3x_nj|C|IoG[xtVj6/>Q~rp F%6/nV?f*k5 9ePF!hmg7_/\xz^'ἶ=4aHDdk3XÃ*|b?3PMMistj7Iej^xKvŗE$'dE+}/zīfo&os u䎻d=6ƁrKs6cQ &7FFA5>$Giy5/:nXۖґq@b퉃_!jCksA$V!| 5~hA_.RxI)~뿒}߮?/]?~Dvym5Cow<dj+ ͂>|l|vn [9ؓա hZ2w;m _U|o%|=zއiMo/=nVgK)wP_ex:, -bY Htl6j>7=eEi >; hTĩh7sJ(o,|\xS׮\[{Bfl[ϝhwһ"ǃ&4-bMM-ɍYnm_o3MI>E++X}ny>B|ᵛB8"uUpX9LՉ$Gi φ;.-f}[\.ʞ{WSj~X0 ׅѫ~#6_ɼ]Z?УV1^FgQE@QEQEQEQEQEQEQEQEQEQEQEQEq_x&ZǬ?W~5$ofMRXʥ-QH WGF_&" h/pi62ĩmqjj0U# \_Fk&{Zymb38:(t~ 5t/!)Z6Itfp< @]hּI5^-s#yX$R5PWEE EeV_p=]^NmZg 7CxjLJtLwK%37Ţ#'cW1ZP{x{J{vm] Ȝġ bM턮I+ 5Z[[iq<2,}+0ZMm?u-kQgF#i/p:</֏i|}j:o˦7ka)$jƀf@)/?kk0Ge_ 4">U3]HipzskĚlSQj729wE"NO!U#U tTQ@Q@Q@Q@g+ylӾHFs?%Ƿήq׽U5kFGH;ٺBm%ojzQEPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Wƿ$-l VUߍI<[`ٿT`KERUI[$x?eikQL(((((((((((((((((3dذZ 7nڪ[x_;GfϲZ?{}1Tc/OP=Z((((((((((((($ſ A5J=ap*I'7jzU, h@`>R5sĶQFtr͜xjTc/OP=Z((((((((((((($ſ A5J=ap*I'7jzU, h@`#4& qUmc/OP=Z((((((((((((($ſ A5J=ap*I'7jzU, h@`mD6T\[m|Q6>m⨸Ey' 'm|Qp=nOAOo'G 'zhO*AOo'E+??ͿUhO*tW ϛ???ͿU$D6T ϛ?.^I6>mD6T\[m|Q6>m⨸Ey' 'm|Qp=nOAOo'G 't_ ?ٳ?5J=ap*×:. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uX:kv bD ];OG'8h?-חOIOc%鬥[X:֍ /C4]/&K{z|]!sG|_Mk4k=o>. _{uG й/&Džl4pĆGs~i`Z[z|]r7:|/ 6/4V$O"27F m_Yj&m`)#1"+33^{cGz|]ϵ|Fu-Ib{y 6qZDŽ<2Ћ^a^?G\_G!sG|_M^?J0a-A7ӑo]_Ev^jZZYO%[cA,&MvdbI0;!@R vQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEtPppƏk ⾹mn/ĶZ=)roiEbngTe +CBnb>k ث[+#@zR}h~c-6njy%qMSK~"<{Pi^!u OKYc[L-RHi9r$I?/9q(-++f!co\Tk/(ÏMwu_XXu:u"G.nR8c7cN7@2ɻe{7/vBKH)K9a(cFϜW \ۥ4AHe5L)cږ>ѨhI^q+);5owG߃/[0@>c^rJiM\·"qmrSs"ڕy>iskWŧu 5SK+1#=R|;ʒ<3Ÿ<O>3Ѽe.. ֭2 ye`˙PO]|j}~% w7:vEK) ED+IFMܱ>.%O<&~yѦaΔϴyGCJw^!u6aѼiip0KYŇ;eGI +o5+jqxoj2xIfkwH!kUPQNrXJx9&LRg[XI W*A -].- p!)o~$mB5;gΘ%6_aH)xLWxNeF#Ce9}Nk$.VvfsCֹ֝qosympR5U՚[Tz QRPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-18.01.1/images/user-settings.jpg0000644000175000017500000017532713222767271016464 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 507 522 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?w | xoZU[ƗQ«-ܡ@id`2O<נdEuE;^1_5?O++^}ގ`Wye}ޏ{5|B5?{z9Ư_QWz>G0XS r3Wom*]!wwģp˷w8+Ѿx>3-%/n ˡj@THR1ճgnEf {kY#vɼ=R?uv6,|)xO -)dx8 Z/?ϭG0XS }} 1L?F&Al>_nC QC? (sDGWۡmaa 9$,}n{U} 1L?F&A|?KkXuXDr$)Ϙحwេ^q֩qhOc.c&ranIl.hp>:xn;KQyd#|$`*[go/o3E=Ʋ0*䬂<sC? (ۡr9pwi:ίZxy"խJ)<(A%TL  ;{[h5XuT]Ğ\o%B埐8F/1k&X>j#9С%9EyX`d_9˃/9(r<gV5#M> ]d];+<2~e,UH5o#9u4(I4O(s|!?M Ꙕķ<-&,svl>khb??9%sDGZb???.&I%_Z>}j#9u4rH9Ua֭1]MG?_G$%_Z>}j#9u4rH9Ua֭1]MG?_G$%_Z>}j#9u4rH9Ua֭1]MG?_G$%_Z؀H!rEh1]MG?_G$& k8$h63v69g)]wu41]M4NĞLJ98ʩpx~C9\4 [5%Mӭ](*hb??9$>hq#᭷w+7oR==kce^ڽhb??*eXz}G<UQ~+ۋ"@OaqJE.I2M?> zڬb,nwyyb/㞕u;yp+\΅-~5=3)x9c)㉃:N _Ҵyw.R1eb~b9XFxi4_? ?KgNVXF#%QH2(Gp!csK˺Hʩ$g6z~63R?KK񆗬x[mgi5M@ױZ<1l=3_|E<5NY^YxN$J4#HQB`i>!o>']f\e|w1㚄_'˿}42JڊX _ ~]ދ 0RG}z8$±䀿7$*[ܖ]౪L|=5JIqS/c|ݤ1+<Ɵ߆zk o)|Gg:Mj/xg:}R]M - L_2^ݝ=3ZUƣ$_]FPx&\HgG+[c6? ).+NbvS=z%Cs=MS^ogssGSÚ^Dy.IGx<=xFy9: >5? !d6-ga@?s^?Ǡ7t RSy-`Y bVܨjrGzmvxJd0^"(^Cn[O|3vKOiW;́N;koCԼ /C]^hR-. ͽ턖JppGB+B8  -4moz/׵oqv7bTvwZZ?#x4,ۋ߱,""c|}ǟ0XronλTYಁ$3*)b58jͽ\2d@d+FpA>+_#ے'n.CHvSd#~bIop+ KR & ++ѕ ܠ<7,1~yN+ߋ)K>B8 >п??¾J"u)?PAlWHGFfN+ΗOk#2iRY/{iuԬ2"o \]W2ݝ}+xK<[ndcd)Y9_;B8 [Ay-xJ4D̆K" f횯khWlڃOFlO"ѤMocq‹֩~RoFC/ <c+x|QCD WQ:R\_]η/i+ ġfPMbd/^s+fUB;_&OWp}yEx_J׵m i~%ԮkxWoaR O6(Y  o'ƶl/vr4kŽ_hegԙ<{V"ڀ ʭϺ>U\N(lT]40)QEQEQEQEQEQEQEQEQEQEQEQEQEQYXCxGn ^3=k'Gi?"_*3Bt%'Gi?"_*3Ğ |UxkQ߶e&0K,?ys1A]!:?ITϴ/H +G}? Ou`kzwy~zeiȐc_N>DU'KPur𮴧 ^jg(#2AcYڬ88}? O 3M6[Ų3I 4LR&UlN+7•/BG Wϴ/G!:?IT`GN;q/8Bt%'Gi?"_*CL6GP78xA#[UoQDU'KQ`DU'KQ`DU'KQ`DUgQZ?'KQN>DU03Bt%'Gi?"_*3Bt%'Gi?"_*3Bt%'Gi?"_*3Bt%'Gi?"_*3Bt%'Gi?"_*3Bt%'Gi?"_*3Bt%'Gi?"_*3Bt%'Gi?"_*3+k6 c`#>>Aq8W3:׋uj ީdHRΣq8REp7d jvcjm?d!ZoX`st}}Mp7XϹi?qaem-nt멥7%B7%|\(K_ouWkg|W\꺕l`Q5 b}wq?g@MO: tPSErC8??kx'pT\-o*Z @Uu4W- [?83q?g@MO: tPSErC8??kx'pT\-o*Z @Uu4W- [?83q?g@MO: tPSErC8??kx'pT\-o*Z @Uu4W- [?83q?g@MO: tPSErC8??kx'pT\-o*Z @Uu4W- [?83q?g@MO: tPSErC8??kx'pT\-o*Z @Uu4W- [?83q?g@MO: tPSErC8??kx'pT\-o*Z @Uu4W- [?83q?g@o=}#bu%+^j 2`|s-[XҵMN1g:ʙ*H íCZasMHYKxαʉ3֧}stkkG\<3CY"e垃xg 6UHv&FyLkށ?m21_X ݨYrȬ# qR uZnoE4MffO i$R D+0*1:UW? uSS^y`o峞[V;wɌTH=(B[⯉~1|W7.ƝZa#C-JpsAwY]WBx1~ Ll51o%6Wb1I12+<2 ˋWww6{$krravbKFO; +_UW={OKd L8{+nc_4 ƽi,4[b5(liZX\E,SȌ@UK)#$F{S#he@4`z Zsǯ9co >đ :d]*lco,M:rXA^U}Ai^_i::Mcqǜ^cx5o^ҼK&_Ooiq[K-410QIx3oݺbM5sp=/8O-uͤ>OڲYXD;yl|X [S.?jYGƲ>._ c )bmOJVx̑c( ʌA#*mfW|a+%Ŗka29òl_Zо8k?Fu.y,@:^]BX4lX(PF9S\G`z 0=|?PG᫕ԯt[="sq5WH%2H]HULtvQӤ&dhpb6dF#I*Ey!2$HQH;ΒomoRkpe7|򾢺-l:: |aJ5躛yeh:+F{`l [_y&{'oT]&z?Z(*B`z 0=-x"xd45YvOUTl1u7VLeJև8&+X`HL3UP@'܊m#!մ7KCm4,@Di.&{K#zw [Wӭ/ —LZHC+ ˞Aj% O=[>0D%u _KAD[֟zeFiQFKx._OrzWnm v HlG{kgW=oz=$`z 0=-LAFQ)h`z Z(1 ~TPv/GFPv/GFPv/GJ)h({~x/xROW!7Kliٕ(ͷxzДRS?o%tKK-zm2Z&c[{0tۏxso jz#յ9y=Մm 71[dgRYM84Sz |ٛR.aZot[|`u`\ ^y狾?6+Ie Kt}i%,&:0UBp ۅ}Eol-)q[Z Ϭv&o [-!I1sm w9I5xψ-WCm[𵾁%f[_#pDB9 r@_NQIwlặ{P|SΘxKNɁok->ﰶ`Tm NM@<M&m,0_!҈ߴd.I(i Z_gx^-6~ d$Үm*C%9B`~q v%}K+K^+CȊ$_-e%avV_ZOx'GĞnWז[=JY70cfY#nvZ ~ k k{KV:s?4rH=/}"bRH%6ehJLZ"-v['yɋc GIWQ GSV^M;:,HbYm(q9f?LCט%k[_ g[SO`tlټد Xn#Q q9 ?A_ٟZ&uk4[JJsՃ)+8ȦhI(|mϊzWZ5ImgZZ8hj]4s8;@d99#cM"vcg1CCh4wWH-+1XmĐnk|++OMh^ #@E;|~np=b5iH~"]x=֧ã/Akw RJ:b|20z< gosx6Cy[{[vTc9; ҭx5~5g֗vxUdO1% P:}}S+ =Ϛc4k^"%׈<kԂ$I洹kwl̏l ڬ]#]ْ)Ak>ii/$6}?8FB6pK&$]yWUGzQ\ڻ:xHmU9HbOOzI7{)duW// :Vo^­6$mY#9ʎw~lI[cgB]Yלi մ}6Oogi A KobB"UkrNI5gotFg}U/]'FGuG5YFڀ9_?xSaZzuq 5߇/Mo~!%@!$129v;'WaIQE((((((((+KLJj,4־bO1z +rA|q ^m:1S?Ǽ/oʼn_ס'_hzC(Fp}ku+McO5χ3x#uI,|-mfDY-s'G W3mwKZ~$oGⳡGapBzj$7`cs1Xj~_G5k}{E|Y]b_ <"\_k:TwQ=.\,{sdDlviuaxAkMkC%,HBq<5ۉ; }AE|?7'sBu IYED#8p$)u*_k]x_Foa|6gg~u #,YpNA?ͯIݤ}q/~ҟRolHkځcs԰]6tYm]%~Uvm~0^"mwo/LM0IYK۾澫٬mL;{Ri[OV~7-luB {xo(EDP8TcC5t?+:C4C=Ƹ)673?}=za_Mw,f$nE;=GC^_<~m1(IM_C@QE0 ( ( ( ( ( ( ( ( ( ( ( ( ( Ƽ_w L\qqYt03([5eStk=cO,U^OXHܠpH5/.fkTuuP>rUA98P: <95ɥ٬н-& jq eGY[}gOե&kiA!);`TJ֠ 7'iM)g#e9ȏckZYZEkmv 8b@aSUmKSѬ'.౲C$72GVfb{Oi6[鶺]Yn 9l9<9l!iEiֱXHZ$69KF F PxCnY'usP ~dbm!qt60Eq5$X2:#-̔G"4 -'KiV0&|ʳd'<N bxU$lKrsz>=':۴]RW/\$F*IV؂ hN?=srx{P?B ÿ]wQmYMwy2BG<(93k^LwڦgC}bsҘ;<'u1X6x֛gWVrBK e{mdoy^ڼyx1sQhz}*SM[M]XγC(22C@íJ_umN+n pέ+ppzz{Ek_ #K-귍|3Hp02̤:U w},m\OEbECzZ`"0$(m9$jqmbHIXQE a%u9K^7wܨy*(,O($Bke3t-3j7awGvPP#?+Y R7gZ-$f6i^8Uo-3#19z^h}ݍvnl׷sPƟgbrj{+}J I⺵XäFC+ G=ZF.,in-c0SҘ ikyEs*+0@٢ 5[ƻ˶AOqi@C,FpVGND]>dWxyNBcq_E~3D'\ONu :Z.%xdPVVpA?Pq)>M"N]\Fl"] #E?<)ՙ1mk $&+mz~4?Pq?PqqU-k+{yl}9UxܛQҀ>}Cᶁ:4Ήl xʧmӃ}EQMD6F 31u$InZΙ>*OIJA hEQEg!{FReXomI$: (YX V,&G ZK\K:KhOJmQXZ|: ?Hn<'L]61F'Sj D^ťǗ˫·o9E]6ZCmSYum#ɼ'yVH2!#b*QEQEQEQEQEQEQE64[⇎&UWHN1K{QMǼsPo-\ߛ h;ϴiY2{XҎ _שσo=wVn%u K-e~EI&UI呱哅 3]Qz=wl!I4ڧ{i}m ҽȁ6)S^>~SVzS&K/,"/%EUI%VR$eXP[$P: Ξmt .afa>l x_3rp8,JJs濇uGD\B+Qkv0 !F9.O꿳 ^.mzBϮ\ڥQR[D0DV'˟^:O<#h7JČrS`ţ.}i+[D,`F#ݸ*$_~_ī.mk\巕sl-r[0)RMkExvA{xRĐhw:kgͨm"0 *~k:}߅^ i NTAo ҐA"($k ,<=]~K;]/KӴ]NӣӣIB}vE..wǸJ7Z[/џ5h~(jxwA4zn7m0)f7E}Ww^Q:Ƴqgy_ivZ{3[>deNi?=/چi;iPI2y f%$բ,HaUF:GȦNws"j|}K7."v>0{^ xkԷ:tK1vi]cxGW- ,м/VRC)ҰSo]PwS?W?`Qq*:=BlSoK8lc|:Ϋ H?.>i>A`5%<5ǧ%TfaI 3pJ͞V_l~%6Z}hڑ9L4[:FC *g͐E}%\߅xCw~w>Cl$FEI<穮+t'@_خ xFlg (A X%շVw co2 v,/S9'5M@}Q7{91S4 v>ΚV Eu  rWBĚĉm=GL-4&)--er3;N2Fs4GBmM!\@Qp8"go+Mf.B:ͤ@) E rhJ̓W>iӾ$/>#5[ϨxpLcw6$VѼo^<;u?wo.@_+RQ?_;o ֵ]bZA_Ŧ·7$" $U VcY>[(~_&ڹA#稬Z[>xXX'A뚞aC)[*3#PsKg_$tzcX۲?g7.跗XIӼ! =<5g+[1ZFn+OMMX}YtUd[D9_gn1%ec#ATΏZ$F"B=A;Yh ?׭ -e C*V}'džk{JM[IIg '@[_)ȯT9ѧ|py $i(o5)qm~xZȩ֏["\Y?f_Y$wi<+]5]/:>%i{gaScdCykp008~ZzQHU{VԼ9y~$MM|$una,$ybDFJyɒ5CXdk?=SJrMҤ2s6 Z$)$K|w3i5q-]/H‘[j4o$&yWAx!W5ͣ Z"1&F3 ~xZV'֑?m˷e!^~T3psNOxb8c_i+,bb: ,%5ICW51Fk+m|]qhZޙb˶˻]¿0`$d[|D|Yk4kjzkkmqI.a3 |d‚#S |#ڟWD)mt7~KziU}o'Ky_~n?{A3ZEs!;Lڊ:*Rmk ,Ŗq^jVVin%i(hQ0<< {yZ5}$݋F\`IRUIP>O+ͧ#ilw?v1+[_aо#hw1 5u Lk2DjHFj7^<0<)7Zq x?jQkv1 A'"?_޾{LɨZ\7$%l;Tʩ-& [__W𕆫Zg/|1$Pm=kFTP3en|-gqkc j_ ױ27Jz*ahI>^cKM~3c>Cw_nXc ?_j:McgFK!v'c<*#:׶kF ( ( ( ( ( ( ( 5>5_xZАBtR_,l@~a]]|?kZL҆eU6']#M.ﲿb컞\ji_ɩC \If)#bB;&r8$`Q{m啥`eI. cRrIAh^ִ'Hy5hZܭےog>(Ӿ<|ei'[.xP$ 2 Y޿\mhOޓm\jjsc4+gitrG8*>hzw5-2Ox7ZŦGnǓHU!Q"C s[EDX٭ﵫ;nO7|4SL(m&/''k>ktKVnw%_PqS -Ɵjϊu u4no&8Q>dg. F6@|F񆃫>,m!ƙovt?NV9Y?j4Q$EIVOU-SZӴ8bRU7bWeaz|x]nw6w Ť*Aa04߯>6z杨_YZjW wo [.(9BTG5⟴?icK🈯tsE\j;1on#`5/'ƺ^&kۋ-1<9[\3OZ̪DJz\7dLM[ 4ϫki4hV{utywO.K:˕f_8xſ4]nO xUR<={Y,O?SJTs>7UV&4H%Yx *O&|.@RxZ&GdjNȡTBrp ,8>-Y_]_S-3 {s6lT0gt'`rN^WkV KSkY5kSu*'VHSIV)wɫ%d} QۺTO#VyIQ[tÖG¥?綻,2M5ηQwwT O|wzŝ=WZ3G80B-oA⛍u⋍OZ׊<=R'Fi&S.#1"/%@n1*\]_oIYZ>8)A^4/KexTDٝg >eT?|e/ú|/E :-xè>ۆhW0HPn'{kBk:.xCh 2B,l3rMIZcT?tE^ֵ N⟈_[\zuC⽈#_3-`q^x=Ěţd)IDݺJx4+O=a<99|g/WQY.&!ԋ9E +Qsz׊5ε/h!Է[A1>v>`9׃mX]\F{2fQ2ǓOJ/o|zǡxZNmO dtJ:?mۛɯ' @Z.=(?] R]‹P¥?綻벢8T?tE쨠7.=(?G*]{kQj~*(Kmw -C @ZʊR]‹P¥?綻벢8T?(OI뱢<]M֫%u$a}%ѡ2 eZƱzs_$Qj72>LA[?+_ k  }@?_֢O64f% h)9[\Qr*w[7:v_ۇx'$@Yr$dA"|_+;_ٿdSC.s4hh3LJg 37Eg\/ vZ֝x_[DY C%2D0q9{O!;]tSKa!Կ"-'ڿ:$W̐]񧅾{'~"&5A̖z>J,Tl|kK6nd|LSQӭO8lf' &u!߯ϢCEC{Y֒Ksކ]bt8= K3)#|JrJW[{k" V!d'p>#H\!!w]|Mx Qnh G@ 1GEQEQEQEQEQE"~xs\rvWQy U,줜*7?os=6;Xg (;_UGym#T [E(KP0U TvqL>x'@L𞍧4r[YGWX !fGJ.࿵h-PGR2#' 3~$Ӽ#^ mQ4pЦs`Aɬ_}[Wn.Y/mRú,ብq:R]gfmx.GΌIMaVgfbIGf޵Uke*\J$cК(^?I:RisA=Ś Y!Hd E U;5Lj<M\H˩>Ճ+ےH9T»J(_ƿ  ٟ_H%egfv8[ob˶NQbXU^4D+}s=16OM*رZqߍBacI¨ oV.i<(!c>cR5ea㻄6Xgk2t]70\M5gɕ ǞIgږ[k$Iϗ+:L3\zU\޵k[ϼ_ sK?ޖ?-]rj1o'ww\3Md}}st-6K!k=XbtnYF4x}dC F^V"A(e%WJC#1˧FNJȀ\ ]KG/%3H.H}/[ϼ_B/4e7b{tæE{Aq5s<%G@ώSzm>[ϼ_kN_G֝?Ƹ.hgh$&WXl;Uq$w(ӿ/_\ִkx'3Emcb276Վ9 Bmikhӿ/> J?MzT`N.o.-H"IcIW8Q 訢`QEQEQEQEQEo[I kF2b3E KeQ(_z&ixgŦeTsn-홷F#T &Cd[|S[ݼCl4?%X 7\yIٌW7cm>]N8cX*bIȪ3dj|$d~./n yi2zo#zZwi}mo~6<~-uz%D<];&m7G SSbTӥm30kWc&yn mXm˨tυ^џel-/geI*рsӌVm_~cs2{]269"ϕ9vL%KvO]}I>yK-~}wN厅c΃0}$j!k>iX,W$;k>:.>|9M|Y᝻qkOҵ9fЁB;:Ӷ+{WnivZ[iVB$(#9 OUY }Þ4{ YOkZF=Xg匑dI`ɆϖH̃5 Yx >=`ch65дY. E>dRvO Ri1BUơdU3H.xt| (<1c/ٚvȥif I#V%vJտokCQ]>? ͠xKgSZnX@!m,*O3Z޾ 6HLzLw[ Y.%Ug3|60Bk+n4tۑ)Ȳ*RsNu-b:uޱi( bFي'$c5vC6_SW:kmxkhd!m?m aCoXƛju Ed Hxs?d՟HK&կԯJ7s 9'^+]eFG=Igſ_ !=EΓq/:صכ :q3geӵ d sʍL3gӮ5WZ]$S@ dsUpF*_ZhZlwQ۸8GwU9ʱi H}xgxG\6[\kvowye#н3 #aT?fk6}MN-_R}`#^y& ~Aox|7vַSyb1!D'GG̎3ٮdO+_ o&[緶<N"@'4/M14 tV[m.h|R@꧁ձm e'-+ǻI$֩=+^u[ 7l-B=r#@$ p*K,O|Eu՚^Isez5P:G aU*xtiQuqWUd0UE1, Ƕkjյ#HA gNI&!<5AK]&gF? oH,$(/H6fU,H` 3+$xG|Cuy]4tM $gg(4%$%0V9|ydG[j s%)yIF3 gC^_OxWN餏rMJ ``q]?I]}"Ծ#x~(cxÚsحg}e)h!U6yau P~6W_ׂѩ\jFk%8ĭϖ<@@ ]mMc7<K߇Rx f]!mQ6 r؋t$"gydPz=:t YAgG!.y Adيx_| ~DºbiW,Vg_O$9bio4_O> ~.S<1}Oo\Z<BP$UPoTemzև6=Z^IJ>ev{@\q^|'bKҮ俳Ucyx?3%I984_~b`eݤ,Y)Ig8%YA~z4d?_>~ĿjZ[>'tXt*# W n6Moi- K1S|ID.<7O{oiu"1Go$/;GH(Db5?U>ς/6U)Ѳ6, >cf7ٵ5_D%{iÇciI?{Zm5yÿ~&/=S7"Ïu$Cb$0u;$KA _Wvi |'\:;=)ClyNH9 zo|OՖ8gJ}cA.w(K13hai[]JoẏW"E6&|Ny{-ߡ/**?~Y^sԿ+R-ߡ/*_[Z]դM?/&{/Us/LS̰ՠqI]NZk0^]C{g: "IFC+>ooM_˩xf63h,KPa ?7Q[t^0D@WR*YQ4˽\>P䮍\kVcߨ4+|7sa7gIM3P᧾e5H sZ>&x°꺞K ~0Cۢ?4fINFת_|}mE|kq M|O>\qiܷm&!3wOEz_~(M|5uk+MkSI^K[Qp$.7FF?oN~{ᘾ'j> '{ޭs-8|\=0p3.7Y7_>-Q?8O:ZnKhI^/>+,HjpBw__[ZC{~ּAg1^G \1 gE-+ ZTkeyovm0 _p*<py#.e׬|ؗPFqXB +E{Q?减[+h/\X,V0I9Nם Pk/kw3g3"`?_ xGW@ңeGQo ܀ $35~^_q}94>j 8E!}xkR|*ՠ@lӿKڳItPjOFjcH8>J:xځĵDIA~-Ek?K@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@`/ ,_qXn&ۼzfƏ|?FxF'{uG∜yVK{JVg ۙ/n p/+s,rOsXZ?뺽֭x/ڎwzT6 ݐa@'1]kZm{cu9QUAQnrzOk:&_/F1aPIoi\(U? ViZaTh\v9Pkgվu+Hu< 0y皚//~;+DM.!uن{!v@>xSŚmxcFl,J[]COxʌ)H\6P尺XR[MxR`qg.Ě}Z(繷18ؒgcn#iOCZW '7"mEi"YbFֺ σ4e.˝)'KIxĒmܱ ld*8W_ES <t[;io4o1D[?xƙюwǟO!ԯ!-cCn(4  `]Ʊ=3@NQMFlCHE±ZT '5`sa2Pfc*I^& "MwKS.C)($+F7 v|2'k{ []fF-Y-3# @# V 6׶VVP[wQs>'/ajcH"h+.IRet_Qku;]Lo[k+H^y)fc&p.нu?7-z <֒ "bԺViivzucy \A:}#u = ժ=apFJ(xNzg_)o[' |E_s\?(|_ ]kM]H+#K؈I>R:QMSZNkzE7Nkz4&\ӭ&{5ck܆ FB/h(((((((((((((((+? |GO-i/gդ-mE(ʥH'8}P\X]31_d@f$o>A=++֗𼺉C6bW`@+xOa6=hVtY@.]kkV)YF] z X ) he U߀ϯ{ -^mSI~Ş#pL|B1lj.t[kkK UW65HԷ8b+jzPm}AlHu -^H<(l{ykclm6E˥H-X| 65u$/kFo?]{^ gm~$un=֥5Mۘ9?Qp'&ƶZ.[|?k}o\:ڜJK|]IӳNmP!Kh7[xC0@ TzԐZmqK" #>zzk [|3O7W25I ۣҼ$(6,`)8!_խ]>L[B |__ƹ|>m \\ikjڌgOHT>&Њ_m-l|j:]/"IEM=5]8B 3I8$U~:M!t)v<$j|6}mBH J?)v `NYZAkqq8#$&K.?OVw5gD:/|AKk:/4xlY_)u ,zUmxWU:-z)+}64wq @K iXd\mRUM@LchbMwU}_ -I~J/^}[7W;[t(44q6^Rűd,iVQETDJ )clmnJ > !i>2.[ص n-qm cDu@y.ۈ:56Ė2R\x^FBI8Q!6d}X,ɸF'#]qDv#.1K)^wCj^7` #SFOxv~xC]ux7⌞;S[ma '1L|x}Ij? &e$Y ?Č? ֋Z7Os.(v>칇%xӿVlM4տ[7@ΦJv`w[4տ[7X6?5HkH1t{ g_΀6(((((((((((((((+~)Ğ>,X^ m, 0px85W^3fB70WpuN3[\G_/ʼnL ~*]}4)eүLan#8`øa]e|'a@A-U<9IVKXʌiۼ-\ڨ@O"U_qM);}2&MIjg;66Ibu 5wO?U ?/w Rm>F[7s( -z=o8ɤD]/um'D7v^m.)43БZo,M_>:RPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEakot6羒EkWtUB(`W$9XW^ noxA/cq@BC;0l{ccT:L<= &V/23gos‚F˜ :+ tx<)LZ0>f"-Uug W1ub.o%ڌ֗&]9isOVhxB|GuuD $g+>Z^yYگ TPyHN_jNj1&'}B/t̾_Nuok[O~ӠKX$`e6 $jYYI'кY#rIJBf%KdN 2_;}B/u4<xsֺq=nݞ9&#%8SZ<hxDN i6j9-`-K:w#fjO &,ĝ,kĿTtG59^iO-gQ^x6w[^:5.0_ėrg؋n9P+5'x/x@3 %u8~+*ǻq*FA??o5 /B (+95$:)wf!6 H,goHPdH# sSPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\obIyfaN݂L@*XkOgYAhz Ĉ~@ !?ƼJZll V#H3oF\ WM g_&h_/Lk(-aa!"`8-]Y;;i? ]ּm;w׋>45lHP$Q\i |9%lOEOtKdY"9-BB' )%dW[]5@W^ڍqj}|V0,(!H_ \ϙj sq.ײ~/0ڭpt7Z0SU6_q*)$;jZw(5=2 ;Zi^r9SzրgM3#-ht5M>Ւ<+rn|y< X<'KMbjWlt5PN`K \"bO0o%ҔRz_ҳOGɶ? y]WKڶk2 iCiI,h%7&N[u8 _ =xozټ+]MeGI yo  ؔPMw+ F3<=;7Ƌ}ZE&[rQiAy߉^k,76IYHL"09^G5ۼ*# 3Ꮘl JOw㰒rIO$wE!`eqyMnW4GĚ*)eG;t퉙J$ГN21_iVd^ѠzMzښ[ %9Tiq'}|`qM?ETUv((((((((((((((((((([F[@{+}OLc5bXe`8 o;SQK93Co3kDy# "Ai𦫧x♊l?WuAcivSF0|6,kc ZΡi;y/?7usKE6@lۗzm}O'o#6ZtCMkZ-F [p̪r SwiJ.npXOCUKAzu $iֿ&M;xmђ69Q*灾U-şcÚ,żw{0epoYp26~ KHck>˭YY}NBrs-ExN:s&G Ee[jR H q / (_o)|Acok.ieik4W u Dukx˱> ߇|-jzFÚ=mf%_rJh闚Zx[W4 }W٠BO1Hlcw^Oz!chV86U8 dsX>2!V;C_j[t=-}_5L3{u%|{ E`];߸l@z^nhӷC(M2x'[t۫w帒~!ڨITZ/<3qZ^Kj634r!|!$RI0 du%jZ\B[[nr1nqYW),a~ͦZDbŎf|wnj|P|%?xn*Lh'~ЩA2 ߳h^*IX֞ѭ.4[2nǶZQZ.d^|0|5.>qΟseq"HMr7!b~ Zo5@+g&iJcsgir3F8P(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|e__#oRt7Z2irb>dٵLjF˭&W>8|GM;߈_zm=37-3&FrS79MDhڽm^Id"ܣ5Ó$9tmkWeV_$;.mf?j!Muw4y9H;b>7߈>'ŷQSkqG}D[\;R,Aېpxz?x  xN/ hV~w3ϧ^N0/7{˳/'^ #Rҡ4Xx'Rwq-ΙL"ʻK Rݓ~Wȥշj&φJCNMKM:އX!撨gw~ oo@~kZ&wj>ȶE&J0Z\#;Q_2|@ÿ9mX<;43^GI&+C I!?*N#C¿m֓7-8ٴ}GRƺӌmHAVfm0GB\ԗ klȆ+|N}/ϋZrXKyi"C3IMk/󰜬W=SEѼOviv:6#韴p*">)I4X~/F*:tmy1aFBM`ڼX=c\'uo |/GUv{1u=L@ Hu+LoE=ŤOIŤOIފq-'MgxCR4xdͤƎ!`O&">$_H_VG A,\=No:+_0Sy#:[ qr+|][h޴[[/Z{~(nG&ke5Vy.5%'͒X%ݻUro<|]Ay|;hjٖi#[tI|46PH.㾍>:g/E7Kּ;wtm QUĆ;y0xPW_P״)<=i_ksKqas-µmth @?0R|Ɏ:4}-E|{|tiZ.uxcOkPmMI(oJ4 glQ[{o5 Ŧ^iͩO)iRuGϴ(E1K Z6N+GTWx"$%kx᭖X{kXbio*9#. U]ԜH_Y&ixVR&]=p&IۊY~m5siߍ>&hxvtYɪ][[QOۏ&u1a";rA'|bOZ4kijORUh#4Y-G&fϡ(:֙̚/i/JObi bn+*dq *_|U^w56-gp_+#!1"'p/(AEPEPEPEPEPEPEPEPEPEPEP\e Okr4la{hY{D/8n"Hd13֎SF-ޝ%v]jH "Y-\W-OwyG>> ׉5k^ ۦэ(d\h`>xo3g\;-bO}YY0̘m#P6Y^]n7wW]˴.&URt+# ^K4kؤӤwn$&YpeĬ|wu_Wαc>Gd4Ymu{##i:$f*3zЉ"HDᔑבKss JAY*I$3.g^x vM>\<f=J,帅UbKa Px// xzIM i^M9rf pvɸ 0+D56WFziWw<'jTVK5hoڴn2812d;=Լ>uuwoFs%T\4VoFKtP:q^]Il .$u6>4o5k+:Y&!m +&o15x_'u=nHkIac- { Ƌ;kah9eh<} |R:$MR^Aoz:B8 i| utoZh [ėw%O+(#hc dw5{HZ{GZ؟̚ka4M@v-x<)-6sֱG[?Q/ ,TKC 4EZ-yooC_n)Fo}rR GQ[ iY 6]el BݧhvEy4a"$3FkOn6 kp 9m$iw@ff%I=pGWK F-OMGGXh܂@%W"2u}z >%.'[)fipO$!GTE,~4޺]w/mjɿF?7eU]҆$( ֻfu)amuE'@<;@U u 2GCKFxSZ:%lzH y1H#$1e%T{eծŋ݀19%+ҨX?ُu1c$iިfv GYF㓝 |,.lK`$󯦙>h;69wOK,2$GFu݂c+t~?tՍvrEwt"\jIp8kkCQ/mk[k*FCꨠ9]j1*+F e6p/ޒF M1X|m@~ ]:eUR&WV /l.l}Bc&r"ޘM.3;V99/{t${gq/S,<_L[Kh4&ӵ[&o"Dc|?٧o/i{ MB;|y)=r,S*m ^su -5ĩ+P[~G{m7V6<#8Ƒkx_MWi]%nǙbPjᮛ)-Y)X 4y`#=TWE @QE (((((((((((+|O{~'o䱹MhD_$ap/Wx.o_Qn2}Kv|QcqI|WB{ih >/xJ<5L{=VW=spl|w.ֵC-ˈQ̢6ٕ\r͟m~ x*HvIim9yR1sN0$ps~-~}n]:m6 J7 KF ݹ4&֯_W&-2Œ@݉,̀I'UѧNQ]O766:֮ /_n7O!N?/.?QK|\hK!ne@(xZZúZ5v,aTr'(b =1Wl>/W}u;x|7xm'WU}[%%I~Re3to־x[wzΩϪGo\no1`.a' PTn~ xW~vrr0b˸7;rI< )o+[X?+/",RRܼPN78H ^~,jڦq,I"HFfP̬$A _-76ḩqI;[#<ⶴo >Ե[:-.I#'q,Hv/pY_%7g6l1Akc =ޠc񆡾_6s퀣'(xvŚ%1 QAlU#|G?Z]Nn2c|tt}տBީ._W% {:x{T/UuQ t{t5+eتgH.|1i9h~kÚ0]ɤPr)2 8<>x8xQZo y6L`K;wlgfAg_ßx?sėjZc̓&n @'~5nFubYU[X猁^k?=/ Z6UpRV/$m;f$lOJŸ xBKmHK$ҙRYmcŒ|8W~}Gk?A Cxz ӵI/K<#G *>,;ž,ڍhooI٬7mn%w9 ǰ0'o{u=e7l->jɲ Yx; ⏁^sk^!Џ9P 1PRA+JVKߗWD:^}/8gL!Γz Hg#8=+hO;u!kMJW$j1[Aydڢ*eW>9״uH|5IǽyYn'|O*V:.4F搖L\Ļ$ñ*q Ov_^Kqߴ+rZnk5i!ioHo]Ȧ$:qs/$ӠI 7B#Lr7r+_KtbolLYA)VXT#~?*By$p ڃ)8a&ѐF8I Gk|m{qGs4rii-md]H3,Ɂ GĿ#íM=xvWRL P[#NpGL6~ cop-.RY""jnbN e ݛ7KR-j3V9OB 8' .Sd[;gx{i)KO{TFL)|Cy.?5۝Fߤ gK+3N~j-K{)ue2$k"i7qF Rs!s &o ]YbKY'UPpΊ1YVWz]G_:敬<,GJѴX s Ke"em7OzmQO~>Լ-7gAom&y%vc>p8׮gv֖xV[kk݋7lD݉lo8y׎eY|M: $sK56(' 웎үww"U_h~4|JԼ^ڍp<9kp4^wi{չA#r,,=+hzu}F9n-m,mR!0꫞1'? <)gg[I9m07wl_ºWxltHgbrĞYi|nkQE ( ( ( ( ( ( ( ( ?z|1z~>ew[5Wps]|a.)|q?-D' )T`Wn:RnɾuKߎ5*SCiZmۤvC uQـb{yz"hGXƸm>4ti6w^Fc3&Y^Xripy]Sc} oCK> (HHme6{su+]Yϡ4~"ҵ?SH+;YKĿzǎ5ۍz+-2W, l~JBs:h8bF5nfa5Uɋʐ;r֯ğ-xm|giVZg%ggC j'Ƒ!-3r_OKqGi'-I~ 5-WGvQCh%rvf#!lW#gk1#,b4P; #FvHqU5ԯѿ ]ͣ./ ?ƾ>Iz+i*eOW*1F#T |GjIZ, .#&2A|]Q?ǂixQYa}sk,\Hp"R|>npiů9%n\$Ϧu;DOM*[xV԰c98BWի\_5w?u[S{-;PywHQ J"6`nw|E?i~#)\bϴ3DbM Rl⤺=$ٴuA=GG!qOQ6g/ 3F[il|'[K 1> bQ܃!xO2)~%]u 8xD=.=r2GLdku$Tɟ]MA=G_~%j:Nie :_{ص]E-./ ?? z_&ֿi [xN-CºŻۢ5ޯ,WZG3F K/:#0 j<1,HQlOp94/z&|biKu0Z\B)8@}+埉>QO~!j~ 7xh[Zͥ}+AOm-޹ό?~+' ]6+ܺZ$M,eN 0e,ʻKț}6#El,A*s\_5SGgu'O>MdV{dž"F4Ծ/x/8şuoLgI;sL(V"<{drQu72һKLgE״PZfnu# @cl> 2+g'kΟU^e\ۿr7}S|Iwmcu]ԄtKdmg;JF SVݿrk=''kJ:|`fV 3w$35goTOѾ xY.MsHommg.QP*|[ \u>)xJ~$kW3i:vVj' gʞppҵZ~IZ_>t}#zd)M:wCwewH=UH#j!qOQ5x6_ Ztյjtˀq6#\ ;kj-7n rODŽ4ȴ$pRI.D8q%mFKS'h./ ?B~#YgtOzD:VAmO4UۙH(Rs<%/avFD,/s%q[TB 8L\uY(uwt}\_4M;^!ҴVk[cN%` 4wT~eږMCou曧Co59AczֺZ(Yw[w&c"HaLU##´gzƭjj^zih5 I9܌T# x5 Zx^|ޮM17vح_"W;2F#xňTR ^~%Pxn\jy@FiD;sַ*~|A&Sψ?=Xhh 1}%G{?'1O| “Q©Rj?~eÛJQöRݿݥxWl2r΋±9Q"^_ %Vz]RlEW0T)5?G*~|A&n;z#xAm94PmeY~F>@^:VsgICDb M΅$#`#QYx#t?N?> “QdaO.7o sIu y0MyNᖷj>ooÍ$WM"$4zSψ?t?3<):>5=+KXx[͂#HN@J/ & /UvVCXc*0Q  U: MG CI`b ?sSDZs䓙G΁{-69g.-<>HNA, \l ?T)5?G*~|A&=ԅǃtyƥx%t “QZ/f`k/¿jͪk>umMњb(Ж#Z/3@&+V|ݶw\F|S\⿃_6*-<+yYiܠ U: MGѺ!7W]NY4ܒq}qfmF/~.|/kWVyd9xId8SS Sψ?bþ\k~"5#Jڊ,@QI$Cz+өjoڴ+[8q%_:'N*1{-'M𶛧VWPEA  nw< M[ 5kieĚ1L-NXdeHf|A ~&a|MMMvQ"YNMkxe}P-ny`_cdpk>iRjYx7H}b?'Rk 7ɒvD?$9>x;kƩ^F%>ʧqi©Rj?~4$z< ZZ}RlI<y=}fPݍޠ>y$T*Qdf Q8ڸ+Fះf+Hdr-wW6.O ?I 9$ڇ ៅ5}vICkqB?( r@T1:-ŝح |P2qmobW,7[gu 6;ĸe6q[Zl M/7)fW_j>Di"BiV{cWBik;M:~i vpXxuK?)EX@uX@@QMaٴy d4rCIgƉQyXGl5o🊴-?YҵzL K[XC+gy CIvhKȧz_Y47Ks(Y%UpsY΋z]xCI4=JYh 4hHAo©Rj?~WC3x 5x,F?jrMvnu(atOVvWQx?IMJtotDrD< ΧIc&-IZw4ImiDc©Rj?~U: MG,-z D/uKN?> “Q©Rj?~.? ?Oy7TT)5?G*~|A&/­EGiD2VGM42M6ɦ@ҹW.p$L zQuz8uOooi ҄yW߹bD;| 8jk gMo\(&(7Ԛu/vc@ފ?䁵LTd} iEXq/ >M&BvKY--]Z=. YReyf w/@˳CӚ+xΟjcaC !~شl̰ؤ1.Ǣp>wm#D_ϐkڃڣ|S9Kx/Qy&?#ٗ #vq]'k_Iex_&XkxYVr 06B}7ho>X~ՌyXߏir,C4![dwOK[[ V|CjzmVzس5=_GZ&ץq;xfm[MՕuu ԿxR2v[l^9k;v`" 09f+hV(bHQT~~$_O55𞽩Mmo *k7єWJF<5|;|M&k0,|+\kr0C&l] ]y`vgI-_L Gƺm$Z\:5UHmvs<տ |6|)xR{B]/ۖ&Ҙbl;QJcI8䚔?5k?Mwbkx___hRxeyQa:Z1:K1ݳ2D, GT`uAL4-jIgg+@ȕg\rο$99ž M IZu|?8Ufp/jaG߅wajLy=dxڜi敩Ǝϡ8WS` |z%?E}\NNL_uMsTFʟf[|=Ag]g_i/.om{1eo*IYT-eωz?tK_|Q3Bj)~WImnv('lK lm/d2/ |yXݞs֟uO ^f&;?j7 `|crqF*"nO|&4Ɠ%ЬnklhdrYr$z|F.Wt;SvuO Ece6:ѡ\?*\G"kvBfu=C\?1Uhg~$Aw?/x>M3 ^9}Z I:uCoQuxm.|O<#[6×W~)fo*VI$@Z2`3(DSFŦ_=-Zq3Kw;sv7>3qj@c1MB1oU꿫Ys6zil|cKel&_.zn\"fs6BC ?qk2/eYk:m޲O[Iupc,#BNK 6𗌼OꗺTOvwQTKe4U$MzϏ<;xqsoT8"z|%u+|&

/]ok7:)l6d>cxfkV;M~(K~7xZug=ݷ/5iU*Ą5D~,/faٗ_.޹Xt3\Q@skPFʿg4>sL94ޚ/O+_+O _O< /"0$3\s,m,oX{#RAeI_}k>?Ѽ-W$kWVu싽&%*J$ pk2^ #R7I9+Uu c AE& gv^}eRu|0G",mSs}C~ ›zezW+ G&ϸy&Fi T73pIcWŸkeO¶ XT(]qwWߍ Eԣ Isp?~KwZ%ΓkC`-n5g%Ed#{vi5mooW_87Kk%[jz_<)JIL+p6 ¸lş;Yn|;جmY\_$rGv GosVma<G5s,̔*2NaS_S57mߵ6/|Viw ]Ki_m|Iǯ|GυuSitmdTr&,oOy5W~!.x-Ieb( .[@b>_ǖ5׋|)r6A$2mUdC"~o4s_H..)'yif ;U+5ۥ%-[Wݮſ?mm/[B֒"س$ TY`95?M@D۹lR|-Ŝ߬q6ҍ4 mCeF8$ֿ_¿g0q_,+ɠN S L\yi+$]x~ei=;6q1sN356/CoU|A[qj6kk>KfD[F$|{44Sž6Eo/of"?};pѮoc)&Ayn@belWK,^>(uXJ< m?~.x}.<4ݞ>%IN-Gm$27"3e}/|V_5X@Ę).Y;@ %퀧*b m5Z3x~!x? φ>;Qrx\?Kb]զedYv́1\$8GoK.ReTC @=*x}/&J#*:hw C7gou?:[')5xN!/Zƕ<Ǩ[D;[ 1 6_x7I-kE"H<;,&ŽY୻T$G V÷fxz}JzGVI /^}Cx“ZMeQLr$n-ez\(e桧[OVO<1g:Ïگ5SQKm;GԧU!yRH{+0+<-Ja-ش E4mP7fE* ~tTS((((((((((((((((((((((((((>)[Y0G5iq$f]C RAkz!V[!O6$*㑆}EKz:?Q;4tcIP[x<4+O_x_Q5/6$76^|6_O5ܮYx=X2’qkIjj-ީKqp"W<+|A*8^#$&uw-q#[34ږc`eI_W%j㿊 i^#%_][4kc:7+_8+Sk9k 4f@8d2#-]KO[RS5{yk'L Wrs'ºG%{YiiY .IfbzKmw7*(aEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP\;ܤ)brNՐ{ (Txox_ v(Q[;SY|3W<0t&Pj((fotoxx-18.01.1/images/broken.png0000644000175000017500000000364013222767271015120 0ustar micomicoPNG  IHDR00WgIDATxNGǛ6IH(R{S$ $.UwԪi4 ` v 66>ml1$`q8c@!dufvECZi=7;;3!_V`ll&OFDx7Hϟ?';;d*=ncML&~t${)9::"rW66lnn"O$:\:dAeaq \.Qܯ! \Q>\+I$,ZPծwC.]>Y\`> p$a(FZexn_A3;&PpK/#!{ < ͍M Q(7ܔ  Dԍ@Lf44 iiN \1y7(pS-t+T'Rvp{Ik{Gr4qIz :QA{c#䆮E y`tui~ 덪';wLj]JdjH1N s6؁7sAW$1iՁ/PS'p0, +'&ljpVc67#~o&M+9P@ ؝6B 6>JHPCWJ0O&gEA76.!׍rPB$1Yz*0[oVN|aN?f9XhooYA̅fS+4،X} EI؜ Ŝ|~N6~`PK 4W'k+Pʙ;n&$ j{t[- @Mb OeR1hE\;[eӐ"fAPm~3&q)3=EC.Zsk jZpy2q|1e󭰬Nlclj%p}%NhelR9,^jY3P֤20D)rܼRddP(8^p3E"Z ( +\hxk'"5' Hg > h>WPB?;'eD,f ,~s&` zl֝NKs6݇MH,!?toNM-%bXij)(_9sŋj^B䟆C_pRMMMK)5G(_C>T8R1䃜B&q_h7pIENDB`fotoxx-18.01.1/images/bottom.png0000644000175000017500000000153113222767271015141 0ustar micomicoPNG  IHDRw=sBIT|dIDATHKo@GICE( !R@B\=8s#**>@H $E@%WK]bIp]Hz󛙝YX́K:w2{m*kmo|έ~1ەBLqԠk3s`K:wo/cM aqI3v~R} }IO~<s,s/#`Ї{DteNXsJ %]":XK(#y7gTpΑNh({%{ :X.!Py@ܫ#Z\WQV\DG|Ny+BvI*;RۗJ%ٹ_R92s-ztaE;';q $Ѹc׺)-UɭZ3#mnA/)0#(I4185ھQȭ[ "7oY_/n/ ]~R/1QRIENDB`fotoxx-18.01.1/images/KB-shortcuts2.jpg0000644000175000017500000022605213222767271016252 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 509 588 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj\oeS$֚݀aO!<2FQN8kֿhC~uiPYAc᯳<..Ĝw௎e&t+=95) y D$Ac.H#8i_w˹T=R}'?¾|2u[_jFG}+bVT+0U iZ>⯉pݥu6q ea,%(G;yv\G! >}^w} A–j[iė6\?AviP'!28每~0/bm,.-|9,0-ڸ|l¤v^p5˭~yl>|?j~_j^1/'^ Xbt2y4$ |/>$Ygؗ6"I%5HnF[;]ɞh/Iȣ?<;|O[I1.j]]_]۩{%}G+6g{qo$rA$R4mB [dt8~Rjl>|?z>El}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DRCuqwt-l$& Rz">^ZV2o5 '( vB9[۔,&(eIj95,$s#-2Mw#-OjVwVۢD$I'@0ܐT OP{?Y_'V˶*C0#%( ަ@./c- v*NqPɨQcXdU^:ƌj\[_%k D:9UeiV)R+ԍ R-ub!M/+Wݟdfi>oA]#aVKTpAE]7??}hIwYCG>{|,ĿiYﴟZ>}k}#YCG> i>};g %OG}#f??}hIw8>>AѵKYUžmlDZ팘E(2oz<5]gګjz&tApG!u`2285W;g %OG}#|EP>h:D7dko(K^6 vZ/iN%̂V'qѝ`ٯ ?h/=o4x?{viLO޼tIe3%`̧t#8|I(:̯֤$QHeA`8vW?o4x?wrϊ5/5z_F9--ZAVVPO'78+&izNjnP˛Kк}bnɇ ఋ~(]/DN6D#DE?/Nqy]st9[6vߌ$q*=)?h/:.6&k[Fe=:/"uFʼR95';g %OG}#mW:ח<,IO5O~ý_z? ?h/=.[~}_?o4x?wYCG>{|,ĿiYﴟZ>}k}#YCG> i>};g %OG}#f??}kGFEֈ (ԱapO\{=w ]nm?D\$mH֪ϧ]wo1KdVS,zG}YCG>{|,Ŀi2qe¤Nj|`I,\J5-jYepɁjO'¨UDE6hù>{şkar6Ntaf_{xӍ,UKtZvOkA]#^kH ^2ҬFkl+.K`H$ds^IoHOzY쮡1K ԯ<CU+5rno¦_ GQ *]erBi¦_ GWYErBi¦_ GWYErBi¦_ GWYErBi¦_ GWYErBi¦_ GWYEx߆4M^3B5"(4G8L?=/Ek%ܱiVHe"- g H/o?i/mOxm+i^ 2(faIV`ڗK?gZneu彽4![o i⦱SHMo>0Pʸ'Qt,-BG[sZ Nk ϹDWF 2)QLaEPEPEPEPi?;14TM-&#?#EKK?'~1x{ƟͫkVs vԙ՟kmpyN2<{IxoŸ<+.FGwzdzGu$fhU][ ^+/ůxto/tigDl,HS*1'o+:60\bW]{//|6|E6eCvvګedH%X .=7-?dEsBߨ`KpX$~̶A'9cҧW$?zY> j^uCźvh[Aq40{LW8]^Ei~3}s7"ӮmbfKKJD$%#W2f qly+,^+Լ9a}Sq{o /vh6lq~jsYԭĺG fxR>p{-v $%~ Ye~!IX\x3W{ %eѡ[HnZfLsU~V?/]^h<)㿸Ie*RtM 9fվ9]tsyc5̶s []~G߁~"lo<9y𥦅隟ݦfb1h䍧=iSݗg`| ~ڣ<;AoXxG5[ -g$-cp_S ^Ե/ZuƋh~ӧdݡiU-"lRA;zo~Bp_i65gGg@|8kƞ퍿 ѭE6>fQcD+:G#H%~hYH|7)-E \v:M"ݕn-"ee 0#c|9-7 ^EWŭ'),l2=_A3=-X|e2iCGqsȂ!.70I 5 xWڜOIےb:L@oSw. Ӿ.ϨuB !v+ђ!ɈF֯{.%ݝw_hV=x M|g xou>8zx*mp9i!PVb_hNq?&xB>MoRԣ.*;q߹Y@b8!W6V),eӴ2_[~gofJi,n0sN:_W WMm>Ǣ)(((M'GF/&TIwbhuO<a Z⏏Po㯇~d;_>W;VG/D;T~a]O#DMt*9VynRq#VN $ҼO߳?./,'a[>ɣ m3׮MxBĺWºaj6\-û2FSz犎7. _6wuh1+j>It8fKHn2D ͎+coħ=Ğ3M/h֭tl>#*k`Z!x&xY,4ycKOM٠q lHġ 2̠ 8^k>*iyxGFĺhZguAe[ 1mռ  GG:ej^ĪlK:;ْG&[p0fw.^vax;E5[JzŦIE$E-%\omgy/@`|kY# Ao=3 2H>r:d`r3IyK?s&yohU DrG9\I~/|Q]'2݀kW|؅`~zr?Uk+nKv?ϡ:-7:ծ&=啦yqf!ȈUÒ |^O=mZ$OIL7"H h G_k{):l]NI$@ˌ$Vzyƕ௉*k6u[2^x.&D*.8Sygt]Bgizgi-m\i&45I漲K8ث$Bc̄; 0[_7n>!iJeYS|l(^rƾy{?M5 ?Ŀ t{1uEw7O(41$R$0 z?~oG▸Ч{a^ \{Da..eErJ}` WKwzÞER(((((+w~?_.Zϟ> Ƿ&+⿂?ɫJRv (3wM[CfLSynj$Xc2\w|Mayw66Unx%W Ec;'wVZNo|ck; !'MX'/%6ɕ%Y}z_-kռA?I|q}&KܒEpWR͛B9cg dzV֍nᅣk $qWfo>;|BOw1Dӥ!AA5> .-ȳUGՍΑynM'bs%E#[l)ۃwߍە[{wM #Au-B^OӮˍ99nxYˉ e;v>'i59ȯltӵ ?R0\\*#t$)"0 A@ş4cUI'|Mch[{I-32A eڤවG6~2oW:CkmD $ \<io7_x⬾ j6v֋{m|պ1B2Oδ3 W ^HxS2i`څvH|wX:rjZO@\q$2[ܡ*rM*8$Y@f+xj{ B&յ[Ykb#eى7d!}׻s%}m]Ηߴg뗚nNH/t'#=ċ rqwi5/;gÍoƒ5L6$J/ex.E8oސgi0Wֳo |1Hmf#uCKcm |G`N~bWb{Y'WҿvݥSC{sH^)0x :K Mb^oElQKEQEQEQEQEy&#?#EKKi?;14TM-@U'Zo]c0i>ԭtzF:í-@}d%0cnr=?"|/yK]BJxPNrbnבxlu}|K ?YM]MZmS?$'ϕX}b[^%Sslg?׾'c(+<|[q2oWkzŸJ4/5EM.3[մLX  r WO:?@Mq-\ClѬ^QhCom$dV~+7Mo 8@hMwsT'HnϽdT;?y7 s~\^[VZki=2Όc4~LlQ}T++m/[ '$ H #$El`|=Wx_05=%k8!uK}! B5wNog3v~%xCj?k}мq}fL/NL 2{!/ S_m}+٥i/y/ k:ĚQ4IQZݵŀoEb9[Eu#Ե*ӓGQ_6-5dqkty1ٮ>\V⿇^=yYu_mҼ@-w{(_%7ĿihnM{rɔG4>PTP]%,x\=iyTsFkZe:vfmHb)'r VA?fVAkZ7|5kfg#'o%Z߀p A?"U|B.r cEHl{xjR+)*ԥ!G`*J uOk xzc=Xs;*q `k ^x[[[6 |0$4,0O̧ CčOEwu?N[Y'۱Rd)'kgiRdg*1ϭW&1^/|Ig!їK?-`I~t+0sզ䠢) ((((+柈_߄-|AZ-ό[eE:Oi*mvrWsxwƏ5o= ozuGM qyg^z_i!'([c/~5ݕΏ[om|(dy=A! k?nW\u=fz[}kEh8\1@޽+p4L:&~6MsZK$ p<.Yd50r>|&,@J1yz `9R +7jvQ/_֍v_y|/ cTF|i溆g6W^C.%i;̠6x:)o~kmhZږ[k71}gl QI7Gª;qu|y?~|Wal5+RfFd2ZG4abQ8s0м-}{w:LjѦHyUeodko7KmzĖMv\\,oGJyR $acrp5ῄ/BUƿiwOG^([4㕮C$#S!Xwo(=Xt_ iOqYmmjp_.Y2L[oVf>/_D})EP0((((4qa*]SGB_&II?x?%ij_~4_ZsǢKm4.PY&hƲg7|^vMԯO{Y][YF9S35Ʀ_Y[xgzņwaM 7iCXXlc+/yi=/U6moo 'V2]7@m/K+eOKË&KydV5-¥̍$Ҝ4JSk.0[?_3w<-? YCዽwCe6r[yEe.pF2 G]oGq}CGst8툎\H4-@w`|>O22hռ6EI8]0b w uhjt-cBcauq-{ ;(Vhe +_KCX෻vmwIw7+m ,AqZ54=/EkH"1g c#g{\Hvd Z|j *O_7Z̶wk{Wp#:2ʸM@=lhZY41i."J !~M~lG?ZyX,^ =ȉ+Gr @Wo-t۩紒 5󕷶FUW#kU1x mմMzž6Oiºm"R ]it"rj5a>k>{PL&^ݥa" Rɑĵ_ǖZ^ף=+?|yӵtCH7V,:m,zCowx+Qo5 Gi֑۬qs,IpGuUD8NUcQ]3k2k捬I{,mhI5L K#HKnlqV>65 oPkM,^]}bhITæM@Wq8ۯ|S7--mq>PKgml12r|sgh>u-;ºk }`Vh<2Uv,V98k}+i[IˢKEWru4ȱH2G̒珌AM 烮R7$yk|3~iu5@QEQEQEQEQE/-sE^▱ŷcxfK嵳DEkkw nxg _u_5 jVi񭅅mj13<!.ǩ;yKۏOVZ j(Ru8>ЖֈK,jJ :ƫqcO_jڮkm5ՒLs-{iYd]RKM6.k *ڥn"3%P^v\!apH5-?c.~2薺iqlmtĝ&i߯5W)Z_v1> zO]׀t}+WXNxܲ|W/m'1˵@  {~0xwv[<_}`#j8nEDF* !=857?lC]w ɼ54)~x\?5_]hzľ(SY!vr2>e8kY[8K_ևϾ e׉-j~#BxLX%Y |P-= Yִ% edC)#hwkTmm~[({tp2K+F>R";  n>ǚ|6@qN?Zu;*QEQEQEQEQEQEy&#?#EKKi?;14TM-@:A>!xko,l2`񧋼GOwO|Ejl [{-I#DkG1L)7V"M'N4*6b.dgiB%t>drW<.EKE?2O^]/UIm'G[CAϧ@4'XF'Rn/$aԿ%>9JOxz·΍k]B-ZnFL$aV<}M5>>#^7g7:^'$l98{ |><|Qy} GKBlyUJ֡n_}vMJW uǃGᱠk:"T>.Ь}w`ƞ"֕Mw6nz -V!*F,dBU] Nׄ~xKǺNJkKHӴy#U(ɸ;ԟ>|]ݦuy_ijfyi+.pYe!H5~/)^xƿ 5 "{/\^Nj<Vxl2Rk>yoO_چm%fmda Cd X-vWT|g]Ki^U*ny؄,UNx|/,iL}3/t7)WΚF Ls޿/5wv^.6>>g5{fX֟ҽ[K>w5u"8yXA1H.'o exx@4(t *%\"2[CO !8|/cԢE0|;ԣE,䐯8zF:7-co [뗐_6Smy4IaI Kn޴ӛ]/~;z6x_/xgx^{(u}Gmh%[bmd;< CeZscĿ~ xK. EkMrIӐ[!?B>d<!E|! Ǟ*Sœ-;VX('q8=1YK Q'xv~"<;cIM6 v%GL:} ᷉IXjzֱZZU %H 7<{=2x3\_ImN9V6eeo&,P:1= l3+H=KzB?*hnV+Ikcuh|Ҿ9xk_4/Ub{js) W#|Go\[Is5.cۆ~|D>xzׅ%C5[s*m/+m$ HӰՓd_k|B7Zֵw|ֺj(G4y\O~o6iS&T%(9E23sV"QH((((hC/Z|+xƞ tu;[hT%Y$(n˹U!|j~+c׵%;jVbTb4 U e+aѣȯ>0D|Q})[x9;Ienckve? 63/''"@O5-7ďuI sZׇmx[?iy\S{A6 ׶7O]Y;kR'̰9gD8H'ʂ!ecxūoNWHu+mRx20J[twsRpj_MVRO<u>'xƞچ6%IgeS+Ϥ~2Bdў }_ ]sW[ѭ|C\מwndfLg?,:EW")?S̚=߇|IsvvVmsFק>=+|7[h5ɮ6ZؖӶZu(gMY1*4XZiiC׈5vV%1KpO`\w>{]:cwߤ2?5Qb́W~v/ckcn|3?Ee&ӵrO; qX~!|XмG}o"žz,N+:,kL<[_ss>x⇇5~_|yӴ a[Lv4$/f p=ŏt^׌WWm5- a.Pg?֊_O=:OWoS[g<=e ;Gmt餋L3FI%k>7c]T] ;_[if]/eyЌf6ݷ[֡Am!DgIB:1~ ; yeĚZ tfcc:Zvݿ3hQY4 iͨkz`B 3( py +>%CI}UY1-q—wa#)5PEPi?;14TM-&#?#EKKŭO?$qxMt/hh.ROxL6$ʲ4<ʂ}~8㖙gi9~^(O-QL1a]gH }W-&\]Wwqɫ^} DSn;v.1b{ωm.S: z^-" ĝOsNn}7WI7v5<|E-|=x"Rl4K]HA7jڄ6/uy{{mnėfV-nXB! -A!d8=@5_~|ut_]XsAaokq*I75o\kZG^'EnmKʫ9*ӷ_|PlR?Wo>='ǶŞӣ 2mZBF&1Nz?ixG^Yvz& Еaբ7]$x F ^ l~K.k_FԦNs=QHͪa%~QO5|:{^W"-RF!hPʢ@ "ۙ5{WFvvwߍ7|RuC]Dk6֦M$ەW-{u_w~}nP}3?K[(QZ9tQv0EEi(]_Tңn4G#s(䞂h((( %'P;?oޓ9M@Z{I*@Z{I* *FxOkc/e|-P{+ʌAuh/|A6^5|2]í,f2>^)aoL7jx{6Ku+ܴ'ZM}@*X,U./Ï y|= ?ڷZm^_YԠV ǣZHJz?)+zχ/E}ޥs7!2bw\rۀ3^ouOǾMc[N֣|v7#:+a2A<w -÷B-L r#%@A/AD*Wܚta-vTe|y<kw6xM)iK`āq\|2kOK GWԴ QYJi(Pd**>(xw$p|Bt8D_,Ff G wӾx7^&}3IRbGi~O0OvFEVʲAM;Ӵ]O/xjPYH/幎@H2I#$>Mz O-|'nbtܢ9;<Zw~5 ij:,zFIj(Y|:)Eo뭿?ַ>O; =ƃmWQ2id-mِ$kR [nj> Y|]`:oln6j%'{~ Ѫ*j6M K )$CȭY|ɼaZ-\۩K| 1fY>?י ·QHԮu85em-ޟeOsuxb*1bă^5|zk[;O]xçZ,ZeQ-YL#߆4v/5g}=SBGb 9gs'ֺo }WS=D5Ee衒1ePXM2i_/ U; xd.լlRꓶԺwxR>N=+e3[nˍqAVHZB1 W3\_R_<9]-ZtP DV]CsQ>Z~rk/\y(N#y0 O}v-^Asūϊ4^YYxF+9h-d/Ioya =+jW=cJ>9v2Gո' ?xg9[>oe VyE)( %~ڄK/]~I~-mĺ֫e[_tUantveڣȒ|x>;߁?1_L}j`B]_mݿg˻9+R ;M'GF/&TIwbhuO<a Zg|:l.T0Ѵ?thlċ!(Ǐɧz[Ҵٺ:da$vM^TW'j9_+_']wo?A/,Gvt}6k[xv4FOUP7`$֋koޞDy^7@2.9'i&}^vq^K[_ ÿ-tdvMV 4>E0gxQ񆳤j!֠NNHm F.c؀p*ߋ@o+|mwš}&KO289B ̌F&=_zzzwa"4-Ï?xWv||/oBi1̇fT#F1;Um/?Ý^7,uXKXVvT۵i$<ǽ^ov6mZ% hßuψ˨ H?bnd$XK 0Tr"q#O 7xVXV__E%Ƞ``WѓξU!Ӵ;mLb׍ v^H^B&U0Wc?9wizwiac<:u+^__Y_~ |5?B4=)E}h 8lgEV,,{Ue1O!WL'-.6CYVz{_M4ӧ|ug+ \/koǹm*h-g7 pEEFc;AX2ZBYG#Ҹk߁?uNQ//c+k4،[n@oq+F--]Š(,(+$I?{x_ď):y7S_^&w_^&(rPO>"DVIube?9I]˿nFQֺ;O x_׌/4-_Ol_T48X%(A74/E+^'Rb"-ϲu11|N o\Eg}H]woc 8 u‘4~xƟu}C'>-lmZ/5`l$C2,H`-zg ?iuoz޵Alt8HRUYǐ]zl5xuYtuH޽>W#ޜ}ԯ+?6j7$||DVE]FVtrDRѭ r,"\:?d}:2|@YY\k׋tY1pp6VHE!v"[E:_*~̟hq1#=bxS7*ĚhS8,HOjZ __rk6u{M$Y+r(Y O0b_m:;o߯7{)G,2n|Hײ'%|Ni)D'{`Ѓ*GZԿ~h&-TU,T` X=II8~+.3ڏG_~ XYs@UO;wF,>DBBym]6;)k >&IX1 Xن]T0n56}SXh-nZX8G#+=+Ʒq zg ų2~Uy ` egnRyn~*k-Ҿ'x1|J +[?w/XcvZȺyi:ܺUmsnVj#X@w!*܂)#׏&EӤ}s8i#ݍ6dpJ֯־Wi'm x7io_'Kl5{k J+_(2&+ #PA|H'#xž&~jM6FsCEI#T@\ <0ЭᝮbM%f1aK ieŞei=7nq֢"_/şֽr6| n`ԵMbG}bK Y ?cŭ3~ծ^_i7u&vX; 0X1M.}D,M7SQRf9@pcf Hy'O h|vˣڌl n&m! `n H\[k3Qi%}Dsm-2Mh|!ƶnٛC'6I~y09t4@^ƶt[qXMٯ|_>o$2Crm#5y C@ ۜkiѬtOnmaJf-BgXG8ȍdb]^xDNIKMAsnдqH"ǘ(VK'/~GEο+j_4 xD`GHy4fa;@*,zjX.&mLN1=Z/mny&#?#EKKi?;14TM-@ώ:a?w7zK>^[<k306UXo\n^>){}ይ;SH$V+F rr~~~#о&6x?O*]$g*SWڽ #K|ׯx,pֱ bAWOQk_n? xgNFq-pYhZeSڕy`f38w @D1i~%|m[:׊> j.5P~oo>̤XTF >m|#jy=ses|nehfB1}*Egž"ot4kI%.M;0_zuA#f׹g}]wS;io7<'/wG^m/ZOqng(9]hw$dټerůl,o!Y#/ӌ8 G87OJh†OLaM|a>;t" _k!nN_j=U[_> <95tɼkOoS_4mm#wt(*A,IUJ G$gBJIк8)F9~J/?+/ )]o h?7èɍcԦ,m[;z5 gZ DaUTpJ=[G_? +)f{GbVjRm )zx7/$.^=+RHvE$ UK)8ܹg CRPQEQEQEQEQEQEWHםzJRuoI -GM{x -GM{ QE# O#7Hu+HO'Hnѷ'U>=|Bо|;/Is5m$[tdcLG ;(N3Ib8Zx,&ldEFhSޜw}\oï5QBkG]+VU (,((((+[@h~5]xoo+7`eO(>^+~ӟ|+Oú{ei6w|m-UF/$(]g5{ۃCZ= ?U]apqGnAadL#'xv?Z|U@ǚ7^H)d8> ,sUATrߢwΰщec,7) exO+*5}7o'_Oo#GeksۣBf%XE.1Gu=۷> ZiZt[+dKVQ~˟` v`;\',UrDwH@B8N T:\(v?8|a|0G4C>&\xĶ6 k̒$R+U}.Y&_>O _|3k0K-teyh3`죑}7JZRU+G '|K5%Ŗu|"Ufqo?[׸z?o~6~]+wPW6~"鱗7i${"6q4/;Tg? |I?|n>"xXPN4 w܁u( ?Ώ-Yӧ,nhNg5*tGܯ5څ\p_5?~nk}V_Ҽ+oB., QT{X"ew$ W_ ?fki]Z.hSaoxg`\E[s]-4k3 { .o629m[_?>xF|XZDm+SWZ°ݘxGƐe[?T4flFNk<Jx}5t$Wx_Tnym5FsF:RJ=&r|#f.^xkڞ3x̓CG,-H;\0aԴ|->/>'wwc <\1xʪ6b>Mikg?hƫ[d `%$[aՊ@RB|=,NmV,r7fPq zЗ^?>&-.Mg[hntFK-mFh? C"OJ~| ueG-սwZWw%ddd(ݒP }\$D3сi%'{[ko~,'^^+|yI|c|:ݒJ=zLDp.~ѣJdʃ?ijGe5wƁqøIF G y`wV$-uM?C΍xr+ɑ#uɁ#tB1RO:=E/ۛ]V2ɤ^h.a|%Yđ޸+V0񴣓pX~pTz~$I^ֿ+?~x 2Y|F_i:q]"ߔ)h1['<~;XEfojĺv{w% 5@>?KXN+"O$F7|#er0zjď:WZO}e=ݾ<b̠9#irY9R%w?zӧc?fo |qBG>!%s?̭#P'h r>nukk _T1i!kP,7 38?*J[:׻h:k{(S/e 3bG9LWNXa]_Ι^ J:|Y۫k(tTku/p&Y#Q!<~|3K+5xynI:~r!㕠hdt9,2~7Ŀ |H>#+^| #x$UxX0 /q ғNz?ʚχgC!R#ԯen%r7s+_CM?h0d*Hp%#yo,+mj$IH4bhwl;b~Fw0#;]◅ğ-<_2s$7KaH"\}|'O; U?ѮuKrm-.,% 6$}>*]KI~687[/5Ή~ʾ*u_'X{Sy:,*VB5EL\5si>->5'kwIoZ/?Qt65Čv!R˂*:~7#_ }@./Ou;XmHIf#gr'/:6s!Ӓg2,#RG|b+ܿ;?y4>.,@n~ 8[[omchHI.UU9u⶙j'T]*P}YEJc$읦1n(bwr4M(Ƽ0?)Rtz2-~I~^bv~G~̗1]$W&GgU ;4.6f%OQESwwQE!Q@Q@x_ď):y7t %'P;kOױWOױPQR0a>=-5KB#ѭ$Po$qƎĢ󴁷'^Y^ ^_/v:FjWW FI8}'C~~>|nk_Y{Osny&HR՘FYc!}^)uXx_D|K]^ 4tMijaWD(%+ (qʂk>4~ M4E$nbwl "A/W!Y|֊_.o|)k%x&P 3#r6Cdz]xHcX`ӎ}d'ly2R%c=-/(עNxzÞ'E6'sRIv @ϫQuQEQEQEW_xž.&ҬmDxbْE IID&mlk|cEhum|9.bpcKb$FR[?:;-:G]ki}ki.$KR}mcOމvB0b/7zW/Y~#xDKh+ >C ߺq(aӇ+ߐ61sҤ .z$zٰ#ah.A`abI2.Ox╗-!=~v^xŷVq:N_嶡imd[4 0RG5|t .^xIm4`i!"x~.vfy,>о$xKxE >P{l1SFY>ea?4M }ci{PDo7km庳ȗplW g=> VXy p kn B+Y㹶C4.OBqCͿ__%So4O|@arG#\!,2`PpQEK?.QuoCdoZxCEZabnϴmxm(Q1c@dpNXU +QHaEPi?;14TM-&#?#EKK~,-3ƷWw2Я 49+(yXnI3CasSD'=COǏc]WQĚqķҲюhI 7?k/ x[zօx{ֶz>i^tW+AhvyZ+/>|OwOx~VgtuH7z #=jߔDBduS~6ϋ#տCu4"iP)#ʈo,̼}'E]4C7.ėw,n6bldʣ g?S|3[F Gc1jvDs-V2,G #5gǿ'|)/jzFxX͍ރꐽH)E,C @8CKk%K-k+Տ<~E9M×1WKh&XnP d|AC=O7 ,kM/$%[6WqJ:*F]JΈt[lK4C~V,Cs~!׵!YhP,qYo2X]9ޮKWϚ+[%S>) j$"tZS` Pwzlt5I{}ԟ[0c.+O3O^{eϵEsjS mȄ vJ~*j zO[nJ^<"lDdæTu+[?f/O_ltM \߇O >—7?g> W:rʶ"ex@-X|U𽯄n#T_NH8rZ/4a7Smxc{W(wy NpFFoWj_|Vė^/- >ŪO@De[k)?)<VV_K&omYEy'iK}gD,M-Vs4{ՙ8;Aʌ U| JN}2XYYmcb|HӸGvw]Z\<k2>)fU]MR%}* 9RQEQEWHםzJRuoI -GM{x -GM{ QE# xS>|B4T]^}Rk8&}4W *D粹@٫+l>w9!!F k4S^[ gv_/n Ԑʘ#+xp:+CO~4~v#?u;]sFѵhۘີe#lds~/$ ]?t[xL]BA,ZNpekЫKԋ&#?jj:ȏ:ڶcZMӖ8K[y$ G=+# D5K˽ iI¨ I C{)(wOC/|j4O|GZ 7V#_Rސ]̬Vv60 m 1)S.ѼLShnQE ( ( #׊d注|97KZZ=W˵r̀v0'üQSXwľ1>ԵoڅisEkw3ۤa bƒ qݡO w^=Y}2LJ S['y%<xKa-2}&uBSj"0Q=t}'V>ռEax)sĀl` `j[ Ҵ}B_k}cTEE&gO3|v22g xJ?bz#_࿈|o6#ߎ4[:]ޟ5i Hdda3,>bM} UkUA5}KF4djs d/>6x?Im[NΞ֑MJX`m>BF{G~ѿ X֬<]cui>T.pcnYYA ؚ8 $#uO:6^vQٶڽƭ2?J؆X ю º5؛d^xú-PڄG-,iW|z 5EKqiK,*2A wt>MO[.vHfEy̍b܀˹`cJJMn/!E_čo oxNtWN,aFe4$UђTmYq0>9?bVZZkA-8F3` ^ŴEPO}{i̊DHcVW_>_ ]KXIY"< ?/8.˯~-{iExt|Ua%bJm9G#ct ,HCA ,|w'=~'U]61[KYU!vd0?~ҼMSZ57ZCQ.'ٰgU%I#=\{r-?z$?~W㗋Q6^Oö0[H0J1ڮ_n~$^/H5WR]_iSZm_3΅!BkY4iYG ,~&0dЎj׏>!m[2X&::co#p$c}'rLkgj|s'6 aO~"jyr8̱;e\m+04K76c*V^RPc!dXV!ҿjyڜ!3xbX,Sq&mvyİ8␤nN85_>ީhikvYnnkxeT.DX H54U4rӳvֽGǾữ6'ývr[=wn3J;%:d_A? nӏ,4uQyv O9!b1V_ƕ;ȼ]$^[ Xba&Pb`cϦv|SE6Wּ]ccy[ f*!S}jH&|w!ջ[Q {_o֫hEGOoEdfUl6/4knDdd"9kAx]cz]Gj~hvP9+$  jo|S}8A߽]\vr D 1'nFC 䢥U_K囗kOW쉯x>Wmþ!]J=:x qlb*ᶗ~~WQ@~:u hO1#$;$U[b2% }0\G.y| qxz^X]?LXpbᤅQ<:ן|0]kAYhگ#mSc%v+(#.SnʭY0m l<-{˦Z=ݵԄ[zbݐ/`Wi1~Ϟ'o|H4 ɦk^K[2F rNNE}eE0i5o|hO 'uS$-4$4O)O5²0H AEK\WQ@Š(/GC׺WHםzLL5j?'kثuj?'kب`(W|NGrDžm/jPG7 {Nٔ+ s^\Mĉ> Xտ^ɧ|YFk`@ 3;o-}7,|'>m=i }EbfYyNdϙ9 nAf~Akei2FI4I#F) 3VkG} (AEPEPEPEP_ x{Džqlu-63=IcEw IΪCMcҷ/|M{Ngᄁ5mS^ixĐ@^&Y%1s"2I!RvUg~=91'TR}xJ/oI4mZ[g]^đkW7 Wz,26׏g1 SEj63 s4#;X'G/[c,Kt8M¸2+\4)ardq$Y/{~|g^g5ax3XԾ 軸QH%Td@-–5u /xJu{Mv¶hLn^+ }B!xGs jN=[pmFB$Mrʎ>^ǧq'&ZjD7d5k`,@ ȧ'nw|_E7ʿ菚g[?xDu RE$y%2¹MvQ|)- krKSU֊ rۓH2` >5xWW:[ܴ2-ڇ#/XwE$z.Ѽ=>%EԮSodޕs*^ )'Mao+?e|UC-eop\_tCo|@/^ǧ*i~3iwmiudb@|aojYKBsk:)gchwWo3* Ŕm$ko jnz޹xCӴDQ36`Sc9r7Z:xSú?$uA3EiAHO-ʡȡav5%Uot4 o^ͧ|]}wg[} l!$ A s+訔Tk|"qw]ϖ-<;ơ֟CJ:}jp$HxqwHq֖Ox_KxZIѣ`̫ms,S0%`U!.IG>gׇ[+7~kk> :_-lu ME72yn W|Ҽc+}xoS]w dcĨ }ESwm%EE$hQE"($wbhuO<a ZM'GF/&TI!+O?j? xMtO0[,(s8ʑAv7Z(}gS5;4ͪϪ?ӟH;ml<䐫9~z~$n ?AX5Hf:"߼ox?|[MjH:ROeY|Y9QUL1vQqEI~=ɚO?kg3B?HS xPѷet{rB\MIap<foH.lqZkA\l8hHs.Ҁ wL|we{x;t;/ LӴB9c>>Fխ;-6Imw:sf 5"TS,`$ .[t_$ w{W݈|>׼;~ ͎]MΉ5NUEcq~|6ռWhz ZnMN 7o{ĕ[oFß /.7ao^$0)-~Rۆpw :+.-Yuoz[Ij{9Ϗ8$|<][zogRӠKWyHFL ,c$Ҽ3^hUMŞ?׈_s[,aY k@LًPSc1,1Ӿ9>|?1h7Q6nquaicB 6]ğ>_(/:ظUn&`LS,F8`3/LQ 4z͞mq'}ŝKIJM}MNT>3t3_+Y,6ྶNo-!*r:gEU+~_j*QE"(((((/GC׺WHםzLL5j?'kثuj?'kب`(WcuK85MnP]BGf( 23蕃Xiz]w7%D1U&n0rqwLt/ ţkMp-KrA ?t oȣ5jm/Ʒ 7m2Td:!A~8 9V>>oAighQQ76z~̕Vx."vc7cG6܁DZ!E4Wog!7ğ |DcyE$< 2_yg)7cSݫտh߇ş2v%:`PKf(.Hy q? ?l |YVi^5 =GH7w0pep35r[ߵF;B~#^+WEYB1u!pFA\43%I,l7Pr<*ͨˠ(Š(((($wbhuO<a ZM'GF/&TI|}.~=ޡea8Eim6+eV1lU.@L.< 3}t]GZe_-׶ H]$M–<__<%%(<143&w.}gJ e?/xREǿhou-߆>im`Mk"3 d11~4¿|;YM}[PlH_'lo6/m ?hK}#>+ҤV!Kx'iT},ouFBH8=ۿ"y~ ᷌O>2BޕZhry_d7gi!;ў8t_)O!?-.ͦj[CrIeݐp}EzMXQ||G4O yVR'wj2+>/mldV$)g"Ӯ$פ[_QKk9/ BD1 1#(m]أ.Zo #?t \i25ȽiHDh!ed-21~ɚkBRWiyn"D6.K#HA?ZQMi.~o(_o E:(QEQEQEQEQEQEWHםzJRuoI -GM{x -GM{ QE# wє5? >6^j6zY[HK'XG;w4^Jo?W5+i.&{;U 7$zנC=6fhn!RJ 29@2r6ZF)P) ((((+uR_~)mm ϨMwi,,\nXr1WW??hm'D :^X^_ Ot/~ -"5b8Xvl' 0(KJ=7dg>g㉵ y5;O8Ydqfw,Cn3q#?=ŷ#𧗭FG-fMPӂ`p[pxo¿irk%̞!)uC"XI lRi[⦹Z hPFn L uO λ7`n&-G]_?#⏃ni5E5-ha^kJV ql`۪˶WX]ŮkQlwrV\H 1f5ԚD &z|=,nu8l;$P ᑗg8#mt:Io.ukZx*/O=ؚM@sd9 xUyz~u"HZԭ٫V׼Cj^=Ɲص0ꖌOH$H'je b#H<[awO3[Eeo,Ba-̥jɐK1sl5[xC=?Znnrv(*૝[:RЛZ燵t]GM7bRT9UP^9q#vRWKe>E7o?>.?=~E}oqoxj[9w˖^Ls"Dyf (_;xJo;i52Y-niqjwB#V?|#:_3,_7:oy_}Zl"#zwiגp6FH_|qg߳xfF-FN+_1Z$em&؁ι]ռۿ-}>_}zd;@ּ[ x_/Tk[Xk7\.<}7knAGUE +/ޣҼqzHj+0MRr/<рvh~<[]uybD2D%;.NWKMK/mXZxKY/;mݨ\yB>㒠>B]g`((((4qa*]SGB_&II?x?%ij|y.$fsŚƝ]Dl^Qo_8*n2K>w?4мAãizf64Nd]p>eqt>~JŸ[i~"JFEzO7kneߩءp%fUv,p E{gØuM ]g\Nխ5[;)'|pWlcPc8mZ7/+k_|)^_G|'kNMGe%^3 PUݑ? o~|;ڎo^-641qs$B31|ͽNq3F^~}_ٯ;JeYͪwMsަ;?om|g̚ʼqo#-^&xʇ,9;6ር{/o;ExaX]^4]F+ u6?mزE d$9W$9yiYxC%] đhrg{d3UؠRaN_? o??}aE|~x|5ၯ:ƉgеnoYL *w ْ@'.Sasꐏ1Nu P^Zer|caP|mn>qX fc;x[4{eZw;-"oAA?+ݦdz>27dݒ;vƶQj~& xZI mT0k(uR/ ';xT>5+hy#W!$GNQyS׌,xӶG/>; 6iڋ`߻'|<-oQ>#jQD PuP sxm C}$ͯvRQE ((((+PL+ܠ`_87q>**>Kk\jVPT[  aI;7 Y>VxC@КXvm L/|?wƋegtdV~iX**cIĶz#8ԭbI2v!AQ2sxfH-k:# Guo/*$ӳǼQEP>jCkQk6$,uv>:ēhjc%<`֬_w˥ޡZkڏ͜&;eB< f"Uz2۾y~ w<+~񶛩wҵi%EDP E-w ( ( ( (FH9 |3? < N-]=ē0 !WxS?bڿloPOjV:~($3:Dd7`QRy Uw:Ai<u;5iN}6&bw6-._MϿ 喼kKԒnZ309(O@My{uv/n|h jiЛf I-cڂ# ۸?DI6&4/:j&iE䌺d,ȩf.&i/xʼn=OM^K}4uootg<#&)q1ySzL~ΣKѴ !{8KH<2Ey &׋?eh~%ɦ?|M/ RЮKEJ&]#[6 66?BN51>ۛlƓq,i2fYcBYj-)j+8_$jr{kTnBʕ1BQ3+Ai=zJ붟/[S~?ey_S7 w6lZ} r$``>>.S[Ek' ddLaHmCIwFdLJo^x=*inWl]1=LSᗆ3wo.|c#Cя(Ȳ./?:VuӴ)_칉>PT=6V/X՞s#ڞin.,tO"Xq$}v/ٓ $u ~ j_ xnj'V :[m<6rH? *Y^ַ6.(EPEPEPEP^#JN^v+$I?&&rA5UA5T0AET+@&3់1j:RyLR% 9VV5k<kIRWfʑK8n*9]]pqITwGiU׵)WRN[HbQqƈl* NID<y=sE:oUi~"'RK#-9dRbM% mɯJ?м1 wOVMsgu1߀ga9݌U}}uu}7E|+ռc~J5 -Qٰ/ǚ3Y ~RO!u}r= ^W⮕en_T K%e n011|Q]m⟺&Cz(Q@Q@xO{uoQյ6_-sK/+MU 9ELl+1FB@5G?HFM>ksyHOp`; 5AEٟúKA5?xxKpxb_C"gy Ns?ÚWt/M#X4Źӭn.eھGU6C%OuJ|F~*k~Y5 >#e?M$\}_ʌ C)|v"x`x]t+)Rm-/<+ |$Ս6N[WJ]wOxNխu다YϧS[6HHd Ҽsu'__WD9慢+I+AeGSniBio@*J ( ( ( ( ( ( (6Ҡ$Mp9fe1v`⟋_|^Y0$xg;C_|+.Ii]^#DVK<#6ܲaN낼TiЙ5v?ԷG~l5Mv[].;]2'ן9 3*y)*)"~Ǟ 4=+K) ;@ml$#o3{.Ur">'ul&巕V-K*yr@{ţkk>tK? zvB->;KxD*O/0ot]N{[ 6WR$U+ 6UNT#%}[ȍIy E|sU&}fj>9FׅEow%OfUu#s0'>$k}OW|+;º e\5Ʋy ²pqFl.BM5+[M6+{:i:͏F{q]CJX%Dt)=ibq"e*G9J^7־x~3úMsLIR# 4̮71%vm~!}ϸ(&.>!ů]jKE\+RK)SzW(((((+$I?{x_ď):y7S_^&w_^&(p/ j ӼEtOiNe2l%^ Y;|V>7𾫦VRjEa$U$r$8?: kGMAmg湣xxi^Ii2Hف#5[h&R/6#m'rwO uoM}F 7+`@5 cxu 'LӴ=Cᓣh!ԯ҇xb8"RI߁ sFoUtZN4"GBCqg4>Qrr*!>&1뚗'^e?m4ha34v$((;KO2:nYzƙoI-̎d]bLNG9rzk@C!Ӵ=NV!.4eb%Lk`Nrs9R\~&i^ILKY+F D|;k_ϊ4pWQꯦ axd; 5u};W(mOvV]&CFe(?."/{T(x9E!=+?<9i\A 6Oh\A*| 9"ҳŬvfYsQUGr89W8WCZ𯊼_xrDkx4tIdg&o)@pC YzVoOg2OZ à 6_>Y<$Fq#}vC;^#̓\KoWz`u?ObԎ5׆ N;G -lNO5h |9i^+me=֣uy,d PؘЅ"aWޙ2^iq]x>.j~xM'Mg"Ca NF9 >otv]K_gAv 0FXP\+THգoiZo=cyˏ>/Ӽ_óxr:Ak&n+ax.VLn.5DmF}^W̰W.̀m ';7zm|u'ZY.k Vs!B`Ewجۈv•_wMj?ڗ3w7z㽊ዬYLGlːDarTDen5zDž~ ׼OivCKĿԞK$YfX%eVbDP+^EQ@Q@Q@Q@x_ď):y7t %'P;kOױWOױPQR03I};HK[6Wn:=۟2.WgN? ]oW2*((((((((((((((((M'GF/&TIwbhuO<a Z5}$U̫$p`5oS!'Gt;moi^5յXoP2W d O?<_ZxVz+-s{dƩ{ytH$9.3->.|Ey; Y|[RxV:_-P*bW~Ovrc%'MT6zE'h~Ʊk~,O VKFcye.w;GUaEz/ZxRp҄q(L #֡tiW:M~%MZWiwKA FӴ-Cpk}Ѷѯn,?#Gݭ[Fv1 &dBv:|2;/HHoB]mln}Mdٓc:vҦdX˄I%(rzτ>Y|:6<y\G(./RJ]ҬO(lAu7W>m+O^&%Ϋx=*Ԧ)eTX۴(Q_v_{~*~挾]2E÷]:V+fQC0c&_į&vvvLuIVՕyqGc$/=k/g~!U[:_}_tb=B72ܥ\,kRr9>&ktg4 SW,gđM[[si)LdtY<;U\,+{D c /{Z[Ѽi婝/ X$3(9^kҐ[8KE;>~=(Y|>Nk ot_Az fvyb.BRz_ςOq?<3coЯX#=vQU}V(C ( ( ( ( Ruo^^#JN^v13״ b״ b (aN? ]oW2N? ]oW2*((((((((((((((((M'GF/&TIwbhuO<a Z4#II[-[~);BWzW|࿏->$i[ZH/%`ۄs%<barh_xvMVɋIk,@!tKn .+z'v]ϥ~\0Q߆aee闓2pG$p=k'[/iVw6GM (,ۡ |' 7muk;_]GwiW3/ l@1Jg85#="v{ν /\qpW!?@;S%~U"y+^.}|TU3|3/<45it6r FI랦/o/fY-c9U!Z1q3`@'ּ+e|/'׵{ƃإwrHȌ,|_9,M[\$&ު`H6/|I o ]^kki)VO=s lyMa^[;>K] 7~QH@4%Q@Q@Q@Q@Q@Q@x_ď):y7t %'P;kOױWOױPQR0|.aM|.aME@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEy&#?#EKKi?;14TM-@:~7%(+Y "GҘJv'8<&!TR M|͢x)$V]i\ybl6ȑ 3/48#o|Yo4۟&M !WUC)sr4__F'ʛMm<>]f⌾!?~Я]ФTE,!䎽+W^(Ѿ*|pڝ5RAm:skuHw%ʡ2NYezOh#_-_k~j]ժ^&DhĢe/v*%\2/wz5;=.M&;@yFb6nߕ^wcZ_H|) (&jZ喔DdV$2y1y@XcĚ__xGfO9oŦin.IX+8ʙ*ˁNsrIs[U&޷q]i6쏤*YUeq^#ԧk{=3@YfuC#`;*æ:ߌ:&.$ 7Z`@QON]?[#߃,OekךibmYX|^;Gm,.t+7Z4 6n%'x*m~LeuAx 5ȗZ-љ̌>@6M}A_迤}ES((((((+$I?{x_ď):y7S_^&w_^&(s~ mSIi1@z f!T N- jw ]R++nf]G}*=@ zohuB^_Cyg5-[W_%뿝 zoidIIϗ0 8=*1Z5XM%6A'8? zohuB^ښ.?𶮿K;;G-Qp!uB^?m]ЗvvomKE&G8.ąb@$cSp!uB^?m]ЗvvKb(his>98-[W_%뿝 zoi]w1ZZ¥wf<jdu+  .𶮿K;;G-7fO wsz=- jw52YRIcFYM𶮿K;;G-;Xeet9Vu5[W_%뿝 zojj(ں/]𶮿K;;Lԭ58!Q Ux>Ƭp!uB^?m]Зvvt7DUVIKHFUO ^*[W_%뿝 zoh͹;x|rN55[W_%뿝 zojj(ں/]𶮿K;;SQE1%}JOR9.aN;Zx—w\`1EʬI(+Oo,/9Sc8L?KksX_-;Ulg1eUj) Ou闳ʷy# Iu`q*`5ZMa|>MÓcAEx&nHO[~pep\Al?jt߲6Wg(Oq62}>&X}[ω{ϲOEa !-꿘BJmA[ sT?lUYm&[ 3RZMh6!qU^.XYM #ܛBW`FeN@~&nHO[~pep\A.l?jif.l<l"ei4W dTQp,]M]e}c-a !-꿁OUYoel/9Sc8L?|M6a~,g)Qp,ZM1l/Cc;Ĕʒl Olcj (6pD}K+ `~&ٳ˛+3Qp'D4W dWx/,紖դ;F .k:N6i7PIS׬EQE V;oڔw~!NkhimZl47Fi2K϶58m?HW!sw,>'l 2,Ph` M\}x? >;דY]gLm>61GieGN"`ʼnPb@~5Dž׭tۘmhX{iO=ᇅlt ht;U`Lx(J|=ӴNMQ["uS qV4#㛟|67\Ƴmȕ+bduꩼGprO9|c߉ +?C\+wu 94u YO51꺖k%hwI Tv4= un4׺Kַ0$]ʬ(vO]w$CǴ_o>)x!omhDEylZFUov(O$.)oyg7^񖳩Zj7vɧAmd`%6bdr[Fp)_'vM~fl-?v6G>ҭuk/iVkk|@O ?n?V?3٧kw1|A~_3=lZ݈#SW$=ɮKT-31ԝGki>{kiE1p&yy`bE |Ek66c=ﳽA-\5OQ-=ZNqsG6 c _b׌OxXυ[ sT.gm4o A cOM>qf'sa@'#sÿ{L 4IK^8lńe iV9 }W7~ GmW7jR Z.O!(Suiڗ2/" -Wd():[~󞕭]/|p/OCmk=}`rwl,ի'ŏh Oxb_x[P->H=>K͹ƅV6$96Km?BaRE$ v8v_~ji &X%1[*J1"q[' c47tIYq_xMusi@}o1Oނqw>/]=n?*PeCٚ5$~fxu&V&$zx,B@<itFkck%4Q~Bѥ_e((((((((((((((((((̖ʦ;I8"Zu@k9ZTPo?ڷ_㕥EfjQu@k9ZTPo?ڷ_㕥EfjQu@k9ZTPo?ڷ_㕥EfjQu@k9ZTPo?ڷ_㕥EfjQu@k9ZTPo?ڷ_㕥EfjQu@k9ZTPo?ڷ_㕥EfjQu@k9ZTPo?ڷ_㕥EfjQu@k9ZTPo?ڷ_㕥EfjQu@k9ZTPo?ڷ_㕥EfjQu@k9ZTPo?ڷ_㕥EfjQu@k9ZTPo?ڷ_[XO9&Tc b8bXڼI@50,jQu@k9UOh4W?_F ?ڷ_jRR\̌WSUe5 ۸4;;C34?9E|(X#k'h?ڷ_[^$fIx? %ju@k9G?VW?_F^$fI,jQu@k9TncJPӴL=H:VAj-m#%Y3̓=:Pu@k9G?VW?_F^$fI,jQu@k9UOh4W?_F ?ڷ_jTvjQi5U% /o֡[/m+Ke"@Mw<Οoǵ/v&_j'Qx!Cm9?$?f `XGq=ռrNAV 1vxkௌ G~n/XNV[-DpA'J_ҼQ[xzVZ+ Z%x'%u}'%Ii'~zrj>u\i'k)D,J#%IS;C A0T湍jm^Ǻ]Eq}$G$V{8T8\V3+6W,i .:gm{km%78Td#'Ú>˦\蚾a%gvq6p$]T沭~W5%߇4c iYM0PpI*9O@ =6P04 qpN>ZvnV󷡜.oSm=ivV:m-FXXV@$;՛b@y^j-CׇY46|^i v"Qvs\b{T+Fx5,/5OE}y N y.Cvnc[EjRK[zsA6tgK,v>8hfsc]Yg{[ZUVDv,䪲q涟̺?M'GGΝrlnRT9!^$/Q@/!^$/O,?R]]hq}&s*EPI]{5KZ~uOwji9pw$Lp; ' )Haa? ' )H".4ř#͆ 8/uelӥt^$]H%2 `1 r2 j@/!^$/O,?R]O,?RG!^$/Q@5y\sj17 Zsf$~W\~yMykMfp.DGʪ1ݮ ' )Haa? ' )H ' )HC-̒ϳf?ڴ(fotoxx-18.01.1/images/help.png0000644000175000017500000001314713222767271014573 0ustar micomicoPNG  IHDR11sAIDAThYyWZUUW=9<=>Ɖq8l1 ZR@h(H( !‚D?VZP ʹ6 v߷g|{<{3}wUW^O{$lóTz}z|W[iwcaqn-!g۾\6kqJsMÚ{5g4_]"<_d,b݉c'Ő_l_iLJf*{uꞪ qj[lW/k=FLx&tMנ{t䲔Hذ62Q 6O%NJ O?70 : ;O{}Q_\8b $IĮ'XuF\J5_S]m۶x,f,\4 syaG(5?p?N7! %7q,B2D2md9R-,m~&o4]$ u1xƊOL!΢ɞ6mt4@ZiNmF];Ve9321{坭[G @*lNg" 'E"Vm-̆*7`T1Q;YՐ0+`mD 0M,NE-c5ؽꪩ;'X#ϩ6[98 꾰lRTKn9$>CΑ{|O^“ :^r@ ;<;9Ӑetlc oN g5wyѬi>5Ӆ=RTR$cQ/RyH$b^b:iz 偤J+5Hy@(_V,8(蹝 rU+pcT(s^ A$z^ފ}Ep|hMZ&aQ\ q̟ׄH$#̐&X:O9}_E!OzqT~V!<̩aJ4qch#Kt]@ZUM\3@=G}]]ަxr< 7B֑&ɖBj1qr_)9HZ˶r:\A1-oDUe;sB\;UWUg:d}!LRM]Zގ+= H9y﷐T"-z"$DZy_@1 l޴ nj/y#'C{Gpr+'({/A"WhhּV2XJˁ~sO'ܵ𵼬 VcQڑTY1 O187.5Aw< L=d=yD?}zDO 4ÞDf1frͫ/Yamq }ysK18:pN=ݏPF+ЧT7 uἇ^3o 4{hT-t1 R읻 U0~b_*=ga7?ϕ,|z($m~+5N:lд.:c'YQ9,0dS襪[5k%lCgNeOh.DH%顡B@$&=i|A" H8cBl]EgHpH>]-'PZa.b<CA* Dn*:G9" eC9u]m>PM}rtjN"!{!Bl+'<C;I g\M W/EQ7s9LA*rb+uPcLnDeʚI&gb,,χS3 D]{<8DˆR2/%!@&6TN/댇:aA 5ϜQ;#'(C}v(&\ z.\ \ H[>q25;24p҄D.1*2fYY&4L[$o)DNsB&]}[2F>J7]O֗$ щX3h;tJ}OäJP&*kiw*$J:;E?q1Xȭ\ QچpiPkj Y#:*iWI8RVZUTZڪNDݿZqGe Jk܃!y ZG0Lځ֨ م no|Lq//dիc; acVT6`>|߳ SM7{w)5h{Fni=氨URZJҘK6ooeext>cY}܇e;s;#a=Ahk_t17Ӂ NkU>Xϓ*{E$%"dsTdr}}~GWҎDw?ook诪5n[V4)淭9DzNhv[A`ngL[hB%bLU;Yb>8q ;:zUkQ |猏!Io6Nguj[ΕU~^ RQIKXN&%;oS "/:*w\QSS_5U$o=X86#w]D2oM } Pu߽j?rK@$*Hߕfɗ kcM /OwH2߱|V>JtQIǕGwSUEE9"j|r/:&*+w1O5W's7$̶\HIʍ@]BmvyJ 7/P]=~fA֚nSBk&6TSҖ7"ߖ8V!bJID8K.Y\$*`h)~BV%d &]3q;RqL9teT]œ)VRƶnٲ+=rFR'N"55%win6mX_F} ߖjWH4-uT -a#>4ӕ&)S60n]֐ꆬ%.RMBW [G"RDAEnلL0=v%'-L97ؕGDܳyfT8r'kjRD*_"Nn3pa*""4Kc3,嫆 tvcjN6Uh4s#?X#X ~&!^3˓c.D/s @m=Gw @)rz&wSNIb m ۥU~/R=,:+3TT ܗ3`X]77&U~B9_{cs F6JG3i"!|-Jh-BO1KVcIjZ >(٣xͻ8iBeXIfMM*V^(if%HH02310100Fotoxx:paint_transp|NE/iTXtXML:com.adobe.xmp 49 49 0 ]¢`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-18.01.1/images/shift_colors.jpg0000644000175000017500000003266713222767271016345 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230+0100Fotoxx:resize|tonemap| Fotoxx:paint_clone| 4http://ns.adobe.com/xap/1.0/ 158 302 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?9^UkyPaNhq+;Y hkҴ1 bS4;?+o#W>&-a⾫K̽cǘ3W,pe RkH~|ösĵOlA3\"='Q?t߾ѻJ[O<<7Wl#_&t9_d/֚=ĸŭZC&1xriJ=KSQMbN?rWspOkpV] ge3y;?^tV döNsϴV?$?Ey+"Ě}M5\cO+{wѼ?3IxRZ?ŧaorf O=/] j?K⫷Kt.\׌uK 𶧨x{j@&RV\`75I4 oײuI|:>kOJ⧎.U:ܰP: oogï|KON/ m- w?$?E14Lgx^;ol~#^ݦjV[oWg<]/CcK{)dO9Qj}-⫯AM'KySZ&?<)*hs'_ z>aƏe[뮉LODWsM/?!1izP b?OHg@M?!+/0/:ŗ7!K7kgwO'N⩼Qx(׬Rt}=A@}?!(?$?EV_t} K?:'D>[@}7b(?$?EV_t}OH?tO Ukn,`/"oUoG۠ ?:'D>[@}7b(?,_EV_t}wV"P[E>TX@䎀Ecx\藢ne麒Gi]!x+Bl*Ϻr9h.i׮qgOe{ۣ?خOO⏲G sx#>:}[?s4V K>/0n=wZ$)<)|Q`bx^'M%QGҒt4$M}z!?y '}CQtDt|7{{˛tD<3"C6?ʱkڴZ揮j={ϲ\jM%(7ߣoּ9Oް|yKiRT.m&Ύ[TTK.|8mhbD[>‰l&}>߾(| }GPŒMgss4t)2JfGytIxqڝ<ϳ*>C)YSPԦY<1'cOv'|j²\MYNe_fwޓ"l=WߣoԔs_ noRSfO~ϟ}y|Iu}Bng\\PfO|'QuFo,?| K?)Vտ}}_MQ\_P+,27Rf_Uo_WTR/A_cm[7RRڕ? OG_'\xWz٭&}oAyo ]HQ>|_JKskq}yxVIoC-mdo;D)rG Jꬽ##o ?뵢NC{}At½h+/ ?뵢8W1_uiV2m|?zEq_-./ x)FHR?+RY5/jW}Z'?.ò|w>}?Ҁ>MweeO}7\3Bў->!&M蟹tt}T5kwW:.EIo Xϴ!v|>}Q7}w)nʛ ;*b9}S/ƿ'?I/C`W<6Ǚ|CG(uKf&zB?YzcOzCTjζysȟ߯MnW4}^3tOzF;8#Ӵ8 #<һbM[@e⯊myxwZ;l|>]&r sb=O|Wo>Úm!ؐ:gN~&7 )vkUbT&x]>tGD $/%׼dnߢ]⧍iW'5hfx|폿뜱y:EK.fӓ{M6z,PkzƟhdZk.t~K{ñ~ž84su-|Kkϸ-vlDu~_Wz۫H^gKtG:RX|*~ti(Mv"C7ot_9xO{+{{xoG!ϳo|r_u]z/h{"X\Cl|IٱM eZZxb[jeq 4УDMܮ>< eY=yΏq4? ެj~u}?Je.᷽u't_@V.SXQcOh/J53G4_% <_w}w@ɏᠾ2(O_'j?f((_]<ch/J53G4_% <>W~pς|k?+ȴj+FM>ֽ6ZCɥ^i<Ϟx]KҪTU֯Q6[$1yPb~iC<<6}Q ?i7EvUq_6}Q ?i7EvP ?i7E6}WkEqQ "*JWNd |s?³xGԧ|Bn6Mwc>XuNJ5o jf[SLRٟf׫_ <](mus3/~ϟʏ|oWEZv<(!G?p"FߌOKGLK\lO_xoDimfŶ֍y;.ViNϴi [@_ wڕ}{k:y&Ćf鲙}Ox^G"5-ڜi3>D'ܤF>yx+cIյ+|[:yɿOY U-K32mkimps̎ނou+}gJ-ϵ&SS- %y?O>8O>8|.GKCY|]Iٽ:}Ѿ?>o ,$RmоAr5m?h QM5S[M +gG]Nޟ>Jź>}kt.Yo 7z>[w aٽa>;+Wy~&BW%k2;o}dZWxm]kP)^-}7/^j |{24ɽ޹j_tvP߽8k߅տ׿ ?1;K|??Fz?K|??Fzt_)^,ƭhYA[ G_ [ 7 [ 7ɍs/?>cVwQ4^|,ƭk-+'t9JexDŽ4}?BkKb}M]Mb ̐;RV^MWv]['pQ 궲MhO~m>"Wm.~_|,?疳;3Q?玳;3[ZW-??Zff',}x?#4?玳;3[ZW-??Z D:?O@O-+c?5;Ql 2 ~Qa_ *h"$H(5x\KZCuj~ Ϳd(;⌶>#<-qk朗w^^NL7U?­nz%|7}%koD߱yк?fscZӼ6t'ly #𖭭%QfR\}{ͳdr}?G5 tn;M/j$xmﭷodn#wMnGMM>ܪV?uտ5 v)ao{eᄶDGG3߾PWīƺcφm3KwaIM t]㢦< t[~*Ƨ͗y>JM{EDucH"&zGx%s_m|A;4 æ}=!Mr&3W/|w;W΅wxKO]?M~#Iǚ VZ֗ _?cSc}w t}oKKDӬum3Gy}mՐzQPXW/?|K$o ݣlOuQs6J99~|~IOG_1 x_a/q W/1_&=#%>OGG?:Ko x_bLUGJ};Ώ$t~u08ۨu>` ?SCXmнOs "&^V4;Ծl^E87Zk?U-.Z oM;4ZwѢKK>:~G؏8V0a>cy N|t'G)ώ;!GܮKBt(O.;!GܮKBt(O.;!LW |?i斉_iQhUN74>|Gv)WO㭪/_m5lKhv#(Yca7Fmoufx^mZcXӼD_nѷٲgO~A3Gӧ-|%G%HcMM"; ͳg3Ht=[ [e :G.~!uz(~!uz+nn^cE*卤w ?}?J+/)Vf[}}-Q+/)@<+/)GجZjجbKok˪bKo}}-P..}-Q+/)@誟bKo}}-P*جbKon+/)GجZtI]7Jɧ[ZXQYϥJ>e>(חGUm?%UдLΉ@]]sx?IAm@G\$a u=?憀7Z?&? x?::+4M hKY GԼtlni?=/ED4F?X?+B9"Ƒ^0 ZfaEPEPEPEPEPEPEPEPEP֥~֯}&Gĭh>yٿV;ڒ""owSGw/^>:.KWUB}r_ Z}Σ ?`t3o3Ym!:$cRC;>䧓6U2 }@TGF뾾,} ~V{P߀̚}Mmw&}͕UZŐV d}f|2i{YC fٚcr؟+?Fa\лss2<ɱ%}iQ-Ó],K罵;|o"F-W wnืy{9e/Gx%]+{kֱ:ޣ k ܊ dO Q>S@5d>_*`>(+X>)W?`M_S@5d>_*`>(+X>)W?`M_S@5d>_*`>(+X>)W?`M_S@5d>_*`>(+X>)W?`M_S@5d>_*`>O뗃V/cqs iyg`M__X>)W9cG' ??XO,ɫ|U|SC&Ts Iǧ'ySмC4ܒ޾g55T}2Nk._ڥѿww=o^!GcNw+X>)W?`M_T}1 AIO=| ?2j/G,ɫ|UXWoG'' ޾j5d>_*byQ'+sÚgPO%ExT0׮o[f\F&[(fotoxx-18.01.1/images/retinex2.jpg0000644000175000017500000007441613222767271015405 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot 5http://ns.adobe.com/xap/1.0/ 311 482 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?[ֵxF8[. p*u1 5S2>(g/^>g.g5eAݫK'-}/5[I,okY!{ 񪬌O#4rucZ?N]wW{U}q.sZ^}ؽEu[#&K^Cd y|.3.ya'ݚ'ZqƩy5 뚹jpU2HK }Oy5d.N{$ =v\3![sW=5ko5EO%Z=Iej8oƷ<j7";FxnvI30MrkCIR?* 9Woh,+uK8ЖX'tRwHy$'b|Ic?/]'e-*,G ך3)*rRjlkxk?uV'ӛŞ! @LzCk)ڡrc1WՖQo/'!'ZK=]c /!]s IP1J2(=:r:>$ݑ'&,^I4ɠ|TPZg"MMx uIzL:|>&_[Ԋ$ P=)@MWFx[bJkzu'_ӦH.s qq $d׃|RljsoU?دv?S_\&֕msQNnT7#F+d|zH!qzvI㺱?$)?js}}/թWxjl^>eye9$NxU"1{2񇈟H5AdAi#o>guE[ ?f'ѮqOF)&%a:悮h}Xi&pp1Y'H7ncq941k!Dt/ H=A/6vjںbgpUF!h=AhpRreXȨ%0VAwV.714ܗ$Prwt.I[m b wV|$_QBzbtð+:>JŠe5ć\J#0,Ł玟Gm@90Ȫ0Xro:ԛ`<U`@5FmDB Myh_qEVznq KEJ[85oZE> 02kC5T.#&VߩU>6=VvHʯr Kpc2 'VQʯzVwT. E W0M)cLr!#=ORͣ!y\f,R8vl+Ym%Oz`0)##湉M:eYOɎ(jS'#Q`9>#׽-2&riNp֟6WT.v~X1sI"rrpj,r0S,Xp>vPUzڋE=AP?(Y?1E?#m4}MƏO's\}1{6]x}z "ƸI**y5~>- oovyoq˱]緿^*6mG}%G=cdR}\VqV84FYGLV]M6THѪG_oUL7Tb2u)߹J 23)*$V :'}D%Rg%ץfQr}}5ƥIg*ʭwUF 72}*1SYrtϩa(7Dʼzq1LWC5E,F;{r wd6D{brB|$#}W` !+nfO+C%ĬBttc>ÙS`!eaV{Y@Y稬fe/ H%Gc#ֽG'e'bkY">B!lczMecN[aIl q4qӐ{uZrle璣޳nHmj bfnLSɅNge"m/͒>^@Iªn903ۛ7%L8=@i{gΠ]QLKn$<6D( zZDbՑ{czU[h` 5jwNF[{T9ZSܧOߏ4Q\sfP ;@t:`Q%J= ;lR].4$$=fiK *q'uƣe>nȡ`'XSgKSs#2]XuR^jFtZ0;sTUF5qi#LGUk)m,lB;)G[h2p߁/D1#k k*yQQ֚Z{J:^8lLg)F85v^aEeYNJE@vFáӋ9ڶ.gyv 1֧'  {?Mm7z{Ui ZQv0`hĦ6ڎ\-7k '5yԼP2k{P.?Y0sJJ>JF8Myi6THr#zԍ/c\jQ M HLs\=MK7rN(xc&B@IA3Q6vؘ#V IOjLwN6SOsL 2t4i.NvdizQ]:푢2F:⫣nq4 J:%ԬAқߥ[&WL9}*$JԈs18>F;i$cޣ!⁻,5t}%cYQ{I')O|2}MJ =N\t#(S]w Z$w^yH zcJgԭi 8㚷g9ɐA(dvJ,v):Thծ |Fշ֚FFG|Vgى\EZLM"Ā\QJoy o AO%<hsVFH%fTNU^wRǧHX:Z4# qk7vf%ǃZZzs'&f 1&F9lT0SǂGMx ҁmzwn.+8h؜nҝo,PAԃ֚Iu1UTYm`e"ĖGV>-0n#Vݜqk,abeZ3yxG5Lit!b7 2=uo)|8V!Ţ^$W@J !O[ IK\,1.F#w>޹ XRmݙ!Bu^pcU"Q*Υ ǎp{OFm{H3SzfmGi>nL@OojU}J\vicqˢMomFcwV]nm7m#@cf> vWN*q:ȏ[=r nh\[m\1`G~ +P+ dpwwִ&VWJ t}Q]SӵGYJr;W:RD q]$^gۣ:;FRx:ur 0n[wU-;#,7YߑɫW.{fQ_j`| TZH87[2c\)힕fPOcY.zVVi=Ng$g@5(qƊW 3Udnb* ?jV5_Q~.pa{{"@5;UԪ4SznyDܯ Kz=JCc;&rIL@d5s+JHJjy'<4e=:ezԽY.7$\$GseqzӦuv j&p880JG~.ܜbYrDm#1s4{ht-I =@X$n^%+9S{TѲ\nsHde| {A#Dq?L`e,mxF|Uц4}N#}FZX^o -?3\\_ƌZ"ϊvmn͘_chlIr I$Q!@ɸ1'aW}Fğ"18V7c]Ā=s+\;B לW"2 }+XjLLlXTcۭY~jPޫԳ6&RQIjNGN$~X`$W5 o8(lN1Zە$O,$MBodyiFY?Zյn-t\ƸMG|{}ͩH!6q-Uҡ|vF|/٤ f.T x *_j3'`RKjo mMS+Oc9 yUH`%}ܐ[OjuըDY|a ꦪVl!Pg".j/f\d ¥ y*x)Љ6`~i]հ˓z =zmp񫥻{fS M{-,'+;kGF&B'\~)㌵onkc46{H>MG#$MxUPsI+H-Tw!TgiږZ کkkX8++ `mY6[b*F#j<n;Ldɘw C =j 0$ZA>s%lVڄgVl~W(ut$錚Ԩ=rAa#?B.nn!(,=?*᭚e_)9+.0vuo K5  JȿOZsֆ,v;u7˿su&/|Gqmm5 d]B忱b0p }W9ws}w5aXLoCW.t1#bDLՙzUr.rk$pܹ%eAw?ZiLnIzWW`Ys=Cjխ BXGegsP7|OcF:ԛM=Y0:B3pps|VV=6 \$1{R4Tc=1ښ_˰1JD|Lb0E'1ؙlW9l9FInz+Ɏc׭n??U?*2=H"=Uх,}N/^5#zƸq]h|jոzF~^SjF}J m-&s(':APjǐGۥ/Jk xJSq3$ʭjm8Y6n·%̶ZeR0yS.J̞SJ*Q[tJߝIurҲ,;rF dv:כ[[+xVç^iĎ*ŴVMml.]cjWP+AqS32ԀR@\\p D \֎b-bD *'nF8+THTc;j0\$hdS摎z.,O w>_=IU2>9EߖN^(OU}($7Ԥ3} g<FrFځ*BcR]G{R"aѧ$;隠ԡds~aMv,r˨ȀcN:1={98k @=`&_o+cCwQ#%dP['cVvN1mwr[6~lvnLH!T Iʘ:Uŷu!v8啱&(<$]\~aңtn#:V>qe$Yb$&e*1鵸hG$+n?Lfl0]ILӰ$.GSMYN9wCQDCۜg0 1FJR?"@\Vch* qH3Y UvN;jܓ;>p֠yw1u rje6$Բ 'ު|dq) c9zW7q=W;^dЍU'VuB5H79Sj|oԛfp8o晓Af䎼Ԅfg~Q`VagFOېWJ `<piK1qB0P ex#6c7~&:Aro8(Ww,zbJI{I8)2QMjǐ QӨ60Gje$3<4#޺o>+'Us޺_+?7o4}M#>o˿\㎵m2`ٍrM#;㸮C>-  qޡbBsߥEz II#+ ag_YnemAOV'%3&-I(j/ct];YIvѷx=Ak[UhpF__nϪwv'ݰEB3aZ8VʰJ\*?:3SRih[Os|y3QO"9~Za!LU㙣8a9ZES#5Rh=jkk{i#5PKq.7}U2m>Wk TfJ׊ĞGi aFG'n %&֒fHS= M#y{p 'ޘa0Hjb>$VRY>/L?H2l>Dž4%X'Ǫ|+/l[`*H- 5vz\ܟ6=*4(#2K=ZXB#rZĽGX!Q$w?W'w,1ոRv9L8Vi$Ч;$yRzZo6R:k0x* 8F qde#N veVPFDC'Ƭ'1M!ʴg*E9QKsnA 5+2e9nX B7"ȫZm-5 '˓YMټ_›Sh#ҍ%cފ@{@֯?2F]9_2F6OQvRg;;N3M-ڠ1SaIjvzIMJVnTʛhSYzՐ(3SqJS(}*6: $dԆ:aI) }i^b=) ydz3G^Ú '! x6#Y+<瓎x)G$[* }Sl+}K?jH=z}ːKcW[?ږW$c2$:f#Tf ;N70}尡 t#wG2'F=3ޜ6}eVpH$Y+6 ϾN$玵"DJGk2H5tG(%rI?\V0:w$gϩV0ȅN@5%$&1MN3Q8w SvG6PIjs n"l@n/# 9)&.IX9m쾇T1U'%mwĠAޠksջ3,.|p 9=4,:{I vZc[@J{T|Zְ(*qb GҼw%tW|A$xY'(̊խg)U18Pp C,`ƌĨ9HlqV!fA!/ wG*|1 W3N[ H( JmXp(@;Ar:n=kfX ģ XWvʷѩgdfLZc gX6c9Wlc eY:s 3FG|DWs]+nyi .}Tjj'BN୫`Ҭkg}j-yWV#kmϣQW㟴zsZo?2F?|UO)S~cN =0F}*P}ɰS=zsRL}FѓȦ"cVЫwNDHe튔 zP8r*)6拁[79*BqS*4) ) Y= ]qHbj|Wc^be7#G]7Ul vjBdHnFk3Og?l|+p WxE]כH2ΔH7`1MHXR-f9v*Ԓz{pUF5V;VxrA\G9՛i( #4X9 *W)8g  ' ҷ-FgnkP%ݿ˂U&нZ[V> c+ YŭVRGc׊ihpp3RnbMįNG>կ *J"b p}*nW%m2G. {a aKq<"X۴j?2>G.sؑU$3:do^*IV<#uM+ɀWR-fSjG A)37H<inmWl;DaLVz*ޚ| X{Q͓(oNNؾm˩2]j+̼ ^ qEtSz+ux_C ̨Fvr\bG^7ď#]W%j1+ŹVxͻ;{#yTԿxakNҋW|o>iUǿ'dQ_vO ?EkAhi_+G-Ok%a(D_:V"~*xk%a(+#WXa1(l+Z~C]ֱ[]hF, PQ\GǦy()+#e@VEI5Yp?&A=RSlCg?YC⇊-/&^I\+%~lc-kVMV#Q O2W^4\A zl`?+&}kQ\8 )TQ_=uC,~a1TůC`}">p<5w>m&ˋ(ed'iEPEPEPEPxnF_|2>U7|2=+uHj]ⷯ|?ulW@3gmnl (2=wj*̨BxnG_ZΟ+NԺ9T֦Jp'an6m򐏥cb9lfiG woow,1UN9Zznɡ`H=& 0CQѴQF F~nިxRpYs_RL5i=5BBO@w'mE$GԊ`ߛ\a2?UIh'CETONy2:p3^c:}LE$qX/p}+r'ucoKMJH& F}*{Wd!dQy@otAT&-Q\̥+-NJ-@lW4uİ8 MnT$YA8KFk0E?s? :;q՟}?lӭ'`sZm8j%;qόO =MfQ_4Ц f}rs7 5H:}7ۃxkL|ݹ&G(ýE6ea#&܇?"íR`xi$?R69潍|O0w.?\桸)EH7ۃ/SSLm4y|Q|t ^kSC*=S_ -ԛ"y["d%1P@iWI_a ϥ1zVVu;Cr4m)\۲ܳJYF\ԉ >WVSdd1mf\#$]$j&c#K{WӭsMG9]^ypŅzGlյ9yeƟ$_y+:Xkoºf'dm ?><+7 0$z⺜5G8"vyY"M5#oxD!Y4OB}no*ԜhZ|qǠWy'홉´'>]1Ni|FDPGܬdp9=ghl50 1k;N䶕9míA( J Ji3}Pn؝d8XYl/AF32\YkJG7ՍFZoVgYy⡦G3 -n>V294H6*L3'?/_vmaHɏbFp}+{AiLQ&~:3]p\JO̊y'Z7r}V12pqZͲ-rT7$Q5b l*}p޳KxdLMhjϫkp I֦s#-3gS{IKt@E|})c/$V8i}֞bD=Z`# `/r-bgC&0Mܐ[BG\u&҃InbmMVϛGIiYsC-lY -~B^]j; >|/hCا 3[5_JқwCGXYJ5<[X`ڎc@^[yUO{SfZ]zšjIUrXpA"\ LRRJ CJi(ȓ6El'^OcòI=2"pB9V#񮲀 ( ( ( (<+G*mH[ rMkILC5*o FkY&q\|iщ5;d#[γl(8x5@&$3u[m`aY0kQyk$LF֭ h5"C%y@ETku(,nmI28#k3) ~ !lQVZ܃e,IFxaeӄQ]*uC9sXN7۳gG:՞[ 9ʥ܀oNƮ -3I*\rjYZ!;axc3=j[g H#=zV -2"2 F+Ymn!Қ乒hI}HVLA<5Ebm xܰl;Ww vH>crI}N?QJ)cK/"[4n]= =j($"3*shvŐHWs[,͙MO&Ƞ.%ٶpO==8_F٘`vdWA5*G2 tϵ[i+4dnm.$,H""8P^1l+m2Fi91gVi=ݕ³,q}k> ǽpml?Jm՟8l[˃M#=sFxeЊ.z:xa\ƣO?~8?_C]^hzl~gC $ 'ԟP?SF#SMlE"b'EW(ZJLү4N&~: Vp_xA=Vr0Ȩxe((((}S%z[!?S꣸C5Ē@'WgomZK, .{ZUk $Wn|+ƹ[U Z|VbqiKi e3ܘO*ZUN 水mE]߼PÞ;|G#EJE5L`~4KX%h,Ta>3Y7Ɵ$ PsWf +1SzfxFÂk ƄP+ˌ[Tde~oo$$S1YOlSK%͐Eg1玃ƛDJFAq\cFr U,jA eQn+orz)G1򍧎[Xnnf gm9l֝BVӱ4iOOYY #Ñ yߋmlQ$ $nXGor:sNS3$5J9X6.<>R۶Z儀?I"h%+3c$t$5s"0] 9kIh$[gBxRq[ # _ V 8Xwp}+̼[r Xb'Mc[XaZ}:P&nҼ~*YjP"Gr;5^u(H@v|۽u]I\>\SZX5[+Vg 3@ x=3-,(tz,W׷$2<ֺK  za\jI>FT`[ӊ 8]zh(-O|@&*DioJt;i >nyf]ѻ]-f!ibrl} i%UհA!O=OZ쬘Ep^!pGuEr3[ό4 ]2HS{JжHLQ\_<eomqieFj(#s\'Zx{Wg*KS+jZ:((\zuVavkط7[c/`WR=GoʯAI)Y'P؆W.@v0韥`^[ b wD`[U޲9$_lG9<+C[O"O߷})O<*qQ.oR[ka 'ΒIvSӟ昛dӭ\yE[[2䅌FHn}IᛉcI=Ym8U錞)KpLL=U] k-Hd1oN+;G%Xt5/ąJr p]:b[YG.xޢTOjD6v4BOݐZVX *xGnVyZ*0ɢB {ҜcQM{ $1BI'zWu نY(qLgQ&ʰۆ޵ௗƚ:_"Ѕ`@TlFX}+gRƺ0nw^?E>]I?#\EŎ]3p`;Yx}c^EKcdhB$֯FK5n5 e>N}k#HՉu ;KyRc $ՓNg1ydMccE&Q-sCv'!b 7(WL ӯR ܫ0m=5#f$5U8. wIL.n02G^ΰm HMsM-{gӃW<ۗF6:qJq`=l (mNo F% |J4ma  k :V8C wD:IshmV"#22!n=3YZΥY9&Ð9jmfBڽE *?L`^,wgVT%r1nV)jCsI,flqQ$7k:XcXǂNVBA7 W>?9I4- Z8F*ǯҢMNQaqy¢Xp>\sRڽvLTq  I*{k:\m^ךZbǕ$iЬ2 iHU>FP,jnOQV$д[t?.9K3#c9'nФ7cc 1HZ2͋y*zVrsKz:U L)BG5B+iT8#&岸8vW> DSZ]^H%{fe7CH7o߷=cLԴm@a{FXtP qV0 T)5k̼IדRNYH]9jXz85,dBI#+ldVσ?u?V*vڼp)OQ( }m6jdGz߉Idax寈t-֍)%w]hwJZU8RUp )¥d*K>iMRlH{tDY!pyܛk]9 a߇ë^mE "#p9;Q%vvGƁnG*]^-UmR7Z@'}:eEQlc$zlgv+ʑqYck~U-\$k9~LqElpvԞ~Lv'-$Tl^JdwRpr95rH[l-1jݺ3Fpz?X9,O&ɪEoubk|"pZ>2U,Rn xϵWlǭ_A//WA૯θt,-q\Ytmm/YT %ws~EYfe < ץsJ ~T*jsTg ~dEChެG榺]I3|CJ*M8.3e]*@+pFVqfW@GZe}hpۂ XՔXҟukAThR 5Iпoֶko1[@Q@Q@Q@Q@3fOja~78U-+SUwSi(%AvNڼSO4_:[l֭e8dE[}LKyHXm9'=G6KV1#v9=1c%CPu¨U['Mylͻ"+I;q /p^VM$(؜seE=vJ|-;R <N M__Zͻw{-ȚfURTrj!$]IN29i3UyR4+.n*J|{S @\qҡ%E5];~1Lw 帐QG.GUd$( S*P}Ed]̌:9I<-n@S"ϗ-8ԏ25Io3 珥Dzy0AF$t,Nе iqB{ڶm?,ʹ˧El6h#3&zgC*j&CA<еSOh«G#]ƫ.rb'5C$v)9jTc_7wL;rRetj d.PGGL楱 2@(Iuz3M"*6[-c|9OOYCBA.)k{=J2S_I"[ɤd|bRtyjySP}=k7Mcw*mvH by?ʶsOc>G҈Lb>_ږ);rGRէyc{4K͸*6+j7а[i0#N ϭVVhb^^J:TA' m?^RK畊Fs^n' XmO.AǭdȖ~&}'P?3MقQ\h}3Ge"e+udRP̧ #E*N ,[qITkK'Ȍ̀*qeyl`z߆MQ-)HURkuiAF8Um幚2YV2*3U:[mM$ M-/hf%N@`O]E Ucap_ksVpNmA"9%-]%ଭ)AZ )fe>Qթ[F)A7tPhIpA<5EnNG#9M|gLv_`#~׼Er7_Nd"& Hn 0^2kVOveIe9os櫝5aQ')E:JGD?k[e -$-0O׵j$((((cƊ}k?jsSc~V ڤqlasԟҭYbӷc}*2҅bA&I ;tE!曀TЈX&=XZ\oowo%vD'(b=p]r(Է)?/JA!lwcAeJࣘSfitS8O/"mIy"|uTUlRHb54%F SSN0y=`6=T2#5'*`6([Jn~aJkxqS[n̙Y+gs F ҞR`,#D6LZ6F?1SR.2t䤵+DFcǦWM*|V8Ё/ǯ\qT9(F {b_rqiF[$SNP@9Lf&8*'޴Llɶ9c>VdB;LGjGY_G󨙡U&#b \HzDZc6R{JCpf"10 zu+һt դ7Ԗi mڭ*&w#*nc9Aڤ(m=W[1J) 8r >[.;ȒL@?TX$yj8 S+H;lDr1FaP*cA"N?6{֬ؕ=[Tބ>-t3浯Y!.|5z ( ( ( (<_ |_xVCq{47!ќylYpCE+⏅>8MªCE3 |j'C?@KU ?]Qa\W**U~7!?(ʇ mB'ޠ8Ozv0 8ІGC_ ?\OU^8'MAҏ^7?uEPl? uG*vIE9oM/. |rF$E; ?%5ō;=.sa=q .<BD44=A*NE5͈9t9Ѡ-y9(5[.:߿h\oX\&vKuUDف!CQYd6}ov4]ÿSO"^`.t eb9b"8b񨩯!1R$d o7'<(NXΫ'Ȓ_x͛߷˻<}S)xl HQu] Λ ÍaˈPv:Nz2,調Dk*@3 *AO.e1!C<Dv'}ƵnzA8Ci /: VnGZBZ.h/8x bvx<$BĚJ8=& ڻ6Z_KYY݋k%hpjgBGJE"yxS0N^QFIB6XM|,Nlp^dFnB(eԲړ-+ f,ߩBw-oL]<("Nj5z:k`jt H(NS,\ ]l;D8X ˩U;pV%reKuuBd &(v8 `2$8*bSlV^Voi;ڿ*I5mCd4|tƵ}N9S}%3STmrh,)ԝ/z L*Lk b[&0NFN"RGXP:gY fM#QgD^Lf@CNj@.}~j넨I$B=^"T&Y]FٳcK) (.vF4lqrcvS/S ]B\a4yzIC 2_& }<̎ W8EuӸ@]XJc~ ST6 M'tpCyxs0Ы پ.^œr-wKnKZzUi#׶ޜG_yY F 퀠|$܍H1Ci1(X?9*~&wn2lC;%~ =Xݶ^ɓ!gXzjVAiݥpxh4UU/B*'#n(z[jPdӷTC\;9x,m59gMҮ//q'.!&ʱg4 RS("hj1ҕȔ&QA.FXDzҦ&JE`h8W@7WJ *~DSw,xM_ͥ.?|絺KM~c A`DD'ֲ xbUN6h%=5wU嗠h m..s 2=A|}h.)*XV<$ "ftПzBQj (QR](NRi1"ce|$S*BEUÅIZty6u_ϰxbT}'uΣBQSo0̜bLFfQ$^`QT-\lh*zYwdq:*Sq䇡*^yڱm-ho)]Sw9MדH_{8y} o,7 Bq\.Ca .@3TyY :?-wגLemg%vJjW?̬ Æ ٟ! zRPDeiC? h\T"h'c2C*bGewg,/w|\{{6^ Rm5*TdžL& UMQwze4gȾ2>Roooj1 =S΋@sԄ{w3_>|8ɏ~v}6p^t~(FK-Z:;/~g᡽{uGG/CN[Kގo   'Eiz.hx\bL~RE_[F生_H+h ͖L Ed&0+6b8xi:P)!무\% V)&SH¦:[4pg-RrW€ :c Z @mIuD)Jf@6’C8b ^Q) Wy~1⋗"\ވ)2j/QHg8%).^e:72) dS)Oo?t%qdB/!8F&AIGScmt{13&uŋYjXa>roa`#[-tu.+V/0)'NgG)uoՍx/޻ g˯ ~F=V2LbaÝ. bP+ +o|Px?샛QD*C"\<u)g"Ny a4œgŒˌɮmpMgG_p۟ߤݿR;4\Q*>rڙ \w[sQ@hQO:M&UI7XI I'SZТ۟@ubǩX Á8D53(to8O/gwz31豽I."ɠI#9]hmYž, d){ 텲 (Dog 1Hĩ:8oamjZ/hHkuS]-di\ĸl£69ۗ^<Y} y`hĨ8H 851x0..PBcCm Ո0)gQcrk3 ZՌ0} f-@3aJ~G+ =c8c,͹2ieVE})k/ /8_D܆HF̅TlZ';R##g*E?el:m51hj9ѭ& o?Հ7]RP.mri 'N^*|c]3Qs6*(F-[i3Ii:Gn+c||PVl'й`M3Mm)hc"ʻч CEO[r@/fkxy)SɄ0%A*b wAj0 FqA 9&q뇑66DiB.o) }˛jEu'_,qvGO݉v/m;(q2SOx/4~6+DA~$LeeZq|"iSޯ.xheS-zߧv2J7Yfq"_1M Bǐqť#.h)kX$,v_&1|Dgj!U8뮻N?PC<[(]G+!V(SfVteʪG22 C9}+kMJA\ji^Ro=vU,1}IRr_c\X<~BSZ92LV*^OZZAaWEnNV|fDHSC}Fxu\rPyΙ +r ! <+Zۮ@ .lql~@^"`\~v'K4gK`7H.Hrղ_p2QǀS= 9H5C ҐVE%@H: 0|6-%y^&8=B_u2tIME 4zTXtRaw profile type APP1x[ ߵ /CdU$?N3vHj]H/_׿>̵&6f|&g}Gʩ݃} 8g3P-8go?y0&]Z] w\-5\[]F);r';r=@V]k̺4~7'/|(r1uXz4SmҎV =Y?l0OQ:"_G"ϟaMJnRߦ9J]s2Daows#D[#ByQ{wy3HN.O.jmX 9 5 jtc%mDg:CCMVGLP*xX#(fiygN1G7%duI5k\lQὩG+fi: hRߺXJ]Eݍ$-_~kS__Fmi@]S-!}#L56XkϞ^33?ڣM+ܺW0ElnOٻI=)m䮡MXil۔ N" $^l!,ݞHaY/]<#@n޵G}t9QPjSn9ԆҞ)lbwwܦieszd ؉$ 6ۄJ3<a~pby1ue&tWPdFYYe7IJhVksy<:Fn2ҪTsj1(XՁsGF(ד1AqLіg˭@-<4tF[dGT ԱgI~)W%v)LzV$s'ڙp*' tQ%6Yٝ0E[:;עjJ +xfo#vOzQfmUHyŔ*ui Kt9eN˜}PZ}B$Y*#LiPb͙:a1XN(Qt2hnnZ76ʷٔHYv+fSٍ GiQzYxo([_F/H܉VZYm *&cRbcxĊzY@P'WLC !C^Px1HOߤ5 yp#qVl( 'U%j;0mhZ6R(KWI7N:qڀj{@u']:^ы<$3 \Щ;@Enk {tei̙/k9[6M]h @a]+jc\@!c+oj1 7zaG 10ʲ) .$E60Mt1R8 "Uq s8񲆗lvӀ.lT׃&AEͭ!MEJ/ɹ›*0:~r΁H8ripYm42U504 &ߑ.&0p`zE.0"}FJՈ%sb2_r*SFZ=\ V8U_ /~ |r?zաb@jhkA:Yč* C}#A̍(+*;G?m NhujVWumoy٤MFx~%Y 1 2 3 0 6 64 64 1 b&$IENDB`fotoxx-18.01.1/images/mashup5.jpg0000644000175000017500000003132713222767271015221 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 182 226 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>މ|\i~\?G?Xi>X 7"7"G}loDEloDEcޟo|\$|Dޙo im}goFL7Ju_V3~މ|>މ|_.gkڽu{M:i0$Ьz،L_B=?ðϬ;/M%e(ӃS+o5Kߛ<ȣȮƑxK.Z_dkSEа;[+iH<#awZkT~.PrU=N7%qȱ'}IOQOW 5RB6~"ix,nxA4+˅\T/SMsRo hMioC!y6*.Ϸ9{Z o_loDEloDE{ ⏈꺥ͷ<3Fz6r $xP/Ou)~}嗆Eeql鰡ފ(/ d猞cFCr\9jV7B׶yGq4Av7pI7)'>,~in!"h^[՝ ?)dtwu/t}U?Üe+<;ՓOəsG~û>O~&z|~w}_?߆_7hv263G+ h>};M?ݿ 'o w;M?ݿ 'o M uዓ`cFf{~n㎔WhZ޳k[d6I'?V+\o,sE)[YO'qOrs鳇y9Mr`c$&Aݿ 'o eA?l?f0{}.<ݰIF/e-#޴kgF}eA?l?fwo/ a5ŊBoc˳\^UQO'<_wi`&+t9qV3ܝkS~&';Mڎzj%dLV.6%'=ăzhw!>+->HVRUeu\n䎹Y~j/84>>Dt6iZ^7LF$g-gd:G['Bn;G ^4ݕ\ Sw_φ<2mZݮ`zLʖUH{r<|-7[[=݊AiYBGuFd`q^*m3:V`vgd sk:IYIW˾.wwjڮk·#s5\(N"xn9UR}%~.Im/JwV -fy $w֏?pi_ xUҭS[Se=.2W^F`V셷kE ?7_25gus#¯Jt&/D+5:0w?0+WM_ci^<̗7[^A/B˄;~BR)$=o/_>υtHaKϝ2Dtg'I/?kbh̳yHif2xȯ(ׂ5g xvV<9G$pjVJ"F$nY(“\_>Gn^ m<7 rb($ݮPI*e\/57NQ7 uGhe)9*zV#}3wڦ鷚t:j$61Ychv<'$z%nmA|5Ow-`V#$3o; ϬcڪJ_Rc.mY?@.$6ֶAgpAavMqoEtGk$6pf8'KO;ĝ%NLׅ֟1<[ֺg(Ckn8u|;Q}l~!WsXky49wcQ(*r8f_߯SyQn4aiS;q\/ž5 ln4MZ;V&7O#W௅/(Үt4}oYKs%(]HcW|+eyw/]y 4dM\ulx2ޕo/$4[[Rfo9o¥;DXQY 7S,av3Vc7>xVҵHum;7֏Cco)gn6MjMX0Ȼ/x>o$4Ew!8Dq5dS_{tv~.е [k]kNͷO ,8<i̚z/]Erq_5 oMi:5/c0łEa}8ƛf%ޏzMWFԮ'HEbg77IouϭZJ6fڊp87J1%ήP;M`~vtWm/[YHȗ$QĈoV@ߋ/%.t]FBI]P kkqqe2e1eHٳ(M[뭄k_Z}nGq),LXz855x[{~߅f߈QQ_-`{: 8ݼF.gՆŠ*=A)q I2:R;~?Z/H[Tj"5謏mS ?O.(^?Z/H[Tj"5謏mS 䊊w:uW흜*d-]b@2BL̀2N M^4֭}{šF[^^٤ #8W[E  ZõΣmՎ{yg2!IQeB2̭9?Z/Hеv_GBE 5謏mS ?O.(^Z/H}Inqui#$P( ( ( Ż}*FG[Uy>*FE@.?^>B]9o-GV o j̱yd%*xo.a-5mE4+xfW"c夛"Tb&23=ޗiroķwwm}}Xc+u&HqTMͅƛmk>7Ȳ,}/0~иX|g7][G%ֺ5ٺm_KIQO<`G:E?2;1|yxj&,t`/^YIgwּSE%υIOuqq .0K",m̅wݗ?_ 5ր<t~x}:Nu5K>*;~\<;&kW i%OxM"bpeku彖gk%3BeˑRM0V۴31X!ooLjdΙ9h:myuqo&H#`?v*p2r ૕ԙ,:K}$ 4:nuvK u~eǎ5k -wo=6(,\H0IKqwY / fM3Xj& iiŷiss#,Ҽ. JmkVm~uۗ.=_4oWWDNmm%(mE*p@޼_Z`쏂dmKc}ujn,U۝Vkp_ K2inƞt#>^o,}qƷ?d|C u$:O|^/sҼws&qmqxLH^M!ibv 8_sZ_[ĺw{{iכe{Ȼs#.`wn o$%Cio{N wi3 ^o*f@OE.F|c+RN$^I-۴OTݸi??-q4\ U4Mݸi?@^l[vhطI*ſn?O&4M^l[vhطI*zȨpp7[ȣ+@Q@Q@Q@Q@Q@Q@Q@Q@Q@U#㌺+1'h@Ojj>%]׽h׽k̵> 233 294 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DWK+o>)>ӝgKUrʉ#yP8F⽶Kbe}s=-uj v[]O2W}IG("PX78@@b#(8]t7>\-4>T'n^A0CAp h@ǁ<-4ƭǥǩOMC(`F$Ac.H#;{Ի%rחlj}'?£ԏwcwݏ\W[ @[  ̓,RFw`p5|8moEΧrt}gsX2F([vTN1Mk~62W~o|f4kK4;k+J;ÕA=2x{Vcj(aOcޏ{ӰDQ WjlCsqyuk;I/qAS] oc.3}Ÿ@ }uա<#K~w76*u{hͼ,q Yn PG) &QBy9ڑgs5=jzT+=ucn弰A*NqQxFt/k`a[JYXrT0#%Gn_GI5OR{[k1p; Fݺ49x9 \6j2X ҋDƞo-y oY[ )(bLjGp^y_T@Koh5 @&ַ;Np7 O W7vc2|? q*{uHl_7+,h}kM#uC& i>}?; +_GM#f?>>}hIwï>}hIwï>}hIwïwݼ.5euC&q|8*8AY#ȫZuJWy7vV}YEVEPEVV\JI O`KESivy wouleawrU QgLK#wt iTfUGԀE6$l 4vh Ɇ5x/4=f϶b杀}| $ 6#Of汼M XtnZyɲFCG^J0ǂ3 Edzu(KxV mE ~`qZQE(((oiz Z-JgDi1  hEQEdX&񖫧ǬivV ydV$ȍ1ޘSnũ#"?Bx{TܠBٗQGE}=ܲ#D;6d>nŧEEPL`U"IsJUx{7zHtYz>k۝F;'SmyM$͂& %7(8u]a|6O x{Jhn/DVK̹?7ilT?x6ԭطFc`2!;ƺu i^ա[=^M4~Κ>O Uӯ4ѭ{;->9ݦB&ݓHRvqơ&m Reօ^) 0y*h,ab|l{zާ6_F&6riI,;)%1qRO EŬ$zD. cmA;S$HB2}^^;KUHMlsȲ(ьdďxlyZ}/*xQvnpʣ p~%Ugׯv).ns0X$X[h̉\zi>~zB\_%_OzS߇ *JYZ֧g_hWݽWdc'VbhyO+Q xj⎼l/-t/sos#>m_Ʊ@_0,Wt-EϊX>fttzk<4uhA2F=+O>o־ >]6fOMd}1 ~>]\~EH,ljI|$ei?c +>V_z^Niw`_h:gonH7ZkGYaK3`}9+ٮynNiKwy?2?axß|]?z{"abmЉbYBL#bY j;ޡlf2eđ6]H`OPk[___E|tMO _oU++G? ~ӼM˪]RH*'Y@)&]~aj3:ζ !F83^#G>$\.l -RԚݯ Dn8+{k. IXnBO)ᶒ7c5v 'o[~,7 ngFY_ngv's5'_/E.SP5;aRtה[$Q!c&Tkg'wR "6ћ0H@#90w^`eX&J{/ bml=v$'_O+cFr3c88mo0{^gͱ|X4jYŠ*9F?6ERU7mqj@IEyh7CybKv֯uS[JBӍ[l̖>ȭ5[˽{y#fb)@ff򜅏'=_o0zq~jQJυ$B&c ۋB\J"2p5nB};c4m$ okurKvh`]sޓvWbO+K<yyxOVЯO^Aĩ qbFSFÁOuKo C\YC$섗( oLr71s+t3Z)=λ;W*͞?ݫNC~)j-Ν|I4-Ν|I5'4_=5nmb|5{sokO-$5W!x|'t(6(oi;|qmqJО#Km{~tOm{~tO֊,3|Mox_LwwkllMq9=v[5?+}T(@Q\|t<<|mK&t#kY[\2끃jE(&V"ğG"ğ_0OX|Ṵ|MRŰ_&FMFZU_&tCrw1C15yީW"ğG"ğ]X7]CKbM[g^0#k*[Gm?销*KIT3"#cTX#>I^a;f+-ˬB!df艋>,?^R|'if:r!Ki(YM8ӷ+ϩQGo <^?;tL%ǭɧ]Itx_&8ٸ7㊭mՕeTH`ŷ/CWVa[OY4 iIEn0X̷QEoWS+| >꺔۽IB)˾Ӵ ''oQUwb_ק wo& ZM!X-㹉X8IT0 :{ԔeK-$|Eh~f5+'◎R%.M)4cyf6E@vbmQ@_)d`ݶzh BEZߡEh?g#U-On{[(-ed3隽HFwڝ `G'5^KAx\,5 y-Dl"d-!!#c>HNmh#IIt# ӊώMR 嶔PK?(,@&#YÙ$G.>tt)V&XY q S|#u'}]v"]_mw$;xc,/1G?(9v__J?> #W`.]BoKHz;/7ºeuM/U5 kZ]޷orI_5|JuKxV>Zw-P췗R\v1$\wOcZx=OuD2,1^BfT?~dIEԼWk-L|9RS C*Iu۴#_K;guLb=m'G%"xlgž; m$b0ɯM'fڇ ~tV1w_ifvUdbRi'qʯz_m_Y> Aqk oux̙gڵᶴH%[K7AJ䑜rqIG;Zso[%&Uei {A=N{Y^2 Mn#g+Kt.=k~*m'{CI%/,\Ux{|3.U_ E׮7sci gVKnh|gu\[I#<_ݭoSu9c7,-5H5.c7-.AVM h^5tOmmͬ/.e~.HB^6 > C xOP[KOt6k4w.# &Qo`[x;ǚ^XOëO XcԬ )yK1ocOmt7kDdKcGy{.˦0G0oSkb\l]ӼME'ݬH_UaI4;-,%]#V L1֤#_m隟;x^b'Dm!c ǮF+վ\7?6YGPpSӜڼ? |qxK|G=카ވc&,rێٯr薉ׯoĖW_GGw k8,/lK,+mR*Mʬ۲+>=?`En#0@TA6wgא񕕦4PReQ4O_4SDȿY-q%G*0*J)I7h߇ ׋"dƽZ0Iۭf^xCZRw3@Ey>@Z^m.|ga3gvY r7LyQא:W|[ sW|Yk.ra7wORzca zSuk+rO6U5cYdKƖ^'TQ "d\`YZ/Gֹ)6㟉jhFLzQ3APUf9^ z;.ɇ-RCySȥTh^/xSZ ֚A?%LYٹڽzqD&J:=ԎTAWWgCm}S\i]{yɆŻQk +i>UuOVvδ * :|'_;@δ * :|'_;@δ * :|'_;@δ * :|'_;@δ * :|'_;@δ * :|'_;@δ * :|'_;@δ *|C&dE/!VjARAaQ@Q@Q@yJ߁lIiWڗ]Ĉnm' IZKeSd @Gs-Uպ5ikG^ k QiUhX2:6YT,oS[gm>jQu@k9@m>[ϼ_ڷ_jP[ϼ_*?ڷ_o(| u@k9G?[| >m>jQu@k9@m>[ϼ_ڷ_jP[ϼ_*?ڷ_o*H"4X9ŒU[^p?n {}(Jպ5V׿r4[^p?n {}(Jgy-0{@CLѐ~]QEQEQEQEQEQEQEQEQEQEQEQEQE}ݝZM7M&7w-";: c~c5\/B֏ao(|K@-i/t_ȵ_>%T[sI.O3UdBH;qد@ڭ+/ Ĺ/d*IvKXf X)MsOoE>%nYKFiZݕuwam]"Ze愑!lVݴ pƴ|!MgbNlzuޥl} b21 p-vwZNڄs.m0Vn ):^? k?ܤ0(((((((((((((BO'ǚf?)nƺGkR/tF=:!m,zu^\Nmeo$iW]9jRh΂]\6<[q.<_nvuc?T~vM_sέɡ/N]Xu n][^@c@z.'¿^"u\CJ6~% ࠌ!9z!#M31੿ Fz? 1+vt%k&[Nd}7gp+JŠ(((((((((((((((((fotoxx-18.01.1/images/color-mode.jpg0000644000175000017500000005164013222767271015677 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:sharpen| 5http://ns.adobe.com/xap/1.0/ 268 200 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?%X[2Bi6L 玀p0uW6ۉ>3eTϛP6|LqǮkωYMՂވi%#9ri \)jSRg/̢a5cwG*2Wk/HS;=65ԑAB| R{ɯ~|Zuf/n2bnS.7`8Mf+kkTyLQmHBGao0( QA0x ѧfNe.II]=5.;D|ђz"'5G~.&ԣ!ҼS܆RگK;bC=FG$2H_b?e߸~zߚXe񆖒; S%=`h3hvzkY",ܘ8#>w^EiO>*,4xc@=årWO,dK ,BprkS׍naO-5?iv_j%FTHQd$QqX2?/G򬯴ׁ~"b|~:^mz"yed*@is+خ^>d_ʐ!x K⏊ή_-j[-E$hŗ~r,e8#Tyu*jwyt (ouo"mH@}mmm3^U波 m^Gj-m[#,͐XFC=vщ+mkG4N2Rq*WqƯ}$m oXIy#K9:$+Eeu*C=]cd_ʲG}n;d_ʏ2?/Y_i>\,sx烵 'Wwy6\`㎄px4WA-4Wm~#Zr9a(|Ӎٌ %FYpG+m}u=2 HE#MnX/nJ ؑ_GYTtUץ]߯xX 1QӪ3u(]WO/&5O Km:1+X)yXgmdI=ɯ^3IsjTXAn>~*K"f $cؿoG"?cr[&/[3➟Kmj 4bUb^x#=|61Mr2-sF~./w+|IrĒIs.?^E~'_̿-SHހ֝oi!tXV#8sziwZSs+/E]koRsIkۿoG"?cOՅ܏fuWtOmeɫ6)Hf-V6>z^nmz^to``ӀV 3<6Рn<v~.?^^%Oz_z?oG,z>VoG"?c]eW[¬G:G,_{vZڤĈi(C_2D5ufYz-.8efVws |>"jסO53¯i_G*W_ƕ5yy\vq_)~+W U v9¯i_G*W_ƕ5yyrg U _)~+W iʃ_*W_ƕ4¯i_M*&׬u{ec ٵ乺 J#1!+ʾ&~ַ kSм1\խn|`[fX2[r*UsV_ץ {~¯i_G*W_ƕ5cş;ikZXZw"}CZo.嵟|wH",$svבk$mKh _^ޝ}N7Un !a 8$I_p)o>_K_ҿUߍ+jWĚEe~R=OLZ#[UH<^pQvj9$¯i_G*W_ƕ5yyP¯i_G*W_ƕ5yyrg U _)~+W iʃ_*W_ƕ4>v/\~ZouO9;x7\^HG!$ƷLJ Nsk}?nEKvLvCaiv/W>ʑ>c$x8i+|!k# <n3 |;gŜ^6%|/ug'b̈́Xxg{[=cI4}OB4o{ <[*OCGO+?O? O-ſr{CGO+?O? O-ſr{CGO+?O? O-ſr{O სWVlX4k(;]q%# ֳC^)Wo-|[CGO+?O? O-ſr{CGO+?O? O-ſr{CNvgz׉|[Z%%LƓc⫍zk,Ec?ߤ!^v 6A}jz2/"HhzIb(M-:,X[!Иn1q(J$FY0SӬ卆UH=AW`G5y4z~|Hj6wvP i$c7^6ۜ`+c>NJN.a:ڃx_ uIpıȱ#ZT;F (ot&ɕҺl{oVii=V:pW.n6򗍈8@]W_ŮiA$kR\֭./'+XLmp~dnU<ӸTKȴ_;X<A Qť_M-2ǽO$ e)^ދMkͣ? |FggAi<b 甮[XA@pt8S|I}b6?4E0-+ `@Ҿ^Tn jSZTz g3iVpj>C'#*4`!;|э_<}6-f_[k~įip(?h1]:?_{v]Į]}l;o |`^]_iڶ4DWrƨ$da kYԾ"6z;}UV+kdžxc rֲm??焏uм'>k^aXՠI3ZO^.ra9x†cdvo\VjOpn7/WߴzXϭfޕnr‹CӃɡU *G "EzE3΅]wW#7RoxvDlIyD7Y",;tRGkω\ A!o![Gn.LҾ Lmc[6 0訋~@¹?xG+CΛtX.rCD_X!U/a]W+CΛt¹?xE+ ?Je?,(%^2¹?xG+CΛtX.p7|; ?լ̋:܃Ǩ<$"&#~>[[K^{O#;wc=EIm^]oDcKy-VE#G(\Wz63TeG8}wH6O5N~[mj:}7#` d??ޏ?޲} i>XhVL%Y_\ifVXm+"2@ Eyď'Ӿ|A𶻣u{:r^ݙdti`b>qDAǸx6<xcQ{tj}:KVUb2P 8$kҮ~>"5_5%ia"ě%b i%[TG!oCHҒj1[]}QLvZgl Wxrc'~,k>+׉tOvEnjC E<\X Ǎ/˦xPxٵM ڬaQ(`bY`aWmT {C}ZRӬ/%=8c.U0cU"1qWk~_p_]K |/_Z.mkLV2lRi€6O%aTњ3XՅnga,Mu[>w~By:zšAF{mq./RXy[QeHBa:z?~= Mt8JMم`"9/koEY%#>.zύhT=ܖ>o&X m/5`b)DWd j5][ž|C\%cqkVuJVqf=zn?S&_7t-,tQA hHXT$! y'͖h_t}WǿOknͶ u)l9,[pYFA[+q8ݿGi_{4lDM$4m4KUTabYg 4/V^ wW>RmP4jҦUY\;]IPx:7h^t+_l BZ4,J!an7E3Da&QWagxᦽk_ֵk]jSkqx"+ڮUvpO%ߙ*)_8o|rxZn[X|v^!]-ɶKs->F0\`r @Au_!c|Fkk?00wWE⏁:/VSZi1akkq0"u10c#9^״6-?ھk k:oesxOb+K!Bm vR~W,wkKkv>>!Юid<- ̠Ɵ]zcY&shW-\Y۫-v;ન` W< ?xRn+. <{"s20=T'ձt&kxviݼk_܉C[pddkV_?@io6֬5K[f:ο-.}K|U'b(V۸:VV+{O!ſ5+XTv3Z(x !Jѻf ]Q͘߉KG<+#Kt5ųĨF⭂xZgOxSXӗLDŒdWtfI6~rp{/vƓ_=B 4M?Vn,/㺷*VujYLƊY*.yZwOo]|e}ts8֐st=K] wFCyȧ+$S^M*0=WR= k z"6F6cݞ1}[*[*/?ޏ?ޘ|[U[U^sE.E. >-|Vt-|VuFncw[o; +Yl(෺e$]i#8#4n+<o{xo{xz<zsE.E. >-|Vt-|Vu\;|c/){x{M|Y<ֺMx>6cR7 ݊{k[U[U^" oWG" oW_Ayyϟo+w?o+w .|[U՝?þIIRyu,M1c?ޏ<9=mRlDBFo,Gn8یcbtrhUBEi6qF {4HvQ@QRPω'n7a޻&cZȑ^/($EuHsW95ᶻ}($4M$'f> 1z>^g ?Je?,(%^2i>CD_X!U/a@zO->6ž5]rVs,ȮU;) cxW#GCD_“W@🀾 :O>/׆qZ\ zH=Mͳ#n/K"4UPl x^{.'3@ޓhbjUilL&&.cxW#GCD_ŸVG*_vxCL!~ 9lƉkxV]iull-deeIfv>ᶋ'h)|:fլ(ao},N6OҟCD_¹ŕ׈_6 dJ q1߸ rJɩ)?[PMYZ[<]|{[|otckX7P؈{k&c+&v8#7x4XQ ?Je?,*a/ŎqS<_ 5k?xU--KV״=B]CD_X!U/aAG}ޏיF ?cxW#Gzg}V,+̿cxW#GYos~캽pAq3 i|69е~>[[K^{O#;wc=EI^]Dc{xbDfidX vu;f7k%αi!ֶI?`޻> &ru(%:o! P_u1+CΛt¹?x]gG@+CΛt¹?x]gG@+CΛt¹?x^)?Z{xOv!]i"]Ist,YbzO>*k֧|u; i!euvº‰vvY2 [[M[=ΡEĿM:?\PCĺymGXaWRYDch Lg_~/^jW-SP76zty,IvNW2Fl;*`_[+CΛt¹?x\?h<oxcXOgBŰ2qe(Tl4qɸ6p%~*߶z6R\vg=\,Ni<0V T/;}zjNΡEĿM:?\Po+w?o+w?ŧ%4Nd]6S1_Fk'ڲPWTǽv< |5n,4o/$P%\\s$0U\U@/E.E.Y֩ZmUx~UQPSqM#I'A.+EOݴ {cXMXܠ!'@p xE.E.Xgm;6(|QwL+ql,'3!mD -#=xKo~%յ岈j3y]GuP۬J,jLeWo{xo{x%;_][P_ic%DOpf8+@ڤu?i>ko{xo{xbeI4}E.E.eI4}E.E.eI4fqǭx" oWVt%&/Ko/$4ŏgO 0GPE9Y"iQ hO[T[[ێ6ae֧Y -1:zBk<^y$д5 d8cLhyz>NcKߴz.4z<zOicKߴz.4z<zok@6d[n5;xnu`3խ1ۂuR34/*_[66׿_7KUto`-y_5oO|z[.s蚮T—Т[߆>vW%s `EcKߴz.4z<zOicK>zU .9N&T$Vgj07D`φ:}u\QHftYAۻKY+Ѳ{Y%xdIj>&r6oILlw]N-?ַOommݑJUxnx8@R%^2F Ћ7fτRkVIm|Łn!tcA ,pkw.{7:$"yi#?uJ$  % ?Je?,(%^2SοcxW#GCD_½X!U/aG,o*hE<7x4XQ ?Je?,+h+5Y|5"{soNZV Đ_%^2PF ?cxW#Gz-_CD_X!U/a^X2$^.{N ,q}F ?cxW#Go5{V,OKulrdgc>$2Ig]xz_:DY<oZ5_뺌Е/ vKˈb?xb;Wv;xRGa{5u6H V;$yO4 щ YB(g*[W߽Š(QŬWc0Pr<#qU}uU(?}uU(?}uU(?}uU(?}uU(?}uUżuFN4"<]\}2n)8{PUv> f$?Oxh.?oxgE?}uVV߈?{Dz\gHh\ñ?c7G72R隖n+>  D#R7.G#3Zֱy&Tk` 6 q$?PUv> f.Cr`"?w:{pш `co*;]C3U<3AoxgE?}uVQ' 4>!> Э-Fuv! w(@O%C`dMo[f!3''ϙ9'TRQEQEQEQEQEVOߎFf5\0k;cvpt1&p_~%|Yuy|cQ﮴wξKk9d[Io<5qOg~ѾSF[a,iSڵ!n.zY]I|>T>|H80𶗡xYY+e[#A0Y6ʤĿYOòx}< 73F,Vbbc?*~f}]"7t#_//n|+{_A[2[N1Ix!:3biKqV,~>#R"vkk[>YuInf?!P5)Ǚ牵+?QYIx\ҵM1no&Per mvr8?P"7Z:%nEgz*ՠ$/ٝ^c2e䕡[K[Z~ ^ڦ 1Ew+̹4jfu4}L vWp2Y>ӯ"a=E>"V&Ů@cDLU*3:1Z|c$ןI7ps"&޷ ZH-!WZ|-  >K-XȖזw- JH՚7Vն8e#ܼgį|Rw]O.mkm:WKW/ qmlbdV'3c[j:o -u(qw.)~(%.e^E1{G)w*U~'wqDžnMz w:nqj<) Ͷ(ו%r~QW|II-\Mg,sGq ն2+.GQ A֭'ĥe0Ɍ nH S?~(%.e^E0< RT`QJ]ʽ =]—1x/iOiufkif' Jdf*J+r :QEfotoxx-18.01.1/images/bright-ramp2.png0000644000175000017500000002770613222767271016147 0ustar micomicoPNG  IHDR< IDATx}iǒɡ$wɰ{0^06 0`%ionRy&)%R]}*#"3""O8q2o]M6,1ˮiu@P @PNv;_-#{:7|C&B((6H`ʱ qgXPƢ_HA+Yk? tDd1EUK=xdd }ih ~ẗֿ́i-.xw|}BL@D 3x9FsGB"Ҏb}43Edf_]kj*Rc'_`"ap c5c '7k*>mb#tv4P;%ŦEIu@=]z#*uEi}Qygv{_= yjv 2jl%3u0VXERSК[F"*Yzmk 5`S &0-p{gKb'k|;_<1O!:z&mV#0@-o.@8x!w.\zǗ?-^:yVW9tsʓ'Oߌ2⎔%%`&Y.":Ĥ(R%BA1Ul+A"l۶Y?Ν;͵to+W}_7߾o޺`mW/~go 5#1Wae$۝d 3i1v܎4U\^/,73g\|õKܹsuS7nX^Y?6єj.lޭqQ5U æ1-̋s MŌ'(*U"2tKD= XW4 Dшj\sXo^`h96gK]$(*U"2tK5cGGRAL/ Lpo4=wg<O =\=t⣛ݹrumsCDePcI%#~G|Dj= X#Ycj:`UR̛D W/]OK'Ϝ<~/{ٷ~Y^rqJ\E( 3` Jࡧ"ǪĠ?9A:v܎4Î r%(֛;7޾z?trɼIǗk֎8tsOV}<('ϴusJPTD=e8V%8Hb9vY< E40aE?ٙ$_|o3KK.^>}[y0m~w ,^OoMQ@=UjTւc .oE⎔%%;~^Os{mΕ[_|/?2MGj2D$W =tcؒWDR'u;Xk=ľ~?$x W?x4?۷}qO-/AODPHz 8V%0qb0O@HGX{H4*بfqn/žϟ`?z?px6"C>&EJSFn@*͎qb095N8c cxGܽ4: pH5"lM0^ݷϛE|N>+α#PV43h~ʕWN=qڟ1乓oܺ(b u@WMhE .fͤ#ڪoaWawQ{h}CӈAy4~ 23~Emb|I`DxX8rG[J`p'&̓k3>x=w/ܼg?c5c*COEa`4{ ƉP2A:P" {1pYbR'޵=/^3OͫVw>y|WTm^`GTTz( p.ax0N |& 9X `01`q_KϾnOWz S'̃>~|zdž}nek.T #g"*ďlY?Mi{71ƀŽ L+cg$w8h޼qKݫ_x̱ξ{s:S7m#31^`1YqGJIt^p~oW/NgKhTF2&CrzJ<@ß_v鍈5mDHGkI\*`Ctc */hM@P"O:,$d`q*A ʮ :0 Aџ8nh9ֽ̜@ϩ4վ({Z 슭Y3=@F@VeQr;>U/T^uAB a. x+ <^[jrNZKX<$ O:Xy_<#/P`wJEq JN. Ñ홛iRZ׉sTqrWL^e,-n?iGF3&!.݉t&`'j"td/›tosxfu& &:,d|9<,NDidVEU$upT0X|38ȭ h%ˡ(RT3ewtx8B"tyv*m]mDHZ6PԁI:06(rLi=l}](g N%jڂO@!0u ܇߽Ն W](#],Z$ZG#ە(kuv,8\k% ,fZ&#؊V}A~:' A%Ǽ.l ` DGI+vRĜP=V޾٥h%:(iюc2UWч)8= }H >a!ƉR0YmUUXvnUl O^k' !P:;^֕@v )miƗ*/?86߈ڄvښRP_lM}Mxg*cx~00 (c>j:wz !1;< @`!`$Z?}4G'B˙v)yH͘ ,U@InVdlERE& m I1`qLL[836DGꋮF^;y>}j+VPel`)spX}T]j_=(m# ÑXE ]=_c=r{XŖ|tHD$4UJ0():vJV10A^cq 1x I18[VQ+*[lNm&Yx^g^Gh셌^e,ͶmH@Fڑ!;~A5 hՁ8'+d]ڎ,h_L3SNT[C}w7{X_`zt$fz!ǔʼn0 ̪ȱ #>Q*2>lQ]܉Yb[m#sIWHDaxV*BӠ:Lʠ`K!צTi!8[@`qԀ*6BW b {At/pL$Nn!).累ձH*џB=/0T, BZZ-Q@TxqdsrşJuZ*N|'e3f*ci=b#H>N`#5Qʁ+?dv56lMV{K3@g7DB@4g0"$DY a9VXR%,N?|z-ٙA ])b*Ou~P-22eo.BnSUR[R9u?6u[ h3Tj' bO#%JP1' 1}jj\(wz5 HU䘀Y`;InKR<]Δ׵OUE HͅȠXTJ;`+RE~GNnG7eH b XIc 'w&&}\}H;"څs2+ET: @Ɨ]|aKm7Q)ԥ)zxH?t$f"0 eZHN |[c٥8V%,N?|z-'R`~3|gUє Qn.Br R[\EBЎd)j:*h,ݦ?A@ۦ5&yGQՁp(ܫ^ Ry1iM@ tX8'%0M[ǶpM)|G FɠHDDX$5ҦqLIQ{ɅlDБPƀ19prgbo mҷ]!) 2525T:PicL,.M' QvTT.Z'"!2)}jyu@ wz }Đ' >ˮnx\5%Od,@Rd6cA,"V`FF%NJٜ\D94hpVg'^GLiH;g/KݦWՁl${Q$+d[iLe4 D륡P.kӶ& >fȲ6c"UDHu~>À}K`~3|PՁJ"ڋ5jn _j15m#cMzw N@:!FG18V*Hx0ݪWJ7?܇߽ W]L&`p6_ Hv{r:t;XY.wz2dJ.ZT+RE$9pr'*Ņt88 pMtd NBiG(C1&`a^Z^_}vH;RiJyM@A3DHZeZHN |[c٥8VK̳'llf z'0GK;@?Wtd;1RH~<ҙL5F m+jKSV{h[6mb1v7Bs͗5'~ztSkGv qZ~EBIA/ A* "T>A/LjuX8'%1R<]Δ׵OUEf w`IX4HUIcH)Dxq8^c XIc 'w&&}rk{#nFbrh:T@)fD@gW,nit]_K95dD IDAT,ʐe"96 mEeXu,g " CX@,ËWy&ô{>CL7& S"3=f{sɶ|؋ #W̕-ݘ]^G7mj&rL@h7Odp(ܫ^RyJdAU\40l7?D=a^3Vd3"BҞ;h(A"c"UDH&{cNo;[8:}`1vbhmX#yeu@&#L'=;\.=&N8^BT!9т&`>5wkMEKks}ݫ^ Ry1N 4+An5 &Ghx|%]d,@R imY[〈"$QSx7Tb`, ]K=<.ѷ,;~9*#74ȁd/ DX/ o`ζckMl K$Zɲ6c"UDHuyY{hM@  Xh`犎 c'F*O& HW^3*m6W&#AD(n.x-NVIQ Δhb}rOb7F8"U`?0jtt, vɥxz0%g+2?E!BaH*r0i"BR"J8prŇN@/] ` DG)˙H@FuRBmk"&v֘jeSL1L@~|srDH(6Qfu[٥8VK) JL@hY`UeG)R<=qz3,Em#ЃXA6ZmY{HD0;b`쬍HD ~0tv: Dݦfw۾-=`$WE^Aʫ.!cꙀ&HiM/\ިmpž/& >$B6d /+RE˜O8,& 1`qP)cDG 6h$Zz \ \f)4J̥(* sE\5 'jRéu u&<pq3)6|R۠Ds}ݫ^}}X*J"7RR< +r")t-Ag `=C0AZ ԮdǠ|WH,b/D. 9i#Ln.`K:2,ݦf?,cu*U:PWd?X!,.X.%uxj^x7ߞ~T6; hdafHHx;E^lf0,D ԁ|:R,ER!GA\6nЏimg D(Mz'^ X]eәh?6aE;>U` OM@>]@ŀ.)Z)fŨDcʼn5à ܼ[^|B'\/<Tb`,).슣 L#HPQ'`QΪu`^q ڰmSnv5`Z)/H!ox$B&6QV!o%"/KѶeޅ5x-gK) D3t R& l[1+K%{!"m7V -oL`$KQU u]5Bu qzU )bB1N@G%n˩xcqfz/0 F.Hl`>iv \l|K%^x0m#7^6DVqP&湶s)0C ]:햚8Ҟbo~ ]ԎHAxB' BDzTXtRaw profile type APP1xeO g``qHCHU[eJP!g|{|Tn0 *>%- %2x,tt=]ߢ±7€Y;μW!&Aʛtsg$ $"$ b~J+/)Y#AMԉfǾR:yASB]:diTXtXML:com.adobe.xmp 259 427 (IENDB`fotoxx-18.01.1/images/favorites.png0000644000175000017500000000532613222767271015645 0ustar micomicoPNG  IHDR00WbKGD pHYs  tIMENө cIDAThYypU=ey @ HjԊP*vܭVi]:thә:qv:hghKj@$!$d}ۗ=K yy/ Ih;Ù{w}syvo׍LEyOm$Q̄а,cSh]E,IlH2'Oh_,rV @jYVF2?:],s4a#PdƩg%2o$}bgl,t; ybw !x0(@JLJ3Ou.jaw9QL/yn) lD@`xl_O/9dpUɔG~ GK KmjA)pvʖBP F)ٙ27+"x6ۂaD,B + d Ÿ3'IB醊zSe J%Ej-`{f`T3=sh(w>ZLU>rxU>&{MR-ӆb.(/v!W-w9jk% LjeK.7"}}!VT5lBUܠسZrv|]S RNoάsJdЮfS0 ra~39o\{UJfh7ev3kD=9w3:&!C).Gx~csΌ% GSaT2'5=骽brehLz1BY 9 s/~ͮ ݔ}"lON>\gTX\qyuKUU"U>;+9qɑEoϡ;s )dcvu6'j܉jp-ˍ;wURW. ;W/WkM=/{]ksnX0SԇnWIrtx$nc55{ˮ?w2endEIE` QCdK&37>fR@6TN׉?r,.?hNeWʽE5`Ê}[N9b:f V7&!mh6`B!t6.Аl{;XۯoV|XpJa;2y^ΰun72S [TH SP4QH\3J];|puii 0T .Tf~`lpApd$9"_Ypz)s@׏M` m}N@xx9 UmN ؾ1akw8ygcA\ກDMʐ7G-"0˪b .2i!f3Ʉ@H ɭO4N# ArU[:]Ήb$äy\d(QHڕaY[ ,km:]&g)mPW+i4MǝoS]&CDIENDB`fotoxx-18.01.1/images/overlayCC.png0000644000175000017500000000173613222767271015533 0ustar micomicoPNG  IHDR@2cJCIDAThA 0Dkmų[oEӮELṌ1yϸ>3_3/v6pA `$+d0q  8lfLv[ nSxG xMs1{3mVq3Pl-c43#;ᨗpuv$2 2I d8-ep[0$1ᨷ`@gI. `zTXtRaw profile type APP1xR[ )zc ǡVVUQ*Dv$c<_ޯtwSw|(I /q \gq6:ꍼh#ǫDURg\*q`i:323crS+a/P3a+æD<&7}"M8U}hRIZZ-vd7K [(b~RVh?yݛu2m,|Dxx~iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-18.01.1/images/show-RGB.jpg0000644000175000017500000005746613222767271015243 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 345 290 0 C     C   7" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?“@cH S]?5 ~Ѵ^|C{xH{%c烻_ ~7e:kQ^j,-.HmЁ:ȅrcq'9n6aOl>|Ao Ok{k'Uw鯥<+ A*d8,wSqzV׀>h(ɹ5;]6Sˤ#XXBϒe;g[~au^OoaOl>|GӓHOCnuY`3`w"Ҵ/xGw>+kx}6_h/}o^Y춢48 zMZi~h?x(aOttO|^VeiPcW] ;Wz-I&[Vv6>}G+^}ު6>}MP4E,Upd޴|* fymʻ6'wEpjZ;-14[#)UԖtF^pp{ڽ:OFXr[98+;# lۿ/<Xȧd+co:vq"H"]s fI~ tkI.dtfFlm-DXGoP"s"s>%}GÖ2VݚN%u,jUI (s,r #{ =:]F6І*=XQ"èWImis[T *O9! mK뢷S; őa08 ^WWH;d2dq dr~ZYҍgOq>[?EŠxh~ǝ+{>;[x lT0PoL9讂+nWhC8\hIowï*_G&Oi> wC&q;_kYϟZ>}kM#wC& ?i>}7; >*_GM#f?>~}hIowÿ|U6G; >*_G.~|_?0mwÿ|U6E\|w?šh"m7K](l K6&1I=^(!4;t  ,nM#wC&n~~oY~"LK"HY\G ʱI"ү!.m#y8x.XeV AaW=?0mw_ՂCž56xXwi̢XP^hZ5Ʊ5I޶|eln[(Bmq;_kø zvasu[XRԮ4W 3 Iowÿ|U6G; >*_Gm^?i>}7; >*_GM#ӳ ?i>y,<2ysPFA_wÿ|U6G; >*_G.|4DXkBeYJTiI$iE~ø z?aW=asaAqz]: \h]t oZTk$++8 ma*I,qy?_?0mwÿ|U6E\k= hЋE8h9;s5?#[+{ZAqq;_kø zU{_>^ eK߂_>!kxOĺޝMQ52úL/,Z P}^D:м_} W[IEg$O"Db$X0>G :x>5m\iZ{-5k F7?_8obԼEe\)dӤhup0,>a @,zAX2"B\\.8u:.ij%=m}m[$QOKp^-+k#H_5ٮQK dYqkr2Ey_x'T5}2]V~|S5T,14cBM#፾r)uM}EwyOkv`ወ(Nwl$g=_wk=Vi^(-H{ʤ2MGG*ԃ|eeQ)yx mbOz͞n\IyteXe #Ŝn Bݚ}A? j7V`-@H)Y@SL2G[[\wkzt< (v&R5j8+\O?[׼Kq/GXt\In,T8ӤBYccvU&y>25t/ֺ5,:4&4Ve4c庆HE;|Oо x/N=N.-$rW(Tg<V02\ z1/tM+H/_j/ӗ“ֺ}D pPm zǃ?KkU{Xot#˞m)BVfE;3C_79va^|C> h~=Zy{mg$ ۷g7 F3|4$+6m=8@ Β" gH;*O#e!'[DԬgt#$6,QLc R>Rz?weudO 弰ǐG*:sڿF?49&3xRxۛw_+i Ut^MDžD}OG:_ynb!I;0>hvÑzޗTм;oc22iټPTwqI/av}/wX9K{vrʪy8Osҳ|PxJ`7RaąNC*ds^r­Ş FͪRVp[ &ݫ.ᇊ4B/ZhVF27q,pxl9` mvA_O>YL$i.pW9V]>Zmk놉mc-b|URּ. ZeƟ-[5e&ApAt۱̧VsYhb1>D3IK wb@UtW}os +47ݒ6 EK^!=RsÚWus=qceS hdf6fRӅLM ;QR0((((( :KٰK-2# :ḐP(>xv[X^QK+"vcN~B~'yz֗Qj*I!^F8L/ߴ,>мom-VV:iW9 B ǍVg֞ҟ_<_3lw>gɼ'ݞ)-UЛVW/Du-2om8ʱV ʬWCA[ t KV4$;ZD_݉KTبIwSМz)P·{QuUwu(R9vv ٛ>VYk!hy3H+ܖc$[QF(<|;Czu94G' ۨipպcڸϋ iڇ{] O֯VZpuhEy.}Ig N4u&wȉ(;oJl,/4+]Ѯ.,n]_Mo,T 7JdF#(%fh)}uy0j(1UfbANxj|}>w_YР\Ei,umD2w+c wvֶ#rxm@d['0T۝313ɭj |mk54+\6=v̖?&+(Y`@v½DWVQE ( ( ( ( ( ( ( 6oi: zxGkvˡ[AS2"=mlr2# Ax)ipZ]w2]nhE%C]'񖱯ǫ7F|%WtjL|8#Ԙ_|=5~>J/nu,@dvU7c6OG{mw\Aq=\[~c,>^⇜]l7|/w^!ENɧ2ܼK]`yc.4|ݵ,[{F;I+`'b=}E6zzυ$x準Voc&%ݳq#]7rA*(EuQE(((((((+|+sWԾ>xXGi#XMN4$a#*p(k׫񎷪So{+gZy&;6lGk+$"KVӼSx!֖DдۉmeKc1 ( K_hB4E\8ue0r+ֿf-DkZƧceZk1]dKYlml$Ico*@m)T`qg_hG`¶RAmŨ6=Cbc'cEfk8 -mdF_GY_>l|-6Sh71p@t$m<?5\Gkog-lt RȒ*21ֹ?||o7v4 2:rMa4,QCͷd!\t- SRo..-Dh )$:mBrI/M$kDzdUMFڅZw{KPqMkVtkW4ˤG8W!Y37F(=S߉V}.[Md1,6A{5Kqku|ܐZ(dnf84=|^7y?<0Eu;->ho{G7CAQ_|Vlj𝎫uY)TᐉxicuOC@||=g݉ #Nr2MxͩZ^ּ#j1KqOq3M84] $y<%w`jv-N]lg|q&65x9+oЅ业9#I;rNG+> SÍ'\- y*[p.OB+'>Asye#Ʊxr ].,IľjF$#g!Pk45I$Ӄ K_>wA8^m<7XKZ/xnom*y|[Oh'Z o2ʎYYvBqe7tۋ 6js_GxMR78Kīx7߇umO[wug¬aT[GC|@}oONIw]#C6dv#.ss~j<+^GԿ Şu%[prp׶ z?u'V&Noj6'm0^8Qrqt? og@2_͹&V Wa?^GCM x+w1kdMqmR{f.2$j ]nn귲{;A4ǰaa%uSשK|%K]I*J ( ( ( ( ( ( (~=j'[r'C$|# |23*+4K[>$Ӯ.<-s+3 c*̮c#@S$7kuYQyMLVu`>fUI1m[N}E|תx4o-ԓFtXH|l3)\L`j{ETL >Q@Q@Q@!<T>PogӯɥBDrH^?bg,uki-(g ]a}ZM؛$xlZG!?6K@m[?_QS_W~-?sf|Z^ uMJkЛ{X)$ (Uܣ_N_ )D[]?̵ER((uwIkz׉Mgڶoma,KMPFήKG KUA WoU_ #q<:uO$3ItlH$ElwFTVz 'oQ<ȓAlm;Arc] ,ĄL>8WUM4*ך]߆$Ss:'2@*UH,JiW͟Uxyב&{P[#/<,]'+'y\|%}"lm_^un\h|68JukZ,+qmgo>EZ*|+7\Uo I./?u[kklI͡_i0nHoCVzm&4QE_xwZO|Ai: X]j R 0ҺѼOYUư3ְG~ Mee*c`c8Ȣkk}W/#nK\N$ iM-OVh t1}fW6Ulk[xZ662^Epא3DYA(`N"iotyំ !ǯ]iuVX׵fX;vg8Z3G+Ѵ-MP/vayd-6KAo=E`zk!usow`hz26>YNSk]{&)5wx)4(,tW7IJ @ ݤ<7|ۿa A∼5/tG( C.fCyQNDZA?!m_ϪD# &]9eVXTKq}?M$ĝݬj?<W+,Es#Ha\ `ׄ|c_v~5{m%m.'_XL&U F?fkK SÿK#_}ɲJ#A.`dĶӱQEIG xč -lj4-gMǭ걑iD1t'_<3uj3[&üBN ?ýd?ZD κf}Hp 6mɌ6H۸C, x_:gjm4E6k(JÂ@1$soYkZK3}$qo1,̲*$ǞEt%I)|DMo<>[yq\Wu+ǁ!YinaѧʝIEF vW^t& V[Z\&Oyfsyv7GK_ևoc{gxG/,e徛Ė͒1" r t콣jnl&#u8-K_%L1Ѩ]\~9:mX@+ R&뺆>ǥ_EIˉ3K?nR<'TO kNN!gUi9K֣OЯoJۘg|BesHo)+VXmYO ;k(u[)0s'78?rv_ן~'xCOooKbGe02gxwOwzvԦW䶗ΔfEK%yP!yV7Xڿt:ku\D,/'\$I >ژ$yldH+{ Id_' _? 5k ^hvڶ,.-PWЫZ:uE&ńve6"*sX珼!(~g [Xq{? ƙu>%Gbֻg) [=V_=4[IlJt15a@t)S)K6p} ɘ–e>cmT.5si ?[;?U?igҤ@}Ÿ:z'(<3ur}j]}n-^`%en½Omuk/lna\[$E=XpAzZ50xO/cO#sB}/wcп˭#G-fBYmnb1%)~D(,+̾'|g1ZwiA o3Jo)Lc<+zrgAbo|a2uoO:N_Bb&@lt˛;3]FY[< ZmΏ2MF8fuP rFjƪ Eo=B`F&nUpW+]bc{+y=G%ύtY!=%fD>R끎2G5|t~.A-nv rضH!%pwgWҵxx~H/5}2Qʳq[u f O ixk,ikPIu4q2GiG#y0 QH ?/?EQ A߃BC걹X" \*uΣٮ:_7񕿍m|_L".l[ g8^Uyg埜tK>:5Y|=j {4qEcd:C*̛̲2F Pi 9>=𵦵K9tvFRFA) rm,ѣ]jAٝcqXOx_B"my]=CC]@s\| oW/&a?nܶ?ۍ;1އi7> m>&]YFEƗWLyjvʩ:6׈|}HQHd4ѭ%1xbO\2ypD /vz=Dž.ͲKCqUy13D5)<}xfO \F:a-.Em j$}1\'8?4o^Oojw^q@cpW\xcǿ|MoSiP$~4"۝?tdx=_.QcZn|9iV-w \4 6mNNoğKJ[P-`8v[cYzh/VgHmI.moI5<]\Nח0_=Z$Pyɭo&׮GoQi:m>wυH,)b y勵zD2iU9VRI_4Bcb@w+Kfh[Olg ۛ&2-Ŝ*ۂp][%s_SmCմ_j+YCp1ERw?޹O|b4 yolၬm:bt߰{ٌcׯ4{^4[3MlGg*IW/\_(¼Ǻ5\ZJԮ/mvF8|@\WdP'|{:-zK-V0O&ӯ^6ѕGTMO鯮cR[EC+Wz͕Σ72Խ4HO\'Gt({H b@X$~Z]hp]ci$e$^0{ _u}~%44籉cL XHy ךmhOVZ[RK~dXfդ{[Z!r>=xŖz& A=wb'"Y`I' ĐX ZKC ,1ƓeP"}.[h R%vu6# OZm~,%<1akk=gPUqwf#I#R#9rvUujk7]Z]{oRH-20PP+_<I/u]!k[qHO.9fq`Q;Pװ=4QE!Ǟge;VkhXگ\Q+H\y9&W4x,֤m52O,P[agWqcɜ}gEgGum?:u[J.#F bAXNv⣃ )m_]w+C}'W-5~_ Z7z麆|uA)'$bWU"(P+OI~tSN,ЛA!7gl^$Uh}3D?4drKLۋ;NO Tᧃ?dG ڍRa Fb̂2Wٻfm>Ϳ0~&x:djxI4W6ZL) ژQY pͺ"s}^}ᇂ<\w(~"}Vkv&(FXq :qҽiI54EPQ43uk#b:cł՚{Q& 6|ry V_bIo$~_)=.QEAa_4^gᶻ:}B%{!0[-yl&4̟ E{-"[{nsYuh>u2xv%st÷8wk:Nsy֧,)lLʁW'݀"oܘ.TD:_ß2}nǞ𥮣ՒKg]8+IJr,''?/eៃzWDе}Vn[FO5^T Yu$qk:mGtMr61Ksx sJH匀S?c3 Gĭ,zRZpGhI.s9nve8Rq4kO_ZiOqy7ׂ5]_ZQoMl@]y`\~.t:'^Cm9Q!K(e>yVV 6P(R{$Rݰhx-$оk5Kw|۔>GvKZ|,k3{Y׭#H AƠd'ګx⬾׃+'1,+'`=9?0jhX$t4CqY^,f) 98JӵVw,4->:gncN&"p1i{J< ʅxcLLnn|nB1w ?x^\x-"(&#mf\XBEsG^<~~I>^BAq ռ,tA,ځџ&H{Q#yWs`<澝ŷOo )P$%JTҲ{AOHυOat9GMn1-w]<3kz̖ Q.d듆3UwgIi͛4QEAQEUMVk}6M:+BasHݕ GӏCV1mK~<%{xYI֞R;9YaCr0 no8yt}2D6mb:W u,EctG EGƾ֖[:]ͦz66!]0%KY"{/'ڱ1.yEQE;`;xu=^jq$mGRk$rV K1jQ߉e_:}"kK}AH*cU)zE-]_<׾(ipZْUt[f18Zk<] odt[հOQ9Xm#HBm c)ҷWwO(VGv:3}e!KpxϱW_w;MzoxGXծ̾5g8̶" WQ{yWo ^vkkJj X2k{F h7OsoS^o'P6[xvOۤV6+ì?t޽m-Z>jzkVU>ЗDOy5$ȍwc`/y|mK`^0kV]fk!Y{DjeDH-B=C< |<5&Z~q-9HQF"i.~0oj:ujzL[[IXXϗ5giQ1:)ooJI^|/;DӴKh.Q-ڛ3hn(< ~ J SrmYZ ~x%2hfҮl5Z{)]2 okh.GWGK nf[DⴷhRDP6$c_<7x6m?xG^E;o>do*gKx~-\553Y-b}䴙޽O/<'H!٘BFb$8wzE+QRPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE^KQքBOrUʺ*(7*7n쫯 Ң33{|Aʺ*(7*7n쫯 Ң33{|Aʺ*(7*7n쫯 Ң33{|Aʺ*(7*7n쫯 Ң33{|Aʺ*(7*7n쫯 Ң33{|Aʺ*-,/PI"'\ҷ'<@?W_ te]f4zwk$nFZ|AvdUSY:n_,:b43{|Aʺi?u{Ǩ>?5{f W_ te]f%λwaY {c !oS 4CM#:wlϜ=νud&Qts@?W_ te]fe0 .ﮡ=$]u?SK=L W_ te]f?E4:VO@V2 MϨ\\##Ȩãd9NXSE-QEQEQEQEQEQEQEQEQEQEWR>&OUGyFo2FĞ!gXkUshSv=2>j顧gsO )Ҿx^(gVk:te $tG ld ᯈ,Ӵ R;'N4.oIm",1g$Ƿ|;.e}WY y9ur~`棹g5͵4va3dbNӑҴn%d%ym.5b}9m7NNY9sPTk~5mΩtNG[Ղ6VoY enbr=j]/Ɩ4M\Y uK",I5J?o(w_ևi!x-@ԯ5_{SHXZCnUg9,( 5N/~uxR3xfnVI#p3U54l/9Cs=qNM3&ps]-ynl-t>;{h"`UNG@)-#g@) ((((((((((+7TޑHj:U6][G, P/WoH?_x[#"hWoHU/4A".?( XTE0@+OfWg.rYb$(-Bޑ4¾ zGEE-Bޑ5kM𮉣Oh3ceF PQ@fotoxx-18.01.1/images/mashup4.jpg0000644000175000017500000003174013222767271015217 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 143 276 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>މ|\i~\?G?^C[VGg-7p6=p+gF_ƞ; R/?}ok-2$6F6ٙʍU8I5I3IJBҦ Ys#HoH;tW;v~9-\}.+#GhaZb!䞙'/?:au?|QjizCs-r)Y^wv8{y^K6HXo3"~Z6Q #LoGoloDEloDEzτ<;I ^m nFVu_֮oN ?xcYXBe`$HRERa߾=OL-o5mW~)ޯ.un-ZQ@Rx jVh/}:cz'(cz'+so3_YZxfV~ uD4ErQPPU<Z |;wzՊT$ R )tY<&(T6yUJ]eOQOWj g2ˬk~|94VYR|֏k`@3h <+x? _xKW!nF[x^31F5loDEma?^~o`7MCT Q) W3{u/iw:0iihR$/@=W%ĝ[]F$HBqIOW~?{k~=H{ůJifq$@m%:,s|K`[_%}9bg*5zWmRɧK,O0[xtǐvwcsWB7Ww?aZ;j6I#|Ki3&3Le 2+oImV+?~#?(Ԏ\ ^XLDŵx8___/hw|263]6gπQ<;Ò^5 In[~?fÞ!K!L.1/lq}eA/l?fww/ xa4Yυ~.Ӟ\{n_[$2wT6O`{Mf[GrE=1Fj•@['ޝ8 %_ eA/l?f5 sOxZεCTmZ'oOW]RMvKnY](5F0B>m5l VǾkww/ xa4ûKĿe`>>)[խUkPw]6sZڏM;"tEx.#k1Tpp{W_/hw|263Uf-~' =ku[o E/^-º^DhWh99byr,hi"%αE2$7rc־w|263G;KBtkoůmhl-S}}MiN:aeyؕQMnfF %j^%_/iيƝ=^m:u+g)rPJ؇w|U/>.[M6ȶgvVC}0"Y, NTdE}eA/l?fww/ xa4$e]ϑDz_{ZkzFҮ"Ca,+#i kO;&o$d ,߂b1df23_pûKĿ^%a}|5bj]>k ż$Q6ho}=3ķ63"x\|b9s_pûKĿ^%.],?ş}4Dӵ{M7vmabTRђCuA$4Gŏ>𥎽4~Kytx r :G]z %_ eA/l?fOxQSwoj)WsE ȃ||b~?BƯt%U*B>@w|263G;K,4^јy%XdTljXe`X9.GLJw|263G;K,當mhd9imn231e6zL!!Mww/ xa4ûKĿ.~zƃ~ہ]'k]i.{3 _kûKĿ^%y h]gYNTp9bY={~w_wq=Ħ[{? +C^%FE-Nnrs{P5|M~I_xCԣqj02([ w$C5;TI<.c+ueϪ]Ϩa;Xdet9 BzQԭ49.縐F$񯋴Z>hm5g`#-"3ʪ;v rđ]WOxww41e VWe3o0TXJit;tW>[-Yfk{łVSo*E8dlwVψ2_Ac]?.y-/c{%|%9rf# ]2Ӡ𥮱w}imyIHo Jק`\[iJFpyټ7oN{]hm: x.PY=Hf_h\2n$"W vҹ/K@߳lUqͤ[h9b.ʐ*VM81Z"֪榍qie}Pf##u"b^ ~ [>%NL5k"-¿j.5/Igcz?f(k'ݯꦧX薆Q fc@ps2Iz_2sl/_%گTf4V¼䌨< |A>#ω5_[z3붚}ȖMbږ`,qݍRW/K09TQEQEQEQE[Hu=[QӭgI/b@!sD( 3ZP P~Bk oEv,LK{s]Zx@K(eO E G ⻺)j<Gw/ ]iFG ǾC| w$x@:LB .}0ł'bN&( bTE TW|c xM#WlMQeL;]prAx;K NKH3N09pǐFsQxOF7vƁhz>bhV gi[+$۹tU] 7 rŢ~ⲕa0A[U }vdE #@rN]/I״TbɆQ^>̷9MgOiiWZe\%/@Y 溏k_Zz(+Ɵ<1O>|CgZꏂ7}꾟Eń>즰%#h]"xc"g#nkZ_tֿ?@=w9;u=RR^q_:Iuu%fId+3c$UlSGvx?D]x p$ߌֺ;/{yVhXNA l>7h"i )@*/_ V+Oh6b F`e  wS@QTf'=LIJ?*OQk=rk=kZ_t\Z6ײ,E1GULqEY>3xt/ڥ'&+ZMt.fѕHWC5O?|KBLo[LZ+uD~$PH#oO[g>u_"QHdA^ 8L?ZIi$UΡ7FO2ו|B'o#&hwmz[DǶu% A*o*kuipv!{Gv< 6nIfWʹNL]c_=O[ٴ5"&,\ՙ>+:-WMOt,gl pK x?.5|_XxWMZo`o}3Bb.#yC*##H$eJsS>z6Qzo ey[8(K6/O54~2|;|@PK@XYZ($y| ve`s*kЫ|exd80ԴtGJ# "u·gv#KKiQwyoxj4ʡoL0!) =[ PimunPK-aeٕdSTa^7|M.x[5-Mcko?bH-S00qcKa\1sl|Eo\Mw4neČ^<y^=l0o-=ZOk~Tӭ55'O-88mqִ-:;[[ƺBVKK.}mVlz_9?3x7Eѿ̰x?Vh!tKFHYZ@w0K%kx'Rkj77M(34*ISj($pIyEݯnסZkzea:]Y]ē<vH= xgেKn-t[i+ Y.`"^(՝£DXQ"(Uz Q$_ KSZ<}O׏-<rדO [y)4-1 m7Q`F+=f5}oA~%4+{xfSeO̊A 1O\&ԟE?i^=:E7먙¬#F$Dˆm^&?C Wgk[[{8mxYv2QZ#}V[]r~٨ <ĝ2ry?nҙkb+ Je"[R"RK#TP2I' OIjڎIO3P6KTC(㩤c}.,_JF*#B @$q^ }k,ҵ]J/ j>k|M⏷W2[ I!bǕAEOcմCšu𺾈<ivB;M,y$YZ#d57OtD=m-SY^5cݜn(u7ӼT?x2hdu VMI GFɹ6 GDֻxN{5^3yt]Y.go'8tEp% TeWђOT}ki~+]P4˕ ݤH!GZ_ c®}Ҽo<, 2MPmM!3L; ~]r}"0 &ql-E>RڇP"M?-M^Wwm卑̱JQF]{|?=)xWXgFP8eaXdipOz[;Z𵂊(3uC^Z)5O5rRT7+p~$qbDT^?@]yzZ߆ *ůo./Q_]]yzZ߆ *ůo./Q_]]yzZ߆ ^t5 &H,m,ʊO?k~7L _,?Y?7 W7Gůo.k{ş_?G?Ft~zZ߆ pxycfYBZI@=:օR ?k~*ůo./Q_]Iiְ֚Z0F$FOM{8,- a cE?k~7@k/L\u bm6OX51Y ,nYTcӽO?k~7@Y?7 oxtoKꯗůo./Q_];k{ş_?G?Ft~zZ߆ pk.{jWpF NtEY[ #Я&@MEPEPEPEPEPEPEPEPEPEPEPEPX0k:_ͧa:[qraBĸ? C[j 3.enQB9ZR|wcɹ?n`6nU<5>?k>"_d^yw O4i|r|>7^[Zi3HH;Ene7Ğ# V#Y%[4,P.H9ڴ[[_փghb b,,T`1FB@ފ{QR3r((((((((((((;Ȓ}K6$wSBmaRFA+wGШ̶ܕ랧P熼> kwj-SSih G"AkKy:KOͼyc!$|}}悈^YJ'| R?]|YS@ז$4>,e4b\y}E+6Qjc+tD%# l%;5J&}ɀսyU snRC8+lg;a;Wo Z>ؘCa;SfB;&l2cl~h(af"t@\N֔ ۱`YV`;/H"7vb(>37 hi5zH4Ž%UH`?U9suH/vuΫG"ѯx4ND/xjĥ_H<G݇|ٓȠ/ #a; Փ:?Za1ex{;Ȯ} 6p1#=s =fR%b@-lGVY#.Rm +<ۋƗO? %wbdY=]X7֋\@]+k`nLJ"J&k,KN{tze4J:ΩPVhib;@*w*ng0 |ʥ/*io[AE~4uzBIENDB`fotoxx-18.01.1/images/burn_DVD.jpg0000644000175000017500000002225213222767271015277 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:tonemap| 5http://ns.adobe.com/xap/1.0/ 122 300 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((z," }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?^*\Iz-l=?1䔝3NqR݊JRTco;@?wv]Bx6=LYw'Temh?_veܱ1Uɪ}T&w,⊫<:e[H&lvsS"sFsO&&CEQERg֜&qE&((4PE;֒ZN(PEPEPEPEPEP:''޿fBNٮ㽴iƓf%߉o*~ݤEA,W sWOZ\m/ft$S6, 0}6u.ٯdظ˓l\泬|[ jWWfIL0I!y3Ƿwqd~5ID\ۼX -BTHK*1zIx.-fYY-$j#+HIl<_h?-:OaE,C3)eSV9݁"]{HMm3I$lb!Bsm98AUEivzLHVR7?1ڴ%u'5mFfٓd2m;OZያ-CE]M%c"[m˴om$:ߊQCn ( ( ( ( ( e,Gqit(7lSק j%&4wFY O#-uqNy?\җ"+?/eK;V;?vE6}6vZ_t;ZfE6}6O'}W7~iG"hdKwWZI V[z͏g޵cE5DU@P`)hBroq)ÁUɴPJ*Ab3ϥ;λULiWw!|]Ҕ]*C]*CIޫ ty =(u_λUλU #ޓWw!w!u?]u?]X ty tb]*C]*C*w@Gw@@(u?]u?]Xן ty %@!LA`c8 zO@ Tu=_M5;3'e<ָ\YzgZj:)bySIi=݇cVVEu`A d%]mbZ_חl= teI),9v ~Q.xd_<忶wl~Ƨ>jdV||qi؛_+p:Tbx^X=vpqy5|Z޺(Xy~\Oucd2yEs[;zA=yߊ/,u Z]ZX 6\f# SW])'6f+vgC"@q 7ǭǭOF@}AzѼzPoo>7ǭǭOF@}AzѼzʊTA{ǭG3ePDЅ[/J$xsRc]͢;RX~Y,}N3M%ڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏҍҪbZmGĵڏ҂ĕ+U?-綣tbZmGᑽ (v5K{j??Ht;CmC[Mi16l?:MTm,,H #w#б$έ]C#4ϳ<EKEExȣ(R@}(-Q"<EKEExȣȇyG|ȇyG|_"*J(?"(!1"#!1""*J(?"(!1"#!1""*J(?"(!Q"GhGeXYlgyG|>[qxݾ):=MPާQEQEQEQEQEQEQEQEQEQEQESTQ}AR-aXb)X*ȬO W񭶧weh4:H5ghdSd/$C/eM\\ũf$# ]fh,FѢe8C#֨/tEA7-ī/46څ'gxWF;[xgRA&`Lh~ln5WZ5_ygeZG BSqa 8 FybzU$9YRwLmrXm^H5y:m6ѧ2ۋB!~ |9~r,]f5cRGj+24H1-Ybr{v 3_%X3?ZJKƬu&٥{ǒ h(ɏš,1kd FBh!U$|Q}MuHoxe{Y Hwu Zڇ-5u[IPdDh>y 8<ҧ:J$[+HR#}2O Z/:Zdb {Fzb[z1\iq}{raIౙ5X ;{sYyEQh+؃U$TPAs<ĺ%إXUi.yeu zS5XNĵ?QҢ3Fk={j??ZTPt{I|%U*}FqV3Ѭf$R?h&xSzAm|]iQڀ3m綣tbmGG3i=/zZb@YekD 1}z(fotoxx-18.01.1/images/directed-blur.jpg0000644000175000017500000003143013222767271016357 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 166 190 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>މ|\i~\?G?Xj-KZnoާ 3΁UFu;H;0]Up^CFG\Uܿ_dsQOW~G C_ױ|=b[#ohG$gC0t x9cյu 4`n,5foAuP68JZ'.-ku>3G?]ߋu |9qUa҇5bЬj0Z%P$9#wS]gø|xkx?M^K5-QvK(Q潺[ >[_P1RD;Fb*pkZ;-14[#);U;ih<{-'XnU(RāPյvG]^+!cK_,asN# :0e"YHѮH$4D@f =5_$vK?')dT/ǟEz|s{}6E{X5ӡR5Pc2sOfB}'eM^Y u]^qpNp>?}j-k-7zM uڳ'?x cu`1[b -y^)ܮ~L>}k7(1G;X18#I? ?1 ú~c?o)rb$6_m^\ɣH%Ӧr8]D@K?8uy{VO-䷑CNft4NJ2QiAe/G3V%פd3C[ű6bI$u'9>pheӴwH`K;Y L.RGI<;[%1f=I$g__O[?? ?1 OvIe[Wc|W czŸFR:_O[?? ?1 ÊFoc˳\^UQOg>$}߉̫om1[O?*{~ú~c?o(t|4?2VpT+$rbu_.i=~"³ЬC5t6ZtG+I)ڇtFn$sƈuoS|CSzWZHbIHe.6vrG;q wN.G<ͣxbI<{7w 2\jws[ݤDfy!Fc]q.8}jQW1q[j7Z$RdkDrH;O3B>XF.ӞX}p2 xk*sx%i3xa!ŢwH'i(~d >S2OV_خó藯 imjq(*6g8zsI|2o{Kk`3Er(#+gO Ή=]⎣{w!ؤdB*aL'52]7OsA Gb@Ѻ3^F .Dv"i ( -M_$[|ׯ~'7Zb-Σin.N 2Ϋ枿.OTm]!mVE↶<9'[GZGtҭlitBB, \? 5Sr*_Q֏mXim K(ki$"P\qEWA}}wZX=]]ClY er2ry8Zo,qA1((<7#"VY9`ӵ q4 z!7>W.!i(ҩ;W8j{ZwkF(j)hL(cdx`IlOWv?wK>v>hEz0]TdОkA|I65[#aj g~qɺ7=vUm"k++6mԁ/&Ы%t<ͥrt|&j~#Ʒ30-\m.31fo_&C˿&6)#L̄)AR>Ďx/߆|q-'K[VѬ5n"H[.#H̊֯{|q_f8tKAS塾 W#`W|=^Ox"zfk)jT|0C_$;u{~)ηYoqk1E$ʯ dSk+mXGʩ`2y« _x/Xjzg-ҿ8lm1Muqk,o +u׾|`$?{*MsM@Է鷨$ <׈d2©ZzͥשV$.`UuQ#<*E Fbj&;/viL[&C'ޅoxY]f/ҽzgR$ʹm335/W#Zt:g_GdKa{y-8DK2@H.EMo o{>mkNI^6Y28~>aFbtpny@~W-o>&^.LikZ;d8V .+*9X|8iouymiA𵽽=q%ԺmNFB4-@*WKO>̶TY#`؎-xsxc·5ԥn1; 8Ⅵ bU–*Wj'pKb8TI$f:w2Z*#sT*Z(|]>4f쩜qҺh (P0@zTQE F}Zx08X>.'}c{Mf)<㻈8F8$dzOEfsm<-Om]JY"͕Cv?@]"J`te 1ӒkF|+]nz$+T}2?mhms2kៈ2X&-NKj3+@.H9:>eS_zƉklMWKO4FIw|PmwR( ZWqNHYg:|U ~[lgRgeXHU>Li@)%ie2Jn̆GbN }}iu62)k^0?]9tku=յ2 ' xb㍤{OL|1Sƾ5DŽ+a,ZЛP[^jlZ( 4-K.ceb@!xFIl"Դd0K)  9+?ٯ2z]xH8|.ooݔl[岷_~HOwoymrM$7O30Bŏ/~"Ll? hvݾtM4.H;Hn `=B;͙Vɹ!h2HHԅAf 8` $sR_x{WEQh"8~s3 kxWN,O!-fOxaA(]QǾD|3iwLp!A@"{ |Ҭ;kgxf/i=pg||퓓!ltn^_G-mw3͟A~|Jno\<c۾v2G&DEʃ ==k!ϊn/:~O3d6K+rpd;Hb냸N|+VD75KHl/'}ie"4̛Y95 @ [Y6VAlӠh0)F96+><~]S{4q]2SI2 N>ExK3đh~ mkIu58.b8,zw$dRW#j~K#45GzGP!۞B3]߅_{`0c͍ȣzgN¹cTmtadi2Egg5ːvČ@"_k@{f(/;z_o ? }=w7L𰙌Vʨ ?@G|t*_ }_?/v 3W(|Q|=}TgfZX[Ef.Ib=R+mh?^J=xhs\>mKs$N!<35Vi_M4__k@{fokFRC. $jڬqk7~#5[}ekrKs[$BW:A=Cw<aw(aEPEPEP^AB<->AN.#f@ ?/'wxz.7- E/4-JkM}i{C)w6/Qx*I~"~-ɤʩ17x AlH5V}ռ6Q pzMOxL}eu=#mN~AAqھ3֍?&_kFDT3m}rRQI.iǃO'Nz BEF/um% cNIgHn8j0W< g?vͫvi ti 3d1Ii\ڂVV5Q@Q@Q@]X_*ͼW JoGZ߅ Т3i~+*<3hʇ BЃҵx-EbaO>i Zj -Iw,lDq)lmq?GAQ? XŨtQOqcYNJ\G IUK)122vG~BA*~ v zbpֺ/A ʴ 7cI0szPtP˧# W5#$Z:i!k7QX>xH$6Z`z`ҟ㿊7kkMFwk[+m#Mwm >POL|<?hWh?< [D/Jkn;FKK U``U?!LsS_Ƭ jy()nbrVڏcrֶL-dfeVRGК@O{h:l{m>pd?SYڞ 56)_pg_IKP0Gֹ~>~{ܮ}5J/"nČ =Wh?S_ƵoYH4ۈ4R,6AsG~Bjx+4?]ڏb6%5O7QK-x I4@::A^sp5F~ҵͿepM>b݉"۟ƷiQEG]=Dz?^#V>c/GO w9#|FXw{F3AmWʓIE]SKq}*ibzWw>{ -yGMy]3헋qiٴ!ፊ9rn3o n>)6qJtgۧ˺9!k-R*_L׀ "~6I;_싼~]% ]7srh>Et,o4W\FK=ʹ9C"v\=?i|f]b?|%.ndEe.C8޾c`9>̶=Q5̎v$hQX G t Mw:-ߏ.m"\Fe [!Tl)=Wdt|.tGbŴx"M4?Ԗmcm2Dힳ#Zx&Ķ}^jpȶx &| jQ|(:w7-d6tJCi%ň^җer_ȋt}W9.>4rrtxc?_n?ЖmcQu47V̫g!%b%C^Նlv..<SYo^YBcQ>cܬӅW 0Hf. +G(A.oHuG"J/9.c36L;i~kĺ`hx e<+xv (@QEQEQEQEQEQEQEQEQEQEfotoxx-18.01.1/images/anti_alias.jpg0000644000175000017500000005336313222767271015747 0ustar micomicoJFIFHH 2262 3200 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?m}ԠQl ltkԿ$wWo*f<'ċ 2k6|~u &7{wˎn?޵܇_[XqƬTr]fX֩x s#y8v=5+ܞ2Eĉ$ZȉF]#Ǟ"di:z/u|J"^YGPDdTd5G duePެ]Ư~yVO$є\uLJOЄJQLکNnHͨA3CĿ5\AE$8$W/A<.C2J<#%[5ŸX 5Gb)VTe ~&Ӽ]?[4pS'#=[\ֵvpվ8ou ׯ׀^|6P spp|Dzwaֺ0v|."ݎ}?,PX 8 |i\$gz-MEap|$`۟RxAVHC6mzj3վx"մG]d58: ,^^;ox#X]RLl_q_i|3m 軒͸uf9lMish{XJWmM3j7$x7foLC~HY>;Sޒ<דdQxx.mX_i2Kq^y3S۴kWFֺ_w?C v_0._^'#e[M], E&os\ӫ3uKC/A$kT~B4;7ŝd(?qjM$V#>is eJy/D1A*1:Σ&3L7mW/J0e =׳F6Z&z}/YA\}Srkb׷OB_OU'usOk{xo! u+./ך|i?&ҿ:ZkR[5pA/SJGTv! `㡥'ʮiE|HѬA!<5 =f9 J_xq\Lwǭz}^` tLⴕIAYq("(ޥqP- ڽClC8ڼ7'%wֻpWg%t|\jڴc?Z[=cWLi_=utcs9^[/`K,h:G?JkPvek!Y}+.&.nL Ҷ v𽌚f*/j豹\wV\/KLNRD5/:ȒM v\Wy8tBǭs:ԩٞO"}v_ck8s^Y0v5ixwZhTwi[|C ''.k MXP;ʉ:}\ƴykҵ~aֺdʃ׳F<Թ[ef'VVy1MVE#=NKVX4֝j-٤j$CC&j^tdA㍹[co~kcQ3q'jˣA,1i-ol|c\ XT$[5y70GTCv2t]f$PCo[\S$i< =*Qho!E{Iڼ w>ُx]vS*CӤD0]k s/fCӿv/5_r³T2=o^$}Un$ȫ!!/'D==dgciKpQ_Ñ[# r9\填;Ukk#am:OvTmyxu cWS%ڡ=gG8+l az_[5HWڼ'l;9·ٲȰp*|q;E^t ,&7kѳq~,F;4%x8tW6P6Pn${?ڏ*Bb@¬aͥGMk6%`Wp];uj3h۬GncV.& uܣ/764yZn-s4Zr ac7 0Ik7-"MEYR5Tcֽy !_z:t ͤ{ ;Fȕ֟VG:wVlvgȵξvk!tn<1c1ޓʕ{BL aGKuŚQ=51%#9FYQ%s޻nF6d"5*w!մ3faml|3 XQ#}ӓ^_iX0aX—bN{_%ߪk+C-pb#D*\ "; U=k#<nW,41G'QLo?:zԶ_t_Z3umIJƚՑyMwBO3$(OW17+%aZVB7EblctZf T2ko0b@Uups׋%{З.ˋf9==+CO $kVi%Wֲ}۷nF}V$;;C ZջYS\eFVzlq#E)h۾؇U7Zُc_BcklH;WjnP1'J|fN#fR6$=z[k[*+_1ERGOYn.Ji k2ֺJ#a/b,sԌuڼکvPrkj݇dKcZZ_B{^}j~RrZhzP5qMnIF4y-lsvpF0lȧS\L]fhfXW¨H,8V$FD=&𤋮a[H,T nxL6g b{DbHlWZڈU[hsiiD1 -UL+^a{c`_WADC.o֠lOcUl%2jxGasFiUV CFX:R C?46aJ۲_!"R#kй_Xk.FyvWw5bhll ;י6:}n&*HNkάGs4̑]G Q2 Soqq'bUcJbAua3#kWhlxl&`vҳϜU[YwRiؖ< Xs(sͨkXy[JTBc%*3ӥKLoE:ڤfFy8\sv7VגvҰ(btMA[ jK+AV8Zy#'>vߖRsܙCCmcO4+ݤ.[WQoMp]vZfڍ(#f79uw7$iamwqum:E54X`xgwW`G=>Ɨ y^_;>e]bzF3' BA$Y2pz6i8#A'.a<`9Z*B3PcHڲyqS%ecHy5];o+ܠvmRQ+mY z+)SѓUβtHy2N* ygi ;b X>Cs^8Hg> PUR5RŠ Hٻɱjm4(ԭ<~:g+[w,@oS+/Sh\ðI?bvէej1ys.nQ+S&/c]D ¿KF_ ~>]`quo}ӥEMA[U&(%!öOJ<5oI!+V&k ;`ס'٢?tWR| NNHM2:kf/"Gic6+8#uUhn;7kaU_ ovIeÉdbwX6V.d1^ⵏ>Aokp|N3ZڢƲ1O!sX*7Z=4"I$y^*'N7E Hs5۵lM|৵bhdx6Z"[X Ab zr` 0n,K tAPcoE1T`⺽0*_/ztcs50uOa"o5|ihe܏1]FЬp;?0-ݱxz$N|qgp12:m^u)BUIU9?Z񿌾/ qܒ1^}X%#y/xQx6gr:[IfYb)FFswFos.N3ܸgRp~xS^ >zbioT_IBJ.ݿ"֑QH-B:vgn[܋yuO,zVHKcj A5yAm#Q}ÊIvSWE$|Y^"K˂8+ |Wgu U0W.N41>;Pvi7"q]%DȫQf }k߅ۅEtD,8UuZx\r,ī ci8(,T שJ7H1$i 54 z[2'E8xI#&8ɫH/Tlq^z@P88 ɩGumjf Pd&By&κi/@'4YuY'#,<9s!c? "+2 qzYe6LӴh0zW,Ms{VMP݌:JatZptx%7ZN u"4뭏QFqLAE5th3/ *8wqLXm^}saےI)C"=: ZĽ6r+V^-I-{2`]A,5R=k&hy˙Y{^$էcڧ.Z}5RIVaSW/IGŭ̿$`Vy{̌P٨"޾Cյ駒ᢖ%f{ת9i{0Ļ{urp؟{[k+1l bd$w~Jy\͔:26[^yx~HJVTt륶O5@6M5NYHۅV5[hp+K%Egs|۰t\BT~J/6<Ѥ>⫑0qZuJ #\Vwۗlu.Us=}m;vK,פY[p|E{w*?]" e`G՟?vk۵f9keSΧ>ϹQwpra#;X!|`)?Q 5[-IsS`ÙP @jJLj:~4<}J=iSȑq"*KrMkذ"#5&c7LWBn"RGjSuGշ>>uqv&l៴ׂG|-mBϽ#1_E1Gj|G Q3qgog3 ZZur2&ːy q]ᣢz4-4.ݸ;^7 &&v*V,+ V\k3Bƞ 5?wɯ1=V7$- iIs;T\֝(HUn^٢kQS GmUVVa_uɐϹ>cUes<-f/Q5>}O[}kҠ{W-va>s]8:DTb:*'Q51zޭq[IvZ|Ÿ.t[Ir1⺝4v0N 5|vݏ7 ʒz5gz̑eWD!NGZSO_ o-F+ ËwFѽ?&é˛{#fcMzUgOGWʡ{঑=mĒ"i9a,ls*\I zM |D~{ט:ʻq_ |X\v01=GF|ysg]$ZG~X'; szw>^b_xt]6iYLn3 6xc0qںz3M4E8EO!sRf]֫BN{`%ݬr2==kծY]3Acu _9V{_(s>+8Ҿb7%Ky= &\)-=ϮC\Ʋr6GQ ۅi! ܲ'OJ/ٳYNֱ Ms|s[RV6 _ K;xjN;=Ϯ|WfZDQ1!`=:cV,jwA.0RN>#E!Zb9OZi<'2s]84Lu}:dPsntEQ`9YM2BJYOL׋DdE#yGTz>o!ִԎB_pvM! )n-A̧}u TSלԥfEj}JzT6d|oa8؀ⶭBN.[QKqʅH=)bmO }+XyrP0#85Z kӃGՇ$"(ڙVff dqZt։FFMiRHqޕv<S26OV4hJV#=I?ֵ53gZׂ\ Mq}S҃уSm.4-㨭-6Kj]oH`}Z7}zl UOg^n2VцxB}RHm9S7Egm{"֭F3^HV7c?]Uddꭕj4[ki -I?4gaw0FT(HRѱQHU8=?<=[*jL%ǭ}C?&ytśi$+ sZW40GbmN?:YEYL-o"91.*85x&;;~|YxzQ -+Ч,MTCʙ ȑ yͧeI`$Ǒǽtpq̎ܞJct%GSWSnm-ڱdS$A${Uvc^c1Ї6NLmt~–YL6OUQ*Κ?ej1HKG. cjv~`Y=0Md~[9GYѵ_"@Ìu5 pq/#kV29ix}$WȔcꆿ:d"FN}qE 1GZ(qbKJaZ򱯕wapгq=7R1cq߇VGlu^{uK̻ Լ!8{TM3GXs`^KU!WUy<,رT U*ȸiv90wFvgC M]X ~+%Mv5=ϤJͦ(N)EᴑR'괁[:OP3(j9Vq5:¬S᳓'8iƍ9d9⾍_ZO't%jXvV8\WIhRMz]lŢW Qo@3c4964O|ѕ;Hb{y(,ȯ^m2Fq\ x]+=u:AN`T/8YHԿAOJ_݅%1nx2b#u+3+X[NMդb<°+/')k-NўwV2PMPg^mJ`g=렶D{Y\پQY9z]yױGX|7J)(*IM㊣=XV'nRE)(H_gB+I9u;1yjT6ҝQ ˽"^;3x~+;(I$rX_Bjziy䌿"Vz(W  qڹS=5_y|C౲d˽f`/^qfyM\Iݹ[J$y3گ9 Er .TW;(m! (~&L*( w'ѬڊșN.y^k+1\l#` e0\Ibw7OAoo#wn8*;T4=@١A+tWoܗ@wk4۟݊M̅~Ow_t/^iZ'n]cg={[W EP'Hjzy+D^fw Ռ nBZ&k]cۚϋzZV[|5z݈ NzWo']ʋ XUǮ(#n5ZUlĕmM)vt2~ߚ_ j Ӥ~ S>Њk`2q#ظ6=D FǠZʋwZu ]$W7zu4Yҹ|k^qʬǭtV$m֑p  uhr8(@fmQPֺsB$BB.H\+>.k/o}QjR| Ufޣ;5=7Ed7nҸOiJ;$ץ>>5xB[&H#f* S4H#M-O0+|'~ +mh%+SKxeB#u]x_Ƌ;K5;DH'#y8J>O^3K}?ḙ~m\ K3MPy (6I?5v(\/i09'n䎆#3&Z4][b8WrYȫV7BPv\(ףPZw]zoOڼKD>P}D5و<|\Pڷ_Ҹ{W+ڴp"(/ҧ8^VXNt6a=|[BhN%#V{xEn#MrQ9)K 5P1iᖱDVP;qjs-}h)',UXmgD$b5;R;)C_]gg_H7 KAQ5qv+k5]F? &$z]7eh:iP^ GQuU:]O?? xjX1 W]zMJ֛tIp1^~A0ppr^dc:uFwF֤;{ϑz┶+~q|9) V#ɕRY\΢{mF.bUqE9.2k˵[zΰ=1Rg5Vw-JMS4ՙ U>Xi0H=kӃnk \t.IGU 3<_[X GZ)Z^Dbl>poC#↥mm\ +bh}- LemxP{ىP:TEu6`WkFsQkSEoZjtХo/ºT2?:eRcv06>esٴMW(]:HWhZ.X~u[g?:q+85 Ztȫw/ֺV}3\]HYkLqj8#/ c±)r~AglfA⺽QR%,ix P:7cZ|K^Hd#'}NϪYVz#CM_ j7rs]φDi%NN9SY_|W uco/(PW("U次|_[~IbE7$`cҸ_E4 yGW\:渹/$q59j/OVHaE(ז-G̯sֲ][H5-lJGރ~E> ӳ߬إ9#WBeu2-[pzƠ&}i-&/Z7JRѾ^q嶈@ּgCԥ?tmW]wXB.⩜:\8;uk7g̸U p>y7)z:x\bd{~Dv`H;Vx3Vӳg>IV1砮C)C㓚ZJD9T!Ljԝ3ꋧ֠LX`2>wqٮvl\4ghtrIgWCkiup;vc.{eUl[Y\֐.Wd=2+/q0v}c{\8L [ޜvM㋇>.".ܯ| ~ AƕNwRHLKeF^d⻽-^E~;>8ǟt{xi9A$PvdB?]4ĵ_間>:btHfyte3Q<׾>"[&:RA!znaUbi5GFܑ*}s+6Tb8iFרqe´9{%(`Ct>msNnH|efvdWƺVA/*Wx*ை5f[ҕ皶*рN8h f\~MÌԐ9G5*W&)S;P7qԍ wշ3y\S9_z*Zj\Y=FUm^i,1u[x;XM*6syȖ7BY~EpGһ*m@dVr׍}sM$VCj_ 뺇]3Љ3ZU6b1^s=n a1l_a*/Ue`́  tfQ$`}+\+OM$0%L XU?.ǷvߡsDb#tVaoE?u b#DaxkO9刡B^N^ $*Limc@Smƶîi\¤4R8){XJ5 A"ECn`u=UŸ{W9lH$ُ5v?gMll5o7aGz it8>^zٴV,f(97e\ݑ8u+>>LNڵĨq}>>$vtάy#ln?Onbֵ-b>3_I7m99ޝMy|eѣ ޛjԡLT ;I>Lje9?B}#5/Ǚ3T#*w^i/&`7fLd$wSVZ.ɨls_/g'9w8rF@9VmO1ԜkJ$ 3fk8V&4rb+Q)jeԍ:|)EuV>#X݈=MpVWyNq[T]ߟY )*xz+y9̘Kxxb,zL:BK(>X~c@ tfwBZEI;" N8Jٵ03YZb7a]f8T60kAY mj+AdmAۓk}5qN(x>sk1R0} ;zrBr"$?Kw{M `995OA;T͸P\w9ScT^Xo##ک1A֟2S3} _+a\ڏo+M}~ݠjV΀Q_kG^^27h ^9[J[:q"yTmz~;m1 ?J5rRK{XPz%ecRLܾM owzG]҉|c#[La`^&Uz *VM<~5͎dzSpՑ͝oLY;Fb=7j9 /x[_bǨҶԗ+ਮ74LuYkȹR6Ut;; zh6?jn6{VͿʋuWf SXsW0m%Ե0n9n|v/怦6jlzqΙuF%p7NyZ~tþn$n|Gյה%g8ljt:Uƚ7~_5dӜu)S^(]C-.1W9(nkNPVOw?0--eK-_UΒVxć-;W"c*W2~ĬHܜ椵k.$EMݩ"*֧3t+h"ٕ8h/[uEeʫ@tFV!ŎJNSvGЦ'X_(0i Bvŧ"0k]Q1…{p|GsHe+-<2{ӊ#QʕU[28cqq~sQS@(;S(pڭ+kֆrvskSGv*n,*M7FWtU%ph+n0O~NF|ՑUs^{57eϔs_ca𥞱=`כlt5ey%u+ıF¼UR4dȞS-ggxYGz_զ;Ǟ\[jz}ūB.z"xU gg,Nq\>ǡ|F>봋60۔e:q6m(C)݂{WusyDe&#{U-C1a$5yfJޏJi.1s[7<<.8.a#wE޽__[}$39f* X6&{v!So]w'?+FODU:UmPi+s;G[XFV4z U|Zcmf ?y3DS~}nM _H>0a9F)OZJ($SZu4)1Aj[CHΚejv̇сh*?;Xg9⭣gs^#ڴW9Y \xZݚssZOڼ̶Wc>%a8D;A9d\F9,RY|UE3XU-ZY* =^=3g!ZF{vq_TZ,Y GW ?ˈbwmdϺU0n8& ]w5.MqEݳB8se6Jq0x\eͩaڱvZ촛1,kl[Nk}+n{VAAK F9h2=+GIBdWTc}O.Sg\x(NR?ݧC+g[9h0 hKhs:?#%NWgqƠm=jս tļvkS  W4w緭R~4v++L-`ޖi澁 3)v;9&OޛisqS19(0;H5nKm .2Ew>R rz8Ҹ?99)\c/PӠ 468U#ފ+CKAAdN٤F@=W-XZЋ hHĊ嶑j2p3(FwGbOW>ojA5zo&'@RF>Q\pr*["F#:+y }hzqXѱ"IˮcxҊ+Wh#|۳TntpO8|v-mzƊ)EZ#cL֊+ 1(4CRʊ+'ԟG~T-Bp[򢊆]JON)8 δT^$}j 5 aӊHFW"FqEGzCfʳHaVkT>}ڷ;v@vhh|*RvG)eq<7jQEǡ폒Wףxpu#QQE|'h6WaQxEKcí4aҪ=`Q],@.zTc([ H@M.YKzb+ :i"`FmR09W1EV %#*NQAbbGQdeWI6I8oƧSyhJNZlR\#Nc?fotoxx-18.01.1/images/mashup1.jpg0000644000175000017500000004372213222767271015217 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 200 254 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ W{ׂ~~7Z],jWF$1G667d\(}n+x(aO{xC\ko I]i^Q-̗N;~>i^/7:5K!.jurob v_~_mn~@@ఌ`8aO{ZgxG.}*YAjאa@I|t߻<UC𝷎Eľ}Hs228py88voBm$'?DW⦧# :mS';;4b#ⷄW-_ñ]FO"vHE޽ 'Kv2}G+^}ޝl}'?›&!h2X{֟.ӣr#n(?B۾(Wǡ=3 uz'qZ΁(:ק?FϿm!#{_/?

S){O޸ek^,|㿅G hC  t>Ί"i-vA#W]#8\hIПww'Ï̛i>}O;YvG;YvE\I'ֿBܟ?? j?ܟ?? j,Oi>qA]#QqA]#Qf?=}hIПw'Ïw'Ï0[C>4r@&i֕r[>دܟ?? j?ܟ?? j,ú/u4tuGVXC$܇dܼ8h/j𑨽ѲV"5.$1y",*9WOg5Og5asG⟊ԒZ{_ê^f-TyRH^:rsK]Hj+VKBMp >`_jù>şkù>şkYA{_v|X<=9POT_YHBG"l^kxP&wꗒWrn.9y8p1}qA]#QqA]#R?=}hIПw'Ïw'Ï\IX'xd牷#A ~qA]#QqA]#Qf>nDXkL$? u9턒J68{_Og5Og5as?.v[js/en֦k I Vvpa@U$Xc<;YvG;YvE\_=ѡ%̋prvgv$'s I,Vf`dzq,_F,_F-XYWˇϽ>Tۜ sfK>OwWteb[M9PVe!b5$nVfՈvSu{ iϛs+2c@XȖQ4@Pg>_(Kv< \͡jNih ԰eaLbX:w%I`HQ䱰.k5do7Nˑl*ڥ_ޛoOc&=BESE'R$'&.MJ8N =} τnFMj:~$&#@kfx.h|3"Kk7_S4}ɖʸkuJrX Y?y/,~=aunͨVHBs'$V:턒[PCwW?]NlhC{qd-8һii%kyu1,+S^*F'MK[}/4HȺa\!V"Il(5 uzφiӢQsDJBA"̧(5KIwBSvM3GִE_-pfQg|In|kvHmOԦ.5(oKgp3¾:,ZpcFK6/̍!kK Ȋ4euR4:Ljl<4| q1_妊Rab#fD  ۳UƵZ\,;.IX΁iI_Kʾr5 TdWt o[}ƻjՅƕg[[&$3slѷZͻ!X=tw |Qo>u 6 핂 (=U5x蚁-@MkW3_[4n@PO_)6EJ4a&T)*ph_!MF;i#Pspy5n]GjnVF.K bmݕ9t㞏Zj> ]=$,:qPKNm.JJG;M:O.u>./3 L gJZӖX#7K 2/>]֛5?LkOӣ\GuH$goYjc6Q +R':7&e5Vф7@$S .r +9+]OhhGs#%:򢤤KERQYھxa90]I'T{Ξ%Y'duf-;v%\e]x_K=ɫA5f R2(9=*ޛ[[I WrB=A<؊? jaziVۄ12pLc@騦2.ѷ Ɔ4Г'| Acӣc349ĒI5LiQ+:Mjy79LT)xEҵds IJHcfQs9pkLD* 'ZcM-"n4qU?1)T<&/awxS6~O<[$jn?PMWď~m^I#KS$@7a3(U$1]mqeϡejNmXwQG+(6 Da000NkGY20e=9 qQE!Q@Q@>xGƚzށi߈V*2?2f![#$꣍bQ*((:~AQE (9fh5е;{%d$ϖ˟VxwO ~5k~:U óؽ¹%|@U ׎c7v^KI=UC+)=-~^gjn@F!V!aXޕ/.Xm9h/^VxM[p@9y xJx~ pjD @rb#ho!v_]QV}/%F.OWwgcFuO-kYʂ$ Efͳ!nf#Ouo>_uO$qİ%!@-W-m2+(}\x#z䚶=\7^"\EYU㚝ӏ?W[]?Zjz^"\4mRMs$ I$$lA;m½?mCV&\-h|#YL91U27kݺRӛV& i< ?-ojixCv4 ͸ e)"Ӎ_[q'屳[\ccwQ Joh˫ɤF.˹ vGDʬq8w]+]wx:};QX328npı9Mr 8<׌[y6]{"s F2 FQ)%nm?eO[i<6WM{P{Wu[?ȀƬyC.ljHq#j5Ξmƚ$l a3*#rk *ͺhT(_ڦpOj/!7Fv,J!1'gt x j{^SUY[72Zl0T1 /k~olr ڦ7ȭq/73\}-;@̱EbUYnqUOSZt_Z{RLvOI[d(߻xϠ'?]mu=]#Xa#4e.VV|dS4_:vzLjAjf.چq ܽābTX#R% 9__y=Ҿ'?~|L&ծSL!Y+Fk[|[hn7X|t[Gl|GZ]iԉۇ @s9ݮ3|Ѽy+bQ&Mj77F&Q ;Nҷ8{^6/<+;:֚6`HL hMt8]/$i hB]7Xk˻am'UM pG3G֎OUdCaKWsi$O/#& @$uaas4^,+Yi:,k J+nxgd;٘(Y[,g[]r^0~>(|?ĞӬ<'ZZjo5PGmUD +9gAZgEAy`/d#k '8>;/Z}oJѯtcx^E/NלT?uw.{6urjsv2O#w [1zjo5&.#hpk+A0@*accoin`5$`ry5bk5{jQEHŠ(((( kWmlSr',8Uc&ڼ`m2{Y4?X>[_vkubiZWc~^NkH㟧0)iŶ~@빼ߔCg T)KtM^whUBʥA2X)=<]˟| 5nzڮ? ӳouCMԼ -mk$(fu(жZ ȑ0UQZz~.“WJNMA;5s3ΝwA5֡im uE=6y+Mo֯ylMZNZ]wdZ8!YvV$p#=Z;٤z{j::c;cW^k.]W[Y*p[?oHüGe4xR|QO+" *ȄNdҚW/եݯnD{;JoNFnoeDIIuOj"<(;9%0pv1F<)|_ysቮk jDDfBihY 9ko x'5c2éڥc};I фb"98ݥi~wroG+M%TfP.)=)vJr,ٚDNxrHzʾ.S& ZQ}_׭Hlqo&Dyp[9]đ[#ӵx C=no^&k-{4ppFX`u=}DJ{߉?ĭ.H<6w  ZMx{/i KK # pqc|=D:<@fԣ{2]r~pNy&'t?~'⎓mkZWo5]tuX٭0M>Al\\tņ k3+mO*2y5O|s4WZ7'^$:pʭgw&eI"l=|8K^ԼE=C4&oϊtRqA{+Dw&v rE-x ^='R17(&Xq)a e=??>_ K95HeDiHAesH _Qc5Lj:zP6\Gx㘌3.Ve'=_JSi NאQE%Q@Q@%ʣC#s\>oMecӦѦUE23a|o7$XەТø+,5[ewk cdWҳu iwf7:Mɼ"d/=|A s:Ε5ŝr>ߴ3ķЭ}7J5[_NvLѿSi n.Y}n{%C*E(˜dY^~=E[1Z.K}$O$cR־>0mMgN]J{ؙ'Lo[.Zi7]8wd(q K{Weu}WF;. oKHeּ J3x/^^MdLYc iq"8M蒡䄮COx!|m3_i9YpiWXZf{svÀ adJ— +w[~8[,oD|=stw-#`RA<ּ;zGl mt=N`@ 4"2JW[.rTQEAaEPEPEW-tVXLfKVht ucgwep,K'2[Wpau#/yx.X).Y542F#8JῃzW<[oK}S[":s&vWR1W9kH' .A[/Oo]߳߅mSmAėz!ImH.Tw |SָX%Y;B@(9w?%?+e 4^VGY~~Ңi e[#&YUn^9'$4+Cd*H+[%妓H!|p#a`H%Ns׭v?%?+e 4to5uIfb2궚Y~ki#9|Ġ`\6~$ku85CYOTm=%ibD@S"}{]#_G$Gl!?ƒ[! 7]]=^:i&ʀc9Z>K.!x^7jlNzW .A[/O]#_GN_>y~l( k2Xٻ2U;qڹnu: 뚔˨"I,@(p+H' .A[/Ow.~AxȺj qvsVt&jW>7PTƏ]_$Gl!?ƏIt B.[|jj&-R-\+˹xArK+Q/W| Yj6-K[5%Z| 1HEw +H' .A[/Ow{Ң%?+e 4KVi Ң%?+e 4KVhJE`He# EQEQEQEQEQEQEeH"ydqh 3u&\+?pFpF?PX\vd.OЈ OKW5o>4 Yy8n1rX`=꿄3pGF[KM^=JȮQ3x iluYϦk5G%UqNM?o"Y> ?YϦk5Qi9|AsNԾ;^G/uY6GCD֭j>#үt\) '{ڸlséBLwQVf=ɭ;,m^yps\W~(xYVOi)47~l\Rxj'"I½͏KW?MQ uj&Uxxg^x7p=X~T B$yUAbNp88KP|@ON:}ʛjB2е~GY uj&T]g> ?S>#cI[i6V ci<Кz.潦K`٤\o93PYϦk5SZx! yngȊ 9mr] u=36.!1<;9o V76-t_&+O@ER((((((w|D=tտT54`aޛ@Ccx#{mi[{l',. xoѭ$}]:_lZ΂Y᝘+pW$RAՒifP0)>O/*SNַM]YQ? jxDW7W-5[O٠6!Bh'E@15e]x^$$WGo 54w)*wwI#˅ŠϴxK }ğ*_UO[]3Sk=+zuxz=7&+YpCofE*QxIk[ė-ֈvdjK+>-2b]BrЩVzxK \O*3&JV:ݭդWqGN8Fr:'zGot۝/pc xTE{rloyT2V62$*A&S$*_~VzxK !<{/KO|5k_m7mqV5xWb1Y~oi6Z M} J+mkyf}$Q^ЩVzxK _V<3_ MxP_ͭ SNL"8‘nW` n ^L>x3Kӵ fk[er+G?T_=Vt}sRmRCNUp#$j ks) (((((((((((((((fotoxx-18.01.1/images/twist-image.jpg0000644000175000017500000011122713222767271016067 0ustar micomicoJFIFHHExifMM*V^(if%HH023080100Fotoxx:trim_rotate|sharpen| Fotoxx:trim_rotate|sharpen|NE0Photoshop 3.08BIMfotoxx,  1000 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?N4% V97nj&7<*n& *'h(:1G:dW-|Bvwa0o޶Ksؠp|RGQiC{dEzoM0DIJ,ry)rG3&n$dajv*HJY`N0:ӡ]VW] ^I7tEKah8 5Ĥ]ʰ,Kir3s]C0IFtyӼ-}y{)l*(UR*Yzdv>ZakPieg%Qͦ;06IeR Ujzu{.+X^b& r+?*Wxoh x _Qh`iery|JzW~Te}^H;1k.i‹< V2(c'iQxw[-mh2ﱶݝ`ʌͣxgۥm!WT%‘I3:#.i5ZbrגOf# :q[9_AQT((+?*QQp-}FW~UṢ̢[ʌGE_AUO22o+?*2eeW~Te}U<<.ʪyy\ y_AQT((+?*QQp-}FW~UṢ̢ZʧӢH_/+;̭4_SLL񯊴!ҼKE.XLDNXI^k 9xU~]MnKyUP??r' 'II(??r' 'II(??r' 'II(??r' 'II~.>ɠr˃$v8+=\k5SF_hϲqA2#׌_N7a' {sO#we {Vn| g׉iwQ-N8M9+{źkAQ 'W\_I&mE(GwLdVkM’Ww]NA"m?"L˴v$Oۿ^%9(>Mm'|Bpr~UO^^yFym"8a%2J> 2H)]߯wY#>!j*]B8FÊYjLc?s63^xw]ka9KT p0@}\VR%_2;k<1[]Z؋&jnAyc_FzN-R gXz1^of6@ې { kTžw.68RUdl1$jĮWy]Hi-?#Ҿ'Ϩ-ڍ v+uʰzoN5l[[=X~d` xk^ZUDmrk"m"xdF1イgB"'kMJJZ5/4ԯ-ND/Ю2=N"i*bEu%##;ɥ6,ϸ9+f뙙>}sk,w vh)w]<0izԷDVPq,xu/>ՅdU\UbGAӥnk:Mg!;8(x q^e̮6>t>!N3Rk{q%̭7,[ dv% N[q#Hw; sIϠ;⇇S'`x9 ܒ7C.@<.kjir#[  ~RUd @{nYC^fњVxlPR?cC#7}Y|45 -N|X+rn٩ЯԤfI[<*bAdvŚ6*겠a!Gс<{Ogm[+R̝Hxc={ ٭w&IK{NJ31R]єۥ%Q.2px'W//{y,Ũ@pX@Liuv9u.[4qZNm"Dԑ\v gpWϥy6D[qmp.Gk2X{ڴ M%@Wi+$rI$DĚZPq @{`g͍{V _gkON}!.O6 Y7 tҸ/_5y4Gkڧsԋr.KN/ 61ӥGİ?k>SFAQ}7/z᝙N$d~UxwF ƓXOX. VIѤ`o)6z]ʢY17r0H=rZ1۵5c-: け=kь+S"Ig|mO_J4ox$EX2R7;TimJ*-VwXn% +HzCGz+݆rH 8h=?g76x$ӂG^4w;ף<owXVê;ع|)wEQ@hN;]|Hi!zw#<owGy^* yb,F&XsTDGiQ~=1үg?ֶg>+qi^]Mosp6(P6ؤUQ4H-Aiio.ђI' 02IZ{z?Y^gs)%5=;]2]24C.81,AVl'לvӼKqiç]5̓Zڻ={z,tE{{Կ{z?YGRg?fD^ggK;ף<owE]yy/y^=atE{{Կ{z?YGRg?fD^ggK;ף<owE]yy/y^=atE{{Կ{z?YGRg?fD^ggK;ף<owE]y6g?+3<owZL& wVp|18q6|Qv7,UU꿵oۯ^UVHQEQE>(YF^M}UL2ؑ_V>7\^tu ^ ..-R^Gސny83^_]Ι"ɣJQ~^F&|6z8iZ k/y$p{n'5Z"FC"yk$߇j򻅲{{h9FCnhly0뜯kiqZZr[$ r:q:ܙ«}&,eʙم;@86(qʡ+emB4d^Ifss5h,n Y32BFsqmjG!h}[UFsּgVo4BM7U9%A`}c|LFU;vl5m&T+%0 01toZ\Хx+tȤ`IiTupONvv%wտ/$YR)$β,Eh].pQkZLɥx%l2@I̿ g#޽\Grkm[oIdm ,&pyrqZei0H8潌!Umf:;u8߉Q+o4{_ # Ϸh#PҊ]'䳜U=sUu-wgV#K& #98>J11GHQqyo Pp$c$py_%ƺZ/k ii:~SV |̪XU#M/R/.q.؍2:s8>_iJBU%Rˁ1ܞ2H ߴu B oG)Al7^{e[%)O !L2>ڥ^[y1wo"r2RGԷc^SP}I,M?LOhg0G=*V:8[11]r~$W<x^j.--U'#rKe$G*񅜺k8<ۺFį5m׶M礷V2j)-wR0rk>)M[de1>Wֱ:uͼhwl0x 8'euc[vujaʣ|ǯҖdL I5[Ky'ԙ993_CxC־M2} s8쎃ד]t.D!sx zə"cn"%ZǑWwf0/ eLl.HO$ǿj ڝҭފ=>%[m:50`J793~kȕO{CAyK1ѺZ8d9u>h3S,<(H!AwcEjZ6*$I^_<=yiS"Ѷy` ҉݊֨`m7_Gc?Z[ƶb;~Kěܘ2(yc0'Ҿ't? Eas|锷9p3[C.Vu+-mǧh0iQ0Cn䏽'?μ)Iѣ+Ve2_;^-rXK7eR鷍}RX^Nߞ T'?grSKا^;(mcUiNt'x#NMXr[>U$8ݾSW-J7쒲)v}EWfQEQEQEk:-SjSYVciGn t+n{Oioo.+2*6RT$$Gh7E\^jsHaU`  I : >,VY.5;eŔCzxaEnk6t襃^T"wIe8T xPDf9ln/ܠ[13C'oZw:s jk`r=]}I #<J' h5).*vs\1T۹n\ڼ:ش]q1>g PX`yw2X~mlWr"FHޏ;ƺ{Uٯ<7qTwP#,E2ŎvWao=:e+o߆&!i]2#%tF_9#i^x[\j''LM}&ۺG,@9[7 j^-Ѵ"ʎEpP[JRWЪK1n6%+[idSr,PNYß$!(q\ֵnFIamgG}2!R2AUfFwyxwKuN,C,D@uG-ov_c[,~[7ΓJmV'2qKhZzo'6 S$#i8g C·D"{{Y1O5r#  #+_j|'u+${J[o)D",ɕBsOyNN5+ۂdW6rT@Nj+[idSr,PNYß$!(qU<4"VEo PED`aWT:{W;54;Ļ[%w1bd '@U˯9:Cvw'M2fkk{hd;@6gs0mSzf~7SJ׍o$8B)' gC\օSŭ֓iużӹ47dfbWAJJ,]yharQ[K FeeeF>i<]KEZʎߚAJe峀T1# d]FюjYORiQIWv2*-X SPcݣǼ zdZ45ޙq*KEdᑓ50wHrұTVr{0˅3A'Yn-'b6֓LTAt$+޿_hڕV%j0wm*AbGZHNogGu,,4,: /y;*(AEPEPEPVTvր>x*_ڏKIb* -GڙR($8%>J+[ +茋 >[DΩB~$޽ÈJ^IhK?`Wiiz]vk٬p!\zvVcI4e?J^eJm= ^O oxP5"дŎ(AsEi~'1hVH jb@.~B>5yt}cs ?y&:^Wf&ѥW7"h,@; Tg_B|YS9l+)z2/ $bIvȷ|s r=ȯ^L1EnghIR#:|աɦL9"8kh·`v_kΠ5 y5UI3[,Qt1{օ?cRU[&Ub< ,j?*&=6ya*C?ZXӂ]-afAeAj:n[4,XzzGӯI/O:UC43\$]:['5ʲ)98#ӌ׮|'ԟR}MN!Bq8^;-h>p۹}Peֽ?/ڗM GYP PpӓMFKSWL_zw.͡gjN>+_~Q?3]߇\*$~x% IFfcC,n88WE /%+~t^FoT*`0 2S唕t~?υ-^t 4ʖ!?$rsS\_ukVfNy"!a;ћQd<+qx[~>5ay-qK[JMeu h7(6(dz6*ԓwFm3ݥp$q[4i+%Ё!$͆\qMl9#QX^)Ե=9-J7cDnx?/AG5O R/͝G4G5Ziݗ޿(?_?_T5/Q\$~) ,G$~) ,G֩kv_z3?HS@YƏHS@YƏS?gyEp//ZQ}#O_e?#O_e>O>[G4G4}j}Fe;c;_k} SYAyW~#ѯniaYU(0 `rkVPT?.8VFL*ѝiiEK+FV(#G` X4˫EkM$T7O? :<^%Oڳ#?x~-?KshW?v:+W?vx~-?Ksh?x~-?KshW?v:+W?vx~-?Ksh?x~-?KsjťοEi2(p|Hfr8?tnQJghڏKIb*Ux* Hukm7Lm'O"φmbӕ!.˂=\elpIJ1f8J7ZCY]C1 ƷyZHQ0#)T7;Emj0zTךʱKo۟%B¸~ucAcԮV8X,rY s=BGMiak`6 r]:kiw(N"-C~8#bchHIlec.>=֒5k{[Qˤrgt y7zf,pdnD{ZZe-}jp@^DXaGӞiY'魌8l#@2 0ܤ0z^Ƴh1Ssw>f=߆^Iٿ/jRTdt+ɼg .L&A4,P9g5֯/f Nue8}ZUegں ?1,v}>Y6w+,n#2rg]V;u ɂG<98?P+b#i{:՜_#9/y;Rہ|'~a'k]Ñmm,Gx `iշ8N:ZtGuu"T1'sCҢ``Zuo~'۶kiiӢ'fOѵn?W/VC v*F oOT،>tU[? pV_$ьAԼkpfgR$@'.y@ɬ;]wU@q2䃀q8ȑK<>+¿=$}k[To^2? VjJsPTUٓ7DBfY2ڳ}:}A-mdf#'@Cgۯz\6 eWV`qv3܃̷W&He^=^VTjJ.^K3⤖Zz]-K̲1MMu iF:5&٢ ˒m5^K]D ZV?J>bCdq3 a1Gx=^Fv9gxw:h[H{Rp0:=f?tHTF+8q9lⲚ;Pe{#^X(i.|7wڷFcZ\$㬼JσG˯CXϠ F/ JsWQçg,7Z|Νp6bU73\DXrT>QWxo"MT8Hn.ZI̶UG q:W'G8Ysw: BE-XEp0v+˛W"8i6`lTF_7- bb';u9=MhkחmXj^&oQ%j`c#ނ8JO⾆3Q[XfcEC7 ҢMqMK6B, 1ҭCi@gnD;m@?d3IVM7"( g=uw#Hu(yԺqԸ^?WMe.hy?,6ߙ,Șk8$`u 8>ƼKg2' ?텯B.9\ȹ`1I$h$2z_[iۅ]@*{gk3JdsA8ި%CrNI"gY̳1>@X^{(-4,`9oIV$ю2.{sgtiVBD=zԟS\$ަGC+=z]h<ЉKpW'qq^u冠!H!IfUgu8!<#5Ԛ6, 'dQӧ_hȿ<۲sOC&KeZ-ٓNח/w I]UAy%Kľ'㸽wv%6ãHSw{a4ڵWm̌TEXvRG<Ɏ5&}<ڂj^yo6k`#MyriQWRZ:ض16S$qh}sg=IuMm$n\X63'$V$=hh}ϥKͪ[yYWnvYyW诱<?v_CJ_'<͜q\/C[{k;$%貙\EQEQEQEQEQEU?GU*ݟ?j [ljUA$Pzw |5B= }Bv,}p;V~?xŚ+YkMiŬ'qAǥr;.D ֑>'|Ⱦ5Ɋ{/Y?]:^]CS3^ݰf+6N[ܒ}굗\la%!q.:/=H?nu]DMoJ1#rO~?InFĪC}@;i H:Ӎ5o_S6[i]k:]WY@ {ҽ÷ZnvqdDՉ,- E%baT{Ο.u+xgKhclpʽ vɯ9Iz*zs\i.H0.)~8"sմY=OyLDmuvJ~oO~̌$2^!1V= +.ǰd.9s u8>XCf`d9^jX:ō"MP@9B97O!-`カG95xEK{Hӧ+FFq߮A ISZ3+-+ djRI2ky21Pˡ[Zڐ?m*>PJkm6ֶ򳁖.I\{jZKwgv\71~G=iBSv*ߩqgu{f2p|ɬeW.gм7VL¬J$`p8ס?|)yLM#>ǧ\(ݿ}+qCgoj]:glqRԖP?z3Ӕ,߆5߇ik"툆@|90zƷԵɍ2yq Ǿ={޿> Zԥl>\]H2V vힵVd{fL"3(N 緧BU*|8B;M"ZLk_f>³,ro Uhb]RYcIݥ17<*qJHhڥ+C5A}ɝa"WX۝v+Xۇ1ӯ_Rjm VHDrpܚqr{*0bj:ܳ46壓,x>8[yOLc KޜU˨iے'(8I޺i}r|Iy,fStHw!}sԱ7e\k%GҚ"WG(@Œy9eu DrjFťf;_tqs-5R24rOʯqȣ"LJB?ڕ i3ڵYQ#MRdR5 Zɥ^iwڧ!2^D;QY:V+5!v6ۛ5<&-Wgl85wF:if[]>]Jn-QF&70r9]Ȫ:G.l`.=NmBvϷN*ծ<K`i]4VAn9ۏz]<ŭuƺL66)n2\U~^E 2vx [K{{d+'+mGgE*~ < o7W6uwv0hڭ)$nDE!JA9.R+k{K;G*K+ 'x$玂K^Ĝ}+ޕk^ҵ=SLIn!YXc@WDFF+-5eٖVMH2 L@!'7QI/QmF >Lrmx\em:Kۍ6^Kᱳ%%Q$|=g(𖓭[H-e`˥WGG48m&v|ep_''o knn/mc%aϴyP~OTM+TtW>rlOZ‰of!Q`'ױDƷ]NkQ$ QNnQ}z_%ˆVGrO ^4\ e`=1Jsn \{k֗O$H.\gj+@x+O ڵɄQo1v=q5WWLZtoi&K$kXI$q:F|I-Ngƞ/=YF?+_,K% H3ZaI$` ;/uC,,VA$lGQZ%72eJ÷ҵI$Yq#s,?U}o>j RgyI=Y}+V'-FV2N]x'㷂᜻Q.A{?JKn-L9{~5&fR4,AxA;1]Nqlo.5-a\+e7-{s6v3c@+ܒcm@?YSGx$~Q-nNg};5t{MK(BBP23'cM:qb>j93ֵxuKB"Ȋ2)'~Im"̲\rJێ2ўEu 'ΣhΠ"պ$Iyw 8^FNq\^ s+sw'y=VX%g lgA֘*p U,d]BtK&z'7>¬VRѬ+#z=85Yw:`NvP?R:_Kjm 壎fyC$N<W% jٯ멥XI: Qv6wCi{l'I=TH#co%FfCPxA`y^  I?Oz!^*)1r뻭On#6[  ]Ķ3I=G^"Cifg(4BYL$ >A[mc,,Ն'a߻gG%K52ps[}mGR)e:el\&5 2i jzךꍧʳ]mԻ(RY#g_dc[SOѵ n[Sw5 ۰NU⹹[~a(z5~η*6Kk*y{DodA:O5M &Úޗ.}qiuq]YMɊYa2# 8)1b8z?Q)2\cZϗ[|>wavCLcΎ3lrT[>lw஻uY 6gqzl*UڎŀU=諫N5,&n}1EU $RH̩leQ# 8,9#=9TQEU #Uk&fm\nQPj@! ( ( ( ( ( (:[X`nn!3u£w23z6A7ePHYE?CVȵ >ܓ K2kGhQ@Q@Q@Q@Q@Q@Q@[T~R?Z_Oj.Ldu>Rc*׎t>ibx̛˱>R61ՠF?k-] }(㹕qԏ)7Wc5iO{lĄq' p> ߍsZxFjHЙ0X3fǠ^Oigof#3I!n=c~?*59%~zfP(1FT]cW-WڶBp r1g ~b8_]>Xa,I}k.5o*82-D[6H@y sr$vݗSqө ; lw &;Мuޙ pQ^[鹱5-Ĝ(%T`w5w22ܲ44מ?iw5ռV\W}])}9 k4qy8ݏºouf8ӇC}VоQ[ VڒYUKZ[Knb`0?u0hGC_?t;C#qȑnJeβini)EL ;A=/1eu=WdYy(i:`wna Fr^)᫆Vlqz:Z9{v7u(l!w&@ÐkoecqZ7 g ʽ\ԠWE³C cr^(-TC ˃s9tsY4қu 8k(?"0;' 5kE-^OwhXZ)!#Vk6D%ě :qB]<7.創[x3lO8-b9uGZXXx{NI$%eۍIck¾*hů6.)1(PKr~} Z\rY%i$`: x|̖Go-g7+ZvfnGIk[Po H3!TV$v"]UC 4ho+]6KeeQ"$=l{8O%u˻t@̭JӁ:V{PaߦU rr;Hȳ&m:nW?.=Aی@h"yi}JgR}@wFNzׅ:\һ=hϕYUyZBYmc=NN2p1=+S\ܖzV*LpJb7l#`Cň;Jw9j֒Xer9;V'oᙀ;皑oDGm%u]Y$FGU߅NzX; \֩2ϩ\tK'nڣK-iM<{`Uu j  4mpOt˴0-\xk嶞7W[Df}P{Vn-JXZIHi;Gr @I=Mď[3Y,`[@OSa"M}OZ OWnsi^ie!PS`-,/'g\qWg[U /a֯c}3SuEHS,o~Jlt:|W,imt l ZF# '55TQt)XɥHݳTg* Hӳ^_?ț;?r kovۏũ{Co,x#оuըZo<wjY$IKEojZ'Ԯ;WHӸ*0sO:5QwI?Uso_O q?&_&+/{:>? 8?FUA?Wek{?q?&_&FUA(#o_O q?&_&(gH(Weho_O >>? 8?FUA-_H4sPK(+wS 5S%&цXdU*.{"x8z$6+9[d0w8'=$WxR-OIn#+heM^  (q[~mpaսp+r9Oyw/?](^?;~.wyG"+9?EQw(Oyw/?](^?;~.wyV-dm9`LϘH`ݟp== =ґ*X ݇'dVFx7!ۄۆb_(liJVv=ú٭#41wC|889ѫ7rq6u9 2WfbAsò3Z5¥viPV?Jt{4ZV ?}<ǝY.vt2W ;x~!jZkY1޻_=1JG>bN*Kfa_x&|%fMP!y'ޏZ_Oeym5xwp ?1->jFs lZQ9Ea^<(ʼ❟_sѝXRRfuk"KLFH98#}amXhwj6G4,Dny#twz -<`k8 e3Iq i~ϬG` ,7Hֻ=5c{yl6"/e4^1Ŝv$cz`햟Ga io40O`0O^z 麽wp7LWɰMV'Y$Qh=?ZMrKfVoz#&(inetq{ t^0iD8ӎr뗉qz)HB䓅<5w7pUcOZ\wc ㏽굱k;iZy$" p1H-:w'ui{y8In0J.$V@.m.%Wي7?U/ 7mH\s,y +s8[ܰ9,9,˸1>-G drz\zjJJ +x S1?im{Xz)>IeHqd *s::7Gf"u[jHv61S,ʠ'h|Kv3\[#|pga[z?cjzti$(t=Yz}5f_z\(SFu\ג*?6?C gs͟|n* gs͟|n* gs͟|n* gs͟|n* gs͟|n* gs͟|n* gs͟|n* gs͟|n* gs͟|n*?UlsU6G|gs<gs2=h6?<lZ2=h6?<z(6?<zL>lsTyύ9Tdz>lsTyύ9TPK$ŀ6w ;فFQJgh*k d6P~T"dFOK 8ڣ zCmaF|0 eF2<V%vOkMg=H6:|׊NU&T=GMҬ[+W LQ*޹_qsFd77sfۗ`s73 A*~g^k 5I&۹XOJh[dGC³UUSYzvb MkhN{ɴ刮;+:}"[u-G)np3j 'K@kLRdI g޾nKò\,J|"_O\͟bϩ.NKBQ\VZOe wj&k1,ሊ02 =kᦘ'U$iRļ qۥz}GE/F˪QF{3u%s忎𥏋t&( c21gsz>/_GV2[?kCn5߳US$#Vv=I$ mϨOG|!x7-[7I#BLgTw'Mlje߹|2(V7)-4LsY2Oy)4-$A=p;vq]#pAye-DJhb?FOB6N:N![k%mf$ zZ44LϷ9b=8-i5[hb˵ ̋z~5͍K- A/ݤ_l0<=v>>w fL2"&AWW0Γuʠ8$'^uz՝^:n|J眶;=kZ/9ުFt:5ыYO9\0MDžܫ95xӠhHBɖLH9 p21:f *`._J~цsںW.7GJpӧJowuk*n{WXvm3!l.K>en9aORRep>i-X###vPgY皦ۺf;=Nb[6Qۦ: qáVF 11_Ŭte>#aFOHjHK0f`ĕ3TU#tyb-I%T̺ni99u5%/u P$8pH8s[~2iyS\a|=3d\-ET<;68eAJb%v3l=%-d1渚=7a `` [B&:L&;Lď]UB񐬫nf=ko[inY}}Vm-\ot&}^љ²<@0{lczTȽg2 Xr8-{ȧ 2F9sլ  8#w̴7m@<3Bҿ?gҿ?}g5Hhofc-hJ,Ւ9siq,R=ؒj-2JBq-"S=? JHgдפ^q}Ulml-)ayb.-?̻#"V`]>9Y͆{IUo-靤NZ%χ$Iχ^yk 2 z{m.4ԋS0A$ zxKA]VVunDOjmCr ^<е?.z;oIXd[ {HYpA\~G4kXݰ IH\i Qw̱$woBcAS!"^PYk\\VwX9Ǚ6}5hxg㦅U4$0 ͠?iχ:vxSH5xmA/|NrH* b3i_ Qχ$^T6ֆ]O v3j0Y/R< dq֮帳}v[ߴIݼ! ")Pns5Bnקχ$USGԞ4ijKmuBHV# VOkvzލXk;&?(,k慏oXGn+6TX \Ot ˃f@ݎVX±Ep[fTgs[ְ#9uxZˊ߳T~1uE6 )|{+ުQ*~ֻ>$Xu-q4-c18Y[N=GG< &).&ai?ѹg8 O~W62.TCjj|gUm촫r'*jkl1j6Daj_F ]$֓G=Ȼ)Oix"1n9RxUzfh^vOzW wC*$(,ͅ+xƯӵ`V|~uEJ1fz2 oMD] E5#6 k:4kfvoۮ n]pzr -@0sܮ0q ՞!Mx]]d!0ː 烆]a %,!91r*wFz6>-C&&?,Ҽ.uu6=+c,劃-`R-v2wyf>,_2vy5ǰa@VoG2’|,{QGp9 I$>e,h[+JA8_鎮;}"ZMml &!i=ڻ!YpsU<%ίPKhN$kǕ5^;D¿q3'+_ϣhwm]2|XOgSXm $-գ2vGpO;qq\U*hUH'I#I t c,3 ~p~x#5aIMXkRQWmF.eIDP+*wdF1\#\K-pf"cs%v#^ǟ2gP;.p;}+amG# 8#8q58NO-1.5Z92-A=ڻ_\j\X]Fme͏ uݜq^kJ6nIe쯖Urk.F8FBazg5A+^H֗e)YXFC m^ҷۍYmKGp"i$JGn ֮\1r[Sn jK;f; `d{`cjPMq"vsT rxm~+m4ͅp'{^5h-gZ*}2C**}YFMVQ4daPGv}ҶާhVZMN;Fq|fmZ6/-x佉ۤξA]|?]? Gy%UFg>h"00 ^9}fQ,ysIi *g| xq| 3VuԣN4?&{c.yG𮕧ΒC cW.Ӄ}3WL/+IX rqøm8>j嫍q^ 6@N 'tQ,ma֣|ܺیz珛ރ5!2,*}>Z/|]Y,1Yp͙{Oұrqב*=+Tk ImsC7ob#''F8f[{JvV$gUfwgv,rI9$kקMSI#QZ((B) (((((((+ltEծciRK9G2d1N Fstqɭx ,-R%OnȹI#5?Vi[ f t3=]P-="XI'- )q);+9n<#KXXm sT^7FR푮<) p1v8A@Ɵxr|ꁝ,K>Osɭ6A0@?P?[j:ns4-]̙W$^e3nzn*Ůnme<2} G<ЎB8Q(#H^$G4߳{q1c]nQ_ǿ r#Z"{,?>8OYjWSd/ڏKIb*+~ Je-,(cXHFVn=Ƽ%MJ-eGnFA##e`~GߠշBI)URsОVY1IR1#UvMZ(ѱ1.IZu9Mٷu9W8Kx:c%שmms>!M:+xc!p9=k4=Dj'(E\D$Khc;@?z2^Ȋ6sM6r6+|y5STџJR2>UܝrGs\?f j`ad҂.IA׬L{;;;yΟz5Ezrds hilV$gtrk2W9Hùh~4wEIan~9ePp~b;u=Ԛ`e2pL`6lƭ|>!>Pr#W:|==X*g9x5>vf{<}3\} ZhHrb<NO[^2!=֑#eYZ_;!8azE,GpK- v^:Wp!er ~=a̓!Xxr{s{|UұxIMwIXiD F}?yTViөg՗#8bƧqKʬɶ( L?oYF ce?6Qdб^^..!'A Fq]k=M~ JA(L=9tSʍۂ07@gϵ^[GP[±&vsA\Up-N.~톭ne4q %[g51>S[eM` FzE"E H9U.xA=t wHZF9G$~X1K\,tg6mc<۸*Gw Ll, ?yHYMmn\~GFvB2p1LiW-̔ۡhNܟoO^өR[3ʝ7NVy!sngki-Ȅ$ qt09Y*#Ok 8g2ybH<{*sEwRwH BAN21μ](z-BR]F|ȺV# qRG$`fjz}\O[%|"gǢG׊<gm2y63c8>f_bܟp{hbv%%.mԭX6GB@sxb|NH; Nh c2ȫrA!` 6&-[kHknq[ӫpN+%dd (Q@Q@Q@fotoxx-18.01.1/images/mashup6.jpg0000644000175000017500000007034113222767271015221 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 366 285 0 C     C   I" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?ސ2BD@k~ [(R$&7gGx?TWz.{aaaKi"X`jW9m޵|dKKi "X@x 5 'C7ɤ׾2]K) E p%,T yx9<1kCĺo,sou ۘS XaU.r3rz$R~}G+4Z?74VO{ BdRHebۚB[!J㏅w/o|Wkghmo7o +@d`![qc[_#[S AzH" |;/|wfYjYYi[K["I´,[$gg~у?ſN7P$GDc$rsC+_uI_+W(aOcޏ{Ұ͏DQ Wjl}'?–[N&eO-Rرƒ Em3,X|W{X𾃧YZZp@n f99 ;!3{Kkwi%=AIyOv8j[y,IN[p%OX>{ ugs-{=ź!BFUw,yAr4q,xa'ܤO"Q I8 rI]\o_kƙ}avf ٹ)_.$9GI8o+n|_%@f9d>WfqUㅣ*]#˰rqTvr{\"qBdp=J\+]N50*`(H׹_K4M0ʭq y<:ubrӎ|Qi3բQgFpw>?+}_;UG;UE\I'ֿB 7 z? 7 z,Oi>mAe#mAe#f>G|6KƵ~ҿbD=W_x/O} vShZ+!Gs|6ߊ2s|6ߊ2.|3RV]>,L ̐bB01oJ/|; ^%͠?ʘ0׎Mm|f?F/|E5_cBc fMߎ+w7o -w7o -M c᫿+U[V:m1o| a9V@9Ӽ]cŞ>6oc$¦}k/~*oG~*oG.~z}_oW[=oW[=as9]#zE2}8 AP+ow7o -w7o -0&{q'Q cr\VS642A 4ef g s|6ߊ2s|6ߊ2 f pٚ],ckE)@=_AQƁ*K)ۀ~W;UG;UQ:Q kJTZNҋk>0t#$yj}xP{|ۉ{+#~*oG~*oG.]6F3svj]K쬒өEywwh:OudzKilH`<+IP3f6L >%OZQ-YuP#6˱2z}M~'M\W.n"3)Ec fa5񕿈綺KX4Vm"[cUnj!s8um?⏈cqxC*VMZ۝w'JL${-[zҼ=̓8DErV*zkoG+yϫkRϩ7tP.A'gԁޱ߈z_s e-9 ο M}RMK5H[r}h(89%X }||:vw=XkjVM: BuV?3`ԁ PNǰK[-,Dn~^H3p3V cXL$̬y>w$.<-f<)sj}ig˼H-82yc 3Vŗ/ im)-?P5kM2[}j2$B6Q^Q\7娛|ۿC+ecqslB2אߴޕ|wpiڋG-侽EޒF)\ K *w:?|iMkg{dzt}eYd x|Þ3߄~Zxo:M|o67WS@ TO.X2g/-[iy~.o/Ua.m{ ss,ʱ:RrO|L,Уɸ",g͉dN߳g |$um>JlWHVH]n-Օר?FO9Ojkqæǩf#8ݓM,;%eU!&}Am[_YxJVsKbEC[ZjsD$»N356m]cG,¶v K$+ 7rjR{YhMHx"JÂK.1 E,,͘iCu~j3]M9=l ϚX(Wn[i\P_קq'oXk?4O cQnOSMc,тF2guh:ͽ :|   ]{Şޔ /sA~,c0#$>J:6Vwҥ֩ryxl|{Uegw$wuvk6ߥ{ [4$aɬ|FӼ/h;S8toe,FKw\+!8Ŀ +7o/|Sk}gucY i"&ki"||mkfSm=jo'K2Y 41X*-v1yK_fҴOb|߼J98O=Ozςּ5iR=l̚d`41(6cľ'>;~|] _0lHw,ln W$WJjc6mԼ؝O+.Ou#"+Yyrc 2ps|dzE?&@5nXjzhn^ !09B 'ռ3kmO??K`Y 2K*G qn[+T_aw/_DmgڍV#P}P&E+j^wӢ)VeH[[wsax{Ě/'ZLt[_gh1/9_{MO>mf:<>dQ$^jG,Kl 䒾~%7gougx#ƛ,tYGcՓShDJ#HY\l u_v`'}g|U_~αb-kh*km\=*!uȭ3NAxEoGsk5LJ+g4_j};D-yǙ6iko[h}ms 5<.2FTKz75 ]hqۻylKq9̱}YC8ՊNEj?>ii=ݖ`ID$M ;cOH,|Ajz]徣]ij[$he]I rՋ-y"RI#TP2I'$ԭ!;i?_^'Ach$ѠүuxF5ILi6p8k>+5+\0n=N[My_sF&~7[xUοl`f,w(=-9ioco`KHVY $3m0N㞦k~ͥu/n< w5&ijPPp8+I#^Ώ-4 JtftӭU"$xOzJލkiW:))_0Ǻ~ Z9^W&*ǡ|!MY脮|/z%6ٖ0Y}d2qo*ۜvkW 6W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @W 6|C @y&#ǎ?#E]?._k7W]^[W,.c)l~:To ].onaO,A'_YV_f|Ϲ*"?7z lĩYUQīri ?Q ¿3hZvqX[]fύd. Y#8aЏZ5+ $dXxYkm}حw?@j}[ZVjjvzo/w c8Fqķ񵧏<&c,;:Q㐡;[C)ȭ!C|S6P_,_ ϣ/*XF_Uf>'xSM -d"xέyÿ4[-jI#&Ój[J,DBR6W$ m!hZ+ Ÿ30/?gѿa]'#/]':e [`dU09+o xĺȵ=J#Mݼlk!q,KpQo`) 7 "OoETxKjZW_۳@ֆBJ1T zWYc[gG@"; 6Q،cvOoEQ Ÿ30-x? [~_ KRY3D1X]P^YM$x^9eaQ`</?gѿaG,_ ϣ/*sOG_ʕ?XF_U|)C>-?Zu*, 7 "OoEWyiQ`</?gѿaSxڥ[Yke`ݾ6Mz_ʹ4rLfQX^2Mz 2ovд,#DPK31\ŧiִ} /dTvrTm9B`zE" g>-H{[sML8r22ҫ 7 "⫣dڇukX: 2|5X זƹM ldmVLF+A} 7 "OoEVoOj⇋4ctuSx5K8%wQl|0>{w_ʪI|)C>ҵ7]uMB48fe }ʓ-?yǎ8׏ZVK<+|'}gVn[ox°KZ4iceda}U-df*U}+--ŽB):@g#]2OG;15sWٟxůV,֢_ʳۅ\qD,pe&$>&. j-m+%ʈ٣r8CqkWv3T W7 :c=^[j ^JږŪݥݴzo؋IWbF,?9Pxr?}qyv ]KEE2I=mItiZŽܶi^۳ ?WtAIs&|헋gg%]~u14 s$rԫXw>J#kO6"A> LF1Mf`  CIMcWE4?UqTA(Gy~ =e}XUG- '}5\_Er4?UqQ CIMcWEZOk*(Ϧ"-T- '}5\Y{;]H:^ $U2YƆW ]O@5-5$C+<JCr'`"ѼOgk^[]<ǩE-FtIBW,)GF'񮭦oĞ?n_[xg]ԙ%2I?%2%Kngil[xVHk$eM]g!+)o!51y_Im*rښuܨ`## qm߉ҥX]{\MS^sOu5KQ43)(B} o[ST:_Zz\BR̬dLU## {WC CIMcWEMWޚKM]cZկWMҴ\Fn—<"ݜSNZT_~ۦ4ܴ01FQp=B:xNWz_s[,vZo"I÷Ek|n}6^4GK@A}$y\_YvKSmY$2:dqj}~SѮѬm-Q6F)[ѥP{o!hQ^3ύ.15-bPrLs N0sQ\O?w^l4tV]&F6hg@̛1yeq=Kӯa̒kqf@U0>_wKH!%hﺌݶ9>~i_  4&u-mI Yv'^xo_G4YYk xOLMZ]Y&ZďnFH2 ֵbAo\:-clIwc籭`$=MSnM(<>hI.#E"4{s|KmzVW<9b"óǢM[[\#YI"I>t_ΥܚJZmC? [xJheo~)n&2Eq}Ĭ%%6Am N>?ӵTҼ{muq4ڡlg:@&1%DʍjH}QG x No'Z~ʿ tx> l|^zOE{lbxp7Δ܂01_KxKUkԦRf縊!( V;N,NRY`c@I!WIT4_K[%ݿߏ|WQoڮm$~mݴvUPRI^9| gk+].Y͞c|@35Gw]]egס|_`<{%:L7=u/6AeE##mbGC÷p'f[hP1>7dĴ <KEo+|w< >*x7Q{-#O|dӉ 5$Ur)5tQM?$~ ߡ|/[DsZ7:^jfq[5lI*0cnKZT,M Rh9~j^X#7񘕐 $) !@m>fёΏZm!̬͟/D<pIK)|wޛym ֹ eVf*;A`5M.J_Y^GC5̖J3.I `'~t'v eYYLC5I4dA**]6G ,qT`$ ]ſ>-ƻ8e$Y[qeWS<9X8ʐ$-QRQXx'ND _%72WH[Ed1U'W>ӵtkz|lAԤX>Пh+Gc3 "?63wwZ\ӼLe6;Zۘ[ Î?o-_^֣^X[g_1U{_K8.bϗ4k"ȯ.-߄$_%;8&KUxP ';ҭ@ͧ|U~_x9uGY]7Ltm*. 3@TYT95of/ &[{i&[MCKYᑢSk)# 5~FÞ3Ku5MkGpڜZ=E=;T=vY :hxWtBݭ"#$L#2aʰoN_km-/@5B14t)_.?\femj~ =ACau<רWs?*:L QEHQ@Q@ZX_> ZĚV }{t #X3`),$SU>x5-_K^4mӼEo+4e l`pR0Se|MΧLѬQAK{ZUCݶ߃7hxA4? +B.}>KV9XXԶ1oi!Mgt? л _MnLf ~wI| v=0ƱC~T]tq 6šMIEJZY!_";y 0U=[Xldﭴ8qw*e2H\z ka $1Is2ƲH X=+7ĺsc Ism$*OL?iV/vqgGSE|p~ǿ?jX։mYiij!y$27JH9$c珇iW|KZEam:}24f1HEx9?T[geRP|v>aG: ? So O&_.@ &>%~^·+HZzMM@$nq9=1ۺ՜:R &Ia!mHF{pxoHе%l ݤF$Sr6]Iӡw]CT%K5n>֙C FA@u?O J?7$MqD]_Z֧<_YDh1gr¨$\w<Öz5mGYѮ0^Z|1V#SՁ ${I)Q¥?R{/&Rt[>wfuxrb(K_?I^kVPq%!ss%êqo$OzL Z(Q@Q@xG]*M!8EL1U"7$E; ${I)YQp4?7$xvk tfDH#Ӣ +RA}w㧉?/Z=Ti O% rwtk9K.s[[z4ryvszRZJu<.׋G^.Km1m.I4_.mq!Q-:%k,/ H=4ڋ>1'5/#\xr[ 9kE$@>ŷ>#=J;cW0m"Q)ku *Ŏx߇u[NúeΣru-4=&ݙ1;OlT2|3ZVe6ZWZ-`*2 ?ҧmxM7WX4nWAIER((((('A%kW˧i|B9%*ZIJUFbBMvWx[㾧ڟҵguhTuK*gGDO|rHZN&{o=Z*Q?x2?p?}ՔWǪ>)Bk:aF!y.bBIgʚ{];GZ&;5%j2;gd$g HUǶ -`#hwV> _mCM;2>Q~,7t; xRӬ.t<eef^Tげk?.wn}7`_s\v&2pQ7tRJWn)5=g oC[j+}?_ о>~)x/~ j'.\i%y>\H2Qx612s.q1l,)J+%Oy|@-#yGfT9u\A ܒĖ =A5矴ޑxO V_xZ7FJu’)Xn=U>LP=vI#/  1^}~3dZ].t׿Տ#Q M{X5wTQa/+ 5cՕ=ƫe=0$e؍ϖXO?x2:Y%nh^yʂ+?-%tncGRjm3ؔ5Kڝ:M:W'{Pҿdo|mu:7>W* Q\¿ x_-it7A*jpq*C#$ȶ>т7y`sqٿRR_w{O+ 5c°^V?]Xg M{X5cxe WWr_Ko}wl.&TWuDRB*vzyoj?ZL Z(Q@Q@r_Mw^%y~}w(Y*e ]m9%V7 WZġ-w[b6B* `5WsKJl`. ZhK)3ܦ+K:1#95P+UϺf^ZjjfmwJM4+mAۊ߳s[/<;AwNi Wy+$NpװOwq(G7<9aww~1yOvwߦ #{ᗉ gqf.Ȏf.H1p/? U~OzXfuݴ]O˾Wab(N]_7H5׹'nGn_}񯮓Q ƾO7^/AAOgq:UH`ěI?w<ۢE'Z9p''_IJ%ql@ h$^Q:Ǎ|IٮhIs kqs`/lwy*Hl AW?z&xwH-dvq$q$4+ލ ,+q [6hG-o٢|_Ud ,(3şѿY/$QCfUhڦ\wi54s tq2py goKOLk{$3\YܘX"1 Y޴<#iuMIO>9nSLRz#Yl~ |'g!SolbC \Rw}ѓ3{EѼM {ME&,H2avW goKրk_ /Q [6hY?xz7 %? goKj-o٢|_Ur>PnfKi;#& R##!,ލ"?ywye7ۚ #ySg!,ލ";>koiz]\[x~1K{suumvWްnjI~mHlu_>qY:62#\Ai.lHG!YX1\W0&!EWƟxg5A_6>:Lмw/fj_ qmLO&Tg^2Kiu=GKмE\]r ll:!Hڻ$WZ5wk-\O%7ng(&I'滯 o~qizU4KJ.2oFu`TA5o'𕿇t1K5Kw;O.PDpخڲvoO<jޫዟ gRx7PRI*AF@Pt|9[}I4dx8#px"MUާ{ekZ֥E]LCE T]$ı$W֍~n⟽_3hc  6šM^^O/SPɩ06袊 ( ( d$YJ9;)C P7(&¢/ ~TE B_UZxk-'_1i?Sz?ȾGhϵ#:'oՏIAI5"*N1]=텶i%ݼWV z<_1i?G'IEEY+#1P\VDn> .cU^'SL1c ijow럈qhͩZjwjOҬi&1lL68mƝ2N]\4,/3ݏUz\ǭ$}G|QE}5_b ʲ#^Uy_`Y=oAOCw _rLOk 'CAKh߃'toߗ~mOiŘm/2}=Jѯ]Isqkgiko/×+Yֳ-BDhX _r 뿇^-[@xFԿw+E$bk͋9=}Fu%=]汵+m'<#8`cpXg`M_ PY㦅vF٫ڍ,Plf 2<v8 |+};ƺ ^O UU  ^1um Z4uAj6zlQDD7DFF]=Ϯ4:zt=FGgS:@ic i+$dn8k;_Nu'4ہr.5oL+aowHW[`2~~ޕQ@Q@Q@Q@Q@Q@~ S6ycVVoUEgyYc<[|1h@~>Vj?V+? 5Z4Pwϕ?տVgƨoUY]YY1\, gB芛s 9e#Vj}?ҿW/$Q_cJ\@[|1~>Vj}?ҿW/$Q_cJ\@[|1~>Vj귺]5ʹ0yh!@Y Tjɞk+h%ţcrI2 1_oTyYc_cJ\G|CA+rE[oTyYc񪪶 3il3d?("xNZ,;y$=LA}2-yYc<[|1U6!<jQe|{gR}?ҿW/$P?V+? 5Gϕ?e4}?ҿW/$P?V+? 5Gϕ?e4-BYM&{y."psYoTyYc}ťv25ճdsIY '=)e4oտQgƪ|CA+rEe4oտUGuo-RqY9=?%qW4MHr\2<:ގʜ{P( ( ( ( NiMN+Zƛ'SVv$ Xhe ,1K)7,KHp29sQSj-wOZtJ%8[$#(${SጲkΫW _[*A*yJ>PHkF_ X~iZ$fKFtK$2 w|_'̗I^4#OӵGu-i">X8߸ʸ56DoxSNo_oUȵ[k2UNTwcBH/al;Bկ>+m޿75Y-MxLK f&!'~f׾9_'k:hhƨq[d*<ϼpq?[}cHt=3f]Eyoaq c5Nڥœk^1{"EX OϽ ͞u6gglxG>)~8>olwZs(2Uxz]KAcZ+@(|[PNM1#XFt!bQe'GL]n}ĔhS:OW1IaUG\rXKBGX߶vmR rMm-gx-_e CARͣZxHm^ɴ_&H H\ςJ+;=SݿmV]F([yDU1 eU$M>=ƯnwYK3%u4 $^æ@꿯C#Vl<3^ڵk-x cC E!?6ls2Jr2;%y|+P/j{(iE"Ew"-2sZ|wldW$M%rگ7hWCYA=4}[^Cz=cf-;#b!FBB˂~knM-7_i&23\XO}OZh-2+@Ad]lddZ?OU//ۄ.|1-um4}QE( eX$A) V]>-4{]kBo3?7+9S 17%9JoRڝVq>],[˅&Ğ@8.i:wzW|24|696øͰkJJv%+!Yq\P~ $֣N}L$¶oWgEq}o¶oWgEq}o f^j7(O6^PdNgbKGD pHYs  tIMEθPIDATHnEƿa-JPNS8 )ϐr䆄YH|X&8Lzl% nX0RggkuUuKrK7 eHESqT` '6p22_Qm:nSUPU>ćF,FڑؙAEJWm^L`u3ss؄ꜘSgH# 3BEDU"]L|qg#`QP}O>ә"Uս{ޮͺ?;RFf@ cL HŰεǭ |^Ǿf/.#EĤiz+h\*q΁I_}RU5ǘ2v4cZR5D^ӧO?}5g;|v| 11 1@sG!d "F3,gsj`] !^a:@#iDR,BpN@̫aq $93jGpPZw''Çbi`m;7 $iIH(`IH !U E"u3@up@L8 xχX. W 8:uco8:*uK:m˼mM4z~~lб,o~,`RǨ^ (m\\ڇq '~8pyv~iqjwbG@@•Y!X0q\=\!!$ b։\Ӡؐ<8aR1勮egN?)뎾job6@EH] p%&* "鮃 *^O⃨ bDQT>_q^p%ZP:ήn~'MI+y#CzX7("R D`CQD"mN+Ü#pQt3|dl^uM̿_3M\*e41i7[ܹM|=K7ĸ5\㹃+IENDB`fotoxx-18.01.1/images/magnify-image.jpg0000644000175000017500000016515413222767271016357 0ustar micomicoJFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 805 1025 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((X" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?R7iNO̼VY芓g>mRA9lֹjri s3\wC%HtW7X;Wr O j$*D6+D)Ey#l dF8w $lN7F?«F[$.rNj6~7@$s oV9/%y%F:TY!ar$VH1Qy; HZO=~jcI'l~)$akpE޺?6.Q1? $܂/X6Jc}&$hOqS&ڰ.z渍:(]$Oh\rP] 8ʂUXpX@yIh5n9#zY@[*ܴ2 (OL +763"m35i\bԟΡ4:e1N˸ «]5#C, Tyr)6 @ךb;8#nUc`GsvB J&ܤ <ӚK =ue Zs}xAq1sC#Ϡ' {)%' OR{Ip֘妓 \Vv#Z|{O^H􉵁9֡gE B-mx>U-{JIFB:~bQSd廏S"ʒ9TUv@VS#hgnY˜qԏҤI" n*wb[LXp3U}X<=*HzԨx=Fi 61F7c>[x§@!B!Oޮm BҕU@Sި6@{UKme%䈊őN98jĶv4jA$Ce~gG"Kj;'\:_QEmǿj%,99q~Ґ#@r9'ғ#w ӹV~Z|9mZ?!|X4-K'C6㐧cWB~A=?Z*C,mIo~QrSƪxȮ#p{;MKm?f%*GqKs$qeeT(*rߙ]7o˪-Զm4N4Q[ d qeiVeC0}yǥ4&t'k'Uhl(^\nplޢV6VxIhR!|}FA鑜U[jMC:vљQcIpFGixkb]F 繞IYq98+^x^Kxhu}6춠d30 1+94k,uӮ>T1+鑟Zi6tmXjrçM,Y\7-(Ilv5s9*꤉V zdr~fizvuyZV%AKHz<9y>4 P2l ڙxIe]jo~f3ec2`#oAdvMye4+K7v옡)X'hL}.9&Qu&GF31[x"Xtoh˺.ۥnH^ILW7g}iMj1Kq׷})eͨݱ^jji aFv`co~ἷ'I<+u 6dK$g0rNMA{i}(LQӈE{$$bׂ?Zs-6\ۤ7 u*ːn;Rdh~5{cMΌ#G'4LL#hk-嶄$`1®hy^v1vJļkE U@^ןT{yUrnNQaH1UwRSѸdD+ yVng| ̧p7p9?3.6*e; G<ԉϷJՈjwR3#o?:;H XdsKGq=3V5wp>Eg q׭I2C{@%p0Rc 8@xyUozbN\VcF0Z -c rj=8W,zR7 _KШ׌$G^>fdn1R>1)Fq֥2*29sOǎ>3SqqIvMFAsS1FUe<~R4$ 43𦛶 0$v$8H,qߥU'Qwql%9+K0<xp;4hI8z]ɵ L( }9#8^FqW|3k l=A ݚFX:t˶lN8nG!Vbi& zuB(MƜۇXEjFvJ@232Ю`I}-tr$㺂2OZm6NDXm Ue?1}jlHKr*␬[Tg֕G0{h$h$( ?҇6I<}F=?>`A=xW$Tx*0N==i.ZF*BQק'"n _z}2, 0EI lb29#qG~ܐVŖdm ϭUK|#?bF) ǷNb0gOl}*$oN(uh58Rl3]F\<7'q'=8ӥ8yJ1j$R֫*f^}T^OۤGHA^s+)67JBDdcV}4,8aZ["׭2oripcd܀-v'> ѐp2GI%.Wiy(+מp}jٝH]|IO9%mtpe;}2y@gUX ꠝ>l i.{PnZ0Gb2pGև!6lA+@ʻF}G FNݘ VE>azouA$c8+T~-"6esp̼ zַa2l6IT0=9[Gsq ?_??GO'_DQZϝGO'G(CI+(\!r4O"(CI(/i?ܟE}E ;4O"Q"}WPEA '!rEϝGO'G(CI+(\!r4O"ϝGO'G(CI+(\!r4O">w/i?ܟE#Hd}KITAnMWԿu\A.|jB`VJlrFzU9ON@օXw,^[s{Ԓ;±]z|IURON+cZTzO[;!>؀rFzd‹CLÎx散V,Tq sߺ8n?0qz}^Kyҭ C =)Y# iVbr8 d8lwUq'`\g'sv3*ųe񜃃XyC`1B߭1™e9\vҤ>f"B?_zkSOAG2@ǥ. 'm,nz*G%XS*SF3<Ѽ3Xz)!,Q1uF .#s`N~sL w8U#T[:}2F]ڄq>HՑ:u4a#\H~k쫤+&Ypvc\e "ԙXˏ!sԠfd·R@-(R ߊ*mدId9!AUC@^R簨vLkS<#۴|JlcXQ~\V٤vnz?_Ρ6@Fgq*orQdT2(;#qO)GmGQP\!NT-8> X|'`pq*PzA2evqӊl5+,VmJ$!r23 ¤^ݳ[Ac+a7 |{k;<۽;ŒN3G'Ec{xwAdI dm)Rx/<1pGiYZբʰiGvqٽ :IJL]ekqGAXb-Od/RXcgBQl3;Nz^>4fD0hcyI;ִ d5j.VbKT@9 Ydi {O9H5:mKD{[W؎G8$gdsW/MЭt˨yT>R"r^Ch* ՊK(ѰR &9<Twv溅3CJaUcuy?=iLLZF[sPcx rk&_b2׸]]wö]2GR(dL$`zg,4&{Yq$^GPO8+P.fvqqwoۿt:TfrZB(*#[Zo%] y=߿9RIFyՉy%;*N>lq7/$i HhZъG&'OùfToԜ}9iڝ*U=0I tͣAѰ;@do.`? \~޵4* rG5,hJ2\T[8\ V$=03@eȨLc AR@r9튂K9"nJAWR8 (ƍ#H$\(wѝt?5f`oLVDDQFHXzs>ZsӁ4IJE &} )#HXRo^I^6]5 XƸɎ0%#RiczT]ϛ}zq9WOik&!k__^HECG/@.&l ֫F3Z+"8;f Gkv*;v@Ҝ슌u3t)q'Og'6ulul\cmNz&ۓ-t@2>VtXy4sxi ~a';3Zz~7iڤ,rg?jRpnIHMMnFzqwy` S3z]$VJzOcKյ Nu>]׬fTRX0ˀ}zU:Mn`zIsA!ԖK’=qʺ @X(VM5HJNTR0@@=(-ӭUM=*S nbH9*3!w"l ֬l.׿  ir>9ݑ}kI?T' lpW4l`A'$IMcdĄ>XbUg?ONx F"hV%Z!A}z#o8,3!GMsV.4L2y8ӵrXܢ:q]ϐ{dgYFc=*"N]x⸫9BT6s9kGB2U{XFNLd'Z*7ζv JTj!AeA֧K`Le'Vgڡx=XJT,fK ޢC؊ti y~5,khFNU-J ˞))C.f {E?jX#?үϥ]i֢DVNҺԑDK^IEX0*sEzR P@ dC*\Dffan5|?P oA'F;Q[qh"Y-uvU_Wweqek ĬE&!eʘAFy=wĺ6Z|:Ą_Z$w[v԰\樯|'1%ɕMʪ)߹'׎x^n=]F "}Wr KLqT{Woh~he)$idG831,͎'mr˦ ѣa>1qHSi'*j0Pw^V%dmц$`/='mT%p!kl9>յe_ еf? xIK['to|/ޛsh]nGWbrmʑ_xnLVPn.,"erW6i~٬juymg4 bGLԚ A 1%хIӝ͸zZ hw2Gr,׉`C%Dq;cjiEAeym}es ;yu$r:vSQES.8C ) +HHٿCC-!X5//QDma\8䎕612V)"}1{[VMKZ- Ȝdֶ{>[mn8']JjhW-Dr7"sn\\}3\ ֥ ,w€pqW&Т&(@{KfnT #ַ]ە$fVlY vzuz.9f8ÁkĺʮO^5jQ^MQi!2|c8{U9RI c3]=΁eJm3~5/_SۭtgUZDM9ڌFs֞cލ\Nz~8̬4!UH8=jdX d~Rq#qקxb1\ K)ldPFFxVn#etIu~1QHB+ՏmBeڣ3zZԻ Ɗ@߾kW%е^tjmx/;Iq_v\WPL9ϧaM-` $ڹQ4&l` :d`q|K `'`8g5} 6p@2y;moMW-KM>}j289"(xV9`C=kK(f28ڬ.$T)qll-{s@2ԕV"j ҫH 3֭^$9<o^̺s$P.vsZ{C+lO-epj]y5͛ D~֘\;Ԝ8Eh 4OCK' z{t'd}=Eyޚ8scs4ӬA}7PFa̧QW5XF,l(#FNXdw≘#5xX/`5ӯd_J\Qzq\F'wh2Mi"W2+v`x%hۇ\u_O$r8hE𷘇6:eE~s|wo*F}롸'|v4Ř *+ Pk?jӠ(8P`z+klj!ֽqF璸P\m.>5dQM$?=in5=>`0G vW34EvddqEȣl(]7"ZrN!5[9װKF%ߎO;DN3{|_JB.oѷިݪFڸHPFۏ=3T%-EAԦ8ɈSyP'iWD$HwAe^iʹkn͌`wV"Uc6;-ڷb0Q #~Y$CY1I9Ka:iX#n:s]I5#;ҨG|ASBul%8X$jkXxif$ex2D *dfPw9ƽ>k Iﭯ&K` 0^ nO*Fj߇tK[#No^Il2qM-ΏZ:ږ*FC |k oE[YK" .99? CvMgX̷ l$SpNH;ZeBj6]D%T0YO #=.Ay~~7'8+?ŷ?˫-Fx ,F8kٜFX9WKYKC-¥#hȊzldP5 ։]uwMs$&fxT$hz _AmgEե #MRp3jt&mH6d@Pw3u9xzv-K rŌvjO.Vn4M2UMp$94_P8]Zյ]:}}c{qn.? qN{;2Q@%dPr@vV6kem ,1FQ7`JD t6r@fH2,t'JU,+)n'&#9l/$p*q%UMcA7 ?V궥:q|*:|c]xfֽ.NӢ{'lq\MG<E ;o}?VHbWuW9sHycgG,,t & 휓+R[I~!gs^}:y:q^Y|G I)^q= È^ψ\uE`1kؐe޸žׇb+WNuM=8j,E#)Z/ ưڃqb 6a@G^+w8ҷEKyloFs}.N1ֵϖC%L209#:$r6eFO_mA :UhK\KN=8:gm8(V ZE =6l33<r+j Of={ tK@`k8#nHa?5̊V^B&iwVrиʺX=J~bCww:x{ZG͇ElҼ9k6qF0Ptڊݮz²ode{gz`hAg~5DrIPbr@\vfo*WGr_(֧f,s1\;]ē4aRIш g+m6#,[k6N =WG;::`~M) 2*ǿhh|`Vsq<=qk+>+k+?Csq]4䝸#W ŏtlSQ )JA5QxVPԐs5|O)Nzd;Jd$g3h^hSCU$jxY$.3-w9㟜Q#J8%؆ٷvQiw' ֊/,8z+kزLZK)tص4?x11z_dfu"5Xy+۾h"Q\+/ZZdI+#8`Ax=;sG]: ;RHkv4(LH6nKptMo[[Qu-4ZQyP\*$ X.ԟW6ǩ𥽁CnNrW S'!^+_[m"wu)&3rHKHbnbE6@08*1Fl0E Pn8idFp{Rw/j;_,o *ƹi%ťvW[p%+G1`)>`P6^]K#vIhKI-:zV͝xF;!Ԣ+yIa=T\5I|x nz&i(SlQs1 .Ȑj>O1,9>Տ"Ŕ]h\(a]rą=VĘiH H#:gbZfMB024` n8%}W3Z-/5kt( OӤt]2ml$I=֠}*;[pH^YVcՉ뚺QB=i6lB#ҤQ#\1تNA &r`oG#klֳ҅ns#D883RJ ]HZm_˦+<9*zI|@\g$# VzN}41nh"1#Irn>vi"Pg ykF7~jT,6WVKxdxـ)j ~tSv`zvʵM2Y?ٞ8FH?tחVvd4_1a!$>o-вI%q?yܚ=ռ*BR9\Olv"ɒkim;L'VetaBʱ55O- t ۞蚌Y$/J6+ -P<7WdHӦs#Ҹ] SmFohfYvPWvpw-mso(G!d#|u淧5k.k rOOOtF\d,LB{շ1'8evpk&mx FS0vpfl3c;3j63E-M6%{5dK7IbSӯ",VVT :'sF@BXo)Dr'标r sϩ)H%|˃1ҹSw5$##3bs@z.+CBFys5aI6Nq%9TA*Ih+ZǼVbKpce|Iwp6WUk 33,y-D@o|tbn nU?\(SJTǫ~841<~ˣk-6p3ΧEP]"ЇRT.2>K 0@L^9]?Frt q'<_)sswrS I-j磃jɭ̕ngcto%E?H$M#D{*F&4\ ((()>0Ny)0p=9Y$6WY|I 3n%`q8zz+ʯ [v8\z:z[[<2Iγe`H׌W-ImSB1q[QZSWs<9G`@jڽBQc2> lUo ܏B| i?]n $0\rdl)Y\_]$V ڷP$̻N,dzqW[-pbU ߇53v5m5[ /5S5׭iYjoyBdB3'{kK; TIm#zu[JKHU7Z<(>ԏq8Y # ֹ_5Ww3y3 W`ז/It bݺ瞕~IdYt&+yP;syCkk+_:1j,kWtr亟4cH&3BBrx9R\=pLl Ş=NGjJ qNJWڅj\\2^3[ ҄v] pahҝj/cH6$^z&[TqH[n.(jU"w^iVdj,Ľ"8 rZyx5LqY|AZjp]0oCcs9j`{k8WTdRP>_ ttA+]eIyhjrClcqzW2#okf5(|naߞ?6=Q[u.ӌzԸrV<iݭd|W1mo+aq"'MGJiss;zLW ӑںa'c[ >[J5oPIf s54sb$G#1aۥky$)`z5߿Je0l8$ȄgxN)\):,H@z-u$`sk|{oҸ$@|HRΙ ~%CTʜu=5w(m/P\(Oqئ /'8I4A7?k@)KzVT%x|>{f5p}k}Yu=5To*7]Dw$1dz kΕ7cU#b4Dn99ːN0F?J(i_Y~b תiZGWLdM ]9'z![QιÁ^GZŨ閏uAijzYXdg }ij|W_[%ҖCR zQâ_IX\y#fs^u6ZTMim";Ie@H<\gUx/mn.nm[bh rkv֔00\qcc}” d~SZMrK}JX]I&iyqKrfcbN@ 9Eyw=>cy-Է[MI"m Ӽ5sqy:kmwhWiW.K.yU9/fIHdKk0$rt=j=S#uyӄ&HNcyǷ5x]Gũ\Za=2)U`=f{tyC228#?i-%K 9 Ϡ\:$]E`R)pyzT񥖣\[k4 L,98TG;IX "E )AA$\&YKHcxd֗Ikȯܵi"N>R?<d '39nl撴P5qT氖J's#,O)f! r}c+jpw>Jx}O5u'ʮvw Xmq›P0eݖY'#2i 6AV, a=q\P.oYY#\LS2e%y㌊ӥ6{Qa]_`YiXUN5rf䑓dn#'޼Qq}(+r?w&Kxט>cr{wR^ v񲟟c rj= ʋ#k4vCѝ\d O_ƽ&MgNkkYg.e\ gQ*vw:J2wu6;@s$LHO^s\-c}g"I}"$}Ӂt{"sӊ4@sٮӮUc9=0 rr\Y?ykiߑ"cbݗ*NA]{؎u ^r{X]?Ͽҝ%r6Iu#\{QQ߳Y:ۀ@}讈Һ9_SOۋ$vֻPH AZڍw62. H.{vvGw$#x@CڕIMjsΚ+[kiw Pnz.U #N*@gI0BuW~'o ΂A ha Fm#+1oS5P?I?iO@[iS֯q;Mc$߼$dm 򨴻_WK-,[i z8<~Uk1I~(L~߶ .ur4o_/YiOGcPur5? uWu*pdb8 TiOGcPT?iOGcPT?iOGcPT?iOGcPT?iOGcPSWMpԿiOU)m*.r}(O]BAskҼ=[#pT'<汼 ,Qv2A9翽zcmYfeqܚkVwҢnK C3-fE$15<%N22+% 4PDdq_NFf:J7:XW =Hp=}k Q{ Elw<ԛi\CHbEfa zw6X@@\jwLNGlTQI9|q$yio%VC~{Lʲ*;4gbQZ;hkk&EVk1QہW</J|y]ݪKD4%IJguR8n=i6R,Yj:\pLx*+X07 w SC3Zv@jԵo86B$xgQ\spF\َx,vw#mxЪrF?Nܸs2lBNA㱦K D8q[ጎq N:s4+n#ZT2Xd㯭t&1Qzr{E#uZ9U<>`$q=z淦A}G"3k}-]e>ahJ^ ˹yUHcTxqWa\oC rAƫPkR _p}TGKlC սX_{i\8 wVFYl],@hȹ>Wa/Rm.)YL;뫛i#D =Imu͞ҹ{FRgj.f\5;llww(<sekr3ZC2l*qǽ]GSQ˫hvG#>Y\ڤycK33 !BB8+=^ )e=zr?Q[SalլfqDMTH9_B40#3[Or3xր*Fq;ݼY=U5ԮieNJ>Hv!K&B;}:B&:[ ˵w={[i[2I`RRg2d@#F:sj=?j'mIxdlLc_^rt8bg2/G:I\!pFIZG+h뙯4H AldM{WE`Ń!,!X|awǭ;\w6̳/ =ѕA3(((((((*;bi'RtV[Z&8U@< $hϕG/\K5Yk MZPin њh7AaG8fsW`qAW?2TLt n###ohnAYXnll[1:.1x US"?CzOk/Sl 5J/oj|?jwB93[`%|GW.., $:ڹI~#u;Xda'rF;G*^Yy|g?oXjOsf[)d,z:jŸ/d@Z@= im<={ BK Y)޸h"Ѵk5 eV { cvrkjI$$,]^(jQ.[[* zqҪV9 W.\rJH|[lTcPJ']L-aǸw,EXgkkp]90K5n-dG;N10Q ުR3Q-'M[d","dXЀN{WuyHB{Z㩣:bag/=kӬ÷AUm"Gl 0d$ @FHfJkÞYH*!&݂98{R[9ޑ8y$敮U2ue0Csӆ[b2 E#䓀8<{]F-ݨk$4d*!GrΥ.]Za,Ztk+H$ǿ >fsz{WǧG? ʅmX{QZQ@Q@Q@Q@Q@Q@s!S\5!Koc;7F}{wt}iGy+YM9^"z@Xoky..RdUH؉fۜ q97[5cgȺ"0T 5y^!+<ώ ?0@ԧl#|UTȓ 1 QjN_GiKn"P=̊Pv6 H8ڹrs^!+<ώ ?9 ~Mm"(o`o>ǖhi= d簰h׀u2\~Cyҿ>O3Z[imL;3p xf ` 'pGq^z;]AV)~L[$G#;xV"H,kCbKc2sֹy̚q""`HqU'de)s!rde8!{&;F,Ðēz줹G dWkzA |>E`=?Zb|3HNFܞs[D2YLOr -i` =g)%woe\g65iqtW;2N9>FU kT՘1o?@(F =1BVu7;$VDIWLkqxqs)vL}+UJi${ w**o;xvi0= '|E0^lv{Taƺ@scA^%>9(1:feeؐsڴ/lt]X4W<*-|bl&#G?>]ς HYr 7~5mT-Bhʞgھ-R(67sJPTlpxbWkrO,zo%c''>Mnfs%%GmgGYr# )+7O2jZH`qk0w0as !v=2k:lWe ^}zW9!5*32:⯭r"nTsTm{ϥ=4i`RcVm&m3b#vca)/Bzǂpr;֌rkt${I?pxQWfұx|xg$Q噥qQ& @v#ZIq3^*69!N{ A3[QVFrwczv<(:gR \H{*MJI7HfBF*{w{/XCpGLs}+xZRw _+e O2޼KGHgS`GpTp9Q 85QQ6mF*8=q\ qt~$=:Z-'# RjLTIkbM^?3O2>ʗiH삃{59ᕳ skn4@Z(rdj:& \gsiW%S+b6rxd#zM+QkWj6#|BG_| [k{U%U܇ ՝&Gx3?ko^%$U4`]R0IӚb ʌJnBU]L[Y[MA"j/r(^84*_ZOkwsXd`61WYkS1x2bˌD* j*mJ?@*{fI# V&CQCpJO$g>՜h 4L@v}:m4 9}N1TqBuq}j~ TgaH4E6ſܺj ⼢P 8QAh$eH^~r͜8Qijg&M 'ץ2 9;0( y=1lsLzУs3JPYDN܀'+K)FBX_|N}jVEb#k~Y5#Cp+ybqnƩćVi-x(b#tLc(yۿOĄF#y"irx g>ܴIrȭKkdH* ]9G[&8rαQ¯qկm:wT0ojLJ|5sqqmNwS?8#{LQ}}V| <TQrЭfG Saj-om≠3*imԓ\ض2Wgc+v)#qeԎvݏ/ rv̄f8DZǝ8 =}IMVNM u4[Cn b:3wԋV5Pi\!vh (l*g9k#7y,2>I<&Y5oDye 0޹CE׮<1LrC@>\VZ5.8͖';FIa'xڠ+^eV\Z7>\2ǵZռ[ZX$mfq.mzq;M.18֕7I\GONڹ]*e+`tr]SX ; 9z  ?ڨg.Gy* v6"ЇF,rOTU(7* dg'Ki儆alu=wgO$>V9vt{& n bz=^:FBFﱀ9CktƘ^Lߚ]X1Hhd^{A`Ӓ=1;Ta)kp5pnp5A%qy*1ކu,A\s%sO~u4lTXp8ki+۴qb;pU(S6XԂ'ܧ*knDb4q@ss9I9Val9N췗4I>QlqzUM͓ӹ5h\3N܏t=Iݖ<?mP3K` u#ꮘ"lёpO sNGM5虆 ``uNV%m`l)rq\l د#iӵ"L5HC&x9+;q튵 ㎔v8 ;wSppzuC-v*M^Rt)8"Y:.7hU' {cVitq VďYb]6 s/ N+]IkMիj:ėzwRӡv_ homqu1۔PU}*ϙؚqEy9 Wƫ4lTyk$/3p:hOJ'$8qXՌlpT/o+"sל}kKz{W;7aB.ҵԙ-ORr['ws+~\OWC*H݊·xp̪\F=A⻣gKSܼQ2?\;°_`Fc~5&n=+ycE@{l@37x=ykHSQ!\HdATqJ0H9u\`w CJ~8v"ٵ 29QzmkS H9ǿS\VPKvI,݇ƧJYv=랫sA-}\}ryiĨB/<ޜuKD0k >t~ %I\ҥciQflNYƑH/qГڴmxRC wU/&6s-cE+z-<@738 (sƸINm)4g\z{cZZΐ,HTpr2GkĶW 9/sةՍJ8!׫)"ܮIzqֹg6(ܝ1lE '&Whې5iȷH#q9od߇Z٥B.V'1)iGDt~+(dyߧz}^ilH Rqz\{=b,䟩Eel݄?T9:Mck9f,p$+CUmԪ~,ɚJk""JE{Ӷ xcwG= ;o+&O ,Pԭ,$ġ~bpKoc5Q`*icy=46kvVRI$ϭC#4l3sڒb3ҩBȥnCr"x[Pߨ\-#:0Nx aszq]x84\CraӦes6[h#xnsp^)ejP@q׿_zɯt6rq'9cs8v5rbu޴:fF\TYǭ>h܅Fsej7 Hq}ҴPFvzj>I`2c-SV-]ʰo a%aLuQN-0@SrcD ٞz7zM,ن+GT{` p{UՍĺtvXZ6]uxpEW=Jfhr>Pkk IOWok{Dẗꯜ9};װK}#XLv>?p%=49f ڹm˼`H Zm؝'n#<{_z`s) q;SkC#sֱMx?1tb+yY9S^ayJ@|rAg#ڜ#v9HϑݘjX nx {֯4uRLvPs^ǩKN#c `v5J:9.@!NK;`9[k`Fz4'>GBXAkq5-׽f ,z͏4lQuZs?(aǽ+1SN[p9'[1yS6!;:x'UvNM`xE$22qfz6&H"rZ:5tMgQ~h#\j`"s9 Uq%ޔYۼ[ٻլ@cR%b\&ՇMy*t>7tl:/fكQ۽B8=ǭlja*cyUp^9K rG!zסY\31JpfΊ 1I*qnua4]iJFGN*yW45`K q0zwb WUK8PwW V팶O p\&x=sړZ)6&{aeR30O]6Gcb{WoH.vʺ}.KdH. H';p;&kdg⎤V)y CY\Dͼ1=c№NxBҺc>cB̤IcZqYqw9TԀvq`:dҖ ;bw8ڳ0 =L/H_ I'LVК"HҬJ[n@9̻ qBSyY8U\4=.F9npqYzzŐ)Frp:qvΜgH뚂b^D/fl|)bO@c^Ԉ9'(|!'=)9Ol{VnBԃrV^`" lיZ^2(`wzVnL0=tnsvk-Ok~#QL?^juq d`)$+Zůj7&Y]{`cZ_rEsj*LS0`$$p;;VЂIx9MY. C"uv@"SJfm` (\tCsUأc&xn?ϽyѕˡtC%,e pxllER=s1Zf0@+"i KK"r`Ե$QFn.B\ghz3Vdo{ccL䞃꾓7T=^M$ \({UJjnggj7ҟ-:UR7 b a'zWgo7#nqpf@=4I pcJE6ʊrH)1$:~UE/hwE-"EaH[޸׌Ӛ$ֈs+f¬i=('I%YV·خpZ(_$E<}k5&RZ9TREżAҽ#{UAwp*=8)5XK(#5|? rP|ӎ W(źdkJgi.p2H}p+"EIP8u+ƿpmQ[Qr{nű 'ڮܬ$f^ydJ/"8ҷ"/N*v=^/%.tx|ÀszsM< *cw>+Ҵ_LM4fg23j`\ȠmrOq+yny_ '):Y,&ѷY"dnI7]tڕ1 VPY\r0SWٔ< ]0 \t*W#.k\< ϝO1Zi[LI jkv"C溽oWVc?{oU:h`Io$$x 1;vVݢg0ķpn]t8qME{SΤ{hyTfmi#rۂxߎY'fɦ8M{U{F7Inrvv.G1pEz6|mU?4s_G/-x=*Q<m RF:}kuл#R)'㰮no } H#9kk3 )iZջ*Dku[מM-O4`Fss޸<5cy 9T }@$+ɵqڽPS^1 M {/Ao<,82`xQhr^%N}R0I`&PBJqZ![\Ư!cn'cQ6mu#U:Iy5ۨ|X!S+A>jCyjkFUrNy+D!E 5Շ/SہRrF9kѼ XMr3%9#?L.Yr8#UhΈ@iJ<╘"a]ŋqX`CNkҴK"7X73@q[Sv.W'dg\Q]΋.IKN AAtTZVD-m|la};L~#o7"xz`-u3$J{nfykq+='`,P x%O+I08 ~)omJ xJj%PҳA 9\v % m$JbCɸnG+GO+m +}5 vfi"{B ;o_J1,(vN7dAݜSC]g`>c] +4kOO>nU:DiѤ,d(}я^sxڞ"Km4Xh^ t6-%^ T(tV2Gj?ڼԅ :rG^ziݳoƑ$VvvH5xN@ilN>i4!\@pz<҂:T4M+rؽ./ԸǷJvDzQU)ҙ5MU*FyXcke=ˡأ hAH$Al{su]I\Ԉ,?Z֟>$cU\1ԳMecbNjUnՐ݄h^Es\szκ ?PխR{i@Bx e$X,n-ݳd©iܖ:7C TRC~k(\7F8d2A&8ֱ|A$H& khN8xdaBњqHC5bo˨e$8P8JRH 2Q#ƻ71Y:.7L$c?Yy3)}W^zmL-"i.5 3_0LJHsH9W\N] yټ6; 05=^b|JOۂ'\97=zeoYq$-Iu#RB 9޻#JW0 T0-Au;tҲUʜ``WBfBS!swAwEIXr{z$۠# ' zVμ`jx|h I݇rیw9wTP #Y$kub3 <Wa\F:RJ@>Ya<ߥnˮHv }8JMF(80ct;8&+drznLXuck9[e&lrxǧj&RfwjVWEKC#ߚǴkj[/(/ BG}wbBʠ.F;zR}pU[3ԟNiilt{M3wv$ӵ6e]'Orqu\^Y YEIۧb*-'PD4nCg,fq1|&S vsޣǖU'L1LG<*d s9ʣ[yi;f#0A^,A8Rx?_]9kLWWQ[4(VYP'5f8=q.QH$pK]nI2Bt*/x|n|==+MٓJʐ!xUE~l<O"og'ҷKRxV趍a%aM۱[۹ӯW!aw%XIMjtRHchoQ$m`HΛ.5c/qh̶ВTU9UPJrGif 6ַzlV$1"t %-F9sk}v%ǂu XH ։,;{szT"i檆VPx}MCm6o'@^=\[[ÿ qyӧtﵘ[Hc8>JHF\824^o'oWːnWϯ2-Ky'6rVx6;GcJV`2u. aC=qkė oqb<(scڈR7T3++Z౹!dRiMZnl.#&ʜհP+ͦoi3v\$l q{ޏYj%f\7O/"R%YA%^cb-/[HY>dp޾&mJ'= 67дH wtMo 3WHdE(?Ü==W.fΈX-Iu d8xē[fA0H}=WU7eܲ%=翽pZ5> {ff];9=?3]4a jR01?_\B:q-H$af0\M=ں=,-4tp=rzʲ f=~x^&Pq'ϖ7*Ѫ#%䎕K}kx"2UGyϿ# ;{ڲٕB:IR"r=NCZj alnq<0#tOM1.e\  c(ԁ_2{8}^Kc[DrC6X{!7l' w43Ib+QJG_m2W_|r+CM:˜^U5y ]&%rrs+rQkX·3G4*Ze!sGM4\NOoMupqYZRD+c5oe'jiQE8##=?bkL[)lc溢e"/ yz-įqi3Һ;xfF)@XDZkuD5իaeBŹG} H$3SȜ9~jD@tƎ_'+[jV 1̽=B+cW~Fa-Ⱦ88?Y5Y RAz\o{Q^B-I$^9ǮRwш̞DMD9.IwNKp**%ZtH9Fu5w҆VD44E${. `_jKC 4lN2sֶe LⲮP8e Sam.;(R1\cڝ: ʑil勬y N0 tr Af`]6Ǯz⡦.d˱b%1q=W5<3&SנC0hIw<92hMn[Zo<%\Lrۏ_|lUaY1Gdz9\ıDJV)VXՒ5,Cs] VhhsR|$fx lKC3 \UlmBaqq ,ݍ}qLӥ['oG^WK=(Q\WFXLG MfieT[yWQEp[/=8۫Ej#v9r~'׵]2Q"ʋ#*e&aFH4a߂k[眑,=iDr\Dm)m!=N3sVa6SH'+JDϸ;+8yH8 ۏ!2Gp+Wcmȹxy$lǟj^\i^y[ קz}R*޹NNOp@bTq\B{F9P*3fI de9*5$-ǔ|abHcu=>[>9Tdčgkk-Q ($0qӚh~.5_k]€0QЏ_ZFVЉ#Km=s2'Y:uV}=ȇ#G{0뫥i81로"pk+kX8"M\N*:{&@ipԃ9'#楱67 syByF㑞x)=d슊?^)p0Fgf/-(r1XMuvoZo !< "ź|Oy:c+,4Z93לD Ԋz43.cץTP*1 kث%,o hʞhȃ۶wd훁U"YC]1%sʮ1 24i=t(cgp2ϭtZB[-XQG+Ѯ࣎qNj735n_۷N1)=No>1YF@O&(P<6N9vMJP- \5`"FmƲ%gp]KnED7DPGI&6j߼P,ҧ"[B15m}nફĐO|ՙ}0kjQF~*!w0[K}-In|vm>.c7&pzzbiW":F=:@M$Kbh[$6amوgF8@+>[ [u,6 r zVy=y c>3^|NC׎E$iUpq qcm .~I>+ )-f@Zӵx-1(O~G&ɫKPZ3ϸI$! =ԣ^`#>N3Ԃ =@c4 PIxx3V.ňI[y/rx$sRiڵD*VbkW~ ֦`ivhE쵭. CLY]rA{J3T#ygG#\晨xOS[^}}D;?CUd^ <:If9 _(ë"*è5{;y*;T֥&lg0'WL=ԕN~ufOp~e O֓llȣɞȠ1162;ґf{y-`^6bI¬qL RM>h'fΗVg*T`}W"XE"dSdk񭪴j&{j5&bvǷ>~]R 3 9Q _2 M ʤm' 3#tyr:FOj|o\Twڳ7Q)3\G#?5]\Gor\vA 9WAB98ǔq(q887c1\`W,⩈cN+Ks2FHwg)8,TN>Ic4oH[qK#HsVdi y"|fѵk"/CdU#N3ZϩXM)l q TTm>ZT9<~U66!Jb}x^6Ppx}땂 ePrMiDWh'8㊨ǹZE1F2Nhm Fx c}ˑ^N&*歔R*nMlAxUj1+b|S"}9=d-H';4+[pZ;#TçjIC.JEy[k>V5bKJ4`庒. 8RZʸ,03>՝dai i3>IB-(_j {h{yBZ3!f*`Mkgy* '#ҸKAծSjE+OPsְ^k&m ڻGsv6WyWS@g C1ZGm/4aI'1ج㳽nY/ ΄%pz䎀5$LԮz^m k^kOO#v:c9kug\Z]hAf\H:A޻hP 8rY&Rl.$QA=+OU}\קZ@j%͘{ >$ .I#b=l׍[U9Q=71;H0z}0k K(Qgش۰ OܶFH_ "$P1\i\N$FI&]bǯ%֑XSCi9 ~KB.qwd2CJr'ujxE9<p={S2HF^KxoB{r ʂu:nΥimzyzA zJ1iI6tq*EI*[3EOoP֡ȓgqیsy06ϓ; SvC3&noi) *:n-e?Jt:&nfB}:Va;ʱltCOIh9+# s0xZ5T7^@ݽ>v O9̋crC.6gj9'U4$Wg1ܳ]JiRyFGxvz|%:Yk8+.^ǯ0v{]J;;TKs%Ӻ7F^+ЫbosNҰw~+ŎxHR% #bww^"S6O0ڵq[+p1 ՋXY6 nvJLb F $jnnTg#0E+G!Sny+dGlIRo"7:>1+sZݺ) 3Y&Bqi7Rءc\{IHey!>[LJVn#Q5z8~bAHgޅs*Gk#_\VÍ>F&HJcj&9a9]?JڕR Er3>mjd#+lrYse2Fvgc=E,L( v֢ku{gi!rC3OZݳ N' rjTMWc&BdSH7~5%=>aUo4rz)^I,Ii4q0N}WBz;6Jmd*s랤&aG@G>L>w1 r@z}+Ǽ{k)r\|zψyfD=0?4 e̻x|(6iqrjHX)7+l v###..85y[Ι}=jk)vB4{@īI*XQT3SQѺYn$z@qMľP!5i]jyŷ-#Uhzm$(GWUta]\Ϸl=IT8OS7g UvLgSWi9'ڡ~7Oƽu [9 VrXsJTq x$⥲bHrqO~j`gAuZݿgml}(x zU*$jEC:CYo$5̌qFz/%2LЖn݌iB64.ړȦ"R[FNy*9#`Jz5fK&W5hhЪ(f@JM4 m~8gI w+&vMz&rZW22zQ4ln4Ŏ֚0s֬ꭶB#%"*3H|GJPȓ"2h/Lސ;O|S y9.Z61kW?;ChƿB%NOOʄ#|+p) C߿" <.g*oH6=ﴵĔqN;PiWBqcd\Cӊcnr1ZI$*2mlM1?OrA!sj:|LHUt(H" `qޚ7rO5Q6OH0fhg*\fJE93R>tpdtRO$J>eɏ$*!U=*p?Jt c4cFi 84'灟΢Eœc'#J=)uv04*̹~ic#ܚ$nAZ? rWN9L3ZpZE8<xTdtIVPzqToI!ՎOMtzXL9I8v;WOX~dlDyP2zdi]sI"3qⸯmUrQu1^Ҹqԟ~iqO0jh` t+!p=k/rKos|2>cȬ.I[W|!-Mgww+4-!LO|<nGS>u Y6(qn HF #9V7,}8^]ǭh(n㙡C` ]fYCJ]t%X;"C 'x԰לW/ާe:%{Ou #MeI`׊ҵ'C[{N L.{׃N;W[Oxc_m_Q.@r*{&cמ=>`Jǭ18 l8>Jӛ¾ xtXñwwlo\=Fv׊EBF~V+yFi%fpG5;ihBy; ts 4dA\gu588vRr:6.eB+ qsWM|5;DcZ{VfoײkWnDv&D5xE^.y^񪗚?jU@xȮ*=UV=7j!qu4ҬG8OiʎTR$#3RRa$5Å 'w 8nL#x&0&LvvM?x4vF =`fSckh<¯jw_S!\;_l{b><1L]ZX]?zWyzsyT~<+`Vmd(zzcꚞok.`7yN޹z(=Oq*mNӖ==Y6U6_};ù~t1ȫ:7 Qfaj]Fռ=>maH(-~ʴ}L-j>@96h G=EM:uy ӞMbǯJէ#xZEsW0ڐ;QN8{՘$S5eK]rsSVmrm';sߵTUwd,B" ꧬGGF݌;Fi㧯N0 |9OB<-I8}尊H" dkw[.U9'kSDcQЫ ^u7ۇ+ӧJ_-K#1r03!mRFqad;3e`8֭Y:涼MZ}Nsa4GO4Sܒy6# tyW,O͌g09OٖrOȺ"R$bmڡ$|?>՞;aY]%3(a?ҾJJGEwf֯)nQW-p7+sEs@A# ]=%rH"%L`2L g'ǥC$F@xU\J%Ɣ`dj6A;8iޘV^sR9KF,F:Qv=*$pII hb3Sz*gsܼI>VOgsS".v1j2oZE%{ՍrxT\N(, s99vͶ|pT|{' 8j膭i:mYpIT$SX3d1\UգhF :`~/W w&o׵uڴd;_]H9\)_cfeG5iq]ib-Ϧ?tukRDByn?Q{qk 5$+J? x;a*)$󞣧ָMwFI8T#<(%Nsߊ?{b_m?LZl:ős x=4WZ|_{}"le7OkbH{O5-3RI5{tzXv$ rW$ףKG9d*sU.O<:֌r>j&@j>u=Wf\5r?tZmUK(##?#xp[@޹j᤭'p'=1VbrxSR-Y:^Ucq^JK;!4wּ#HKN܎޽IQki QUq}?U]@fb̒52H`r+Ŝ-ա8qu2wڼRapFnkŴzX |fcG$8b;wh Y :+%Qq-0?jB[ 璸?ktՎ9akA5^UheS3z\dzpK+(u 8^_GlY*bGlg⽶#U#uQgf;kO)-9֧-w:ԕE ]֣ne(:U8c;Pn>hoB8(†l68cH}Če}3W-+N-\Y}h;ke@]qJKo,{>UlgT^rZ#9qseflT mY'8$Li^̆5 ĩmnYU/^+PDA@y^գ̻]$g2j̉U I!sUY#FKV)3"KpTS"(H3+HXvUoioIW;I: ~k G۷Y{ښIXzCr!+7o !Jw*}b.4۔{V_\i+1#gκRF IwFg nSKq5ϛS̀349@y9VDYUL3?ʮIܮm슠G^ Z17:3JPgntWGVvIigr~tx?wq`I"E瞟_ރ+zCmv @3YqF?sS~\bĩюsV,IB9e9zX׵>qJ ׏_©OFzz $}V 4+򐤎t.$`dsң(J< 1I7), wk@OnzWTur#I㯧z5 IAPk7-Ǝ$qinHKc$r9  =o3b.$8MXˤD#w^;U7$(${[Z"ndŠK\y=zQTՄHOy7ָƊ+xly rJroV[MNB(SB%ch>z(bv?EH2ſtZ(bGi%fBhPH!5ڟ_*(:1X,~4Q]X=RkZ?z+)V+:~Ҋ+b֊+/ǜb+?~HQErϳ4ҢE&I?ޏB!Ҋ+w>CVM'zkJ(r+]uOB(CCK>E0E gC~QJ ZZo1E2Z\tR}h;F kiUv+dAS_d >QRR4T_񢊕J#(>A(d7V>E#kOTu+X}\ 5V+Vaa IDATxˏeYvGeVvUMmlگƴ 6#dCb?!L<1Ĉ%FB< @B 2v[n6]Gefd}k}΍̌87luskZqNĽڛo<o<-ٷ+flliu7lukQ[pg#-t=YHl~'ٍ6Ik?qg6:3w>gwDZX_=zgk??9 KVOGTN~b{o%oKP3Bd%=p֣G?M!|~!VNHikfZշXב@^,#l;m%L|vԸ.Vo$;q?.~zNveWeX4kg/{pЎ>>yNvz)i6nlnӣ÷G~ڹq4Zfs-%˲O|k_ NFwq>;?w}e޹v{Υ<*AK-ʼnm=7A;?<~|~Mmn;oOmvuXq7|S 劒{xo.g{_n˓+:0XiMNfG߽3{Of:<^4e;! 5O5'b; ryvt-A[{q!ζ6ֻm%) ŧھ&f[#yx\o7z{';?9_\}>T콿u;;d˜ ]N?h==hN㶿OѲ/ $@*nu=yi/+J{&v] xXo|޻׎)3N1v=}jHIi%q^Lm;|߾'{er iK)wkpZ;j+[eMsU'm>hwiz nt>|p~{ۭ!rHEtZ-DVaҳ-(  _u}9 S@lQUW'wULZP;O5q!IĮZ/YϝvCe&@莮}B[o.-9޽5o/>޵I ?@ pq% ,uyG@cɞ}A2N|]uH\u;eqȠzmCOh!\ ^VIS'Nڗ&`k[$E]άVO6I X+8LrOO9H!^sci}g#0H׶V38OA 缲uF2d' %X=V" S㋅ɖhJG:PL[*+{aVO9A'^,q uEp0Rl4~j̱N$ P0ZsB0iC 覹*ZB,UzevcFN!P͞_5_x.R8zV+xY71tv;/ er\LH^#s@.9&j19M+x; *:+BmGWV' !A żc LOYl[M KЍ@ /U.cx= pBxi>l@jBg Я3ξX'{Upb~}-p@ɶZ[hX/j? ڸb C,Qwl:urҬHֿR VajxUȖ/UwLh2qՊ29l>+J)cS9r0-8W^z9dg/ X%\"Y? 9l< LO\jz+W`+XLOŦ1Tm&bY9D8/0 3R$= _1n^%TN"m_x- #Р;) ՔШ*YLOg*3Pfv+ (me"Á:FH[K3妮H#!:%\`= ** LC 'A}eRJhߠՀǧަ'@)&/Qʸ5FNJ\ԅ;ռѕmm<3Hfm K?+_ Ag9PKۄp]@.O |.]#<1CYz1lX½~* 6/^-2"L ʜkcS@ ȂA$x?9DY)Zsጕ' pp Ҥ 6 $4iկ@~-zB==$-SշA^p3 #V~?tUq񲦄 <*aX]Ie" "5) {'''T@gT}nT[炆ZIv=28)vpiާ/e&#bPW!>CXS=cNۏ3@v:A.}29, 9 ӭj7&m Xevy}PO:g2"OؑIUYܽex `@6N_C^4>eˬTaی`ezDP{"{=0a-W8*/'F!_e=,kq3k^.YZdY9fꘝgyŸv!Q*1e3IO63ot0>qj qǞ rv 0n8LY4mH0$C a,?)u),dAz C`e<' ~VEvVTb(PY̊=En }_ `TV B2R >(:`SA?:?x tPM-k0 1!8}M$ |'Y4N: @ }SוXx U0wf.,`F 3֋DA$Az mJBftեEEv GH ):n>+sr^KIx=p _r-x..ȏ|b `HNKg1KycLa|EIXޚib{BU0+&Щv` G@%LH+m H0~9UpY9ӽB MOT,Խ&#nsLxM\rҽggͤ M`<W) @CafXNp(J~+`XH ʙ*{n6z֫_V΀A38X6B7,ucw?/Z*rIt}:WtcJ{utWd\9"=cvWma\'YSaZ 'U3Xe/rHE3xz/ ee1S@5>3B"D jkc&'+rS)gʀ:i` _qj78餳\$O]Ͻ|POG8BE’NQG mQaQ: ier  K}սIcdX晟v`'\ԥM h&xs5Jm@_ &%|HlBht-ԯ{52=p,Z ux5ܓ?_j7β =c0u d(JBHb0,l'\e_HzZ0N U૶> *5 gۉPvlO51`|5$XxSFx\P9+wqf=Tuȏ0Iԩpcqj`Ak=>Z/VOND^]ns~\FVD!T dtJm]!uk !fǬg2  ٟK0xvZ|/WON 4HNFw_ C`d0K0'åFq$W[Dsr d#~#hJHF"bK3{FS'X.|ȯQ ]ݧ'"T3TFZQãjfXQYlKit"Ë8~&g eǩe|q>HTR`^1p|棑ٜ(SE~~RdƳv~]Ŵ}_UxU{~@) k}^y o(mxv2' l^4(}Ja @~yp/S U_^}N u%0ɼVΩӝrUX,#\nq+`%6F8 ".U ГL0ƴl!8=[U@`Y 벑F>疞ɰVtQ?QL\cS0v 3-?ν׽y&+bqO=_XZT#8ɡذ/>O]8J 00F(xԸ?d8ɐW7HieL jBDANNAx~q!]I:9-͉揂 , 3!rk  5BC0#.k$@(}(Kw L?PT=1ٷ,4_l$t]BMk*}IG`""H j#}ArWFx(?V&X$Ph`ubU(r(`vHzoh@7upu /;tSzŒl%KtGPG_5*[.PIG`hc^XOW:Z'@9(-E%Trm ^Ǚe o3Uu4ߞX-g8Q@"~>S `lHUd~jl~#jvTB9kշ v >1{qQk$,pԸ=IjW:J6&W v y 2= G o4)Wm%/N<ԍ=*eu=*|@Nw]IHYYqƈAg9I4v'A+Ub$t PB>3}y4>[ph+VHx_.AMP} 2"IDAT3J=c_=#:f$^cVe]9si _э6f4\b'5#؏ c8t 3Ih WcV9 DOc}To'qVeg%gz6Q Ѝsd ~d> Fn^4ρ ںFzD[hDpNx̘Wh̐J qtI5y(01Txi2(d,O>Ů"> avf*b5n/Źm"8 Me`CDl=  Dzm>H&8P~ڸBX'@DNmğՍ0&C`= W-8 X*p p轘H6.cZkb~՘BR:>yq:ZŃKA>3n]iM ڱf{[O~[ӗKzF.41LJjxiC_>%ʋCRabJ `V>,2qT ~(#)u18c:2J7'"0ZOgWyՕv}$TGezHPD錯0\`(L'3Pʸ+/|r4N R(E_m3&0zX%ן_}|Ծ۷_T;ؾ?zQ۽<܄K+ISR0Sb3@<ELUMhLOfXv8!!h9z',8W! 3Z)QeVXrce4?{IVp/?X'[nGwCO$Zۻέ'~=}ڳ+ˏDqyE͏H>z`tHl/!+N"= BJT@8F 6Swkߺ;mDԡP'~!;m'[Wvok5-ho}7FRI2Kyu0ߘ_)PWPp"WSHL!g fjF;Y@L<*ӫ@xrA R~Y{6zEWU:ۿgڑ}EM ]S5 CzN\sNݷۃeo7\JHurxl]،o vχNW Q%ZLgzX:Aln;< @C\F(|`f8H@7gTuTq+]m|w!ΕZjUak.n߹^W 5g/q{?:Bvmtd%$aȊOP쒷5@P>UX:}~~B aX+8A]44M,f90&-Lpi􍖡>G@hB@ϟs_Uo{eQ϶R==0J>21=JҢt~ r##zH7[6ۜ3q~N/y!8%Tb F g0)tǁq_px4tWՌW)ɜjbY#pxVfO j=T+%ƮlXp%֬|3d0hp<%j$akFt/@j>5>h`MCgX` %XhL^1cKYtgr8xPQ7Ðꇑ$1N*^¹ l9;pE$I`"3ug28pɾ:=㾒_$^>\j 8|Y(|nƹ@b)er QW# 5 7ӂyr].oAr~Eff*,q_}^]j7)}ڜOT|!ޙE `Cs_1x `L837aK 1 Wߎ;zP9N@?x2\1-xx$i` <6<,91n\[:~X -F~3wU ԭkT ױX# h WDq~ (;X F±!苇` ڣ1xj׸u0Ɉ$z+QH?thaԲFH$J\0Sk9|cH _C\]x~p60M[۹#G c2Bs pEpz p+WI=^P&4y"YkD q?%hs :_pq;dq 8Uem)@ 0*ȥ 5I/OC+#H/}_;VBڃa\a|kyHy K~A<@$F0$ p$ݘGM30^PxLeݬ-(rVӜXHT2:'T݄Yi` .W'V8QQl>qǃo#9иohje~3_y 82CFʾWcHc?`B@tgjdi _53ߠ©1FRMa}>3s :O?V% `W"ք|ڔ1ⱵrpbPuN(9pKr;N `NF=<5}Xtz,1s@O5Fm~PuH`˟N~<}7'&'F9`RzB蒙V:FkCN@UcFm04?]wȘƸP?7G` sGFw_j~m ʛ Ržz |cp*f 05uC?[OKR0d~ه${@%/au<7h~g6xc5l8;Ш d-\_I$}\ pNM^PET(~(+Z㌡-Pbp+@!<ӊWoxLT霂 4M>NnС 1FũG`<yO[q`,C%FԾ_er` e D #\Wh]g_f P&ξ;5$e]& ^xhT:!Xi,2P[Ke'TBs; xƁ&@TE)kpqc#=4uTK?L0a' 5 xA0(?4 wHc\?cVN%BX]|!?3\t40#12>9Vӫ1l!@1)Y0(RA/AlɊnOu"3e >zYV 2'Y60kj7zڠf q2k1:9C]1˵̹C$]3AIs;a1TƊQC75ݭ6hSDŽ0{7 z V/Ə_l25FrFD1!c ` 1s!5ӭ9/T+b^4;tI k8 0YkZ8EK>V{uE` O7>_nBDӇN{ `+y U! #:l7 5pàfK02[f5E[t.Z ,.w_ XD?|sLbKjk`v{%XSCnf9#B+G q*`4$: XCVaMx ~a4O6ɧp}w"0n3؀zWw\#cs}*ern} a*wzQ VXg9N$ZXzSF rǏ1T<` ?i/+xX:eůt72j? >g䛹>^tЮ[J([k$2XVG!aue&{=<Ӻ AM}2Ն#N?WHsV:@+XS`R0j8R4 2D2<8oM|$ Cr ;0"V0Z Pn\,Wh@t+>.+@F_Rr'kO5·;Baj6&J/Rx/jQp(̣GuOR=Y59"(`*ECxO%e̖OO4wc[-r>f Ni`PgJ$qߒ蘽z@xv㖣<DRKYQEJIF ϫF+Ff?ٽMɓVm!o{{ݹymm 39E'nwRtwxh%Lu Ð4;,'.7jdC|? Dm(O>_fm3 Hk pF(zHS)ɒ[[W%Iu}[O FQOҁ0*n7BZ8%5~^DC G1^p͸ƏYOP2_U_ C}Qq;8^/}Fˀ ڴc22ʉD㧇퉾Xio=~Oщ$ӾĪfa4#K(c7p F kH`N_^!_UUr /~iGOگ_*SX/4õ2`r D){>kۻvd8V\S"ů:yGC}KVރca9T=>^ڠOhC~yL9[_j%TN՘kFZѨa설P`ξo hܓ|4>78NO\ZO~`c}ꩾ7DW%8Y‹1X{E` t8;fsܴ1֬_ylhthM :mҍ㴋eT9A;mhXfC kk_vAP^>=6w~}}K>U;ʰPjo=]:]uX@L+m`I ꒛T\GC쪏z6"Cw7|Ӟk?}No~-F xDWytWշ*-CPώзBαFjzv,0Q`T.y f6#[+|ϳL;_wn{5sP޴xxxx`-_es65:;zTXtRaw profile type APP1xeP g`la*RV7QBO27>6OϫnM 8H9FP 0$ޗoEsmN=4$1]8deo& tD6 Ԙy.tȯZDEXPu UZp2kb<8v/Z\$iTXtXML:com.adobe.xmp 256 256 R4IENDB`fotoxx-18.01.1/images/print2.jpg0000644000175000017500000005267713222767271015070 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 273 275 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DW|oOħ\ӭ&Ե}fF41.$X$a 9_|+g^xL=\ck9ʩN6vm]_j;3ľ}G+ڿI/W_m#R?A^蚄QַI&Vh gz,t[xöm5b!FxrpON.m] ooLT&XY\eP~aO{o ZŎ{&xAI+M4^w 䍸|Kg$'4Njƨ3$b/-hN撳]mCٵɞa >}]_~N~yR_}JPeG syڽ+1l>|?z>N6>}G+^ gq"r2p8tr^s:~6"p%ŸBl--mujW@gɸ+; Au{o h~imq}?; +_GU#Oi>uC*q:krGO~ø z?`_Z=.|mmjBQ &h.mIC٫v?QKomEڔZi122,݂8 U#uC*KA>5?hj}qfu%팰vPȅ$ fRA,_ZAjj/73mᩬ8n"qԜ @'W_?_0xwïw:wIL~?:Q_??0xcT<)U$7v>'5_+ O'_<;NXbF_"ԟO.E۟g[ /32jyIҵkZ&etcM^kijmH2FAZ56'SiK:'cxbMJMWkWˤ\H"7no.a wxRw6VVV(ckKXt g(Pu c%)%R7W>׼G^;Z>IIgm#A@洫OxTо$kGz4sf)P2y`` =#ľ(,xRg5gOK٘b'QKF!v_>mwj>+I׼A}_h|6]5l9,[9#UJ ;-//|Sxph?0wdTv&$.MY"'iڲh 3@<=~w Mf"Wˮ\xcE{YȐ#l(<nyl4/ ٯo=ʊOx6P |O Z0Vyv+޾Sw3Qծs wΡM1FvG%uq_[\<9>&Z&Vk+F wWg?# _nb̞*Wx;n̴}amYAygK_0k>04]J/[kZaik;fD2Y$39  ?u[vha/-,a g)]4Vh-5OYww]j6:H yFp3*^cNW%Z?%?hoC;xͿvw˟Zկ'u{#GWg 8μw?\ћ_Ug7 %I.rvtU׉-|a6jւ2jQ3h̄^}N_?W-bxZJpp < ۯih$ghnieVR@8}z>O Kgi^:5}:!5ܖڄn }H%I"YUF˸0G|r^ޥyKB]r/jh Ap+;|;I5OjA//K]5+ٍAb<~VⅪķk:>mirym xi*a:Ve_Q^Nã ׄƩi ^qcmcY,Qm;®Uѡ@6x9O:?gys^FvQ2d0F1ڪ}RET!=cXx+Y͖Z9;#iUl~@rrɭ(v{> k661f#/'&_ FxmuD/s(Tapv `q]5njyßG+o#}O(cI 1%ݍۈ9cº,c{+k##9kRwd75̺Nrܱy8ZV=Kq5ETu.lm?`yOGc >V S[F$T#YO_x_F4to4)QlgGUq#hb?7'vk?-{:>{kkqcW:Tz4q$!b`0.8=q]Gm E iH0Tz:Um?Lu[NZ&Lh,fT( s'?6A">&r5J5υ 6DŽ-ZZMiN,OVGRޕk9 &c̥HfnP94;] W6wz}K;|W$zPmSZ:ٰ{hc`aР`v؊fR_hZm̶e$n $PeztD|MA='#o ?/ X|{ȴ-2+,[ƣ 2 )-3 MOS Vˇb*óKI$ZzOhG_;FR4{ Ny;X5i܅,I$n_Q,/ &oͷFEp͜HǚG^/C` bFe 3D2I>2K.t"I->TI${?xb/콞S-vXm}}"v='vD|MA='xZݵ*U+([ 3@`S7'vj.@V6ڄ1ȲwP:0 k!|IsuNtt׿Ԅsm,I=H,u-$+ m%(3SΏ\ơΰul02f~|v")}%̎3?`0N+7'v=U|+# 5TgxY$`=MIsIśZh]j4BV* 洿7 xAciDN,QY[]敩:+E6E 4/z-PW!x'RKkxW1p do?>x qEVm.=#T2]剕 X' M5~ ].H5[ Y.*A.CtevOPz\6\W[WMԚr.4koJ38P_Յ/.߉|Sլi? φt5by`3+̞n6cJ`j柪چȰ D*4[d[o _/xuK!vDz^DpdrG/x BTYRKe a@>d2 3OV|l HsJ>`?uOе94O#!]ZK:/+9kQ}\xv)P_~?-UE47}F=92!6F_-82xLXsS?j6_m+cF@AkFM`5(aQ`( Vrml(O|oaXҶmyGKM7QJm$X\27yF}kGV/ zuA>ܣ?c^'ω5<][M};0V0"F}I1궿./ƞ"7ū^Yi~д5%!ź!fll[~ϖ:oA/*WYץ`i.GLVV%YLK/b']|M&w'F~\U'4(/[iVKs {Q@q_׿7O-;ULj *Pӧh'2Hd,P2F*νwU>iwixn`\h+(# Ђ W?Giy=s|bi}GIoW\4+4s7_;|{cuΩH[VukRkmʇ"7 [ g[ A񗋤}yAMZ"0`UFظK^ }m_\5L%udč],:.)/z7K+K[_q4ɿ?LSiO xg)heZ*h(NOE.,ejRY"[`<9m`~q[ Ḛ Jhm&9Ֆ/mGU8{|{vɞUh`F4l`ĥ@9(c9LV*i~ CǠZʍM![=[֟8±6_V g$}>fVF:?8јUxu+ x"Rψum3N> "klQI R'mJnлoWu|&x|ac] xڑ7K9)ڃz߉|x{7n_E,4Qe`Uة4iͯлoWtc8>+%~5xǖ0xrA1< +tV .I,>lF}ZoO骶mheXʼn vh۴vD 2E-#:(>o|7{?VWXid# A}{VO~]Y|J]֓ gbKEn/ʢm 3ve,@h;-k?J7j @v~"h-$ 1cb#,G-?eqauo-/!hdVF czӲoV#k:C&_ͦq]ټ uYW‚/^hg?FNJZ=pđY Hs*_<}n<Vޡ[?[.$"ndy!Uʂ&wſoHwſoHdȶw <xW!r cAѥmc:Ԗp"m-^CAYp$}^}[7$Q[7$VWmw"1QI.u(luXƒu1b^\_[.Z̷׮MNyfR١UG>oJ]2Oiq-du[{ A+9%$eŸC>|=k^KF_[:ڵ .(`(822!S(itl#w¿jeg{KǍo $2Fثm5_۾-E۾-ESwi%%d|a牴xc"Y=Ko;Q wy#; Ī"Ó"Eg20`cq\ElGElN[/-3?qb J/(w>Ҥ-WPU,v4{p8[ZFym}su]}EP 1sh2-mi oi IJXHs R|YP?.4i{Kf(@͖Bnm>'C\uۛk>}Kߧ4o'υbvwſoHwſoH>o̎T^nu|I6/Xd2X2K S| ǧWn_n_Euloxu+ |Q  klm5X~"oD*#8R.dȁBrZcѨѿj?V6 {-F "kf+m4GAu:^~ڗk5._1kkGF- C:n8[PIEh&`Hds"G*m_{tOKȴd'%ZOuxC5m:ƿemoqoey3 4vf/j~i۵;* Omq$ . եu~+Wv"O _E:x&CXs*ū\)b6gPi]藥a ZAWjww;QUba$q$qʬz2 gwv٨|1xAc>wG]Yi HMc YC}FxBomWP<[% tV;` 1$ ǻQ^W%O~ JuTdZ50F K '({QHaEPEPEP^ecN;uLkkMguKXCg9@Ոzml&/7AgL2#\ĝ]QҰ,5 7jvGpD7ђFdbu+I-b|gˑnA r:Y_KPZ=ʹO:l6Y-Ջ\eؼY;_ x =G!7zV&~(4o.K}A  Kosy\[̆9!^du#XAh΀tgmw~6oGC߈5iZ8"dmOFdr7ǹx!8S`bht!=tys ,omgG;dZ<15н{Vhj(u%CDBޢ_O=.}E|3Y 0|5l|=<~~Vdi'eMzץEcvLIZ]]3'.dc&rČUr_6Bywj ægQ:^ؘ&>Hq'P|CisKsu7Kcn[toҳ_^]_ӱA#[jwrj-:7BJA^`qǠ5Xx:Oʾ6$|}A¶Ԛ6y"vkb'PFYd-##ђJk95m7W'itlҬ%,"$idK.FE+om}1ϋfid3M^^3۲)em۰Tu#s]|iw?Hqi7cԬZj6<х|^ t/پK~.⦾OTq ̻@HܓJ`HK.{$c,a`pyo<'ho%;oHYj6K$;d/ $9z6⮻?ZY<3RPe;T<7GmOբ-0a*ƾD{oS7WcoXKo ޤZ =F"6ڋ ia׫k [7o Jܓmmdf2g+~]\^񅇉/XYqfމco*9@^y%Cr;w<]xuC44Bm2͌'g85aKxk+I,qI1IoA,<+(p82LlV?ݵ[[f- 20 Z[߷#d)'mTQEAQEQEQEQEQEQEQET0jv76w(%2p׋!Fj|3âZh,,} .%b-pTN,|[ uDϋ"ml`l.fF2M7`XtSWDʛv=|-XmSIR)u=ZV1D'%\z k߃1|UF5Kټ=.oqL]Z.֬ǩ) 9Q_6K!վ6KV:={K3D`.ebp6ՖE:15W{'kv%QPXQEQEQEQEQEQEQEU/KnZ9&[h^cXT.H8$qCvWcJ#{\#a Pcl#d˰wujž"4 Xi sX|H{ICѭ|;Xv1Vvp'U'񭯆.^[Ix'H@˘d+`'=#Ө_p_`SV@{Ƒl6H- 3`\cWW\Ŀ K=Bm#TeO'Y D\pI;=!u_ut?'X^hzV}< $<5Yz O?-]L闚%au$2\2}s]|Ws.[ k^9LF]Juþ/k_]jʆ!!v` 22=4֚%}uWZǀ4 &eijXA=نFrf#ǡoO k+8"bP"F0Tp|5?Ćǁ|WE-dmBS`p:5NܕnETQEQEQEQEQEQE]g1cI>d֥EEφ5={zW t(t#KJS"=ߝ7 c5hZo'Zό xQҧ޳gNmm۶G(e[w?\QUmo_>h~³xmgQMkR6%א7ąw+`s;6QՎ㟴͵χ<4bUl:d#o,KF_vH$cT ]j&trdj$Jmy&Q*4h*eQ@Qv1~ѿn+-u{uCB5S}<¶w;Q]OxVGLA}o,<6ra2LFZv8^)tHa^GEYMFW|S}iO7]Z̋+$ fx!e. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{uEf`Z[z|]iQ@?ZTPo?-֕k=o>. _{u\Uo5 N0uiy9fj?Go֫Zhcź"M'u? z/ iwz|]!oEt?MWJ4MGI}3LӤʑHp@ʃϥ 5-k=o>.kֺkgk_"1$e*x$z_C _{t`Z[_g4_C _{t`Z[_g5Ēh+1-Z : 1UвױUgc0cL8%#I bHyLHDFO\(QEQEQEQEQEQEQEQEQEtO@כ@/5V_&K[WVF<-+ګ~+Z)-ʷQQmSZGt?^{{+mG_XıDLEowyPdݸٻ$CVƞ;LJ1׮%wcӢ#bXJ#B眎xĺy ApfpWu#,(jle8͌2*- ge8]f -[xdbl XbE9f*oAks%!†-)txF]>b} u#5=F|<Դm71[ iC-?o"KQ8Etq3AhYo|[/mX<3sw{y4إWr+VBacYq/K< C!}p3ҥdhiFIG+g < Z<^i_, ?}ORͨK&KHmRbK,B:߀#iyjjz-w 8Ax! yH@'^6jnSh+t!1T :j:o6ɿfx@G8ji _Gu{䢟5+Z|ÏTkEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-18.01.1/images/match-color.jpg0000644000175000017500000005251413222767271016050 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 227 280 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽsSc?}'?±Gڽl>|?z>EaOl>|{ς|fMCV Epe8]Ȥl-u#k<jzVh^4[Co YB$ I rǙo4ݿ9'?zO@+д_^SL>ͫȷahƄngGNkK/1RgkY4=VScA *$f;Ł: וo{~.ek3 sp8N@$d#^_Z|K`Ӽ) MAonmHd1@Ñ+'x_H><>ڂh ѣIK|A@R"] >}^~xgV/n4粱kwDX/."U`YʐYAf^|9gu^j>-P$m U 2ND-AzuoG}'?DWڦ|?4 G3a6o7 >7}S|HA\bsj<]>!VkW^-ng\]5+g;Ϊ ]ܓMVaEY?i >}^'O<ROD8ѠաUoh/Rr+ ~ x5m5ӣ{uİ`0[ yI5*]H@`|OOY ^swZc>s[* a_* <yؐX{15x_$ gd+d1"`A5znn,t{P3 a껈,=jef<\.sur2} QѮP5mS lmY%@TM囹8,B:t>d[=ztRX$Ю=pP}>%3'ihnݟ׭C{D;CYe,qXy97Q*ݴ>$Vi*.XA&׷[S/MޛxZ}. Y}A^g7u~tDVl=OG_Mna*#`yC<ԃ^K%=kLniYfcuMK2JKs,\hIпw7_ x-w7_ x-nV|OZ>}k/^*G^*GOZ>}k/^*G^*GOZUx]\) Ѓ__oW[=oW[=asϏWۍ ZDp9wA r_?oxCڿ5 jcxP _u:ƽ-f4ByäX(B(`oW[=oW[=-_pNV> 𯍵˪hW&/l˂B}jo|B5 :J綹c"gKx9UUs|5׊2s|5׊2 Y|k_F}4ZKck)[WЬP|1UO0m'I ΟrXFIJ[cPHvs|5׊2s|5׊2.|c?q}.ƞ&п'9ql!G\W_ҭ5um[3iCT3͓ך^*G^*G^a_qſY]YŬ{-9iVmЍ7^y5Okiiki-v->? 5 z? 5 zn-oo};mmK]㋏ ?% ϡk[Oek#8J ğ%_SV6N;w 2e=TCtbZ՜m־s|5׊2s|5׊2 r<4pG8GJ՝ۼ|Joz^*G^*G.~|]j2_IHQa"A ~xgM΁eſmՀ>;UG;U\,d=h2}|n4τQ㻯ƖRXo1[3=k{kAe#j5 z>EYq،³4eKy.Yk;U9+ϵM^#Ðx[giW/qIu1f{;B *طnox~YIpj i V}RIbKJc٧|.i?Κ _QO__%!\ٲ`0JJ,4_6ƨYCdk\\G 8xRſC_Sv7𾯨y"Ld0cф\׈>ρA5O{{;Yo]X8IkI2"`O#qE7%p¯_VG*uaȵJ"Ǧyh4+85nm[iŘ|aG RK w~>~񷈮5l>i"("1{/D IF6X+nzKGWwqc㗼5 [^i5kXN$ssmpO ۍCįxxw-/k{ +I"}vnS$F$lTU /mh^4Z;FETfIT 3RA= ԜuD|V ͗"]G.H6$nTs]oēig3>7{(9- ʅ6e<M/_؅+[W*zńr_[>yh)S39|k:M|Ariw:U٤7IO5M{xg/z卤5\|K˒ߘm1Ox%MƇ/NͱңBbύ(J^'V<}#[:-Ƴq*\?fYBCH:ù^@PD#jkwqqo3OnBrhds|moxToI7jzu2ë]+H#EN\\uk^=ү/|oy%9 H ovP(sqB7m.}kE|e㏈~&񿇼i(4D v,Q!C>]ǯxJt7׶VI]H#:]0!]_ (s] ݵj+u\'(x@]Ư4x;5#TYe*'˹)Z koџdQ^'ž: )hNsohok'?3_I Η&i5?_ny_[ֲyȬ/ӎmb:mƣfC1ng,J9]>\ܼ]2e #8;I%z2~~6;Z֛oaxWN$yYg~ypIx|U=tBK{%Ӣ3[) l:lA`8~ C&"g]ƪJEOxoiӬu.A^QNV1~q|RjiL&kugepT#\/M&썲yb(ҨƄ<4x᷅!&gBnQhcvg>7 8]S_i<W!6v{GZ/¯ t[)FTcUD+|!SVLxm'.2nNkvZiR4mno9kX?s.^DYNPF*?D_e+@EN1 5 Y!]S¾-Xl'tedp8hmnHI_\Oʽ/^,Ae[A$!'Wß[ ko|ڊ*[~L>sV~6&kIcMHx~ˎ5H`h_4DXe݆$oK]:fsj[GAI]>_C޳=cKmя׆x{ƩiWO\.z:HR+=Fkv?1yqӆaڼ;7y k<>ڣxmB5ܷFEIF@+7{ _OR_A/Ѣttyn"bPp2͗e ';cۍĶڅoWSO1rUb+6Wu{o.đX~C쐲Ja Fᕷ5f26|k?Y~ k-sO /o ֵ+;O xªI$*P(sE>}t4f=ޣe] QDd7DyXb[㞻ׇ"fP%+ ::8!=9D5{Zz=+NMK]Q42A y]qv%++񏈣Tam.[($"Fbfsf.mOiVN֯eXn$cY&to)~"F>PC[p7&yje0G!te\%F(]ok[ ߧ[OD4\&mB9(K9lKFh\V'>bW+|1B DkwZb$[׷i$;@ Ѫ j:.A^ER((((O*$N kZa74 ;V^iI}n6˨Z%W-jMObs[L:^G鲺 hGtnqJuiw:w&j:OJѺ:\hw6ݥݼdXMv\ʶW4[ɭVfV2k_տ;㟄&M]m iy4J=Kv|7k?vڱ>i=Z]:I_` *2rāSi·Zdֿ3RDŽ]k n :h&WvXƒ0x.QfԼKmo=9aIU^)HX2 3I_aum$Q=k p`A Ӯ_ƞ*w^ ,u]Q,_"9[=mivq]Ayo 1<,7oBf@wc=JS{=:/I"\yS;*6xg57~:ΗxGUDcFeR`U ; yEqkFcm|2a1uG'Ok]f)T̨S͝6 2X.z xK߄.xCǚn#<; o\{k|Cz#FC"61#V2F٧aEpRkΧ{οS-$ދ<#39d+I\s9%;jZivJ $۶5%!]ݩ-vEr_/ k^0𭎯׈lՄq{rs\R2FrQEPEP5L-Z6Xd^ i⻯^þ$4n%cU0Ed)V8o .6Hnm!Iجr3[m'k%e }{@Hx.E'n r 9*Ҿ Mmj,6ܢhv6(8z:' fA?j-?WRI[aȺNy|Vx;Q\¯'OF qj l~of7Syh牾쑰e?B*Zm^[%%s>|OoK't+]]l!G:8/] _Ǟ2m> eC /QϺo mٰ"Ʌ䍆ʨKp4)@X~=is?[|ӡ-Ο-4j>*M=dA29χߎBCFی6̲ov@X} ]J;y~8!7-M;S&w{L$WL@\c`'? x7tcK:ޣ{es,<7p)dVܸ݆S<4Sn? 1Qϓ4O_W¶DVh~Z ڍ[\[ʱ* <>\^y{Śm-uu}j Xyvj!G< (}*i |/c|Ril^qJam.p `2&!ƭ]W'H%.MVKLҬWrO"^;iT$~'8Fng2+xb6:115QCm4UQEQE'M _̞!JKdh$ NN Mn(X;$qǡh4wkZ&s_h7ZƵ+v^xL5PT$ZG7b=oŲJT;vldt$Ү+Fa?ٯx`8ansۚCJ̫3y/>;'K\[h tcC`0Nk AW̚[k-ׁm Q? >? N YvKOZӴ+n~ήqշ9s li  .>jnΩy-ZIfk#R&Rp" ;qוo0ѿWWQ L爠>%嵽iq5[gF` \t#MѤ_v:kG,Ƥ-~ӨRkIĆF d +ogM]w֑Ch92\'){-.k߁sqXLש4lsO4Q!|(&_O*./?կxz g)R8<֕|;1xHx2=-<],) .Eꠋc_>& YdomlhL|qd1!K@ Դ~NOdO:V]V"l4HX8,uW >{i.$< uԵ=eFb_2 aBr r+qkc|NM{)QC`u; ,,xfE"3l6^ޟ}_E|J׾2y}k{UƟ5䰛k$̌#P(k_xZ6,UY$ˋreCR7e=z_Q~(={ZJϫ]}U y-*ϥG滪yO e6<`|{g<3kGI' -B,4m%\ǥ_"ki[Ňh3'm˾3)TPt-U?~¾k;[][G<۪<F B䌱Mkt[#xKݾ%߁oEnEˌ i|u'k/Ln|#.KQwyuXۑ1ga&۷sZ+䯅h|p\ķGj2NY 8opJx"ϥGCƇćR]Z\xیV~w~x^ ;‚Ŀbm~Y%Ø`n~X xyլ|څQHeq"%RO^yj5ss|K qo-i2yEzG%P,"3hf ~ gxgyUjz_sT sNE=Ai| ًkƩ;HH[G$* qRC<8fX>ցቴ*]?PArkm,d cU3fj_SD4mmݮn6ԝF@*3aYA7W?Gmcd\@L`o–>HX˥ȓVr]^YX sV- ^I,n }vƢ9u~8xWi?iixjPiSiVn'!IFrx>"^AwBR7>&<[/[~낣 ;^]' iVIC me";Ty0o4g t8<}xOJןI{_6r/,ɑǕ0F@vsY:7Ÿ x}45j>okF4KG< ZVv%'u~sG}7eyo 3]4 Dxx?zڦcK Ǒo*=|v& qP<GCq$R/";ŏ)0IJ`m.1wWO%'fx3Lg$ "IfI˻,RH㌱H;T9O;/m_ j1 Μs4r$@X+r3o_>'xJf/iN|>,B4_%&ۙg9_SxL~qgm7gYRk]7JW~PrIǵj / `_kZfh-7#ɬsI{{"y&L$ʪV`@@|hM-ݞ; i~"^2%:r ew2ا^V z:m!Gl|MuVTA+; ȠϽ_fntK\nV"Bd ]rqg#KOm+}"Pq4x~ȏ&rA<hფigs{y,:鑤%IRT$܁8l2 o}6rbI7Ы|WӪxRRlEjdY#8>XI9]ž=𿉼?=GzPé%Ο'9?6-Ϣ᎟/^ RKV ޾_ )y㊩_] h$2O3$$̈P 87ngm/R9yVX| o;[kշ1Grkc2&N ¾觥v~#ڴ7[ԧ]0DvDce0kl~x7MApA.r %2Zafwh*Mnd s3> dVBYQHPGJK~;M=mluմYkkZ)EvnĂ#1ӒO5HH(C+X*VAfENkF A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZ A;?O-+vZfn[ۅ2N3 YJ' A;?OX϶5G%?PH Bi_ uj(T|Ua,/`B!L :' "ҿ'g 4ENjoA\>,RzP ' }_] A;?O-+vU.mWjKUE _i_?"ҿ'g 5WQwƨ}_] ݂fNOZaY3y1 KY`,RE\#iPEPEPEPEPEPEPEPEPEP^M})|4vYӬ#8y%DE9g$u&f:{z]qiwIkSM@$IEWã; |_TL9xGXK7!PbF #;/Yw7Һ;>,Ctd-۫gL1:ͶmN+Ih-#X~ƺikhxXAZ4GM W1ldNWמc{MOM7WpjZФHCq9m1cIjtz|O[|"A#s~*uo|]Z7a,GlN, 6!FO"/RM|;mR]}`>\[\F[#:* vz ju-B/ w4eKI7dxf`GdV Ú,-.F" s}drÐ;4/>0R&Fҭn.Y$˳k(O,f' Ƚq? ~ >=^KfWNNJWӪ)o_޺t?o:<ŌZ˺+A*hz6k]FWwemp]Bv7b=m/Y\=Z5;-}#1 8o5ܫ_o;Wh Se?OA+kZGuK+LymdlM HX";eoßUC{)!YNG]ep>YRqOO$R2l;]9$[qMQRPQEQEQEQEQEQEQEQEWZZBM×wZ"6eUCaR28#(~? })jiǫѨ9_R?*RA۫(f$ܪ)p1'^Es2Ӯl,7W6Wh[p\Byz~? P)jiǨ_R?*WQ@sХU~#KPO=^Eq~{y􉴸-8iةPی19$tҊ(fotoxx-18.01.1/images/select-area.jpg0000644000175000017500000012157713222767271016033 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 322 466 0 C     C   "" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?zt5dђ;8ޣ䌎9ݤ&O uI@I+[?:JV7.6[nc2#~%kW۽2}RᇞB+e$`dN1IwoФ]?<:3+府2|hxwZxP:~x`|ifRsOW+'_uCaq |nc|Cz9?$.Jͮp?m9>El>|+7/c/`5y5 ab_ɾfXs+5O]/΋,K$wըݵ!/ocaOl>|h־ ij:m߆irkiتA%v[tJ(IWn1Ў>-S4 GAjŝ.evR 5P[EJWWN?l>|{- 7%W$|k_o|Syi9Wf/)B0Q1', |+Y#I6ZN:}Xj^#c?}'?±Gڽl>|?z>EaOl>|{z,)j>YEپG;R8}$Wڽmw֯IOsn(sxvjz\H?XPRvsMEbsƣ%Q0R%WB9F6~nzq=mʱ]\(1hn老|n#)UԖt\ut7ھֵMwo,0G_׭u.Z90SOEw/twh?=3援Zq9*q9k|i>}G; +_GU#Oi>sC*q9krGO~ø z?`_Z=.~{_??0xwß}G; +_GU#Oi>sC*q9krGO~ø z?`_Z=.~{#_uk-Naq5drkt Q_~ø z?`_Z= 5F|Qߍ~6p"9(#4'ޝ⏍7񭝝5lֶȓ'ݐjՉ}sC*q9kr_{Sנ=ښL%Q Fc$ٟzFW״ojyڄovޒ'O[U#sC*ast8;#_sjM5՛Z۲I1q9! ĴaG@1hڌ\]>lO@OU#sC*rχߊ*MqŦfK3FYyolT Mt|7I%#o R09p5; +_GU#ӳ O9ޡsk o￴<>t7YϜ,,<\v=?J,#m>K,1$pC¥9 mq_vø z?`_Z={χ_ş漓úY;bX DdGlU{",uW\[Kφ'uB?`_Z=??0x(|5$oq{PiwWRm ޝv7+^OҦ n1)[2I`_Z=??0x|M|e/5 KZ74[H@A*УI2hr$gk! !fhj(}sC*q9k(^3?^#[S(H$\O~ø z?`_Z=.[lGO~ø z?`_Z=>V?=}hIЏwß}k#U#sC*as'֏Zq9kø z9X\Ivq]b6t>sC*q9kYυ<w$EmX`Cu-nc1e<ǩ>ֿB?`_Z=??0x0l#OnvԒFy>V]RiI$iE~ø z?`_Z=asՍpڛ׍6-lhu/eDMn; +_GU#חK*ЭCq%?&2k",fK KUbHF)>=k篇?|wxMeesUΊW6Vw0x;C$e['"|SQ֓㾩xv=vVlUgV\lf]bqʱL(O?[~=fkGPYBɮiq&jJq2ss_A_7xoǒxjJ;I&Qxx"+A,>.iztv N9OiC#F)̪_ě_o\i_iL0)gZ7'ޫ&,ږ-]g98<:/,q3HIfe )Myjvs~ mNdoH5Xvn0NqjPՔ ;XqM1EokstY\|p +|[y\x#F#<=my,W)lcIn 21A,:=+}/M5s,%[¾X/#SCj읿\ Y`&U`~T_q;Wa\xoE֯/t}"K+7`U XPfFFPHU(0(((((((((luOy+JN Bڀ6h7>6^ E[ۙE'F, 1,yOL=ҾwEyNDi7zMφQm4+(Į +&Apվ<5l"_O~l<&sxTQtb:M^ScGnok:dŽ֧d;HC;?PfIYZ2 ,!Ҍ.9i6]Zsh_,| hz,.JvEchÆL nl.l҆csy6kMl\Dz+ Ȫ3(|oQ槪_ÚwϦ\|io[yhJ.Nu`>V7U 5-dT+ *eEvHXeӳW[EynChz .փ=-46Ջ|C,[] yeěg$`9h"?ik1B>Mg}uRI8S92;:m¹guOwZe=N-f ONJ֓yY]18(ծ{=/}^a$֚OV:f!$imŇȌHYA* +f{/ qxSvc-c kb&U,ȭ,em.\~#x+Mf5+CUm4(Iv31U1pNF|c CKAMeف=18h  UR[ƑueU5-B .JcF)j ހF'V9@pG Z;ek>֭y{i4gշAw {zEyB|@,K֧ :}r@lme*p]`T=.u²u? Zγ귖k>;Ycʒ9kQHaEPEPEPEPEPEPEPEPEPEPEP\NjaU{;yeYͤ3hbg޺zkvzPHcN=(V_i i4I~dTεʼÑۚϮ+wF,Afnlf2ƮcpѼn:#PkCxd3QŽ)+&AIqo)s4֍)]Τ=NtWȲ ? lXxgDNW"`n _EQCY_ՉWdoNH#66rZ&[N]0YT; o[-ޥmR,/t,5ikXMyb!+}E$ӧh׮P>٫]\4^ WWo  Y&ܪA$xQDđQB8J}WlH+ZCY=#k+2NN3]uVv< )wT O`s'n nE0=Hʿ|;3fMms|HYa0'z)dy_ƏgM[hr?b>ݧ\pA+/?dӬ Li4sL-+HK X濚 @ X?)ɯ詷޶OSW>0ƿ ɡIy*x+۝:y,c`m9VȈl ֓[XUmRKtxY#rk*)D|q% 5'!n5Wp[eDr#FNy}Pz $(&{ [I}e##Y YA+b_\\iy|cc׈MVR`Cm/0rI+-fo>Ѵ<3i,h4a2po&2ebyW$k~_|;jֻ'mMak$+ ^y o dWx{'|-Q_qSV<'W;wWWj)5wis@`ʯRBy~uO)o7k>jvz>r)`1 _g/42o i}qyc<] 5HL ev,Ma|1?u='Ö\cWG&K9 [}2C Rc4,(iiB1)KT5<ᗎ~ u=յ;?I[Lu,q $A*Bn23u3fơK-LQO 7i Β/aGq|4['Zc%Įc2m l`V6:xAK[ EVk{wu^O#d0$fg!N@hNn;zy6¾/{ 'TtM;[q;ٗʁLJlj]MV>M2WBZiZkRD0@->H)ts6bK|OkuIܭpkE%ʈ#UED1E̺OQ'KhF5ȶC$Muf_ oƿ|Pk֩ss-qgc H'TVZo N OZٷ`WKS>xс5Jf: eZ!5) gf+5drxve :}2i\f߉ |0?}Z67ƩU?ZzUŌB\v"E17}i#Kg :Vq$ֈfu\czwZu˾s4Z2QQ hX'˘ou[|s:/i*MiOi7p$nѝ(*]S9ZP񝍎cBl9e7k[y$@`'ۂFEw:x 4# &;/]?kʸi2>]326S_6%Ү yd5HVe [(HiSɿIe:V[xt3IpG_]|N7g[o떖KkKkiR6G{6o˩hN,kG GlY|5>ҼCọRK"Km!8x=;4źk_la]2mNJu{"m>dTKn;yb C}pkF|9~&>#deX" oՌD}tiGKYkwծImIJJmaF r44}Mϫ[WkOrB,$<pr t5Vףrqw$>$kPIiơhQ[bec{ook0f㹷 w,sbGz#3)V=FS[ T{gl\(mo*pxSºO<=chvIVQVђB.sԒI$rID5n55{Y֣=tt5ןN"m5},nbxpuV 1޳ƫ#:ո?F/H]f$2`㰯+'{㞽5<[qxoMKl%qÞKv>ӾXx ^!-RRQIT sa鿳wtMњYͬ5$v8SQ$Lq<oRHO֝eY+a_ _mۭ̉$6 љ Dn 'iGĿZ~ӛN[MֹW:.%f0[[aQLIcZ/ j״Ku(~82[~Zƭ(6vrI5O_<kiמ ScXvRKnz+A*c?}Ó4_7S_>!G8_ kiڵ$ۼ۴p5%*pI<5Z|Ks%tJu {sk.fm>6湦<9qi6fuj|(D`F]fzX^^zJneQDZ P ca8oo|WMѴK\jz3A$ܼ DD,ͳɗkNc-}g^i|bGK#5S9 m&I="4:Ri7֖Pr%{lȷ hUXBdǀ/57"YHmR$ȨYАyah ;iiz#F*Ti)QH)QEQEQEQEVN6ikX_WNwMm R'jȿOq#W –k"WU/6bJHQ )xJ#W]^/{4㔰{o/Rm:=[8v\qA++X~ [m%ֽx!#,eI$}!R+7Rh][z,o5; "nn59}.W3V-ro;FS99(wkvou_\GZxnMGS. <`\LC XwNoR5#S<6[%6K|`g<Oo[wíK^۷t]+K^;bFKvl)ڤ`צ~ ͍a.h1Oioyqn+yʪP"nj7v.VW%s'dڭ}FKHPّp |>ƢNuqƪ>*c[;L7p}?ĥن3'pD/|yMi H"mnͨ>a#hVSL 迯/ WW}uE|{^7Ԯm53mո{Hvܔg^ { -Đ&\io0`pP0[};jz>w2M,64Ζ4Qǎ465M:$8#YQ'*}|&+ -Ů4R Ah!0dh6wU״ t+_"vx,e4ƻx݃#67U$'i(_ceʗZ؄^U*p$Nr}1ַ+j}{vKm-6/m#o.D%۸ݺͱݏ/YfХmZG 6sy(!mW*Ku>)QEQEQEQEQEQEQEQEQEQEQEQEQErPB4/Mo)Dl8aBEj^s~g+۶p]=<=V.eBzn#*znx;4.-lPJVHv v::+'_3i ~ǫư.%C0 yȉ~fZIE5TM/]MkC+t% $7VZu 7=:L\&s>=z6@QL22=Eexcƥ}H$["5a63Jآ|m__^áJVrŋ0>_PrF=먻'|GiCL{C;qAW yhkr1OEsfbJgccRm2 nN7 ֐($S=pNæzQQ HB%Ce~aoZwc޻ʀE2IR |ۡ8Wז_ʿn _0׳mͲN]n#'s dG'KƿO[PO),2$8ʺ0*G [BY`98 dI\~<3K.Rw9cv(:B(((( x{y_ lV_|5muVޏ#*C)#ހ:?7&vdk-oo|Oh.%5p uߊ-Uxz6c0XŜeǁ$I ;; >|9f.qbC=R66!)Etlt-t-O@}*ͿO[4c..xÛ]|xcAy<sg2?n^H7DA!&)*qk^y{oއX𥞽Mwow{*V :X4&LIJ/'nOشS>×FV<^|C>O ^ݵw:u6^ȒKXŔۅ$3^ ;ox&OڋXYzlojڄg nVQ4p{c'@Hs_^QQm-+?֧4-{Pn<Zhe.Vlʓ +3ɳ`_jULjMr]){n 0+ب[߅;~t_gi&ӦId?whw Lj5un4=;SDC͚ YheA]-o}GmonC~ҴwsT֚ .!{I%zXh # 0C+thEq ㈾%[Z}o)6EqgS圱$k(n[\[}OF %$6ږ/TX䌠xe~r>8 <|0^f4-Z}"iQtY{a(%*;_{W7gI5_EkBo(\Wx"9\CHUI|?+g;_q$ÓW^!] ,$2ۋYV2тҢb%@qז xF GIEh:7 8XyzesmݷY%߁%KGk lt 'N"IURnd2"o$qZYxŚ5x&ϋ/4mF;[3’y%An@-˥x'K4 xD&uM6yt%K[ZREܮxzΟF= wV{OhxkAKn/X(J$89hZ_5{y_2ѵ?~7Ѿ!Mu]>p#ifM)0 &_R_jVMZ+;]+vvLG-ZYUjo qu__.7i6S~x?|P΋^gJ己hm/^s3 m1n$X}WKՓqxYĖm<>#ahTDp*M}EZvwG/+?~&yG5?=tKxm4&mIag4Q+v@ʒ;e y]>i;XsxIT Df "Ϣ>r\>=%k_5RHGiKbq7nR1n]rz߀.׼;kv-qjWBؼsMp~v?s}ⱤhWZVoo.4]F tfJ^ vQCwL{:m 7É<%M_""h3%U* svƟgWOTiIٻ;5O BJn|E}Y> ?Aa_1Af?~Ծ?{Ys\]B̕'|TK~iE$:]pL2)U`g,02ygБnbh'ihHϴGG5g*qW[]9i JVw6袊((((uu o4?LwuEU,~@xxwjկ#?ZIacON?C@'t?%? kW^%)`ApbPְ\ YnW^E%ƸmkH1[\y9DZL$!N+Wv;xgU.EZĭ_DlGQ#t?{+V M vT)$QȖ\Yrb\TF^ 6-%Im ص›{XD ۆdU]BKҼA],Y?wlm 1!5!UI#8Z+Uף>`RJ$f`K5u֞Ci u Y%mA6$_| \K+_ n+AxŚ'\t͎ո. wTxUVj3+zߢ!-\] u{-&-BG1.5qPِ' 't/t2O Ock[HCT  @ 8@q#oZ/6XiJQ!#dYAo[ӮVkhny,Ba^Ct#~"xa|A[:m{$M+V1R`+4OzY5U|AŅ % 2P#+쾶Gxw7wkf/:Qw1'$[v<9z\0kTXZFKwn˂;5Ki,;Kvkۭ2[h∖@ cHmO]roHQ]wx3tωҟQ4'Pip4VdVöS!_?_ M/Ef,?! *t#ϙ3FOJg&G&ZnZwhtDdg$x zsIv }Qφ}MiV:;Z\]!(X3@xH𭉽ֵ[-0SqpGp d#i_iw^Pפߧ%#j pHrcTњ_\x^YKam s<|n@ H ?5=O'v_L񎁦^XZ^km֡͜ƏsZ:gsOŽM^kY{UʚS(*9bJ_=k #hK=C:Vz$0K3,#E) Iے?ęAxZ:%;[Gk#aq`LF:v _\,䶎]gOK@t$mۛM/Sᇈ>)ZE.X95ک$e!Y5$K;A[= ]^%QS jVw>Qˁgw5=|/gSb Yq- 7 A+Z:CKK+ˋ`ou[r$HdH\l.^ qR P.jD\rبeOU .@<'ҵ|+P^k>Y|Z<^dD<Ď}!7ʛ}qv;jVHc_I:\dI; wfnfԡwzWjկbj@gЯmflo1# 2 `~*| mƯxGh4KH܆ zq2ѿ/?-lIM]DY֥]tFN8=QomLƟ8{Ks7:$mlm kvwh]$>*I1}?o__yF_rGke9ZZG!ۂ\tXDlu=~arǐpF$g"6oŠ~,촛XdӴ-+ydEn`@T½x\VV!M>K\ApTUd;ӵ:|ZIxtT.YUKr(?lM.'W$,-u9NK,DDs%a`O_w ռFu]wQ8b+pnZU%vG#4ڽ߆>+N<,mA&k8%h /7]Z=OMů-IˤGy],pJ\I偆3Hn ˻M/Z+F)qr,uRJ|j~1^}w G95)G,4]N,r\DBrFI RѼoۻĖsi zkx٣[[ui*X9`,֟}ҴQE"Š((((((((N,'&)2+̵7iwWmrtCrѤeXnu6 dG[ c* 5bZuVV¡Tg#Cx4hoy)>t^Ci}#esiJ_ xJo庛S-첡XnQGU(l vWL +m2ou+/Q%w2ZpR" Vh&ߺkRt~{@=б[Үb/0w'xf+(;;-oI5inXkRH#Vt'Ǣeh?%j<y?Ij׺&[<ӴFHʎ\)$NşkKZpgZ/$?7yݜq]E# +|]Dx#_ZX4 {:w2[Z,o,X 3q}}$ͻYC ^|@լ4/Jm2ɻː$Q6paЮ75/+Ƽ;F|P4o x[Ok{mZMznot՚ڬvDM&;1#l__?-l7nYb@Hۀ$qSJmwݥ+&]Z=9&Y31/ğ G2$!Wxd^_-$2iw+dƏ#.~Ip?)Q^g~ў%pYj}umŜX' r9wQ)~H5OLK4T&Qu0Ag`7@$LW=>O&l?.;%ykK$DMb34Nk_cwҼDmkzlzUX3Ɖ( ߞ QGQ\w~,s~/<1Ov5J15O1c,Y λ#h k?iOq$Z[cqEs>y7eG,AFXu=JK/dǧ<]ͼsT3Ad!kuW7zeͥwory^(axĎѶU,F0FX=>j'%W[90q|C~"YO=ڽe,hmC*y \g.~ͺ, {WsII3"+)`H9MGڦfrr_⬷cC=!fq+=Ԡ({WV"2RWGZt Fidk90'ѭ_L<_`{:T-QH(#X3?#'^7|Ey*>!8s\7w=Uߩw "h*OT[K]Z;ϋT4 ߟåxSVݽյkX`ہ.RH'NWkz5#^'|Ho澝n[If["sR*{dr+[ߑ GTqjS躥ԎbM=!pX3:Hۘk׼y"?qjJ#f^2$~t%ؾ?h/ǿ " ygays4?mROg $#uʖGX扤"xS^ԼBLna[{q;YX>7m^jVb3oS +/wOg~ ėW#uoQ:kG5':2@Tşo=gQmXu'. (!EJ(ꂛz[V.QE#yQx8{uy33GV*mRbHUw͍%I8nI;C=FOZW,awT<ДywA qʆl@}'gOD+~8//VӾ2x7F+Tt>b}tR(?hPfӜW~͞--ukOk]kv[[{w ,py]6awBߧ9k/ûM1 =.'v%wvbKt đʸr>Y|%.?qw-g &2;2+*ZcE}SfsHv,UWY&NugkqG ln# 'qRԭGеO |4𖓭ߍOVaVf8$<[^?YRMy`S0NC)#4٤&V$P *}VAVx›GVGeVzU4ɮ,tFmY!J1H{x/ώMMY I~vPĐ  N$? i?o*͓ʯ!'URV罿if8PX@G5nR]Gү5K-Sk) 1 <fߑ'hasEoaZCk VƋ:}2\I|g} & 朶CgojڌgoDAcm/B0**w!8%Ys<84^R鮋Tw~cĚ^ 5:I:O5?u [@P+8\׹RiCj?K~3Zi{VNޚHH۰;sibj]sxKָT In &(c;BV>.3i\cigA qz/,cI/6f6;ek_ϋi[ớk/?nn4-.oeksu壹)2a˨mz)[VGux^jVjZ蝭)eDcTA$<;(2{cڟ~5Ņ{m@'Va` i|6v!P4ztKMk?Gr~1ޏ +M?W3N"H4EH?3n8? i?o*%mߑSIyGJũD4Y}o%`N85r(@QE '-|cTxO_g>YANFy$D̪x4ittmgQ_=o O5M2O{#ILQ14XoItfP͆5E VA?8o&+h~fN >ג|ߊ|Eo&]fGeqivt%a2F ]I>a[k.g2oBýHhp$$// YuUX'u|/[/_^LetiBʹJo3nI^6=o>;O{(/ ŷu2&`=ӗu}+LiQO5|K4aYڥ6I:픆>R!OFƾ71f 4?uyaKú1! i M6ϩ7 #=qK_W/nMc6y'd;xl7%Բ! 6kt)44HkuWE[ 6^J2eCm\G16ax'=+M/-StXAi+5C.m TaXY1|k~ZLڷE dkvN Ԓ09k%( z @Z<$ '8O xEoJ/ϒ-2ǩ`Gn:Ì2*|3+MHYt.`s\^\`̛V1U__zԞk[Nߎs;pz㎉xsᆽ\k"GzoL̑0(ʪ37wx'j棩jzcKo8Dx-;v_GTW?~'xM`N"j"$bܒlzWm7~g$SCV])59,Q EN Ym➳kiӵ]N;kȞ{vS1cDS'9#o:V|Chnms&wdgM2MpҸN9 m¾˿\b-g7vHPs'Wt閗 bku8kŶZ":wWF!4}y^gWŕP_M{%KF[rDDщڥ[omĺMd.VpJ Ime$|Jx)-UdzFyN 1#7ڭ?N%c{gn$x{;F@c qMT?U;u-#暷VK&!2쑮?T^]׉'ÚO,[ yܾT9$05t?&,Z]#JA`di@kjִb?-t(((((+pk:ܗIs,q+F9v%FU U+k Hф6iPC=Q P_:A8lg~͟t Nèۭ=E B*P+hwWpזiwws]ItŐeR . ?fjΣKm kH䙱U!Tp @+~?5 ]u;k4_=ɍ!*ch͍0(2 Htr>THm';IL枺e2^< + q$dKJhu;;dH@&Ys_ ñhtoMyڛڥ#/w)گ]|&wGy\h\$s~tEY}Lyf^Ցs wZӠ9.VSA;SknȦhON<9A/ciqaZfd5 ^[M~ce.^=sc,St[ȏ8s d+iݑ1N `(>K=f f_P&u5$p DHb ZPCMlmn羞 ئUZd_9uYrw~jN<9A/cß6?*s~>4{OKOiϧdV0c%NANcHq|>tC-iMhݛxیbӵ?XWk kNLK1 dp$ZOEԠwr Fi" FRAX cVNj xG6ZmO2еʹ .׌ܬ6 +qmb8 G=I LJ??lU9o~Nr_xr1 zlV3Z'U%< 'NyٛώexNյK{+k{C+HU&Uxd w'Ujm%)5 ܂hM&ZU ;W%sp-Q 򮺱G/S>ß*π?G (_ ?=D]WOվבk]ŋ Ix87 [֓ŭι}Zi&HfJp w*|N?,m Ph]x+y١ShJgȬ=v_;xM&Vk]= I zA=_kVzLx&Q{K[C)U 83j}&~ 5oXR?/{lx$~y]qTM]?5ݿשw 'Þ(wJol纚i$ ,#@"1@ W5RVv˦>6 nƥ$G_S/TT\-,тSi!-~_E]sguwa5ͭY[&@ǵxy.F7w;.k,.eU*-NNZwS ſ|Vu 6cd$Mzm0:Aˀq5-MwvZaa|# Щ$}ϥ}C%j?WƭXٳ縳*d#)=Dmhz|Vr?T.i6AUU|V]""ϑ3E[FٵGH)ZмWeK/XL&;[SE|o^x9/rO=揦Ov@\2 psW`,U|t!h~A9eĵrNFrӏ.;t;~[x-284?ǒE,Nc4GSD"j5 Qc nGBjZV8c*^y՟N62GD{}}+_BV0^ZLd;ԒC=k[(s*IٿosvISܝjvC xOv\ͤ`:;Q<<ˀDfSSa^]8xoRǃdԒKc{? 7w&A.cԇ%寞0pxڸz]l}FOQ!9+wܿ ^Ns´E8Y4;|%KY?QYʒX5 k=_R_ f/5[&0My[0!H#.kM>)NĞb4{LWr <_?~ č_E4]EQ]B͸s~7>_ NK4*<݊`}Qq>`n,3MZ0H?Z%xK}n(jόt?թɈ:cJՃF&Ln.'V3^ !hi$vE`O1op˽ݶ[mֺ_C66 B;Ğ:m@MQH(((((((((QW3g,ϕ +?QgmƨB_TyY[ТW|-~>Vj4(?U+? o5Gϕ9ug\Me [373F.d29VQHH$Lk7l31W}gmƨ_TXi]/_>ioī*iơy*xk_TyY[[m*/d*?^gmv-JF\ȹ#*GנY[a]n;vm'^V ?5yY[Vj8/MP/_ \KPwֲAI-3 W|-~>Vj4(?U+? o5Gϕ +?QgmƨB_TyY[ТW|-~>Vj4(?U+? o5Gϕ +?QgmƨB_TyY[ТW|-~>Vj4(?U+? o5OmEQ-G嘁<΀.EW9ƵcoivQs X 7׌xƚ~kQ"c7ٴ& e* .wT\Jϵx7 fj>O5z7O5UXu c! بl}V~(}C֡徱t㧫<#vw엨RH44K[Z_=^$nYzWEm{nLek*Q\5ƽ*q驦c5e b,}QrXtŚKQOKsT iv3ZG<[煙sV2'ڼI@5yǎ<]?3ˈm/K@)[َ\2; w-^Am4=>yb?b7c Z;7FGڼI@5p64N;/}y,_ò٢YE~lL 2_A ]#^PJUZ$7!';bllvXW?oF:fs=̶Ƌ)[yєbT*x+^=ߎچo -Rpk6zgjȩm;.]P̓[[\jZ3GYO/^4'v,O98}ğ ѿk7#W'xW}cLatK˻it%OQ8Y^#,15JP i#8+/a7mzڼI@5j'to kZNmeu]6myRHK-9 iD4=cK}z~5Xj7:ihuALlbɶ' 8;z-uC=^$nYx7 fjg}֩+M2RƱoijvbkZ\*vBkĩBҿh_W}+Iy5FʲHci$1#07J~O5:zvkoms4\Bm.ZtdFE| ȝ%=.nt/ kj-aԭ!]roAvWS,e^@H$w/X1R-*I8$zE+{NK--n VWhb2W?oF;]xHk+)"x<)]j/n+퍭gsn W#E ϡO5W?oF7+iV鷺 ombDy|Fg.m* 'hm^Y:ׅ}:M.k]fV&tHM͵l᱕,a]Zğ ѿk7#Q7F^[|k}J1jP׭#I$9de"7;H!5O:Gk HtjTV}+S,Vڊ$q`JdT,2!ٰ;WmP^E,Kݔg9C=3^$nYx7 fjC½BN,쯴[FXMi5Ώ㑈. 7i~"ȏGش?i I(wXm Nb}ğ ѿk7#Q7F^={24]ίaỽ{L_8c vaˡ)"mlƿn"ТҬ=6,UΣmk, NFRYFFE o=^$nYi2Oowv.eBbOB9^;m5tb++tMj[UW3r+n>́X9û%.?"uSTl&ymėw  XuҤW?oF8ϋ5/ ֿJZ[jv"Pw; 8Gԯtn4M3UaCii=0IsqVB r)j=^$nYx7 fjǏerP\׺35֦o=QsY~ej'HգFIX@rN"m >Jz7ڼI@5hX_ѥt6dn H8=kʴMWzokxbxRMH8] i?6m/oa^̎ W[+=;LkYI\j2$H%V jOx7 fjO[xwRJյ+-:[Qr^r꬟5#t_۶m֓WnC$&\םUW?oF^$nYJ"мCg6$h׉4I$jVIL|?;t^ &ëkx]߼o2m!sɨXiZTgy]K0Q( ۮMŏois ݤ/oy x%G$r!Y]X#QE =W_sH=~Or) dV]Fz@`-^6|6dh}%\`g|C~-[[md6;-;Mns⏅mWĖ:͕cۼOx[PW)"2r0O\WwWcS?  v?*o?Go-EkO%7s -:k宲ڍml!mԱr"OYIWQ{u}o>1i1FT@$FIWn;;WeWcSQg੿4IkbO3Eo[/|[|I!m/h᫈nb[ 6H.n(8̬_H/X~s\麼6);)6"ER+1੿4l _G_sо:>Q$WoJҤLےkDj>e@<|ָf+.R嵼h; $R%ݖmc{w!^#M+1੿%o+i.{gscyѼo&fR3%q+;VY|EjмXEEIcj^}OiͽҴD&bijaՉ=;x;7h^ <`3Eo[Ր<,;7DƑJni%*{1$w _+/xO{ML>D u۵9%_cj .#ٶ-kbmVnF}y@z_ ոYڠrTt˜jOBGC Z~? y*׵?Xb=^d(K{7nŏfk]5;Ѯ\궖Uk{XZX2cBGC WcS6?VS_ m@t){42>4JG JNX rk댴NnamC[IC 6"Ha[ڻ:w-(0(((((((((((((((((((((((((((fotoxx-18.01.1/images/scroll.png0000644000175000017500000002017313222767271015136 0ustar micomicoPNG  IHDR==bazTXtRaw profile type exifxڭk& ,@UAOw&7J*vuMctp8X Z^kЮ= nZ|(s/Ud~cZHqmT345caMm=\7su:u/4ʴ~M#SZ@.֤^sbel>hZRmx!Ix_Rb>Ub4um$ՙRMfv M\!.EQ91ۦm)g^D&y$u}4K5 cW'CND6 =XlI.ΆopO)n ZE]BȚ0X plWW3a em|5pdesMCi6+ԠXMKI@Iv3|L)}]J=YRFwg$m+0`lre,&$E/b92+`חe/עU ZIy^lrOEL=rP7wAzD 1wup|i~e Hg>}TnþSVT薏6h$H $%f@=&M7v~Gd\hN.DkUavw&d*ؐx.L{+쑼1#Ripz"BƃI_ l &QT!swP 'd~H&qBM-b/ ~+8 C Clq6*ԝzYV@JN~!鐐M{dqM8ZCtxI4j#Y1gEiDU(.FI"y<;qWI{o8Mb#A¯ed$| CǶBw[q* 5_3D r];bE5,i0t,fFcvpDk.*.r gbZPeo;: (DBBeCf6 >h-H@M2%Ep VxWZEɪJ?N8tU@mZᯱF/yN ( tR E׉-tKV'>Y27-iN@Rߑ*t1cF5 1|F4W&Rbk@躥x'~ D wI./I(mB^Z7c#bC j^_ȮH%(}>s(G{Od4Z[+.᛾P<^^(64T vFqŤ %toP'RJHO&5̃E } Ɨ!7'T'sBIT|dIDAThw]}?s۫؎ @`L*$Eq숚dNYceRrT&ٞ8,8$"%4EIT!)X`.v=-v$R9==)+,782e=>R8N }mC~R[8t-qe "PP:_7<k>;H "! YqP=ѮP /OyHZy}͠߷j|k[khHfBamE1 P 8ړ(iV KkG*5ƾʜT~%csIx&ezS=C Z$$,)P?wdyf{-ҡjtᒽ390gYHG&쨆W;.Z7##tMM-_̬|>9O5.Ip?Á9tꈕ0(jc}5k.X${bO>|󂑻ZO. F#O,`=mXX&Q}t}y ~{;zK̎@"EuiK=3֡r'B`ܷ֓ih3f3*!ۇ˫ +b#}}k7^s9߿ I?co!Nw,J+^wK^ 'Qqdc9WjUe;]z|PpL'HSM PMb:G"ؾ{&Z$R n9B"_8\~aΛ؞:t8c=H0֑kC"e7rE[ɑeO-0y6Z9-(Ӗ l\ڕ~=̟Sw|ex?5Q#8" "E\[.frؠd]u C{OҶ-sfi;VvUI?W΂1/medb1k-xG-Zk"9&j ׮wZ&7$Ce\<u@heE V%sjxLmc|#4RхyuQ6字S<ry/ҦG[C"L7|p輒~•VvB+#JY6 fqF5ڑ;EǕw~:Z@)[GykmN%lGSܷN!ȭGkK3IkB;g׷57,ck<+G[?7J|ua9G5V8c Ǧb0Z֔c88af9Ytu޺y 9Do|<k=:F]>XGz){[g4YHoot$5 !xDe*:o," &a<_}%sf\wf[i QݜQady</M*XES9LrbEgNt&c1IK[$  lԼgt?OesExu8vT$LX,)?m$(WgyCxC chuUl~bO6D[@%gXkJ`)`$S_l r-w8L[׺Mw(3WabehǐVRc8N.y{Z1+ιKT3kdU,/”5pe% A9qz;7u<̛{vt1j;/äsdYFdi7|6N{9VH|Mh,ri9To͍eU ַ;s詋Kf9ic0Y&o\[2vm*t2Me\[ ii7:KFZc㛻^b -UܲiPm 6Oe:<-2gcY; TfKt\F+C,1v/G)Eܺ}-t>cm}4KrɐF;Hx|g rnY gx Ƹ"AZ%9Ѧf-4'zM[kKSMڝ08j> ͺl.]}k3YHbp7 g)hd6"pr͗G[P.$> CL7S˽!S,Gg!usI{6Lnҳkm Ztt[r~L'߰veXD >OJ)$*oAyB+NQ8V흟A#rf暤ic ֘b$ NUu<>I1\!S-dzG:=%I(⯿4󄓳˖z)RBGgÐ`mdah`-@ L ,JXl|ZDĈRE >Ϟ`>p5 o̵r1ʹ /Lg|j#k=+ b=-;TgQPQ+ǒh)> t)l ~ElzbcIb loa7o(u|e"ُ+I\GܼU:j@ vu9:;  RPY}z%>wo45䙦Ns]]=\/"..pSMӌNUہ`v}A>(drp5o_qnj+#GI>~OJPҐ:A&wj0V,G ;lO~b0T[[P"H/E/1 y!c&kI3>pp"o]ѯ-jy}ZLI`KV`@lY?mw ˈxul޹bW $,s`:w_ۻaPQHH];v  W#益Cn~blr{ߺ,l~ncYJhϗ;Vw%nCF!~r:~QR8xxnPPIg-%CgPBYC<CU&w VVv]c{sctܶV%n19]3~} Q(Ԯ\e(ЧFQQ$83yjkה8FlhK&~]\ y7?'Ύ}<ㅑVἂ .EC)Pb*!t)cs$@<j-@)%Q"!R$lw?|{}qzAaP!}j6=tz $V)JlpEU8P /$}fNj#aɩٻzz;>Ax  DT,.eО; $ "q@Pё.ٲ0_y٢$ |s侳 :8/ ygv+IfJ}cܤ;@I!qT![)!c#kE/V~~g"{Dħ{^mŐU JdPD"):pEq+(%N'U6VPq@xPAHQjzZRGy88r/VM-!P$L#O=28sR\.{@ tP"P*@{_ぷ]&Ћ/:M$U nk!յsQ %xgK(I1κuy>ջ۳"@huS ۍc6%M' QJTpT5ڎ<>?zK6 +Xq",4`Iz|7jU8R ֫lX?G>2csTD | ]D7۹o꠷vڙHԁ!`q`t^<:Wl0fZQ5o"\_mLâRKd7kǚ3xS'N4HUD*] 8@w9_99/~X%N qOkIP)/O?zoݪL>2;;5-tblEI\qwE+A8TJj_YD N^ٵYnRR ] ]a2 *<^_;_rxJ$"{Ϝsxfj^*_<10>qh9`3:ZZke 0 "Ǒ Ht#ynɹv뉝Rrv^V\x+oӽ%gK5X$"@8̚4NM{J0P- V$V(r3 y{j7yɴup 5]ByO `[}/^s>|brѝ DAw) DPtBgYT#:ʋ~Q^M|JBoyI_}f3V~xϩԽs;Υ̽}צߓ Vիg|o: L?@ACDEFGHIKLMHkN*XkOUX^akce@lf@Vlg@lhiHljkHmlmHfmnompq.nrtuvwxyz|HnHnH>oHoHopbpp ppp`p6tP Jt Rt0148<  ftDVEPDB AFr @  C ڲڶ\\@bDDH`?bdf}hjlnpr}tvx'z|~ JJJJ"$&(80246 > ڶ ڸ ں *X XAEhjln6r  2KKK9999*U8$>. "0n:&@>H6 "$&*>.Z(,02<4FVZXDBD|!~JF!HNP^X@BUxz0\*dbfLT0:p *2* WB6 f`Xd3e@B}DtFK4  hj0@l$& "4(3*,.02 HJzprxz}|~}tv} XZ8:^> L\~]\         6            < FD` b @ B D F H J L N P R T V X Z \ ^  d ST " Z $ ( * p. 0 2 4 6 8 : < > @  N P R T ` d h b f j p z | v V & X l n `bfhdjxlnz|~vr ES:      YCB   C 7 /         '  " $ & ( ^ * Be, w. w0 ff2 X Z y\ y^ ` b d ::f ::h j l n p " $ & ( * , . 0 2 4 6               xmATB< " $ & ( * , . 0 2 4 6 8 : > IA     @  i 0  DSF  IS`bcd^rtvx{z|~~ k~~X~%>~     (.RCN@B2D+FHIo "$ & (*,. 0NCM  $0p JDSCP'=e[?EeuW-~`/S7wLWueUqU41eSq[e]~A DISV <<<<<<<<<6?,>  OISG ~~~~~ ~~}~~"9@C~ ~~~~=3#~ ~~~~ ~~~ ~~~~%~ ~~~~~~~ ~~~~~~~~~ ~~~~~~~~~ ~s~\~b~~)~ s~\~b~n~):~  [~l~z~~(5=.~  ~~~~~~~~  ~~~~xl? ~ .v~X~M~R~~~~~~ $X~l~~~(3,~ ~~~~& #(~ ~~~~-51#~ ~~~~ ~~~~ ~~~~~~~~~ !~~~~ ~ $~~~~" ~ (~~~~  ~ (~~~~-69~ .~~~~4$ ~ .~v~s~h~ 0B~ $h~{~~~P][X~ !~~~{~P9%+~ z~~~~~6EM;~ ~s~p~x~!8~ ~~~~=8%~ AEBMCGm<j^xTU-P$]{w`Sl\:(\tJ`jz1uY=:sgBi[aeGnA(kQ_]MQ7FB@ %5!85(!g %),M  PRSTu-A_YgI]us15T{au_=ޝWv]M?WuWw_Qq[UuCQUG}u6RJkQ^PQ]MP]y%]_QuEE5qU>Z,Yu\uu}iD]ij]UDqQQFWuWVwF\yv=te۽UP]TU_Ugڏڌږځڌڒڅڠwڔڎڄڐڛړځڄڀڑڙڏڋڨڐڏڝڟګکڡڡڔڞڵ}ڍڐڤڠڜڙڛږڜڋڂڙږڀږڎڇvړکڋښ}ڏڎڏڇڣ8a*TtZUKT!<#{2 FSV#RTR2+jG_y'HO`[r')!.@Z[*NS/;p7H=8:Vg5YeY89tyQs&$gs"e+t 1 iGZKP~<L>"pVghifRF#"YXTtfRDIki"KB;6or<y%354z:.ok} PJ57)UOAK[ :wsFK?:+<) +&[- A ^ E-&zre4AL_F:95N:]^6j4p,eZosV,IH^ +v' Xm}!0Xnl^ovy!Oo=_ZulQdja >G{YIO> Jo92=?N=p 6 'c Bs<7=(=Q{4NEM,dyjftp7ce+q}PP0C?K p WusjbxbogNaCrIv:EFQ  X>Q6#\k5*5*DEA w 3 .] 6no"t,@#^IQnd[WdGmURZRC4}whJ16fl2K%+)1Y>1fs |I=~6RI<AWl'C883 /7'!$2&"{6-2 v $ 1T3veSVOJM y .kd_ORJlP0)X8Tq,_Tbhu"$"'9OG,OY0:>I9352=X*trnsg33yu2r&eiZ!*~z O g* Hn`X.TOURd>; t}UZURS/H}I,&m4Ge(BRHX!&*(2Bt@,BzrQ)a9 WB825zymm [(T\Q02T6iCZ=kR Ns Hnua. 9]?%AuuwUFvOx[>G<7]]U,PNVUuW/EuqU}V#A_\IW[Eo/GUEUWFeqW{vWWc~wU_]u#|}mU]Q^=U]9дAӵQ״Mq|vIEUUUUtmQR%UM]Yu:WUQ}UDIMU^uIS%UWSۥDu42]]wU}}up]QQ7U]p{p]|UsfU[ `]CMSd1e5HwqTv?OLs'W_U]shw%uQWw ][ W\u?UM|iݲAs6_C[bQWS,Օ7qqygWRVeSu_AI/M~UgE1EwׅSzPUDUYuWM_] WuFgU]U`T1UmיUG|}]uO% WtVE_|ݮWey_seck]wRYEwV_ww ]zOou{XzUfOQuUOGM=ST-PuIQUR_msO՟UsU}x%|O]tL]U:6T_]q-TA_O@}WeUTVE]F ]_WTP5]q\qUvuT?M_\wO]mu)U]qUutNv-^}]q_[Q@xU$^^Q)YluQgetU]5aWuyc~\{6Uyozp]MM@.BR >'7RmF'UuTVUUTwuTUoUv5 VUq6Im>qTv?S]WUO=PqE]URr} ]Q'57vÈUfu{\o-%CTו}Ttuw4MS KEpNߝտ_Q}QN7e>Y]P]]T k]Q%v\kTEv@tyW)voVWDvT2HQ]-l{U!O^U0U{M<5UӕwWuT_u=M} E_{UDc7q~| Q]UוGZ_S4C5iSuUH_oF} ݇qWAE)uPGq`|DG-^OUX]}EqvyuS'.MumR\UUDFSYTYu'wL&uU_QQ(]~~TIuJgW;Ufm tT]1دu)>OI[tAֵ5T_UWTP{hz]u\\YU5 Wn\FEeyZYqP@[]L1}S:IU5p#M}d;s}!tSe=]QV$]]VC4Q{WUtPO =UYw_N''Wn1sWUמlLe[ emSU" Q\oSqv,vG ~mT_EW$4=E1uY}VKUUG F DE}_AP%"Eufp+Q^7dύAt}Q]Q_[9d$D1}]P57-aՕ9BaP7UU]EaUwCU__%}FoGU]Aw]dWnWQSN55}\YUWW}U7U ` QUV6VuY9ՕNl=EuU3T?&T?*uF78tW0gV}&~ݽ4WTsSyAA\SZKUGM05]M]d5ֵQeP1pTW=sUIOWSU\ _u5?UYRS[ 7E;W/]E u=U=[}U_B=U7?u^erN-Z\@_3WhTZTU__ U5w%]Ӱqtse,kqy;+LYWD[Mgu|ZTydvڼ7Y\7@\5PRu! DA._^KGOM=Y~ vTUqTP_T-UnEK}AT v]?\Xr|p[|OwM_AU-QSE}UUu\V"uLU5UW_U7Q3]Q@ē]7?: !]/}JYpS7WtuzUWߖ]Ho}__}utW0`UVw5UqS]UTTuo%>5SLG_4]W^AuxasywUu10XTNu yY]|PߞUDUgU}OӅt%_]+eŵ4 t_qMX5U]U*2WmsuWõ%}7WQ5sYB_D+k]Ww%wQwer1US{_, %Vg4=eQ6CW43+-WkG_zq`]}XG>-AvUe!/-W]߱UQqt{tU_UQCUU9cWyatѧUtU_'W @.Ga]4U~0զ7XV_sf[tuXRwUXݕUUUOO|;7O{Vf-HSUUU53\oVWTUZܕuUu@VUWŋUU DYYywFp'^T= }EEaM7uA U]|(q]\K] u_XuyTn[{XE_y{QcOkGWOQ{7UDuygYuYS dwb]?TU6[75c]^U_5U555TuTE}5!H?MO_>UuEPٚ8$QpY|KQ×WWU_\UՄTmuPdq$u/WG_^qQGg7GuWQUӑuRQUU^ ]U^pScj6X1\`w?u5_guQ%ӵwdQ_Qk[UwW ]v,vw]SuywQC}_o\;_UGCi)RFaZ|QVUU_]]5]{_OW]4%LqE5xU}O/GwSpQ]А}x;D}5Oe u]{syI__ =^r6EBqV5ՂUt{VwU]uW\@w_w-? UUCPEtt\UPw9^ue_eAu_oV\QD}]3QrUUWSu_~u}vTr2}]WO-wmE=}Z{FKTUQUwM7]|Tig.GsW_U}[iyY=uTM_WqeWgBKGф}PTES_,WtUt e]{E{uTY1EET\A\\Ǖ}q;Eut'Sřu]4uqW}?TlET~\QuNv6V$7]5E55}UTU$QY}eYW/Ϸfs7=3M,CROҧg i 9@|41w$,*PUZq&DPm[{|J⬓ʴaTa:5 +L ̻C2qלxt|k#yPnS±N}aԪ(:*֔M[ ԭo)6短ukzd|uP!B>jӬ1*+'ּwb */7 ||qJw|mkhfEq`L$d."+1#=)S/j{F-x#3Ƽ)nΓ4D X!ZG>RݖSӺ+5w|em$z!й,G`+-q3役SHPe+sCUoIr!]p3j֣kmwulXD m8ZL=)c2_je>c#G J_>~scyA]d`>&-gYմ{wh`m g6OXi[_ߩj^tcl3; 9~ӊ5 |AiC@V6([>Pvos\'YG3 zo@Xj6&vY#?#NsŸT|y1|ѨyC!bs=3l6#+6<iket~8Uk;~u-ٺG0N;*8'Hor֘tyЅ|i狼q-ں25dWz\wj˧Jochڅ1.0wc{Y[8:ag҅ٸz{^YfMVi&[^|ujXȤv~O(xNԭ[)odnrrZ6l5;;ØYX)y.uGnikoNs.+ M_q(}_E}4=XUޙq -+1?+/"i>֭ gxY 0õO҃{vopjQ_׹ ae嗚fWo$:V9 Rq|xY!jo 02J[AciĚYI2JiPFFε?NxsO{=;N6lz\ /:*j:k+c!֕]:ݧٞ/ͭe']A9ʻ8m2,[pT4+6 jxIb')>{KR~ }BAeiF%VU;{l?ŋ⏈ `}=\vy=M=^ dM5Ky,u9`eMG?Z[2JUO~F8)u=&!=ؾ9!4;2pijs=y;W>м=%)h҉m"ā##z8:V&ySE֢ͿiuaYhcibX5mwNM93p&{7aȓ>l^1ףMpqDߡIBWqo\=J}crcWշ)nCc޸k G67qEEN[2Hc^e5XzpD_s֎oK긺~wca5/EɖH(@מڔ2v知{\# Fz;|Eϱ5[^xTk;E]qI!K`K=vЅKVsxJZ~oAZ/j!)"ɦ`>5(<'uD#cvx^ | T$DDqԔ)+?u[%uO&*S $y&_FT ;{Rbq|S=nyr58Ƴfsn-C/S6%ֵ[zds*komN<_gl0x:/Z/#VMوI#<}ͽ;&Dv&&6wtzՎ$u xc蔲0 Ȫ5hzŦ#++)>brgye/(\ pQ̲F̼ e=X~ kl[? FMu~'TLj-Ziy *܌Fvɪ|7q }ߑGh:%η*Icom ݜ:'G hvF'tb GzKC|uKY5>헷'rTpƿ][hLoQ#U 2[ OrP"" -̋,m=:#V$OJO bm}`yRݝdr06Any'V?d: ŀ)8z>6 Zs34,_qBrn.O)إ# N8s7+D઀(x潺}JFamЧE~ml7L@PALM3M(z+}^%$oxJҮ4A,!Cp:tEUVvV(u1ͬEm{'KVDJ:$;.hq*~| `j6OzIݚdĕ,#&; [TNӜz~5ݕ9[l|ղۋrJz$v~h^W&[^O#]o1'8P0>L]\ ik :aR:$hSZ#]s] g/cx{㷵6[;GJl$f+'Lj nmt#O2 {yؿz}:qjbFmǯϋK':z`*vs̿AҲ?i}KfpVbH=H5[.we"K, ۸ 7եF>6]"ާ^>gkbxZvI[_j:jgF\`fl*/ (53ԭF4q )=۷c3jf\G֣2ٕacg=)pzU ebJr*0:TV'o! zv1d(ψms}_ "Z.c ݕe3Znv>o,^{k%?o7&m۷ns޴tOk6\fB1mg}L+FƜ^l񲧌^淣n蚵ܞ1[+}d+k-6}9ikofSIr._^=t&Km:Chij13o'-~ĩ\ifO8y9O)c)5*t'7u>%Ϋ鏧Y7d}M}=hpVu<_}v]Sxʒ\ n=s^dθ8'$H6E!S9*Y|]IEB `뎴9L.;eePU6ۜU\e6c$(𭦹.Ik_\Fomu,m׹5Bw- 1T(6w aF1f^2Go\es=Eo[i'hUܘaA63;s]H0y%ݐW˨Reeyz4\[΁D8m_j n1XU_+޷^3s,.ܛv5-jN;,qCX>%ԭou(#2*A ۉs&%ŦG洧[3Ud74ȅRE'}fWW8%앿z<=G&_v ĐH= Lh&]NE1aHaS-[͈DZb>8M9Þ!=ns׺|wY#*̀?lץw8w14ۻOZ_dU$M iܸ8ͻn!;gN?pt<>FIGMqqCoB5x-·W{-tN]v MDz|&D~5i?26UsQ7>/0L~ iJԺSП!<%*u#9wW$ޞےK!܅rsYwqXZLF-9 nZzP M|gkh']}>܏Tv)gOľ ?Uo*cK}/?-W ?U9KA*;>+AM|kSl=w\'B܃$3xT]C=?GC73T/ҧx.I of4~ҧxx$K osS0h*cG}GA*&-_ox z{*cK}/?$!?UC7|GsCqr#ye?4*4o% u*(Photoshop 3.08BIM ZAhttp://ns.adobe.com/xap/1.0/ 1 2 3 0 4/1 False False 2 False 0 R98 8 6 3648 5472 2 2 1 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((({" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Ҵ m }ǥtC^q\j~9WXɃѝ)"jŝxz"1SA_G IaКxYX߁[zpP\?Ə ~P_)n>s{⏩bX# ~\?O+Pҋ70FӚ>_ŏ5G՟ؖbp?rkW%M?a#mJ]Q[)U9Rï_AFv=rW4Nm_*WKxoX}f~t1kBirlZgU|O9q֏aa#Ѧpu\U X_L1O&SX}b~w??tsZKWᏽ?qOŇj7ƟsN_*[EO{3bNb:>Q&e BC<&}eAw&r湁xG}O i/U}|>'4?omO3ُu{b&ʠ?Ls#_~%4/?a}js>ț~i?kupOM7W4fa1jseЈFhd1?h7LO|4p}jssd\ >¾4DZ}GvSShO?J`X_|sF/??}N?"~%4q.?ֿhbNJQ{\GW&yT}_5;bb9??odEhѣkFn֪>C"F`??־9DA}?pS'#_؍D\k<ۿֿi Oϱ?gt9Lh]f?fY?@d/־9mf^yGnjw>mB8/ֳum2K!g16\`gJ@]ܑ_ӞV? /1T9'| tܣ*yI2W3֊iLu[D~Z꭫d:Sw(/G?jo ÓH/ N'a_G?S]-2xI,1A]h?خ;$CxՁ;k-'~mQ OjCZm1jm!F?h:s",m$'ֿ?&<{MxZ`$}%ϲARw${Ax?/W5t5^~>[d?gy'(4=OTldy 0E q{зy+3_|K՟EOR/ȩbuH#_jL!Sp_ƽ83Ru?5o|Z ?/(HG4Y 1WJϼUD((֗Bjo]>30IQg~΃*-S+4oZ+?g>|Eר{ _w/z?|/!kv0Fg]V@$7֙N\ZfI'O񥡙j#T@E]OZO{B3TջM/]/֟´qF8O]4>WJ<_i ȏ2ݫ'+k? TU&z>?/Qg4Jr9(Pzo}߱^mx?f^־&Wwav*gϬRgYϬUdӫ]?0H{` S~[)` 6>oz^4?'OS+WMm7m3i~W+GdfL?+m쵛$(T('qox!NCjҞU~ f2$M;|<[5U9)>fzE[VIlnD)?tUsL8OOI)6t}K |&=c6:_Q":~kz /";wcڊڡbQˋ@ϔ 75 x`]g 9=?_G?S]1-tq:t+xa?ᔄ&> t8N0h]z;q-`BBh?&sLh\'Gl2?c5b<;O"Ek_DܥmK_]N?q'HPOU3[$?t -e5ٕ |~u$?V/:<}/]thOC]xIb"/W5t5x*- RIF` oL?دp{(Ed,_ֿ)~k?b3,[Zm?:>,0: ^GՎ#r*;63f+C5Yl!Z9>/H;ڔ"bzTh?zw$[^}E}K3@)7/b?:7.zO47o`\o~}I+mՉ=#Okͷ^O_ܿxg&+>7h?6l޾_ J__ͰLz6D (F|U.Fޢ3֧!@K;WL>1BܧV?{M|z澩~c>gv3>~cq(\TW̞| %f>7ho`DvK  =k7th?a_֧3<P\է$o RQO ʐXw'VyuZU\b}[PU'{aasW/o# 2Be=im;HA5fX^Og}o/Ռ%hh_K}#ҁҼfy(4;UDS0P`Z_{)A$sc2hʤotʩ}xT7_ҡ=+ѥ b.};g41*J*O̿շSq$kW`>X:}Zgz^|V{ԌiZ*DuLt=!@x_/+]xv.~Zc8lZnqf vc;:ע;y_W3Q^}׭kJvB-<1υl_z ?{C=WQ݋winBۻ?ȯN+C&\W2־&;[S1h:oW]G+to+ÿfbEU+G7/So>+6ω6R$r%V F9_re%ޡ5zRp7zGECXb4iܺxNJ-PH`9~s}`?u?IeAZ|\j1[Ư?'"?_R(7ơ_#m崸xnlp9V=MlC}]xjJxrN&Fy&4gj+''tKTyWL|8\gSXI^pbSq?:,Uisւ1?/i&i?{`F/~f{UZdjM k3^A?}ĚG\xGʨ)'MzHIhc/~5U]WAwS&9%O)m__,\wyϴ& z?G!R1E|^Ɵ}ph-??X} ǔ7Fȏk_=} ]O#f|ԟQ[>s+ϲ?W(-psrSZ6gp* CW#S~,)ȥSUJ<1sk;ç&P}kJa?&s??N(P8W~(X~+{qUV[L8YuP|TP}hV:/}YUЫ4?WWUh|3n–P.}B^?t0vgo־"_ۖ3+>+5\W5U2/+8v~gj?1^ߢ&^7ok G/SR?V O+7?V {O+Ͽ_{KEֈOk+5ijy>cRFjB+CO8|Ec53!W?~??eAZ꫕t?K7w? W_~vb(:'`ο}Ϛg! lϔz))hοZj^hWqOOEXP$92E^5{#4CBjkz2:7?OEEpAvŗ>?&wMqשRٞGC܋;e,#]N+͔:o)gi^fi#ha*E] ?Ha?1K ?OWS^?֥+JW_?{ltrC2~bQ/aR6ROZǂIs6eM&ӹ+l>}T{/'>*5ҒՎ%i[9WeGZ?ruiƤ_B[J-_=Vdϭmsx89kO!GO1z^NgO ^Q!6XMekNN}?ƹGa9}O-#fmGL8fV- dwVK7j;qc?TR(YԒF:kRYz^_a4|}{u1IV=uoiVיGM7~UR'N2??5ڜz%tPpzG풎~UJFZLUC~+slD==mlڹU{ _+?fkUOt/z?|c^_(v Dj_.f]QOmvR+JUVa>K5};Ec|㬟]\tR[Ȭ o/K+,uVe%`J'wudȮGBӓQ౑RF*Yzuar칪2߉n[\jqjw_l wM:;'FiBaJʾeNPV`^J^ 3yʹҷT?]O<&1=]g?/?5 |O`eFw[z|V*`-3zZA_{+/oc;#|Aysq>\ҲlFq[~)|Auc ;N@?֠;{Գ(SzI]\+9ʒWO_"J_)+S쫎?c;26;o6ze"ePfkuHW4 55)&;&|62,~Jz6i= F?ƏX1?Ƹ?缿^-|ʲGEѿu|?޳m)ud$V?`[Z ğ nl/<ذ&u: yR.Xw7X\\o%FS?򠅕[pzRJ_uj[MH 7k{~Ώh阯ucBr4k@?瘯5|kO>5;]-u>oO3赬m?:V/*1dץ3n};0OMFXy&zǬI קGq_y'yWsY#%:wDņFFO\XL*=l <%NjG漐xZ`tNuX?c}3+ + W1 ε?gk>#5{e1SJUbѕl})ӔU=?7? }_=xwźf ܛp _' 3f+R;ZO*=ׇ̐ߡAH?ЮK߿)>"q]GVUM_Qonʙ QO¢?}p5V-/ۓ0R'5k| +?2?hyTgU=_w|O]W>CtSo,Yͻ׌L0#BT ͯaFx Pӯ?"?+y[渟W?Fw:*׳y WVR^yZNRNiZG>6j c{ RFѩhEiNZIU%ԹcsJqū?Qe⾳>9lK_$)?n&sv4UeEh,'׻X ho_!*C쿮38?ٱA^qk_E?εPla-ƅҝEQ'ƱЫ>#CǤ74Nx'C8S7vA@Q)إjAyMc_5|1A5$t? 9&ċǻG;m_Ű&#sTXo1u}n?☋?1رE.8 1Lye} W?u_mWD:h@SW&C3LqןÊIubΫr%IR!E[Tީgޡ ٤XUЫZ+?fcBrΉf阫__o>(k?ZП ~h2Kel5%%${z-sRW_5!t?oZߕ υ_r I6¾RV®ъ2lGK;^mmT֯Uy"4_\q/.̱FZ?ϼ? X^@Z|V%X[c=!{| LSX?7HUZqm?|c/q ^\D fx|j9cP+#z*:׭x-?+<6>*WĊ,Xc_ʓȌ_ʤa[ \}+׀K 5ts~"_5\70$?9WAJ:^!_YY#s6 ?Fݗ|snؓA5+ u<ּݵxD|dSYEGcik#]?Ɨ]?5Ьkךn?*G6o?I? X:- 9a늊rn[q"?ux W|5WWE(w%]#5o^tVhF?ڍ_v:'5']LMv k &2@o?/R B~"rƙ'q|r l"\8%c7qT?tUGauT?tUr1jF5cBn똯9c}Bml똫_2_>N=oRW_5ǁS[G4c᷇Ha#=Ζ} >XCҾH'[=}dnBKDݝB bp?)P▫_~!U̿h?D7uռ b'o!¸'w1ȯy_m-zEy }i$oCWF)GZ]*6CR!޾?l<&cu5SF4VxKև"gLQMݎh=cqO?3 ;'JCz&ڥibFxl ?SiY"s_]=4τit{?3.t|vO}y ?º?#4ry:>AZ_UG y~Ʋl]ߙ$1֝<!<4 Ȍx<)&>cCz9_T'UI|y]x#t BUCc9bdØ)SU$}b v_G l>ߕ#؟¦PA)rNGӞhaΏ0GjA_TGo*Љ<+!Cz¯Z“(v6Fzg#Gs%sȯ/K{b@ORzV|D6`g *>KRs,bM> Bk fU ^w`y#-1qGMa|7d xj@S4ݗ*[-{օ|}փin''b9۞ 0hts+.̏z^ Vsڢ- ~ খi&Ѭ򶫓7u{:zѳ+_E|CjQ` H }*y?~Gi:vc]+Y%]IMOn|;a\+Ziy?_>Tt\JWH ־7¯ [u57S͵~U 01f?ձ7Gi'$3m1ahςe Nw#7L\_U?ƹgQ[:w[ܕ}W>Tާ4cM}T| mwzjT=fFt8>h (cКi'M}`| Y̬y rדr)|%hlmBy߾G@\DzS}u3Md_S~ 'Lyp;g=M IuPY7WcHA$-Ĥ׀ɇi5 <Lfpvx<VN#?o G?&_*| @7LQtGAxIE12T<@QTn &}k?/K ?_.|}l9S+O't>>q'O#Tǻ8֣)O(Q__*]־9Ec~]* ?/OiROɴuXk<\o%mwq=;QH8wT Q724l*˜|W<߅ Yo*>"ko*}+iCۏ?^|H׽m^go=,Gy-v(a]zm--tQY 놯um^$9WR'~`uh5nS[&bKd9>!NOUG?W]w"PD˱64,M89 o#I ik t?*=1m'L;'>i슕RxzVl+տXZ}xݟu?jH~"zF#=F$9^mwL*G׫0}Val:6O?ĿEtm(#~䟽Gԃu{EG4[Gd"Qe3>O!OJtO*'ŏ2m#M!'LNL6ڗwS ;.:&B $}ҤID$OS%JZGo!pw L?nx:o'Jl)*(n>( sU0_@F|ISeZQ|>[~0qTrpt7}<8ńe1KBp;7O*b63THtE5ܟð\'O*O^1LɧF E;:")~p;(^~OgAgMc*:zSWM?p~TW}Z(f~ߩ?6?k >W}Z #⟉/&UMOq]_ռSj7il% 09wcXHb+UqUg7.Eڗj({EqG/#!^C%Vs-nrVok>%|1qBxgdžϡAjzoG*1TWК-Gm'ho^gK4k>k[O_J hȣ(G (w_#lj p?|17l F@5t{I8Di^E<D]OWGix1Sk~OW J48*;F;-E\'$\ W8æE(e"5,N}+OʏW{+ G݄iil9U僝S#ڌZ_?<"/xdz׭"I|1.xR*8X~I+H/J߳A7M,s}um٥po2x#=W7mPi_r=GƙRi?y}KCx~uyQq*<Yy_RԈ8Ky>(ak ):Z4.Yjͤ*bA`zYi%ގlr,,(yq4#n+tP?l:BZ㿇:pwYՔg.]HCJl C2M>cn!gA S,m<`s^gx7R-Kn',lC!"3X#]x%"H,籚>i7ֽ #k]Il۽T GP>ƨ_k>ӠԴkk!x9?bp +kYMόm܃b%psحkh^jf~->UӍտD$OQdgj:+*\'8|18G" i _Y 1<߱G*z( ZϴQ+_,v}}b C~chSE]}}chSE]}}bhة.>k>sqV(*p;ڽ_?`&9%HHF(Wz0{'++7-'0[Z?A>+X%DO tM8-wzy8|;"^fxdú_z ӯ]l|=OKIKL~2Y׿kQφ4d?s1Ƶbf5x#EkOkL/?xԴ Nkww|UasB++?V|}3)CoFl_v5Px_wo^Q/fx@WTYPxKq^{x?տ$c[VGA{KUuO_HǴ_7_Zޗ#ߣZJ+4QEsw_/נ|m4f>ex: 5+S5Չ M% .@aZ_zowGQ`7 4|½2@`xC]3I-|O{}j%fȨtM{i# G't6uakyjwUA %jV[R8hx7( ՛ _WZm|7sumo4r5 *rI=W\ćsMz$!0#!ߴ@\n<#}JYN͹JYmWiu㔶畀'j($Oijzm'(08=8",uk4 {`#AK5CkrxSQo-1nE&DRٴ' ;hd8z׈ޯ4+Z׵=UN;JX*0I;<ҰZmy_xzu桧ŦP$ ߐ[9P4WG5,2(t2Aue\]wUCm4P[(QI ( ( 񏊣>%q㿄Ό/LO\QS93չ|EBcQ( Auk"En~_W'L`ӭ.\"K0>`'Qב^Y9V;GcE+bj^ evxncXA)N½UR'-)6{8|BG~W(Q"Oeo#W8ЍtZЍyk."ƻTjUuXcPcSst+FܙaJ% ne![K^/W_)׫sG~zXHEq%pz+:بN(j:=-B+23Mwk)9#9Smgb?y??.Sj{KgW/uOs)s)fw >f7eA3Z?97Mz+NJ_Q V=OI^3\O%zO*+7VA4cqXm48fgG{Fko +-YOuIY d{o^ ׅR=N6gQ!+@/ߕ6ki?ʾp7}O `W)iNi0^3ZtV/?RvOʲ:>3k4?+?*?c$?ó*]̸u5vמHf=X\rPwZ_O+߫t&Y>1 7G7 V}o\2Cat"U$p*y8ÝxKVZmɊQC@?0<]g |6TZοgy%YOoxF nRi7:$3GM&Gvgwݯ?Z(TxGѬn۽>{U˯ 7z{y[4FoooQ@|Uz81Z^yļ`(N1I8/}[Bo-Ā TP_?ĺ-ƕeb*ÐA~X\hn뚬rIMyykY#$V{:(AKVYy`(Yohj:ͽ[PmXl/`5@E"K&PMWyK5ʾ2G<'u3C:,ȥd2@FN-.l-]u]:[CRx8=0c@db'(:qQֿ۱{gkh$Jn]>?IV弹Ypgz:Vx/=^cAkysnϜ?ȣN8J:V^ڧ?Ul.-Ie$4=|O$(sG?tho5cI.Y$i>ZJ={|5-H`85ni>3y^iX]ckxh`xų0W\^Z_F4O߱^gF$esi8'tަ?#'?أ}vV-y.)/d#L)u7~&5'f 8?)Q'WF?o׫'~V7VkFu(kFm+\Q_-}dd5:kGk;{(vGx4\"BƗ*c]?u3S#]&Q*zFw¦#ghDbB1X`#+Exú4$Ή"?*#!1Q24n+o`@ttm *ǯ͜U;:.tn^\Eor:p "ӢtƝ({YI=sUSTqqZ@[hes f.QTm:/o/-2m'W|F_iOm|2UG)E$|yFckYj흄woe72X=+2vDT`+qk lg21dap#$hv#TD-1/񼶑c^GEnrI9?ZḆva9& Fc88y{QEQEQEQEQEQEQE_?x_Ckb^ |潌~DKZ"|; K/+J^eϰ >)q@xRh_ Ӿ u? %/G ^4)qH;Ҋ煬_?[\V7C\Y hd"*| ɿEtU/vRRԝ/ CL'kd}r_ޗ+D|mO4( nOMQ]ǬJ,OcA#Dײ׌o k٫6{Ew,9IUO}^???ʼ6kՃ,}QP(>(PbAORFoKiO#_Rfr1_e|?=G84qAЮz/ =Ctz>yqRV4 펋"]y/xWlTASF:>.]K+Ns^@wC2_IL xjË? 8́h1a紻FBy{־6 W(Jgҳ|%? xOlK n˴ʽ7*35-C1\VY_Rʹ3"̙ ŷAco_ \ۭGR~bye4?xV׮cr$q|g*]4G4e&$C\@ei|5ӵ.o,/o-4W 墖o[ rzW|R+8IlpW] ţ&::pjZWO w :Xk6S"BrO ZVZ Nб״X!G17 T|N'skx%፡PWZf3Y0)@[ _$ǤYh^Ž.$, )/@<4 ee&vN15-鑵2Hʆ`q<֚bX1hh((k6.5M^" dԁNլ.uk2 ,o5|${($E2c$He]A}QER3R@P2II4I,.F2:o/JJ񿊧+Lӌ_ǎ:p8('Wz)xxQu>J}u? <+%ƥne. jCڧ5»ol=YR]ק4gPJ<ˏ4Yq>~fs׆ɿ'+ ϓd,5}Nj ͠xQ +&,qwz?hGno,-Z;d F=XוX[t5`s*xn$Nƾ+NWM~to?~u(Η0gG^2 ?d׆H6sV~_2{8&eݐ3ɧ1p?ټ=ݼQk$Jh _Ϳƹ Cn(#-939?^~*8g?*te%?-ڏZWcNrfFt}?\/ =3Pde6܌}+EK G ]lӕ pʻx5݂Ь*Q5Xl\&ܩcy>6ѠIao}5q&ߝ'ׯ#^?eυԣ]{ m2y:_<?{z7&+M#HTt9za?7aG\oDLqt9CqXysUM=E<!_DW+N~cЅ}\~:q|uuÞ5ˍymGf~Xx458ƷG:Vئx'APdEu9~/rN[9Qűp+g#h[|:`J/_Ie|(b9U8D3Lڶh][jMqqm H%(n=1q<_yI/5Hm!X9ghAFE x<ĚV3J/4ا.ۆ9D!{QBLyxWqҀ9Ov?_]ksŭ(me.K^+{ž'ώr×}(˸ wnyH1|xx"\k:dr5ݝ-](gR8~9~]"=7\Gt_)e},wO%o 闒n"|Zn0?rqw/u-IPދK i6%Cpvk;9|C|YN5i-v# UϢY}9n5 }10Yg/ r>#ѵ'FxǁÐcڀ<Ɨ~"o[wZAF5.jB7$vu5^]QO)4&ͼ%ۉ$|tpZ>7{mi}.H\9!Y!X k:ύ5QY,b$Vfdn0r1@{R4i캕_q[V-t՝Yn}؅8<_?'Y(nl K0[K43K12,remg-[K' qo,[G\թ4=_W5Hf.C0(꠪ }h]&g?g`8C媆S A #v}jZ̺ml/i  rs篨xnĿs,<9s9JKii)< NBǘvq[eAh /5"1mqlGQ0G'߽q?Vz^ ZxgBX Dw+3*\UPWVz`=q㿄tr/w4SX)<+=Ct1^M{=ky%Y֝}b?W-v>k?J_!yaY}Vĭ// >7hUbxA+5oK~ȷ{_g@Zj.z?פWyOS (c"=b7K}{ő=b3K}}C?XLТ>J=T{IČq?|!Mw"oa1]_-y%VC^Y&Es,bk?Xg3%)ٰ (Ad`1ס|!n"מ̍95_/5?Y?״\Oo_FWZ!']ӋuLk/du7AEWqQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@xŁzd^^5dKO\x3 C`CRoU;Sy>'@;#+^_nK?qgc 7cgc#|Wьr|&#,JhO&j_LS|OPc_~+޲쵹E:`jF!1y{9=e46M+˫Z9?N1@-rƱr? _ o~~g[\Ǐo]4|Y~"V$([9έX84'FtSȻ}_G@Zz ipeܲ:{WP>.*o?¿8]lTS}tkӌl+n*?—+>fY"5b3J4A]׌~"[k|V!@@w bA9JZTFͿ+ O ~&kbHjq#W_v>o??E=ToL7+F9 9STO"0ɿBM3f@Pq]} 1T*Nj*A]E]} _K_W7j8_NoO+bko۲04u!=zL U4W_`hNTb&N*O=3@}+|ϰKQK G~~}נ|"':?yC7?y$O^oִ?#{ZψҼIXey"q3fC8Hӿ?WUE|AX^EurGnȌdW_fe%~6F~6#`EPr-cLx,u;x]ȯ3լ|OQ:xr[[fI0P^>nm,/,!my{nנqHˬi_^۴$'sN.;|ɯ9 .+?D}-N~|{~ ;xBCd-Km܏8TMg,zn} sgkj.n ,BNI>yV ٗp##W-eirg {_,p4 [* rq^F-lmᶶH (yMG5BXYj-v%/Og/s }Ҵo}7O8o ;iik|MW/N3kcKh?0~[hkSPy3~Ttd|GE -O8ACj83^x>BE8ZFo?!;yBlr0+ =>3iۇK!_GW6]coC,vā@\e;>д6DmâdUn W7vǞ6 GkLF |湛:-nk6$S<{eT>92+8L5j0q 71Oֺ{h.0JJ6xF&VڄKir"m(q0>mzΘQ"Fa 68<4lb! ¢÷uWB# |uuim>-iD_fG٢yOJ-gCݍϗm{l[]RG;zܴkSYz桮i:*q fxma « ޤ@ki&Hn-XbrFT78nk;iͼ91xW mɧAu3!۵9+ޑ[m֖˫MomDb*N{v8:!=)-]!Yq0;=/~"k_.5J[BT 3 c{mⶾ65rKǦifO5XDȪJе?h0𵆵j$8Oe!s@6W^FTq:3t>+Mh: 6Kڠ (~w7F;O\zjzY\Y1*хS؜ӂh(?⭏Bz*ߋY,;_3 CVb>S#8C9>\#o>ֽƏbP}x3ƫIi|cٍvb߃L?^1νFypZxU<6=?k om?봿ڧo?l|{g]e{-%-2BNGbRo ^^}_E?סP ZJ))șyէ{ jZ/ jerYKx[V8^wà.wҖԣէein;mp01^2S^mԿ}Y_xI%QwEpU\碁Ͻw\hi_ۻjϭu3eqn#>2F;SNԿ{K{]_Ko5}CQԬ-Y^?,ł c֯oBjKe4/%]N$PsC`4iwR l-:jVYxL1ʲ c߳qfUG AO>Yi+kWn?E/yP@*\](ɣKd}~RռGX\ݥB"RY6,W׵tu% "0g%7Y5H%G/ļȸ^ @]p'h1TnwZ^e$CT:Ǟ|19k6diwR{*?tmoĚy.xPSNgޟ>EO$뵄ms9oR> ')G{o\Ӧ26q KVeĎMhSPhZsjq-F EXT =3^lMf7?<toO/_5???3M>.6|UB*Fa#b9mcj8g.k1<[Eq'Ab[{_^=+q^[?SH9izU宦lv$*NI.Jj'_++8l}R>=xKxj?w/ W'_*|˰zi5?:]/<3ό/ٌ`V [麂^ٌq^X^1F)_}$>3x\]@Srx_*lfar?\?Ƹ>=ѼSZiFΊJl{Fݬ=}y! m4.8ڹXRJ?iߛi~ok3@Ec75OYVw]A$s^s5 ۈKpHQC|qItv4v&Q:ԭJ5?ΥA=!v`m?_H.(/#QD¥"P qGO`ס|#C?Oϋ7dPiZVoZzUY_,mrO 㯭B IaW3<  B+Hlg*1㓑NQJFс֛(󚍈;q&cɠ =̘4.G"!.;qq<Ո>c6:ft1UC;>ƕbjcwܑ hzsH6օ4*j`x=>0`&KCߚ~.qVO5R@p:MuT~nFAdpѷHu z z߸+zUeH)=O5qi3:-?(o֧DZ?sקpE)? >>cFx:w#K<S^:S3EZq{<OiA'5Z`|bN>QOS=*ߖG:-#^4QA~DArs_[ܥB% &6/ ?S\ Qe\?ñA~߭!_Y*`;4PKǓ-N!c^Ǹ|S(A^*Ó<~TD|^GsT}r}Z+“mפJxE/K<4C[߭/f~Lxao_ʏCC?oΐhmq*T_J]}Z+n:?yCתymSMXV?L_V؄tC puF濕"[<هՠy{h!;?Zl:)%bfBH"_^6G`c&G.KclW}JKEBIēUb򎵧&UQ$1{Ҁ ¹z#Ԍr{Ҏ21 xW#WҔ"%7gv0E8C%=F=G/J/pj)Tr) x4S}) RR:GJhL~qR#ң=XZȞ!Sʊ9a\`PtS|x*̝`L,+GJ&N@*2*8H4JWb£8+R9cP3Qi ]r}ʣ4^n6(EIv?fotoxx-18.01.1/images/blur-background.jpg0000644000175000017500000004117213222767271016717 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 191 300 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DP.܆"=ʼ,YU^I<-:<cs/ڇ7w aƥ iI=(ګX6pz$|?Z߁< q^K<@.4xJr& '$!1j$c?[|EF_[h>KFEHc08P!Xu)'y'Z:m /xHFv><~;7p9'hXfb:eF8\_? {׷o'?DV?ڽW`2B,S-Ŏuyj|P=Wqc٬Zie(ck؛BW,DBca4ytZmvrD2~-MJ8HOb sV(E+XFnHp,<~&mny.,Id_2/̑sc=1E\廞et|pM:+{˹spZ~#A7UmesvBhcD-#]­F=֯o=4RV%E8 5QQ/V{y>Z\OIcu{-$ $OAIBA \B]DJp~S:Wz7杬YV[۴Ki2(*s+/\IQ 0y`@?ϗc-2mvu-Tҟ4'{_G֞fh8_?_Z=?gZ={Ki>}G;YVG;YVG+ i>}G;YVG;YVG+ i>}G;YVG;YVG+ i>}G;YVG;YVG+ ;g~i3~F7$k [d74NTto8:\l@6v?? z??? zo'm5>8<'?/E%=YKk=4'܍1yG`_ORD V(@vĊ:g??? z??? zn;h4#'+-K;i1y6f%3,O>U1^ H 9.,mnrI+ ?gZ=?gZ=-A;l|Ia:_n|%mʾKy4mRgDi#g[$ԇ5CW^W|kq$В!Ǘ"N@ɯ?? z??? zi;OUc_Rxvx[]J-WOMp ˼Oni|]Z_[|e<?? z??? z;)_֧h񵿋T~$!o%GntPǰwn=?TԵ-C\77ږ4ɾl}bP'_?gZ=?gZ=O0xO׬_OoYos0e"(k' 4/5?;h2}DP".c=_søşkøşk[Ic'֏Zq8?*q8?*as,) U#q{6P>L|}qAU#qAU#f??bWE\ؑ*O>W[oۂ4;YVG;YVE\DeN<ւ >a ό/,4[K[w8}q;YVG;YVXWtu15_.Y.:[FH,mMv=Č }O$\W#UxQqAU#q_Y_Gp8(8Puf9/5bH \yGzo`Z8-o&/-X4<'_%dgj=+($KPf4T,{54>3H5i2xOڤvǵ1aq~F ;0 _şJխ Լ'kvM\:B-o$2% r[[[]Ʌկt4{?x/on4KxGAI{{.#MkGao+y$0|+GUT&u=SRbcWoc wzI_`ݝ{hcfWC{i!VU[(.MLYd`%6U`M=<_wih\9Ԁx5Z[{R[CimYK.q$w q@C:_|D>@Ku珠徭m 0i][Ax`HfPŀsp};sZ5/6~a}H$;Zp4%<ښIО掣:?q@CI jƜ][GisͤC1C;JU$yQֵ+_gTn((((((MfY5^×"lmi [͑L1z>'(iT$4G8 /“|/ׇ5'T# KOe :oVS  ޽g۸gխ 5П -^# 0FT".ӓG: xY׫C*;[~ќs#=Ԅc8B:[q3)݊c˾ Qbg#[7RYTeW9ry{kæxjRME.R"3giJ*( /HgpYUp1to1ۋ{CȪV 9#*16z gF &{HχQ7S@$lW + 05tTm ~ `SX|=xuVW&+7toI8QDnY\Z ˞~fMx\o'čhÞ[63F@Ob0?'vz >E_oWsCPc+TҖS4^i*5\.i-DZm 9YPn5k i ixaԡN" ;7I V|A'/Meѵ62*DrOD$Dx''j:O^t~ JvR[.!Thc&a$ ܃/[/?!7e+>⧂Ѡ sJL8_p veOsV~+(`4i5V%>.| %|yaag$txXR1so$;g+E??C˱񕎮[H;IPn'xoK_ϤtcX a="\]ZeR\ۯA(r@Kok0O7Cx4UW+85~oڽ٘e '>C\-޽%-OVΗrp"K$6$w*?n|A} TFO5M?Vʳ#Yu&436ͅA8 Ҧ{oK͍̓Ik_Ejka54Gcw%8;7&s xWڧMw>/m|J h#N TE$r2RK/]/xkg"- ##ݓl]{y@E#Sa ~)dak8Pȗcp\qjMGs iGH0D\،|no٫.7(XA"(6p| eBIÓW~;#ԼoKNҮ/<3=-Au.ٴ̶H?yN3gU=^o~!m~Z`S56Ϭ[۶d], K& ~ +Z\FivEM,]۶H q^uO4t޵Zsjʹ)^YBUc@d >n#uwp|=B'Pd..ѩަ)}z 6ݶ};yiL &K٦T#%˓$6_4wktVVqpp!t#qx^ ?fhɨjB7́ky';F$N r_MgQ֍uisrH;KyDr0c\@ekw~={O|)k%5J%l^'g#g8 v9ς.]3K: w6)@G"@۝YyAr+ |3񥗀u]"ڝZG.}8_A*K %ݻ/eD$Ė&|wxY.c6mkVZeyobn&W@T]A$KݛğkhV)n5 \6հ8e`y|}gU?ĚERMi]P1)+|5VҼ)F|74MZKF+ďtIck_>%~>lVZ6>Ģ [P xW;o{~'φwg5߆AJfx_/>8g57ĺxW'l2ai QJffc}ҩw (aEPEPEPEPY4?Ƴj_mKVq>8H+LʶverJѦP9 粟J?E ׭'`˕6]bm+ߧ7'Uvv@OGSiPYU/m״}D@N|$aW_VdG]w|"k#Pȑ^5ڥ" OVZTm?:ȩQE((((((+WM_ -> kne(Z!<ֻ*(pc绿~/G}+95rVf=\! λ 6> "GWeݮDhV wZ\˜hi{]Di=w]i }KVLEFȸo7z}[ed#$Dž}iZvx b.-8ĐRц*V}񶙯]U4լAc`sB.#[yexd v?uR^_uo|Y{ ׉ou Ny-E]=w2eٸ I-%xޱ`hv{QO[^c$q,%HUg3E?/k~By mmaK-UԭbTm\?Fv 2ruoJG_zڕF2ΓB8q:)wv X_7cZe_Ȏ'9Z|ǩ㛟QUe0H hK2]g̛yٵּ_ ψH>i*[W?Ը{&&ʾT4?O -A`ӣO >k(W?ϧ-lRH~#ȩ7|Lu\3 X/4[c'HP\V@q hj_-Λucqiei 9H+"c4h@QEQEQEQEQEQEx|~4=5|=xYybHH /*4eCBA#=r^%}p^A-ķD$dE#pNNAϵC8 ?NOfmu=yappI<; ֫K}߉t? O\%eoip!LT7R,2<åk elڼֶiowpRYG@5 >2|yoy5$Yc/ h.}ޜחxo_W_ЧMj]n5M&[I!7X<>7׮E ?}Nߎ (ԵMúVc?Eēm#s*$1*L+g |:ԯϣZXZqo1jK1Q ;sē|uY趞/ Mdm\݅I٦S+!s<Hmo \i2J7%&&~A b'#G ,+NV[[RhDiIgH5uǫWۭj6^-kë-Ze ؑ*9F+ kh(ߩkRib=J(I/q_|Min MVs|L1-̇htY$;PU žc]еhZKԮ+KVFO<2ːO ;;Rşa/l<9x4V˩m y<e]ʑ)knﳷcM,Wue>>>,]x'Z헉ރe}k`mGS<#5ux~5wPcm-~2~J¼FV?!Q.ƿHmjfQREPEPEPEPEPEP{BOUkm @Oj$Gl!?ƴ It BҢ3%?+e 4KVkJH' .A[/O*(7]#_G$Gl!?ƴ It BҢ3%?+e 4KVkJaVRiI+`س ޤ-?tUCQ֭tDϒW+ky'X1@?EcU?%Q uk&TOiƩ^y+k\^L'{;1B_KX5??Myƨ$Gl!?ƏIt B?N֭uGtώT+y T1Q_x]O:],)m;]#_G$Gl!?ƫ]g> ?YϦk5@%?+e 4KVj%R=uqtk?UXH' .A[/Oq_[fi2b`8]s_X8 yvg]#_G$Gl!?ƫ]g> ?YϦk5@%?+e 4KVj%Q uk&Tg]#_W;XdIbat`Tb+*?<l]i0F 8d٠(((((((B286gO[^M^> CŒ&9[ώV_,5u:͵[aDgMO|= <6mΒEȩ%pE|Ũ|>xkX+koom|5.(Kf1E^Z_(}{{ϋZ>M[P oBؼp4ކX*0:*:u6^bOO!yɦeު 6bv[8:= 4I}g[鶺ͽ{xđ@BU3q >tZJu=`R Apey5hvc7P5=6gn&.XLTKiG@;MJߕ2m7\NtIi A Yzi!pvl-38֌Ŗ~1°YeYJ6cW9Ӽ5j /A-ͼB%X,؅Fq^ uK555= kyivPG$ ;NG~'x3[ФuD8-ESl]ͷ>-tyj~/5wdoVK;8W/#O$1.I.}!Օ_^ZƳOom{Bd3bT'@⨥D֬5}=N BppG^otAKᾣ4qdف]"~`G,Mw3XS.,5 C7!KfeYO.FPU9 QGin|Ek'5!֍YhiYXB.e7k/> :~ng:>/,i@C d.1c:ũZMg-α\G.<$_X0" (Š((((((_Pj){ϩ !RƌX~vPA{ߵ[ GLu N$y9kg)@xPv3i]6-$7vڲbI=&D.5~)k!q*yתQ@u)4DpcYcv} ?#0mfW5B{&mRDD6eHw^уj(=_U_=GEBZ*(~/o*P.FHV{\*(Ouໝ&vMwm4r]p[si"fLwVI̠zk kǨ_U_=^EyEBZ>/o*WQ@za`l͔3O=QOS8*(fotoxx-18.01.1/images/brightness-graph.jpg0000644000175000017500000004553113222767271017110 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230?0100Fotoxx:trim_rotate| Fotoxx:voodoo1|resize| Fotoxx:paint_clone| 7http://ns.adobe.com/xap/1.0/ 1600 2560 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?ub* ˚f:tځX%oݳ`+w5mG2Grг}w}nö΄^6ݵc٦̑;T_̬yjoZ<~^69klm(ZOino}ݫjEq4~y⋭:mRٯ5ɭck]5W5[:^l-A5he_rlOybEh ?XחO6v5{VŎg-Xu4G'nXo)GaThv?]䶺uKu[VIvݿ/V5oV.|lV+u5_G$nZ)}۵7Mv?ۯ[Ol`fXIw7oX͍yut%Lw _|ג &]^f{]/ٕ^.ncZ9?w2}@v_|ӕo.=y|GY9֯&Pdh|1ܲ}w̴jWdθ*~fjb'vmM~ZO56iO!7ͺOVƃ{^O iHg"emwP-1̋dUW,eFW5GP_|ѕC_|ѕCzr)>؊)cG 0cͣjعJ>ejG9?ۿ*,{-mIV8Fo ;4~̯hrw<\פcx}w۶ ?o&?o&f>x%䛛_ZlwkŬnX]k٣>fh٣>fha6ɂ-nިh6h=s}Sš>}Y'ګn$rwjZ4[9R̻Uw4 M4 M9yo<5Ɵqcc,Mu7ZMg+MσmV;7+2s7~z?7F?7Gxwu.[Uhn>'ۼnm> y-6ico7ޮf@ls34r9yDŽ[;7_&Ya4ʭMjuK[;[w=$_Wls34ls34rfh٢>fhs}Z.u+eVUos7;7º.GZجWJd|4 M4 M9y~`Kh6ͻxWE/OYdUXHʿ*ozF?7F?7G(sMmAwj7_e~bcOUv6hv?7G,?ͣͮf@ls35mmv4_ M7f@Ahڮ}y?Lwϵ=GI oo+UY# փVS~k_Hu;CV;3nw2۹۳Y{ydE+W_~ CL%=\]˻\7;׶~~&|6}Zokux)U0JMծmEWdQEQEQEQEQEQEQEQEQEQEQEQEQEQEgk ?Aj٪?Aj: 3Fտ%?o kAPʟ*|j=-yʻ3mV[uox:]c1xg_]#^H.zvsG4eoM綏ử6Fm­2?#p_.sŁͪSu5n^%xy/ <´^P<}sGT*QEQQEQEQEQEQEQEQEQEQEQEQEQEQECUY-WAT5_}/|t@g71׫KZ ҳFտ%Q=./bh/ܱkyr_g/k9{ fW[\j{N{eVo V|xfOgkedS4 j߽5!(a'Z_n91gØ=9v?,tO K yVw"ț_FXӌ%ғERSpkox5 gA޻[^kVtOl0*4"o5mZCXx3nhնwW[U{˖&Q^qaEPEPEPEPEPEPEPEPEPEPEP Wf_K_Pi UU0^-h5g׫KW\5gQ^-Tvg~xSȫI_q׌SY-Om}ag7m&Y#o=̬{&/E MBE\V>$ |I&k6Inտݧ~XߋV]TYJ`43پ0i><4x_ x7º~c񦸼y;w+2ײ5^.5Jvy?4jmfeDx#l>}+ZvLq%s.j:AP4iy2c%ק=}B%Q=.Pӿkg=Z[,-BMCRi&۹7gnUi0[eii5Uʿ)H;9OF,O"&j߽]_^xWŞqL5.Uej[-oG_O=ֽ_6ƫ$p#mmp_d~ [iZ\:ng{cqoX@Wk?7׿b)Ύ1ZRܒg Jӕz  kOPc]U^Kˋݪ^_J\r9%8×Ws ~|]\CeGG32/˵¯jzXu46ݼwm4UUU~_eK #=֊ށjriڵѳ+@+/ޭ9 OE"iXJRѬNQQEkcxQ@Š((((((( ?Aj: 㠪9`Oo Z棢xe?[{ۛmF۹~]9?տ*>32fhU[T,y9ORQ<Ϳ--7yL{ohw.[[ <+q?kgZ:Z­$,?Z/;=Ǔx5 s|~ee{/߇Qcσe,WM]W*l(QKSֿa0 /]OxK|Bڌk=#GvUf}ou:m?,/~"Ӽ_Cq:{^G4,c/[vvহOj+C=Qu K?-Uc۹o}xJrc ^M+'ewMKJn6{ 嶟p/𴍷u|;k ~;񦙠Xjw2GIp7ݵ~]TzD|iqq[["IsnOj/۫ O|;iۡUc~_3n⯑kWU/uu3.hZ}n~g|] j^ \lIy%n3uV>I[i@&Weɍdo&$G?wv]ڿ"eV=Z$~[+7.寢drGt;6 uI~yYU2ZMy6{?ZKJe1%ICw?N4K[դk!]|ܭ+af|7ſ ѣ[[xTd_Wvݻ-:oKUht'sn;|HnhZy,v۵mޯ7uR{(3Xn"1P%V~Z}Ncg4ύt/ rYnE4af[۶| gGoܵ$mzlھUӮ>Y.Ykn]+|~Q1v1>aqLz_}?H4vP1nm*֍kYJU5 UZGeXN |ygJejV2^k,F$VWݏ<%)Ԯ|S9.÷ g6eohVO|0V|Wiz} ooyon߻E|oᯭ7׹NcqfNOHS\//;Of/ochY[lqm|7|;׾ƾ:\,u INE6_&ݛjXE9^V9>3OĞ񟁾"POiImuCeY'Ͷ+_7e<;Z fmKF.o{jG9%W C3=-iFK]f8µ[rXiѴ[|/-yv3w{}gBg|iok ا&fk|1O<+-Oh^5]Oom$,ѷW6_;M,_5D~Т?>/| hLJ%^i{r2civm] ooB|&kI׾Geob-wTjcFۙ[2^0G]_|q|zs5V#L>Ҧ˲UvȲ6;Ķq40jvp fѳ}-k "59k+.۫gO:|5|C;1t775ڎZ%z2;Z'rҫE>WikM׾[h4 tFkImn&̻Ws2]jO?l,5Kۓhݤ]2խV"Ǘ<FݹuL+ᶱi_ݭ̿gVգfۻs||`fKm>5{!Y<ݷZ*9gi8~ 97UnMx^7n?|6΅4do%[ͷnmo8֮n<1? i6Vx}{'͹chjW?a/',J\Vխ<W;ϊմ贍^k(6KJ5#煫*~$Pi UU Wf_K_hr0G^-|Qyj l5KGKhVO%I>m?o kVxu?N&5_fBeN5qXJ?oLqknO_ 5>"[ˍGgif^,[ܚ~7 llJ_=o? XHe[oos *x>'jNey~ZV?wijQN.Nm-RZl YhQ\玼y8濯}KU3M崛w6ߺyY(ӄyt\׊b/-%x8K8df]M~|^^2u+O =|#2Sv4*s|_K?J[R8K$o~qJu*> /<x#wCĖ+ku\[,2Iʫ^HK]0)bq3Uy +CM 兽- )s }]SAJR^1o<XϦ=+/ܻZ,:׃M+W> ͥȭUM߇Ŭ<'Jq-#DgR[,|t>.+KaG$22UU5ee2^)SƩA.)X$q4"c2fo}we_%/<_J#MlfiFV|y~Z詄-xh_.\QMsEy|owj0x_\QYgZ*sn5+}V_xiK}1νuf>󥢻7={-mJm6[neܻ8M1xs-yx[xzh֩N/WW~ o ot+qksjr4qhm̪­^Eya핏o jx,MZ^w>=-e:8d3H__#Nox]&mLa7ͷoU6ͬѴrC*Yk+/.4^еoZ}-;}:䍿7gܶSzK{ =š7/|I$ɹU~2ٛY'į?5x.SK#_Mu qyn}a=TuM>6i7kVh+|2t//}_?+/ڧN]gMоA/YiJ j摷m/> |FǫGOBң h,ymۗnwW{ῄ#z׺'t",MܫF>'5QR~YiǗNg]ącMRQEu|? nr ( ( ( (ޯ2|_1_ imjא2Ʒ ەYԛkj>|!Oxzf4}֙-K9YcۻoF/xó:| a/!w˹]?|^Oi&OuG#Ki'$mk֥U㈣IVZ&Y$H'oo]_}=$jKs^ϴ1j6):Km..<7o-VYi+KOx6O jE5fH[?ޯTZ2&&xtqܮ>uae:xP?6am ̉yʭR>$}oE: e f۫4+džfLgl\\ܶ3Wߴ'_?zt6Py/^T2Ƽ~)JײV۱S&:~nngZYW+~x T<'ϩGzվeWzL\1ܿ —%Z(L)KE_g]gF͠dXګ܇AYڢſQNF:Ug*yo /5Ke4M_Aۯ`[}J}E'!m۫sJGAZPo}'F0m`1l| c*c:GQEb}QEQEQEQEQEQEQEQEQEQE[Qԭ+YvsǹZfP=+Q_j&>=R$]߼_3mv6;a/uij07/w>E7Zmm K{٣e˹vj;4 xxfM[u]?/_GQ]q1q-OQ|#ߴF. 4-2})nFKcmʫmž:ST96Mf5_T|<3V#D]B׎FׯRVT+Pʏ:-Ưbږ_-ws|{ΟKK>STt>)M.9<[vUo1~X*pتyg(ӉOZA_.~&գ灵>"<5▆YEZ71V8t]?}\Tfګou~U`?mQtXm۬o_& -?P}Ri#|YUm'|0j;K;9-4gۿUՅ< VY{|`Uc~Hʊ($((((((((((((lB'g^|]=ff1YnoWʟ}99|~)?pk>(մXlcմXѷ7̻UMgo'ڲZTw1u1^pCៅ=KiԢ o n: տi V+)TaRo9_0ڽoG(__ _PTa-;ßO9_6o֎P?i᯾'/sx{+VvTV|9o+oG(_BO9N_ڃ/5CY?~Q+zL+}h ';}hn÷o~Gx-oS{}i';~kG(^o-#"zi_'~kZ}Կ2wŚg)ï|U r'zRP>_ i'wV-n2.~ZַG(Q/ō-,_'/ =g?|_Z@ "j Tmٿxسh՗Ԡlr/u[7lXT%o9R.tv[sɵzڳWCጒWז'׬o:HlXSᆨ9[Էvoἓ)sr1w;^u8.صU[]*Ӿ>E5vѥg^]zsE KxcmUmvks_&Xm]L͵bř3ý'+u9O6V] sfO+nhe5}z7":/P7G3m+m]/ÿԾ**ɯXJm6MY#?#>efvg&txTnUmM{ܠzBOG]߆%]j:ckޞ8ץqxñ]AmwR+\EU#Rcj=[2Ʒ i]U?5iwG$Z}+/8\x6[ƻfhmVOjSuo׵~7^Rb=RejʭW(з G./u_5ȵz;o_KG)4?fm4*_w|ۿhKDְi>~ofʳ4ە"ۻq0evW6c7^ox^$ljV|ݭ~^}5Wnckޞ{[x_7j]k^<V}q7rcVv?מR6XRkMm]+ʵc?iNDi vvMD۫[&,}{ǚNJWE$;KxfsI#H|H* .atUm~$ꚗK᳾llm.Ieh_j]mIG4Y-n.i#Xw7*̿jRLc4V56~^y>,zoF,kw#4jo<5^x^,uRfi#rǩD^&+??Yz 查,u%PSoƯi-cCmfXڨV:[II]Z7G/cَ?&Gwɦĺ}ygHѬp.ڬ>ox^=R{t2HdUe]ʭ堣[_& ݼ:*@o9tbP(I[ܫ$jzEglӵ5 ;uYXf+3.գEWjRDV-!Oqj芞m/Β-дm< .2z*n>xu'cnqش-owl ++neۗzxA}Ⱥks/foMVwTv-x ZƋhqyui,_[3+{tkv״T~+KU|?s5k O}W| ÿ$m]]Iy4qwQ qCM=UԛVkeF~_io»=]Rֵ.>upfo⑕mYkPuk5-^->JFDdo/W--s'-[z=W7lnMtl2m-yNߋ&muOC7ojZ(>9Ծx_P]]]KRk XvuWZ 7ږ}gk>mQviw |w2|~]] r^mRծeYheea]?zZTgdHWD69>o]\y?} zG*\G']V6|W3WEc/7#gǦf_-YvտE'ܯ~-|(,fW[ƙao,3VVjoe\_j^o++vҞjsޗyfiOm۷wUh?4m5sZ[O.#mWj]̫wnIGG,1Z5wv7ŏtmYZ9#š1Z}ėVw q#nmwvirh\6o}w4{nZI77mU_h((mfotoxx-18.01.1/images/selective-rescale.jpg0000644000175000017500000003521413222767271017235 0ustar micomicoJFIFHH:ExifMM*bj(1ri%HHgnome-screenshot0230BƠ0100Fotoxx:resize|tonemap| Fotoxx:paint_clone| Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 143 263 0 C     C   s" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?! n$X4,Iyk"o$ml]N$oJAn_$_MGeQ*1|I~{J1G9Ds r~ b9# >fZ ]bp7m"}K$4x3sOZ޷:jgtSa3K@ 㰯R_iQ< MB'm[d( Fn~P1`gsI- $xegR1t,xG4KxcIK8Į .%J|qr9,}'ߍ sK$3( d8 : Mlu}wMI(eC~!xdũiYmhA ٌp9"}hgjmޛ'y%6ȫCZ?icbd``ӵ|7O῏>-xF<=:݆mn[y&+gԌVo~/S_<oEAsKj [gfG xoBЖA7q׊zJ sUA_Y&#XMnnHiSiyyWk9-g࿆>5X~ xLҡ:.+O>eW$?!p1$s?b2_qHeH| JPѦX6YMA(eH }+v/ kI³zm֕qwaMJ&Y#9 m)݇)m]^]<0[F+FIǧ5i1lUҮ q%ۯkfѭ|gixVX$hv2$Eߜe Jt_"^B>|;qްvE 0EG]%];xW\pGۓ !u~Ե{}KS{GWSx>A}xCq#(r8}xK9N<GS$' Qץ`xӅi^u'Ah˫]y?tQKX5UŜkt;\mR|WmI'9#=kvR$q?c4-wN0`c?'^3)Є՚ꪖ'@OK1mIW}}RI!5xo?ahױi7-ŵ`22:c{H@?LcOέ 8߱_/?Ə_/?ƻ6%1~nįPcCAqcԿ_cԿ_uz:6s{7-i8Ak^#Ѭum2/tEŽ_vX ARpArڣQEv{qQSȇc 0Lh'=_#|4n/]RмW Fa bB.,3"+ Wb%2ެa}޾~1 چiis/n4w+G©#|Ngc3{#5oCW7ޣ~| =jfْFb*dl<ğ: b³kW0hƕeiZ\i1iټI,|˷Wj澃x\{c<įDK`g;Ug=k{]WJ֓]}kkKg `PFpzeʼnywg^?:ޥB&ӗ\v0Ž"iˉ2zO|7 뷾75;QS l(a{O ;5jWG/tȠa$6ȤVl3 ^{Icݒm͸` cf>8ۇq8Wq㟊v?@|Sዋ{8mQJJfvG2hU ã`#࿀4tVkh bbɺMگ <j\|7j_\[8*9-L2x@׼Pn<;=4짗>P,>\)w:_|gkgku-ŎdCm$Q![ 3^ |dƶIh&)VbCn`̹!F[O32*x䑖26G* *F29qщc5??[jI [k7DW)]cp܃JeJ+_ZtsOᮁol6į5u]@yQjCHׅ_K[TN„˫q5:4_SχP+Z$7)٣l܆w6zBҼQG幌H 8>VO|*eY.`k 0)+1̀0k|zs]fL|y*(!PHd99I$5jt}ڭтg|na~Qi7>=ЧdҼfӛJtBK=#Q_ Ӌmj-tZwP|@?o xD[[٘Y,eQ+Rʘf'9 o.xº~?ǁ-|1{PeO6U.Ab_c,8Gú#u >%wyx`2IF\ 4uSgh~V[-/7C/~`rG8~}^44ۭ)_Juu\Ӯp q^sVu_@~#4}^hn<+`K0NEwox|m_ٚl}8(Q3m|M[kNM>A~=[/<3'fr4iZ׉wqgvIcח Ynx2}rxK>4Х|zFE 4)Ȑl I)8i3xDƱ s_oϺ\yon ;^|7fb/$Zn/cc|umƟt\jzeلSɥʰ+ .IHq|16G5ȱWIYؖe+@V6]t_jwi|c5l"S=*dgfa¸K@|6Ԣ4N+/Ovf>NF7(S炾"/F}>R *YX8#+[?ᶥS\4m-Kuѝi0$. zS8=<}ohRxv7::~Dz#H~Pߐve%pr֒,`)G[?? S:\Dڦ77%iT^gE1^waW' W+-oEt[6;h'ԞX}6;_pܠwG}Sp>]]3Fc}Ez_ſy\7"@Ϗ.p 3>爵o|)'4NmyIPDeq?{O:͘{ |χ-~%)Z3io>mnw|FHȗO>-ևM%_2m8xLG)$3_KmY}ҍ^~Z >VѾ|C𶇠:woxj};Kׯm- ]1)6HUfg=şj>^-kXëCxwT?弹_=:6v0*pw5o'ឳ?BIWRo:edaX> {i:ř2&)؉L`1` OlSƍ//tOiZ_C5Vgma#C#ڿk|G%; \B cKnj 07(澋GӧZrdBgl`u>lᧈ޽(N9`Dzc+V |L_mzw>/M&j]H0Dۢf|RTd qkvYב!XNxWO,ɵ_kwWSIXqd|3վ|CaC#I_φXP)LdHsQbPҵ cG5u,q{Wg;gk)ݼ54vA.i$Aq4X8o*sGHK]Wž.Mb6 XỶe.AbdFj,q_͙ȿ_k3+.j:rxS~֓VGC5ҼUMy&f_2EcBI_D>"?odk?v2≮8|9_[Zi֠yG$bV,wom< Xi3Uγcq4V?Ƥe3UlqŏTlDI?&,sE8@# ?.{ e(Ͽ?›E;> vO8O*1֡ǥ:Avd]xL*ũ)YiqD#W6Tk^Ĺ>|;濼!8\IqF_TlƶA=rL̻),R!cqI.dI_t P>"i?ZûYth[R!o2-V5]5sj:仠Q跍s=~0g\aa6h:VV׶?h[%L?4Fw cɏ?#=ۆtlΖX[Wk\Lʄa9O8M_~i:Ʊ}vѳBifwۯj~QeF̳#d&7ERԋ#քdXmi@o_no)e񮈚܋15~GT~q'j{><%e[xvy1jƶHN+x#m t.) @I"..aUgbdxkZ/8[MU6إE r;Ǐ6F<_LѯaSE;;8\uy@8r~GM>6`֏I?N|RʟAtYY׉_2fyYUoUϫQAG߇ugcu{4ڌ :Cz4O?[Mmi>Z K|r{x=QcP-I)ޡVR=Av⿃tE[iv$K-KrT qgM6-g.ӫ~gmipO"];]A#yEh6 Y?/V 0YG yd,qvk}Ԉ 8Siun6^zPZi%ljaz|ǦBӠ1l ngRı|9"aKM;'';+X񿌟 UssA|!>;K-7]I--<&$gC*'uquվkrK)]>P@ Ww*@߄?"YЗs#'9_])8,+xW5N K;R;(`f+:& Z<߳^0&qW~Rx|7yS햷ߺ1EϹc=$ikl }gf1;ӺXf棢$GZy0eé-]㻏 u''8M;Qx6I|[uuucV)q dd(V ^C |CvN0mR( xOp9V:#>8podh2> iiODE T'iWHzHAޥ}.~qg6rlg><.2i#T8>Nr6|+CJuRZQZ~E0 <ϵ>9c=MR( (֝H#(d!>• ~QU 4ED&hTwGr2EO62XF3(([-Scx} PA`qOP3~TQ@ ?E80Ҫï hEPfotoxx-18.01.1/images/resize.jpg0000644000175000017500000004611713222767271015143 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 237 391 0 C     C   `" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽsSc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DWO?i&+Ttۻ*)+#v5𞗩;a-<oXF%[\+eb`\+u&?}]|/­PѼ]lHo5[i"bv"X܅`c"0|8/l1+K2}V .`ʰơX%˿^c>}G+ѴM2zܚuxJӢZ74ʤq|+_SĐL<_$~5.;bepbD0W1ioO/I"n9/o a5{k43br¦U|+l|U{?|aykbmyYу#ELW5t&yg(aOz~ vmwƞ"mJķjmtH}G+^}ދ >}Xj^X DS$xt>mFTżJBBlyDfQz懬[[DӬ,n."w`@C7GēӲ8 n6vY]& GIxAAݽNSu>ա׼+u<[\YHB`y<Ju[ԷVwRj7ڬ6\[R:DZw8i0BnQwk<\hIпw7_ x-w7_ x-i>}_;UG;UE\I'ֿB 5 z? 5 z,Oi>kAe#kAe#f?=>}hIпw7_ x-w7_ x-0GO~ùk_oùk_oYOZ>}k/^*G^*G.~z}_oW[=oW[=as_TehͧjRyQ;D# :<}z4PLDe99^*G^*G/v ?JQJw^<6,*YI *80|#cw<ږxQPSMӮcгO')w 5 z? 5 z75OakPjWRZ ܳ0Icp'rski>}_;UG;UNW?=>}hIпw7_ x-w7_ x-0GO~ùk_oùk_oYOZ>}k/^*G^*G.~zF V"-Y>ce^ÞW޿oW[=oW[=asMcZڬR o/9.GL=*mRhdv-K21+^*G^*G.| ?4].Yċ&ӌ{S4}f@ɕ%uS@ 5 z? 5 zΥ(Ջ5Zt*F'iE>oP!܌༃S?pwKL~?:WG;UG;U\X\mЍgXF8QY%S(C ( ( (E^RX\*vGғQMJķuWskk %g,x#|M]_=[ķ:eҋVR/g({z"H;jw?}OQGs J,X",+-_ĽK0~$&tS,~o#cW_C1ާƯZW7tau<d\2Dv(cr%؎e'ETQEQEQEQEQEQEQEQEQEQEQEQEQEQE[Q{>cXw$ fR #MbeGPxP*`iIY;;oh_< hwj+&QyM}"#q*$I-`*RM\2ņVlwsJ?ȴdeZw%$9+/Ӥ^:UjKmxb ("T㢶N@sRcx-}+f#[ϳ#oBUU@''s/"}׈KMӯ-3kjnͯKtȆGXeʢ3 66*ׅ@BXʛeg.3b#Y~Eu&m>٠6˶-.Oz|m@m2->661.xZ ftKccqjȐB8&<h'V7jXcV?OC̿UX u W'{R4Q`3+QE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@x 'ŬO{w?|/?qD",Ϙ g ׷מ|d;i@fWI!AFralFq)5뾟S< g$'uYFeZh. S֮ZIi X FjSjp͡ D&5͈YUFyzt_q&=BEGY+>7g;a=6W)̒GFƧ$Z% c3oV1HrqrwK _#ˤ弚 J.'Lc7s%xgw5>hA<= >;w- s#$ o1 ]xE&LQ'ÛkNNյ9-vTmPͻW1s ێР*&\4:m~&x:.q a|BeBB3 敟-K~ztڿy. K߈$$@x6véhe2DHUT25x(ɢq|ZK ^-: |3$j q 5ZGc!#T񝧌|%I$ޝo4o\ԋKtҳ >◊GC^tiVOjyiuk9TIn&V1ʐNHWMB?̫qOP ΣcvfKc?6n6]yeϕs]cwuM!.|񏗏}gf;m=1.'5Lj//DEw[WtxֿƍOk}_\ŧG=rMnOTt ZT-R8y]ja|7c~,=׮5ZO0[ʪr*p3Mkw_ZI!l5>^߳7V|Q{M:} z^&ٷ )Ȋvgv ۚ5ŞW36svu1ZZ9Au>om B8"Hc5 *ZIh­B^#JUJD^ϣZm7lNtG?+@CLPEE؝c@3&>ǟu=['K ݫ$ jVHDQ_8΁Y.g@,M;[X=oI~߳c3 ?x~ kc__^jfKCZ1U݁i8_?K7% %EYv˷&;^/(:/?mUtu^;]j]Ru ${~bXgX15h ( ( ( ( ( ( ( ( ( ( ( 8Z[%0URjZcxz+xL7*R#@% kN Ya&U`G-;|-?y׃˯&ZP%kOD[K$v c%0;Z+>^ѯ>4\[Mfojq({H8m -$1rY 9>x_FF,s}yoga,bd\GSRoWѾ, ig$Gjj5 |/kp=ӵ <{UG4 B֝qjC4w,H*rVش Jĺ OgQj2A=VGp(8Q$'ևZ+ x>6^u4HVQ/1TLcPަ|]U<[/Vմ]R{^N@PѼΨ?KD7dWgU +hY&p?SA\ˏ'ծ|T2>= ϭ"K#1;ӎT5x]d2>-;|-?yksͼw;ԆWϧj m:6+ho/jz!EssĺEܪ`η=gԔ[.Љִ?Du#S*r i}N?i~+7YH=_Vi`mƬE7_k O^0uG-=jMCYT5׉u B0ڳF!w1(SsmOZ\?<5Ow֫ ,Ǧ<$3,B}̭#wV>ߍM7>_jVzdbK˨-#cjrz{PV24Bnş]ҁ *:yc9kTrB+G:qZ{+μ<5{ǢYjqwơlDE]Ѥ(AA'σ|I]SymrwC!d${ai%E] 2zV_K;h'ԗQdHw$%]0Lylzb R+㐧  4 :6c>lK!DI]Z6_ HGC8?Y$eo ӿ">o>>6J@gmn?g-#_Eyԟ%tͩy^&,{Ǵx.|[t ylwm|_jMo'' fvkgn@;.ߧ0CMtidcm$q1S! H0 1uP3O&^&;)-<<]oYkUx3WZ=:Z0[Xī0ބWQNk*"E*Ҹj_t?iMƸ:}-ϒ@de\rwڊC8?Y$e_ "Se׏~k&-χ^3etiCWqN NC]iKρ:tmS'$h&XI,# 2$׾QrN+xUմϊ^&TkHғ1ͩUU~@ױQM;mmHM_6Utf[Xs!Wf U lZ먤3_#$V^%OVí #$[pq=s]5=e;M.Rx,5G^ gi6;Qm#ku@! .u5[1^v!,:cWB~VvkWoE7#Qmlhc[ |E*FޜwE8H'xAgEs]\\WA*H彵UfiUB44Aϵm@}^jh׷bbNPcg"p9?ׂ~sh3nٸ2J\ץMik R,|={;mKO1i@BIrķ"qПk"|M#QhI.닂qSVN$x*^1wyqco[=6L3xľ(_W|k/XG^ɡI |æ+Ҩ4^%:%١iw7Hv`]Tr5tQNPyI w :RW=5ψ6kk Z׶CnLcbz`rY]ʁ  H&BB\3ҾJu~xX,9k}fY=rZ>Yk_)|E<rѭQONO_G#֭_YRGEeb]C#mYf  a_D6ϥiB]8GNk<kko=hh9kXa af E.Ww>R@ #8|ߡ|`mJþ#S Kk{cK4Ȍda[χ5|cyj>Ҵ=ZN^jH2:/-#s㑚I^vhGz׃|K>iji{]Kv9 + tׅ.]$]ZdҡoFC džl|4(tm~FP{@ *gTh +Ǫk!7F0W8u]2qᦃd<3~N@4vGzZAzy(wS:dV|6&톥IT\Vj%vTYd ma02@KݿHN}Eqo/>.nӻ o,d߭v4Y8dxDET9M.oPpqg}1i4C夛hՙܜVx{>8(!l/#܌׈|KQvggZNUwt< \w>4I |gLT?/ 7Iuo##q;~#Ş/[h&hnV(@J{seڥjvFMYQٕ]Y9 WpxnWѴ-sO5I6Fe]K9z{#zΟO66g%):c[5űail^bv@+M.f_V}sER,((((((((((+_xov׾t˅.l9 rFx*(ѾxC.u-d-M|H>ke@;>v4.kVv4OJn$q+E+1T>@3ۢ 9CφI-tm']bs/¯ +­-va,@$W[E0j>!f $%ֺ+++K8 |1P(oCZO9,fܳ 9X+ҵMOFMM\*kz%nSɶhdH4EyUx1#ExPnv+_~| MFz|zM?"FOݔVGf]Ѩ+9P${ǽW% sٚdI+Ŵ%̍^S2r>w4۸ұET(((((((((((((((((((((fotoxx-18.01.1/images/upright.jpg0000644000175000017500000003166613222767271015327 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:sharpen|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 115 327 0 C     C   g&" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?%ӠsK5}M*o-H($gsQ@"|Ň#zWŞ=[}K:cs:}#WGׅ>Oe+0JVm{X֚>e#B1>]k+ocqIĂ5!f gׁ{e w6Gzsש"K->AYoA[$5׃,]U+\2߮_ez@ A<{]A}e eq0'~'冷}iZfHf[ba+czW +CӼ OܑjR~w(BH3v\l{%Ԣmf`Yb)`:A yxWm[oIK.m3#}) OL5j^cgO( VOڟĶjb>X_n D[bO}̾KvZF//AX5*+6ý݀Jno̮]_R $!\_'? WMtE'̑燼Q&}y{J{;fHlf= (ZOu߇>7gMQѥ%IB8ݵ`ggQz?O( =xS|ZTon0^2ŃX|q5WڬwOyײf"]N}ǀF =G>m}'Wy|~xjMpkHhK$pOړƐ tЯu{+}=<gbP}沿hÕru!G_W^;XoR_Em@F6F\^N+_6~"Ǥ_Y} B'(X7 ҝvb> G0X ru!G_V7ڽWcgO( Wj,.j(=(.sD[aJߊ/sg}%Hۚ }+~1"(QPq7R,,ȹأL w>٠ey-frJ}*s.u"Y%GC,It'G8?ȣpG#3_wP\AԮa];3 \ne${>#rxJӬ hW5Ɲ"mgFIZϱ"| As\޽5j-g![ kؾ}(aλpt? koaMZ4*9㓂O">xBw+O}7MQn.ghg!00>@z/1"h4;(5iVhr 5χM{wgsD-%4hg^@[_c+h:ս]h \EnЁԸ+ԺW}tNtVP[8ʹq_2h hfZ zןk6w-[ n<?^ZxU!|! SmZ'D85M$SvO3m/,i֟K y%r2.q N{GRSX[d%ލ|&7/@ѼR;kdtd9K ]mRWEAUѼ;O oS>~]J tf9r<hwbM.x4ɬ'd p$l T^yrj-7!cI񟉵 UԬ-㺹 $H^R?ŏG:?ԃRZŏ>QQ36λFzW;@;o3ErWhϝ3c$"E34qቾ!^_š2WSѷSoc?5OTݮ_ώ5gy5!Ҡw+ǁRž3 ._ ۜ|7ĺgntR_kp4;XtO8լL0Nme\69qτ5>mt/Mڹ{rm>u2>kŴm*v.`򡺂=ya~zg">*G G!x8sc7{s u$Ҏ_/96oGU_- 2MLĪ"G+BpA(iaWgǰpz-#>R]Y(uKCPCK!Fa,o&B`.0_ŝQ|ORMNᵳaxr׳*@c=Eh=u~ki~O~f5K-J($IU3TqGxD׮5?Qw6I+!I gϋz|1w>Or/@:I@#YW?߂wu7sl|gm-͢ɉ}qwQ} C?})ETQ@Q@Q@Q@Q@W)-aysl۔UwNҀ:Zq6^.m]GzvcY=լ߫?Gx_~O΃Y&핧62]u[;CV~Goj?՟#Goj?՟# Zz@tW? Zz?XV@EݿB Mou*8׵uQ\z/4u+.+KX'j3HdiF <XV@;CV~Goj?՟#AEs.io,}RF BZе=:FVPtR?ZE`ח/i]Ԗ"9{UE՗H=)լ߫?GXVG#5k^_=u$뮀50)`dxz?5j Sjz\pd]\Z6J p ۜZ}ږ&ug ƙ$^CH6𿀼9঺mE^詝bd0|T)ԦtMR]Aj%HX#9˳z`t4W={Cf~3f~:+?6juM2Kﱈ]n&TW`ޠޢEOPD=F*VlZ,5t 64C*դ$v]p8횹+]v3}udY#`֪t֓Hk.<`ےkx~𦇫Yf K%=3,QDRm9*8jsR|Cv?+5*`Bq\)ȰOo/}JnT8My qNk ğ^Z÷ SQI9oer9+O6E?eͷ̯OvSuUhxO( bն[;ߎXO oX]NrAVʠ0%u7x_a0|p Ins89xWo L [X4=q5JV#˅X U7Zvo}}OOUpG4oN)f$ռWv_>Ƿw_eWcEl?a}$? CfC.I߃u)?ļa@HŤ֌5Mt}m {5P !̧R;zVKxIr;]{딛Lduu1?*r:g8~^赯 t+#ίp 5naa9$_22rHWx7hZ)MԂV8H1u0:kK[;|iNF>Ԟg|gnyǮ+o4NJ}ÚI겯owIo3g2bH<Kv .8uJ|[7f}l>&kڝj>Oa.qw=C"pXNJ^nal5#O]^7kh%`r`'C'+46CЬ0*X,6䜒{km̱_j wHpA}Mzq/f׺Ǫ<+8.{?/R֟9}?P8sk0ۼpV 8*kcK^>4?k:hjl*4mUbtV$bxϮ>3Gt(ds-Rv{tYH-?j/nc2c0?C/7~b[i4ѯbYἋU%#| Px_4|Eޡc qilcsJd/II +խTQҥ'kY;<>IGB8EIJ|MZʖ.S&5kJiM1gQ]wxG/_1vx/:umkJKFM}mVUׇNx;_1NHΒg+vCɱQ*T~RߎGqL"$W> ?[U{(5}[ f0ʪIV\(%~3氝tXX@3z0$Mhzn%hܞDK;}א ~.>hjm&sݎ$qaxFAEd^؊nTdv #^Qϫx ccA˨:{ip*{G~+|Bѽi\<+a :d2g̣WQwm2FWSpNNTJN(9¸ L"__\ב] wvXi{s/wp1Y&yb2 9}z)2oOÖXdr8|v fw z7Q k5GR?Zj&v۲Q `; a*1\e[mCWO;^8⾕sE(8Z?mx;o±㟂pKӴ4Mk[Hn4.&bz*΄.1m u>k iD0-ޛ.`88#=*-ף|_UC^Tw_Yn^W43NÖOeBQ|ȉ) {Wa\-ף|_UC^T W0HU:+Gm:݆{%,Z\Y^HBi<C+^F[;$zmG5x_Ӽ)♮{m2Q;#Xx޼{?-o]~!V($-,^fuŌngڲ|E-;*lmr`xcfS'*0:V'-ף|_UC^UjϮ~^\zF66ZZC8bP/ DŽQeZi>(R7Ě])D wrG($+ə+?5h_Uw~\j9Gܸ{vS bh'?f}5Cjz& L#*ߏAq#uEf?6G>MI.̙F3N2WL[MnMk l@@0N+ϼWCGmĄI` I- ݹַ-Oף|_UgڌzZ&rzg%? Jg͈익8qNJ/j^KOԤ֮] 0R3q`zKo?[qfNs]o}jmWT;Eq G*pG#F+,UGRJX 0;mݳgƣwuaqڪ\mVm$m kIcEbי~)K[̀+5#Ic\|/n.n,Me3Y_dw>SJTXe1ɨxr2upT}w -#c?'-ף|_UyARj>96ӄP?e߆:]ީ$Z6ѩyO4:j7ǚao?mrC^U{N_o+[[)kYDBUr8NiAB;#b*bʵW3( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-18.01.1/images/rename.jpg0000644000175000017500000004376613222767271015120 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 186 428 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?*PfobIg^xKy#f\V^IkK&D#zW>75:sCl}gh t=W>O 1JVmyYjڄ>d d}j)udK"8[ƸSYk> xPQ#Nc.23$|OozۍfK-򛵋cep0ybbO<&)b誫K~cy~&Xy;魵xdx^D43D=umƳS"k0FؒpHsV,ke"6a;d⧌[Ö^GSx.USTKd``!{t\C˞mvK*5x\Vҿݏ^zkoRkm63--(khOpHS7^sci_| cj>5m7NcZl^6p3N{[2o 7ݭ垷[GTńJr'L탑Ru14}+_&_~(? -7zj6Z儲}kYBdRAi/.i^!tۋ-k $"MF_9ܬZ0徇׿i_| >ҿݏƫGFԭt]GC: :?ZJ$YRHă`m~^3Y/ oŗ!M 2$XMwpGV%mk>Jv?Q|`U>fϥJW&X ;n8p/QM'16zuG>[`@O*r9[cͷ\(Jv?W: Juɤqҿݏz>G0XJv?Q_z9Wcbޏ6ҿݏ}+~}ގ`n Qv8*J}Yk]s֎`%/n+KRcE1r7)\{^]|Ե.lIEoJcnGحȮz*X֊v;#'<4[8xR:y2M!1o,,EC'j~o<#Eb"AR4H櫈y]l筢dMo<#Etp75wGijXI.X2fo<#E_}Ý-~K^;izݸEE1#*TOxZO5ej2u׏݅YO[lOحȣV?Qsʮ~x+Ү;|?pV劕"#޿(濉?g~-j}Z+o$d60\מMzحȣV?Q_OGGL一~^DDP|C[R׼7q&mbuߍgzحȣV?S}Ýv8+B 6hEw5އ(wqdڏx'AŢrME$D|np@<һo[GحȤt6.Ey7mf)j#;Gr#$1ڬ38'|?a?S>қL:d?QJm1^+({6glO[GحȣٰF_=gj}xG|>o<#E͇:2G=SV?Q+(l9їz>Zb"[GaΌ{z}xG|=teޏ{֧حȣV?Qs/}޵>o<#Eb"fÝl+({6glO[GحȣٰF_=gj}xG|>o<#E͇:2G=SV?Q+(l9їz=kSV?Q+(os`?_Rlh|F7쥹u BZXizd>m؀ If!@$W[^WJ֬G4#]a{ZC[S$! B[?5 lm5c=Ä(ՙُ $߶o]Ckj~7t!0X\jPyl;PNxnƚtgKXhZ:ךAv8U M$yB_= !F!B#7V,z֗D腒` /YIzy ;a 4B\|R4e.&׈@mͺuŀ+~EQTzu [F=%??l?!ƽj@y/'/ ;a 5TPBo^Ey/'/ ;a 5TPBo^Ey/'/ ;a 5TPBo^Ey/'/ ;a 5TPBo^Ey/'/ ;a 5TPBo^Ey/'/ ;a 5TPB[SwOmvcGW8kXO,M4gPQE5sXW_/,EW.nd ]:$+&#blW4Q?M'du_Qg|E[wҏqe}.v]ɹV]yֵ o x9]nM3^֯/lY,&$kQu$Ida ' 1[o<;V5Kxƞ".t/Q_Z=pB++qּwW};nU:kZ>\jZu JiayT8Y׿iN^Q\w>kjėog &s^ܬv2]b("4HRW;(=&H՞V\j޶p'V24{ě|¾w|Q[𭬼AZ-Nᝯ^#t16w-ʇ6ٯfo x[g5a\[X մ3hD0C`Ih~ߍ6?ҾQ |W .i8Ӯ4c}oqs$m[.:2~$ӚRnar#!?ӽjK_߉DMTv>@&?~hK[?V𵗄<o6m(,b9S˛nc; >\Ba-~èG ,CR7My [sg5$%7Q /綍~%nik2C<+źGoZQu+\53ubWx`t#acy'gZ.N;ògYy-w?yx ,Yʱҫ*;79^ /綍~%K_߉[_A$ݭXk_uOk*4uhU 熯EiyD߇wKa!;E*/bBO쫝OJvPv{ Sel1 /綍~%K_߉+.|]SO/ji>I-՜um-$ucbJUzy՞iдVI3$6"w.@~ORXɫĞ&']h%ۻ\$>(CO)]}`R\$%7Q /綍~%Q\$%7Q /綍~%Q\ۤ9A/?_9@C/HywW ?(?!GPyEpG(?_9@C/HywW ?(?!GPyEpG(?_9@y? GERC/ևV|IMdV+GHbu9gV$cӢ((((((D 񖗧j:-g[K+yjf, ĿP1ۭ7bݤw,oeH $sT~(E¿rdX>y$^7pF~q\w>|BhfLttM.(tflľByqe"տ&Nze΍ v*Jyo!.F Hbk^]kZf~t? ]-6O"+V60!DC( ך|Z3A5 b _I1\Qh<eDEfO7v⇆ekURQJ$mCNDdWfgV Y؜[Gio]? z~|IAxoF:?k&dѼ9,En4_h6+6Pbk Y&d89K8#%X2Ducv>g+wz^wZZ^K n#[BC+!g] ѭm_OgwI6VU|`?@z//֦1nE7qp`|[T3oji:ׇtXH]mʩ YT@;x-? !XKMOJK MX[]vX- Ʋ"dn9IQipiY}N;Җ=7-Log-xjh'PE~ by!`A )PFOcRk>xgV4SLƝvlo_~-dXв w+e>(\|#_5O&;5]krWqu/4[X[k^5|D#roVbu߰eo"`j?.=/| AV] Eb:[Y 8XUs6y};s[z|KSDX]_m4nu?"2ed` :| >ľ)+=w[f?huNARzcu믇 K-lICr=˼16f]_?! s:f^R6lqȈIQTӊSHu==2/hVx;|0Si_~x~x:Ojwh_f്XMnD,EE, fڦxJ}wy}TTOn9}č܃#?~%|FҼ7k:o[mA4ԞK-k Y6D^%'h>m3Wo}|8ZhΌ!$2SBIvU[Sݟx?ZMAwx-x#ް ' J/A]rx2IiO-i+wI͌98+Z :!!9lq^]N]]Yx3i' "3o<.ON#]SYDHwysH[j+GiڒZ\{n1 Sr.z2A)5{zF|ys׾rYxT_7lu3eksJmΫC Pf>ĽZ~:jiuMVKY8g'w&rѮ6a_Gj|Zڏ{5}GqePWrgC|gO Aؽ6AnǩD+':mb<<ĽvKO Ut4۳wq 2,A $M"&Y2j_c+mkS$nO.sh68cayA]Kui5gI5o krHGWYbW"RBrk<xsR,tbXMh0A Sڮ_bRiy/ i21YAtl=N;xmĿCR0Y&_iS\[,P\Mm(C,+Lc{w1$?~JLúlz|PKj#xLf)Y@d1EyZ >|946j5HG,&u >&tKag{[G' ĉm,BYG䁥2 ۑ^BT8`9Gs{=Q|Dhׯ'vpȲgz%i,Ԓ"4\'1LSӎWVvO?4k0u拪xsZƣZZ S,iDxR+bY80xc Yxl[yUnx۷? tq<3m$Vv=8fspJV>c|txkW/5m"5U,9N4q|H#IcM͵ѧ, %mrDg#Rj?>[_ << nolojE:}[He>J%Dc!•bZr/OrZ¶٧AɒCs󷩮irQEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@\m>ޙQg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xGuOʀ,QU~Tg[_:xQKmɌ3@hi߶э/Т~F4chB6(d6SЯ Os04~Ɨom} +?_4cK?ƀ4(i߶՛{kxϙ 1{((((((((((((((((((7ka2jsY[\AxUh$tVy׍O/ M/w6'3c.;E "1Ts,Ӓ֯S3/>5MPh -22sn!l(q[:_?sXe#KY]C ŴPҴ.L6hN&XB߼ޱ$:gc ]jZxO^Vԯ]"ktHZ%Hؒ>'d_/Mk^0!'u|icqe-g[My O"F&e^pF{k_?A RϤY(%p;Ѻy[I@ۜ/|0&S>}-l"V RU *$XHky\Ѣ=u#q׃Iԡu0\NbCsnd@A+*pMfZu = Z񯅼Kmm xN÷63KnJ bI+m tcꚄ~dZn̳]bdFEk|uDG|z]'ⷌ5 oU|'ayI㿺=%ɖ$X,?.</b.|Sj9;ۀ$4j6C? O+?ǟ^%z$0\]-qnxHUID1 }UQ" 䲸'nnX'Wt 7^0w->;v\ */:k+_V^-wLƥiWP<x徎%|1sO \_Nx ͆>&/F[q(?$cs]_.xV(0rvՍx߅^"|7C# Υ>os 㷎Υ  z⛅U-Cm|20OK!騢EQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@TtQﴽ>EWfRh+/>|;FG9VNGG>yj>nvyPw#7ff9,t 't='SxF4Oa AFW=(-EpDEpăjGUU+h=ދzkdv)&(D|=B?? / (D|=B?? / (D|=B?ӎ("4$R8*pҊ(QEfotoxx-18.01.1/images/metadata.png0000644000175000017500000001566213222767271015427 0ustar micomicoPNG  IHDR@@iqzTXtRaw profile type exifxڝk FkY >LOVm[-S$|Ͽ8$6fGժ8}[xI_㩿.,_D18ϟ?uWxglVER)Exj%\jY|{5s.>8+dhʚDo_KH֝$~&=qs< ?o?W ]e^Z?g2xa3_#¾6?i{hI{}_=>ۛxO)(5'3l*yH.kXgZ|XTfqn%t}n60~Sğ†rzS}\~ߦyBFoVBnot2@9 jYJ.3Wn.>7d:*kU,oϘߏWW7R 3."2@Ɂ(Mq*ADZGuSq[sQJQ4ڐ{:7<[ͻ{ eI̶j+#fZװ 숧 Deж[XYtcqf =|g=`:E̟[qr| Mer)v.:TPǚBx_Vk,D7"WvœaCm2lcݏ9r¦ɊCʏ54yhsQ|MS up9LSƚ[;.AGޡT<ĹD}Q^\&VPG5:)>T:c>9ec*`j†R9>SGY;4 8- N`*rsIlPeuj,7&(e2gƲl*&bi}о\MO^ŤFGbRXaDB|mDcZeQ$G8>(j/+ `/W)mGnI?F}n3ΰt`.:X6sW{<-2'{i,-r1|i,aTKr6MBֻ&XI)l2)Wcݶkƪ! I [=ӼFyAmutTAaٶEB!e4}ܬ^ {I9MIz?@9C>H]#b3kJ؁#a1i(}&qⴎtA ڀWSrM}7rr_bݺ\̍`2-hE(p5 Y}ytDu p7M(R  Wk,JA.΅AL6;2Ty|LQBQNH]IGM~SǨ/8[ lhR[!};!uI9BxgX MdRvD :Kh) -hr߂l! =2߉;Ҫ2v!NWPڋGEKZ}<Ý2+DiЁ{f[TRݰ)P!d6hVıGfjNPp7B`{8XB Nv#Wpu7ii/Bce+e~ 2O:@(tL ub/peRڍ! o a l{;bֽ`:``]!M%@goxVw5mVJUJь|y-((MC]̔S8–LDA{ã-Aj?ڰXm%$|].&_<<ʲ+lB+F}o 4 arQVZ`-?gabQ-0 Phir <4~wp(rD-%:{$t!׀#"2ݸ޿(|yӣۍ]I߸6iSOڞe1!!JV{(‡XC*͓S.u6H ihhLlfj3aP*hHe$sBIT|dzIDATx͛yU?VuUwWWwA,l:,#G xE 8 3ǃ,"N@ d$tko$tWgw} * P@ $6 *ssejjB 55eHU0ij e~:cYɾ\&D"/uvx V{TH9XCeikg.̅˖3sբ6L8d`* D-tڦ'%!w*TTDk4jfDh=;:}z{D2 pge(dW/Tͮu9/mirӢ?aY>[0 UT̚khi+W9cQHTúO^ٿ$~?LGqU}xE-=OqSl}5EGE.O *7\՗UR?DUGy?cowO{b-ɝM}[+2ϧOfq~ E( R!]CB(6e,o1@"R]+TUXdbyaZ*0`(Oߴk1S7 \d|<uΛBaҪ@Q$\~Ik sYvbwpZ4=ZP 9/{ft PE,BUYp,նݥwIY 1MbT]YׯÍ=e%"G$I|w n\r:.^4AkC(Ǿ]YqM&F@+[8K닧wJ{`p\ūN_R (b==]pa/8{Xt)h . @U%tZXl4 V%)Fufزcl7'`f6V.]>k~nz0SޜG?A'ذ+MACCwBs[Y7jB%\婭7E2ꙕlL<9Sw$Q ԛ/gre x8]6.P1H) vjy@ m> lUְ֟5^:оO 0.iZҰ~euGc1J;jb\`pDO  ָtAKO'y)Xqbic`"巬jY wJg|~!#?N ˭cT}ߧ ͬP.8f IDn=5"[ + \޴%ɑ|/}9T 4CL frN^>3'pf<=5GAhCL^Xa ae$3TN:Q 0pŕ- Р`EhTh~ USNK~?\Y[#b2SqJ) %y m ԴmK`8S|za?UH 2#K8y5)+oe\EՇz\iFU;ǃtǧvIvs\RI;s<K# l][ڥ6v6;3`eJUz'BQ˦&uڋ(켏cE-a hp俳7ݛjW+ʫ0QuAi,4ŀktoR:>9T,fG6l3}.3G?TM Y`i$>IXcn'g7L1"SUt|# =ݠ텓[2fg&ܕICUC>]dC匩թ:S n+\KH$@낓ؔrwm=r@8 kh8#@| F soEoB x}̢GB#ma/_Mvw=mf   WL268IZ,.(8v6|Ml5ݗ؜Ǫ%%˼TL.Ux۲I/rxo9*}͠m6\cΖCUTuO]oO1g {d-I}l1h8s2GjŌR/8^&!&I MMUЅGp1%, >X=dv[}񎃟 jբX๒7_LQW.R1QTU%$X |0ϛ/gK ͱs/Fc:Q,'(9e3Q C#'~sOQ!̓ A  CSݝ6< C啞'uIL]a rA;o4ҡCH͂jS98զn,k׮}7hlldN]Ɵ~G_o+Wv~^,|eJnY&H)G%!MMM[P6qEѵ=(3;vYi:JN;-Iej8j.@y+4tգ+H8EK ʐI&dž;{6G=pxH<ƈ4B.YxgE͢Pw@@a n&1ڱ]{&u$jj먛5(DV&-/ߟڽ)?R*Z)\uU M 4̞cpg̚Ot#lJ%wJ}S6=N oQqwY[Bg04L.K.`@Y\MZ)E٠(3tDzE ~6KX =NcF0DT,"2S I IH,i)eҒ2ٍ%W!68W6 rh+A?hQCpbeyYd$v/2^pwfD"D׺6yYx/@pЍ{DIENDB`fotoxx-18.01.1/images/edit-funcs/0000755000175000017500000000000013222767271015170 5ustar micomicofotoxx-18.01.1/images/edit-funcs/invert.png0000744000175000017500000000116013222767271017204 0ustar micomicoPNG  IHDR00`n7IDATXG혽o@ƟG*(IPAaBBBLbi``bcX00Q&ԥRA (6 FD$,c;g(}9e@3".v{av:NVRS_HVWVVJ85dYV.) 06$C !Yi^o6ъ:R67%~4m4V+ZQgcx<\˴!Jm0s=J}^Ĕ(. 3| 1P̈8|iC22g(N۶j+,=`)WCT e ɪ O A*Ie JLub[a|xçazs1<˜!k6$CCK*c:Fcu؀>턪ыlX%22mH6$C9CP%5~"g]ߍVԡ’ƛ!VIENDB`fotoxx-18.01.1/images/edit-funcs/shiftcolors.png0000744000175000017500000000161113222767271020235 0ustar micomicoPNG  IHDR00`nPIDATXGMLAnOĀDЈ5LとhădjoF1_@ PJ[vXݱ:}itfxk(&0l4%!'Nk3ϋ[#ĕ*WC"D2DG{"5LF:!Da{BdEYC+*IĞQBN]fAf |6if/:eDF[W!6 J9e^ʇa:e)CXL}'ӂ1ڬ=wF3dYiBrsg`)k3.qN w c/9P-:<' [9jctʂOm/wRetʂej|PF:@A0#M$^&a֜CbsUYsdٷ[5RE_੸qRN!͚,Ba/ҘL'n͍e!hΑxm:|[ *HX;޵6N]zx¥ t)Z 5J<3/;ƳovUf9 ){ZH8$-Aswn5rfS9fד0_qze!uRgifBDwY ѳ5`0xb x}p0c"suBGk yaR@}p`"߼$ru!9^Dt !6vY'7} q"fB+tdcrPtSVtB9\s:Sahr4;E7e%!'JBN(:R@+IENDB`fotoxx-18.01.1/images/edit-funcs/stacknoise.png0000744000175000017500000000572413222767271020052 0ustar micomicoPNG  IHDR00`n IDATXG{|gv7IHb   DĨTQTkEgRPԊ~~, @BH@ 7$Hnnwk |933gfv3< /tlw\ K3\nyeiﳳCuwi^$nDҾ9g|fVfз_Ygz !\&_vq-@[-G.!S3dgk"#S[Dҕ?L4eu% @f)\AܑyHr rI2W*e!*oOaP)1kiϓs?_ )OtAm֧N>=r(LN~&v|;}W-^)tu3(@ koO,Pfbbk hD&y^v}YLj#z,wdxmtb'^Gi=hbᩈOz嗻!3o<5eZ/ewuckzxwF7~vǯ{A^6KVF$~8{HW7O9V}uCm.&hfhf. plx!FnM@ RT5 YOwQBy "} 5] trf8A;n)@;4s}` h+<02ywzE唖m:J|Ly&JjΠV@ io;a~o|BZ7M4w}.7-я{or@zi+\PgH/:*~s$o֍޳[Dq97=Rۤ1y2Kق[NHbޞ :.&c[5Ç4JK@194< t 7J1VݏVyG~Md; Q@گ{􁪼 F78C:ĦsT9`GJ5'LԒ &U0'&S z |T h`_ܼNGEtT/)䀿p3Rq Te7􌜳ZDO%Uq萢59l"{UZ v[}ܯG.A4WK.1>ANS{ŦG͹q|]O2-AkW*q15-"HY%A %6 }FrΜ),Q;?0y$BXH@N^;b ]eMqXK|`^\/Yrʱ7>XQ+3DoruoqBξ C|Ma!Bg ~@2wzO(+88`%<Ъ,V8iU /Xn4;Z6xOskw=+jrs:"eYSbdb|2I!0 G7 89z1OXM8Y&yOS^UAAN$aA3h=QBGV| ;P8ΪS&[$Nwg@; ! Fʍa ](v|YqY tx,|;NYW!w  17-wpxvpC{+c&XC=ݾyng84FG 4b/5ǾDkK ԛuH=Ι[?x(pD3W_yzU{\cy"1.ćIeZBIQkW%: ӗPEp,>s dqqǙi9#7o?FvP k6iҤ@9I͹TƩŇ,uڧ+uؾ uoʚ\weZH~eA>Qa"WPs%3ꭅW"OQ Ip9K.Ӧ"mB-rO*Ǎvvt4)W(U4AA+T sqE9wb5'hRj(3VslzF'/48n?/XfB, uR1M3<a@lޏ%f?ΈOuwRn1nSV~@4͕rd މw]b&i?sX]<!$T. o:Vq@LeN@pN0 m԰>=G'y/.T2:IENDB`fotoxx-18.01.1/images/edit-funcs/expand bright.png0000744000175000017500000000207113222767271020416 0ustar micomicoPNG  IHDR00`nIDATXGOhUǿn6ڴPQZUִ,9z 6kz){I KjI4n6󞇙?3 9s޼{yo6ϖlŖ^7%nb+Hj(]a`@w6p#u61Vt^ :U^"U*D77>;11?R@zOBw9/58NoGHyA~C L&͗ˉ~g->uPCcR x:YSRiVY΁FdHO^2߁f:N\Sr/-\F } +C 4͵ʾd Q*Ms*"5qK5463p~ ׺ؽQI6~l9E`]MZxLsga:'jU/@#T8k0xJmRH e-9ge8w">AVӽ!zXR=̑B0k0R2 uXꖲ/*K]!v$ٮw6O@sng\vf`Ũ̪Ufc92Teȭ_* Fdҭo lQvVƍs˗ǡ:!FcE{ν\ MҿI*2ؓTAq2]5"d٘sVЛLSJ<3\Y CFnfAބ;PbXRyP&/,|H䇙RiH%6t*rGQCփ j?׉ #b8)?,rWCQk㠝RaR%)m'R@Hs&oWhOQoJh{B+mos|7" .+IƟR(p-y<`>k@ |@6/+o!e^'WV@w#3"7%b|C e1iIENDB`fotoxx-18.01.1/images/edit-funcs/panorama.png0000744000175000017500000000105013222767271017471 0ustar micomicoPNG  IHDR00`nIDATXGرkQK^z؄j٥Xb\v$89(bKTt E PZ*Uq/mHChr\$-ﳽq|q;1_KҬs;c5hod쩋29j}r4eGЌj}J<9%ynjb}OSV׹̬{b?kȤųG4޺kTToW84L\!B* [aGOEW' \ălJ9r';^GV16.ÅtB:\H p!.Åtwи{kyO>Bչ4n)7CH5n@اYDO{ASRa1jV`~Q4*aErRXl*1tJ6}ޞ 4m JU:hbT(p߹%[A+1v4.IENDB`fotoxx-18.01.1/images/edit-funcs/blur.png0000744000175000017500000000441513222767271016647 0ustar micomicoPNG  IHDR00`nIDATXGyU963wm)eJ m,*`% P?4!b@" Rb-IJ@di0b{m}wk!azÓ3ۙ,PP{AǀTc?^hYmSg*yNl$3cq dT>?; **YC]%UTZO|j-`-<(Kʒ() 9K%b-A@` ZBãEyAQT^?Ĩ5A A@ pB9YNify@ aQHI)6]ByNf > \D!QLS8(4ͦ(rL}z}}})YNYKJd Ω74!SeI^ett{tV̲9! #M)F(Ȧ=(s3<v:Lv@+B$Z8Qb~s8ŕ%yNtg{&tC`@W E& d)TQ[z`)u(n H R}vgBzI4ϽBF4dhHED!")TYtmyVyVԔNN21ɞ O+QD+DqL'$9eA!u8zԔNue8TA%i64M %nRg*08ߓ˵7E;J3{㋋.A@3` >eQ!tW7& ۮ}hDUQoeܦݟaFiתq* krˠ}c0=8psw{Wrʫk'w-;7{._;_ԵQ]{;E@j\rڛ 8$C :ؠdIuщ"ٷN9#VF~0TS>nO\"'IJe*neQnɏoҏb("k B-r^ԞP}K0  @E(=;G]Jj ;o]:ǢQx9$Ֆn E eHTP7J! Ujg0->W˯'3yΖ+o/vquj_-&9ܣ/u?j%{wqe952oY ,o'[hL 6JJfjb_0jƷcˇE ˳m?Ďc]=g֢1UyTJfs^Eg_̼i\w3zŠtk{&oU[N/sJlRi]}_^jC/94E_>qxϚf5!Uݲe o}zM#6?] sk9-Sa㎵Kz*>ga ̸FRN=zk"7@ h!PV>Js솝nnvV/وp93 sΦK3Ϙ7 !d>q9YK9o5G-?Upq(vE7͟] x|Im ,[6a0E̺ʒ%{"r圏_Z8E57~3Gݾ4c%'/}\|,($|}JK^4W3-x9tb\G^#oǐѴW}uSL2IENDB`fotoxx-18.01.1/images/edit-funcs/tiles.png0000744000175000017500000000647113222767271017027 0ustar micomicoPNG  IHDR00`n IDATXG͙Ypy],.XI(d!)^Q$[eS4IJ*,.TypRQpJ%JlP)(6)x)H&4 `ŵ{;=;Gz~=Ә_kס `wnFUN[1_e x.ztfp9%Vt%d'8f\f3ޞM=ee.$53k׵9ap.k۶n쥌09;t!ۯ2д5~g[0JF_|W&~Lk}vUx0v{?֦[fy@ht48nV{xRdE酫7+H$z^uW*7 XB VXRٜ-ąL̻da14{Ky~bb4]_ ihDӶ߼߰I*rk*"Ϯm[U|>%8iE CPݾK5wBvGGBٸFr\9`<ZzӂUk\{w7>Ag׮]W9Pc Cc+ƺu6qJ 6}C!o޹H@LIVE`-K^Oϭ'~jZyꔷs5<3aƓoU?Qi,)ݾ!: ĉ=?|Q-fF0ߗ|5ӱ=uVV1Ɯ#rm[ulOW[ { $4q^= fW߼F8aE++6TVZР-ytZTm[ꛖݕ4c"I$9;Y'DUDOe&^rl_p}Y\jԲ^6KLyLOuj[)5' /A)jQJ7f\99&T[]Vɻm-!YNFTq*,|oa洙/FTWIT E%D;,\Y2''C []5sbƪY5%EӱlMCcYuØJ65ɔ)]KMMUUTԸ,y׌<0[~tӖ#'\v^|y_ 9|{o^&`Zy'wlO8|7dcO:xO+Kjj™گ|Y<]wڗ67xʪU5v a ViaߗV1\D)`GEE`^&ɼYEÕ###)5\|Kj+UU?pa$1lu+ G0O^bў_XN Z}]ܢ9E>6:?T097SP^kV꽛VïEFS2Qyk_l̷ -ϲ `<>ٗ#kńr$.d;xV É7Hp8H :ID2]2]qp;seB*}Œm-I%fLZKҌP:͕,Qgͱ)ϚxIrcGTs"eٹ}ȑkş*ĖU,y$]&'ōdH*C,U>m]@9L%~ya!Mf^!' LcI2c7h+YOjD*\.ONvW2< a&U#QS30= ]xJ})"bmBypʕau#BFSCTs&hk<HM#f[ܸs2̘eCˤY50Ќ3Vy|G%gEҰG—yJqnY3%gGfKubݸJS)+8Ri0 xr_ ξ9Xze4XIENDB`fotoxx-18.01.1/images/edit-funcs/warp affine.png0000744000175000017500000000431213222767271020061 0ustar micomicoPNG  IHDR00`nIDATXGŘYl\WmLf;I'㤍Ӫ4-T!Ax U[ DDyP$"Dœb Ϟ1WY2'mס#i1;}c3 eY%58D++rEa%ȰݕT[^O>\7EKnVϓ%nF8==zgzwKY3w(`[8y*2T辣>),4aG{Q&W/k3 6̞XѼ|USw(;VȐ\Kz=6~A֫C(S؄}e$>|kВu1<̇zfY&Ml}#7YEݼ kYYӬ1nc]@<d PƬ)P&ƝI/> d%̹ibh'no@-j.Dp(nrIͻxBò JG^N1Wț"Nb\_pcl-"|CLם=$=k;+fn 4\SKK/.HƉ[p{}H^Y fxOWִ?b#p}9 kj@o7 ֪2)wTR4sRK+7AO-m̶Bs;CnWF!Tn^X[Ip)r>N)0ĒIkf:_TZ/JYbE -~U!&WBT$Zh R6P37ޅoIȨ̀Ex ,&t=·wKGѮEW QpOsVQ. ~敃Ӂ Y65٥\Hu F_OV ,C<@?36d0ŵZ_aMH]~c˖lo _ -z'y:0JtTݒo2;;E&f֤T 3`)d2S.yͨt )Nz'ۋ2fbe *xu$e0M&Y~}:YW?F#-#x"F?s#XYxCVyC.SG%q%?ąqx dE_m )qo_ |е(!*QSZ^,iD%rB)jrKjBUu.%cr'!"`2{3{n$BYLKYdL Yj¶S&#Vpl7fejDZknYjM\I:FAV٩-,^Đ2XGɃؘ#Nb(X-ֲ(O,LyDIf'Py07+}^qNrC_kzC69C@   "$ .{3Qϔ.UV,mt*K%Qh" ! 9^dz DdUrWq)QrJ%Ԉ:]6A]@k\%DkV  ZZ*ۺVI3^KTB79 ^,y@ V| *<}Wb[C.VwzRrJoap^M\mG!xD_tV^mY}"T,[ҧ{Q-嶂Bo9~s~wbY,'1_Ixw`aQ0 0k]C/^\rڄp9Μ9L$ G wxlZxVj2L>`0effE^otdhʕ*bK$pEQ&$IӴv811aVbð}%''AVk{{D")--U*IIIr<<&&Ν;VH$ v0\.XݱcGaa@ -98s,}}~#PX޽;11tļtB |Zfmݺ5777hK{K 23ѺZ'Ʀ5ΝKIIŵ$I,K de/N}̾{.uQ"4MߺuvS588H4pv;@T_uldɖaafYh4655zBaBBT*a &@[Ax[Z>tP{{nH$_nE'8smmNq87x7== AL&1!*cc;gNg_H\ K800R{E9ɐ%.b fcڨ=<aoi&!Ho~yK'AzTsBlA~bbع8| DRitCq%Ms ryQQիZQs&[,0hPT|cy|f )++h4|u7n<p~JA4IR.-0!~0&:;}MDL,`0H҃^xv8><#Bf1Y gaA--Tce㏒իgv'&&h&")))h B(TU%8}Willyqĉj&5۶ vNM3 Au:P((bLMMgϪT`66JVS ;9EFh $Iz^eVkQQB4([իWG,EQ| ͛7 /8 T(ʗ/_Z*4l^vMPq|l6 < cAĐaǏL5Y|||NN8NJ(݌aD`\_  Ċ(c/@(PUUVSSu/(x!3Jj%Cǎ+//ϏkdPRR" ;;;lw3 ube/bpK?-IENDB`fotoxx-18.01.1/images/edit-funcs/crop rotate.png0000744000175000017500000000212313222767271020117 0ustar micomicoPNG  IHDR00`nIDATXGAhUǿnt7)iI0=""Z@-7A EB xA1 EɡHJj R! &6t6ݝٙydewgM/1,޼8Np~sS)"m e咽}CqBϜʊ.1"Rʹ9Z5O7n ""q//q!m˸ZJSLF! +ă?+B,,-UB$"BBT*F" zܹ;u`C #č{Jkko֨5iͅ*_.W?~Y.uo_i.^TRFJOqJڂuIkSxC@ADvH!BL8Af"-R)-%cLz 62qL)az'""LC*LvBLwLVĿ2ppT ,p!e A@~Uo%$'b!+rYZ+Ji)QkTJ+ZH:ܹɓV*)bBo ] oɩ)c{IKṘWdlwJ;o1gۦ y\А}p&eoZ'{*mIu! ~N&!MCc,6M[G]c.{ YR oeOqɽf@?AOa>ЗaTYY?o ʾ[.+tFs~P`RW_+Ck/5#@ZW} ;ʐjYjG둲^mp ?Z248fP' 5]Ļw}Y ,t?x1/;nIENDB`fotoxx-18.01.1/images/edit-funcs/keystone.png0000744000175000017500000000351413222767271017543 0ustar micomicoPNG  IHDR00`nIDATXG͙PWǿI6?& IW)Z)8N=:W[ts8?әGGct8ZzwzS ф HH% /t۷o7nT+JKd1WN @iЫjү,˒U Jc6yzlmףUUhjP~?ZZ`4r 4;_ǃ1  zzr ۡP} pq:NB%p$`jÀ" 6RR!NHJ [~8 ǎA$H7nàˢ?l=ۋgE 24^o{;nB?$8\hlD?ɓx%Xa`J6l\ݻ ヒ _PRRy4BSɐB`2kǕxkzjRkwm,+s# Uۘ"Z}w̧ͧi&8m_]bQ!_$"[Gc?hv|HSGo]1i趶Y[*,mombQ!%{>{HQT8k R*33[:鳵__KUl)U!B@t~b2U;U;>MÏ{Cw ‚=_U>^ zi㏡ Z4E"ZqݽGk`)B៞A @5b%W.\{ Yvn:"Qt}镞$u:s#V6±_2K dCo)JϢd:S,'P29J)lVʲt|$X!M,|b;cJ.}~JڶNZ}ٙ!fv?\tt^ ﶈ@,0QRiBAM/Z]o ãIC'ZCv;⒒p l(%YY0XpCwegw|O( .L&PmÆ n9c |90J%PX,0 0g2)) LA3<h9c^2 16&LOo#}+>2ttq N-dF#d2@z:y#4?FEX=yۻSOqL&x1G08}@@hjBYgÁ JJ`t.yXB6;;xp ,Kc|>d]>Tctx|5B,"!TV@7[oA xR\q"w|@ l8ѣ8z4PΝklFn0q6\  gY`0ueGx? e%L!IENDB`fotoxx-18.01.1/images/edit-funcs/embossing.png0000744000175000017500000000303613222767271017667 0ustar micomicoPNG  IHDR00`nIDATXG?PYe%$&&@ 31NTQ 6:v zRuۈ7R8 g(*3f&Qr " YH6dwB 籨n~[46W{IρѶw8Z(=J"h+K@)&nѩT*X,-..1 zCP `풺{oN UU=z4990w%߯󓛁Ĉx9@uttpRTAfD͛`p4m_Qϟ}899Çh4zQI!Dt5@-Q0 X|wvk$ISSSxQSEQD"qImzbx}6E;vlG5@X,6}&aHxj<_>P,vGUUQv;˲Fr0 q=P(ZG}7$ \.UUE1vС\.nF P(Ȳlt V… 6-ÃJ̭y@WWիWYEݥep8|u}S!`ltjqOOqO>( W/9sb(c'nB,l6{֭X,O&zڵp8lP(D\.WDln4MK$^7HӃ1fYvppaQd<7}.W1!SSSH$ eir|޽j%FA:! PJ !;ŷoM@iii mmm;7i,F_|imJFFF\.Wng !/_+\R/2:H$200,! y>NSJt:1ƽgϞ}ͳgJRm)O<=xjyIAHӄ@ t:@T*fN80aEEy~~^>$iU5:|>ŋ֚OB>@[\P0X(G0:!R)֯Edqb8Ý7nګx+W4ɲxdY?ܿ8ʫ`0% @奥!R2L}ιsFGGhR14Mn7,|>O)+x{nuSJm6۩SVWWn022rzA0 Ƹ }(N>o+\A-TYR)dR?/"I333Ub޹sgbbb1njj*JFMziL&__ذy^/1Q۽FXBnn9'KcANsxxxxxB^w~~4dZ~Jj!?~'4$)gGCNߝ Fj= ƽ0@kIENDB`fotoxx-18.01.1/images/edit-funcs/pencil.png0000744000175000017500000000240413222767271017151 0ustar micomicoPNG  IHDR00`nIDATXG͙mLuǿw$4HYy W8JheLǢo.@\tfNaY':TM%Z` :p׻E }^\Sb/0A֛NH[H J6Py):|A#}Hfߑ36|Ѩ쉭JkAdm6Bzڭ' u$8-;ڱD p'/u )Qup K76NO> JgI~)QBͭ>;^,XAg]&DcX@h4X_WCn WX Ӵf_cX6W_?]%(?]/NHO[]G?Z+m~/B6+j!5Z998`04j4<Lo槯~ଛG?nbQ6`"}cR^t| pcKՙu9#Bo4CǣX#hSP]f/ 0)JR2q K7/fV=~Sb l:>X7E`BLi#]-sN0Nj5 2bnh|$SUU#hSPqUkwq0S. ژL`l!cWb)[qB{ V'lfI؎emWLc׊ʄdϔ%"SRT(A@MLIY^(+KtP2%eIdJJ|!A$KP*v^Q6Wu\+T׫-SR Unk 5E P.LI#D f6͔3&@^=6yd<'Re?%Lq7pl HYQ"tF>Sl:WЕZh/qB^ Fg`d2'%~Yc pR6 % IMS|jvSS:o`XBY 8ХɆ#dϲۦpdgw~qwN!>4hg)k2wY}D ,rH.«r\n;$3.݇{b8)?*tk`BP Bt:vYoYCo삗*+=jB_/BMd]RXtZ. D#*HˮN/-f[#Vj8w,%I_W *:QsD=bJT`mbu%zjF¹'TQ‭Z2q㵓C7A&HCao!'~nv)}E{dk3^ӓx'oy2x,upg#(\j4#DTbG"N.vVڀ ǜ6f?dc'r3ӭ7 %Rh~-F;R" [e,iP<13'+4Z Lt'|ٛ/ݏWМh .y\N_Pn=zeQӦtS3]/o,W3ӭ'tE0__TY4$,-2_8Ka|׌BK6ilG .N*}oFӶ_VցC_,kE-5DDD:ӈFnieT n5##ǨEB~JMΐGW]c SXc/L .]B0I#OGw2NȈ$c'bƮ{'p$TNUwt|ĪGR&X@b z~2w#28,ud hjXYuacj,"Jn@O֒H@h B` ME nO?o"N=yvl,D'\' a,bwfp*vf?|[UDL8Oxp,Zz ^:B \GRl͜:4C磢wIU-˝&ĂjNLd"WD\D\b]:z_=h-sձ~$lDϕنС=.4׭WicϺ6O^!H,w>|ig[īcwd`Q4U=W绢\Cn|ɩ)mby "lG7T5sFA;k&"akL688 05HeӋ|yf@O'V~+'T& `T;R g訫zyš? s"^/_=>>6mbR z s'ZժϸwCdo%L sא^vIm!vAc`\To:bmnB &뵽Y؍T<Ojn2*F@Ã̘ q(kdi̚\S8V#X$r&~3L<_=JCdMݣ\E B3 t06I䥧7by,';.65M} *Rf @椔H]H)}c(ƀ"&`@O":"D6(d%o XDojZ CR(&[Nr-ٞ;zgйY!7΋fP÷dnw&1a{ay;_R|A J 2 m;[? L̀-^qy\|Djt0p7=V!:QIENDB`fotoxx-18.01.1/images/edit-funcs/RGB.png0000744000175000017500000000240113222767271016306 0ustar micomicoPNG  IHDR00`nIDATXG͙[lTULRn@^ 7D` $7ĐhbQ$Ĥ"Q* X HQ@(Z^hi;3}9k335Y{]9ggDl8&r*aZ&xAo}TC-mftƇ7x=@3R1mv8`[΀kP#Wçڜ#v0tuvHT:lMK dXcmlXgEsFE0ێCp1Xic> -@fÝpk~B=saL8\VheMM]Q"O[\]tuSZcԀ(EDŽTv)5YsS<>m))!Iyq)$1$ƣV'H3i+Y R:x6{tb;e/(UH4 NBY`!}۶+.}bx蔪$΋r!pQvh.4K2v7iV{϶[FӤv~MeB,sRN^ :PEi!}i pRTnJ-vr8bQ/ܒs3ZAWS GNmaz=lyCK!}PW ~޸A!~LyKwAlfK6`M zfJ3 yȐ$DoW-DV1ծ(C ̾!bxz ]9hat${ E0ʠ..^_B }g$=wPuq0Jz a Ec}YGK$ten^8mca!CnVCy4ica4@FZ/ g? XW;vB聽6 sw sT"nu}nxP4e\EonV IN M:ڑ|O5Ga1vqcCO'‹$>X]4ӔH$8Ly p ^bHIENDB`fotoxx-18.01.1/images/edit-funcs/smarterase.png0000744000175000017500000000156713222767271020056 0ustar micomicoPNG  IHDR00`n>IDATXG[LRqp! "0d謑-KS{멇Z͵VϭY=\Ӛ97-"0/󊂈vrpy<>y;l9wo}0 "#.DF\ mVMPuz==,MdsDRU~qBaEemMįzoJbL)"iO;pwdm@t!@W#Q̮#& :na0H&pd8M%He4P!*x)"xb+ˌ 9X]=ͦzJwnrHoc;?Ûuw|oy岙:"9vrr|h#k1;<~ L<m7׃va3dl_ ܱϠ 5/px=gCH*Hdqlߔ E;~Xl:܍|C9Åk 'x)"j_(*`T=+,dl.z(A'FBdąȈ q)J -IENDB`fotoxx-18.01.1/images/edit-funcs/vert panorama.png0000744000175000017500000000123213222767271020434 0ustar micomicoPNG  IHDR00`naIDATXGOhPKut0v" ԋ xDAǝ<@Et*@y8, D "iC]h~$}iZ\}}!wAIK (x"* l:ox b83&WqՂuB?-5^bXx!+!y~I?+v=m(Dq_Hs1cN5(*N˿PD2)iwesw[kVhxH )}Fvw[.Kpݷ8^bivjP})Fug`.5BZc>L]ܼ'؅*S !a;k~xa(@kA=\Ϧ"DhTӜuwMrMI玙7z?+tdR4:UZBuqC$ zpi֘5>fOCia(oM B,]W(w@p@+(wY)οuB8e9߾x}#vqkINC / GgV*8b2xQMh.IIENDB`fotoxx-18.01.1/images/edit-funcs/HDR.png0000744000175000017500000000101513222767271016311 0ustar micomicoPNG  IHDR00`nIDATXG혿Ka?o֡D* PS ;8G^98A&^"נ~3A'N !x/#f5{hw c,xFW쳼`.*H 1}#H&vD,c4+LävA^/JQ;U6IЀNj{Z.N&d|N\>Gz6 d̚2!,[چ>m B<p;:NEg.6U6`k!xBIlMd */,f_: -2Y*QVWl6#gjy+*HSmޯtr5ݲ;јp1(wT*Dru]t:bP>/ܦVA2T w}4Q_|fHIENDB`fotoxx-18.01.1/images/edit-funcs/CMYK.png0000744000175000017500000000302613222767271016443 0ustar micomicoPNG  IHDR00`nIDATXG_lSU?L $n3P` daJF # @ @y@B <8BAG$M\a=g]vO;szg²8RaOg$9{۷њx\)-ef"IIk~{;vP]-=wpYd ǏSR"D9Sg'PcƍrT<2H<.a^֏3Iq16ӆ/##0~*]ZBo5`Nsz3K&L`.Fa$#$\]Ӳ)Kn$BK 1֭vjױ`| A ;j1ԮVcgo4q2@8N[dnB >𜁚#alOq1g8O1U2^RBBMQf6Iӿ^gk2jN:6Ĝ4_Ǔt xUM52d'7^suR}Ә6Դ>Wzp$\v44l^te08P@CB7V؎Y (!ۭ1AM$ϟ/PQCGYvx]#,e.*jj.:u3 ÞeXf2?pXz&~k Lq|WH$mF10s?|}AP `rH@"sl#tu1-J 9B^0C),lNiQ/$N_3&C=W#a\*m?^6syH'`qn5e%B AYj!_z(Tmhnv>i*=ORec_L% cr< Q˥K3ͷaBD"}ԯ!Y}20yb1We]/%9ϩYd08d,z2(NR:* %M& 嶵2E\v<*P:訢dʋikn1v-•̕{MMtw蜦.~Ky`q(}̉3pǏG~ZF=Bs[vvSZDf)451q"POaN$ SZڼ?aY,\ȼy,YZ?=Z[u*r`VZ{s88QS`|uIENDB`fotoxx-18.01.1/images/edit-funcs/dots.png0000744000175000017500000001031713222767271016652 0ustar micomicoPNG  IHDR00`nIDATXGixeoUu-wN:/QD  :Y#"n/ 0&&d餓0wy|y_N= FQ +Wew^%ݍ9S?id3Os[H4Z6 +Wufugu/E:/ mv~j瓏MRF&9eUeRe]d>O8 9^\+_3p]/>@ˬYO5NXhk2]C-AĤe>ahbRl̿D'|;ǃ؏hTC[?};qto0yJht썗1Lc썗:K!lo%h9d_37bakk.]6Ԛh φNj!uzgGZZ~2u:{b |r״ah诞 ا<] e42*5)4g4_W~ cK8Ԗgw]kj+pơ6Ɩ[,ߙlIx!鹵y@=Z )v;BNݒeJ׌Ѥ{ _JM~F1WHI'6p?֪MSp6I42PQG4(4>D Ů `=Dn{}_( m9Ys, G5/B7vm]ިHFc67$:Gz<|[2zcC/y&W^?ua B10bԕ9Q;te W5U=>|;p`[+e %pNLl;JL#pSt>RkGZ$CR,yqSjr)r(yjnj(*_W=ܜ' @5ڑW-Je۾;%Dt-3%G[UU^wynRy@K̽4UU'=6<3[;$RjڞyBVy&p]s:g5 ޳]hm9g g\WOYÌ"o/1Y+`snB6;--@+.S4XA}/QIP ߏ)OH ^mek/6aӫovD苯=jYLvl^WÏT3g>d(:}ùݝwA-B#Ϭܲ08ك#Lu>B!'5_^ʗ9@4-БjbH!,WhfׇvIjwFEg2WV0]\tkic SAPj}C𩙃0LKU5 }b7fz񄕫w5ޑmW\d%]bfk'W59CƔkGѶ_rP/zIM%9]LJ7ˢ%=_:hŽmiuQ|Mm#T bG^,{yC>y֔'d2W{]r-Ӈ=3 Xy~m#c11'3W4}\>.NwDzvR˖g7>c}UFuA},ˍS PvobsT9yB"VDF>5Jji)-W-@e2QSu6t$сȥcG(gM19؟=B!ٗBvsaC񙮎S'ÞuBggOe[!E?Z[I2t޴V&ϚϵA@NX駍B^ǞDzWf:h*Xuҷ2WDT T椒ޞĹ\0hݞٴv}vV*Ȧuu *:&⥖\kH'N8#=d'nh *}C{|%\JG{*nofF /#0͹_0/t[7':e_sh3?eT>[~ ǽ^N6cڀqDEh7J_57ળ=`PV_f~ݝITjj>Q;,BU77P*JmyhF6#^ rX~kIڳyv=s3ڛVοkZdV;mq=oh*J+]Θ'v]ǧ $$6*WOl(uJE|,OŎ'νxyovm Hw-74?j3g)u|u#3g(Jz!Hy5j(aPVq 8*;g8*/CGK"$? l>qbT>mph7b>wN74*ό_ԱwLƎcodl>~q9 @ m/#7g{]'.>+ELCk0IsQT 9U~2!/5#*]8|E4tiҨq }<4Hr 3*,g.#IȎYRZNސ3bM#WI2* Qtqkj;F'}{C͋FZfxA'#N?ɥ!]Տ74#"MRiw _ nj<^~Z]=;]9\%H1Y3tfMP<ZHˌ]b!Ĝ*@`ҕ2dk5 f!-ZNf$ ż.͜@(L՜\7 ):9ct-YoSx<IS j}͟db:O6h?I^ȳ $\zM<)ށ`j%ށXӏ#̙<0}1K(t}@fNbh];|eϟtIKYB'QY#2+DRrf=  ki5%ׁQTdž@{IX Ӎ*OWT@ey8H &L"Qq0ʘ- 7l0V{ ZRgPmz#Ol|c't6:m Lf 4\XC}B\crLJ~DmFum(bTeHK' t*c'OUb Z%d( YJj $S̘NG%ǁ0IĐQ] '(ܝQPw!wabahX ]w߅:_U,Z HgVx.gs _xZMg:}c:PddOr%I*p}ä  ,H!<;zb3^)O6!R!mbu&%gT).ZS@Cv-Z:KA?1 IENDB`fotoxx-18.01.1/images/edit-funcs/resize.png0000744000175000017500000000174313222767271017205 0ustar micomicoPNG  IHDR00`nIDATXGKlE37R*$ !< p !N-R8R#q$Jz*=x$R%4U#Aqu#%Ye흝[{=z]Wpw;~~񃡡N./࿦'DO(PT ?}gzt)+` ^}G}~4>ܟGE 6ЖA YX)t:!R,_wd^X<"ivZ!MɢQzY 8^9XݴVHS2!W_~}a Pθi4Bzl1z~SWhhԌ1 -r6f #4|4|;vYq#jSxSͲVTxCs)PTZ~4c Nmz:+rkR.BIxzbZ?\NONl4"eccE۞.K[M.?4Zy譙۬oV醱+L0l\뇕B9N $NGҤ7}U9w`u雒(2ѱVw.k$ToZSFgIӇ̅RezN晵 1 }2,s|`Ny\ۨWIbw;i٧|bv:9tݢUh㌼m+w3WKi9xBvI6&InZ,1,3kH.h++.O|`8ɹN* k5AV9=:L1b.Va(܋Z[Cw˷C=TS Ǟp+do]rcDcѰ>]X,~+xNNmzjzBA  'DO(>奅sjm~^ZIENDB`fotoxx-18.01.1/images/edit-funcs/brigth ramp.png0000744000175000017500000000532113222767271020077 0ustar micomicoPNG  IHDR00`n IDATXGiT}K30,= LpQc"PB)hV0BDK)RD ҘB,ʥ\T0a螥wι'}=}?{_73֯.-ry_i5@\q~C)+7<|F@ wߧˢ9&ֺkxxֺ<~rz~|jas,U|Zݻ.ЫW|~?ƌVOu=)fOڥ: G1x J!bf":fYrh W7FՍ1 PQQ*JmiÅn PDDx:@3/Qnp f\<\8/woY6rʴE4l O.^{mmVwpV|WۗqR)۾w<"c WӴanK3l|MLbHww4DDt@0:.MW`LCLL~Ol-kˣ7ف&&4Hg/.T5f̈R'2eH|@J)c8$"dMX ȀĽY\}==L5G1Y_l#,!X3ݟ)>@!$"===#G [ 2q:a8' m)[7-}(v 8"af&׈9k-kW.( !"9Hdoz[d_DXM0'cbl0rkG"uGr"r"qJMvvcD&W,{U4:: (B 0 ̽?u8Lz@PP> ! Lfc%9DN֗;x7=מþG:׷6H aOngCx,4/BEDfġ0TD)VE""̓M>ZAR(g۱7D:#/S"`])y?k1cX]I;"$rLפbQd/Z:|ߟ;o^MMۋ-"7BRQĞK}U+h6)F!*x1]DX/\υt!Ҕ1 @)D>xz݆E㺯hcC 1}4F/1udjP:Y~㼁;Ղ^($8I sͼ^;ah:^~chQvy/T''e _OcL`g;r(P"ᆯh mXWG%z"94S**,"(Bp}_zfymSo(ILBښ/ڭ]p#)~a"(r׬Y3gΜxJ&&F,x̭ 4 "VWjj{u: (!wVIENDB`fotoxx-18.01.1/images/edit-funcs/bright distrib.png0000744000175000017500000000135013222767271020576 0ustar micomicoPNG  IHDR00`nIDATXG헽kA;K%P 1x@P"~!b,ha!X*.DPmRXA+6cq&73{Hq?f}yggwj~Z h2 Jj A f|73NB4b^a$!4bꪛ1E` jL_dVB,XW``ޘcY~ʡLȔd7g sOL2jkXM+D,SFj+SиܗB(bh(XH6E7\W :`8 ȍD26t[98CGWej~ ]S qB'\,p1k&EvarҎ0t>2@࡚\]p:rU!fr_r 2dzѧ7O4+CǸȹ@<̨9j+ =&vÜI4#*{Ir bٰr^S:%M(Hsc܍F{NO<gDpOuw63d9 _bn˦Do< DXNNj{I|NiMhyfJ57^7_dP7̨y;CYCxgLLоL,v`$,,jG0o?U m,fj9IENDB`fotoxx-18.01.1/images/edit-funcs/flip.png0000744000175000017500000000162313222767271016633 0ustar micomicoPNG  IHDR00`nZIDATXGMHa3<1 ee4*WKjZBiv^[vIP! &u@w *JNB3/*ό;}f$ӒW$\@Q % Z'PUr4U9 IAdA\ !9>g+ )pqiWAǽ<}R6y$;14&xT(V*o)7ᚈA'Y N !@X#dpubl\ `.A._$w~ㅕp:ݠ"E";i +a6AydyZMteWjhScf*Z٠W>dhf><njq|_ ?V.ձvx$k9Z&M&k= rT|ՇX0@* ζ}a周>3hɯeYZio\Y.={y>J`5Y]"o\/Og,}gjoTPIN̬'ϒ_U敒hquy%5/zDS&i_zv;v]&x;G} 9+ \|<ڧ0^Z`2HD b(Tnj>wI w(ŻYMuj/u!k{x/R1dҜA*]ى3}0'qu.wY\ ku\qШD|ԭI_>Z\5KI/~bMRC|y^q Y'IENDB`fotoxx-18.01.1/images/edit-funcs/fix stuck pixels.png0000744000175000017500000000246113222767271021067 0ustar micomicoPNG  IHDR00`nIDATXG͙OZg"Qz]G&22,.;v,ݚ.EeQ*r"= ~s}>y9oP&Yc" [@ ) ¼O~e檮\XU4M$V/J&K MLLLOO4.A;wtx<.FJyP#`"o/SW2ȆD幑^CjE5ECh|˄/Jt6={KCNO?QiMPu5ނWMk꧅ϖ Hݏ'D8{E$F!aiz7iݑ9dMzlMYh$_QIm}>;vLq!GLZqd}؊P$,::ܹ+u,adBfP 3t (jO\|&%cPnBfH9lhd&T:G9c,[ ex}L)$"qwZ橣mZ-T% o٩6lܖH\+= B+~Ml#Eeq-`iuZ"ԦMHo~% YGô-"4\`,wY!"q\;{ {V ƍpL:B47{o8=tpu4&<;󖠙)RZKEFPܯdBf]6h2ۇWs(5"R'M/Cyax%O-g\`-okƃĠ-t?}I<$zڶxGBk}$ {xpr8݊gPLȋsK{;rԐП|![3C>Z.+ہg%PL!ت@e_~o-C7DR ZCٲ .Ҡ5ЉgeR\hu9뿽s$eUkENexp}yn5f %sFϳGÕ2JB^m}G[Y( ee]8zmU2JBjAz]>>gPPP;ЅW_&Eއ.L&777S+ԖR7o,0TCCCnZNuheYFuTh   ܨnBn۝[3P ]z!S!) @#cO^YIENDB`fotoxx-18.01.1/images/edit-funcs/text.png0000744000175000017500000000374613222767271016675 0ustar micomicoPNG  IHDR00`nIDATXG͘{tMW?{o^r"" I㝰*^m0gY^`YXJjڪNgF5)E )b,]dH4$Md"IC^q"ݒ`Z[?wٯs$4B UXRWĂFB396P*4WvhbZmhj'S%$?0_Nb؊5Ii+{fcv :r\ruV!.ˉXf/!G)0573e`%;ɿEԍE,*1CРB!+ Edy4+Pײ|Y*1##c}HS:x`W̢ȑICc{ (?K3,Ɔr+>d{Y㾋 ,eȠVJ%# O~EmTCq[UmDXXi ?sǣg$d:JUHUHӄc:0rߩ@t6dG*[Li6pVa=ΗnӨJ\W,/Gu}:0r0c5ãJ+N߈o> &ωV@/e?wˈe:lQ9}8S}}_ךcA)Ilcwwʊ (6pv P)>9g'BARt=ǰ{pR!sqz*W$G:)QW7_!E+5E'ǬYz<n,Y˜Po [)\ u P~b@,-60͕B8; U'Sh[%46v1g|-{qx졫wscUKvu~J b6%a;BZi5VJL0SJMC;*VD9 T}z8KBGK{t+q/,%TL6UzN 6q;)_x&R,^keTX /5KLNSdjĹ޵Y^){z)'&w* 9zwUL*3u5j̎~V5yǸ6U(Ʀvs{~EDr,Q퍽OÀ!\8zg~^7r_{ jTM|)5p(!blID =#a+ *Luuq?f@0ӏ[B%J9JHFphHYWBaC.;5oG ΐ̼=*ܥsR{zշ&mtOX9b;xCD}7wث0̨qk<}nVQ ͗ Tz x.D"aE@Pf677!r\(JSSS]]];x,=sf3333 F׻P(0<8Ng!L&Q(-//L&AiR^D"Q1lq=>>:^҈DŃb񪪿 h4pb .//5 *C{{{(ՊyP#˟pb<[/O*Z1@555t@aTS h{$vpbnw$ao3::*pbB:ݠWWWZ78^!v]{׵ EQaee%po0RVUTRJvt1L I2 yHD {~~fG, BfbxlltFZ&[D"-S |D`0,B.C\2lll S W_'~' x<fIQTww\.g4~? $I·l\>00Pxs39;P9J   H(!-R*z xZ nxjn!@Mwwwj0 477K _}*wr|*rr[{PIENDB`fotoxx-18.01.1/images/edit-funcs/fix fringes.png0000744000175000017500000000046113222767271020104 0ustar micomicoPNG  IHDR00`nIDATXGٱjPR -NN:}ç B$qqLIp8}/'@>McRC9(Gr~76` g^ݙʢ@r:~iq 8Ѽ FA1 b(Q FA1 b(Q FA1 b y&?|.๟/$3gnx7ڷnpxn'y:/IENDB`fotoxx-18.01.1/images/edit-funcs/leverage edit.png0000744000175000017500000000335313222767271020403 0ustar micomicoPNG  IHDR00`nIDATXG{twfvgI$#"IMHDM#Ğ`* QR-ZɩhC1$`Ɛ(`d6cvg_&n=w{g?̙%Qp=!!DJ^D@3_xQnRR4'ؠrvˋ}% .@$;=xYVɇ"!KvpIG[Ly?5[M|.2b_7ъݨÔIYCU5+J^X69D |!{E6 EwܘU+eSK؄<=}YȔ㒮Zޗsf 4vB.yHsW_MNi;Q!}UÇ6/ذ 3 _]Ľ!$SN/ JJ V BK> |Zh%%q#`k4OӴsn |=7 + a!qG00lX FkIv :Juw YdoӯtH Ƥ/J 2XP.gOiJW x62`N$ܪ+X]"ln,|NZ|(/ {Κvs2y NOy(t-F  0WG_'2/h:k7%,x xTin' ^=?O_vv뇝}MGg̘LKOX<{p]d0TT)h }ڠ_9_^Ɛ WN:'^33 1YJ@Z-⁆r x!Lui.W륵%U*2zL{qqmzAIP7QX1&g[_M.zw.H=3 ,2R/&?J/ҦN!>%y݆!1|tt$uhb%&Vͤ`%nm'^hhɚvWfW{9?! ` sBgA^9Eٍ '` ZPk֊Y&ؖч'UJp&!4NBeu^ɔA}'P{7J7[uCW7F#u`eZnGHJT ByFFCK2hoDO/$ RВ taӨ +\"x5k)#[~!^_BhEvE@9x5kCV\^ڻʤrr4jDZ;a$0"{ O +Ii#IO@"WWdFjPm*\b_nF/nޡ Y :^Ģ3;!S=z /qDfNc<]^wEQ'm0\=i穰f5w괣?Oy:$==@)LmYtMTPSNw_4 7Zl=*u`>/-&uN Yb ;Dpcc+ $ E ",ySi<699|lX wfz pIW\T0^fCfEus-@ngIbRąPl}zx Gb9*&d9/XA& 17d1'qȐ%7H%Gl `]%,.$G\H1'w;!IENDB`fotoxx-18.01.1/images/edit-funcs/paint edits.png0000744000175000017500000000445113222767271020107 0ustar micomicoPNG  IHDR00`nIDATXG]Eǿg̽viJ` "V  A$6 hG#1D4QcȏրJ. (RXvn}{g}xrvsg>=gdiݗ_2 tgN5DDs~'nXQԧUwNϷmivٌZMdE/fjMٍ_fkVj^THL؍d(λ˗4c Nwh]r>^͌B~EooĹ}ڢzvSU n٠9K}:8 g*n6Ԝ{i%u.5&н>ŷ6b737|i:w-6ܞ#vO7o`; @=f.J=bͰg[{?Zzѽdhzd.'9}z%pޝgTU$kvԾs>-ɞU#\ c_ H_OrJx];rzW{mw5({Z>V<2~oYz)zw@`Y:y+ޞuho@@B;<7n:^P dV}k7~E x@DVE-RND@"sC9$GaY[-:dxo!N["M-۷L w=3WŋO=whP\Ȩcu/$k=z&tɵ/9AUMDB>^Efn\u#9Qwo`cȥ- Eb@ ^$JyÕf˫^魯cyͧfɾf;4%XyELXK*,*$1Q8Gp6+<9KZ+~_l[?O.93!hJX+p1 Q0 ä_$6L[dVr̎[~G Tn%^>=Ƽsn#?8b+h%0)H5%TK)AUti /CfYih*Y\bжe_Wwf-λVGvVRҐ.QRFŠbPѨxI5bq@BN`Kf!\3.6 lx~ktlWKOhab3i%0ZRMU#j XKx®P4-+\s3ʤz|q \}Jqǽh'( @xVdA͠V@XmUQ͉XIVDm-ld#jZJ\?F#ҩL $2,)#i%YXv)@hE:~ )Nr+-I{].lgfsqW<Q+wV4r ^Ӕh$}!/Ef}ܩ"s**4w{ jו"LLFK(i" Zadub=F'"K[/:/:+1'i9+J4 $"(څ8pD svd? ( Q{uFg@=Z[2ޮ($,D <+2 S”0ҊX\ @I4C;$ԠjYNbPD;C!" #RMU+P+5HšXQ"VL!eW0L3 H@Pt᳤PdAP_JRR_J5CUT0XAL A(?'zo=^"hTKRn[NP@!ʸaJ5-UZfҗ)ST Utp IN@skog?: ӁkZc46k aO,^su0S8C4!s;זW"=LXh\lNpzceȴ"iŒ&OąR[L`Ar.ȽcQBgvתӛ[ԏN,8ueST*wK?fT4Dwꇸ"G})Z]zھd1qm 0b#\ҽ]Wq5}M<n*ֶL\\mn@yv؟N+gvhۤ0Flj=Bv}=jR%U6( g9zkrf/^MG&g%YS* øqbd-]9gv]K#ӚG RI`KFWCw=y("Wi=Ú"Y\pca2sc!>X,^jJAҼ/Y\glCxNq0AѪ)ͯv.[b1ApO=[|ͱܽ'հBĝo_S+ԯ~S H|ۮ5 i#lF@+G]X\f'?O#cKUjUKs{ ޫh~hi9=Z`[Ӳ-=3 Y\>"c8 Y,?8_WkkK 6mxhۦ%_ݲ$龬5|˛[c,qu@BFq7ϟu-EM =/ |a}3i!8FSc Z%|kǽ@Q\ͨ0 9܁/dk$1Рo KXm J9@(9V8֫UI#_cO*ؐb )^.T\|&Ll8gK51\96%ELl(ڎk9НН"ZwwN7o$غIENDB`fotoxx-18.01.1/images/edit-funcs/stackpaint.png0000744000175000017500000000223113222767271020036 0ustar micomicoPNG  IHDR00`n`IDATXG]L[emOE>c" `∘l.b \ q .#Y6.t[pcUV!c le)tmRѽiOq?ϛ=NCCCSSd%<~ss3E:]ZȔfE+BXvBH$jmm%;rx\psVWG> :2%"Fh:jkEQK:}47ΣtB@BRy}G@,3!1;U:n)F}S ķamoy/,B S!S>W֦LTy: 5 \dDMhV?`lo__yz!{mM_ܻ?:YAyVNv q;>pdt?(>OGy馊,~R[GT&o.|~E>32s\[o$~ıv\\{pr[۴/W^<)+hpZ.9mN=z!*J-?w%5LKo@]/9ƉLfBD!,q|iϙ5onTu)"t86 ٦gTC+)Z\oWFhooWTd!4k~3u-DГ zI=NvzG[{hO Lq@ aŻDA ֕HtofFө1|_#o K..H- Ak}<÷'vX&x؄V&PВI-!I/ã)b,cwuŠK8SJZ)q&: C`X:<+dNކ{6 %$LlH4ԠIm7)^|컉"; \rK&mkVˡ$2(T:ÜQq&.uʰr U UNL,8PYeL2p!EѮt2 JH+|yΨrVfz_;"t];ɯ[<rJġ"Up2g(}lxYF}Vr8L;k}{j/³K[Tq2$2-aډ S J7WA2p? sOc v6*m5}#ce"ywf}VKx2ʡm3;W8 G.%!MnOWpQA(̏ 2"I KVYD`/xfafA3 PtpKP"UjR !%͒iL5to TEUC-B(LvwؼW}=lj$ȱ[-T3<{lg7׎;_ \F :D tԡY>Gtʵ+5%|gѠYp)ZE(F1}?Ϯ>NP.J]@w@Ht5^d*"qA Ɛ]$dW1ٜtOoU֮3\C%EȞǮQIDL,]vzmvP<8ԆAt4chUw3."pKƙ0(]پ`πQ#`# VQX)j3&V,0/ P} wQX,VZj{%mY-Z ,$!EJWY d BB,1jIV,9? ?^ g "AIT\$A"wb;4U}ev$N(gB[gCwB[ND+;bXJ]>|l=_l_ ݝo~2 ƳFɍRj݂,n$ʓݕTYiY"̡º"v\5J3`<IENDB`fotoxx-18.01.1/images/edit-funcs/brigth color sat.png0000744000175000017500000000330213222767271021023 0ustar micomicoPNG  IHDR0/*ޞIDATXG ?rާ?`O|D#EtZ )[Z" ,;ƺ>ϛZ~^/qill7ޜaiN<6ɝ0Đ A ͺWBXtlAWtl|DTJp7(Ӱ S"+]zruk[ΒA.YdRY`W \OGglݯW8C2cQK f +A_!'?pv.; &DHHF2RJV2PT% IBQ"K BPo^ur}*&Y 9KT;oXkx6t@ZOhX*ofyl`LHo՛]92jYl ۺA3}jHa/'ok ??٥d EZ z/ wh(igHk%QZnuU@DCcߡ~'uF,˲ex=.,GHalM}hKRR~%%Go|qBuhT| ` )HOנ2-Bj!,0 &jywga&/g?ul/v䣎ף9C|G=^g ?f%dRt,Bv=`vT˫WwmW@>WIENDB`fotoxx-18.01.1/images/edit-funcs/warp curved.png0000744000175000017500000000335513222767271020127 0ustar micomicoPNG  IHDR00`nIDATXG՘klޙ}zwwNˀДVԠXBE>mBҖ|HVJD$^R!ibĉ:^gy~pb:Y4sνssJ 7Ĺ޲zRH?=! \D5jznbѷ.+D_Ԛ1)hW.-lO( G2yMF|P_c l)!zTgn/5:ŝ~Yn-Z'R*WhһKpMCI@`5Vȶ%VCm `SLkRQQMv+J'G뽯vU:fwD)XRzAYkV[L'MfffdDz͢rTu7DľUk'l$H\ԴȝT5hSL ss6,U'-L rC ad-0j3gf5jؠҔ ʄ} SV0*Qb i?fog9ɳfuo}Z-t1 RfG77\jzocu_.*KZ ƶ% `IY JSNmO^x=w\)AV[$ 2:yj{RM.vZ /9k@PjMhb[ *FԐB] >pFэ7(tŢԂ[RJ]]H7VDiȘB^3wr3Y,3sOb}leP0džWt0&ͽV5 t9~Cld5XCI !q09E Ej7R~Zr&DO. OM%qNK z&b}ʤxjnVV)DzI,T+ˀ)PӄOʑgȧHU3^#kN`Ih/nAsT0x\l+)龯z6y Su;I}Bl袑04D؆t:w}Yt[ C0=k:JD72> pL9H.l]t2Yj,nH)<b;)$NL"=1TQ:?M’<ch vJaxI*zLO/v܈~ 0__c7gR פzNu7fŗ"-e_Kҡ&zOṿd?S=6|Q%ފ&NxF5:&<:0M/ eR2TH*H]~j0|EF[='nxe-ҮĽq J2*j &vvg/5O 3,ʻ7OTjQ^3D^8ZF϶B)2"r d՚Q _6e^a:ÀH㲑ީԺs(ΕewX G5_Rka荶jFue{ó|u]7;r؅P~^3pHSnf.V7jٟ/IENDB`fotoxx-18.01.1/images/edit-funcs/landscape.png0000744000175000017500000000160713222767271017635 0ustar micomicoPNG  IHDR00`nNIDATXGKLAǿٙ>h"x8xăP2jvP91.jwlPz [3+N i@ʅ:ytxuLLP(XUx,ߗ@PF"'yf3CGoH_v|r7QUٝp[R38c^=oN͖]7 *\<"KߨitڭƱlʺ"=4kWL]mq*{m{;)f6iI+ I=b Y{'lhWi@6bxȀTt4nGh1[*6h XN:2_k;N!Ƴ>T7Œ#+K-rwW:>" y7ey(nn\R㇈d 4_kx ݔQdF!yW(^IENDB`fotoxx-18.01.1/images/edit-funcs/paint pixels.png0000744000175000017500000000473313222767271020306 0ustar micomicoPNG  IHDR00`n IDATXG՘kpI60@"ĂĖ*-j ꌵ"ԊTduj{Qg;Z?tX:SQI-$Cawd}sN?eFm}z=ssD/ n47Jz k̇)Π"{`s+:9Miq澂b37p2ٱްL.1]wl1FOZɒxÌ2/wg YBt";Ni2$_msJ5Ng 8 I S$w 5yPs)sJ'3F[\y%.ܱ]~V9kV{f+,zh@+j1-~4dFC] JN-C]" %ߤ];'[C()2+`t)SnH!);+yBH}kEّynܿ\@[\]>љBV=#[}ZΘܼޟT"OJo-cѨ~{d J+xCHg gs YoP')ڒ-Fː ǡQ:l[W~hH=ROh[q`RA]+`8! e]9c h2]+hRna6\P_uN5n}-?"9L r[nR ˙hR"R4@7!8(%L C7@5P B@gA"n988kyUUPğ^aps=OsU@PJiCذL c$vV ʈnU@\T΀+L*C'ۇvAeϛ7wpps·‡T_}kܓnY].GtBؠ!P* Icr4H u";H**{{y&+R"%f̨޲GȨo ?(P1@g6saS (Ibh kӊr cɧnyѿv eYV}L4'uC.5 $!(@,h\ʤ K@D:uʩ`pWx{ގEY殻ZBʨppa@8E(\BYZF-"QZVh,MMxnXAQXrK SeEE4]!%"@)LWsNu__ukJ옧%IENDB`fotoxx-18.01.1/images/edit-funcs/HDF.png0000744000175000017500000000332213222767271016300 0ustar micomicoPNG  IHDR00`nIDATXGL?qd4J؎LM4[ML[\iV)n'];mnlS9PjҺͦ1K]Y⒖&l9Pq޳?^^ύC}y_`._\ΚSN԰ _~/Ϥni#@s6 r,%#Z j6R:vZfSU=0Ukpx5kc;})f* ><ˡ۞ 6Bc+=̅dUG7>ћDCw8vygnNf.P\WGO]TGXŹƖ7hUK0i,)) uO8Es7ΗsR`}oܰ>B8VSg^~wo[473/7/(>_)sc-4<<|KIw}*<ؠBTʫ\x滍 DQ\Lsޕr+k /E/=r_%R]lؗ?sQSHFKߌy~ʖԨ{MQZwD^#D3TuHD`[@m{5yb{z"ag$w-?aWԖ@='Rv~$B{{Tی?Ar!)vE/}9GzFkհL]!hUat>☦ ***:w)% g퐛ŝ~+n(Nڨܭl/-N`#@טdq?rNߨahXǏVw!t)2=F@ ;uK,t;'6?j b,|f1|X&uΎ܍GLC(^1>ӄyࣃ^>uĺό)*dB4 VHS ><'LZ 'kMm3;̬lؕg+;rOvߜt"T)֦j6:R򎏃'M㔎txB@6V ᘳX8'GMޚY8H! C@a mǜSBk _H_oa?lk+Sb\Fhkk#ƹ5HM.S.\` ^XX2$6=w$lt:50Gp`tYXKz"VU30W/-Ъq/BozO4вA:/Lf7KɄ$ulloydšeZU^gsǿw'&huh=t/쁺8w&Mny + ip.=;fzNJAƠvnj  2Wa/O-Z[[KJJL!ϟX(O x%^_` Ԑ)Bذщu`ϔM͠όCPv2n3g,0>D>5i Ȃv98s*aWRfvq]tiڵjxhwQ,8IENDB`fotoxx-18.01.1/images/edit-funcs/unbend.png0000744000175000017500000000350713222767271017157 0ustar micomicoPNG  IHDR00`nIDATXG[l\ǿ3]zm5;@iU@JH@CUTVZUjVU*Q@Q_ qؑmx{go63}ٳ;+ $5qϘd1= 3v ?tx03sp̚2/qgTp [ 1-#xWG) rr!`5Ɯ==iYӓtm lK/PC_i{W15':@!| ye_s>'){򪻼sYaNz[ׂWSopMOtmwe09SIIOzy8"rKYsA 0*n k'wr@@z%C鉦w;&uƢ!`SXr_?ra7t\#SjTR4*EcqѐKzzKSv5ݝ( { 96TGK!mh0R׀<& .  ndaPsCC_zQ:T$I?XNփpgp$B@}8ZأW^;C< Cb>H N  x@7B!_)P/oI`Y X 51:'T.G !]2 r~ڃ44M=Vrȭvj-^ X=Ȋ`x;5GlP!02C^^)k)ʭXY ͙wZEs]EƔq9Fa.IW?yцFH)Q )m5{uu[?~)D*]ߺ o^k?ww**Mڿ()lՅg_0rL9:kOF+^|$u orC8a".n#:Q3籷G(#*.v|_m YV/PT^g?~Ծ ms3>43pO&a"!I#0=} zhQd戒8&I y!i~tR0X*ŌTo~ϋ&<޺CZ~E輄 t"mHؐ!y9"}.if=frkEJ!oe%K,. YK;g ʠԧ]Oz&pzJr)#Rg+_ b[ٷ.w2{&;@$٦fdiަc ݕ!nДM&(]D|,X`0:JA$5#ys@~Fe݅ΨQi_[YOTWXRen+}7!*Ђ T0̱&LFUXF)wqhZĕ@eD%C[^sDq/X4+vfe튔rƙs࿽(ż;GoT-nR{ #Dhh4^dɀu@A4z|H:۸d>w45ሆ<5&'N3\R+r 78 טpcsVb(=nb6&MZ݁]39ƄmG 5S*IENDB`fotoxx-18.01.1/images/edit-funcs/whitebalance.png0000744000175000017500000000342613222767271020332 0ustar micomicoPNG  IHDR00`nIDATXG{lS?DZ e`%3@+vR mQMM$4ֽPI6nUժb-mve`U%1J;|ry{nn s-kp`|r§LЁeJa*,?]ǁxNr6 +(OŮ%D M {WpPWRh"7|;߆#p,A%.YćXPfLTac#?dVda2|' TQo"|9v((M6vd7,燌CkVYRhY0J!pN@7~\rZbj\>3,a/O 7U_s?<+SQ u*\w{Zp(UnJj`'V;a'ogo4Pi?:oZ8x҂ۇ S7? gރW0pnf݋72I@'b+3 -U6B3a/LJ돽Hi*|dd@_3/{w &7谦c` ^MXδ;PSRJ_U+oՃoUjt4^8lGm YK#[cIxp68*oH49rSi4\֗nƠɚ%䏕kh<=v /6+>xM8wvv(~R3j])molA:N_7 emppاL&MSMQDII΃lXtYrllA+Ꮌ27p~O_sl:ƲLӧie?YxవARAnx>5TY鴦i*c>oG%N¬j(>_eRTB1DŹB^:VV°rә M"A!;QѡLV]̇`#9 ‹it; M0 @+.8 +JF_oH zd̓PMV=p+_Y ޑUàywp\1ʚ;xUx5m{c@ 'S=W[~`eQ0|aknh\=ѩ?:/:4B#Ns!Ѥ>}p0`hAV|~l ''x'IENDB`fotoxx-18.01.1/images/edit-funcs/tonemapping.png0000744000175000017500000000757313222767271020234 0ustar micomicoPNG  IHDR00`nBIDATXG=KH1s'U]ִ0^{zQyo޼ fGQ@pAu>7/r{?o6ƈmmWz1bH}!`D0$RBf(eia`F-DRtQi>̆OO_5hr9_:#a۷C/_eו~(ni۶4#b۶yhVKYPCzgZ.KY<{TuK秧~yxx뮈|nò\e:}]e_oy\|E:cm?ǿ]Vj"R}6>i:WǟB3c9>>ߗ:?ڶzqC?2#4Mˉ#x\A/W˦ZӲ,c^|x>}{<ƭòz8aj·ϥ2r^^ami9> q;o///4e[Jy`EX]8]rcFiS[nTL3Ai֪9/`$S?/ò{]^$7<}ĶbVLZtrv[EViZ'#0 rV4Q) $ۏӃpnTdҊJAs00W/ڱχ?eAuBi9qKA0V)IeJJm%#S-Y z$uɆ,LH0E,zeYm==nrV* QIclFm[O%Qp%`]vצՇ};iu4j\b}n~^{wֺg0%]NDʪ*A.x"8!)ϠNғ#B2PDlmCk{;_.DII$R9*hD:D yL 4V}Q jP\aϯ޿m֌.,{iYPJj#8ꤱfJdHeq"=<84MBN4KzVܽL= G ).\.~OI$q iQdJךP>rOMR2}DgMaY@Y>YM+ˬ0a(Skـ7'm rZ6wcb # :yOJ sa {L2"v!-F>ƨSq.@ψL3'!3<Gjp<4J"[omz^o/_Bqoכ2x>338)Izw4{0zLӁvJ#i9P_o߾}zy=b}YHJ4S%Keo۷oo/__ӧev[u#}H[#Yo=%w)Ծc'yáz~[[o\o7w'EC07CBk-"3ƈ]:q+hC'W|cYJ6N ~~>zriZu۷ i*RZlZZ ֚:>2AJif1@Dza><Gxe̹Ƙ}L馡ݐ<ϒLe إ, Lz-"dJ&p69*tBLE9=Z.S 3sv"Rԏ I 0"$(3LiS&2ler~PjJFF13e IBKEff&I"#B={& T:`D+r:q>L=k sD}P!bIB#$AAWǠ w`I&cD&(مQMS-Hݯ1vasCd4"zq&:daLBLct+ȌZk{ F"HجҐ@K"AF ?!ޛXat/u=Zčc sB# zF!FKtGRA 3ar Iђt-`dDmKۧ_8_˼}2M;mL$H(*3Xn?@10yrzݠ{*{Gcܫ]eRHPtL}H$I: IfV t<,VmO~ZJ{$F{knDA;dWD"3yI H("$%%0A5PL}~y}}wKRk?nnw$~4$) ^07Y"Gk݌ {kw3)F/tRPO52&sXov~^}\Gd,^ۺNr~Yf&7s/H9cTmkR'HqG"I(EA3Vzf3Sߏ:&9>IENDB`fotoxx-18.01.1/images/edit-funcs/sharpen.png0000744000175000017500000000367713222767271017354 0ustar micomicoPNG  IHDR00`nIDATXG͗m\e;wfvgݶR*!{PQ"!| CiX#/Ũ$QBB4F)- ZLIĪJK[hKtv.-3gv;v?9kI] ~ha+`<m0ض/o^qܦG.ϖf=;!U|q6bGHDR)rw9܎PFPqv/:>{5)^#~ uժ  zf(oQ}7} 79^j4;V{;'[=^Z^נxrul~]P?Ou=ڕ mۦ Ė-jukHF# Zj۬umRcFvH/R-Iw'3:ڡ|,ͳc\ycHu=Ѓswz}Y~mb u 𦒜x$ORV%>M;NfM%֐ղe[kV{g'ŋ[. /xrBE@f6dr饭V+$i5= XmFڹېqхfHeMA3+ Oo "+W v V%zKljKW(Z+0nkșgjWNz۫eYq7ZWK뛟,D }8;5t$0}h]ѐ*:N7ea6\lߔУ5_6iL6q5~bCGKcDH&qk oxEEXr ^svr6yՏse|R#G%XZC#fvJ 2>XǕOEg&bIENDB`fotoxx-18.01.1/images/edit-funcs/warp area.png0000744000175000017500000000245313222767271017545 0ustar micomicoPNG  IHDR00`nIDATXG[LUǿsΖeBm)"BV 6Rm4RF{јֶQ, `C|h+ 4զiIZ(rr)^faqfvA!beof9fQڈX+ !p:C;_("O@:}; Fnkk c~{V /Q[3$45R沒_m?hB:?v;G'$i+/6[뭯!kBod_/pk24ŧR}{OuBf.jFU!Ƚ{7i* pgzU`oy1# x,Cb~fXa >k^ %m]L-jB?[A8j϶wH ׾>4U15[c ˡ&t@|gz]lK0=I&rkhHƂ F0[tLwBt\Stϙk@u(H|Zx%yE[(g\W"7ĞDGŒ(&?t}Pv265NX6- Y{jק¤U]Op-EW0jfZVz=`8Yյp/Vj7ܑ!W>~(x S5ƁΪ]c$ a΄}u7>Wx6݌*J09Qj 9ݧM|9ey궨b\˜&dۘVaR08mE*x3}n:;UUBݔ-:C Q~Kqu=!OHXtSdh. ŒXtr>g }IENDB`fotoxx-18.01.1/images/edit-funcs/voodoo.png0000744000175000017500000000620513222767271017207 0ustar micomicoPNG  IHDR00`n LIDATXGŘkTp G \퉊D"D#gIx56%x"JjN454jT,ATb-X bLA" r~χ10ߚ/ϳ}n0Bl+W ٳ+**,33"Z{{{z}``EhϞ=̆~ѣfx8C:tH.ggg;;;[`knnn/B@@ʕ+f#š5k,cٹ\.7˗/g&7)))=~~~&ш]NN3Z"4bvŬg[oÙ;GP(zz<|Xmeeej 'NaSNd2Џٹ^jf7~ܨ2wOMM~`޼y\TTdO<$,X.˕@HHs7,E2Qo~o1=j5?qT_f1eDyD'''ffQF3>U(^ Zjcƌ Ř1cΝ;|ڵ0Zhs;N9s^vrrbC#>|0uĵ/6OHH`"W ~f-:#d2Yvvyu|qgx"q+G:[[۽{2DEE,2rBCC0+ntS=D\TT!!.,gG h2yljD_&&~6WWWK(3g2ۈFV[fD JCun7n8w.1q;A0OqNgRYKtq#QJMMo56"G "*AD 1"!)Sc\>sm۶f2kI9rd`zl&u5ˈ77!SCD^%Av@҃w}Kt1))i„ Je)6YDو[ ިYj(tG>f ̿sg\r?OX1#dB&yyyMn/I uY:X5@t!m `IK8p…&bf ihP}||&N| lguT'߇FAaV/gs[&Lp/*ЕVio(,,h4$H cook_^{.!I0VH;O :$k 71wܯo(-c FEEEES'I׀ՃTV C :2xU*ج_wU'L@E+QF2BϏϐ(o!`j\S+0@\gرzNNniZcBS{VVV24~7.]K"Oi.+護VN|(Iϡ˶iLH*U~@>SJ}A{^ЪUZ&999::'+'O$BFF7_Fgy8 Ɇ:/Ҧv{ Q&ۈ}e|XK7pf6ǏFDD8::$u~˾_%=A+ؠJcD7 ~WV*ˉ?":SYy#(hYd_|FܰaäI,woߩc*h+!s kii"JAZOwww]v'&fn?;vObť(p2,""=j} )jwysmnt: )q{D8ixbJCCyZ7oTb֕%_̈ N y;.]ʬ"jTNfdՔȑ#DILu'zNSY~''N0+kΝkTz QnF S3;;2fJ5n8>b:T]:UbPۘMQkk떖feM̀{^Kb@@@?3޽\a>Ĺ ajf[!005OT/2wF{zz:瞵|a>rK.ILm=6ƣEeff>Q,III bÆ !#r)H#s BBB`]]]zii)|כϯX "vk R]Z\Eϯ;11\\ ńN[də3g,cO?\3 =Ar^Tg}};-BT*EQY (l 7ei7Fjkk#r O "[O{j2p|OSSP7iiiCmgx qNv3H+ #/ah78ǻQX?&z[vO(]aF'*ODCUǀAM]+u .2;:%zeiewIpTM~Z‘&IENDB`fotoxx-18.01.1/images/edit-funcs/warp linear.png0000744000175000017500000000302613222767271020104 0ustar micomicoPNG  IHDR00`nIDATXG[h\E3fۘ4fSmcT"RKQAԂTP}xA"*M! "("i.6*MMd͹Ό=>{ڙ7so.$!8gPӹ{iɭ\Q(ۖ"$9Tx1)O,UgP9|naJtidr‚=4`SVo #j #L63*G՛=f:4Ǒ#H5C2C$&_ڃV:eqxnAXb#}Ggg1 ty;Rw삮cy{},#|(bCOXM+]ήGdm5xLY}{{8L1l?0Ғ2Eݯr!kk±}x76;5gg!˷6@U#k_;^3u|!L`CN!l캚 A`M>QHt1Yh =]VjF=(Pk\V7,ĥwzJqMNȽg̲! Jb2J OXMbM|Bm[[孆C-KW.T8ٻFŌlq WaΓ"\˽UZ#vy֡΢P3r}Ǚ>}Wp@=c)k hst JMX: +j uRPOYʐo y_~;w3:2Fy־*To|ҕhRN:x(`s 0ٟܙO݇D7Mx2;lf\ -yB4 +Ѥy$A ;٫cokj@; [ &pk^O ?ggVc׺m ʹझ = Gl=+_WzpN1}|M<.ElRtyymm= X`LzhP6Q%g,x疚dM=%hc1imfCWw ѸaS_&^Eabhr:ٹü6>bvul獱5kX̦?sz? F;,M*.vқ )Eu:i<_pOF_:AP/̙Õ.9"a{+B7~]赪XrN^EmzTBoZK>9>a+o(/WڍkTe(',^Ğ_)^deAn.V,%m#hnqB\{ n060ܹ7ßЀ\ٗ7tee2mL1}@($6T]r}3uhPޫ,9K/[_>);!(9ZTYYiIرc p2$N4cؤQ__/)-g)P(488hIcpp0 UģfC6Z?IzqrV466:_Z+Fkz%N۶R[[kIvVK9_|aݚ\]VF?A#AMRWhx%䬳%y9l|ƙV)A7U---rTWYɍ)7 R7v~\a&~۽q-Ҥ6D8Bh4*h4ZSS#8~+ <6LM5YYTTT,j^ĤX}`Z4MzuuupX?N8z[[X%P=ws<OIII~~~aa!?(C󳟲¶:y-^&߀ު[6ƫ,o Mn]˸֔Th,猬wֱp|Lv0㆒ܦ_8weqiuIENDB`fotoxx-18.01.1/images/file.png0000644000175000017500000002422713222767271014563 0ustar micomicoPNG  IHDR>abKGD pHYs  tIMEÁO IDATx{\G}?=H%YB6~ <Id!aY6%$g]dM 0%ۀm cْǽUTݞ!iFȶBtZ}u{T4IMjRԤ&5IM"T~ǿ.}w)>e{7UhHt;o~J B "A!x91{pBɲ Q4^88gQJR)(޻z;{9_oK}e%x *8 "9R*>De]EkQRfG,;㳳s{T4)(-^ FKKm۞׳}v6].P*tsJ: pΣup\FdΣRAHvP2"Niօ~ZZhᕠ TZG-. ϒ Y ^33 e8*˲{GFn~(! \G|U۷شq{rr˭l|S K3Ȳ4˨UX"qyu&& k=Zke5 bdh4MQ 2a&GPxCphrci-xI3V*Jxq(ȬG'Ԇ(VSBe$IxvPhIRB!ZZs/;:O=() @|Η|ؼ7wµ^C\azzj﹧v𡿽ow`FmpNBC'@kOO MozӛtGG'N sIT8\JӌEX\\dyK_or'&T*1G~nknş_z?1LZmr"SVcrj2jmۺefz5Y=4559Trյ7wW04t&gaZ,fxѵ/=]##_V+*GJ@߭vCi@΅kIR %u3mW\{п|5zn- E[w ojv#>D`fvuXXljuT5&1I†37?syظsssG=*G˕W^uUW]wdtxZǃw Z a-1t4}};dY 6oTrb8|FGGrSߊ^Az6MW`뷼$j?/T*Ogg'bnn׼55ii)KGGO?}I^2;;Gfغu:pÇ}1@bn߾cU~M_ 'OiFg?MRHsk@6n Leppow?Ǐ;22F_ _wZ#| :zM;Y,WP";v\MVVq-k.ZZJ-tww7T<R?|޽JݽK쁹Yvx'_V#PG[[ە?u͵{^;V?\.s 7ѣG>m{ySU@"7\=;6>ynJwg-!Z\ ~ˎ3MӉܙ}&Pzzϼ{k92tNy}}=LMԓ-MZZ~jizz{>}?o޼}|Kwc& bZk?,,O{hSM_3`j\(ak[Nl8@KŭJɅKfhkm@%Ke{1`txVIMkK mbu h f^b$ ZbskIҬee:5XbqKO#1 Sw6:Fߕ%)K5&)oKD/\P[iMznQ]^ZU&=$WaVDwaۥ_e?]<4 @;wtti3b@ +tI %4K}t,4U%Ex)nZtLU9+2KNM.x ]z Уp45%YUBȬ,!\iqq>{Ǐ3;;$Ή422ʟcbbbMcjrx+j$1M MNL)g6oK\+痢j8O{&&&qwv%mƂϢ!ۏP./MWïpo76 n{m|ᮻA7yԮi*UD=?1|vG o=y6lXow(S?~w -0jA9@w}oۯ7%7::Ok]}>7 P-MY/_8ڰŌ\ٻzj[z{{}kUdiAw4g0*4Å j`D!8폵!?ϋ kT!;;/ڵ/T*bn_UD'F$ ԗ伀gZ]Z kѻ &L>=gfAbrYt&_ϙy`Mj(ib ^eϙ6x"g3f6pUwekfgg np>&a);Oognn)4裏w^v _BmFggR ZJ$'OرG]ik Oz&-QOO_ zzwmZ'o%;G`B۶m￝͛7?'ڣU5xiVC}}}ɟ ||_blrtzkX^XEX!עMZk^/})d߾q\.S,)~z6mĎwiӦU%pV#kQ,P6##۷ogTlbKUH`3tI UM Y9edH`52`L3|ikQz V{.52ZjMH#Y+Y}yA+[pk55g ] ů*ܤek.J,I4*$Fכ3&ghl0CԾܜnm]hwM7m}MN< T(# Y"-WZJlbuֱnpÇz.orZe~ .2.۰C?.'MS*"g?MZQFFXC߾MLBRv _~3JmTg eY,lQRh!MGW:ׇ?7@5@:&]Jk3Ty/kV֤gP8C9&džɬ[q#j-kpݲW~ZW/0r:t͉Z,W{wwկXwڏ}9)ϟر4^6T~rm5ݩ홞fvfAK_v+_3NNu;SȭuH`D)A$ NT\xB .nSG8N<š ,a8f K{ՀDw(NZ@G{S9U4[cZ.|]ɇ5BSJp@j),okEB%kG߸0觷ujTXPC@^ր@ss5~/ 31-Jt>w  <>:dzd)xei"(gC/{nn<%k|e!JD0z\.U90 |w_zN/\ Kxd&y|Br䗊*:YzGgw*Gy!N3qp6V>%u] Jģz{O8P&y%ΗӑYCO|댒<.[c_߃mz3@VvWt/j~i$WKjTTmu*eqa8}ڥUH+Z)𸺩72Q74jLBZ,xt- ԥ$,dilhY?RPYhZ$RE/ \]i0l· @V;q4>Zx784Sn o?H8TJ W_&ڇm 8~ p6ۼDJ)Σ 4{'xgSJ{(^>Xv`G9l:25s u6 qVAv{^ rS:tw25nDQ́V LR赪to0*8np;>e8,60ޡ(lemy պRY[H-Ji,‚Kq$QKyV Viڼ!9/1"Zkp{$.=r~!sRtutuMnRd\=K\`2,3S-`ZKjJ ;l%<@nBFKzU-f-:)Љ/X@wYl[( J#am6as=Vg<L`C/2Jܸ{ZxenN_OS, лU~e;ν}}{GO_X{?L؉fY/*Z EZcQΦP5133U,e0F{;JEg cBeG BɣLPІFNebbP(hmmɣLNE*F4 6G',1H.N0S,$d :;)uad$I2Į0Nu8a߁GPۯezfcth8W!$,-g7xx~ ?959EK 4J9R$8-0,шB1Bki-XhE[%Iڄf=zI>ѡ ߨ- -5((IH J4" bс^$ă\(6DmX(: @5CSet@˳Ǯx6Ɵc;=7tGcnaR-"4KT"1 A,NDpguo'(v$}D6,"b"s)8H D$uN+No.J^{ +aւNTTAe{0IX,:KhhЂua^Nfk` ڄ1ڠ&/"`uxN<ֻNzK[K CG2-keg@i#f^{3Lgߢ6OURp1=yh9bg\DGxL#^#.y6*!I >x's1XТ A.RP. ;3E;%1 bY޵(MB /9BnÈ @ 2DM!8"$@F<#&"t%o^zS: \ٿ}ϧϖ_6(J4" "!o ^(%-8(I8248{YɬV=84tޞ0܁|U@F<{T: %k71 :8W>Fl>z ֢L$)RV;ZuWKZW "}C:1&tλWֻAiAyA^At=ePJS-b\BKR.QP`q~6T⼦S0xɨe)&)P&9RgILB{{'CCjOFf# '?I!I@+v'ZOeD* Ep씈CȜ_Jx C2,J2sDv:.C):ZK4΃A$j jZkQ8/[Nwut'+0:88cv) bEQժщBiZ!UϘrdPNA"V{L<ChySH8Jm-{ |h0%5`e?ztlb^\fQ.XTnB$stಀ ŘQ׈,i1'*LQ1 ޻\)/GGG\|В۰NTBP4%"(faN8nLh JcLorw1#ц.d("NLfhj"ţQ4! 5CZ1N8vQj# p,.|;}%CRHԣhYPA_,qv<*ўh7!*:\բPD|ѣ!lHhElH҉e Ő+EeA8ZpYCAiY1Q<-Jӎ,(2 myPȆ8nNMIaN?SsΝ`fa1NVg5H& BBm!}إ;cw8`8l񱑇|r]A`$ !SB &/:̛Q2I Y!¥N %"ṟc+EL1!,$]($h#^5^o`CG@uRI?*A'PG`fb 禹yiQ˖4[IDATcz!L='82'P/ft`P_R[ :)O=ȉ=.Db,)_jO<<9LwG'j\xyWM-6mVN :b-:jg!ƠU`;FʀxF J b8F2y6$moP>$TH_/%) .WrA!$Y,|JfY:p8XM:8jT+Leq|{zM/bU'u6ԸX$KY BcN.(aPC iCy"HGQFpi|iyp~bRK`qp$@cbV&T7WJ]#h1' I0mAɏ~]]}LqS,(J Ć[9q0U;:alF'?ΑZe{ȣjz@Xƀ}xɅ7=ߋ~רnPYNB=,d\j PY S'82.pu.P&hQ!d",I!x16kՐ#3u1<M ^lmJq(9Z;:_2g]a Z禹ޯ:FRBbaZ 72,(c{Fb&G+=p+FǷ wS*dzf H %!TjhmB-r03 REIHI-zHom-{6nrkssʢbiGq_ȓ\B?sY45ŹpVq>A`%d$K3. R1%ij07$Yf) 1,.2t`ta[[w٣#k*^A`R`ֻl$>uǮ;ōwݵ\I twtM[[J;:)$Jww1bk]NhJ=Hҫ<^ dD(Heeb9 s!j|,m3XBmQQbQ` m*z:V̒ 8 Erje19:4p1lQPdpse٪WM -x=,۷{1ۊ֮vbkKR>o+լJ1)2=5vށm,)ڙ;OVZZ3XTJ ֧qnB"ZղBPKg>LX̖ZqӸ@!7bP@ he6O&3Ihϖc--#TsfrYeԼǁ8ϕ^ Q:zXf^.کQVc.1`2f#kqԻSLESj)<FFEUi8nW*z&TAMéF74 =_(Zd4&^)Ԥ&5IMj$1@텺;IENDB`fotoxx-18.01.1/images/combine.png0000644000175000017500000002542213222767271015256 0ustar micomicoPNG  IHDR@@iqIDATx{wt}7}7 @ MQliFbƖl%~#[/%/'st(^g;ȊDKlHb Hb: T+-vgw^ MmV |Aod㭁_v5vUUk0O}.~O~4n߷~ۇ4L%ogۗ>_oyڏB!}]1*]TdO#cY~*E]R|CSvgz}~s70F 6 OT-0Kc#~|?<5ݢ~۹M0hb%dYH1+]i"k_UC!G“*rܠIK4AdBƋe8 &ic۟X}"8^[`0T}jFI=5季'1:1uV[rŤ}( qg`0>PK BH5045i;v12Bֵ'g.h/ [Y- n͉ F5wb3H\ I3j5Bg { iR(V3^c͸zhQ<)?isv 9(a^el_TдܸkyyP798!2Bɛ!IkGN@:;|(ovyj'.78q7Ȫ23-q3E/ 4 fOT]J壀B9J4C'9/{4^ TlOj gCS5O{o&$[3I՟ʻ0rSdФTg>wۛ'.'5Bi|5$mcsy꣘}T) m $!Rq HFlotsUc}  9@?=/] F`4:AiI[*/5^LS:4F73ҠʹTw>fHok2q*|AUxtrix%3&['S-F`IF6m;ni!炏IVLy8xBf.xPdRh,Uj`f,5/w,DŽF'&l6a{$%'ܝ6w #ڇ+(_\ΑIF= )<5łgGZ/Zi>ĈյvMR7.OwY ֦%9{}pQǜdb m݌{#8Uh|8:RWDNz ~QZa2 WQ2x ڏW |ӎ50&sx׻|&hx W_i2u"*/~{#.lvttdР(!/ûW2a1(Oǯä1>E(Fa Ew{p~MJWZe aA iԐR2k8C0bhL~u()āqN'<^?N閱?ܻCx+LVnފ%S7q_bb n,w|>A1l?_}j%ũkmEX {߀~IA~>a;@ӉCC8 Dbbcq oo>T!/N}4vgBU\-,Tjk <_kxB1*wl$ߞq>QzA& I Ix~&*;qP<ɊUKK'0Lʇ7xY{\ *'[P2 DiR(~xk8գi{ǗPZw.4z@b+ /+wWo8P9ހ?سŹxmԶ$~J$ b GDTCۂ@ɸk×Q%9[dpaZՏ~m KGD5pHܖzܥED9HQY+ qV޾F1Ȩ2٠A)dw)$:ɞ-Fiyt{W3; 'KMPVL\㇓+ܮ~{CtI}K[G`?kPU'[MSMA!VXXMؒ Ta@(<|p܅^,JO79YP{$tyP/n :~aƧ9A%(+b{ݑhO~b_nj`=^.δ Śl-AUT(/3 0UEHUK;qjr'Ʀ#=X6ՅOy~/2 Uؾz5]luyƴDU]F a^`U~nb>,N'2 #0Qľ"9x`=-+!goFC 1Ks|;Q>R!58)XVooF@[Lxa2)į/tbv1@«gZ0:$_GA Mhy|U !R@?s{dڤ,o>Zb߯kv/LJ؀$8Iʌtc M:_tm&:L7F,  T7X6FsT^/3|$Y6jg޷= 49RdGjH ̸I1`eRYSV_N<ڇeY+WNиͮf30飥/PA|5d-b=|bJ=c5J@5~{f*s_04J'owWe xZטL. haB-d~[FgbpuUp?5v@ c31jc5^j eG ߙ^N&߫kz1K@BJھIN;~l07L~{;\'?~ D@WNUW0^=NL1ɑ'Q"9RP*3čvqbbm"畣W=.ME_ lZRq]%9%ֈpқfPQ/z~^###&XQH5S^IDyybo i ^yyn"hP(G/`0{b+} naqk rώH]i t;@o] KCXMPJHt`w{1L i+攢9 Tm0=0_ W{bdzL8x>sSQV]o!i: Cܪ8q$GA Rb EF@5XIѳI )#gB-ikIN ̓ntF@+/AN$/ ZH` 'GBms4-x~zF<"'AzG2*Y!׷bxb OTd#VBPED(I3@_n3`cTyJ@RmոyH%e2)Jן*ֲPs PN0Aֽ e_ f?F {1' ӄiOl#h`tEQ"wNlֿV腎!lB>w7Gwq/$@4aE)=B 'y%G? =Rj"rYeٽ;PW\ISǵT=2p@lsMpF3֕a;z~4~T ى֣8Վ.Ook+o~8j0* vqE\pt&f6&H&] 9"arn$d WD1$3KJڻF QAfw"aS3aT8#ÄwGPtC#oT)ɰgs?^YIN3]UƳ98s Zep$7C~~3|Ig:MzgE2#g8ǘ}I$qU]eaaAub)1ݙt>W{Å>a\ D1 xuIe>J*‹K$wLbLPzS[hN1 tOMZLxOBӁUl Ƿxa)` M2wM;bi},מ:S&V+,cAe RdO$*~~ p ;}jU'ؑiI l)ˆ(JA3sHW^zK˨;-uo-UQ1awO"X{`ΰ2Ϗ~Ғyy=b Zx.ґQ"00jݳ)ܖ@*]n*"BZa!t93>?܇%n>b,'ҭƨ%9qZL,[nYV¸?/}АA @05nҬ\`1lT> 4k9[ڎ[8qR( Vd/5ҭ4lF3nb4v|ח {eߨi?HCۧ@*yCpWop25KM]X qU!]"% 0&xRûR 6aMd;pS&B#_x VzG8]R楫 XɬL>}*9$n](]X frUZݤP3-c*[^_|b+fbNlKH7pz z+$51t<<^\H(i)^fu ~̓ع$st&||epDgJWekx4'`5L Nk5$]ZQxp6]+f%s^]<[@Bc(,T&sp3Pط:OW!/rG`mrN!'g ϛn|zHei0 OO|^'6/$HlKDs(7O'TP6o~$ iEbI^R]*CsP5a c 83PV&\!VL%)gzCywod8#A[UM1 I;YBJK>ŚV6hh5W_&kgL^LS04jx2mQFͨ{yFsAIJ0@:pgVviJV1 MK Qœ,*s5Zs>vS*9Aa߼1 qaeӱ(ǥ>s+s}kZ]dN ;_*^XZW5kc#9m5IϾO*6,["Y,i?- JsӫKHT*ΓTY\ JmS~߇ VI_YD< _sXT%Į&+ p~zG_YTO{?.1.3Ia+f5&rგvjQĦ7}b2>e%)MLӇ|4ԇFz1)βDX 6щ] P:siA+&(7|[2%$t27-AMCxYhRT6"4L~ٰδiZ ⨊PGwIHtQ`J3%$04ibqP#}ZLtIME#g zTXtRaw profile type APP1x[ DkI`9|:&a;V_i$y&nzL7^+kԚxYXH狧ϫ|9r*Ӿ k0q@*{M?>u-þXV0V.~IvY{+7)>ǯEsʑ<H$O֐7V[Io%_/4͵4RiЬOe\_J~)}Ϸ**Y o?xndG~[|Q(S޷쓦h_CS j?sN' {/58ZίDD͹}-3/݉7{c fs?99 uR1joEAt[.ٛO؋-ro%k!ۙ,.R\,fĥ\m~k巋윿Vr_ݞn)D`T@L.ί4$'=J]N۩8\eMifNky~)"BC͞%p`F/j^Md HH1y!ww4ͦy=z7UPRNJ@y&f mб*=*W|[rD;R?w;=z!Fv ݄'lx&Rz}a]QU4 0mR `9Z=bO25S(tѷ&BD:WCf*W׃kGbŜPA>e h5FJ1G?k }/h 0ie]#gK6 )*a#mfO.0ݖOH *c5I<3:r4qCX˩n9o.Fؘ=꽴1@/e:]ZZrW[-3Bo!6G66D/գBUe] -/3;l@PSP:l!g>z[47pW`}ڱ:*3RV0uv-t!8D]BR#041IL.@9n%Sőީ噻뀭[vu-Kp80h *~m[iE.uCZhLN fv5q9jpr)Өk*\ՆM(FEkow`'zi !hfHh95hע&CUAFq -`:0pB X' v\WO:AZ,9ވ…-F$˄#4>PƂ,`##BHO+N-JGOQ}.=-C 3QlBd#lC)άTwrt#&g )ceo!:.+Awk.AO3 mJo 7~7[R-d 7s֧E7k"il\Ddy ~!Z%a}đػ:AC5W]Jkނa% z#aKDŷ@`,Uv"zXhlp  S e;:lՂRrY2p=`K?Q` b0HPtA1rR\E<"eϝ!M[gZ Àq%C'Foh ptAܡH'04h:I T2ڔU nĀNx"_I/ւ 6 rI##$n@ *†Pt܄R,x6$ ɎW+i8 ]܅~$ɄXBv(y=jJH`V*s'K4بPTLO {BIyHV < 4J&´`|**iTXtXML:com.adobe.xmp 1 2 3 0 6 64 64 1 b&$IENDB`fotoxx-18.01.1/images/zonal-colors2.png0000644000175000017500000005626013222767271016352 0ustar micomicoPNG  IHDR#aPO IDATx}m6Y"c"p)V /"F!P&G`@IZD䴩R^M[}5sդ{wv]3;yMOk"|&2| ^uZZXM3Ms N^WxcCx*@G YpкG]ڛx/ha(}lxbt_>3#g_Op>ꋢIFɋшUeaD5~'oMiC6Ms[32;AJg/J_Әx a=b[a>7F@G_g}3,rK`-qbq'?SS(eES%h $+dpfHz9bI1^+?cMi%0}89Ġ Ǩ' R:=ɝ3x * }#W)uUZ6+#XԊZ\י|JHw&pГ~6;4~|Ͼ%DRCL:|ft~rdM0"Z%';m P"M?.Nlrwt:_R*~ J' v@@0d!DO9벇dYx',ՓeX l X#S=XOZs= +sU]-v,xMbl.Az#?Ră3L(^:#N $> 5HjciɱCWi1WH޸wɜ \a=V/ $ /-È@B/Őͱ?P >r-2T'40# 7wk}BQW?vŐ@=a&c7_&ﻜ-7k u=Ȫ$1~ z!4a2H2i$m">f2;@\pVxr3|섪̑aD”PɝإwjQ'q(h"p?(cШa)0toW HudCWr6!9#{Iy%qh#ۇ9 2GF 4i6ٯ2L=ZEp5\[aWٳmZ0FI3Cp,ȢF>(gVJAO[Hg4zf}_@C{5H0+M zWG]< mGb+B? %rЌF8#A02R0gZ\5=kϚ%CES@%eZXGT1AsT5~:Skb+~_:e4u={?7 A~L~ܬu@Ǎk4ևl>b])sDLc2pf玌9il΄QS3W0Ri,n/k%X?VNv5bĵPϑuy6kwȱӏи4P3Tиb!ɁC:uo#h$-XLF XԊh>|nK/MlkD ˑ81YOF4PN0F[{Y#rxfp7Fش<<&}!;b]Hm C_ըzG9}vr$ӍXD 48O(fD_kFb]Z}[t}%d{y!g;4Hdӛ}0SЍ{_0yc|Y9GeW/P4rPgETOÈjih`a15b8Ռ?[b0#LK\$b/UsU|&͉ O0:fJSܬoԶ8Խ0'5a]m3UFe!"kgnV-v~uC|T0eGYilCo]6@Fdnn|TQaX^noUQ2 仫j7P홋D0l0WcrXC"I);Jkm5岁H:0E5,hg̝ͭ!0*^~ڛ"q$tp4R!֮54F.= $ RP7M{f#Ifx切3.7~̵_8]rjPKR LaYvCc4cKO8̕.$u8=ikS-X+5=bmXpd485<{|@Qh6Цf>6y5Ґ%0VX0rR{Ƌ }AٰA3>8P rjnoYQfY[Io5D]N1 Izo,F/-}Gĸ8|F"o;7 ̶~,V)Qf d,C  ⅰEfjpfoFZ0]Ȇfi[SGݡZ6eZqq^AZֆ`6POau'wFd|m?fd29  ;9H֌H;/ uUDo&d$wȢ[lD`ާΠ53c#6t+I!󹗳wގ--^8Agƪװ6t6 M;>n!5A4f^1ުY p7FYTBj9^lyɂ3Zt9|6ϋ<6Ͻ q%]'Ds \U~5٣` &'D;:P J'& M7s`c6U \Mt|vobi)]rn;` plX`/"P;'rBso8IPuʠ֘'tn-+WL_# -LZ`oT9cqd[t|D&4Ɔu8y+G~'~HeT#J`󐞏ɥinOM$N;lco>_6`/xe"͆? kL Qi_p`k1|%j!)53$rx1iF4Rgnno:7"zդ5#;[ PT8+5ފ;rb<\9)?׃dO #F`y4j;/쟙 c:iHV?NaHJ6ㆋ3*fzdj2O8ȑ5nN笾8g|Xӓŵ؏C{Oh?5%k>z0 3HX.O&ݕTT8"׼!ٟ;>kÍph萮 v6U EICU ^Ͻ$<8X5 ٚG묙ebYP%o`ݝl] z R!}{ wn<-ϫxz;qpOmsXytmfLxIpDLcH{L+gV^L9}qb-p)ϞYfDio6qu<(KfIˤW~LSQ:o X!ĩ#&N MIA20>F4_ĵNpb,,<P]Z|(k2S@vC (Dd " )"Hn%w?`3GhjpïZlEQr:%,>K-e FMġ,IcQ[p.sBkeΡUxznL4 n8 o0{ǒ{5E\F0 ^ Ńݛ<# O?N7#:kє-,jr䃙ےC;`;6&N.|kdZ+kOP jh^:{QE\vΕ({e-ȚW$WF=G`ycSÏi8Y ZM.KHMPH^( n"%MwnJ \030N\%9 kWʂd;Ljߌ{`a1Pl>p3/w\rs:Kn7dՑ S` 㧵P>4È.-yj?ʊ~y~\ %EMDqm`@s1KBg#%i~Lٔ\ܔ PdD>{3ϋ|`D:f Z뼓KyYqsP~@}gbN{XH{wˣ(f.0m&%xWq F)܀i"6dbâE R]sĵ`,i+j<.+W89^*oFY`%Ehрۥ6IW+<VE\ 3@sQo|00ajlߌh7TԶb@Ќ<y$A7D΁#^]J,ߌh7$'?%6 `cw;pdq#愝zY62h+\XP^Kl'H%@~n"?p1IX֖PwnYf.0jxk\+*ų<{Fސ4"7n^\MKOoŻǯĺ+k{qs|; ΈEUA؈:RQ` ,tMQs$_|\.M\)7lACwh/ZK͈f,,&,Zڊ~fnܘs^jF@sw΍xX:X Zrm"z@3,@7#NZ7"[X{<afu+[k,x 2aT5&ܯLKMX\L! 4ݸ11s"^[AKWq 1͇KMȭFH\.U \!ܝ䳨@K ϞF A]!v M Lcz6GbU6pn`7ύj;>v2 ;'PO H}S,Ncܻ箹0s ך[} !'Ċpc ,Y X0s\\Y+zZ:01sr1X'` >1J!O%Ҕڅ"rL6 V" 8Ѿ!J^_JtzԿnRLʇ  /@_U ΌGz ڕz$K0 7#p [Gj^Z9yLϭd ›ϥQjɵKP;_ O@0;2 z%hq(oLAè^SIo4%5nhB86?F)8c{ރhG\9p35ׁ}4O7pD#D0Xz44Sp66۟Wwע&=|¤n kD~Lsӓ!omN%:Ӱ˨ E< pqxCF8nj:fߌ6=rëw[xp=X{]Gཊ]!WpFQiRi0fЮN9lς1\JQ3.]ޒkڛduڹ\1~׺bTx׹.:ZjTqk:Kptҝ["g/'rlv.nj\ <,f>fn?0.G5s1%@~9lvƍ㐛"$: ttA?kc0끨a^#n`DMzD-o=Hgp2.CnD@B虧bqK4#s^cg^W'kV̍E%P@!3K]'pO䛑&,LEؽ3O1"3,fhD#j橞ZM}nrNܠB+f;/Cgfsع355:(jz%`p;'@@a!0/0$Zoybq9hyfUA0ƚHk5]K IDAT]Q]@]Ps3o >3OtϿg=]"c_YSAKA쒂jn0<r0#W~ƫ:f=Lymo~L&t5HAejid .%|Jp >c=Lv$Z if# 3̊:KS|y xuJ,&,Y3VNJ)=񽠑!eh?7}ӟ7LϾp)k^wc̚E9̽iuNyH%&Q$sA3}_771/_g~Q[Yk[Za\ TѤ{' 2DэrdqW gƫbU2X5\ՃQ;^77}w~kn{|*Q/}l#9nG軓a7 7 9୕Efnzm$4&܆V$yb2?;^փ0Sy:~j u`)͵f BN**K}+!xn3͖?ѓDr욅90Nl~Je=-|#YXͳ֙A47t @O;w湑湈܇ל|ľ($4&5~}^5=?%!i)z4 F ^DtrӘ~WZnBΕ{~Ǔ[l\n=kN/I /8?雾ۧ\=wO2HbV9r FpDI'!FTXTimѨ6H#H)@h%J_;/aOyfz|ew˦O(a}v T8 b'x,pT/nKX0w< v K3Qb:Zn%rݷ\>#ͺ<\PStcv}&w] /Q66/k/rzAlPptTB nj&2-mfs x~CǓZW[aEϯͯpǐ x],h[!jer*XkxMt;P,7v[Vt쉖и\0$ zss}r`>LUx1'X4iG(-58[$$oQS$fpvGv#.*܋^$fD` /F`SDOΏɁJՎ8|%|u4SDVx4k?jAr,XݹNk,}󁑇ߋC ~Qt8 -[*aDXEڜ[FX_]t͏rԞImhl GVW>fÝݜVlX(F>]/_\4u41FFޱM#ULug5@.D :/&Z(FC၃s[k\a^4d8/m;_4-[HhWnSQ{6*͈"m4tn{qo8=VQG9kϟ4D#loA؉#Vx?U}q ٢m.FHB&a1>yELfMvKBG-MA8/&`Hݍ9X)Fâpo$ЊF;-n&^t-O<ĔE]HZ6[ַn|7_Khaج6X06KkzΏFKd^(b\1_^_m&}`.,`Y0Rb`Gȼ-dOC&EtZIR!{J;:<םp i5Ŋ֐DZn:G`54͏x{5qJ"Ih,rdGeM8'N lLԆy`зlyО?{jhKr/^tIJ{ɰp=S\~RտjDM NywFM]wI܍AGBuLjΦC|v OY_|wȾ."LK@`'zCcÂM(tP4.a6`OHT׈ 9Tqr ;{MUt0SwLcrԯ 4~ڕ93Ma ̛N0^"MzR5k(p7cz alY'a_w`}>a^'0T#N0RmW% f'@#ʤF3?"7.؄GuEf<͇&`ްw Ƨģ:A46i\kw9 M'6]nqMj0o4*wt/#e}RDft1Ȣn3(ҏMH%w,); F ([hyT b/T"z\1010\sGDE#Wz ZE=^!O(&Qzv`(2ށgkvkp x+Yad˪~W:8=LG#dXZs钐i}Xk\'`*0 .]D-ĞttQaQX(=Ywş_w;y7#E]P{~T_r-4fKDĤB¸ש #.O12 TNzw6]g2hЂ jWab_ډj" Pق{(BQFbFךUT8-obXdڼV,xDkBH5Z!.8ϠusBvadu8sZ/h!GͭC Iճ9oF\J6Y".Q^~! \J4Te\Pk ?',Jn"xcDQDr9QjՍAƅ?jϽÌDOϭ1 gfDpO\G(NLT~& p[ExO9_怍óF+5l\`J<4=+47 >J-X if * Z OD F7Ad"oDSRtcdYtD02+\:JRpJ4Sr+\nx#L7{"⼈"r5cXգWM#m~ }3Y$wyAҴ֊wozdل=O 4kS=J?e#1- Rii!e%(O>Hz1۠Dy9ݿ^OKuTtz-S94x/I{W:q6fIzAX+ʃF*-@mm'jWa¨"/{ +'<ɺeHӍiYằVׂ %a~8H{ ^#M7垺%<=9Ըx~(&? .*8ND%H{Q% O³^$x]p u!e‰ADƉpX+@C Я GrkEC4 sHnqaoH҄)uRaY!* U^@.\-wR) `P9l3=dD_ uii"Bf4JVҍx-袀D=F θf7Rh4怛a@ݭԪ Yi iI?qMH>% Hofy W~y%N떏5dTfwdͦƼim3{@Iì +lQS$4*w(:W X煺Y q >bNݔXLZU,Uѯ xfz~1ON/~W魿~onA<adfIL3{@K66(,| hCW P{w~5mLGdOM_}Nz׻zLߘw?P|>l)FUɆ6TM%'% KI%+U< m@A}җI$;g ,"/a >R7}xXAe5ȁ3=QEj{/߼7_=O-(UIN>fMߌlg?G ^b.|j\J<{I5Ea "79@Cϔ4}]/m7>wG?1=w&E$ad޼ lޘ6 /C$KXZ5=C5qp`wNKOM?hE/ cm02VOIk%Qbҡr;Ї.\ss(~z[~b-/ށSN@wFQ\-XIwM$X`(fHja"QzM 1}۫^a>?soKa8J`s)nDё~2aL pĤ-}3#OfO:ak5'#u4SrzϽozA.daj LxdSȣ9{v(puJ<O@90R E~`8y)9J}"Yzˠiꒃ>w{8_*^/]E'_ $h6 ?tb/`7j'Ho*s?t=Gm-?Y? J6M'%&C~hm&4^a90WNc~9O>sCr# Mrs\qD=?JmXh\L}%긗I"{^a:_^, Çkh}VWz0K.7˸=|' &2$7`3-eA$2O${i$m2MនD Hg>uGr.AG%N6{gH+MNNlphWKi^1fY57#*M;Arwܴԯ!EeôM:' "oFPaci v3h%iUF w4sR<zXvV{^#Ti<,f |;Li>0 #^{ץ80$O.i7=j8Կ'B,ls#ojxȇT 1z=] }3Z9N"SfKAI|2$1C?9y(!ߛ7# 8R7 )O$I#ɆIb_A'Kq2.%yP/p0Dj, %Kr>4ZΕa$٨ 'S]&?,Gʤ i4lA8' x HPڴN\"7c{7N왆?Vh?F>Fxvl:NoF6M+mxw Z8p>j#ϗG,hȼUIgl$}lN wɔA\=Wr\^(h~G{@a$YLmo'󤕥oқ{E& xqM(QoxCXFO\>[\5W10xo=O'&,mxrps+[QxvXа#o'& IT2;Hx&o] &wGҺZQlkymO@*fa nnDϠ {49Z. eAas) x@%i6&ᮈ^VhaP?˚E!'84iڢz'IxI6yS6 ᅄ %譪7<_[=q=r@[ܾVN 1ͦ7 ?c8 ?N/C}+XC)hZLQPج~"^(b-K,`%82JW .7#8k O/J޿ISdg8R+4_ 7' ('HOʬ^7݅/zȬ:(_9=qDXt3gMY7??w @r:8>#'v#[&}ub ["O0aDzc`z@e\8H$NŘ{1jws$ vnǸ ^ dvX="l`F\<' LIhlsLP6[l 9 ͯ{'`1ÈƦ8" >~nL,%848s <~LNXF j|"+YR5`9hlAxD8sbQ3 Sa< FX'x*:sR܈?E xbd3‘ga.8sbqs3֊/+:'pH/C||lFusƝcfI~>I}%F;Ϟ@Fj=f {YO;ؖa+{e@Fk#  Nl3 gM_<O@;oF6- !Fݫ\V+z' 'uzHޓ<9YY17>{i|0ΊEwꆹQ5h1&rNRK=O8SoFa@}>Cp%T'".W' $:n`In1?ZJQNC5N1O~-xo'Y/S5|wzO}3l)ams=O@M3xx$|LZ80w% '˭x@oF7_1~oE$rK,8ډIp5F WO oF,Dfqךn9z ?E^ x ,AJ*#[/2.<~?@I_BxoFɞ'O)hz>xN]#fO^N{*5gI_<Oj*QKIHpH'řؿc>驧>iz_1/u?+Ӈ=3'zI qpOI}TtҮﻒg^|S~߳;]?ӷѯNWO(5OJ@06y<'s '/2GDȳ떴dt>>zi_ӷ~/Nez g<[*MQS{%|Hp9Ir:B/>??nG7N8~' ~Iִy%̎}ap#Dɒ0[?|x F¦"@aScs?A3k1ܡYxǦWLKt7d5p [ASK0o7ۧ?>Č0y!"3>$~I\kЄ/;A\F$^U郛-^QC kϽcW?9='n5z;? {󛦯E' H'zI6Ƹx9Y;F,ў 3l"ca#9+p ?t<HI69%VEI5=5~a$ T=<"UKx/]~yol>a$yh wtI8Wy) ;O.XKZ H-L- (%>$k*a\$^2jMBD x@FWp 1 \ܾp5蹏vqj݇dzI%EWRޤx$sh4'Ж&G_&H ބ[LRR=GkG{FO{'`1ad~$sVŸΒ$fԙ>$Ϝ$~'  ^=&Yo‡aORVJL>LVӨ3xh2='Pf$ 6DPg./!O!C.3j mH>L #R9J9y2 On$8ѠQ%\V<Kr1ȇA &F%jsCOO@g#"=uk.mI*y"5tO`NXj~B7(,j[i>![N x' ÏiW+=u/A^ݱ!ƪ5aDsnY*;9Q(Svr/_0 g_Ygw%:vv?zT B=hn[pER 7Gxa^&s~l4o qY$ v\ҜOA=JӻNOK|q"<;WYfjJȁ<O`?h?A$pJWUУoUѣZ]5vNO(gH0ykK5Oi>Gz܅ #=w"lu WQG[N x'pX#i!H)LkyeVi_Bw~H~-hay]AgZ/\|~#il~p >v xP[#J02?E ^w)it+APtZ5A>ŗZaK4[nY}JZs.O'ߊƩU$SWA*s_ixق\<OL #6|^<O%T/>pQ YUïgw$0@f\'%}0l~`^_vϘ?cgފ;R] YC!WhWojβڬOUDž $Eh>[_t`k*8LaD> J0b t(t{Bxx=N &w&?z \L #V֔ }eKc@QV'Εo['a$< Tq 5:LkVrݞ|)[ m`P֛P_0 -^ANՓsn "^[W^k}U{<O`|\bKm-ZWx^V|L=+XuJO ҾdwM=Q<t.Ne,/,ZMSYߺ>KRW~`'6Kk&Ѳ*IY<O@)ڔ7B;?0<^dĊu̓1+zfӡPZ*;?6<Fm=;v]6 \+mu*mFC`.F="^MNțZʣUu018`(C2p+yy/$qlO0Ŷ>k g1 jn@:"$ʝTD}b<w|sZkh02v[HHP'&A<@>-LjN֧8Ш;snYҒkϞX6VFoF&pՑHc>%d1@W玗 g|{Y+3I*W6k<O 乁} u]~ 87ڍ2]ON`^| kچ^a23`T=Xh"m MՂyu-YXO&0?$}BqkT;$#8voBI ,eP ʴs)EGW8KRlH,Zo9%:r#ObW xd1Ӛ87 m⇑9k$^J&0/d8{YXz6L0b\?hK+"<O, یޫ皃_0+&bsH<O@/yS@$c,d-)=xX3x8C݃' x F=l=ف ~8=&lM_(È|=?TóxyC$v2H#I֎gįx|T˞  ;$EQ"N4̩[wEM>A/ױOrI֋y.;> oy'pЇDӵqYl" X "^+a)t5O<{ɆxK0YMװatក' 0H?\8$DH@q "#ɚ0[$5fyQgw xh>$D6Oc/x l.8HD-o+J YPrf.0b-Jaʭ{' $>$M!9)E9 LBp F73$h1r,fzU<OFd6Mt]V4?.oo3{^:IhQ||ۭ+kJ`0  |%' x *xD4|tEXtSoftwaregnome-screenshot>zTXtRaw profile type APP1xeOI < TU)AMAnYW?[:Q|2 D3ĞÅ^_uw4* E>-Јvn^*^xr'*U T$ثGi:C:'#3C/ٰ4$ϯDkD6Ȑ:D OHc5rVoJ:3_-iTXtXML:com.adobe.xmp 443 738 LIENDB`fotoxx-18.01.1/images/blend-image.jpg0000644000175000017500000005646113222767271016011 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 274 296 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>މ|\i~\?G?Xj^X 7"7"GڽloDEloDEcޏ{`6>މ|>މ|Wjl}}zg>"}WmVOj>y$QH#i받 f ;*${d}(+?> x\Ŝ);#5[G[Fľ,Z-_3bDh& S ;1ڟ+v_]+]~މ|>މ|>GY8t{!8)"::-s{zNmW xN RI%V @"$_?_qloDEloDEnhz&m7A[_[M Ld@u|aZVxZh( Q¸#ko#މ|>މ|"͋ \Yj0`7yFCc~v&xK_-5bHWN Q&I"*ێ z?ɠz;]ɞ/OQOWwᧃ(/+\kU5k>Z !-|U@8ϵs?/i~{èHSi['dE0FA&}lrloDEloDEcޏ{Ӱ7"7"Gڽoak>D犖 \i!d%I7) Hڌ:F}d Em$qՏԚ$Z m4, cӲ4S&(@OOYbshwZc>k[* aϓ kX@g/ɿG?ރV֠𾋫5if:km,Ь6drqd9DѴl2*Z4{\7n{fEPFAw}Nuw8_32}l׫x▥j&}LPy#͔ r9$xx*F#y5}{]dhO^n0l b"\c}yE=XFeO }[y]+R*έx 8kO5`KB5srefɫsGO~ú>c?o(t|4NJ2Wfx?<}hIwGO x-O[,Oi>iAe)1E\I'ֿC ?1 ú>c?o( _i>}o;|H&ц }iAe)1N܂| |al}4ϟvvgw8ۏj?◊]rb驍e]bo@ǞPpHy] _IBo5[ *m+; fd #%3s_cú>c?o(t|4NJ2PVO?>M7FwkwP>?>xB^sx|\oĈl">ںXk`/1Fڹ-;}o;c?o( WOj$OU>ʬ彿tnc!A `#A k )1G;O,|[_4Yon2F'&69MJ+S]dk&H kg, R219Xv<3y\1]}Q4bX̑=Fi!@V$ wE;ka]ZkESW 4FxʌTp c [ Eg\> cn04lѺRU*pz\3k i"R ̥$;F[i;r7c"ڊ+^tս;ƍÓGy(zFZj$u2brQE ( (g[xsPԞ}4k's#Gk1e r2 E@tVnAzB-/&]<bܙ 8a$VVGU/EUէ6QL9$rΣr1@Q@Q@Q@|^5,8㺼-ůe-u+]0K]n1G"y?ĝ/^W9hu+(cFiLmvH+]ɅoSi~dүoಿ[٤ifK%rwpXc# ι[Iծ--?בL\,DڰzteCG5 "Q;HڽXZx 2`dfRT=Fk b*Ȃ@+U&7_.o~Қxzŭ;[xF#K]ԫ s%Ss7N+nhc8yUOjvqB&{&RF u9Jܧ5 o[Of^:lK5JQ*N@kҼ#/+^~%\7tڎMSv?b!m:Y؅]OLjwb?5x8On5&-*[m2[P_L:Į 9g_݌rWq`_vwby|3ȿih9Ql/㷗L$o ]3S5=+Cm"XҬAY<ܒKI$Nj|UsjV_>.w6ئ{">oٖ}-|W%]x1y_Xz -^|;|1֍DJԧ-k%ͽ_gc4LFTxԜzV$eU_\1H wտ;DKyXZ^mX|=6ibޒ\#EʐBA 6 4CsֺU9XȚơ*1uVɸW/}mD80in/-191zН}+_>H.fu| NJE)o3BCdA'*\J*֒m;ᶗy]0LfebAc__ICM%) FoNe6p =jz%v|v6:쳥:vѝIdRp@\%EU&vٰE%D1 ?tm~[-woֶ힍unRQ c'hJ@W o*I0Vy<3_B-ȷʬp~7nޗ'志G|x ?O7[4ptLc!f㴷[kk~\_[Wڶ+ xc9eCE%BA' װU->5 X^Kx>cdW;N0 Jx㟄4+čxC[.eBM,1I#QK2ؤ$m$c gjtk"ASm5wG!D.ņ-U_^-~ +'u)Swx.ⱽecyQyf B >_ Ľ;+ wL\\>+X4 Fџ. TUV#_xzx2?Kn. dvvm8?Sī>Yh:\Ak [ߙ->~X o|xZJeMI].16 m;K}?Ǭi &htլn1yo0qկ|=_xvŎ1ңwۙFtÝ SShqA[aljp8ǻpb1 K6 Re >mּ5mBm*@K ;՞i'@5%4ҌbI2wTmn2|Uyu7rh< <FgYdlTm~^x/Rxebiβ16'85xǿ #:ߌ<7e=}u bSO&@S0:U'i_Z[>l&xEtyH<{>Hl/Twa\OKFyM%<@+菊>Z+fe[Xkq!F3WE1Ӿ&h7ZxX<" CVH$S@rpN*hxB6]kVIdl#ߵDT)5^ߥ_{;~G+)ǧ|[|߼E^!ArR&;c2}'L|:ޫ:Ѵck4M:O O5pB96PU]/$ZA'x:5әYQ$DPF/VvUr `&F@|Wjef*pH. Srwmv#_i&Iľ uj:|>6!6vf,L>i{y ;'cFMGF+Mf0-Axx6h6ίw[ Jxml/ծ7 zTCK 9# vo_k7U ]Ϸ T*LeaGj"f+ZqAm~ xSg>ϩXTfy!QnH $r'ee/<;ug♯,RDVPn9Bp+t KnkVA6iC-F%xl)ݠy[cH"[i^26mϊ5φ]{"wiZWZ{l% 2:g>Ki=ߋ>4 OC#y$}߸PZQ ERN~W^i:{HYK:NQXAjĕ揓4?^cᦱ|4Mi? }(, dryA±Bku K lj#ťm$un!Hi0rvKD}AdMԼ~ wĨ5ȁ ?N% '㋛XҵޱQt<áK ]l 'vhnG0ya]^r_w$|6g'LQMAr}22 1YN y5Q~ ~B(PQEW^mMk VCaHn#xFI[5XZ^x_ >mJ֟e_YN6գyY<>@9#Ѿ 1Px|54w5Cn9N~5~k ]FoصC$#rI'knתIJ!k>-ek $L<,2Z oc-#oc6,a[O'7" tojQO=G͒ ' 'zi_ xzo4=;7`ZN+UP ]A%=??8OZ>oZN9-kUwB$i"R>վÏzG h%JZvL֣;.G2+>n+<;6׮~j~ |O\8gs6,v!Q85|_O*~=P<>t?GsfTŽcd* :7|a{Vi{ycStѧ1l"-YN0FH'[[{9\g`t5-"h,vfTfi,lx91KYoMoY"v#%2[lr}E$n;oM~ǖ^ ߵx[Q!2NKYׅg`0! Vo+ Pxj}^:^l6M <W|ԫF# |/>]]iz|6\9$󞦟5]-fω] :/[ulFؐٮo»b ֊)q%`*J ( ( ( ( ( ( ( +>4 ||; W5vrn6 O(vdh[xJ𵿃3SWj-ZkPNdG$RUNHuºz]vRy/v\WrIgop$F+)cXO n89=x>Sga_k-?9EK@E-f8\0*CHmy|&s+egŷ/u"K|9-闏uo.xVGxo($s^1OKX)V-txl4MZ-4gmW*Z3 OQ|@<<qQG\yc,{#[Hڣ%':VK;Xk+3w)v\-23КݗY̭shm$BBBT#ݚgY\~LukzwkkeZ<3,ڴk 3#+ @3~2;gR,-.n L9Pܱ g>cM=on—0񆟦cMB洿i d"C4+9% m,0mf x]dյuk9/f s%3W_֭~_[γ²SigZdV|E,@ڽOK]`ԬQETQEVVze14K+9m2pk>"xX\躜]hKq9xe(B$F*hyӶzr4m?MyoZ>0j ˇ5k*%*[_]q+ Y$Džu}4'| `Y`HJI~q4{cѶzr5'⦫zlhi5q}u<(21*PH}G$񌺽ygIa%VBѬ3#)V i밹v[o筏'Fcח|n?Wޣ>yڤjT7RG*$\Ʃa\xZ\rШ.(faā{pn}=l9?ds k8Gg@Qׁxω hz95>ܻ\Cyk3`<I{5'V[W6(C (8_ -~!j:&hwi2Čf=I³aH*sIs>Cbˋ]SyqRW~6_Z[hY_YjHr&ɚ<#6U|(< )y h5cSƾ#qݥѨ[6``)cKnT~"-FR75HќC8U7\MER[Loc?t߉x_fm;YѤ.`d|GF:*ZirBshИ{qT?W)?diz,z?}vd WȉJ FA>&?'43NCPm>IDqZew4 pRJ??W(gL^_{ s֫$ M\#:g Tմ] N4AI3v'+( ( g?ihww:v.aehY$pU#^\¹gKO]1)ij-@S%=P M_QռMs덣hcmc O9YĄ PW$σV%J~2'3khFi'\`2qqg Λt¦lVG^|Եv׏ 0Z[h[Ce- 2:3|8_:N]]U$qwڡTuv$9*k(':o! Λt` |U{;&:tUWT[!_8;Mgx{iKo㈴D/C*& ?\nFyiר¦𩯿xKy_ncԮGl`Յ۬<3F^[T3H3npgے?5ߝ74\J@BTv(P9?!X(C W㿃_뫻w VWQgmq(SR܄1F0Wׅk|iyxAA7ĚΣqq0m&XRI7kShr6-|I=:{7㼿IQI7rw)fq-rcˡR3ejoeޡl]7VB3,$j**Myïx5 ]ί5K0Ԣ-bTD1Da)FÀL^=Ѝzn:k׷W]ҬTn21›_W~~ͦ%%_o{[ F}v`Կ h/ƶb-ZGM itc6&eyQw0 *9V<*x";+]IKyٯaYWZ#õ!P˗%^w_z< u{I[Bg7m u.7 ˫+wko^sXo\[71$/%tkV;2P2w+ h 2 :rܽ/%ԖD \5fPX>uW*x Ӭ|:vڞYjZ|wG]b,+1^e>?񖿧xVӼlo$-[$ZЅ"FnJV!dw-Ik=KʶZFH, 6v7 [Ѻ?&7"t𭦋cEn~FVUo"YT/QMc%Kx0H6OʼnRJ\- OҥEkY-GylZl8mxZz F:̚ňIdltK.įK1&T;pĀ $FYjwNqEVP2Uyk%tZ]#:~"ԧ.n$U>n^i-> qFs ?~Luk|UB׋/l-'41n66s$ G# 親Ap]Ə>?G? j]X[5+$2“Q+nis+y~-Au~#x_׈5 b[xRUbdbk~E$cѾ DvIgH'RVDV<Hמj {k O м=sRT8^9 15S |;W7wylIb^WPz O{v-\7e+7W)cIwu>,.wnSTQE@Q@s:ιYA}aa}X ޥ4ra,lMƺ*6h+c^O> ???m[w2Py*+ sj-G v'-x'㶧xº^k~8fejegq;4 L'< ~@!|4\ye!ap{"7/Z4DyΒ̻tr~1#|#2tx=;J'4{_X4,L#MO<(_/^' G?mqu|Y<^8F"GJkO5ѾAk)s}źV_GkGsXN@ |Еݽ?oq6?}:QPܼ˜Z8#Ksj_k]gTW DP2y=I<@Շ>)kEQE6I$.쨃1*{n"Y B6\`4=qqFuhz*).hп nzkROh7>_W:۠@K6vll'SڊniV!*<@%8#7\  YɊGIԄ dcDHcIgG^)yQQCu |R\?*ul -;[bHY)*pʎԿz ߛ"[K"H$oyhV0A>k○'E^utFn*BZ8UݙT<&տdi2K~m_<{#$k=F8m.!PdIqH ]@{O`8=[IͿTĽ״[6sK 2܄kWD=W/GzE_eW.%V18"QEHQ@pcHR%Ws\=kCiJر1G ,cd{ux o;& [jZ7n)D,O;_r|$O xFmItR[ӳm瑊Z_9j iƨk5GEեC3 fXALN2)sSVM7ƶ^{ nLvlo)$q!\{ak5C֟j[gKvҮ:UwIEbCdYb(:>ϡzNs_\e͕W_1ȑ 0}OC֟jVZZ.ӿN)?C3ᧅ_P]D{~&X<}ImwۜtH6V'+]K-W?nc¯IKVy@_6#R2p*7袊+?i- ZD/5"ͯ$ P$_.<+Y$qu;˝rR:B$i<G+|jy<Kuwo9n5Rƪ|Mn[a,٘K*41U𦗭k:Fylf]䳔J"gBveI0=k^K:ݿB݌:t;o+;K4tgI2إ.bq;V@ Z{}Oxڍ7:B[҈"lw\}U-: _O{rhi8u!O.Vt WQSDE> ;dV$@yπ.>/3j>כ/-?V;5IHDjU.ahXǦVVh`$$I$I$ h>,o2^&zՇk~%EȐ4̗a6Mib >X8!{ Q)x>ky{q`֒]jg]w\;} ̧־56GP)A\g> x;G3Monf-fŽ찡l8ܯּ~~qjzha᷸ˠ[0bq4I$?4*x6>m"L.Ҙ Psph:0hABbVv si;tY&I#%G#88S tj_]OfWF?[F'.D_V_Dby$hwYkygCMKokMKokoe%+ +_sS&E5G @h7?Wľִ!kU26sҸTQEHQ@Q@Q@Q@Q@Q@Q@VŮzNu=6W+L,嵑 y29Xtv7?+5oJkF~.d%akmyLɳ{[!b|n5O[o{-uK_Itiv\7;H9S[] [+[˙֎xH\ x$a{_y,ČtѾch0g~-Ս:s[ʉy?#FB9Rp@(}/}EyVIxOIMRsyivַ׺Akq8e[2F@5}K9yZZZZܼJ*!ؒKdp8~(Wit-O0iKa[͍s^O rBdd`18'ßug6o^ŧoiS++{ =cŵσ~"eu5ީb[I*+4p&[wXsZw<%Z6m ڄ#"e VN}k)V_yW`t(M"{I4Aq-N|#P ssI&uZhzuRsW'kH~ȡTkF ^E]q? 2Gڴ&6H.nZ9 nvJ\sOĚ/|Ei/uH#ImډH) CF $#o>}v+šUtcY-0[c X*WnOol5KMB^{GhuE$ "򠌸%/p$=EIl|{,x+|7kuxR×w -+Iq(g-e[8g_Sp8=֑mI}B@9?`P^s4R?c<V>Kޕ>"m}:AK5fx$kn 1]<S)屛E7'* #]v!J)[v>mfZxúhjW:*̒nk( (ՙf>|,ѵ  xT^LZt1*; Y(mG&KEd ]o٫[MDҖ÷z\VWVzHJYgYЬ@Kw0xmbY1qZ[_6 JI":2&F S[l{{HbVEV0X(ER((( 4UҨSL{HVķ7ZP%ɿ?-MVo=o?2o*KyUEgb[[ zdUhQ@ؖ&ķ7ZP%ɿ?-MVo=o?2o*KyUEgb[[ zdUhQ@ؖ&ķ7ZP%ɿ?-MVo=o?2o*KyUS<|˛h\ yG2G\|Yމsx%wƍ2sdgw?ؖ&ķ7\xu] @ٲ#)cbA^ҿ?-MQo=o?2o*%*ͅhv6ڮqkHgp~dS>_ؖ&ķ7XVom5Xom-$i$FǹST?W<ň,R;ZiO;͟ʊcrCD%}Gjij;o6sgb#'O+x^UkLv@VV)m7d/\qnVp̞5veR}|˚þ8CZ58缇X-+!,6"ŧJo5 "9fmYB'q"sXRx]ܚ%p.aiWQWnkzil4[=g_/2, aHHH<5A/G}kVuOzK\^[E]  _$q^'<4-ރ,I Mr$dFzpǃahbyr6w:1aOEo?ω7:O3Wīoc.gusd%nn%tP9*JWzIi!4/֧^ $2ne0?*edeZh|uw4H^v¦n9{1|iആRB\KIF㣨թ$KՎF;Q7_[QM]r^Z5IVE9V+j ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-18.01.1/images/bookmarks2.jpg0000644000175000017500000021754713222767271015723 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 430 521 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?g_ |}xkZU[ۢ]$J%(,N^'Wxt{<Ɩ5|Ty+^}ގ`'WOY_j^s_1?bu*Wj,jVWڽWcWOG_ʲGڽ bu*G0XQ'W򬯵{z9Ư_ʏ1?e}ޏ{5|Ty+^}ގ`'WOY_j^s_1?bu*kھOS6_]x[ͶWOf` %+˗cy}=9{] zR =W>&_!nttoCbѳ.lVe5K_j \xRK3JUxSߎs[16mȟmyW|q}vwZY)kh&T1C׌73ZgO@}F;~^=̨F;ήMMugؾbu*G(љ*'_3xsUeLj˻kGWL$Qwl$@9Wiri)nl͵ ;d!=F9I](ݯ붿 5<.2FC)Rٳ⦱YcigsH(MԤ3^@psT?hX2MM,Q#rRɱ(z_ʐJhZwB4G%2wsZŏxKgKc-ŹWYW,R->-o[I G G0XQ'W򬯵{z9Ư_ʏ1?e}ޏ{5|Ty+^}ގ`'WOY_j^s_1?bu*Wj,jqHTdf}ު]܉.s\Б?cWV27-\V!0t=M&g'TcސҦe")b`G㱼ڤ%b61jK{.w:e"#B 53Y;mB`>,B-uV\X$ڑɒgs𬯇u+kC'ٗ;+z|ƻ/ϻҞUy>%um@cI#ӣXJ2`rˆ('/ OE}W3>ӞWG P|uhv?MO$l>w4;_&I4J_l>}Gak?9$)}W{G#y5hsDG]v?M=Al>w4;_&I4J_l>}Gak?9$)}W{G#y5hsDG]v?M=Al>w4;_&I4J_l>}Gak?9$)}םE0kgWD=@}ʺ\v7o^z#y5hv?MNC6/sE8YiUD?f}ӵ 4t%}`wήr=?{G#y5h䕭Z:gQ֓xY9yh'ZkVcÜ氼+ Hyb0-yg̀}z#y5hv?M>I]X<1 {Khr zbO*=1w{sh0w+do;Dz#y5hv?M.I28籚IY vco;bo~[5I,ӠisN_}??{CwHMW[ { dl6X̮ f&4h--K!Db=@Ͻu=4$8 3WtYltm.Ek"\͹Ig_3𯀯дgw:yo3,Ϸ9 ;_&Gak ܥG]v?M= K֏Z??{G$%/Z>}j#y5hv?Mh}ha=4rH9Ra֮;_&Gak K֏Z??{G$%/Z>}j#y5hv?Mh}jBk)ʷ?Gak?9$9aUT,n$m oAZ=4$8OVZϟuY˴ xh #֎åCP1 I8$5;_&GaksD m,[[8eʹ ^4r3Fw$Q(oR{ײ;_&Gakך<}aǷ7Gky۵yusEj?-8_.I*MEn #]t>&^&(m ʬ],G;W'kǭ5]GO syк]zH*Yy{pk˵ZǶ#:姊bhe7D *FR>M;'5ߧG.Zx[Vӥm`7.Yռ/-̢E\d3w&[^VtoX8&p;gBMAmBi${M0<1bEz_vNF;]?*N[#)M+^m6eg;A+J ^LaA_<~5ᶺSh/4t]PkyxY>`qzoEoO-kVAvԀ6hPjq{ɯO;o>MoNɯY>V$i>7lik%_@AUvwqG^x<'\t k7cS<{/ F[k2okׇd_ivGpixż|9r)/_vҺol5g,-l0sPRcrQhM P G#q_77/^xOSjvMǧwObIp H= aqxAW^X5,o,^BP!jmko/x7:)RhHdU q~&I]RV{}&8ѾvA88PI8T~ ։ fku d݄1\|BsY3^1}F .{CtJPplmQ6tύzFGca+p4iG aV#q]?]ϩj[)1[BXċ$\G#}+;X}+]ћZTϭ,$Yv22'D9~g}Cg|c|ƍe1Xddv˫Xzr^v *y_ 5/[SX ΥB@˜?C^u+Aֵk7j/-IOvO/27*<7Iis;O>3]\Em3Q.AnɨG1EPsW|g7ּyz W3}L+D|zS 8qhCmKW־"gP Z "Ef=-w#Z|9&e[`c"v<IJV[dk!YTR3_,> E查uX"mݮm]Ydf< bG }n-Z.l$1[n+\+3Ş6|ውPQtl`u%{Szbx]d{Ȅa<؎Z#w7NmTZ@$>l ^@c'4䒾;onjqx{;JbmDvد0\8UaqVsr߃ǵ|qYxXZ)9Z%UBqYkh7Ou=oo/ Qo$/FH9*^Oþ~>$U]dM|'3YiHAu)VfP!ݒt^!௃|8!ncԄqEڳd}vsH|kF|Ik\i|c[co0܇y{%tgX$fmL`R^bm19|ϧ|#WetI|L&Ӿrc2H;kpwO3Rе|ocuGssm|tI2*oyGyj{燾 x3ewiy 7nƕ*ǜOЎSt_ZFxNHno&5 68 Bk>x[X[w>]YBPI[T˩Z;" @eua#޲U; hj;-&V*4ҬJ$ $z:i7wZ5=x^*MN&p,$d}+~5x6OS\kQil,2KW0ORI=4~k[+w=95:K7[VCL5p)etlKh(8|!=׋,|?4heU3VUv#m'u6֩R}y%M4֛1j2B`c+3G~}u]="+OGYm#ӚA$ʶf=^Z7"y{ O -b^e(Š((((^ ŖWմ o+JvFb Owz|oڇ]^x^`֛m nCN5-(]tZҎw_$2Ȯ+UwLmwNn0Mt4ڄ4=axf/_~9D4up;2ᲀJl |Q>#wCq[`"L;sZ[-oo_V=_u&2C5;2g] J ,&6_ź" YD7k %mM+K)挈-%tw0NW,3W)o]0#iRj^yLbid'q OˡWկ?>j?|'[X_xIw=h~a>67E.e{Rލ-.f$fLQx| |okw](~%w:ËcF[. ^yyY@)yH# 8\x_0ma-54jobK-8 rS5"-C[/WLO4]Hj/}JY-9NO#Wh G>!Լ9kw%)>ƿeH)+KTuMJwZf&4Qg(]2PpXqTKdחRR_cߍ4kiZ׽F1՘pp9 rk6Il׈"Jy@]2>a^'o{Y⵾/xǔ[J}{VVxMXGcKw34V& t9۷3K_0]/jSǂ(ź+_4|܎=ꏇ/{X֯{BL`)θُP;X04ck`)Vܣ'鎕},1Mo\K3V]=;.nbYʻh!xz&ե OQ^AhΥo^f 65ROuO˧Xu_ϪA{CWUz >x]I!}|5s;MkNZ7 "m~n@|:ODcЭlմmI+@| )U!Kq8KTum7Oţ]f8]?#Mwݼn}{<-'y[dwy>pO_?xᏋkԴ ꊺhWIt,%art=I%OCoWj͊:J$YRld}z_K; =imE=m b^B1V…?tX4s4fფ}7AqEhN^$¶NCv`.x/S2O,mn" !i :D@C>1zeMQm>!_JKivy9vNrAqK V{-.RMwK`6Iz+^,}WNi!iE+1R=klS[Y_x{"Rv3.0[ٟ7Ucnݹ9U:B\'7Kx׺%M {\uҹ_t_Zeю}MtR;_,m Kl88= yxH^NVq~ڴ?dpB/K0p ^Oqv=Bf4u8|=黢$?w{/dgjšM#nf~zh|7 ;6ci"G6>fˏ|UEuhzlfUoBTkľ$u:h6a8񿑞qV k3Z/QҚ SUyCP7`rIq,?2NVWvAƱt59K4̌U\ v YD𶡨^XKV{x#gWPrq|/m=FuOLٙɁ]B鰯q+׎.o9=kp-v #ld O$gv'i _VuY402u?7hj<ޓYmkGWY#΁nI"nOz)⼱ѵ-Iuk;(6[۔}|)RAv{c]n7vGZt i6]e0OA7ď %cG"7lts]vJz6v%ЀBH6P̩q$ Ac}k@+[CS󮧂l ?-S`~QܔCn?|-i7^"m'5K7U'O/ŷc{ ~:q'xW^[-A%#|teܑڬxڗ?d]?AtS=hOݖ=3K=gno>-R$Ki!襳xRI--//dkέY9`{^%5׼s+=n_..t@pH0F+A ;'қ⟄ύZ>llx{xR|YA{+%L˨ͳ@Xc&A?k >$\y/  9#P6oIO:;-w<̥S gS߮Ö?&[sK)u8Kɧ4KE,Wvqq 6_~CZJi;潌B:|j#.{麝΁} -]C^Auv_<*yT4+_/ >ҭ-nc %-׃S'O @SO|u;76 A|eSF?? 4Tx>_!N6gaөtr -75;tGϑ%4sո[E@c1Xi:xd59Ql/ٵ;t0O|u;76 @S-suwtf<5uxmZd麄a U%y Z)P}>^Z_Nl3@6ڍ+ 2RA8F;Jl یҤ>,?|'kZ.oI x1)M-?Q'vV]Cg<<5? i^eiHG7-) <^@Ӧk+-ўݜ9\=/fK]~%-՜ X(qXm&d]U Vߙ 򱝾%_3WOMA_ &%b8Y c>:HH~ /Q[^{/tMZ%ulźV$zyQ`]Rm-(o$Q"#xU> ռ_?r]WebK.m,g#W(ƒݷjG^񮽪ؾ1Kzlj?r#Z!AR|/=[$|"W 6 'zWuw\t{;WKaԡI AlǦY¬@0Xߞ*~ל^~|WY~ [^-KC{JWdAKs$HrvFƕ\rmd֠+d0XrsQ xcq (;Z-aMDs5 3Q~1)+4~5OGVo=>N֮PIJ Hܧ%J SěխX_i:Inr1љg_ZOW14sFQ0?-(U5PXJ>q7.߲_tSxG&N0qn+2Cr-$־$F:*P:VUЅ@#sM?ˡk:3K}Fdę1$ A p9*Z%$Bm}{l(C ( ( (ȭgf͓H1wE#@J}jSenēDA9`V| f_Sυ$v9Yikװ^nZie_0Gi<Dn&^NXÏ??/iZf\iVl=oY$X#imVsrm㙊 ?|Q}[5K%win'ʾ8Ink{+6ٵklxڇ~:jM׋>O{ktH "<|$ך>,oå͠Ipoo$3Gyf;5Z"R(aNI$qcVZKdeӣ_012J{gds4qK!fD d sߏZ%^ַ[Zt|MƗ O53S2]Zb̞e±xضH`9j_ ӣY3e0xI=~_{c(9U.w+Mڮmsi`-l#Y5aЎ5u7XNV#+I].[vdCQVv~GͶ+G/sKx@TqMKne?>.FKOkſ|PQ+4֮>N$AFJ;(}Ꮓv(}S1 A9sb(c-BkM`O7 ^9V_z;OgͿ~0YkᇎkZ]Xo$ݵđ.Ŷr&p^ Ӣk7T5kPKyٜr¶F `W4]fM.ΩeC| Av'{P:u\S($0=qD_AGݷoϏà|՟[a^&h\[̂HK7eoqL=Z(5]xsym}~ƫ]#,ʛcۊKSim&| g'Vir[ k@F0U~voF_Q%k{>@[7>.hoiΗix'Oԥ5Wp fH[0ulpkٵ46U .rJaS]^%G#ECڰxk\ٴ5E9ww&rX@aBѯ^>X~_sg>)}+4{ ^.gKPxe"&Y0xo]oQ֑v[7e}F 6!J;sZkCYir'5ERÅb1MYx^4[ӮK=xaX8b[0R$ֲ-?ɫ-uF\f=',׮u[˟hտX̖)45X-OAƟcq)9y^X݉[} ks{kz-4Ry IYOf> ?^]haop$ƢEMu\`ճmT+dI'Qh?tG$~ǴkhOtG$~Ǵk s((+? =oQ%bm`ig{v daYNG.ou46]JYIs,jXە>g!>f&^j tˈ <ƃ $%xSLGuo}y_3@<ĩe%ij3ϵ)8_0H:ۜsҍO.}^mN{-cvvSO<^|Ċ]v`q)M%oo.Wm_}Zo_͚04i67i"DU1`2?vk ˨O*M]-U"kQf oσtH'uƤݿҬH2JPqwʳ’.v8>գo_W_3;mZ' 'Nm[Y =]R''G\ZGu]G_څαz)i YdJP#-¿xbMo%ѹ]٣T?gI|( \qMA(R"KJNֲn|ygƟv/R,tp2"Gp'+@w&bK y?$Q= 䴯ռGa2{z}жPHtuD@da+W@|o-@ΏQYRY($bQʧlW=حHgF]A8\[4zmzŽoʂw`gHjd+Q_ܿwWO=נ74t*9dbCl=a~Uom ϔ/ϗb>}F̌##Ķڟl-n [b 3`r=$%!㿓UKԺk9 ҧ)_ޭv}ȇn9[񟀾|2 ɿ|wRBy+c=+S7|#]g=W1HLve0]Hde"_Դ Xj>-L[fP 䌱 梓VW r>*((oXكq''riw;_.)E'm5C&515;1ӈP?f4joڦegWzei],3yOwv7U20s9>"87)ysJ wM$P1uiViZ1Z-\Ku]ɫ[|SS&~fGvxN9|+'Oφ;93<5YfAnzcOo5M;PQI leC >'Ŏº}VeԱg%уryO~ u:űp=4SH+<]ocAo07I9W +?If; `+t%o%Y+y7^W|Hn&ĚO6RMRt?"qe5mŵڅ3IZ_A9o²u}s SkZD{:d($<ioWv__$6=ſAGL{#db0"U?1$?C+tͯմYt ;2 =s_b|%x|(7Klb+I)< a^vKXϸ&&+,UR%IqNot'F=3Zoenui<m5&Jk)lwKB.kS Au{76VKtʛ.#I#V1pKp+ؼ3Czv}iIok [\6v΂E8 OǾ ; ɭ}5]:[S+", i6S(_WП8FI_R)iI_-~П8FI_R)iISEPPW)ψW~jZGW2 i%P.Q0KmZ:?'|%qnCa[nn7fw52ب|.OZ]乵֣Y5Y.. (P[HdU  -oOO%k5ojVlKu {$|p2kihHt}~mZMU?FV1- S !p͛H!/?i '4 ONҵrYOio1"GU<6]ݭ~Kny ^u=hz燼M7SLSa%Ҩ? ol|}x5[opk1Bs0Aſ'ix]DBaC{1 zsz_JX&[Ra "PvFy5ݚdWK6m>p]VhTmqbfe[+e[˅NMq^w<+ixL٣ťIݕQ=vGu)-<70XqxVQJx]3.a֬%k,\2΁ļQ}*!-}4TOKU,Tub_~Q񖄗+OluȄY2]0wg9__~Եĺ{i?DqnHx̲r6YXTjwܮ5zo} w4BSz&r#t:o)3Ak:vk8F$vڹ815w,~Ɲ/̺L>W޳yL1ڢ6$|\WѺ'Ŀ xmI6ɘBm/ceۃ˻23\?i1,}a{c]FLe  _Gz駡qvtO~kV߉@WN՚0 Qw}sG~߿ _?=;W_OH"T$6<_е6R>a}k+k719l4Qp:xoº_ۣI D{g+KMw$4eGzk訮VkHz W6|SM^&fӆcp i_Ջz+Es^jZ'^NMe{oI [DoqO4=OPRHĮV$yQQ\߈%Kk&tf"k{{^Hw[qݱN:U;ƞm`G70PG(,#z4U-#ZAa{oYHHKYppAv ( ( (<"E.Oca푶1 W#X0ڞk lt9xG xMֵ0Xۼ2`nlç7_K%ҴjX/e6Vqbc%+rʹ~OvOWvծkZVsWͤM6xEI7Kbt|C7\Zޱo% _GfۚAYH|nٯt?Upl쮯ck N[>՞<#@Man6L Oc9?9~ߙwzv f#usk۝:<-|!`|Mc^0<1.csmhV6HwI$idj ׸A·^(pxKPy4#kd;J)Lυ5 zլV8!]lded=*O\۔ymπ5_|uk{ƚ厱wi/v껇MUh&M[zW govgBLhFcAOQΓ&m vgoXic9S_n?60/4Ȗ[8|CI52Qqv_.e}yoj!>7u/ X'<=s\[\ QJgUb H#{^x [Ʒ}V_C?ܾ_;YpR`@Git 5k;H.-c0מpj 7iv vKo3s򤅶jҧ)_߫V miu)&ռ7Z~"CbV·ZU_9f?I F@ 90Mc⺶g4,8c#IR$񦨞 |Q!]k+ \uZJm#:> {K[`30qҪ`/G[xk ɢ} \ϡQXGϵwN8ɧ,'閚\k`}畉M}!xc%_xY Zh|zMޱqWBq!r烔n1I{Owoi˰٭/>u\x+YčuZX~ŠH\m8kѾ$#^0x\E`,Oc|,lE)fğ'xR\[] +}b!v֍%bTY  no;QK]$ #uK)o#_Q%]o_.|< j:ixCPȼzK-Lob28:43|A5e|m+v#QAx@j?uM3M2 sZo,Qy%`Y`HQ 'K}=_Ϡ=WO^NJk=kMO]-)%;YdeFhV-n%xjSjHHLw*NH#z$ %Tݴ>F?qS׭Z?qS׭򒧰-(WŚ׉5S?7$좞1yJh,v+ Sþt]_j2]KGpd ~bbå7k_~^BWOw{fEC٭V[\2pf@$.Avq_Cpx6WY[Zuȧ.̗qIn̐0`Tc<V^Ѭ5+utd]0v85;_5]II|!Z.VKwyL^oi! p3<9ww1$Pćs6;PH>mZ%|Kn k8w<;u4mFX! (E* `Fqo`Zi ~#z+?6yGį޿CZO~(V-U[ahnDp3ljB&g3qqIap.NFpky9pI:B22)?hK+ +YmMM˗k8PfxvQKuoQe^[{4z~ WƻE\%H-D]4>vw]f!X IdzӞé|Dקwg_ Gwqd5׾&HXf n YH9?ύV?q[Z67ԒKc8 (sTr]2,g{?Y~+.5|$r+s{r৔Dk)r #~~"񇇡E)#>PW{7}X^9mĀ>R[??ЈK}/# W-G-u~;{ٴSi3R!+Y?~6J݇Y2Ꮧ*I:=Z]Ovp2^ Yb #WYе[Bo#.}ҥܕFCnьc-2hkV/7X<Ac#5\.>E{o&XZx .!G+,aA8e]ptO"ѵ t=:k{幖UcPK6c 91:3ʶs-+ BrV:&һZ+((*ow x tgnȬ +7@dc`w} s6[PII[9\ŁrӖ(cʅ{ױV~jX[ĭʌzW~:O mu{Q~QY&¤cp+ua|FyhWY'Cʍayd$8w[?^ ϸp8s֢7Q}4ܻC_> ޚn<]}na' C *c95ᯋ>iqc5$Ϩ^H~d\Fn=k㎁xT/i~c}i'K3%4+)泫ivqo]#XܬNҬ L\]ݷ8&}?<0վ|6 úŦE! bd8-˒%|AZ|5U\Y/syw $ N3ȯL|/>EޛR[j2 5ɸϘ>#F bPV5gCEv]xSZԖ ,;q ytrY /OWJ+YB crC88 ?4_ 8Ɛ̺-{cj4Cܳ2j>-?>ыO[IeS~K8+ʗh$m$Ĭ[:x~kTssq4wjVsȤo:|/^𾩩iz _]_4b$o4=G~? -O;.kX_[˽_IhȬw7_S^ZM>k {x_XhLA Ƹo-"mNNӮI$#FڙY9n0 ]mK,|g 6>5W_7*E ̷k 8w;r|L(ٍ IA{5ӦhHeBB@ی+ሼo]RKKa8VYDZ.g 5⧅mg,-myB](j76022FiݦKoIYUx_//i ~5{=H\۬z#DYe V^6_r)+kTMs Vs׀oZ~3=v:V%ŞxV/eoRvn y(8&߳uwGxsPVR{kdJB;U9=ZhԴ_A۽WLZ)w 1U$|Vj/Y}hWz/t[Iզ$[[|`2TnPI#dy,s顢mJ{ϧ9ρ_|WO>4CaQ]24 t_WGZ^L_{ QmLD*էO!𞡠MkG4T`FnSV'*'(gn'Ii"+*3r85+m}uc(+Z$7 #`d/Ÿm #`d/Ÿm=nwQEr~+5$׈$/N!XS3o,pzYY\x·SnTT Mb:+Ͷрhډ20-s<*^oGƱxZk-[LX,Z;[QK2POlqYN:Q\j_ĿH ºn o<.+F6h~o6y.bN~ ]$snP9|*׈ _9!<9g&a@M#9{:T?;S7)짼BCq$Ab;i/~_&#Ş+|WhZK9VQ M8|W'm(xE_cL-֮l$Ӣ[Kpnaetb<,5>ӦMr뗅,̳hd,/ !ʄ', <|m=:nlr YZ_q7{75z#ςZ_;] N//R[_ '27V>x8mV&5}HHCtwoɯr;wKxXYAxnq|zOT ӛº,S&dM A4S4NX!Cgl(ii >pO>.[Evnn/uM7Re:f(T)%l]Wot_x\w:ܫasmZ]lYǞ_pGݵq ?++[iwi#7 A1c:(W3+՟ןq6o-Hm? ?^{ffpJsU[kr;g5OZawo'{ 4iM,mLq0D Rx9ZѶ7[ܛ+[-/xQI.:n̾"KkkfG[%"&q=8ԔQR"/~`3oſ(QEy-QέO Ci+hWYb $?~ |+Ikq0$ c9E-< -W^?;/v pܜƼsV|].:4W>]rK{e9vjϭE|d~]I?ͯ?w]݇t2P) SoFj-oiu ";amBu' zhgTyoa[_41O%qd ɺ e SX[nŜ#OcnHK$=6]+qgK;Y֮{Mj_^qZGR, P .0IZ "^!7|}yy-fhtQnP&Z+Kxv7u-+=cStz溒Q{F[)7Bkvy<'Sō>.ޟMa.i6~;7 V`Qv`Q_Go[/[ܻ`|׮ [\'αmdQ9i#Dk_!ûbxt-ZFN;/%,TN]Úit[[E2%6W_ßqwQ,,mYв!a+# L\[[krZacoZO Abc~W'sc [~3g_,=>:m΅6;Ym,!+ɥi_ 7bK[ۘфsJEKsI8i>wyk{GM9l<xUd0sZի?o OMZ7V Deм/nuN.omnIOq}'?jm]'÷W3\KZBL@t+Hkk΁IVV C,N9zOxfK+lLg[<6)ʌpM__ !ik~[{xψgju v %u >kpZ?>)sYkvU7F2Bgʌ;s9]a}?ŷ=anr\gռ䄶b$jOmº3h᛫[MH-o/l;[K2&y;A=@8N]-eno_>cFb4vṵ}\U7@Kd9z_XhZX./t%onA3mrg&+oX5ΣwbAt;jGxCP[ہiU'Th ŠΈYr@ysU{jr񖞽z=?]?353 |Y7۞XCQ:TN9d{9+jU xS"[-llX^xK-3T#1gvw'+ּ_ڟC->IԵ}!-`7.;.މku//_aGO<1VZޯ Jв*r[c zm> {Dhh֢ۇ9VH US 02xl(!bI-Κ}A V3FJ~5xΏKM.mWW#h4"NaM*1e'fmI1+4gJ=9lttYY^OvUr]m\w桭_ּKZj.. N?`D|ODOZ߮`ɚ9&8ܷ}Vߎ> IxKCQ5(Fڢr@<(΢kz/MZO>-[Fx¶3ijYi7jafA2Ɂ灀8d ilA*OЯtU,ZY3,?ggG {-ŏo~xÚus ݍMp/M@A8$\_kԣYzItlBɶ<-<: e[{ss]ou/-_K&?#[ו57e9__)׌oto 䱎ºtw1DGEMLKuՎq; uaK SoUWxrSKf!yr`snD+3%wߊ_jrVG_y>l,<# :u&՜Hm-/F,mhc-Ԫ(POh-~/cǬ]ǕXm-] {n ׶xCQxš> f(H69*cRm}}n[Xd⼿D+W|[x^"_ Z躟-D [WIq,*]5&/kzoş xrg5N}ljMȑ;~buO~$WL>+{y$|ĭ)U<5o Ǘs}6my湉\b1!ո ֢jm=O jNӥ(,L#lrH1m@M{GҼ-_d>,4;a{62Ltq. F#x\`kK{ wFw\k} EyLJ|{^On-uDl u0N<[n Ɍc8u|}{})&E I!Y&vi0w(یVIػ;/W=~Ծ+  -IkU_*F>Gc5 8Zhr$A؇`X>VCx/K3hŹ:89 ȡ{+K^G@fPQƲ5kjXd@͹\Bv ?.{s]Ϡj]."'J3_pJ+f-tM{E`9uGNЭU\m]ҾwqE|~e]yy~VZ+j_[Lմ-3YD6ܷ))6,N 9]zSucZ^[OHeUﭮ~^`a"?Ӻw3Ajckkyqi\Ѵ3|1ɬ[Em΍'uoowg`6Ev/UA+|yy^ѹ19j|Rk?~˱6/m=:~;y~ jF× B9njY"s&?>įK!u+l2HT/{Ŀ-c=k/B/nua grp<1;x yz/߇/a5,Qۛ;F0 oA'ףwK_쿯jtFiP|÷}0Uema>'">?Zn5}ʉ ;.F$P1Rp+kLއ7&jjp;wfG3 t-'AGL.nkgI4.GPV qYcޛ+I}}mo>']_"Ӿ^yO[Rl짂T@eUWڿu )s+~7 ukI|]/9.1:@~6 d8M5cwZ6yVzD-,ZQA *?מ4rk+Ҽ[ 1[\Zњ-tX|(I)--oO+U>v8 ;owW'Dž}:䶶\s1l N qmDׯ4 ;F,n4/n?%$$8һ~> CԴ-rKopDi]dN"a`> ZOhVck)4M=&[4!d/+e1RyWk~{Rqn^i^[|hꚆC[Z[Z$J伅pɬ}/ொ~OŚ$泵忕1F +3,VΟ>(>'/ JM&)i"HN oƝg Xk> O,V󉑇$NW?{񾽓Џz?Y~i[Ot2JO{O^JkOt2JO{O^J];(OS_Sׇ|H_3R{&|{X8 x:BW:w3I&q?K+< zYJir7_uG^Ӯ{i +RsԞo@qMsT]B.eRk荺d QG) 7# Ět)|;xZQGSKyFK$2d^ÚWM_{k;U_m% i^%b^kSY5;~6aI|S.O3DP\$rGi}{^xKEeZ y6a򪺳9@<+cjOMt&\o+~xN˨Ι}oT^977c*ۙ'^k~ $ҎwB9K6Ҹ\1dKωVӬn4[e{ϴD-51Z6PP i!V ߎtO|=Dnt^[`HC@f>a`8k?u]_ݥ4__~4?YSowo>'PS͙f_5'[/j[44/g>Z"wNQĿ}cS~ Xtَ7D2 ʐW 9@dڗ]W6ghooC,|0ރr.c]ONudK<|<&O&1M5`YUkhj]~qʗ:7 ևM.=ZH]mkéAu JF R)w!SQF.w6xO[K FU7 By8a"6Ǒ_j{gۼSa_ϳI9cN9(\o ~I ݿͰ(QE|EN>vڞow^&Rcee@9k7~55OM8y~e˼n\s4-.Nt};o'5$5O [ߓv9σl5!x=BMMj:3+pd\m88 ׾7zeu:=gú-cbekyU's6!ȉW^+þ"0x2Hƿ2,A.fKf1ۄ};I Le+ij\t/~x/\.oqyIDu'X6L3Z';r?YYxHѵEwZOos|f@e*byyݜT?f> xXnwđZ[Xi|ʛ{pPpbϳ{x_Ɓ/=🃬%٥#ͼ% ΃dǼ |i>G[Xxvk$y`xd*1 IU|C/O7n#xt"eaóPkOZBN5}D\Gg,%6gfA.#4dDo_WX[X."6iH];! v8glNkhڼ%M]mC {sZ)İ_4!Z;)Yy5AI>O :`@yj ;O lx{K{ u.㍤&{aJUA fVqgoMﯭcZLj|[imV}f:u ǎ  ˄JWq^o"muV.0fZ\։ZK9ɢxORkGoI[v7`Q-.I}Elm_/W~|/KӠ5FԮ5 KD@ \dj^ мWxN|geI^ݍ 尼wY0-|l`CzpxqtӵKxu֊kuلL$n@WD xn?]RXcmmji| vwT'8bJOT~n%k>%S?K]0iW-Gi$DO5Dmltx7|UZn oȲgFw$+xCR^ }CJ͵ω.9,1X#EE;-L+񿍵&ׂ+;FSj3:tC,0z4ȀP[WK^k~ 5j8ѵRg-eܑk[Gqª"IQ!TR>QO4j#~5hzL)f4:]!h),~m$1\/9_R֢ecN]3] `D[ Ėt(?{>Kw~m;[K^5MxZve~~{l"T Tdx#^&`5ƣo`4{K(7BX[i @o||g(>&hZωc5}[}6V|q*6Vyɤ-Mo5;j jڶն1`آAY4NTZ~'Zy|B:?%}K=[_%|B:?%}K=[_%>ĭ(_mCQkCsw*!s)*UAt< ~ķ>+"xg:E^i{{;RH m+@<תX=M{P! `}hf4 [kO}ZhctM9ceoUgpTM{o[F:A#Hvjk~~yhƘS$%4WN#}{jDŽ Tv Nc?৉w1m"EXHM{Mnw8@V};3a᭚izۭŋHPꡜp+n33j:S.xohލeoH$[O?\1 #j㯍$xwĶnjihyWrR`8HəJ.o俀⚗//[5|'wRmxg*sVE6VԾ6-O-1Xn64{6m;VcS~3t.|AtF)$s4IvU ǮO]WZ)kxzZ;,c1g~3F'<⵩kow3-v:~;y^#|SI YKJײ\z#ʯuڧU<=;Хym^H) ;q;W;h??j_*tizt0\9a+UY3蕧 SY߅TC{wnfg.s#a JNGACU GOwCծu 5K$D| >itٚN&~}pk4,_Ki[NO#{6e .U{o&\&KxVDT wMu> ?,Y44'-BlOPz)ȬoXIfZOY;$ ds-w|>м@,-K7ՙl,,0&Z.s-.Y~Elן;~CVuz-vaݕ\L`'5 [:6M>Mn]ۻ+rDPǍȬu5!|5-.LR[Y#ǴᕰN5;X~!^}.>.uw4D6"D3;;9E=n^ Jo_ L>1/%.ig[k0y{5EK6B284oW7z"q/=cZ{gci7O--nu:iٕЂ >lu=BJD2n'Xxÿ#`nou&ckm>ԡ0mڠPI,?<_o*mWVaqwq %h͓DW!SRQ-/u_G _U$u3\ڨƑP,cr8\?0<=iR-GQ}EmG'^]zͯ j3nҼ@llqoB]j?z~?7"/-n-[ na"kHUlrCc F+ʟGo.ׯ3WϚ?hOtG$~ǴkhOtG$~Ǵk؅E 4ϋ?4=ZQ:ls+$ yR8gEn:{,g*$w B0Fvd6I rMx/G]5; B|+^%y`@i=BPDېZٮ}oG| ѼE⸵OWׯ⾋Q]m@<\F+YUې+3_ o,6x&aiayAgb`$+# 95k ,.?j7N:n?seܭJN( F6Һ^&xR,3B8XqVGS&+8mkoO¥q̿4Ozw|oxQ]Xm$(_7a\'~/5gҵ H2f\Nmo,44K2vbAb9' F .enS~tXX=UxSO5on5O2ExVԣ--Z=Rr xz|.Rrz VϪ(uZ]Ŏ+nFW^ll:.w*lk?gN=?Ӣƽ5Uw~h'crw ޚWmv%ݒ}=j(0((((((((WGmDuk1ݘ+~bN8]^m7+?'*uRKw^hmcf~qnIj-ml,m [1[B p~q{4zy`e"8㏞2;יx<ۨk ]?&.XYFk M" y噷wsvkh2".6=NsSkoZ5޷$}n;fTi6c'O>GωNWf^-= !WwR9/C6z֌Xеy+P$;8@+ngʤ;s٪IZn0zޟ/^~yޯAW2뷖k:0p%PhO\U㟄(tTcha[i6baJ>`>(W>c5~3xw7n4Ӽ egq[3I$F6/ ]Nj|T4+K_k2[Jѱr-ة52@xrK'*Gz4ۖ/K5ݯ<Ot2JO{O^JkOt2JO{O^J}[QAAXW tO|Qk=2Cs+ [x/ UoƓxV ehevUL!;@CITk|TjQ^3xd8 SM?xOmqlt RI dN+x-u;]OJGI|_4ɀIEH  7~$麎jVulZ2lhD_Mm|8\W.+?t/)Ҽ?blg44rI,#29Wksmg@"@U=̻NkjqzBjKOQE%Q@Q@Q@Q@Q@Q@Q@Q@οKOxzм-(A3zj[/}nIm>x,bW`aOsk㯊<=amo\Zmoq[sDy k>|N㯈Qj29O >yioQ(C1Ž m|wExNk ⺒D/ _cC,FN*"]-[|mž鯾֧|/ퟁ4.ne UbTN }NsDymޙm=[.YL*R 3+5ok<[;Eեfk= BHӒay$VBev*~PǏixL׼qyurcչڦvs-K8Ey/ߟɢ|u,nuO Zַy}ky |P:jm[-uqi$x.i2$J!5 6VD^oZtx+6}}u⧱kzK$FqZT\ͯ__6=;IQ.8t$fl2>ԟD5㟄 }_^"v(,B0 &W%sכ]|E־'fxMI]-#]ܨR]W o3^X6%‚; dI:?(pG&M=z?/} =. <΢Wd}J +5r3(H0|%mb]Fxm֭qS[\~A,y*Yi7_uW~w:啝^MyqV9gF\* ]O>xw jޚj^0"|:#۵y_ߋi>׊kR1MB n/t6DE88$לR>I/M)-Zm &;Ld`cڃ%_#ۼa#ާlo4hA},ϰn:hlGsS.HVK+/EjަL'ul C`d O^1Z.n5M B:u%>lXhݔ$PwuϦ|H'u?&nuu+{ky9@hG@aKcn iU/{ҭ:]OYjZ7Vttg\%B2P=+kο-egQiBKĚohZ/4G 4߱[:k*s)e.D,o^*7 f=yZ)kE-mґBnZ_]Ě=g]K{2j m$g{Na9]#]tM lu E%H9X1,DAA#5uJ:!*a|/C+Wxswä..].Yacok)])u.׎޷ *۰|UO_OKVuv4+?KquDTڸ?.3 * t|we<ZkP"ՠ4FUm&N6 k۩/ :kIle*^"T|T$Uj]M3kTօ"x/ !??]%ǀ<7w|M6a'a)P6qG_ x_aĽ;7?m/'v|ݚ촏Z烴 5|e\iq-㶹xuお-{5/hї%i5i} 5]N]$rK[kڗ,xVOjf2}7 ^W|YMWO5 )մv4"|k8lU1m9R_ ,S/>"뗾!iKMrʷȧf=1^դHu][M.x/n컕Xv%H4-R}EPEPEPEPEPEP/WN+$,sEi1fP+ݶ ޵ROCCYmiu $ 3\ǿ]xJúD5].dWa2H÷^3KDsikz"Æ3 ٽ,5I붿e'zݢ%jym&KDymS*8SOwRM$%yFkT%Hx.U|?!׵4Hi?4#Z-v5v~4uT"N`oRG.iCzZ=L9?_q",|~*o/td-PNr0Ngx[;eH[+c$8 (x|@Gx/iu;4hbQcQĂc[x&]F4{ آ4Tm;"Q³I'XRJ4_ mŻnoxSv5OӠ[i.8i*>_./I<%gKDчiHaFvǫ޼VԼGĿ- &:ōui!htH(Õ_XC^x{v&qi.%н^a+t1 C8?1s3rmbQ[i5>G:tQL-ح[N_f1/n­6/xOH,ma-40YTr:yy9)Kgg˧k3J36,yO Zӵz/nkE|<弰pkZ줻;}ẓZ xbZg].~U7v/5ah<;j)^[$R2l W'ω~x6y_^RLd_.1Һ?Η "GKi,ԌvJwc'f6Iu~Jvޘ)%ZK r/lѣYd*PgV:΃hү48kTh||˫qST|KXj|P$?+dJϊ~)Y}Pׅu\1j:VAxƢ#pFB❵K#~"N}[=OUWsTu-CUw3#=^T!#;zT7_:ΦWzsqw0̒8Ԝ``=k_>4oƳTӵtKI2?Qz3^:ģ&~U gy>j=>&EoouC_ÿ ~i:4=9{(bZυ<x[4[X䓓d׊X?K_Tʖ?_| xzv0D`vN8>[ |?mh[TX24P< 78U^/Qhk~-nڍijTVeR8$_>i_b|?xvBMn4Z#1K&ϘHz-/%!4֋w&5VA]O?JVA]O?J@vQE0 ɟV໷i\K,f&]r kW㏉^QQ⻎EH$$s(na9xQj$TUݍȼEb&t-m-^)(O)x'×LKy k#nsz.fo^j..xA<ɍrqwfO1$ x5ۍ obB2tSVw4]erV[][]xQ<'YI|7@]\/$tHUJP:b;G> |=>"x4s̚B8j.7}knt/rm𻾸Jo~WčZ6sjI*%r/Um<<3ǩItYlb#;סu?_|<7tnR[.tq&<)&e?,Qm~C8P`~-|L׵Y5;+Xlb-1ǮA~M;z5]Z_{U?RPҬ[u $d&ZYanGmglQ U^sk⟂>7g]ͩ[ڶzEɧ>]ԒHL*bA/Iv.]fm24i>^E^ޗZ4o߂˹[|aӞٴ LDucGm&vPZ"EMh 0}hH ڎgYiibDaivv}VְGmmP""5kH֮`4+seI3`H)ZiwVyWxO1SP{x{JmYuSYMWh6Ջ}>{ආ4GVXIՊ(((((((Ssxk* F/@ }3Yw%ǦjZ֕x[#!L1|s?{Ln)Ymcd[}̿E7>zO n~'j}Cr`,.$d,\pW_2=uEZo%h}7?t{;,Il!#+S\m3XI%{W^:-[X< dT h,f,$ cX񶷭&({7$T0 $`5^Fw9?OI֌5S*K7|Vu'YѴmN4"03]LT$ +4٦ߒD[[Ktoϡx<+,mb=+- B;(*>cӥaqf"$Rݧ6dg줻;}˻Ј>e5쏤?ϋnOҶԂX^xU'=fZu[EQt^WȲM[c2eBXxx{xZܶEmA&n<s֗Ox+_'©.M躄S]C.[5H"B $rp՝_ ]v?ǽt-L;ۧ9!oUGtXu&c,Pg ݿ zk6Z^*WXo&b]kOg .s\I06OWrZZᲚvV7}n.<)*ȡ$ben֛zϬ>nKNi^%K^jPW Viͧ_˴om2= |s⥧x4R/.{#P M ׮9_#)Q1ꚞsaq$/=[I3HI AD_!G޷}?ߌt_^l/lpWWr!1r@J՟:֗oMm$ |Ihk!};QPDkRHBl%H@7׋j~$5?5̶ieN\"V}vXpLʊ ਼VFدi;\p`0O;sî)5:t^k Vq彤bqXљ0x;x8/7p tJ$zu|=s ]\@P dLƋ#|xWiWCV77UA IM Oj]SG-8]'ZW=OO+nF2T(8-`~4eP*r+"D?(+5]ZF$7 l6ǹTN ]{jZu Xj_Rl[asn~VhM8-aZwÍFTZ׭|Yo^Iqo1]Z hLe1rG:s]|ICGN4 8lyg%(cjM'-wj;GZxC]1ǨXGpiJ&[Fv9Zw0ZMpJ!rI8==G9#xR'|?NJ_.IM^-^,±9vX>Sbܢ6sv8uFXl#oq`,E#;\dGqV"1!X`5l1K_٣ᕗ#֋X[BGA$h"BB 8"|F_]'5{>o goUp \9Itv/컲!(ߪG:ܚH[,]jNeB_i#'h,7B=\x@3 iG@ WϚx? k):n5pŤOO!'kg[qv˳S$hn!_1 pNHigo6&+_OG_j~:=9,1"T0+:>u mR]E)Gwlx寇n]Z_jjbܰƖL'# PHVi1j)n~ƥkI!Wxs$KS'M$F8^ '|=CfSod^QG%pw T=O? 1q&K NRn}8;t-lymc- PҴE[C5ٮuQ`A$KFqzZRM*GX,q_¶>W .;]hV{`$H_g|'ˤx'#|t76k!js{Go-ZOc:x#^xRYoƐOeq s)5R#1/[tmz;%_ʉ"dhdd 2 ^UiD5Mĺ5繶Rױqrcd{1yA6T{g/MgB_;Jơ46X[K}JeʖSRkzOBvc#5*֤CehupyMvԍsc4G22O21i7Z{yꅑ.b35~9hvK_[DL[Rͽ_ H_g!Bxnlq|lQn?|"FFәv^bk[.|~F+s\+sHҊ(\ŧM?6WG4-4܉<*X^M F1#"\Ԙl{W/m1|:5vѵnFF n۞~Wiv}z6"6)0ȥw5'|IuV&3 jV6|Gk*8qs߀xz2Cp%,2WWş|Ity' Ұsw|Iѥv\, lG'FkFT0>m?o:0.~hG*W 2aCω??^nO>$Pz,_4X+>z]q#EHUYd8mqm| ğ/_7G'c#E.tlm|H^wX{\ӝw'|ItX9m~$h:iti] ?|! ğ/_7G'c$rQ!\9ϧho:0 ~?M'|ItX9>$ʫ̄ruᶖF_"g\??ω??^nO>$Pz,3HRhY2\cAڗ|Ity' `>F3=(#>F(o:`# vOω??^n1}ĝWa5 p;g?:3\kHA8sCω??^nO>$Pz,tyRhQnDm8, Yv5-KK!t8AN|`|W ğ/_7G'+1č EϞW\㴳HRhY2\c_y' ğ/_7N}~$hJf7Z'# Rң1խɺ^. ω??^nφ+Ωx`գOeo5-DA8sRs\|UѤZU!"By@1ަ_G3<Bȫ>wדQIUuKi,a) ;DqN*IхEV#qm"|F4מ5@!Tg'gіkK}Gf N ^iEz4TZ;Cv~$hek=Pc-?uksO.Uե*,"R@ں_,륀+(`s?~/06xL0GtER$paYCPC8 ^[EzvcO[(T*q1F I*ĭ9Ep!6בQ@u#E/tUP>&hr@R" %J 4H<ŠώzVͤMn wPa1'ו.Io5KiKMk"BMun L^wE ;Ηhq:tnʚ|n.@ T2²~-_;v緐0kv;8d\Es4L"à㬗SO?&wQE -M2R'OS]%TP3hl&Ev|vTb ag?EY[j }o3R ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,M@j>7 ?(o8TfS>0??"@,  ?(o8y~Q> ?(?o8ۋTH9g8_62LLK?wxbíp)(o8ƶ(( !_W_Q%f֗?J$ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9-WF梍WF梀8?x# n/I;w`>?J,~*xT>!͆eI]o4F"j֏-2 ^g.a6p K17>i̷Y ;`O,6jo@}>z ݭCXȶDL1/c=i3wo<:唐؀.&k>@[YY(|$gnIiu5xZnfZlystI Y\㚶v_o="?>LMzyGf 'o/ Z|r3=<{WiG'Hlm5څā#ѷlntFA}K|5i5HS!߂By迯WW]s{i" eYH${ͼ7 6XadGB@O8Ii&mQEQE:=[-&E$<#8޴%X,[Tlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ 6@o o5G$ڟ5QdݗQ/ j TM.[UGlߗ O2V+} r>HI$|~UbdݗQ/KU5{y\ y#`|3]#G+`|3GN5?~_(dݗQ`8Ϟh/fGlߗ ,_ӿ9t4ُ3Jeu*@vGlߗ ,vy-'<:e/}?~_(/fϞkdݗQ/q>%=;_ӿ9vlߗ >e/XSG5+yo㴂[.Fc .:eP}?~_(dݗPTlߗ >e/@r((((((((((((((((((((((((((M*A#1j]+arT 1оGI/?@5@񦱠j^Ӵ[[+Z}#qBA'.?$}G̴7Zѯ_2F$!cqmkQ/xQtkӬG(C#J-pp?P#ԓ?_] 1[iðUwFOAp<+mYjm֗8w}*O\kmfM.m' "3u.0APEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEgxEOe yWM;Z|/hPj,MrnV xkVىU61k>8 egqIp#gЛ[[?­rST&dck;mj}<2S^xgÚc'X$2I" HǓԚg 񺵦xcWf8;9\1EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-18.01.1/images/rotate-180.png0000644000175000017500000001352313222767271015445 0ustar micomicoPNG  IHDR@@iqzTXtRaw profile type exifxڝV[, g^艖#^ށ鷺gSTJ5^D[U4 Jtz3ԭ~V"o׵υ;NBX4vyS4WZlQë́vʍ&;ݞKodbo/0N ݉_ !R~6lq']Lg'bm 1?3wĔcI"o~S|b JA:KMlv7j p@7çCD&9Șxμ[qѥ."G[>ǕTw(/w*_!ofARRMpaZ(hV`)uе⋗D2kz;7bCC\Mm\n5.̏͝GBgu]##4/ͺa;"=@'vleɺefK4)f3 MKZ*d$woѰ6sVmLWå>^w|੧mF⻯1YflQL]]Ec/X&ȡbGhM#, ăySwׂb#s;eUrV>4['N+CJ y{@IUWEQԮ6g8Ro)t:PgsC#[|zgDBށBЧMs' aX(J xdPo+[jsBIT|dJIDATx[it\Gn[zQddKjٲl8DV1LH|իWqT*gyD"Jroٶ?Vq/}Kl @y m8%W_S;Ǵ fpmr!Hd#i ?hv:}Ƃ?go,%yWj(^ `*$ )2;ŷwm48:~ d t&4IA -8H8V JVZvF_jئxǼa̚P/=HR\5AYՉN |0c/Qt'S=XCX8;a"+UCڰ>LRjnc!:Libo0 YhhHrqXP(?tn$e&$ĉfh&\<6"xk?h<---1`z/ifF+IN I0@Q}#;;6ZD0OR:GK> } OSҦ.mAm߰RғO>.3aS6Xb 1:^[C&$`lfS`f f?s_8%ZjOS|u>{@lTk}0{Gg?vnhTXb<\L);vz1&%-$OL+8>Hp[aF̠JkCz}zS$+a/ELkY,LwLb&GӁr$8e .-lCER>?ƻ\{zc<gEM%UAIMτS;8Vwtx]u QUDf _5Etp(ccJ3y4 lB1 6?H$wY{6wիWWWݏ>ڮj׀&1nL /XNWx^rmg@sIJ[̰HssۋF`.Ϳ+HUr [^~Hl\k,obVےm_s=`FM1vZكx{SJJW&/L%_/c>#칻g E0,RZOF{~T#A;w~zRrͭ%~#{k'+aVI& U娝\o֭[#N5q,ʓf෠*^);rixάZd+W `І5r` =xw"?ظv}lvO\pػU zseMݟR+k7v<.Q<݉N߳ZiȂغ ۳gs_=zq(T9g~q]u{N7q>Ňw7oi4Z*)1Yn> @ՅMӜ0А5::jjjJ̙3Cx\V"磻v"4e3gڶ==f+U4`[f]y^;0Eo͍Np!QHvoT*GFF) mJEDZ "r-Fggg_~BJq>\.ghccc3ƺ.\R}}}n|6}}}. Wk&z0N?9kA8][n)"0̅mbժUrJ~Dv2ɵCJv/4H?*sw XZCk=BD?zd.RjyZ0˥Wm۶E=DTb޽W{z6҇:!! )办=m_HJ^<%"m0' ?.\ U>AZl}+} 60B{ӆa(Ghӵ֟m;ػw.Z/,?iƌ7_x?`?pIoooy&)cJ1Lv- R19X'Z7oz 6 -  !di  MӑxRwĥr7R)}ޜPԛ疑o2ϻGArH~PeD݌1 3Lh Um;D"&fuݿppe˖o0Cp,n D0^kF86DfRU0F g7o[`BPe&{M?/K)TJ Q&QRnZ'0Z~L&]eujjj^  < 2Z5---}̘+EZф&b;[[rMT4%jnړ?=K_CX(<~VJݛH$J\.w,1MZ<|eCF: $$+g#^?x~軑mϽFaٳ͙34~1_|<q)]pHσ(V<<; ٮ뺆aD$ Pɶ47:+ٳ߰a[8(dcAc%$Gz^摏:MW)b3|6G79 'ŋ=ؼa2t甥IaUt*c =sXuc6Mm3D\~mD6-IPse-|}pAZ9'xIM,ᅅ\ ׺n.[0& iD'bs粁R|QVJ^O= O.C4oijV`k(cwzx{㰪#g*f,"4xViwO|(v=}i(>v,YNs>IZ^ kŒa%Kké S D'²˯s*;X OZ"xQ%쉻 O 佹K h e,a]E2L" $-\'*'K+D9Oc2Tvt>t=ZZBx;HSmmcbqXHTιf# 2P"&hb(TLᆦPnȑԑV`Q2+/@dпn3('"fq/2t![R:3)j">Q!*ElQ?𩜐B[ Cy/mOVorIJOoh)ΕX[ec,gn]a"357^!4{>~xǀ3${}f`k inH9@i6-i7!δH^WhaLAڿIPgT$x:77 $IOn8%q8jÆ ݭ(jIENDB`fotoxx-18.01.1/images/panorama1.jpg0000644000175000017500000011176113222767271015517 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 380 761 0 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?_YOcf78wǿ3`E|Z#h0`[kc;sEK^}!o\yG Mw]FrOa.>weok~di"ڥA PM> ZCg-gYGq5^[| s5+^mR'|-7~a[JX/%}aɴKnOUP})WjӚp2&hv1^=4Fk| /&H?6 S1F?wcz|[#1=Ifp#q=%|jao Ȱdf~8zڅ鲎5Ǚ4qm=w]FrO` E>Gzeu-ml|hnr8>5mckg3He 7g㊖VP}_'nHA yT- 6 wd tҀ"O}j qԣ&"<1FG=3F>4+.d{IbN㴧<Xo[J$NXF`"عWa&W 99UoM1F?wq? c%;L+qvhT߸a#;6gz[[#0I+8`#5 2;W[B!1m)8u ߱Cf&-&.g$5misi\;!<j̇?(;p='^ ?F&<CaN3E{W@#Ҏ(~̶9>N9jm.=J;F] Ln/JҫC=]FJ#@MŸV$mv =.}>kEtK˽&m~c)=jfsM.< +6;3-x F'iv2;5td8ۦ70]tg$>sgeL=& [s^1RzYmEHXDLǥ 546Y/-FCd㞙f [̉f%Tm-َҒ+3S=Q{Gqa[ >5֢z'eH.?1v8MҮ4ٴeDBed ޮL& KvH{eCnBO̿63H}bW6$1q{ h5 [=_3oϗm0pw׊oxC2;tay}늭?o !%/;C~g#ROMc72Vм{#;Cs}=uX%X b%ɿJddgJ;r>u!=wȫc5? <}<@{kPDM!"<1ٖ#!2qѦϧw eH.#qv0;WW.V`!SSB)Y i7lti!f̲`ޮN09'{櫛{f߳/MӵxdO.ݒŁΣ-{qa 4,a,Ob_WW⏊@7q @ϧy%."-H&#qv0;St6{ Bfd +';dx pߕ|X$jC?)k"l o|Ϳg>_{cƛٙaTD|`\py|_$2/J0A\.7LtFe?`=SONMAc3>B"]Nӏ dwy- cxdh ,#7L^[moh˟EhAMhǝݣ++@\(g\%Q "2Ye| ϭt|v"1snǴ )>c<ڹ>,xnGww<9&i|3},(BAwnHLs.4gL eTd[D;dV[ C튖}szyx6P.kg# ,]1\T tj޲<q;6gz:d:dRK2B-뽝Dx<+u> yG[7LE>R"yE&_Բyz[͍<ěJݘwK_g_.$)qa7vrGlZNORc1LvR=QHqy)4},F-yRMlbd`d'皚77/H#*mYr!! vkY( .;57PiƑW`>k6hvy=w{V~&G43+y]iLn|띛:6؄OH&11z \My.!o0?TH^K4V?g@- )88=+%~ZaIbUp:'99e׍u˜Bh/.dzx|]S ?x@&?3r7t5v :(&F4tk3\ u8ԯn.!5V[cyi$E#$gH H8͒{砬ۿ).-nefw;*xy;䪷 ڃ,;˯w Z鱯X˟:ܹzm?\kpEOoOڃ.FߑZ6LS tY=RInfrM#I5'E; >ѼojW7qH#@;(3Km8+Jq-d{eYgY  Fq_&0XW {v^\qG(sSޠ|>nLƧy`Bc>_z-6I-n F4& ۏh$0:oĿ`rݸ@߯s=KY𖝨-\ݖ]0c=*ZJW5gk5BI6ɟ ;~ ~liWR;M>`[WoS'}wi/y^wO>^1z/}cy]q19zC K"Mm5ew1-v9ڊ.?'/-6ymq= ngHV쬓FP}Nzn3E2]ߓNx|x*@<QY!_sG6`}0mN0Gq.weq\o%ܥp@\{ UvTVWlc-UEJZ`%(&s%Ⱦ-7;gqՀ‹c#TrZrTRj6q6.bR*8R# &)- 4J9QIlir"O 3m")1REf))R11(Ab[D  Z){PJ)9@(Nii⤦G j>}M J(MP(J( u&)qBKE0 QIN LӸ.{ 0@2)j"ޔƐi,;Md8wRÆ $uVznAsЍ S?WӞΈdˍ,I\P s_ [?}1^ 6?&8"F/`k6:G6&2~$~\wh.[R;X>c[wQT֒}~R\w_8KgjVhO&7O!dOi/r6{dԔCqk~K[4jm`[sn*1o\if{.?*9q,w E.<6UhiuffW09;4u2[Gk0mb6v5!:c{|{38Fn_ϧŨ^:ٖsŶ>S79dPX,m^BjdV[V23P)ĘHZ63S\TGC$}E-Ɗ2\)3LnȏANk'<n 2iSh})F( N h]hm34ME +8-i iE8Ti(iBE<%;_m(Z#v6կ(J`Ub^ƚc>5 EN:wf3QATF:Ӹf.,}(kOJXt{Pt=+ڻ&ҷuQT4R"f a3R qQ $eJY<զH־%~}1ƞ]oo=&[!}E)hoeiRfw(l:ϲifDVSp-eqR$^oi{w<ěv3v'w-1Mf֟hI&3<~͞M=VGiv`Wjwdo!\j -~&O68n,6HEW$QimƬ.%.޼={Q@m6:?7nۜgk/پ˹߷i۳gv{j0ΐY&ݼ0,23g".d &aT2y\1 .&[0?W\]ĊL*?7}w,a+G>dR<1&bze,R[6Ow#N_M!85'7S$'SX'Anwx;ʪ{.mwNQ8bmdhw|gA p bu͖V,ZCVGu$ '2NWf8s\V9o&դAnZZ #6X,˶BJ縩vF"G<g{?JŢ=Fx#!U~F NSqniO<{5ooa&J9iF%%Fqw&LEljL*e ^KGݿʃ:b腚,] #ҴJ'=]% 8 \40Vv`1V: k[n)*946!>}M+$4GϿHiV*yion8ۍE$j^UF}66@jmHW76\͘imaSf?]\}/;7Iw)\8 3=@5<6UhiuffW09;4u2[Gk0mb6v$aя^J?*Pkc^+i6ͭ񳺓PqhoJiCWQV5F[MW XNN}9֊?GGSgyP ;\EPzIS =?qK1m%Rkgs%($JGj]Hv:gҶui$Ӧ$ )dJ24e%5t?ЭH\w@fXqr[gS ՠxKDΓO\ȭE?EIBNT/siMuwyH^[JC>˨iz[Y]Kn^}cboB= S稬?7\gTf{sneM s]\(MM6YiE1 HzE0(?ެLVޱ;ԋj?JE51ӷ<`+4V$ TN{ӵƥ˩B.&򕶎AsQZ)sqfR_<#mJ]䃘VjZKsGVR؏VO+@v%K$i !+ 7f֔Mkڐ!ay Z܉)xF[o( 1z OUHbf5Lf4U +^czfXV46ӏ,Vl[I5iQ `q7c&kr#,\tYQ4EF0;՛Ȃ}+NI2YmPPG?B)[5\ TmӎтQygVB3z urk矆?5} X|UR:Ʋ+ʓ׵c&ځtηOdc!4m}.ʚm 8UaZ=:cZ{U3ҳ0: ׻i1ycL3S?:$on`>cl,Ph۾yJ鼕 se#; QBبwŽ=r8})?:qf8^Ʊ.]c'X 'Z>to.TWLq^B5ˈGAmׁE6߻w+q F?ok龾}}=\-v'< )G;w]8OύUڦ0_7l/KǨ[?3REy1y &"}NY{'֢ ӛPh\o&es =)1i$22ap88kK3uƑQˈic pNO|J*型&4O@n es%q>Pf}>m(LdppwۮEj:dW3{,e1m G.y$sqM87wE=h=ݕٮeKj˟ԁ@=~ ƶ\ˊǯ&xPGcWkƖGWF=yH2t+ͬwSQXF}5a8yCKhY}Œ`zU;藊xbxq[vVm1~MSV:-WP+XZq2N9d:Sr9Z'o"DdNx#~ (q+ձ&mJs&nAB@k~pDbGlI<{޺jٓG7UFt! 4lcIW~?ɫZTmd#*xWh#ҙp~vtP*·E?C?\iZa%+qwvM>+ Pn+F<ե~ 䴹lPҋk8t3?3{MmZf8I3{TON]Ac3Kq|{c;Cqދ֬Gǖ ocddgo>5g[M/=}Hyz_΍Xm0؝>so/4XyHNnY-9\j -~&O68n,6HEMsBvjg4uBlB3hS .8-<%vRA.LPowٷhݏ+wό/O/a8:oEcTROXzښ&^B,X Xm'Lm7n Ge㸤VV\g'\ ڼY^aRLݍ$ЋŜrsX^"6ݧ \8k{&2xM I-!dʟ_Z\Je|VK[43޶ a\uuQhq®Tj/ 0+Nji(p\(;ϸa@ 1kyq=xK/=ꭏj#좌?5'WEůi"cplgsJ_5{ ۭ2Lpk8Z",]\II.g7w#Wȼ=?p8u., hvl^&"OJ)O_iNzU&jv&!i )JSZŸ?V&]kҋ ]85)Oj iX.Wa\߉4f |ԟjYk'V[W@V3N.?iC"wI!<Һ x|EU:Um 2lPvᲽOQWŨ u* V*G%~Ro"ʆv#(Ub/4-vfng$iDtHQ fӧ J[XK k[v#>IjfG )pEzᛛv%}p=s۱'0z.=k_^grK 9_nTXkO_(?ڥH.x€xXN#H\F85n@zgO bI֢p7P!J}qȯ`-[<@_nOs;Ѽ5$qGjå;cKPxQU aO`|;D"__0HOr;xdYAPGӚ)=Z8A&38}{? 3h|gn?vx߽) $&4'X܀09ډ]J7v{Om3Dmҷ vmB..H|eYg@]Kn]xN{o}Mf3~[vql|j(mF+$ѻzFzQu̗vrAw,7 O+!:jmHW76\͘imaSf?]\}/;7Iw)\8 3=@4w,74:b;>,יv}+ϭ񳶗„ەJ5#\Ҧ Y+:PdrN7쟲jSVY+4fas7#Z"fP]7 -5WE)!g+QOsɔ؁4v]wwRk7"o L`d~uSjO""2iqʅ tѓ0ZXh* vL̺+e`rO#~f4moE9B뚱s<ܡrHO2`^ν*5]i_SoF"}'.:vH)3i>Q\MWUGG(\ՓY]t:Ʊ$Yx>XW h0]_$H8N's4X~ ;xQ2HzVN}R]i6K1ֈ 9:*HgڤzUHA uZY>y>oo뛭7N**Pѐ-\-3 溟Ww6gg& gH85S`}?u2>uܹiW Ϛ9˃?G$W0$Uw?iD.\/h[8J\t* _S4OYF`຀o;,'yH ޽3_+ggqp(H}8t+ ۩=Ij!}N׶ 0GJ-sʺ_##0ڵ8.rsد {JIX K}'m W9&%<2OWC4)$h 04%WFIs봆8QR0GzVpOIGl ej^o1dur2נZZ[l.wɋIJS ' {7i_Y\O.VF~bF8nδvQ<3}{}E}hVvX&FJܯyӊŊ@.gv~㑟Z;zp:۳3c1U~"HЫ1#w]kU[Qz CmY,D)NJGC*%]X?~'־7Ғ,I3g5U8XE}c/x#K6,o[_v3ӌ+zR付֋}.eͭΑ;dɵ̟# ԭeF%Įmj/l5ha}_=@4.SɍY1r܍> ŭ,ZlѪg mͻ mQqu{Hk8'w'| URCM\dL/to b)I`W?1[2hgFGq=Q>Ex#7fZ=#Nq@3Yg^ky}+:l:(޼*zWeﳲA,cROB+u,c 2+g.p;RAͤчjXqku5hSVIƾ`ěyg+ޝ4f?Z5АC )m?G/X^ga].Eq$QqOJ|"lbX +}v6VMy@ּld쥼\ISA3{ev@>U^|'+zV\I̋ 9u"l"%=ꮌWr`Jg+6xiVrc׷4Q6z4_F$svz7PO5@Tn!M=Ҏw9^לгN\4Jn G[Q4Ksg^TO^/1qwx  .NOy-BW#vI=~ʘd t;.r=3]5*rQ;]kŶ*Y^'YY1{kѼAK7Obdo3U8>I5i3ZY\z1\cz U֧}ZjJ+x7Zěw{|!r1>S۞+km4{jqp \7ӎ%EVF!}yGĭb A2@Q"GX_>KFw#c9#:,X_\DBKqӴyt>ٮCפLm>;AӸmy;^Z219)fHI!;qQfw7zd"~UkIVҙIq}zWxzWPTsBՇ٦ NCq؎حci5?Re\(|E.+{-7Ib}GPӼ볉ck5nmv&KlF@ ?ްմ.i]m}g XLOxQС61>XP~HM!GckLB׮͵R\ڟ%G< ;[8ώ0X{_:#Cˆ'qߎ)ѐe4clK\ }%vnقiqɓ=ԏҒqMjh1`\< @^l*1V$kvب zv>)ԥtBFI^TTSKɫiv&1=uȌ]*/^Aay-ݸg"q'9koa&?W3xxM8 M[Mh"v?7cW3q=9bշAoh$r`[dpi=qZԦ㹅:nȁm7dꦡ+,KrU|\߉=֍ke.˰G$W-ͬE*3\OCQ#&Ayׯ p_7h ]I:vr\/spͅܧ8fPzU>H^Qv2l#%IAN+RBe6N%\ehd*{VY("J @N+>$kĖh:y(xfTHY֤+&&|ep(@?twq[^p5x O.G #ELR^mz! ׃;mgHS$zLҹܦ(*۟ eyK\m\j'(d!y'U{I*j,~;{-9FX\iK}~4[FPPsֲm>%+稫.eU$I:p:$gxj6<{c1"sѰuh? hVum49r!>R9ǹI8<LWF#Tu20Ì?\pL0FrG#ҳm\E1Rg5}ge+W$z2MyLUEBym"1Sr`-\ҩ%ZtsD(ArwW\&r9kZ9Dq՟$[z\ݍ9Hg]l]>e^ixpC`nzבyE/#w>=)&hxwaSQ"-ٮfмӤux+d{85t#;.G_NIW7=ԑ]42}谄 t9#m[i3kDz9Q#a5g[M/=}Hyz_΍Xm0؝>5}$dSk?{?6{Q4+[GK0-]ݓmƨ`7l$c1h9䍼Qqu~E,j?b[r sw>.o~ѻV-xZ./컛^nv1wm{g& hM#=Fr(K9 `H'qߏ sN=GNrzsqs( Qv늗ڌuM+]%Kwv$VT@?ZDI#cs?y[eRsÌ}+9[$3It뢕5 h?ݪ.*MN)&h H1:lfi8.ڊ.V2Y -DV P#W_AJSVB%4a)Gdm늺ӤVVϠHxm$s+`ou1n'q׹]ks]7,|c:V>B,/qBU%y=Ǒ]c;$edVڲ..oPk~`e\vs# 7^{96A%xBxSCIG]pJ4$Ac`F@VτN[4yF*ǰzxω-1*k Q6\,rv5t`)'ҪZVvyX5079L 1*%Ҥ@v)hh赖t&xnmr{b)KF8+= H?_TsːZ ly1h7ZA^㞍=$1!&$P@wp_SA(ry4%6q}* C)x&#x{s`SyZnRvY+1=q\"͠#{>g9a俙Vv#T']!@Jۥ\6Ʃ,`6q@N=zi: `vʝKmBk:¸1&1a;?.k ΗVT jc)ekR@8k2m)g)Q*nJJ]YWcwZ?6uTDD!TMx UWZwR[-U 8t 3r9r0P%8MyX3WvٝCo2;r}T~7>|n۳;Ǿ3_ յRGH3#}LQ +g40D 1!!_,<|]4uؾy>v{Om3Dmҷ vm轂YO(?Xlr9qމmJxZ$n[y`JmF;do`$[IxϸNzb:~Փ.^T[3U5Jч"n٫rӑ#<>u-fi'$3ү=W뫨cYD^IjJїAmz֬WOʲ 1rqV(6Ւ@/Gmoݖ'C OnGWxؤyn}=Ez<_lϞ^|ZR3qZu%B>׸$ǭhi(g-OƬi,Ȭ8jtvP&8'qTЪn#עdH\6g+(}hJuR,Rdr1=sKұq[$"ck Q 43nci<꣪WΝ(5䨳m< 2]a(4mvQ³pGҟ+ZBO0WxP/́Ek¾\0Ïʠ$4M- BfkFFy>+ 1'k]k aв.&HMTJ"!OJp 0h.5 > 5-B[b 4 A`zW_K:-Qv~6'1TB\`WjJR:y8>ҲK)H uG 3}`St(7L9<{ +<`tSA;J5+˹++ Q_Sf^sJ׵hyge32_ &Gѷt0,H7pՉu+H[ pvGj/}x&.ه%vK\}GX(-q`>P+J7ߎҰ|Y"[V5#Q֔jFRrz|-kx:u&p]3<8{z+4۴(*N)mO\D}EP8s~=v:o=$nO0sHK6}y] ZI,V1F gh~\`WB`MͶ7-c|$W?^?{ ~)Ũ02I0H sAp;*'Pl5ha}_=@4.SɍY1r܍>5ΜڃF&}6.(viYO9I!m ߅#3Y qu{Hk8'w'^EWkj k 7[ 28J(γ>6UXoup~&g288;"2+G[2`v#PBf&@`Ϣ4]^}2|rEKe@۱@X/__1gY$.9#'5DiseUS$0 mpHWzDMk*: K zl:pA OlAv 8+3Lw4,`AmsrvH#TGJ{=*F=GnJėRs;̀LTkڌs~zWA 6uzT[vys֨TUYvGIx;R{Q4(ry0P\ٖ4`Zjq$a"WgcK5֑'5[hfV## Ԝ>^5kB>}(6s]w|hdWw{f:Ć5T2OҵOvr_ w-APyw5OaZ!ޱHzin[꩞[Q|$' xUr\]El$\}}ԼyK#w'$H^U$*D- hiolXDMNp=(˖hZ,/QiR=G$qU.u"x)ݷv-r 1> LUKW6VTaFyI,ֱmqv'Uڐk#nVF b=39ֳf[R{@I?:m̊9igb7kp~f$*jڄ 2IpK4:bN>2+2I4t!oz.9U~$ZHG\?K2sM6 3}6f,' bLSR--uFMETj|WIF1rqr=ZDCmPst1-?횻 Ll㉶ $^{BQ4#=d{H_MvEL^Nr 15<'2$Tb>x*=lc!|Z|?c9,;{ךX6*%mFirAMߎvХ%9+M=2 1 ;( :fⷋoWjj">orkMJMڅ#?+RGg-Fmjʣ\R _Uy,/ ,ln^AҼS#=8I rF)j/oٌD=?n?J/q4lUkm$qZYʜ?P+6j݆` Y$HrA)gvZHA{Yt5t6-u^kzdlzn hu(ݑ8s\pH1 {XIsa˯Y ]<ױiSM.ݤ窩v?s\F]OF;-'dL#5h܅S$$r=kZ|CZ:ٙ:*Ltmo+!|c ܧ R}&XgqcWO%ՄQF3:#OOZṉ+Mk㹍¾>\|/o%Ce*EK8~ǟzDNzUP%oIcmņsy=h `HZLFhMX~z8qE1y@ H8f^rHzٮK=% !eV`z|gk+wټ8ݟ+j8;cn'&'wͳ?wn}P?YҨ[UO$F8#%VHczeŸE vrq{-I#:,WY>j'NK*E9G3NO;i ? +hzBq~\Usk6 ̯tMG#jX(}25yވRy`jgQAJ 䜋yCJ"o65QzjZ)}*KnBG yᦛS˒G kggRk~Q*Chڌ6p8cWO(a=HD/{vp?0@~?4.*`?Wī\=7QA25J&N_ ~$\ሶ $_.}@$Yx>xtP ~lk7R͟L8r;P>VR+׭~xYb7(&O*yS=q԰(GSYCB;6=sڳxI>}f'BCzN6=q_Hky?乹m˩®Cv8=| XYG~%yT .Iݹ>K8)LѶ^ǫ_e.2xkʎFQ7OwU Q$-xčsj 3T?[>DMZ2 ?@«ɮi;gCWZ¯ \itUI;96m8G;xk.n5Kc ?_4ɕی6~{Ma#_ZgMZ퐜u Drܱg K؞ 7A92ymۍǾ{V3-ߛ]lda`ShwN1K |&#=3_5UߺXG jz/wF0̂'n33qT0'>|37ݷ=vXzk/'6.!RzP1 n|JO}w17޹YI;$ '%[te1{MSrsG?]8>OZO1 }zʆ0yG򯣤ISO ~#9c@)ŨE-Fc=a.o]hr~q.=qZ=b 5,r]3ǻW&@9ۻr} W\1 3U}Ia_yl7m;>B?9ߑ[3♋/N]" !߂3<|K&MJX䘬tMCQ͟Z;|{6sv=u|O}|ۼ1zӽ!JKfy͏ _[[4w;v]duz/'QNigHF}͔7 7gonێ1=1ބogٿ|̶ۍRymq,幒9 RO07A-pČL{{?ʲȉz![#Qvoگ>oV6n1IȗC'n;3یqA$+w6~G)3wR_\]d}aBmfx;A'Q#w?cٟ-ؓdfyۼcݏ|f{=SYILhO%6߃`sgOroxݤڻz3-8n6cwg6 hM#=Fr(K9 `H'qzC[M,%k8w#L)PGwW3 CҤS\β wPAruT]adk_,|>F:|K7IM-ߙk"F[`yLÖݕZĖqM(m&y [X@lt4Ljyh"h.Jo;q}p(j dk$|JmQ''ڛcoy_}B`3h|gcBy|J,c;|Nў${+erPq؎K;biR9YQ\ wPruH[k #[y`rC0{Q +\%GDX<6vGzŹ|FJ-LvB ϹM[ҵvl%Pq'>goqmwt.RY JmQ#ry60ؼWWb䴅gU0Qq5+ԼL,LGhAN=W{gQ#, Q7@"!u? G)1ywޜ-n'%dn3\}q{H|X;u{ֿngtX+(أ%Zd 8#m^Z5͓r )Vr~CI%TuUXS\>w3 ג,@}C@<W3]2f[>s#DfB Ϲ m'S{kW+()mͻTczu1Mx7hI% ya|ڣfG͎Չ\?iZ;KFHX͸\ <)s-S۩_ΧǙ޼X7sO=1Y]"]N/hvzພ$,,nyA,=7Fz-72ٽߑ R?(7H lLVg$X 4fG*J;H#;3 G Β9Ȑq=mI .٬Lm僽\>0CRWG;o,-bͻW}u,ҋwn]v3 !БϹgoճM)"j@ PQMx7hICžX_%6ّc673o(&J'IDQ |'^wO7#u9}Ƌf*0x}貆)ώICMl0''as;|AmyMv;''RVyoK"C]DG=I8ɠ .ᴒ;ߴH/j@|f&?gyo:w5q^e6i|0,< 9^s-ś]G!i鵆̟s2唰O,R5ݒG*Hi0A Mk& H_"S;p{@rCrڜDKo,JmF#{Q7+;x#1oa\οt{eQo&tsZ:2p}W3-b٦ǧںD\V-6H8>€7,n4a<ڠGjmpIfi$e Kqk'i8u!Ȉ$^×<ȷܱfplMGiuYy_:T]ivfI4P}ʬ .pgsڸw Oj_yv?]\[^X')ZKbh hYȎ9KMM6Hl~\w ;X>c[wQ`uX* [ Fj%$Tb)Qp)/g_:5( c28$t$dhn(.Swh*%> m8nb{>)d ~P_%vW#|Oڰ.goZM)M>"by@ RZy47+!ȌGaOlAuwyZI$lVbU08;A=N3ޢ#d:u?#~<לt]\\hO<ȷרXY(=+*;HeG|^wn ӇP}X]39.ͭ|k ?wGq޹]\A;9Xk%.Ui 2;H>|Ws<&Ԍr*9Ȕ> A@rrڕw#ubTn1j#jsN{%Dް lJ-䲻w9̦:2q43MV٦&m"\V2L ϰ 8nb{>)d ~P_%v+`[', TT,yA6)bUp: 8zxQ4G ,JHAxv$ܚog5 .'W]B3@iF_ΣǙ޼I}ow5Ek{kіC˴7F{g"o~n 1a7dݜ=OWxFh'9L1 4aG$\C{̳Y9̞P96ٓy sArڕHdۺ'O jFl6jZ------l67nihmmmVfAaZ'\M|||rssNJ"f#xxx<ԓz{""""""Ѥ 6-000:STTϪVjݓjqƍ7]tҥ 2~ rpKl<<- FKdhA$\#޽{_T`٪Z &GU"ܲ曭Æ zիϚ5L2 1@;l.Nr9l2|۵kpܙ nMA2 #:eCG8ht &͆LnK+^xNdPhELC[n71$G4"̆L8Z KԌ#0j6MTFʍQ<?AhiA MW9Ș 鮋ʣ( 1[OVn&|l x: >X, ?D AV䏛hQsm]ţ Bx+"D@ԊBaE&:ŨD1=ţl2q⃞QE:*&ujkۧeٳCM!U>*]3"E9rvWO>+ %YGq[jҍ" ^7uTn#RY$*jtCCFL.)` !?`wV\ţ:" `i +H$fH"ׄ) Ot?6R8\F APQLP"hs ?9StaT(!0g4ED|'$3)kWED%Df(NBhMPaA`9!pppVh 88VKq+Ppq;^6o8pvgz̙@Np+I' Mwh|kks5k֬Y.^_G/M𤣯CӱQ;xOPp m/[[[/|@̟?NX,??F)..޿? H&sě bfd4k;`0֭[Kh4ӧO'ఀ %;09%ķ= W 32N#}$yU^YlAHM&OxH(mYn1n۫Wo߾&@)@-VQ>.Ѷrʖ͛7_zu?b0Y)ffX7܁9iho~ZY=8Q 8$4$tp4-8G7PpwAԆ%Otfi e#(W{K6tu$I8ǐ' 'x;IIB[]F> q@"ފ(mDh"uDky܂0ڀŽ; 7! 鮊 em$p<  7qI -D'*3*6h"* ^ZA"ǍQ<ڊSSO:ˋyڗD$2JpRjjP\>}z$UN% K'F`fA[AرcpǏWG$zIOTB\ЍQ<+/P<T:eCLrÓj/3NRqNx79О(.3PďQi7ZA> rc.΍qIL;pHVRh*薴l6^1)˗YO iгnl+VZLVuwjkHts9A\ӧO2d„ ǏG:eI3Dqܘ+W 8x'y̙3I"xIG%A4wq'\pjz[1u/"Qq׵kWRw6M{xxKҩH>=z,YPQQDwvpbA8A8H2tU<kE2I >4&O ޽{ }}}~a(:S6ա A:jf e˖WJ)=z$KD̘2\={~ǁhFL|nf'>lH(Dwbbb?XqP^ L8Lg# 'D&d'%%%&&&00055$@2 P,%(g>@ DFA C7R+[hh[ZZ DgFwnLCTPOXH-BzV\\|UNWWW@Qw8 d(Û)4h4<%HLLtu%W!:UEw8җI^+SN1+/YpEC"#ՑLj4WZcEFF"vŊ#G_\\|̙K%9鉲f-Cp ,LjZMD{xINN5k֜9s~J8@`axV#DbBP\ӧOO8̙3=z􀢽lz~u6} x晥>>/GAAq$1Q//m "=KrCҫ,~HHHXfȑ#h/pnl .[w֍p +rh2aƙ8" //СҩHv[~/_.*/ػws3fjqqq6w=m2nܸ_j+q`WTϚH@$\xg%DuG|'VJHH(--]tiXX~[OOOl#w8<93qK vwڕ{tԩhmŋ/F: !iqPC߁^ND6ys19@lᑟDFlhֹTă5Lz}EE=$gG. z:8VuCQ;d2iڨ($*.[G J+RiJ..99`0h4}H4㑛d EyV7CN8T,Y"~R؊($%… !!!23ecHpg⑉8pVߵ T{aų/nŐv'G?ˆ:ëOw d"DK4 !0 [&NhqQwۥK>+frꡕ$֖^yqдθF. ݟSUUhF0`@xx8!J눚:-8L4 xbZZ*/O?E1) JH!"c֛0&U } Oq"H:.mmmHtfsdznp}ңVN@*lǏ?ӝrdKz Bpp?K.ˤN#P#HwbΜ٤w=̓AtxhVn#7:.dydffigFXim"qPF@[9yd^^ѣDec%,kp)# p,e1bg}֯_~Df 1-'Rddcrȑ3]j9~itpEƏ/BNNNfffD& gFgFLd\_騅:Z|7/\$/]+M6&RVSĤVsZq͛6lؐjkkW_VV x 2D9 3 D#Gt ?ȑ#RYY٫Wy]vwh>VHM4D4q9&OhѢL|ٿz;l_d 0_[%V{ZgϦTpY]ljj*~MG)((?~LL eYͭ|W._uÇ0%?C\@7WN~lpŅ 兆8w.sҤISL<*5Q->>>N[/li/_>t萯VA4qV AomEw㤧߿kjj6lWPP<==EJIL! ť CCƎ.}NL2eʔ)HG)áK4׀hȍv&2$Q+ ===++k}uN9PpEd!#">>>...###---<<\ Ա;>Z!I/ȽIOOxbXX̙3wtuʆGK;h|]N||||||UUUFFFA{$)) g֙$Mh8ùsz~̙?Pq|)*›ӡ(6lhQQQKK 㜤*/dEŭm6|mVXXXQQyf$3k蓆Ujw}EEEHTsйѧLw[Y/ MuU!(q+"\ti޽aaº77EPWg'I &A$L:DPD#NTTÆ =+s΅o;IELTVVY׽vҥ={l6Fhm={$UD"V_r߇+B\\܊+:{^ m8pw[hTk>4"E@8nY;@]]G@@@hh(F2Vآq {QRRΞ=[PPtgF91K.O݃8RUqG|D VIwřF'8C>p9jL/\e̚Ѡb >g%;EיNHFDL2w<bZݦt]Ef OKPl|QL™$M'# Ph+.jK+.f?PRR:z(U)))ݻwOJJM8tNy:hbE0u20_nYn]MMɓnv1d}wFQSc$Ge\#Λ!倯K/݋Dh4hдi<<YćFuϋB'h%^?x+C90_h8e'(:'5jD ܓhEa 73jԨ!!!>>>sEj k$痶 "7N?~QQɓq]ׇ8rWRd M["n$s-]lmGL.-P ޶0L#?mRUq'(`ِF#1Ό6qxH("JTQNy><`^V\/?p9,CDJE~y_eetmv```DD-&&]t Ӈ0EVH'~x*s}עgϞ.)./r|DY O'd6Q g~Xoo+.zOGJ6}xg ^<) DH2_̶J IIIeeeZ eC5#tbIHXQ܀M6uITP6`Zz^tr4 gyMDQA4)9q·zZ(r$`! )**.//A;I>A+#N7vlsM`}-AD6.<& n`8|N ApSa'Vqb'0q6ObbbddNpBVL'nz~ֶ{n$wF/,))=zO>+WRR\[n?V+2+ r%VZ O`!?]Q{fxѣرc**/yo~`0ԈMQW J$]䶶CґnIC[׿ s޽_=vVs 7Om"ȩ wƇ?[ҵ%tҁ&OFի-zx\j}===g͚4hPqA(F<Eهfg [n޽qݾ,++[bŒ%K^{Mo˼,7zArq!vQQQQVVƾw{͚##ss}YjWu:a'X3IDATsN ]$t1@nx܉̛7l>vPPAC AoUG&L+SĹVLܒttws@*GI較IhwzוY3R~]F ;f%.!tUw?T(HBǡrZ4\a&l>\ţMtx-C+%نOSSSjjh\|&E=!+tuoCCr|pD{mٲa׮]MMM׳3=DtCqmyqyɥ˿ycƌ ԊGda)0c:B+ARRRffV]p!eJYv(N7痘tuFWB<-[Э|t꿈{\t >h4Y6E{-E"%""7]Iw9C]*H^CH6tHI: N>ښpСƤ$ϟɆ *//0}8vx:j.,iE86pVkaaaBB`ʔ)e[n̙3VE֭[;L96Ԅn+br34MPPŋ%%%5CNgGGG؈۷oȐ(/|Y+2{8q"**jҥH h4n۶wEڏ>Oӆ }헿%}M$VpD<Ӟ=?UTw]n|O<㓗裏//i^vmDDDnnnUUÇ{Ky:9C{8"M ӧ;\oڳ'W\aÆ]~=99l6šTܵkJxR]mš˄w EHvF'<<#D{֬Ye,YٳgCٳGCU "& h4v2Xm]vO/[4,, Nӫ'g| (L]sAXG.ϟχ f4͢E6tv_Fz߼^>N'PmtZn?qqq+Vkkk %5ґ BJN5/O[]:P,uF̔Y-o;p5!p())eeegϞ-((@:eཡ{8A;:qqq%''`ΝPDyTV,TQXfUVV6mƍ PW\6fPV@'@G C 0f̘W^\grqK233ڬ^_PWl4(N$'ںCݷoſ"N)҂tG IutRE}T:f-''4`ފ2:D ك~B\zO>޻wԩG).f_ƿC:@; EbA+ӧYYY))/-\b#G|+V!Y49刎 ٳ'pl0/C;ӊ8?1 E+47, ٨پ}{||ӿ$*I*E|UB8:̙3555 M&h L0fhX9tӁ)999UUUh4JVAET|PZZƠ756HwhQˣ# r!N _"QѦ4J xyd4,Yһw +.trLlXNXc6ddd6mӦMqmݺDqQl6V xyyyxx m; '`9f6lZFL?O#zj]~Cj*̉&zΞ=ocEY,29kx B3f?٥8q?χ~cc]v ԤjQ^ppOI" ̂h„ls{x.\oZ*11W_=r+W}||ZckfeLp9nl}|}}˖-jq~~^֭Gܩ ['<==y?TLz'PVB~H:H9I:tD9A$PlyŐ vZA P"8KA-Ù=ϓ;3ppi$9RMphS w^ ]Śz$, Éڈ=V6' )\+EqQݖ})\Yp!z҉L_ؽ;:yDqن +064aƇ"a9޶k.΀FXAL׍gEBhJיJ97ӸܺVtZx#t Nh.DɡS3ީ4n -X}u&{O saG nRvH.Hv}9{ШP6&*րDz7NRiD ΍B݀;wg0nܸy2! 3ST[,yn:Ͷrʖ)S /_3K.\ rp UAh+S:;,@9Ѷ}6G~f{jd@(pt w퐬$^#}&77FVٔ("aC' N%3W):h*V6!S.A z~%Zr}Ҩ0A#1Iґ enEH80݉*8,#MxMܞ*D3EqgCdh "Є;8HI:2)$qn}tDqٲO>+6E8Mw-d2dȐÇ"6$28qC+ p~ٜ,ф@+w9Md#mm40"$#$)8AGSGLr#6(. xA2yt~3knKs˝3C17{t$q~2 333- f՚SYYI蒈wN*NG@6MV\\|ɻ.]qj˛6}jժ~ Lϙi@Ͱ-??W^';s^㍿;ɻZZZtpom ;v8wܣKJJ/^ollݖ(;+!wADz8k9?++3hS6SEeVN$4JNu:꫍UUU/N[reBBBhhhQQڵkOikkM񙙙?~ttI}AAs{dиŋNȘ:?%%%t6J ^hvڝ5~W2>}zԨQ=z4'Oݻd^p̙3 &;HM?@a6O:?!==}ԩMMMp 2Lǎ =ȑŴi ȑZ߿|͚5 O-**jgΜѿ̺__Cw6sLj'NXÇZsƎ˔stSO|g;KJJ|}}냂rrrKJJ u:?oX6mڴtR,-Yv}۶mcƌKPsGiY$$$III`0燅xw+88{III2<<\Fhi|G իgϞ;w|뭷}}}gϞݳgϊӧoذ!99f-_<##˫_~W^=qѣ5Mqq[o=}Ͽ[Yf7n/nmmmjj&s;v{Ŋd>IծVI`0466ՋLFF#_r544 h׽1L.]1Ϧ`0 8&h4}XVb47(//ZA!ׯ_GN`Txbܱcjŭyyy7Pqҥkyٳg6o\^^QZZ`;+_~eM022СC 2LC=q"#&&h4 5O?$--ln6Zb8}; ׯ_WH577 pر{.ZYǎӭ+111&&fǎ]̞=Ak׮ }ҥ^glْ=d8]&55 1LzƆ[n_yyyll_ZZe4y m6 /<ܜ_ZZ||| H=N_0aBkks=׳g4FOL^rl6VkmmmII =}xjwsssMMMiiTAxZٳgm6+Ek4x׷1==ʕ---%׿ ]F:Q #F6mZ\\~-IRNNNLLLciZOOϣGGG8088888biM4hPAA޽{''jhh9sfKKK޽o0? mÆ 4hF;vۻv2`v]ikk;q"#--ʕII CHHGxxۻw6p@Vۻw;wz <䡐L]rtܸq\rEӗ[Uٳ' .QQQ-=lcs5tEXtSoftwaregnome-screenshot>zTXtRaw profile type APP1xmQ]r ~{<]3(ƘMfKF? |`l~?mf$ WX`Qqi 8@đHtϞVSɃԵw[-[Tt* )Jnu޺Iw'Tqk+-yPd 690 291 0 ™ioIENDB`fotoxx-18.01.1/images/albums2.jpg0000644000175000017500000004554113222767271015207 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 189 399 0 C     C   g" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&KdUU,G$ V2o"t;@fz Ա$9Ҿ1.ti C#L٤8~lt=W>O1IJэS -RṔ׿#QKIDq"ۢOf g9#]AD9кH˴3$|{{׮\WPi1y~֑ll2-$Zb.y aoS!146#MբcP=]'~$gsGW}wD'YHrO;>*x;eXϊ_zꚤOl/qXaxc5sͱrʰm% @ $տ9?'WYӼj0ƶmm%-(khSe+ԍ׽ ru!G_W>05_=g^o<ǣ\i%恻  ߥE {+֚ޡa{徖C˅wE8f·? JW_ڵO( .h5[k[iU-xž%|WM.g[Yÿ+3n :`~`zqG0lh}'Wy|oMh/6-J5XeioCʰ b8ş94/\zfmZz;r3,h[9ZwF+_w_9?O+ߊ?uo }aϩ=Ʊe-TG"D鷾ki/x oh'iEFӴpH%6I}Rvo> giΖ6/n)V1ꭎDBҵ rhWYXm$wI9ڡߋgeo:-ݖ.-<Ʉ3> LO( 'K{N]*?~&@Xm"U[ٳwǽl|EqK Mu$Lˉ\ÓFビ9ww> 1u2rp7=}*\*+F Z/,6jZ'7V2y?v" >q\_J`1I?b3 %/\T_+F5OB9?|wCxWG0X ooCvqow<*n 4Q=M2?^ʼnX7XK5-B#[eD{N~^rsֽS6?Q(}ÝvooW{:<i^$A7N5u-:x.gu6rsֺGுug^@I[.ɹy!%d\?isǧ}x|>ooF5Mٴcx\+Xovf9,z}.;:{ͥʟweV\y 6a1"G#u/>߈mu ;;]-4hePVH! <;VWxksm)0FA>?a1"Ou=WǁvC >kb%6VEeG Q~ldH&|/][JYyy$K<39Q+~ooooooo<2qX-Xy 8$ %2K+K3P9$e3eu[m'X3"v[I4)[5@8\C< :˗쿯&s7`h5qviy~jmCi:]|EU+~; six); 24?&WfB}&kʦ—&@2IVIJ~LEB~e_!'vםz+x~:޽k>xrO1]Ο`VMf8lbXIϴ|P]wݦ)bYm"qnblW*jVk<+xV,5+#IyPRաϚtA[Vi閚ucw O񜬑 = qKqoxo̖wBq ~5gwĭ_E뚮??Hϭce),\J%UT5O>ގ޿j_)xT-爵OvFmoжYQU;+Nݸ[.|mxķ[G\ iee6)|%3i5;eL Pc(\x[E ϸKdeL3`EEom $6qUQTtO5}^K3Xb"߇7d~bOx^Y'EO"B f's[Cn̢ [_60m?aYQp4kx+TW ' "2.-oۢ/*Z D_UfQEtOEQ [_60(Cn?kx+UEOm?aG-oۢ/*(iW ' "tOEVe\ ?Z D_UCn̢ [_60m?aYQp4kx+TW ' "2.-oۢ/*Z D_UfQEе ][W=7sj[Io$0$As+Qs>} ʹ 2V*ddq^E {mGÆH+hZʞ| =Qq*i/zvp+(!O;),QYIک?O(u*F5M6{ZT9,0D9^ ( ( ;H$gXKPI5%ex[A3BcǥEk&ۭݤ6zmХvUJ^z8# *ƛI=Ņ+k[*RX#`d:pT_|~׼#uvo-P~l|FP}'ƚcktyl/'4 #@h#޹LѽquτԚRT\KݯYIvΡFϼ"}I$^mJ ߀L> z٫rPCm; uD+-v͚K_?]?Mm;o }틧HmOemݻvq]`1zs_ fK=3VGWҥMf;}@ӌjU8er?<-m [Q>ԗL4 5 O[X8 _>$p)73\ׅѺIW/VЦEmWQil./ fP `%F+O׼ii:.>5[KOee;-XU fUb^O_dhY^Μ1,dA3'k~3im5C54qX?in'' b܌b;>g EKKu;A;˝GR7Sy<-#+ jU4^&U 7S. R̛[bv@_7ݥoo'eu`?.C6XΏO-뎆>moe':q[خs{ukv~#jn瑚鴿Kx]<.W&&WH*.bdb ߜW. ZWI>k^4 [wOsc@n'K@&Ԁ̠s׵y x{g4V/-3Op!ܩ0Abln6O=:h_mA&Rg.%f ,bH$qM4 ;oOV/5ͷutUKmbc@'xs25c\-uq>&ҮUıt)Wޅ7a[x&1Mwï [\Qмi]RI\E1kuIC">e@.Z+/M_?wΝ_|*[ cemz.O*{"2y2e.1^1m<|9URB9EC+Aw~ vN*K ( ( ( ( ( ( ( ( ( (2>.eYl![$zs@ϊXjѶwl`M\eY4>KO{UD@^yǭX߅Y2[% wy"p:q\imOv5M6P%VA,(BcFUBx^]`ݵM.l<[*ZdOXB`A] ?eWRuRy<=~}4>;ŘNNiG۲x]c试Eޟئkx15.+Iv"eLI_@~8xUtM/amjMQMIsIcw&C(#AA๬|%nj4\UkN~v|w-ti~!~v>4OU6ZR^H"ĥ2FBÂbmVZYsh!Uuv$ծ[_֧ў#;#Q+R[m+̑2u`i|=;šZGaX°[E77 ZNLZiZj7 gbIm"9 k F>u(\IUC;cxO|ClwgylIchuF 8G<_mv>hݭx|DA-d`x 0Wv9_}6'rJ(Š((((((((tscr#h0y*A{yg=È 9d/xzMMv87L@ J=xW?_qk-[,Z;xCڤ T[kꗖ/ Bk^ŊM?.w&qkVѤΖh%ܩ# @8˯Z=޺5Y5g+J,Mfˏ';6%A9bÊmN^&jW54,;X)Tn#=\ e}SV^o!d hl<,p4#p&$ /H_[=*M,,u{.Y *dO+{L῁ZOEk6ھ5ōzNkWXڬ#'%1?:Om_,u{/Ms/$,2F2)rB hxNsFiGr6̪\q]I`}UTzEjp~1<:$~,w XlL8˧σ elⶎPO NΞZ<[-0ִ Ƴ4=eX CZA5ܤ.2(W %綿t8Yq7I$ngM&]MgM'tSYoh.c Jp GzLpۼnHz 9nO-*&09P[Ux;>l>xG{]ryLoz$&ch18]+XYUM )4;gNЯ#R"HbѴQ2FY@OZKYJ'L+f#<H$ D%7+Y5V9r sB1e',y᭏ *65 P.Mt.lіO1Ы) xt[Jw,5i5՞{sYD<@-M@Mk⮁xZ;%Ğ&hn 61\F{RNhVEMdh "&`o Ni{n/k`gmE˪.'hFNGr(%\n۸t>'|~+^sjVEc$J7mJ-q/WƦXxFЯDMWPbG|mÞWnӵ(."t6}R=m,,x(w۳7;$.;aFoQG -ߑSwoO h$}^ .89[x,Rȟ>ÂO٫Ft|OKGM:m+OuHyiXZ(ٕpiB7\V7+5kƚHԵoۇrF Q+;K!%+E*QE (Š((((((((ciq}}R DnZa^}qƶh ,52;k$ ca؏Jx:M?G,d~O49Z MsuA$r1:_Jֱ-RA5 @.^ m|-t0lk DžQj& _N`x{Y< pr+*VOCq|&ok Zj^2iKK)HT*@*dzp5&cүkgomH&L*y_%kΗ?F f?1h>6Rom֥R`d&VA GOKK4%3m $23?Y<Y>\Sdz?t-ZhP%si:k~ C?i6v}ROi^i7:o$b W`g7 9'?PV^a+]2H[7H&_%ND`=ijq4ٷ(!AQDE (EPEPEPEPEPEPEPEPEPEP{-Lv]pD%]*?h@ڗ?j\Vs@{9G?h@ڗ?j\Vs@{9G?h@ڗ?j\Vs@{9G?h@ڗ?j\Vs@{9G?h@ڗ?j\Vs@{9G?h@ڗ?j\Vs@{9G?h@ڗ?j\Vs@{9G?h@ڗ?j\Vs@{9G?U:{$L)^%BYᰫ#:t^$fY-j\Qs@{9U~Oh5W?_F _ڗ?j\USy5+Kp9!c?R*U` -QK/=2YGh_Kp?.{}*N4#K'W?_F _ڗ?j\U_x? ej>Oh5?ڗ?W^$fY{^Ƞn@ ~}hVg-͜l?#-F q @EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEVK4$ }Ggl4+-LJ;E6`hyG8U._h,5upXݺ?U=λk5QHsůh-U/\$O/[3/&)(UϢk>)[__:\2Ym=nUPur}BO]_im7VխظV 62pM7k?UOOWċ__ٝ7S[MXE|qNӆV# _GAakP/ zƹ\{SSHl"QppdjlK垝cF/<_[ ݆wse6y_ڵȂdEU:e8O|Y='X>7m}L݋5 m),1 #u]_z?<7iޟnOmsee`Ȥ0~9>\.u/wjͬmwDc[A˸#仟b2)-G4fY&[b]4gm,!L4d"~-Յ~ ԵkS+^w&U.B;*yXݚ>)ԡРN#]?JӚ~'TUvlpMM|5k&W /eK++ #dʶӃB_^֯\G6k${=5WI̒ZZ2ʬT%tiT ]x>2 { ރKpo*kHt ӭy׈gwX:ާ뗺$Z>yyFBB|R`\=Ï^swgqcy vڭȵH[1%2UqM[[ZxL$+ LL{@yEp>$%mS+Ŀ2X"(+Ŀ2X"BKC% _+8_BKC% _( <7l{1A--1@=TPfotoxx-18.01.1/images/unbend horz curved.png0000644000175000017500000002477513222767271017343 0ustar micomicoPNG  IHDR]ٹ. IDATx]IeQQ-,[my" ˜v&X,X;`ˆ 8  ؖBB$#uCUuWߗ_<Ivݓ'/^U^x?/NҪ: D0]9j8NB:=ݥ,0ةNww!3y\Ӝv L4dl5D) ݀MSIN$: 8o0&)Śxy ѣf4Kq' |||/ӟ ?>أ1TE*Q:q8{5sis4#Sq?haB!9,{oe RaϚRRvR`,B'Y8   YQStE`ՙ-h=cW-WmC\#y92CIlc#̹5W1]jhS&DяFLQxttto7k~L|,GGh{^DBU\hcߣb `lhbv4{mzaȐэ]ޡp#,U&@Bp`<S s_طz.Pw^Wn}֗SgTQCɨt롣M\1os>Kk8{ڐ5辞1qNx kL o)R I,N5DjIm+W?cǻ(r"A! " "nAg&PJ8a@{ ' GjIXZ70{pp@_2;TO5ӟ/g<)w(|O2.HuxhB(ym^>̊kNښaKƸ[jjZH2|W@9+?ы XbjV:Eš|w.(:xsΝ;xŒ3ia;ߦMPEQQb*`Ti G/x>KB\n݉ƞ@=yGMf*LJ3`3;Eev@i٭ЂSsUxC+_ru#?yO٭&9~x/֗9V"yӌ-'RӊǏ<~!ӈk½"Xˬ /EeSI}ufJhmX3 zkiM#?g8֭</M>L7O@Y1𴊯`J! tq_}"Wt9/VGx׏ن`d{PJ4-<6gJ;C<Օu2rD~"uި`K>mO\ j0RM9ko|[_|vc'uJRڸsȉ7fZUg-Qu nsYwvϪ=~W!}̔ ;rYa3tWPZHoR(NopOee23tpPqFO"CلHP<-ȵҧPZ<ư[s?ztt._l헾w?C~>yy'AE6Bmh#VyZHGlʳozN,+;6J+0dn?B)cƤ 0sO޹׿oK9>ޱoh˘CI^2ibf}9J^y8_N*TVq-zɿBl媹[ !;Jݹs~|۷;qaNӨCWϸ9 Z,;nAM 4+9LnSiaקּ[R !jxj?̍1wo\Y:;pzxxp\/p=I_#TW㒚!Ԓk§^HOJ6tFZbpsJ[4cP]_:>k"γ&3)uRW Ʃm820 uq #Uͅӷ{+9ڶo7t'|1wy[\%de98P56emc!xkM{[lrW贪pӝn-+> +M8GP#gjkC_9}p65G+Y-‡n~\ʣrT{`{?=Y|/> X^, iZsĎ땪jXR>#]Qn=S¡4W#YCݨݶ7>VYLbW&JӰM:L-M/yS;-.*nIV̹>x"|s o%.U{|0SY\GGaxVL"JP$L"s a'[k39CKVT*˾R\>X&%[Y?p[oyO&lyeP{r%oCmݑNJ1L&N,XM,%BkslS?xZ|J|{wT+W_LV.RUx,ZO^ϚS\IC@W): q?9B7ݤs{o6[ՠ͗[g(a5MXXj\+f4)D\gE`RVXhV#tropz(faTl3V>#F0D>? CD}xSNQTzx\{,f8MޛˌZPdGڥ(x"аO0?O.<)x)`.|XMP/GR$y.[2uvࡊO z$]<@ݙ5(Ʌ!{|5x%~qkk5a~O&@lG>qFA'8ԴN,1]A,?Y' T ?1iy)9ZgęN`NMpmtO}unYQZcidXe5$xypM .>jBI[j$ -qwKl@F 0:kB)/Kj1(Q‰6šTN-xȓ V~P墷4LbQ9N+|Gi!VXfɁ>9hTmkc&*,#"F{-H c^ [k I9 vRۤĜh=m߷ٶϯy=dɲQ0%Udl#lf˞B~\igb\ik(|@Ù/ڕ&Cj[N :,MPwB> hq<=B8a7y{ V6W@gPy$VZکIaPaa=JkbM]LKË3t;zjv[(63-c:]@~cV 4aMJo[p>!8Ow6SoɷjjB+#WCSWbSALZz PjrPe1)'ZZMW~eR}b;T?ȾzBrݼF[i Рk #GqLE"vLUUƶJV9 lB~NPu}dcKqPL$>tkU8wDVcJ3Bׯ_>x .}Oqݝfx; [E-{9kG~[;nSO%*GiQ-ᄯ6`9y?÷7Ϝ=saWY.>{o,1P:0$k9) ՙN7rvu(*fapRoWF*J/|;00;ij/?oW]`,>{gg|6,&OP7Ϛs?txڦM[pgVmR.!kd YQƽ6'78'/EΡZ,QM$;ƝХ+QF%>1^|>s6,ov%c,2RRLb&p΋NsHѹb|mIy1&M)Moq$ľ6;gΞǼuM%d,jg 7aRozj#c8*Þw#p*G u&㲏SVxp?&`2Nϥ&,qP"Zwad"7(u eSMb[<| {SxudZLx{MaU64 55uJ,*1#l["FOD3s4+=6T"7/p%ܛaʵS4?W/]4nĔZ-yRlm*e/'IkMY59379k+Ϛ.N5✮ڱ)k'r}{+䕫W0GI0鹃vLI*#l9զx 9(T;1id+z=Y(\8Pk6Ue囓7^<9tm lؾ1zIIYRɪt7m:ܶHs:@W?^FxE/gտcA /_ϟ?wlb | 1ArocOL ΰI#uR"ABDd]-&U㲬 MĻH| ukDCt#˺г+/3cYcB-1Z=EhKp2IדVt\e;X GzW[qr!_5Kק+vL"mVv4IUl,;l \8Sml MJhkucү gɤ"1+t*sG+Yㅋ|+Kw@ѓt%\D1) czx_B8& 9MX1 4i Vֱ.p ^eF>ջ|/_tS쮜 >r5 !էFx& kA xBm ڗMAi C=Wk%&PɫjŒ^@³͛7nN7S|.^|bfۘ& L^sڻj|(L̦;kƍg^/k`?O: j@\K2&En'˦Xag x%:&ZId}?nJ iPA¡t 6E0T>dsFϊr2"z&O{jLVhu $2wZS3/o=6o{+R]r^>,n@Α-PECP~6h't9t:WOu6Z|KFFldj{d(^~Bj);;xXw7ܼOԕ;WnC#é#ezЎͬzk#.| [} IDATdGC/ C")džFBq/|Dn-Ywv_]}胟vFTr,g ޞIř ))LwLa{DqH;/ZlCsJI%/Km}:(V^qVg?[z%2rY0t|{Of@eXs؉JFQgG؎W̆6YF(ӔQխlAJj Yi 0N;M2h}BΕ+'(7Q`yl'Sm#xw+?޺e/p"-;Mtbt==@[vNcp-jxl ˠ͵Zi&v)sT%~ a\+ fqxgUrq^_O祐qWr/tb$i37ڲ$'VqT^g:ӟ :8k6,֯1'rN^(,}},X>p/ۑ"Md(|͋x'>?߽x⓸x ') RZi8E/RvbW:7:lo]z#paEOjѺ[IrcJ!AXQeU&P1 C0‰á~nU<4s`>F< (#IlK]lLvjS5O1)P%ݼ&))AM\\kP2r x nü2u`' 0~еEi ^7*)Y|sV Q>ąʘ~FsJjڑ@C v}=#6Z 2a3m?EL^4-A21QUvK?LjU(wpa2>?~#w G?ifk00SL59 Yb mWjd Q8=_n@+/A>&(\. ,`l)m'18RǪή*`s2D)MS %|YִAߪ1mMa䅰N5]gJFΜ]R`C&bZ+FkљLmVjmz2 b2m?L6zTXtRaw profile type APP1xPQ =Y4Yֶ麟<_{}e.{yM@DG?"RƎhйr鷳6ȧZ3r|eQ%E >^z{3#03&6VQ9ȁKOdRo5rVd:ɸ"6]", ⵜbt>|l![_iTXtXML:com.adobe.xmp 128 128 IENDB`fotoxx-18.01.1/images/dots2.jpg0000644000175000017500000007371613222767271014702 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230K0100Fotoxx:resize|tonemap| Fotoxx:trim_rotate| Fotoxx:paste|paste|trim_rotate| 5http://ns.adobe.com/xap/1.0/ 851 1055 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? 0Sp9`uT [R RjBvINvCK*dbjxSx+2j֩FA'sS@YI!R*=Y06 /g|biIjD <)j/No8* =+мmB@; )/<_ȿ0mB.*j:o0fYc6wm[g޶k.Joz>!n|3yK$7xCZDzg%tWM>2G( $mR\^1]è[ɫ\,RR283LCGuK"\UMEQ)_T$'|WYjG(DuNK yt mt[/ !=(/ᲲؘR'RzTm^+@D(>s&:i>(Uwv7 ;'۵[XxfFmSgq3/`2ǯ\RŦx@ö_$Y2c,ziW|R+mqc#n8ܧ i4+#Mi!/ dJW_&6 +źEx^Qh.y%1i-]!xUے;OO,f18څ%կ"j/s)+xlMOåx^[ ]j0Aoa]2g}1SN+MűJnK4,>_/~Բ^'qHuRKY>RL}[X; R 56.LP|i4*]FvpF>٢l,:gek$7z.v.IW'=*(x͹-Jƪ" KyxB[-qf:2ଌqGs0[k2j7qG]=Z2:JgW: a}>/A-wW<9~+YZ~ALc,sHt_ilt u$ԝ2||5`i2׬-Tڅs#s߾^2gnluӦ|Q5-5{+ֈc.?4Kx{_!JɇD^Xu?5 lg-^7}B\Aj]-U8=4ڏI[5vj qϡoi ,kh^+OIFy2gym?? wW éu7+3sw87v5M9iMleφCm/\-vP1'>S/ xI]$Eg#sҝ/Bx2MzV>Nħem~rUt5 kWQk' 0\}`R ]v^g"$}jjGSGqk~y,l#YWl{dMk^-Ƿl NIjФ.Q,u{Bdvlr>M;Q%um=68cr3~O j~"otG^x4 ӹA֗N}^QΚL2$d04}5b[ zfrrԗ~5ZYig0oN?Iƭۻ?ͥaڗIΫ♮n4wz}y/&okmizT.pe]i,/TnsLޫ6 g면PvUA,j[Ok\Yi@1QKK%M:2D&lp{t|GkJ/D{ptTi_ӨZX~ u=yLqQ8b!8u?7O@2:>O#kvwrSs<1xWM[;_g66FGB{5 Ȓ~!]j pt(BӼ5Y}n>Ƣ|/xN)4LnbV "QƫᄴK p!1,(_̵AriԯB{YY-@AYI?Z,H n틵TseO=󥾥?*8 AlqNexRyFw9#8{{S_̾4xRhvr&2T}E?PtmR;q#,ly8O=N=fK\[4X.PpTRI Nmi<%'[H`]=% Դ 'Ej#'ZM.F@X!O%tIdZ?8.E='S -"3 Y4$+3|VH|zTdH0=5$?_xrTTXvzmdISW@Ds\[V4SN )֮‚ PY7/zԍwSҰs^IaAT/1Y6FQY3Vw!_kq^^/]lܹn篵z#W&v]noJcnotoM%߄m!?{E|~Ԛt EA3ʪ]cv _@8[&3.sq85| 5o )=KL h >&^g88ݻڗ]׆t5M~fdYv7nƳb}nO.sp88 &]k-,f8I߫w? ]RRק$.55}]Zx]Ex*^h,yr'A8:ָd$8}_KwQu6^!άW';TmnKՈY$V*$ =OZ<>"Kj8>ci_;{sLt$ע'`/FNWncwR|EgZNy -tOZj-1G\mZj:pIgYX4!}#Q'{ G#TYsS|A"(.s؎A[xx6=~D7`lthwEshF؝Ϙ55 ]xKhz篭Ί7I$uFd*V,|%s>{y5ӭ#YJ\ͮɭR9ZMKUz_RԖ: y_xr^LUtdqލ/j2(c|#?N nu Q]|jU4/ zYKEgq'n=? -)y"m7}\g1VU.}MnmfgG0N;Xm]smg<2K9_~!a;kÑ $m$Rob۞?Jt>Լ2)?k LP S5/Aai-u6Z$:Zuo#4 4)^oP—MuclnS 03ݧ,O6VA?Ks#MY\3[y| QXm0Z͂6}0I 4u W]]Er+Q4/ēj+rMg<}(i1%_5vpinsҫoR,FOaI@d_r7*&%8`k.&Ɋl5~Aծ$w" G2i 9uKpm&f Whn<;= 0lTn3F3E̵jIIt i ?xWX,B9e&% ] 4HI x"mhCq5H8^xkA4:46x; 28F uׅ| Yg_üI9Ϲj~,|iu6rSdwWGX[2rH;x⢶дY|jD]5de[0շD׆t3–Ů..H@eA _h:niғ,+[ D]Ʊ[˺8EtKO+NN[j؛~t?L֒[J.i$t+ ,5u7Q/' oBյMCUZۆdޫ@95c^vX-~z\1|Gi=͌VZwq NiS?u[ÚΨ#[f?8]G|/^VmKS5+roz9מqhz~Ccgwnꥮ&zTOCl|QmEqhw^>PzDŽ-_I.5-Y`ܰo 6\|3jԯLAuo-2 Úd3}Ŏ@4 iw^2FXX.sVSRL^N~ڈ{B_djEpミ\cDS5ƴeo"Rg iƯwZ1*MsXZբC20IJ:--5{=\. |=xa˵ i:jvFp>xGLɵVyW"f%`tx^ӯ"dByb3I{H7]N͉3Z9zxsfM}Oi!xLƛxOMּ1&jvf8oOjOxKUmg5WsOW}p)|A/XkzEZ^HV!6Ap(PZJ~x8kwzyEs>|SCLH.N3xYe$1*Htj>1xlFiD uy݃LV~| 3؊}7.x2_wkoI[Z^g%T]y&Dؓ~PԾ0t^i,wɎ[ޛOCt`ddK3S뚴z X͗h+J0.=6&.1.G%Hӯ. KjgfšgnQݪ<2MۈuVnZ'"tr8ǭk^ee~~6>f87X|%m{ k̠nvwjO un.M ơ>vwJdwՌֺl7,A[7Q&A 4ŽƘS,OT:{q]Ծp;Qwf=`n؆L`珖=m魨Ĩ!!έ-’O<6:g&rJ< M+x/3 9b.xVƷ7Z.o #zkb-pNx /wkm̏qt{K 3Xrl~nHqNlxODơjPjKX2mɳx^VQŷy/4 [KOLM8zS5?¶&.x^ɴ])l#45}t+]I 8ɱ A:].dNrqD7vvanA?3<:f:/?>KBֆu9M[ixZ5&i$X4W^ h4osEnsI%v,QW/P,`laڥ:aTByfn:ɩ`Yg|v^W}j6x_)ZNb% fS'h[[4q1Oo,C ՙPG5 $&60ĕ5g]uM3]E`g\&: {)?tv,[QK|CDuݼ}f?'|5QO$V#='f2kjȦ̒- _驧ȿw1+׎i5<$ 19(3x;pNs}F[?q,͌u4EV# 0n&{^825T9L]X@8G=iws뚒28Pd9#guVKyo )1%<$⛮I𞜺Z8@h'<3M 4hʺV^dSޑD=;{ d5j$ly`}{=,h2̪>ټug], yѐ >׀jŽ5Z_𭿃×m{c`$۟'4xnۖZ&v^yuM$ +S/#5/g;GhH~b\ \|~zV6~f -7kǵgiwvs}$g %ǝs92iM6w csPG{y(Co}{x4;2f}!7ZZ$|?a-ՑǝqIݣ~c*/olv5e.|||dWsyB]i]%ۡ m./28ݜ '4WO ?3jT9_>t{6#MxgƗFd AʶR9T~KCasľ`4|[fznR. tM]%cO=G_}f9O!E/?.HOصKٲj sPuOM+=,ǫD}qzޫ꺟<9ZXim &rΟ${٫xFd[p9tD} a?d® 5-E-5 SZ5KAش5g%N[c3q+m7ıGg~514GPxVG&.˩FL8ֹjHK!ifbVങ_{Tڎ.g=*'}kqUû:vmHuW4XAć[P2"t>]{%t/e>ԂN闖V laBB?<|ݍ_vDrw#Wwj d?۝wngڤյ \Mƙ <~2@ԡGJ(Em5l7d[[,gҝj֖vh@54[ԿG@L#c׼&MJ*v iɌ_FX[vf=U ;s$SQ>z|Z56isP},>VL!;}4Mwv4C钆!z|,|u鉣Zl&_aH%~\4/|ui•OsM+/jWZr~̆ A>ºuNUe1zێمv۫1[C1W>/uO26e,ce73^xG\sIi~|sڣEj{xKZƙ6cB~\G]kڏgV>h˧!_XԼ7.l*EC<|'RJִqs?1#{T>ռ3gJUo(;x嶚GPt;2@g< ||QY718-늧jgyڜϡSlOE3ls#f/\ƻ{'9Svl&8E,8ڪAxq<=ama\.Gjxw`4%:g~[.Zo#n5 7m!yNqi𕿉l]Xo9x^izݣr[㷡zo lzFmХ=Xϳ }QIPL _ ëE%Y2MxcU}n-^ە.k?]<#wi`܃rXPòhk$߉4{[UkxՌ2՞.< ) l$̎IexA>Q9V'Tk#*ү#&*򌏗?CS_¯->!I-[k&멠_~E1֯j׃_?~̪{A뚂oLCKv<#3Y_I|l"X ^wg#zTfC7lFnzq(r Ox@Ӽgs^iXVD}4YGLu7vK݂2}IM<1vDp\KpB 牄 ?EjNψ-%q4SCҬtB&H̜ƏM46F[8K*2z"}Y_y y?zjZ]iksup[\#a-i1ig$XM5K-FK]*+%ID?wGsMUy҇ '~7=ޞN?sOCoaמ4|]'xM6F1l~,b!}|Qc1nҪ}Eڇ42LW| E/|K[Z}}# gc&W:;} >aیQxIunEr=1M%ى\ž%ip_-3,(q d{Ix{XL]+Iԏ8rdq,tjވ^>X'dbNj4[>LaW\`SI-?${K_g<x8gx[:NqI{Gv`(+nzL:]^<RItex@|Gkyg(y8VֵQj6Z@J֠ѵ 7Oԭx%l29ǒyq?*Oԭ,9& !4Ir['|Z0MIQg4i<`5Egn1ңk;=KHI\7އqO,m3uI$8+r~ ?*4+EմuyIL2}wNm"rjW:74R̜l8[&&HVwsUn|4 W\mf:ey5;iR?tx{W4v{GH7vroz~UyošAhAljf64Ww҄Qms?zևw.VG|O~)tM*]$]$Iϗk+uO Xi6Jsl̛ c֫k#5M K㴞y22qɧo"^=. Iu='Oqg'; t/RԴpC`c秮j麵٦&ujxMֆm4e"1~j]?E~tϴ]JIO#{OuΙ}fZMse5C~)ҵ:E[x Us:FM/TV_{+9FI:7t *鍔ec8T~&m5ţckCٳxJNAvaW1psӧnZMNj,ӧ%$ۥ`}j)ǁƈmeٷی,'o46XŘmj;Rx\uXnѵ,H 4c`N]?P6ǖ'O5xgV6K~N(h@Pݢ]I-dj^wqnЪgȁQSg-|Q%{yaE3H,_%HX&w"QsJ tCN<|s$EZJ"q8sOGS6E_f!>Y~U'>)5[nR35%5O n-!,hK}KWLJtDҢG!ZxĐhW܋e@#ϧj@ ,zͥ-L^{MwDӴ!'2Ƨ-9ψ$ll-WN_|B_J-{D^⠰b 7Ӡ{PH|8=Vk"m},@m~LڙϮk'}a G>;}?z& Oni>7/ҞKVU89>5kgZCq0&>淵/Kxĉyp N;8Y>W.,Ak<36lJy4xxYV[ݡ|d;5gRΡkCOK%*K9R^V,]@2VXOǩek[\x/ .ݪ*:qNgm2q!U؏>$5x?Oy3is?K_FH0 6x mk–^F1][uL RxV ,tm=# 8SY};s]QPA'M&YoxEo*?ҭ'|gi-3Mżgtc 5[ž- :t6DV~r1Μqێi~3}+ךi0ȷȅ\ZN..Ȱ(9QZVM/I,aedH{f[[Gj(0yՏ-tgDl }ǯZ_m i!#U~z5ëx V-"#&#VǩsҬj/ sn1)|7o 7S:tWp1޲yRӾm8 xN.ͫ\ }tKxnq4xz#])|\}qsڑn>ni( ̙xPxɧh [+\vT里:%M]f8N -k>DrA)b-A};V WF-v@ `ti`62lĽ1G*c_O5&IhegFo3&63UTӎbQu C{ 9O7ωFO_nzVu_xFZxvcsddS Cv' qt hb4~TW> kg`[Z4+|]{O xj D&eRva~'ZѴ(ip@lH慨;(\wf=֧׋-;jV:,wm|c8;Tu-n\K1_\ FؚrB\N82#mt/_wH0 ֤Ѽ[mRBF2\*1g7-%:2FzU{ƳxYtfH4 9Hp*OĵMLS5#"8$jտm< <76q{jѥ[KڮO ?COi0sX8.߉Ő&mjm$N1ՋEyFbZb6H!p3?QK)_/T04Fr1$z?V2px]4_$ҧoO׏nA 1F2+#xzjgաUG@cSA^2x4@xtFV*:0xn(D`8lU|G\xq^cࣞ֘ Dz>mlZo_E񎡠뷞":B:jJ;[GTw6`dd $N)!4|!}2;LzR>-_١!.pvzyE뺷廿m 2jHT5Ld>xNɤMN"8[|cE҆RsH\mg=-wB }PX5C֦&]J QQXkz6}MA2KΣ\Z: FEFU5_XGw5CvLvΟ^ey]q[wo,|%seO1Č9_XԼCt Mk t'UUxK m5!q^[P{h3}T1 pA {._CoZ/,du}j 뺎o6mD6)ف՛3]e9{\)t6N_xTx-K 3RVru @RXB㊖ĺx-R?)r B=aG-*>bmŸIn)N;hZ7|Gۭ{vwPGWDԵ=V(JYIb"pb`MXp$%aj[G>4r)ӯ Iļ+qjZ ?AI.ziw>&Y$Hxx 58ĐO|RFu#?ZwnȲxR]ʠ$jL,)3%\ɦ"<B4aiMcGZV2%%Ո H3?56ƞ ZNX, rjnLoZ{EQ1s/xZ*3i%k\Bf_KhM0n`~uO ]y-}+ 4KEc|u^^Is1r1QA2O es[y_8|:_j'6K"$Sj:)Y-斁_xj $MT.6^xJa XGzuR+jYZZͷ4^X >H`۫Ǧ8 .l% KZv Ow0͹D dF0sޓKS59"i96QwtF\y8oNxKxjiQX]9b^STzD.-.^۔Ȥ=0}lxO}%vjnJ41z{x# \0}z)ڏmmiJsU>Ms> &ҕVȧv[0}k>)'e?7![1-Q4igqd\rzQ_j,;% Qt\*wZ%lZF$B`eU*zߊl4*Ԑ y8yj,֦ūbʲ(RAگKxmP(P7jۥiKO*_Km䬻V6quƗV6ycJ.ɮ [X1!|9'ՙO[ξ1J5<VM. wvz~ʗ1ƻWJ+?Yߋ|QvgfD!2訴:-;|]g47[pbO3&S֖Z,4 2j+k^4G,Wr[7hŋ Ska:o " }A4^3Ux8vw{ziht = +@ REo_;X Ua=^A(ƞ6$=] ĹٹlNcV$(1,Fۭ /|qcXHl!M+ob5Ir0I ߁K_/0x񟄬An+ڤo (Ẁ߅4֑Ɵ)/zokG}Ѻi# '~}DVžW |D>\3[/) l T|GC9&B_fw\xi'1孯;3jx K/?9'񞥳[dTeyRk B })/#WZU*G Eѻ^x|4(4NpKw5f=|Aw%I'}0.*xi9ovpA8a+/,i9SrJ5pXqZ[M2`GobUeJRӎ:~5;0i'vЪ\`d˫?x*=mͪ,[~??o~NV7򤒯zm߱Mߍq,Ə> W5jVF_Dmݲ;6wc 椂Ox.-TC>_fx9j\(1xF`zϊK K"0^Gֵ"Ox:v/.9unB[~xTig ]pv# Ȧ(@XbYIs|t튞 O$3YPۗ8<]_IW\y5boXK'|.cOFCzIе>AƐ-՛1_Сe>LZ  [ݹ oo${ 5.kRBZO"9|b"A#IoyC7^#A99-e Q,67~4֞4%NT-ަs1x©'|5V'I,R)&?1WnRg_Y&E%z |gk6,GKH9E!l^GUԦ%obieFo-$skg~]U9ƾ62#n!RΣF[ ܬ> hD`1x8=FO ,QyXӆ$p{TgK11FLcaD:UgΗ?jRWΏԱ LM+^7e.oRGN(`@OY/BHkM9^R?G5}-wƖzff+zR]HxCL}z+e}A#ْ};^ S/")7Cҝoxyk1GkH˟1[}0J֠-.q;yyCZ97625oC0Ԧ` Jj,1e1z*W{ܸUgો#8SγyAkc͓w L6Jƽs15 }fb^3jQ_ZmIGO\izRVYϝ߇u3]u;I >iq/ Xeeι5v4G[/ ޛxgZ^.6NH'wmM|3xSOPWڲ%X?Y'},t/u[ܺA]tҋ xZ֟Cm L<-Il(5yI9Y VS{S 2uIDR:eO<~Mon[%o'JKψe5e\Ə!LtV /Vʑ9<: 6e/GV* $FyIK^B+>bեϟ`Z5ic4?XOF jJ9%^zsRGZ޽gZ뮩p5%DŽuV Ire@w U١kT." $l8>QћƋ2"3bYQS:X%$Kk˹R@]jCRX 8jy߈N' ^X>#_ko$ΑHgK5)|o_“S᫋]X}UC' A;Im^]rEc9x3}rC%9$ў~Zkn Լ; mPppr>xZVyBI4  LV9dwD*8V}|K|5X,1W7szׅ71ȺcrA!Nkk#gtD;U:L"|N㞝j_p;s϶wǪ8GXOu߃n|96ku9|ּ0̳H!VY#s+)EsXƟk}JO-юSGzQ5֑[ G1F 둞kf~BNߥ6[1 ×$k{m_O宷'5CP縬C—>*cW⑔|H#ֹgY^@UݷJY1</sv3Yxw2c8Iᛍ_}vwXMzt7A07nCU6V]υ.ٚݸƒwz׊.7wHrBN0;u\¢;RErLk\Eq*JMeSCҼyDť{ ӥ;D}u>]v[lݞNKR<$`-ӭPHrAj|;:w{I2_*7$=:Rxw~, qۏ8[&?fXn \ybn֎WmþMsbcl?/|?@4Oȗ6ńdR: A<7un-ݠXq"q}tOk~kGysM.;G;صp\\K,4k! w"E{4m$ƮBŽI>XOMLqط9 λtp[js^qp]ޟM-g;WW-f(؍!}X> 𕍬:,Sr/n!odZu RdGyu}KXWa^ތ\/MNߥ>WGm23X܆NAM׼uFF6ګ3\S|Nߥ$w7M{dd\g'J|3|uk>Z&P듀@Q}Λk? G1gv@%~YyGK-hyiz}/#wiOZૂyISk,GC0z⤸ϞH}:wK"'gߏ01o:h!ȅ-:Q\xu<I4TZ=Mfotoxx-18.01.1/images/gmenu.png0000644000175000017500000002166713222767271014764 0ustar micomicoPNG  IHDR>aIDATx еUU("42(39#Z)$"5!i94M9Tva,S$36JeDd )w I=g?r~|5{k}ݦM!;?"pC~$;& ?Б6%^wbpJB٨̇|ӛj:Y#&bbd%>vr1Jf>A\p ei!h8 QY"Op9 `8kk 3G-.-C>}7_+dz~cYQ1T%%\!Lt@РdH٨f>AYE2?-V Il!?f>d7$T2Pmj1jYZb٧ۿ̇C?6mlTsזE5M`$iÇ @6zÇ /> g8,@, >ȧ-i3d,̟k|i/-!#6by ec#AI 9ҦJe;3E*Ûm0ږ9Pnno~k~kmߜ f>)s=}@b#{  x d`YwQnrڛθrx?f>XIr(Lx"[NXP>(|T 0J9 fO"22sJoHTg *_.ې2EYcO7 ?&ȗ,spw Oæ`A$pC: jL>%å+xPm/]xamfYfc=DMdHpA9zZlA۫ 3_ Bֲ/+ذNWx.'-p754d,Wl8X3ٙa99B>r?q { +ٺ#,kl0S%ͫtp: Kg?DDŽ0+6xgbi- g ,ƾfy4'-L>$݌ @1Ⱦ@ō E(K5( ̈́Z<[llmo$(K276"1K =__!\&74H> 3M8WxU!:L>6Ȱ"ڎmDbj6u86h|1/U:Civ i֚@v|Fap~tN6 q8q淐Oh:c='ibQ#cs0&ƆF%,SW o^+"R>U%/@J>N{p# $D?e%4̏%y&$%n)c ƴ^cُC>.Pr|5IB*vZo}l'#{'+Kߒ 8K&a#l\*YhH2|(/Z)*ܔi`=Ovipn|F m雹؈O) s>oK- r*\4O~4K;L%(~#᪙k8 |V+8ضq(em8O i%1Fk;\c,plP " ~89)/ZR0쬄|G`%m|씄9.06'AKh8y):/G>JOAο7pWE6|)dMI8b ɏe?7[u<:}H>\IvD-ȵR4QtI| i)E^Y (1s%9rΜ]]}1w - CBvq]0Hc !ߒT}RcIna?1 ||J¼M~-|篈 7O i3МX9,#?n,+Csa!I]Rc̷ UO۞w-+Vu].Rrq7X }8iY8L!2KJa-o u98Hm2j?;9TkH Nf,voQ:wL@?8GxpdJ?38=#|ښ ȯؖ gٵw=<.B$'"sx2.X6XcL|o!q8RIm̟+($b h,v;k#aZ P|&`&6in[mwA$g]#p%VlyNb~M$7dP-)%IE9N~p 6V|=^lr@奧wP- Sf%{-|d>u`H~<t"_NkB$11ܦ]@> QY#@k[ci!8qtB{SIR{dwvW[ 2ߟg>6H)4 {D 6)l·OWK>ݑ|nn,[b7 8o1_!}s 㦒@%XI31RS!z̦t2ͺ8Y8ITM%[&MR wb! ]!'Ac6\HhL~-+#1L$Y 15E3ņK2\#&|ȟ[u/J$D82$_Lj!'[wهfc'kJo.`Fa|&S+ђ3D 1̧- H>J0~2de1N.H o;tc }*=Ӟs#-63V!mvG`FlH)dd-@z-+A]{k6|]]tZ|8n#g$1Z(K~Ki][c/-C7uiU,(zSPjωK#eB>Y`#Z^)\tr,䓕̧Bv'>k0풌.mJoc216 U|tD W T<\o辶6$_ %9*uM \"Y']|mHΈ#i)DfJ>ݞ(Cx"_NxQ{"TZu9f}| ֨W:Ogp@* g VS,@@Xbs/WY[:6kkxϭG?mXi|!7']lzy'ȧgc%U-E&lHzep+ ߐ̟ bC>d >xN0lF;7 ԦB܆oƿ 7 +){3zL -uM'\F1Td1ٝB?M>F~CyF_4xچmuZtM>\p$FuPraV zsBK{&Z=*bæ :[E~4O!cgpTmS-6x1jq4ol`$N&sb{[2olD-[\g)1L \&SJD0Z@>@\b׮?].92El0tn㛒Ds- Ӿ1 e@|GHY8(KMe$Q#sh 0e[ls&QF/`qA,KX|deJ~- %_#&S]f|Ɠ=*p1C>μVL>bȧ۴׈Y~-lÙqtIL~Q#RV:|̣f>Վ6%襒OK 7|6"8\X]z25@YQ: Q0iP6 3?ik*H>ķߛdoheiFOu^ZtP_* d/B>iV>1.$hۿ>dG %NݑDfi3K2:5ZeBdeFL6zJ!8 ^c{LG@̤Í|Eg_"ߙ8f>967.o\{=oGN$]SwՃ l2ז"ǃH>zؠmX@E0Wی+̇ӄ(,8Ɏ11]inӷ"6hC{79\c'm ] ^([sBSph.d?e)F&!.8TxJf=Sf]jG)dn(9{lR?Vq}ӲTRi Ю| ~IJ̧ITL3KK&fd82s>nҍsN 50cԻxzd>KRřɟ;,2l!4$%p%zNp.gm@|J@[%KB:+[TϟX} v 0Jycs[Vu9ȍ ]/p@w ɵU>Rf|_ *9Lb2Yap^ZpPlgd_q0/ }3@? זf0#(' 0H>׵09Opl<K~-pZ$5:͊q淔}{'pp KuEy)< K[_쀜Ds6rm"'8cb=2'!6%mu:藲-6`&1%!Ie++lOt3L~H> %Ę|]E>iKa- 5n3Ca<~u% ! NK,sw:NBy1^ѭUҢӦ716†ߑ <8Q1J)ʜGqtK-q~Kc5 k¼ڎ ~f c.ƸZmЦ_'^(ȷt0`,n7bP=;V <8&΄Z2?n`I/N&`dз8Э-_`|l.V3A"IZaW ZN[`b5qV$'>mpޘ@1WJ~Ɇcc݋| \yuZӛri82ԧs F/8F sV2l0ZVBɇDڔ6 F/ǯtB0 jm4P#F*h@!khZg25vC'PA1qK-cIڔl`;3b69fܺYW > "85B.+Pd Ȇe3FXo\Z@cDyGq19B.<5Ӏtf@f,-pp;B0tkK 6bc?$99B|]rs_ cp&1'ur/?+ v,qIvp֨{38GmĔJ2c61|S+0惠K|l|#B@o |Qp DR<]?S8L &)18-@g̴)mxGlD%`jG =4g?<Ǖ\nJ6ŭ0*&3]"&5JޓNߏ g N 6 c"!b|=gWIdpf+_n I&j,N@>bO|& K6Х 6mڠ!>z,EOs#CN`?$Q@)c` ЃL';h;iѧmo^^,~/ _7!˔|$ !BkTB V<\N,%qL"d%SmJ.\F* k4qΣ Oɠ\T_sͷ { +y=pZ0vZ*@J>B5_;0t-"ېkC~|o z&jh0]&R=Bڗnd=[;;4xv̧*4j 9~Eׯ jwpF!%?Qv&9(CCDŽUy}VANX_QF7"wH>Sk6Ct’r*u ~c­B*&Rg8̕Ich-!ϗ4E]mz.=WWxxarBF]·DžrN! V WnP6|z4G,f~JU;}]`9/`0*@DtKB T$%i6'lԖHwK;zCpQ)Ct0S22 |k(\[G@6w v080&TqtkC3cdlbAVBCb㒠ܑG&w½tR+4 30$vZ$bG|tk1c`بǷ-;ZgCt>!F$ %׈ߚ1[[271Q#3d>mj3@j|E:vToGg%?0,3FK-+K; wVndo"~D+ɟ R=oqDXZjem@%bLݙxj%~i.ac;e P1& p,-DzPIGV].}f6|o=bsp.qMO7\[b.j89'{! Ć/f~kُV..N5 6׽qn`u3ߛY٫`<|ۨ--[zG76Ǚ1"-e%ù-D YٻCL-.F겓ʿL L%n(C FzL X -N#А#{J2ƲZ{6u ƅ >sN˾'r7v,Ab8eXIfMM*V^(if%HH0230f0100Fotoxx:trim_rotate|resize| Fotoxx:color_mode|retouch_combo| Fotoxx:paint| Fotoxx:paint| Fotoxx:paint|NEΚ`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ1iTXtXML:com.adobe.xmp 256 256 0 -2{IENDB`fotoxx-18.01.1/images/fix-perspective2.jpg0000644000175000017500000011537513222767271017044 0ustar micomicoJFIFExifMM*V^(if%02310100Fotoxx:trim_rotate|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 600 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?$SMwjkT⛚p@֘ (4Ă F ;pL (RK75zC(҆K[~%\U [C#)5q[$Fzp~}ǐ>UXHU[ : VOMu5T&dh2 Wt|InH eAk?j?&/?SK۷ŕR--(TZl@yaIu^MMc,sz\F?'N!mѱ/EH4 c%UfD{ҝo%rkʏ,}*Ԕ׼:sG^94]G8lk/ÿ dɮ;g5E 8#u_xm}!>D8N:\^F9>Ye&h Ql1Aִx&Ч #6/; 䎞{*:&ʖ*[c<G:UcWml 1*NBeI\4vKupN 3]_SkaRIߵhm*3_WI!2D.˜NE{=F.%[4f}i%nqc3"%tf9x?B@#M6Qll!r@qǧ+Ѭ:Z M죰W{ɩ;o<g%%/TI&ӭf*HH% ǩNQnG$J)-dKyq]ԇțrA|JVo)v!{n'&fi#]4G[vo_cq g֩xOvgzeH*l(`tk^Jt-kGkvn?5ao}LnX6]A_ __5O}wx^?c\V-uNک@Z"346E%32޽<&)L7_njOk5)pmZ.gn>z%H9סxsSuYhl$0AȡҾz?[SüD(%[m,emr?y_ #}SX>x%biE;T`4F9-e(FP'8%q׾5G,+rשxBI- dBWAF1 dDqnH3Ӏ+NvBH(SThs_ܷ?kMZ(h+ܿ.qٯ#-baEM>~PlF}Z䯂J-2j6O#֊g}hV.qHM2I>dv( Wimmj-yؿ6m"I༤uO6aPA.w6h?:Z^r8{Iq@Q8Ҽ$Ud]7~08HMuĚ}_!`p;Z=WĚ³6N֙sYէ$uq~KܬuT$%/8$`׾*|-[VkfkBYq|bo/tkFel" `WO!prF+tt*hyej7f6g")I .@|=@P+JiV)p?={'ki7Mq!KAIztSknѴk4+\mg爵*uU?9Tޭ?]?Le*?.kNr&NA^XZŶ[g͓1^WC,۲Iq$q`*Nu>'.筌{C^/)xsLld6ns=!_ ?QnJhw g\`|b_(.'L$irh֐PSI^ETa);}pq\n?]F׵x7s놢=Dr>^d|39)l rJ8W Ʃ[Z`nװ(pG'=1m,]xHrZor~_ ~5."v).k~X1t$ێ8pJgu~ֿysxXs뗞ʼnU׮^DXƤm8@_ }7s0#tuԒԣ`f\kUo]ǏrdSVB{7l1.0=+JJ_q'|=2"Z Nn:\(Wuڮ`\t$ֶ[%ZZreJ}OҶp˷b^V?ibP9QؑOoaUNJ(r*{ ~c@#0}OW|C>i֋i4Oa| `uO_jW N8Fx8ݫRI8q^=zIg>WB4Һw5!+EfKX03 uQi:N.4l:Qudm6]ap?B9c޺_ F.FW_vG\4RRu"(~eXapOS f7ZJ{OrG5ڿ/ <Q5-f;-\9̇hO-!x y\?L U33Oru%3Ҿ^*NI's*"}SW]y'ّ#p89'ҲyStAQqۓ笋@vl _C7Di kn#KmN >bכ s8kJKs֊FltBVj8沵kꥱm"n.Z9$;3zW#Ѵ-Y/N n wڽ3^Y:Rɒ ^ A'9$ Dһ׈4-.;x'$>(Ң J  lik5O0Ga^:2u=KP.Vt,3r ~bH.= ݧżYRJяuUn%Ɍ)Gݐ3AkףݫSe2;OڴclQ1sҬ.$iaYk[Mҕ6E˵,GW~5jp̛-!xxmp{\a=kڅp]H8ݏj+|S_Bs*KA7wq;qzԦ䮍%vvzXBW7:?p>!`DhKo#鷧Zn# Ķ̱ƙ+ɟs-g(%77^:Wa&fٝowos\KJy=:^M>qE|g x>jC>CF\eOj<`4|3c{ɜ;~_Z)912Rt>/2 B<җ~(?3a9!Z?3Aq6 [TYqԟ½m4)ki< 2ldq\WErf" r? Ss'O-_G2{_xW'p+^GW+u?Yt+0r)ǭ#vP~/k>0ȇMFq_gNL 5˖0lZSr0rZj(+Zය&2{NO})/^ITA$?|[iuR'q]g4렺a%sqs۝h)Sc0xd} uo kG8H -U {+9.'Ap`Z+,B%W ʷ}͚9\ W:q^JGetN"ٵغR](=LtZ}iO vKTmJEMw" t[2ѐ$-CK` z+"o jU6D=WYv ˶q4xZ4:Ŵer=G__ƸV~0.#KyD+sSD i Rr0N]llgEibKסK5%ah>G8kWm\minCּCn66ݏ»#᦯Eyu C8޵izO--a['}s횩w!,*Ok2{sU&ynlKiӴI8 s/kn&"+i7HXՋ:#{e6m(sj9[RHC8?YpTtgV^+(.##᦯[}&-o&CE}=5K+{Xn bL Oz?O]8LQ)%'0M(3L;ٖ .Fӎ gd&!ǗV~~ tׯu2} Ѹdpr^yhc\@ utnwY؍}8⸿= E#=VR1qV9ޓ:xfc>Š2x?^qY:5/[->*Hm?0qb0*d&EpX~U^}&"_~71;X z}j ۉX?9[WG | ý ?+ty39?^+~ |E:+>N~[)$O{zǷ>0Pg{h>_JT6f%\_L7ʤ!g?N* P?>FJ1B@9$q]"G HF1qb|5BL{1D$p+c׼IyiN֍6I( ӏn_5Ż9YTg'׭6J<:#X-ãsEK/9gu5r8r>|75<)g8\u0}98ߕ׋'04Vg=pGC]熬Xo#2FʥFJ6?Z='-Ve%bF65rln`B1A l>ܥ>C9J4஖eylKdK =+F4E86%jeS#GY^"(mX7A^Yc/o3?A'6? ~{|1 5 _x8m~s@)sH+G >/kvw6?_(.'LuS%9 kgƈ&HLfվPzrW?ܯ47m $tTm,rgFQj=UuA۞)+ﹳͻye?]yq4#Q?o֞1Jcq1wI*dB𾤚9Oꍛ8K*.3׷k0HOXeLx{ 5><1d]4g ךUZ*N-ZJ^ޯcɾ'` RKEŭś6*q6wqxkxR[~ZƨH#;@滟:xY4֗hW/m?xuϽxfm:v?UC[w>ixǚx")dFu:=}k5Jt8 w@IgSz^:[Y%O99z<^M-֤W7΅YQ|o㹊m#DeVabH=yzq^|HOjOlc}ͅ3⼟moWO7RKm&7:Ds+ ko/w=es\thP\5UW{ooAerSYy8 sZ66:=0 C?|u9m{u}8?O½^'Ҍw/%en,`r{`Ya䓎bp{QK!捈;(8cѵ''8#?GQYmZ 9Dr~Pyԏ5[K}ݦg+8$o0AνM:_#3h%g2Ė' zo=3ýq[ f8.UlOg>{.,`oD }JLѹo*j֜y'&xwhzڤBf>c\y+E"2rNz֬ۂ6QtQ|*L-:Y,۳kttKžqvU' gW+]fj }>U-mZ^z"0SsFb38[UfrE[7?iyٳ3?(#̟+fofL eEv,qrOza+.ӧi-+$sYVQѝvMRY+/?$5` = 1z~{Yla&W?ҷۨڨ2B)!H ]H:VLeBq}6EmO7yS[lX 8#ÿb u5.Ft7'`vί%֟mp yTp+w,tUFgK[بbqqƵ8a)s_K]wr#VkvXB*F7]m(ҠouI#dAoY3g3噘+r>5.%5&)$d~fR|ݎrѥvxΕhSjʪ2ێpsnKAq#)9+q<Z"$9eIzZ[DNKX$,mu #xjӝ-m8IY)&p{ubykj$jTu9SOܛ9P}*so*ƼGuWpc(Msټ#`-澈0'Wg>xk H"i02+G , uEH_߯5ˉ:pG>Ӎ5ٗ۹kA,Z%IǷ xI:.Pw'erEyN)_# ͨ6 ǭ1w )jpj*H~;(ֽޫ]- gEW|%iǍʡ ^Z|96N뫘^z?Km5+ӊ6P~ȪҫNuE^ ZrJ\tMsm~3ץx_\#3K,^O.4YE')Nkh>"4B#+, ٽ}5*noVp$-RY/%XԒY>p5Y%:g{VP@ڻGnZ-4ՒgٖÞyκS^A Vg `m3@VVɶ-Un1̭Ŧmqj}k| 5Ԯ Ngpq;zS,Lm)d'=MvZ;Tɖ#1p:dfsvl史}CdhbHl[r95t 3PFB`H`]q7zm'\‘LLA{baCO&sȪ' :TiBϚ?9Ƨk"3s uoQ{p)G Vcy\ןj\<㍁kߣV3Ov|gW&_? ^2CI2o3`o8UϗL5+i◆ec/C"}c&8!E|MVvV >鯮{2 gom[#kxZFTPqq*zlm6xU?Ҵ)y? yKLισkL­i|kvbzݲ{`{rWeI6SN jIhɘmlaxx91ڏtqI^fRka'?כ\k4L\^*T(j7y)'eG4KX# 95CZ\xh uw3mFin"xVAӥ˥ y\W&ryR.E Vu8n-WN>Y3Cnxui`4=ybaxz{缶A%)poLڍp/y5~S\Q=I4%/0+2 z-|'ek6t뼤[$]LYխ4MK\d~Fx ZGxC @C §>:WB;lO J#{b&=PGҼY[O.mnWAO7i*hϣfOE2_G'{'Ҿlԏ i+ԫN}'6#۫")1OaI^8vO3bfv>k6tamdUY$Ϩ@#Xܷwc:+2qO?z~g⁵G^n7n~.x.i(1~V(E9?/_qԑ$R+{V{dvYI&8`~dװ2n#n̹>g/};XvFC$s(^ף|Sg͸2eq֣W L|v^lu.$D@1ɯ(6+75fĐsj~/-ķ]?kqӼ<dY$_/%M'bo.i&q,Q!iBu?qf)%ӠKE w89rr?h<^I>UUA?eO tj*vR_rlk:i5iXu?\KLCOq_gF'G< wjʲ!q/O¹7xsK%^UH\2W ϛP4ZhԼ\4ktk.xfpzgf-WWZ'x>^zWZ¤o:*K_iE ǧҭ׊Kn4oͣg(4m,HV9ϙ n'8+խ:Xޞ/om$֛ev)`)th8"eG}.b96!HfNsJwN6&mK{'$5_*H$9B2>ջwdw`ωwpWRcaFA?z8isF2g{oEӑֻԅ%| rcV| ّ}%Un+oI֖8PE_JmO,JΚE$gq5⤥gahK *r}Sm8㊡~>SZTo䯬gͣž< |? n%,; ¼z~@׍;ZCA rzhǙ/OD%Vvo~bO\~hkY:y25xQƤ wkeեYX-G*yk/wsM|6ua-aFy_Ii}j6ᙡx <3]M2K(m(Y$AT^(,Zk"fvqjEZqa\Վkp<3iVm#W/NnӖ#>-QJh+ֻ?x'ßkς8?_,6'LD~uɄG9o*@VTf K܀3X=w~]wyiKu YC j5]@b6'n{j)ޏq`unU$p28`s:׈!Ym99aA^ÿ!1GÒIsp:י^W}NuY:Jo^SӮk_2)؀sѩ{I"Nث(`?KZIQu%~x6Yn+9Ӄ O; (L4ߐ>SW_>e;FzzEF?!8 ʼM]/cqU=>p3U%0Gǣ>qtRQQ_-f{EfWܡxp׳a~7o xO.dې-^) |۟#o!(r0ON?ʾo~TڽYi7U,0qIT<: y-rXL w2y] I3Вqz=} -]:[})5 wed*tS޼g.Ť6=^R@99)ߙ.c,O^};V\6&TmӞ çIVil?DnEu ijW1hvZ1fXMYm6 GQvcO$96;X.nVT #aCz׿J^< KLS%V+Kזy @l_jT W֝ߡƢT!-x5{sdɸI9g9oϨAgh XX ݏosvF2[IU5 abt'}E:pyPu;o @o>?4f $l,jƵF[yń >XnόEɵmՌ{Ci2?O21~by=95X<=ߒV$FIv~"bk伋LG~R?X 4iu鿳#lzm`_O6lҾ`tۀr zp6kBFZANq&+һ_x3Ù\^1]?<7]f?gFGM9[D#Ao])<`V?X;kO)%XX}ݨbI?¹O\b;x+ics{ 8/jVĂ9V щ?ʩOv?5$~gJӭ'#Y=)`B`@c9eE} NG S475;sz,@+H9?^p+/\w¯lSU|EfI2 h$O&݆'s]X6+NuZ~' cEiV]9z9>[iZ؞m\QFB|(ד;)-ܢ)_)E֥n )ssJgSkϛ1Mng|UExωgIj,0Fq!tӞT+kȢ#XR!# sr7VOyqg?֠#~{=s1oV|YiEFF1SyX%|us8.,$V)oO()MNX1i|َ/zLjGvMtJ߳>+yi`5[Z<|i|<.0 0Xa q՜Ew aSi- qHsOƱs#@>ɮU*IuGEv$f6VzԃtIRH=]]>6e/#+^ךx&kkKs P?믺/"$YSpIMJvj'45+BY٤FfY] @<^GChQa\$51&l{Z/a'ij|qIAMeZ^ڴOTd{}G?p0SD{F 1z/C>{+6e1r)Z5o $n$v8U> 궚qs6%p#S''])N_SFihNh==EU?!-Ҫ }c>mi]7C+>AW)m!- @K ?+FGOq_dKƶޱq_ڹH?$cOJ]gg;>?-P1-}N[Yt.qdcz6e`L|8?գd6t#zFt% lv6:[y-4GfHCrd ;קC[X'**:#ׂ+x+XK v`ԜA(t>NYԫ7=Wm./+[~'Ꙏ1=4^=mBWTe;78==sXeƝL֮ ųyTTYZDbŝns5_Nddإl}\ZhG %wmfjҧV}j)A"U#N2Tм7{F1Kodο;F8Tkt \W2WԁAw?J aN,c-ͲFaԄ;kϡ_ܒo~i.wn)i0m&xÜ89jz7<'4Z5ͪܺ+ɶI\"+ ,SM3Ge0~@ųO߿5ko/äiFMtҾY@y5n {M[<5'ZZa.S&ԝ ]q w6k%ޡr۸/8W1<勠.ck nZjkWU>hH\ћwOsfg>d[W;X(gMۂ+kdZ" ۲?y;MN]Gȷ_'=hG@sx&gN7pSY5y>-}AǘcnRyk7Nx#)i#LWgOsCqS7ß/k6ta4~gJ#Ӿ4oQ+FvCW%y }z><hv0ڵB"vdI}t:(}@Wi`\İ_-$u<ǃsPXK]*{(mB?5//Eᔂm$>Dqtx5`Smu ZI n_,sRzմ6O1BǃS5Mb ,#%9?ֻk6G͊FYh$+pJW;RX[D{X$_ZZR3JoF]#qF@ZRMFyc8+>mE|Tg}+3 Fubzw=-ߔp\Dw21Gk, K$ sG'#Mk[x](QG=>|F-"OX;:q_`k_]F8KHkcͼ}ROv<J[8ȑywsz|3k'i_.d1,IǧwE!e&^yqv3s_Sx7T1Z5VN&5= O.kf2Y GUPxW}~vRnTA}VxLү YP:c3NWQ'VU`d\1e4gF2^tRZ6gk.O[?Ky_A} ւRK&۠W ^3^;{e-̚tq{63\`pzqLzωa\6y,j)!Hf07 $_M [M2B\@FSΤtn_ǩRQRr~9N){(ׇ-n! ׮k3°+rv)J1ڽ HPדBcR1B2es<^tZwrh~(kPwbOBN95j솃ߵZ² . AOjv5i%bFCm7ct]MFBفxM_<;0u񍖌^{l`p3C *VTeWoҴԲ>fvi"BhRHQyae+[w:f_(iu(UoI+ԫ9('E>Xڝ;ex_\9+0F3w5 j: ږyo~!LLK.$tGqi,E)'ʐ}}ǭ͙ʜ98̴MZ- h'ҭ߳jmNqztfaA^wצgi,S'2n;NbZ(Yr)9Sm|/ )woo""G}q\?2VUdvSw+K iEwvco-=ԏ:y+9C.AQTG5w~Fτ_GNҭg.0Np܃+u5kᦙi=_/ ਢ^iEc˧;T=XgV) "qWaqѧU/&^:LYos1(S>O~ ;6HKarZK@8G~ |?jqU&\ ^zW9)3%JT.YmNɁN¨ep}8^qn.R0#610N>Kڪܾr`z m1b;|NG^|J6Z=$.U.ǣ%2yVX;>S̿$BלՒ]mt,J3eW8rkYt($ u<㡮Ziԋ~qyutAO_P+m3=?g$dL˶_>e+_Xi~ҼWX&+4TuG5|S3ý{5)XYlOC?,6Df.Պ.@akY9@$W>;hoSH5o])V\Þ>ŽN$נ$]ֻg2Eh~?;Z<Z$EQo ,vE.O:BZ5n}:>i䌍%cmz\d8|Q]'&P ہ<_khH %៦:]wJuڬ6 |+¯ʓl)5cÚƷwkq*X.9;@ Uil +5zfb̳[hKJQ W> e$RH9b~$W E|kVF:dx^ezmtPk [y#XUy Xķ)8OE;5~-%FLL2FsO=k[K9I 8^ZΨ}T^?%ܷ~w]\|g#+6V#5%/oK.4`B<>xOO|9jIT #,'*qp}9I瑋OK$|v]C_ӴGl.z?jߎuI.[Ch0M\>f E5=[wPOݵ*RN[n+]o[Fq.ٮdNВ9qxl(Qj3 Z^/ ꤗׇ*$<yVM{xI+4r7k]/DHɂ\&y~Qp^K6ic$Fw nKv?ʾ VI:vM%*9)/ԣIъԎGҽg෌u;k #I\MɷsϸS_G{<ʧ׽GvZ"EOZӥMJo?uЧ3?kLc,]F<z`MMj=2hf;s2)1?0Ԟ;Cijc{ev۳J}+&wn.!vd9 bQ>¼\E:TդGa(=`]HtHRcS"ˁjjuA3`{um⺂XJ" ~fs݅.x}kӧh_nH*TҵÞg[.R8לJI?{U|wq?:`";b8MD$˨xL|/D,xZJV*+_\r?:(35*CQNr4Sģ0\Ri.C,s%o_\xfOhdk IdH'J=%jܚe_6q+m*᱂[>:wiPĐ4&bN>&AgŚn[~m-7``s5/ >Wv6bx=A9_{Yepo˿ŔGY!ʽWجN:xK{}砱 l f瞥\@OaҾҿվLW9H?7:vu9Q$i::צq> ?/k^\؟3CU1q/i%p|/wdW.?C}F-b' 9[p^i]%w2qB>gR?CUNJ{^bNaF1LJs9k|W='P|maDv?\,^ 3`O9Z?BYKJR5GjڛxY̮;rv LEb0kש5c (nJ|;ztzX\CdJaRTq¤jK]SN}@:zI\E0,GO85?E0BNƀȿ7z䐸8A;~4 "XMWMp mV烌uN<2ehZ˷]t[ !'1ZԵ0>'y%V n95GP񥶱[ʾB"$c%O_½š_X,Dy~m:p8Bv8eyJU|L)?|O] NiKu;GNNgsE-`VsusxuRyZ·̈e'R7>4kFLncA{=_tO kw"2v؟3$mkOڅ<3{e6ccS<~"Bܾik7,rx&Jt~Kea'Es_4 J?j|Eo`I>5&OX4,oJbT<=z?wjv,nMޝ(D2ض~TzdNqZY|/Gƺ:cFd_{iFw[iZt&8>g|3k}'__Fp77.<(Uot/)Y]Cq+.kM #瑊 7SuCK*Iom ۷T#}hM 8gӭ"ƒy/F6xg %址2[ @E`k GK}$*[C}I~5o/g "D$~Z\i ZV4Mź9'ҧ,h*n9WGWܚ^:R5;o|Tmb{HU'F32EjI|9GOάH~ߴ޾#} * qӖ굼fvG=Ebl5m>TXn'[ M{XkZzm u^G/8 8(؉?C|7i,](UMsRk>iLݗ&PW8|?ƆM >kZѣybe.1Zuj\v>g85yowd׆2|ҳzCu[ֵN[Vu=5-ZF |t 8$@?tQo.erB1#>սCQ> }jH1*t A㱯BG NCꩨԊ?z/otV[NLUsud,}J߂==zޓ[:uRlm gߞMКZwaO9MMYj7$K_jImm{Ev?imyWp:$ޔk#*~>$OXkY3p_q=wQ6>eĸJ g=1??,4_/O^Xq6Hǫ _1г'YpӡVUnL՝7zxEէ5)eci$nc89'vnx|I\\ٸnVadd^7L4IbRIcGp?x{?Vdxx~U%ʒs5|n%x@ op (>UsF<^LFă?¾ͦ^y)fJsqN18M$&v`k\D[>P^C֫JrkHD_ |Qː__rj2?|+G)N \$Gxz(4>9{XT5hwڎ-rVs޼,xӂX9?+] 8,̠OAUΙ~IDؑo^7Fʧ,35(s_S 5t@ C`Os WYA&Ƹ0NqovB 9+ۛxo*y`=:`Rxӗ774ZH|1m6R]^(>&}pKO.z}NIEF[GcNz{Ms#5+ ϟ#q2 ʨ$~?zU?G*:.xΧ\^4yl?U>0Ǿ/|,l qdV%7wWbY[2ICRkS$Ih>,.D-UN}3^Z?A&2gh u<+GNjgZ(0cע3k5n4.A/6¤i;$jENGUh}w̸d`ꪏ,Ǧ*^ks-fJ]HUR]фofeE<}ңRE|;w'UV#n>V$*i$F9@+mOA%px_]Ȉ F~+.#.lsk_dzsW䟑Tb\sLI/\S=+F :WYcDgzruZ+üXY\Oч%>:줩_>ڽ VPbB <$yj%_*Q\Ƈ*l[i rTdu Ud;p%8Q5@|`Iuc<IPerI$.XF͸u]ǭzaB٫ b3V63»9!IY[s`)?JЙ7) 3Y][A#(tR 8R/&6by. ->Տ?4@7`4uQ>{Q-^9d=+2=Fze"#>󢺬~r2*jyMV'_TϝF& X=|-G{o&U[79tV8Oo![O~! p<')WUhkR8Tm_n$5]Bel2NXduO+GC+VA I8?yOKE> `qƽ;tN9&`vX1Wƕ#8sӼUqiy4ZYc} # )EoЗW.O]f%Ў-@ fkY(`I#?`t^s lVǏ<*tYcs9U9 O@}@Xa_AsLd ڮ%B/5r>t#p2ɪ T|_)tA^uڛc " ʃ=04hUXŴ$=x>6,>z)9>OC惯l!К69\(G;Fr;i5+]2)c}#iYܴhdw<+U݊J׺y}p˶IkJ $\ `+}CT>d"c*ۖ?~, CK{4YVG.cOcOҬtK{X1) y&-\ð/_7Q̩q7J @"=ϥ`x?ީ~ZpE(gHEg[p| ŒU!ޮxf3W] F7g)$-X $ ϻWr厑6|I֋1bp9=x+seŴ#ӉͨkQ>M 89g|L >\T4Ś`'ҪY,ʴ#Uyj8Cۯvn|#c G>qW|7j>mQhgN gw$0U$8ӥ;D^Yw326ߡk_ E4gʌ 5Yqٖ 1OyuRrWQ8߹Rj,W+qFNOK-%H<nf'OJT6sxHs*̘FXZNGM63o, >ΓEmt[9di$})q}e,K ъI/+=HՂ9 0iRjks [Sd_#?\ G&i`OaPйxЖ,=02l+[/g^Eӑ#SԱ5T>z.կ&rw~/}N-ldo#LmAuGQa{dh3L ~Nȣ=Z# -?SԷ?Z>&hh5j[IV@,A 9/܊6]* |9#g-`XGE-#u|CAo<ʶ*@sNzufkZEh~THpc9 Wv*qGmv2iќ?,KW֚y4?:,pyrAWyXi-$aU$c*{ׇhrEGroo_~}aƭ8Uu?kOm5-$*wڒ5Bb8ҡX>աEG/>^pߟ~k&UrnG/A$lYCy?c_ ;;'¬j!viDnf{4h$- ޽ QrI4j;\gs _Rݓ,cFgPxG )Ѓ?|mCi$3 4c=8\v6\Z&mojJ>jЈ y'ol*֛wm=)FbO_馴B{ _PĞϽU-?SF?XoVCUnAkpd,}NM-L$]!æܒ:Tz`;6FpS\jO0a.M@n d܁''N{1gO6Vn5Y/xX-mg;5|kmTp3ȇ.} nI"5Qe쐳Y$u t .yhmCC3Odg9`s9UKNǪGw?b?wJ mcx?}+ ֫Ҿ ;On?+O DK+ëM#pDYG5ej!^0cMu?<5˚>0.xo'΋E 8o#ݬTE;ZerZT!< _7ƽ\qßUM7M/~~P:QX%x&mTY =޹}fɬDf F twD]#Ip38iksƨWX &k"XA]"Mh`u` y|D=yÃ_N-O#pT=ps6b: qݙ_=.@ڲ#^8@ $c]6N|;U~bORp>(umA.&!IrzJmkK Rʀ`.x$ @ uXpax|=?:|+iiɃL߉Dw:4U?R'#p޼Lf!QWmLS7w2[A{cEh^ץz?js(G8=\}On*h)uh-$#"l8?`Oko8nY˳ ^&+Ey8Kc y ʑLY%д:u(Wxr[Kqsqq#cDd#U ?oO2^)U :fpbR,g*G㊖Ǧ!SĄLjU]*1remYX<s|ҷƯ ?:/܌tS?O] Y$ަJ@EK?rبc< )?srh2~|ksAȮ1ʁѺUdu؊ƶ\LN29DR.̓ g]|(͵?EHD1S!O"ةco_[qa+ɴ-^ї٫vh}#eQm!JoxZkc_ -nm-l:Fw9O\[ 7zȧ7#-uz:+-"[;)5Ii8g?yڴgV>]93#܅BԎ+[Z,/&oF6߳ri8y3湪V<0x nW5{*]~$}: r{*m,MZ?=ЈܙʂYIE(?jI6xNzt*/Ľ;vt.ש0ʯζ(Z/4j@ Nꧧoƾ͜| B&RMsKdŀNzWMXByjz%SnQI)Q^8+vEXs]'@O7t_(.+L'=k<.2t\?ʛg&~TTI#c\M>9.cE:`EfZu˝ܘf_C)gv[XQDlǢjDIBdO~i⭏ [>NBA\­xZ[:vu& wN*].f)O87KKU㔍Ѹ8fGy|=+'{ c&zRk ZY4Gu #ҼJ; Xtvzz觌UC<֬f͏itCV=+:[nb6`8 Չ~uGFKۏfN=Os;3iS=]avR> qc5kkPmS,:O9죮k[魦lAiI'wb{Wsx+Xbv'߲Ԯ|/[> 07O_WGS Eg Qj:ƕ*itqIZ2 XGШ#GWQxz-&-^g>i'sJ\ ER:p-\gG5݉|y308Ml\v*zr;~eˍ[YR+7.9'<~Yfe2(&I[ӦtVIf\$;Bk .+vyueXFf<;xR0"i3꟮8jV] ,T%$T~d~SUZoW#+qҮȪv^AndZjZtI"yt I5.8#)G R4( zQJiX.V86{UyùD)*-zo&jyS3[^N>*'OMub1N*(>[3>BwpmN:\ WinE8QPA*c&)u]/BG7]F+!>p"hk&ua_YpŃrv+G)y? ־׾x?zg5!qqQ[~BJY\!xjjrUlڻ_#/#hAO9+f]x4fwZܓՋ_/Ʋd9ׅ<~8wNJV_3^c]0E 0c W)eV>VE+/!UY=ǥ}fe/8$zjg2o$[yj4sUX\r~Tu anjX+_d=XI+ {sb^|-͞~muVѦuiPpA#'d_koMVP,Xŵ}ro)+H𾁦M4RͽFxa׵ydz{-ӏLy$s A _zjOk7nc|֓J0UkVϜto6OhK \ gvŹ""Yv̜?*?eitH%6y:{~gs|[&Wi%Ͼk6_ 1%F?}0}}ECc;5yJg4oHdN@zUвJ m־OG9cLO|X__ڌ&LXщ偒B_ iq@)CTvT_ǂ-oL BZ3SКJ*] %$zǟEEC^msfotoxx-18.01.1/images/gammachart2.png0000644000175000017500000006410213222767271016026 0ustar micomicoPNG  IHDR$KSPLTE  !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~]} pHYsgRtIME2( tEXtAuthorH tEXtDescription !# tEXtCopyright:tEXtCreation time5 tEXtSoftware]p: tEXtDisclaimertEXtWarningtEXtSourcetEXtComment̖tEXtTitle'IDATxwl3ݖe\1 SEoR(HY tBD/c{o}ofy;~e=h4syWΦ+VlZ_"EX+DVa)b"e3 @I\ZE:nupD \RZ"L^K;9-!Ru\S[qsT [BB\"ex~q \1jaǜ|օ_w]?s/?IDAT{}|;nsO;ȃ__o7}Q+j}CRcך:kͶ+4_+R P0>ѫ6-;kZcSC}V[K;>jm;og֮"Ig_xO=􁟤' $w: IDAT[m2'>hMNR_N._) H.n`4tۗ'x$dv$$ݻ@^+.j;R)_GؓϾ#}WBIQwIg=aINo߳KHYN5;roum7[wڤq#IZ{ 7jݿ:]vMIzZtʱHEktJ+nhB⛮dQIDAT3I{ΞI$a4rܤiHҮDR N\pܚ|m6Ϯ@R5XCHz#. 쓏W\ %6#&oEA3>*.݅W\$=~0Id"|T`{ HRiHw[q^И񁷫FcOA2$M]E:h4cnlcU0W[--aI[Y䭴HI~ICݮw$p鐤DчtHd]s.}YK$vK^όIDAT,Ey+kMYgFok[ u*E]Ƈt 9$IW8PƉG]E:,4E( !"7Feܘn}$*@# WA$*T<ȥ $M"v~ZDn,xhsEss4ofےҶ$,I&Ih|@@ܮ.]MDb0cIoE5iӎ?k%xK%HoevI$~IDATI~55tk$nW {EI# m6%aǀ 6IzQ/0qvs}z@CVݯJ烑ĵgoW8w|$A/=XT&3l${ GbM i>1z+/_~+H2oWT~Ƞ % rZDGG7W4I[@U:$|296Is ƇpBHdIDATIŝd۶$Xh'%+YA$ 7ilh,[wծ"qW/I`X*~oIX֡8 E:84cEJ&D0|$̠tI|2:$} U4>;2F$Qt6]t^Œ$=wƉG*V`$" ފ^*| IƇ@=^H{&$M"qܩU$=2+r6U· IZh ~^ʠmKAzۄ$0WJI ފ^*9Cc$ M*%ԓD0|;@#ɌB6W2h0y+@r>I$6>Kw{II8CsN;SϽM6y"Z(0NtҮ"W)c.s>񯠞tɂ@қ~^'n _`*Qh*6IDXuYIDAT$[$Ytg :lB =Df,EI7g\1IvoM$W_c ^"`D0A(`Z1\D:Hz9$Ū(seW4dy߄TK$^$Iû_cMR_A4>D~wBI@ dTKNXIJo5+;jsEDD᭠KdxC-IDATic~N)~{|Ȍ%ck2>7pDr:A$A% Tr.&R%NzI31I{hώ4WK$c]"bT,%uv·Ie|F.$a0G'Y$X~2&$I7m I+1[Ž<"\$i}J!HZ?\:L$L2|یK"3VDIªAs$>krB#2kIDAT.><4F3>B;#bȠm+jq)ifw,iLz`Z #It[AHq}=&wi㦂r>,BS IBtL;HǜtdI' Sk$4g? t>H Lt2zh Mԯ?Zh@ Mb.' I":8c؃%Y$Tߐ1EB3VFIӨ3W$Iat.R$aA IDAT9 ,+3_ :@0|_$2cI&}Jd"It譀K"k%VABǾ¥S$ՌcU3VF!$aj-|}(MM'5IZ J%TO:=Xt",4n|`эHnQtof,IRч`A0W$ t/zhlC 6Ih||(U81I:D"3VDX ' Xaө=;&Iiox]IDATIKz&6ԐF &IXOڏ&$8`*oO'trtDdPґ"\;@$lHfM#,C4)IhHh^wH¬vA阹" t;S=uHi%&Izb $Qu*ґz/$折V6# ]:V.^%Ҫ!D:<4|L8O ֔1B%I0$tH`b@ DOQai JiIDAT\N$^˥kCJ+H$Ў9IfPD{A$a#  X"YAgO 'N+`ә$KI%ҵHGt>l5I`k"3Eƙdծ")JVII 6+(kIHAAiW TV&)9] ^Ed"pIoI5HNHN>6Ȧ[ ^1\}&{$Xh.I\"FQaIDATTU$`RԈ5n򑄩M'FK$[sUO{`E:ZCIxaߋTDł`* D @*(MI.wK"8Ih+ I`{nX4!N DL?iQ%I7דٿmI^vQqBkXhlj 1IPֹ톫Цӫ]E4!S;D$7"<~/ePm(w IDATIWIP͢I?ۦ$ .|ҡ"$iV&OC&* IPg-Iu5I XeQA _"i~ҥK~^*aZՉ$e=,Er `$M"zJ$5ID 4$ۦI~/&_"I}1({i@xcnSt"I5RV$m:4|M .Hq7-wȍ%pRIDATc5Idƚ "jJPgܮ"c?I(!%0R~tU$rc $gHz%$}a0AH"MI[*"߂׷D"px7H2,4,*^xbAA>E$HI./ᦇffЄA8IK$Ү")gQI іM7ilj$,كHGh{h$B#3VfPK_>CIDAT@XE?5'z$m:FwmIfL{ IKڄrrODa$o$ڦ$$5]Tq y pcFn,d@OAa{Tq %'MIY#ỻ2|!:Dr0ktdE*Hj>+=4pc5I#QeRYhQ՜IDTp2US$@E_{3 o+nIKyUIDAT~oE*۳JN@ȍE0j-4,b)Iz'$3)~VYEeTtu!~AhL;$ứ4|& *¥7ȓ(RIդH'呤<$-42ce՘$ 1:`tjS~TBT6${ɥCH. 'W0E7o]k9&XA)> !Bsډ ?IZ?$8$D0ִ :$߻{G x9ŲM~{eIDAT<4EhI3I &eEmEsŚ׃IzYZCB$ _iQt HR~+7$UjMD8H+<4?IB3f$+BE:b%P$(ECh2K1jEF$ɄHod$$I[]T*''qn"ihHZh` *I2:HHުVD(+WtF # ^ҡߛ"Ia+tcǦ!H41iS0F__ߖ/P@-GHjM$'%Iz)7vRj$XAa"$UEOPzUr̋IvgR@ԄL6$ބT ]1nIrc-TeXZ"H$B!-(ct~D0kz*yiF{#7"U]8qzN__$ 0c$X@t PhZqI/I5b&IDAT$Y~obH?'En*M,|CIҌSI/egM%7qVDIz!$. QhXB {"\(7.I~7v~$eƊʤ&i4# T^ 7tV$]|i8II"9ЕI{!R 3nI܍%__3cO jC5# C BRW7N< 7u"~cDt@{CR$vs7&6cO(MIDAT? X"9$w&7~ t⛯bKcG v(09~/$aGE{v^;w&;xXe~C&%H3qzW$DQ8I9ZCBEnfNOVA {_!HWI܍_D!jf,Zh?5ո)E#nBHiJ%DgX/FCyJߛ4'ɍNI2q4SH`Đԕgߞg0AIDATEX!-Nm WM&߫WoTu[v 7!$13Vg$g&Dؑ; с6|gL$A{!R-ndd HB3VAA"IgV"R=$]~MHZz/eg/F!D"9~orH -DEnRBD$oAA..Iw.OIiq1IB[IzHDX֗ xIHߋ5TrS<{G4L7v̴ k(IDATk>>%zP~Z:q8V ;nNҫa$=H:̓ȭ _ +d]x"0lka43$QBIHU2IIb%6$FHw:@Қ$JA2DR]I ӍzSeHVgƞuW7 { Uֹ.9$ի1M7+I-~6IY#m  _e%F29F"UKq"ߢ?|"IҌ=PG#IDATnRR ޗ$A0~EԊHTے€(I"k"YJ! I'| ~^)R=ƳJqK__ /Xq )Ib!TBDtIZ,46Ii&ps 7|):ID7-$I_gRBIfI?I,$3MHqn $~@KxVi&fP|K~oBD־>Hcj-H"[Xi-sqIǒIL"I]bKSe%?IW^HzGؙ%IDAT5/Tt0e sq(7!"5JF~=tЋ; _&INB@[2(#!^)RHӭpJNR)EHQż $ J$D$=Ռ"Uv6IXoM iPiI֒$=d-I" CyN*cxCy"@Hdt@E$@XN I\-$Uj.'nvG7"A $FIKKHt*!IDATT,e" _'nD84IDD`*V$iW$aGET՛xy'BI-IZH€HLB@[xf"I/D;CRY~Xh,XsLThs$b0|) ^]"EWHtsKqt n '$AAʌ4{H*!"UCI&R  "NG6I8& %D,: ×29H I57T:M/$Qo!!$H֬+p>IDATS GQBI  D|y'Ww)DHӐ{ t{ZcW$^$Djh~ ,7;n?~H5nO IC!Tsl霔;v,l&GOe PD̠ARDĬ IY5nfRW' IzU$c|QhBK):`EnI<98䤈DBK$Y>f$aC\tE4bqubH:]Bэp. IDATкIo(6"MfrUkCHHָv7E$` "R%]Ʃް@HDt@En3$LkܔA%Cz:[uBHm}̠ڐz;w t^*!IDEj}.P==I4Sָ!98`* w-5Jd@m}AҒyYIř/ DA$ئdDcG \dָ{VDĭpEn IDAT5$Ieؤ$s6#w"'v ys1'E$i] $耊D $ ̀ "xv"!"_QCD(6m}F$``$!%npqcxT %_O<|  XrbO,5H⾾E if e ƓCRLz4՘[8I,:$ai$ǟ7Egbnb}Ch$m[Kd"*II)V$& !n O&nIDAT_WARH #d"!BV[cEHzJh@ ")uEH2 d&A%Cz:e$I]z4 q"2.К4ƶc$lҨmJ<:<^DD")TD[KO7ئĶPj-Iz$}"Y EΤc: ~Y%IZI4M RIVrf9{9z}}H0 $КM6,IRB Mbtc=DIDAT$8_P*: \ "LHw>r4@ IY*oJ Ɠ!Rn6I<[9I*:0H H*'E -I#ǯM-Iq&"'Fl<#p^bP$39HIqM7Na'ID9?ݍN8I*OHAzZIzN['-$hX2(zSJ!S츁$\>d $>mwڀ$}!I Iu%IDATgCI=QZJE$̠pkH JHa(!Q?;H g$Ϋo6!d$$d dc*&I,9HI |}J8I9?Y$^$_㾄iIz_t["ӄ 0 HZf3i͐\jINL_ i`UQ"H31oH}]yIRDv1"t^\ E$I3:8$`WTʺ86IDAT Lhs.I#I" .T f$AYTJTWk$ahݔ$q&q!ce%H3"R+vѝIv2!9KF J#I.!@tcT'$'o$NfBQt!JD")8D*2\1[I?G"NkHRqfBD{[$YkL? aBEȠ~[K(LHb:UtQn."MQ52[7Nb"k"IDAT"2I&E$vIZvS`뤵qEȠ(!H3]-DYJ V$QԨn7"mFU&Et+<ݲ܍\Z%Ig%I"Ϡ"*'1?)2)E=n Ev7ѰF =BҀP$/j9k~ $D/*I"MIFW$Փ$$w1YUjʮ"ш2$LI5y"$h>IDATUT'D$nK r$Rt罪&GkIIw'駒$)[K0LH7p6IY'Wg)$$ahDH7: +Ɖ< /ӕH$]q\qkJ(SfPIaGEJDJ\*K.Tu͛%IZsE `[c>$zU]E߉)T5IkHҝ,:7`9L4E2TSRH|9HIDATn ;n5%fM$  ͼD$7CnƓDN4ihB$Q]A%$o!"VKnN Ѭ)R$ jHV,!)ʕI/NrRr) H@ҧIw$j AR4ITRD«INJE&IVB$ZQOۃ$\JzJL$ E0WD۹O:6@0.HA%^"Iu r>9IDATY$Q3T..I(W5I5`aD!EvUR\jX'#ILe)C(=If%*JHY'SLㆬH֝J$YsF@t⒤edT?)u/&uC2IP$ߛhUBK$ 'iشFwxD*e@+$-m@һ$uRPDDL3yꕂwx1N$I, ¨rh$-MIj*1Hj6DIDAT1*II#C_jd$3JIbUJ *Gj$DS'ƪHD/d(D}b*$Q1A$A$=^ybN 7hV]c2TiU`*7Ʀ@v"k dIV@tR:$}htyRDZ.7I0$,,D$gP'E8IvB$ !"8٦$`d7 $]{mwr_D(T!"2rqAA" Y,*IDATDn1D ֣$5("IT 6Ɠ! j͊4HH!ęFhxEif2Dgz93iY#s f$(x$Hl]f$EBD2oYI,G5I| 0TH2V.5ʄI_$-BD5uAc>H`\^ _;MHb,uPfy$ %$4SfPjٌҒ" f#"8 i$6Z`ƳIDATHq*eD͢6IФpUq5I7J3!xgjt&I֌F;&T+I1[PhH$?8B(AR_$h}A2(L3Î|T9): I$D5.H#i,A \$=H8fAϚI1[?WifRD " aj-IJZOlIao];Qf$կ q8;l1hlIDAT'Ƞz!H(_ygYFsfLc 5*Հ^zXa֛"ʎ[LcDRFCL8ifD.2xC8I"k۔6[$G&Lڊkf'AJ&qSh $8B6H IJπ\\ePf&A${%I#I.j nDHI&R4u׭8?8*A$Uq$qLA $ Y@$L&jheIDAT$߈$9bŠ37Hy$]`BqG !:=f"i&5"F$-;Ӧ"].D!kj8*Fji^*"!s݇$0j)AR+Y!$H)Δ :HZ@۱nH~m˟$5BBIIXQ&igF(.z@!!DJTToa0H/lL&iHlNUv6IDAT,_$?D'Z"ř{$$A..a!$Rk" $ cdƙ5$4#HeHҚIE95~1HOLH$:6iqzPE#I[n2(MN1ș˦6%}YD}AY^v@Dֈ$ $mEh IDATCH²5I+r;kZ#řDI*LIqZ$5$IjddM$I^Fһ IY$lX4I:iXj$ReM:"I?fD$!bATƬ$i iu'D}^4#$aaI $Pb֛"Ỽ17&ڌg8IF $!K3"\ǕCER IsbIDATC$YQU?(H"ISND$T$},I=Q[ę&I)AO3BR2ٖI:|HQ8I~ӊ it@h]D} >2I6!"+. Hz՗X"I?gxsekK"ջVIFj=mBj *jК:LRϋTZ&ii(IH HHIDAT@3b$ $#p$3&AJ&$I&HZ."]dE3E$M$hjWDjYEXAHz'$Q k;@$?4$I̒0IbA"o$5bj=mH$H*?(w6 : )ngrI:)Τ`#H9q'd4tjH+fF "R`g2H&IHZHO._IDATf&dTZ;l,i\?"ԙlSIq M+$ Cb$-s#_"t&㐄5iu͡I<֗nǷIZ:q$nq&'I,LHy0_W@j-IК:<N =Xw4"d5rJeOm;#GTJpt08 iGRP. 3o$#IXZDGEIG&)H"R5U#Z"IHrSKIDATkj` <DKܼՍdФYkԠ>$5bh-ބDސ$AY'!"y/lUyEOi3bq*I}$-'zWH FέI:2Iu}RDJ츯,ů9IV0H4c?OHZ$YI$#j}5 IDR5$QO㿕GpAR@.L{`($>n?m?^$x"]WsIDAT,d(Iq&V,?aWotc-44$Zˡ7De)TkY$=$#yN̹Y#?!5x$#&ijfZxƹ4-I*ż\ 3S$4\\6,!R.8$I IA",Y8nB}$3$ 'C]JQE ?LK`QIx}%DD!.Iv0`JRwmoIi~IDATltjLtCRDK$i?ј$OT0InUTA:R7"OIjBتVRyI#&x$aA֌Ǟ_ IRK_5^ݥbzJVs$M0B1}i .c֛"R:n\J˶)tBǎ<$a% q"`d= = ȐH]Jy/Usufqp{I{qbq$Ѱ`xOԒH7IDATKĦny+2edl(juWHI)F(Dha%,Dz idAGxKtPٽIK$3&&HZ#i6#`II\)Bi$~YjcIZsDe"ȗHlF]i\sK-+. $ܛ?# $ &RP"sojiyĎ~,E$_$ 3Z_<džE"Df2պ_6/]u &I@ iKHIDATٲ}$IW# :I⦂cHϜ2H32Ir a;7T,fO(I 25,!RP1"A$c0EWmEH1K?hw^-dイA^II2U7E ,ܪ?@Rp$N!7_gPX0GMTQHJH5Z4ItY!/"IjX  H6\H MIDAT6XW e:HzCImIڟC"EâE"djM,AZ3$I"V_xcpדEңO6' bQ*$:FâD& $ 'inv ?К$vctmIzXdٹDP$8AEHwH]L,5/I%+YP8^FX#iʺ}U})I 鑄̭qHW$BY>$Y "mIDATDPO`'k8$^$Rr&k:` $v-[ NҸ)I wr-t$io`$AM"jCod5wb4(duHlqIz)I4@ɪX,7sGR?"{Hy[t$Y"GҤP24>):I%z5-*I"_0Hs񤐄 k:M$ßtMIJ2tP I/#ɨX(?h5,!RqǙI2@zS'qUIDATmthMCo$K7%B$W7,5#IXH A(AuuE"DzMu?#m4#iEIz$~{^H |:}4$Qł[I6H $IaG " Y DWKB1$IT > HPy1E>>$=I2GL@u@AհN"D*tkI$ѤaAQiA?Hy"%IPj;!$-ۉ[T=QdT+qIDAT,HjX$A. 4IHD^ 6O[&5#ke'C ?6Iq $ $ m$|i$(;CՍ"ſA'$dլEuuX""J`(8Iv$ɮ?I=Hd Pmĭ}1I#&rl0KoX$B$ PKZ}u$G it} '1>'I ^"]MI"a%]e}IDAT@$lX@WGil J*ےHWRF$ &@ix!&IwܜK֮k$$1:T &I'L&꜐E?@'#i Iith-F!Ij*5O&*uua!:/$f9;{&$:Iv"I$HAҠѓg`M$Ė{CR9 \uYIz)IjX|G !dT-IDATlI#Rk$QCdOIUL$YQIŠ XWĐDfMA@$ 4EYÁazIDATD^hN(vHBVH9f3&0ԍU,`q$3$^-tdIMHs3kI'fRك$a'W$A$wN*]\+$YMܦ0,l;MŚ*I'Iu!H:I!zTn_F4Dd Jҫ/**{_bXNӭI_|52I]piA$T$Q.]˯MHb&#qC'K7i$I#8fIDATiܧG5IƊ'm7BIz&Iu5{fI IL>,"I&ɨ?lgsI["DV+I/5#ID$]ց**dH򷾒!RlOnlH$niiM^$ibj$ H:@x" `zVDQaHRME$^ꆘ$ݕtUֱIVĺ: )g$ޤiJDmK*T(cx󓖅 IaxIDATGE2N+n 1HZՎXE&ޕO]t|&$=|=r+IӉ$4CotNr4c*IFD inY,Y4#g*jBD2ZDI/,G#qE9fsjQםMTWT:jlu'PEmER[pnH&IVכM-d$C$U/DkvE,D#I"TMkIDAT&U 'uI1~$"R=2n+Flh&L;a%>{ۇ$)`6IbN[o)2I@ Ti@L"d־Ik!IDATZ;#a `mAm M$*YG=Ic8$U9 4H2:W^ Y/;LvDdF G{iR )l G4!' e"i$IS"Y'R$ٝ,IҰ@Hz> $Hc8Z#` "`Y&"ImMԟ&1INj@x$aWIϵIc8t-U$}Ѐ$ }k_B$Z_PtIDAT!RZ!8Y'\m$HqiL$ߑ̥vsOR >Ҩ1# Ǣ@j_)A߷Z_SJNV~N1$d!IS$IGE˯ p+R֩O.&U  Iվ=6rD;n2`d6i8IXeEą VU=JN+p@$4i׾|$DTguS8qDܹIDAT4FkcoHS=ER:d${WppJ>}}kԯ&$"k_1Sf/ORؑ'C${S"O<IR[0IZk&JVIR%IT'$UFI/ǯ$d*;ix^N6iݷe#iG:H]ɊM{_"zZ떍O0PƗ;vlDa$Q\n $ _m ?\2sMIEgt$Y,Ip$x%xIDATB%K{3vf[/P .>BH27t"Iվvv i*o}%W\zWX(U@jISIV%+ I6Dp-I@(k_$$EOV6IuuIZ"-KUHAX' }9$]JICVOXoha=#iu9D-h}I[$"Iw':g4kvS>d#_DH]CXk͟5#i,d:$٤r۲IDAT(#i3 ɪdN,{$aEq3`KIC,~a;@$h, i"_ &$=!+)"MH"7I,Z: ɂv4B+Y_ n }HRcyK4BDMTWI&͏ $$auݨd!zgs,ĻYBh!HMTN삘6vGҺn5Q^ɺzDQ{CVO$YjoI$֗C9fIDATHDdq6ެd]zU,(AUQ@(k_@R#i$j}Hv4#)-I24qIDSNI ׾,t#Iw'1HwI (2#IT]!ˍN4O D~QHb/Iz%"Iwj_F$gk}9[BVqNDshvWChH+1HJHj;MSwY (c[_@R4%dHv$)l$jXɊMҋ=GPIDAT6AD$i"I$|$ UY& 4TC#D]ER$ޜ$1F %H,HbMTjx' }DUS$$9IPEBEHk}uI6Λ1IZJ'/IT8D,5k_{ il-֟1~c܇H$z|,$׎[Z$W$}dD] ]tMzS}I'H7GIDATzJ=Q$& 4ǜ$}ܭ$YI)IOqd"5QUE(*zIr$5%i@$=R~[_ ݷ۶kr/>F+HA}}y-DLgvHv삘9H*ծt^ސ$ kI{**4AWk+YlN#FdT-prU~@$͖$&*&XJƑIDOWN4[DN$-hIDAT:BuԌNBy]I N}$/ҴW"J8ߊ8bCݠg+>$=#$uHy$@

"ٸʤhK$-YP j)# HL/DMHZP^R%K$lvXg>#^kMT$UlKV3rIDAT$$IwZ|"NNHܛ4ܴIjI2}LҧIw'/%ŗ;v6IF"I%I$D$Tb8Q$bJ["H}{2G%jƧ:)'$ݩIB i4H )2$"!bN3L8I $M7&'*~u$_DAn撤嫃IDAT&zF&饞#'ER$IG+>0I**+ƒ%I?W${ $IFM*Y&I#;@$'$Y fI҆Dv(I{x$*xؑ$& G/NYHew, I6\u!+)"aX~oMY5I[HJHIUQ$_E% [DᘕVԌI]CB`D=vbYYIDATk ;Hm{䛐 4!I`c*jnSG#I[H{@!DI $gѫ$-o&IR$i#"I:$᷾Y$UDH<${:@$]]" u7BwUD{[l.4H2}4D1IkD>nJ]**4MEU$=}5H$۱$ې&Inl2Odۙz1IDATGTU%K푏F{_b\JHQIz ɬn$iK$4H`p$iSU8$HҶD$ID~fIDOV$}e-; }j`gы$-G'IUDU4HZ4w )ȓ.EnaH#H徸$=$Y礧Z"IVQ5I$i_MN9'vD̤}LG߀wVH;ީm7]C$Eb=1IZmF }._IDAT.$䫢j"DH=ѯ촕$ivy&z؉NAqHR"$4f$'~E! JE"nhDw dVQ [Ns<⊴N)1fҚ$&zI'A)Ij2UQ*")M$ND(^zh?Argфۜ;Z"QIB#IR$=XJN`vhD}HX8n$,5ohHzIDATc$"=Q"ik"i±#/(ITjUTof& ǍI Kҿtu8IӬCj'fE Ioh͢ Io`UQ%I-~$ Y=%q$ $vIBrP ZDUT iAd$DMH5|c|LI' U0rNFL\P.$edTQI%I4f2$p@w㢁J NVIDATG$I=('魶'VnbDtTmHa3$ܱ\lh-$ٍcI:vIwRJ8Ir;M4g$IG$a;4'\TZ Kh {.~q6I&zI-; forb }I3pͪs&)#RfH+Ɖ4@XBr^|Τ|y;pzIx_E"Uq;'t U ӭ8wcIDAT 3-W_Th"IWIgRt>q%xA%@ڢWhe(BMyW1O~xm/hzmS-g2Dx)‘xKY);)x)ȥcpR-sTˏ?੯EJ ղ)ҥ$_H/*!G.yPвIDAT{an6_VsxxU"uGfQ?_$<۵E*16nb >[Ŗ/+w-+ֳ)h"U$ǻEiTgr&_rPӥ:۷<ҥyz 0}I;2XsQL9-Y{ $R178B-6!)zTt e"9PD*k')([/3&RY°iՎkIDATJ|ՋJ"U3Q ~;Tsiȅ{ ^ͧgջMUӭI՚-~TVM;t#\nkE22n~\)g]1aAD]HtR?nyzBڅ;LJFUojH`] _(Rɗ D?FdO=exs"2EcJF6*IDAT q7u'PJ_⯈e?%VPO<Q$"&~JFN}_ v;0[$J= +ZW:+ I"CHU땂S1EVj R-ys"IE3#QH"<`nN9 -ެ9)8DݜuT.d"2E*5?Ŀ`2D;zquЫ[Ay)›Ы[5/UשjOk5ē tIDAT?"-}1)w<2ҬTUZ)7IbJ]'y_Y"U)/\ /?[%~N5Ư"沮~_BtgT,KZۡ{~T;Z[+\kH ",O Z#G IENDB`fotoxx-18.01.1/images/vignette2.jpg0000644000175000017500000015044013222767271015544 0ustar micomicoJFIFᎲExifII*   (12i%RssPanasonicDMC-FZ1000Ver.2.12016:05:15 09:32:04PrintIM0250d  ' ''''^''''$Zb"'}00230j~  /|po8728728720100`@Js  @2016:05:15 09:32:042016:05:15 09:32:04)_ [ Fotoxx:paint_clone| Fotoxx:trim_rotate|resize|Panasonic@ !`l "$%tj&0408'()&*+,-./0123j456789:;<=>?@ACDEFGHIKLMjN*jOUX^ajce@fkf@kg@khi&ljkH.llmvlno~lpqlrtuvwxyz|H~mHmHnHVnHnn \fono vooo`osP s "s0148  6sDVEPDB AFrY;scc sn??,,`bdfhjlnprtvxz|~y?"r $&%(%*80 24T6> n n n n*XXcAEhjlnr  UK2r r  4 4 4 4 *8U$>. " 0&:&kk@>kHMk6 "$&*>.Z(,0 2<4F VZX!DBDU|~JUFHNP^X@B xzk \ dbfLTk U:p *2*WB6 f`9d<e@B DF4  hEj,"2@r$& "4(8*,.0!2# HJprxz |~ tv  XZ8:^> Ld~]\         6            <FD` b } ?   5   @ B D F H J L N P R T V X Z \ ^  ddST " Z $ ( * p. 0 2 4 6 8 : < > @  N P R T ` d h b f j p z | v V & X l n `bfhdjxlnz|~vr ES:      YCB   0 (           " $ & ( ^ * d, w. xg0 E#2 X Z \ ^ ` b d 44f 44h j l n p " $ & ( * , . 0 2 4 6              qzATB< " $ & ( * , . 0 2 4 6 8 : > IA 2    3    2 A     0      DSFWj x   DIS`bDd}rtvx{z|~R&~~~~5~  (.~RCN@B2D+FHGX "$ & (*,. 0NCM  $0p DSCP'=%[;EeuU ~`/[7wlWueUqU41eSQ[e]vE DISV8888888888FFFFFFFFFFvvrp~zy0wklkjffgjkl_`ce:OR0VX\`nlmmefjjjmz`{`|`^}[}[~[}[|]{_C\B[C[B[G\G[F\E[E[D[UCTDSCSpCS3>W4AU[AUBUCU6DTlkjglmnmmm;O+F$I>-78 -0 ,$YOMPPyZBW8LALG.Q>04/SZTC8S@GEP+-%& "$!'XzzdPZTf`[(Q0')51&6,%%'B$&-%!)60250(,(   -,7%3(# <7F;CE,K9>,&07@=5)-OISG~~~~~~~~&~~ ~~~~&~~~~~&~~~~~+4&~~~~~-5'&~~~~~'~&~~~~~&~~~  2       ~2      ~~~~2 ~~~~~~~~2~~~~~~~~2~~~~|~2$~~~~z~y~x~~2$~~~~~~~~2$~~~~~~~~2!~~~~~~~~2~~~~~~~~2~~~~~~~~2~~~~~~~~2~~~~~~~~-~~~~~~~~-~~~~~-~~~~-~~~~~~~-~)~~~~-~54,!~~~~-~ ~~~~&~  ~~~~&~ ~~~~~~~&~AEBMT :>Ru*/\d^1n.e.YJ"mb_!i3Ga,J!w @m80Y /jKOD&&G26hNJPRSTu A_YGI]us15T{qu_=ޝWv]M=UuW7_Qq_UUCQUG}u6RHKQ^PY]MP]yק]_QuET5qU>Z,Qu\uu}iG]iM]DqQQSSuWVwF\yvֽte۽UP]T8YqUVQ4eUWDV=Q E?1T}UWct_ЕvБёq&vPoW4Q ߝEtt]uY#^6]q)Y}7QOůU]\uqyqxGOW'KL7]X[_z5EWQWEcP{u=EtMUIU-Wҗ%}[W5Tm%}TUacSuuYeJO'U=)GݥTEV f2uQjpQwQ09J W^x4-r9V3tX:Q^=Elw?^Hew_euzEy}Z.257e-_]DC EVw@qy^ ]'}vGAq5Q_Ywo_Y}OGO=SyUZwDt_}UWY]7QYF\&]ٝUd]U_U|z_U_UKB5_Wtt][ U7EUDu$!FCCVcfss%*r qT7?6-U}>U_g~ ^74I 7 fa5d D `m u6Q 9. 6h V? lm /N ^; F k pI tm%G>T B _q8+ p S Sm0T<^ J +t  ` m?L`5\ [Z ZNfz Nyu mH=^ Yl E!~ QS xomB 68 (`yvM eD 3? HTr=gA\SQߝ}]N\EUUUO%uwOY]We~y{atPGt_=U \Ee{ZtQGnYld5TIA_M_qTiMSM\OY\V\-AM]BԔU|EfAc\]{TUqy}W]QUUTVE-UmMU&ԟ׵U15UU3Q1@OOM1WQWtISwU}[xpUqbUw?/ML$WwYwWreedu|uw\Y]]IPe_uZX{0:YUP\UO}}Y-%V5r/@[1u]X;~^%g]5 q]4Mmd}}TUڹiŽ?T"SQq_Y}q4_=WGURUeDUX|mu WgSWc_5/ܜUR; u]uT@}U 5TQ-}=_Awwӽ AYmW_9P?=UUIEsYmuKM}u{]]]GtT'WWTQuUWU_wutUEeU7=~{1wb{"oD5?_Wm_ST06ՔciWUޟ)4˱WHmntu}uXOUjs7-_U_]_V_DVluttqu$i%L7WtDe?5EMtqT_6]_'W6UwpeՕur'uMTEG*]=[D|Q;sCWtOMo=DsM%7-qQ\guU]m VGU[]ַWSioG7uWOWp10G_Wr=E]Tfm%M_\U}EVuQ>Q]EwAWv}QESwTV[]16WEy :_V5UZ1mtl_rU\CA=EUW Q_1E3eP]xqWx}U8^uU]TNdyOW^H^{[vAt?wwf/ W ]_UCOTGSeEpUBW-Wt?u3VqL3u}e~Puwu_|'cEp?UUw-wWuswG\{Q]GwaqYӶWwOUMS VC\KoWZ_tږQ -o}R3%uGVU6N\A%1Uy}uOUW kDN^U_U;\ud3gUP]e9wh=חPfRUY5=u7U]UUoSum(ҭWU!}'x]t9nVM?W!5uq|]uSq}Җu}A]EU+W_GY~R{pv[cA}pZol 'LW13QmWyUBTuS]W =.A%AuuwUFvGx[>G,7\]U,PNVU}W/EuqU}V#AT_\IW[EooGUMUWfeqW{tWWc~wU_]u#|}mU]Q^=U]9WAӵQ״M$q|vMEUUUWtmR%U }[u:WUQ}UDI]U^uISeUUS٥Duѯ 2]]U}}upIQQ'U]pp]|UsfW[ k`]CMSde5wqT~v?O Ls&W_U}shw%uSWw Y _u?UM|iݶEsP6_ECW[bQUS<Օɯ7qqgWVVEesu_AI]~UE1EuSzPUDUYUSO_] _uFgU]Ua\1UmיUGT}]uO% WtVE^|ݮWey_=Vur-UoYUsP4vwe\EUgW|,Us:N6E5EWMU[G^]'Plu}hrUP}U5ps}Ww]; n[+u?ؠu=RsU]J}?_=Y/u7wu}UToeA}Fz}TCgKMG87.w5טreeQEqUv z3_}TU_e8dXQY}U'!FUAW]UMNW%U}tSU7TweY4tUY7pUYV 9Q}ǩð|UUE5]CxSij]X]4mEfgUQV 6X%uBUW-фUTE]qWT5WcqzT{UY_[=W]~_seck]wPyEwVOww ]zOozu{zUnQuU]OGM=3U-PuIQUR_msOUsU}y%u|O}tL]թ:6T_]Q)TA_O@}WeUTVE]F ]G_WTP5]q\uUvmuT?M_\Нw]mu U]qeutNv/^}]q__Q#@xU$^^Q)YluQIgeU]5aWuyc~|qu\G7TSWWUGAP\{6Uy/zp]MMP.BR>'7ZmF'T}TFUUTwuTUoU?v5 VUq6Im۾yT~?SE]WUOG=PqE]URr} ]A'E1{3ÈUft{\o-%CTו}Ttuw4MS KOpNߝտ_QuQN7e>Y]P]]@ k]Qv\kTEv@tyW)vmVWTvT2LQU-l{U!o^U=pU{M<5uUӕwWuT_u=Mm E_{UDc7q~| QUUWGZST4C5iSuUJ]nf} ݇qwAE!uPWq`|DG,^OUX]}gqvyS'nMum\UUDEUFSYTYt/wL&uU_QQ(M~~TIuJgW;Wfm tV]1دu)~OI[tA5UT_WEWTHP{hz]u\\YU n\FEeyZYqPA[]L1}S?IU5pV#M}d;s}!tSe=UQV$]]VC4QWutPM =Uyw_N''Wn1{WUמlLe[ emSU" U\oSqv,rG ~m\_EW$4=E1uY}VKUUG F DE}_AP%"Eu&p+Q^3dύAt}Q]Q_[9d$D1}]P57-aՕ9BaP7UU5YEaUwCU__%}VWU}AW]dWnWQSN55}]YUWW}U7U @ QUV6VuQ9ՕNl=EuEU3T?&T*WF78tSWgV}&~ݽ<4WTsSyAA^SZKUGM05]M]t5ֵPe@pV]W=sUIOWSU\ _u5?UYRS[ 7M;W/]E }=U=[}U_B=U7?u~mrN-Z\ @3UhTZTU__ U5w%]Ӱq|se,kq};+NYWED[Mgu]ZUydvھ7]\7@\5 PRu! DA._^KG_=Y~ vTUqTP_T-_UЏgEKmAT vU?\Xr|p[|OwM_AUmQQU]UU۹u\V"uLU5UWU73]Q@ē]7: !]/}JypSwWtuzUWߖM=__}utW0R`UVg5UqS]tTuo%<5SLG_5]W^Aut?asywUu10XTNwu xY]|PߞU@UUGU}OӅt%_]+eŵ4 t_qMX5U]U*2WmcuWõ%m7WQ5sIB_D+k]Wv%wQwepU{_, %Vg4=eQ6GW4+5-Wkv_zq`]}Xg>-AvUea-W]߱UQqt{tU_U QCUU)catѧUtU'W H>GaZ]4U~0U7XV_sf[tuXRwUXݕQWUUO O}?7OP{Vf-HSUUU5<U]W=#U:OQp5Ќ7[R߷DS We[Post]մGC?GVF{5U7t'_?IugS4QqWRBEU>2]ovTUZܕuUu@V5UWŋUU DYYywFpg^T= }EGaM7uA U҄]t(q]\K]-u_\uOyTn[sXE_y{QcuOkGGOQ{7UUDuYguYS DWb]?TU6[75{]^U_5U555T}TE}5!_?MM_?UuEPْ8$QpQ|KQ×WW_XUՄVmuTdq$u/WG_^qQGg7GuWQUR5RQTU^ U^pScj6X1\`w?u4_auQ%׵WdQ_Uk[UwU \v,vw]SuqwQC=_o\;_UGCi)RFaZxYVUU]_]]5]{_OS]4%LqU5xU}O/GwSpQMА6x;D}5Oe u]W{syM__ =^r6E@qV5ՂUt{VwU]uW\@w_s-? UUCEtt\UPo^ue_eEuoV\QD}]3QrUQWsu^~u֟]}vTr2}]WO-wmE=}Z{FKTUQUwE7]|Tig.GsW_Uu[iyQY=uTM_Wq%WgBKGф}TTW_,WtUt eY{E{WuTY1EEU\A\\Ǖ}q;Eut'Sřu]4uWu?TlET~\QuOv6V$]55u}UTU$QY}uYW/O:U_`'``Oafcebg,k^tY-sxv`vy|뀤yxuZ-+oi 5@ywR)qJE5aC$#mvgmpq6k&TI]]X4BA7+5w.I ?N>p?F#5jڞ:QTW wsU,F-#+6)E.3..J k Vp{!09v;=n40g1-n `)^=;Va|"1-x - i!*{ U * D+J\ l90!<  ZE ?2'EMMtMNMMNPQ$TIDX,\rZZIYpy@[\_ZS%ducN>HV=bdQEO0C+ %I;:@306_6K,  %w .#x# 3! #Jpp]#3?mK~LJ]ןM/+Бi{=/}<_M{BGOai+Гh|_MƯsmK~?_x-G+Гh5{Z_}¿ >.[&H4{=/}<_M/+ЏhUW? {¼B7Uo? |cM/+?"o=_w_-G+7UokO~//88}ݔ "[{TgjHi`hC'Ea|jӃ eaY6A\xSKcRّH*H 4Gu)]ns8ރ&Ƒ*Ri9 )+RzUzTbOiR+O$f()zukgYJDcѷla0'sp4i,mϗ6IDYY{ۅϝ[Sox8/S8RF3(s-waԘѠ-$ISG=3F&r@4\$[. ̷ d̰)q\䓌 {yKy4H;YKھHoc ǖI菔.=ߍi&Q6;6>@F!zE-ʰ?*z{N"<7tEiK(NxBקJx[RUKY.g.$RФB'Ϛ֍TZ<_W2iZ4$@vqa^jL$]wxю{z䟭V*If Z oțc'duG̞b궪wK ?+;> #XKu]>}T}ܔldW1^L>Բ3)f1ʘsӱuqԨ;l}+k_Z%*nVqH OH{D'{=EN\y0@$.- ~ߧZ%ȵrk6H9sʣ4(4屄T>g.KOyKJ2e89zDEo4JcqYU\ }dU|Wqythg;2FW?C^ףxRmoG-p^uC0!  :Us)_mLJ.xgS&K#,s^,|I6Ljܮ<^*gFQdXVy]: 6=T_hwDƸ~Kf%U6Yǻ!d#E&k,|ʶ`RῌPmi]NsW-YpVo Rrt->e ;ei_Φ1F;יjJc=Z#WA m"i5]nEH9e!RH'8 c1^i_ui wGd{z ᄭzoKnRF%3}JcL|x*✻/<Rv^G"B@ae_>6id/r"f4t t?REsE߹L SCR^J1m&wf2^şA19k z9z[ƳL7EĠz7oi~O r) l9QX4G4WRF+nľ(`koA!.U46λ:y:v#)<:s7t}*gZ;e ^L=qӄJ<Zd*s3+4T<!ۃHH}:Z9 ЯۤLKLd^}ǥzMbMFS?~BjՖs~1=SS12n;;Y"OHX.V{)#Lq+Q&"-2u9dI;߫\F!PO?{?|k2,BpJU|%s[TcO,fB[8'Ó]Wom?Q` :eY&si(9dvV~W-xq"Ϡ,}vwq9M 55)\ֺ;{pOҚlu Exz'B0ۊI2Wc?]H{;*?5PfLxH@GZؔ)[q$W5IuOjabmVPsn$?iGqɶqˤK(iHaWlmfx ]㩭 =,B?v['$W[Wķχ-ŋ[G %`@Y|(Dzv-)s7-'EnBӵH4qwI#F6f-5?ZL8,uE?+xu>:zj^S F,($3$5xFnJreo¤qE#>$d*w5ŋuqaR\u^|A&=ZG 6U=ʞ?ZO~i$Onܧ ϕk2Dy3c~ÿmF>ͶQ|ѷ^:p;NIKTsBV=ZB]$R5 CDE$W5dμ3z ut\- gֈ;mV)9ڦ_Q1QL@r!^mCM7O Cn#p㢚¿rBaIE ep?UXKĚi]Oqp 3׊t\O|=F[w_Ʀ9il`w;$hF2TEkÖ3Ku5*뻃]`%5KaI2X)Ϯ UI֦QL&c/7@C5Nz]c&F|xmɿ-E?8W.RWzWLggx\JT2Þ#mRU6RȪZEb̠u&6Cs[I r=v:OA\ 97|KFH\S?0g< Н +!1Ld~+Z"NWJ{"nX Qn~:YW$,BgS*ΗMͰ=džxšn2m+)aY?@+otS,%++ p:q]lĮGxS^k)USռBFʣQ л]ZތudaU=:#q&GNkZvI`>$KY!O3 5vOޑ]L3K Չ0.}~yixumRHԵ"YE*}AIt3̭/T`AǞ9SӼg=x )J)'.j~!nDy\I'$>`r&5\؛+ u yRk{2 ;:`ZxI O1,[Ϲo_CŲG=٪o1x\?Y?Ɠx-w\x-w\]QTx^9ǝqu/&>.ouR@Photoshop 3.08BIM# Mike, Rosi, ZSulinavhttp://ns.adobe.com/xap/1.0/ 1 2 3 0 4/1 False False 2 False 0 R98 Romania 0 8 6 3648 5472 2 2 1 5 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?P((FH6ZB<̉˕o .l7S9<5ytĞkޡMjxU 3aL]`W8yG<ljy,s[Z}s\u],K^]s\~zAnk]ڞks JVŽ,O5{c 3*/vvrOA] ·&[ȶ<ׯWK yTqR6<SC+!uҬd.]O;C-y$ 8# pG6'iEYUHW<S_9Jk)4`vom6BtsQ+3tsZۆkҦ8^k}_/^Z3DQErAEPEPEPEPEfšԤԸu젵8kQb' fB{5H@$k_hc\M4Pg`NF:rvQB7}TIÚ6f?o}wD͞ۃ޹?jzȸVWкn*ݻW> 񥭖ss}ib=졲s}k5|Ui1񢴝0'y:ٝnorC(5i&ϩ|yt8 y3p;|`׸[1J?oaC&D&tlI} WDzxBhoY?ٕz^tL%O81w-b`dڲ$եʌS$Mk6+*[)ږy.'IA ;}ɯ7%k_<3hq*=+֭!W҅MὍ{ǯn]96űҊJ+;Š((((Ӫ 0w{&QXwyQr"Y @x\I ט^|KIeym@ Ԝ~SOZuh=2EgHЅ(݁~s/u>ͩO,L& d c׿|pL^(YRI_b;z}FG WOğ\2O#+<`XʴZB^-q=FX,! =xፄ^"n/U!YY'rA@}M| :tm {LC쎆Bp~k2o|ΊdR5"\u09]Z(Š(((@`lvyS/7i=@O7XiR#m5/ů^jr6wo 1I{ڤcah&;JsGBC^~^ԅx}¶1GlNy>TI|:Kտ|psdqf==MEop1a` S1֯:͟ vyc**EԞ$xnkFt}3sY_4 0GCA|_βw76alT9PP㪂S_|n/uiڴ7ϯ[\9K+iKmd:eĈ$Y)qԣKTA%#x*X-~kmy ~\e;w*TV34* 1" c~K| }(}ϚGg]ǡï:?omt$wNKY g^biCeNXz/Yi[m :A@e/*>0Im&C1 H<8xK~L+H>I 9`y^gW7PK<1)bV.hͦGrZ|:MZyDF0l_^'uUeN[X.'|̸1v=G|AyŬC>V[gmgA(X0AkͿjOן/ (p_raG3[OѵnH# {&km¤nRNs־(9G;Bլldo¿&op5XČ;z'~d FI,R݌L G^2yci/] vID18? t{uU T~~\_gZNYt;7g!#L9mc^7zlBGߩJSaEc>|H@mj >`f ;};WZ'VR6UHm)~@#I,L8"T__G_Zk olOցc,l89ʙGrWmT5Tn4,mٗF15c+Ox^GVUL\}:5^ݐ* ZM5̏S#W^ݢo k)QkZp ++?Cԯ}ڮwhת-ۗ9m`p@'`g>elh8p!M>a4hߊ ZMtY4mewOݹ5f8:jMjKχzc&m-Ps Lk@u0wici[\Fq2(>< 09* ((( z^1']{$(W][21}Gѹ,Af,GNįn6b9VIf ~Wuy_ xT5v1fA ]3HP?d{kmҝnF{WU࿆isݖ6?wֻ(ە{LZGA9ijgWNJ:l4HHEsYhz'KI%\?LWu_-n:l 2f<:|#ǖId9hҊ k9fHWJ ubQR^Pak䰝wıYz2=_kǿeOi^׊PGxBWgj03xKN[s۹- >akQy$ГOhUMZ ٟCƷ>IilV8f'$e޺)0iV#-?m?tE 0Y6>Cʔ9+35͹AЬ^][˖X̏eg9c=O5O_s$k``+Ah ^u{gi>Ӽ9.^fQvp>qNޏjF*~+1;&`{wmkJN{m80g9#~UppWOIhĖK3m5w>!Ѵ:H/hϕF{sμ>!h6[̭kđ1=⦴o~+|Kq5iO:?<>׸x_aף :T:]I=Z\!8@5I6px%#851~gЖi@HZ}k`t (Š(( ׹+6G^v3>TƎY`gpk▫ot2?ݤRIk5} Xq8q j5`{fJiPs |&+w]r9 (WsMөי֬,zc7ѵKǹfqn.^V} LV=泩@Y|c涧32O11c{ _(z>;[ye4wݿA_Q2<^f.$:1+/bB \qzWpoao+[ǼK]ռe%G/WbPO5ؓ6{^Č}65x־o_\_o5 R~sS~bm¶R%LVVKMJXmQ$?:/\[ht>RLM*0K\9滏ҥIx2 >T</h:khFKԺ6~}pʧ1ɬfi(Ǐ|.E}uˎ7XJ]ɦ鲞 EXC۰WП/m1H'?ּY-Eh:W1wBy΄-QHaEPEPWS.빻lDkc|wwci˻{GJwIvg3 k\-}G.%v5Q<<_a'@,W>8Ӛ18걡9oӳn#3c]F xjK]L,mrjPT>)', ck^ݝH'^~sW%%i>s .gX?, F @5+xگ'5 {YBh;V$,'森xWLѼE)Y#%\`JeTtݮzr5MM{ޑ%'F{Ru2<{~ϟy[7HBMo1n=p.BY_QJ~:#[sSfT|LVC(O'[Lsڢ]ŽI<  v$~k 0y~O]X ȌVN\H;fDeF[ -U<ˎo\g Oڀ3'Ыz _@@򺬛ؖ8^نiOQI+Y]BƙnS _<1}ŗg=w̫XU`T/M2bq_ͅ3;Y ѡ#?V:Qƚtޢ2U~{@k=w]"sp:Wg.XGwCsU럋%`CFkt$<9La7p{bKß@~簍bD EʛKiî} ^]ţYf%IF@/{V؉r>>,i?v&Pp>65g]lC A !~T]Ei_ĥf8kVw/CeK$Gܓ>Վ"$՛i¥T+Ԯ4[rK"Chds^@_I,muȒ0zt3_|x/Q[$0TѲTHi$|AKmV1hQa%jĜ SJwt>u7 učr-1/nq\I_»Zy<9fiXOu ԯgv%so>D;V>+լ!Z^=Q^f1 ͓֨EB ( CҖ{m^;F/F1~Z?޵|ujRt+&@"MQ/+?-:^VF$#&p}0#ɃO%4[.dWvl6G\ $ B#фQ\q2ώFH~xx.!>ƴmfh 3#vK)sҨ G4!'пgXxN֋˃D*ZI1çr+~&b-#C 1ӾanoDYG; ZRvj7_|S+`|-Cڑuu~?ׯx=nQafb~Ҽ8=+W0cidS|KR~+.@kB: ;O^' R>^G ;-bԶ:tfk/x?+SO95C\c\P:u>|ou{m{ `\L;)=BIWJeiflK_lI$tKsmmiЁTg یrryHJBPG5@=t0YF:3j1VCu-4{ xcJH_d>ݷ޼\齎>\`Wv@mjT(`QE-s"V2<{GM^-2YI(ϥ|+nh`;|Wܞ'9oj&ͣZƍ~O҆]4dF{MK "3I:C2\3Zvr%]GN~kƬf5.L 9Q?]2Zi+q= ux NG_+g2c9 E0+8inC1֮{¬HAl6DJH yvҹ;]*U ݜ&eq^ڟ[|5" ɦlw\n?JaVMvY+slL uXq^'4kf] }ko~ׇe\/>!~Zo BN᎝_CNqiXFWnG*- ul2R ]߃&.+mLt8ǡ+B}kGԜ;6}5)s2zׂ"'Wlth^qv> {r<߆+ۼ!o?=gZ )UQEQEQE@F"cZ}"B"~[{?6/IGWfeWA9IT>n'$r2稯H!R] G)2W^T1~2AVsjZUP=H?'̮~^6 v㞇ҩ$r7<޽wㅬ6|CPF(@$w9?x̌D8x+~2=ՕR RɯW, <!WB>O^ULDGH=>nϽT6|G!HƅGIMmR0q:sᖇ^?nn4YcIYHȯX"DhaSswZO?fhW$Ԭmj W!Ϝ #x?aA ((S)*0:c_ I?$oۼ:(}$8ĭn}'8?Νg;_ƫe=36rJt ~9x4O}jXؑxu]X{JݭA$Lcjp@0ɞkK/E͝[/H ~T<7Gf1qvK9;}qwDR\oK}{-!99 W _o AQ#ՅSz{ʵ6?] @ %;_]\N2E_|BQė6v섪#-|3nT uİ"DYtA rY7h.'+g#5iQ5 7T}ߌ.Xv ,V~P$޶j{%S`Z\n;ت1, i!,D@}߭n0j<”-0i\$Ai 3c/y+mG{SƎ.=R-+`I2cMQMI¨ p̮I}/.D9 ]ZL.xEE62ԑ,ɹ   D"CI|}YeM{;MQ *\m*k%H终 %PRP)t] 8N9NpUߨ)rs(m94xz02P44;RsS#l$/k@\ݱx~lX>PnE&=(ownCy7Ck1&:=pMp\b=?v՗NKИn-V~Lo@V_섪qhspa'TSȢ6]c%y[o@gjV $1OKlaY30hY(ŃDRKt#y5n.C{}&M/P\݀gjSaYlru5}NY7C'*]_0RZ:eĔHw-M;`3SwĻTxew~ieUF1ƪCcR!fnp;hHRm=LM(vHНQ|?3uWx/:H<7sʧ^h:w^E^AS|U]5 -8F†;pQCu؉ocߡ = m2lD*Yc J.[xUA(\26:73mI; 4]V\&E "_Ev܆,r0J$w|t< PK$8$F;*d P/YKY f@7PPHl82u3Adx"pOxYH'xSzo8\e!O тu5opPMca:&=hO)hwhZQ{!7H]l[jK~K|0/Aa%o]Y Kh)te밨Yܿ.dA t<{,e -om. 6i+XіvwcOp9߰Ps44Z0Nߋkc5tw~MqKIlpƳRΚ~o g? #|؋~HuKFC ړ>iY++S1WT |h1;%? z^lz?4q&Q;IDOGDoIհ 27^ %y(tiEX6m-ޮXLJB^qmkc4k*!+cMpC s_瓤h]^N#Z cS|%5RZfc1"0: D~J!a,U;ewu FbENAUja!N Bɓ+ytg:PAvʍWX=N.P+=ߣP.VYze"C$69uu.VfcL)hz3c& YIi4kPO 96?v j͊Mjŵ?CiE3M駄xY){5!(i"y +܄7A^0KeӬ psY Z)=821@Ϛ$<;51PZwwQJ2.s3Lf LÑ:ln(B=+~Et%!<]Gӌ#Eʺ, z?O7?H"?=>7؂ܪmLQNƑ^d.D=4e-HS- C-K`|0HΦæ[&6gl,C# o!mšf\q~6x_'64h:9Xjpz{!웈{h-d*Q%sJ~Q;Љ`r<.bx_llCOAyNEVpF(!d: GZcqڱt3?ͽgPOR?<fNժi,g'S,L~+mXǡq<!--  3QS'潂x37358#)۸>P\vgE^J~`{EY;X˞$: h>hy*]^ R"JHV;gxKCCh4EA&ClY$* ]]Jf,]s zQ`-FOu#a ׃ylrt̪dkC;_&H]YkFʼn>,_$~+?]NE>i'U_OlAFp' uu!fsY/ ~X,wNN }I9ÜJŝ-e$3 ZAǮ=z햖d&KC;jl #7d$ /sUwcz'K`j\-.]Rk2b&s44ž G3`öָ;Z"*"הk#514j~zڭN rQ \5_1d.:U<#/%8@*ݗ_E=#Bc宭oWdyrʮnIZm"ӑDay5ںX8q_EҰz@7,@bq)%"^A!pn1U@]IMy).{ɖ=MA!f`Kߙ[\/l .WXE/C e5CAmYQ5M˖#R6 }+'8 ?za>!}ϻ<Ѡ (9 5~49)=|hҘ8 `/<-#ΊkƑ f,!CM ]c:ccKL!RňQ"qgY,h2Y&U߹/Ί/Sd"4̤Q Rм5t9O%WM;*,:$\q/ë3c:ҁ6|kw2%Vʸ#.@k,`4P?wJ}Y S*t}r3OPyf6 el~w^Bk>,|FD >8ИqX+_O0|oƩ2LVX@TQU"DZ{BGTX<9љ z"즩q ``Bq;+TUܔKFѲ'yOw>'w'|('7Eq|+J(T r)ڊB() r !_OG@Ta-ʱmKvbaZr2dO>V݀9FJ {³OC+XwSAeFˇO/T>FH6F`Ht{9z kh'Rz}3Qw/PCjt f][rbv7V~{fU׌4~bc)tePP c&n,"ύ@OtIq\RحM VĮj`/{EP7e/p{Q=M jylI:Ub#_aJe11 1&H iWmG, eDr??S͏r)7}8LU Q?AqFѰHB -&%7eG9z 31IS v3_So Ybz)e ?FHT<,Iu&1:Sϭ$6ŒjdwTfz24LU_ RLq!BJa˛OQۑ| %vĖӘ5>)T-: ,%z硠|eɜ3k3A <! {Pz<\ [ڮlAGJbEFBpQzH7$$a%TbH pY敨;nyvH-1S 6ek 8{gO=_]vyij0$p=^Q!?drx0W5+ o9E!-"c~h:z@UUSKH0%QHDc2%G(m:]vDy-jNQ G{Eb \1];*-VnBC~ R4%S_ ] /Do_|ό8b xq%*};X{;)͟p W o?Dž8[_]l psq,eXIfMM*V^(if%HH0231Y0100Fotoxx:paste area|trim_rotate|paste area|paint_transp|paint_transp|resize|paint|sharpen|NE 1iTXtXML:com.adobe.xmp 100 100 0 Y]N`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-18.01.1/images/alien-colors.jpg0000644000175000017500000002415313222767271016225 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 141 173 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&KdUU,G$iV2o"t;@fkz Ա$9Ҿ2ďLt2C,mn}Z?6ނ>xX1J_NQRRcmT" du#QˬGIFpn>loO5j]d+3OLݲk.%lm'B$u[Cmr?Q]xLRUR?1,NS dxYO 9;u|7tv:mKa=6d t8'I חoCq_B9?''^񦃯'։˧LzE1U C1# 8_izOďjX/ŤvqE)E.| zv4 [> csu5WÏ2e.QW>7|o;kMxc^Zj>gow$l΍ H8 V>x񭥀{c9kXB#@؞ru!G_Wʞ$ޑ/ ];.iVݑ'Frsa4s/5 -su[hW6ph$$e;_Vc#+[yHeW}jzvn3! $Pw*@=pzTxs4f $R -׾Z6Z)#ohzW}GmȮJ:X֍= &cM 7 Xx& I#4,+E'?g")?OjoZQi%Hm^y5?a1"G#96Zc\!M"X5"k,fo3(`;c| /ӥ4yOn|Ae@p3vqkoDMoiuD{yZT :0ܯ|?.gl೬X;[Y5H3wy@U 4MQ[G٧yLA!dPQ~eR}Ju=/:4k)eA+{x]$%Bԝ8<ֽ[K;Dȍ 1g9'Wzޗk:K;5lj,&>VqZ(ED&RIyVe65yOԬ!xMܙ E`` Jz+~Ɵ_[x[.aZ%Z³ķP<a7 hW¾$?֓zݾu,3ELC<زT/=ƹn 彅T.S t;r+~,-.KVJ2h36( ( ( ( ( ( (1WB[g["&UwX#v z[g?^&-+:Zj6>n,I=el 0:?YПv_G,O׿/Io&k_(nBfl"o6w|H_jWzyX&i 0ݰ;zԞ8|i֒{XY,xAb. O`imLֿ$Qt̙/HiTMu{!roE6OBE\EPEP^q|utW:NWu֯6P0C$XQX.+W)7{&kgxV?V*OrXW1ݵYL[iji_:A4m?Z! Ģ%~^xnj\w4~,LvIhI2ଘ.Z'!oyECWjFF@gw܋^{tgB"?98=+CO]ZiZ֍Zޮ-QE{)cF$w:5`݆kW֠y:M\JbtqmOVlAyo |Ik:~6mo sQ)y\7` m밽뿗uc.[/^/'i-llC @L DYiڧW9o'_!txm^EӋAeuݐ6Pρ4{"_6wc>5դKx>Cd' :#~7УsA]k:ނSKݐAP0= [y+[ 㺂9q$R(tu9 "|KI(#mѴM>1=a߂)8 t6KD45UUB@Uvo~sb|._`D嵼ٿ$R&dE8RߡQܥ~6?=46-,u-+N{ 4C$RDH2 ܁~zߴ78ji>& vm$;XvE4dc9+_=;_ּm&4 e$.ҹr8$x1%g k:]j%zAJEI(N[,wt_o_uuwHJ@69֖|' ˏ_TXe/Z٨(([Ir#ЃЄ$w+U_쫯 Ң3{|Aʺ *+7*7n쫯 Ң3{|Aʺ *Ҽ=hz]c-j lT*Zؓ$쫯 ?3{|Ab-b-?*7n쫯 `3^![{E^![{Ee]fuA7@Z l'W`وUUf<]쫯 ?3{|AEfe]fuA7@TVrw*^9RZ4QEQEQE+4@;D]ZCT8s|5J#nm$a-Lj.$z=V|} =-<54д~WҬukh3mꟿpv[\:7oc7]²qǭ2D'"zo?uyk Ow|I|'9y4[G*ڼv֫ hdvʰ?!x~)%kޯe@]c|[tK[-jqi*Z 80,ݘ;)˷î㑜 /mVxOѭREB_9VBݺKvWT<7ZA$j0y$/T7¾:>t^xOouJxR8 G6576p`WojZ躓Gg5)䵽]&xW,fEt.FY؀|9t_[U\\o}#uH;)`j髂QBF*>jd|0*J ( (9>g&v쪗11I>¸x/7m?X%DrHaxϮR$'DY#pUCXg$kH$64/xv@ÚAmi)ucOzk2Uڽٗ``p0 {u|-B֏4½ Z?EѨ{umu} 2kdEȄD`$9=+{B1 ^O*<| se~"^["j#Cwm7Km"hP05|#~}*7lRW%|7>x[DWKH덬ãziʭ+ S{o]zn99`2{o6rKoKex dPDCsw|-B֏4½ Z?E>-"; Agd|&A?+;K擡i1H}vZ4fotoxx-18.01.1/images/favorites-edit.jpg0000644000175000017500000013477013222767271016572 0ustar micomicoJFIFHHRExifMM*bj(1ri% HHgnome-screenshot0230ZƠ0100Fotoxx:retouch_combo|sharpen|retouch_combo| Fotoxx:resize|sharpen| Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx, :http://ns.adobe.com/xap/1.0/ 403 586 0 C     C   j" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?iRɭKokgm )B3;-.n%q MuaO_M+O|'a-4WDY,mD+<^t㿒I'I͉g)H>ܒto MwF!ka=q(rTzRͥܗyyav_9$_no |Du[.8hv 'h=o9EROC>xſdK%Y%u Q[uol8o*X[iUu.Y v+{2DT mW̊0v{[4 ǒ RqԂAT6P%yA{/7F q7tmgFo)lrmHT'ɐzW!~hx]rM*=CQh8Q*.HLZGle8xIXږED4{Qu{V(%⸶!l: I9_=.^[]uMWG_Mi:|C4#Ǔ"( HG_nu~cP<1xkߊ_נf$R43C,_/$ |Qex.ouZ^ڵڬgTut= @% mh}=_nuk?+״y֢d,Gey!AV0O~?'x]R!Rrl@d`hdz q6^^Ѿ<ϸY q6^^Ѿs>d/?/yA{/7F7le8G3A_nto}ϸY q6^^Ѿs>d/?/yA{/7F7le8G3A_nto}ϸY q6^^Ѿs>d/?/yA{/7F7le8G3A_nto}ϸY q6^^Ѿs>d/?/yA{/7F7le8G3A_nW 5C1 yk$, .݇rpq+ڷ׃~ݭ#PzԪRwJ^ $|3^LwK4qr H?WS]f!EPEPEPEPEPEPEPX=8?TׇᣍwGNxDervetqPi.B Pƒ'$ /?¹%{e(mFݍ͖&guw-G4^F?A`5,H;G*O_NOZޗG5R>huE:.덿xkIo][kU+!#rl@Q]5uQ@Q@Q@Q@Q@Q@Q@juSSGЍ:8f-_IuY-r̄FT6E!O8:㿃~+ͫGF]@iq-ԆHA&v̠1$ 1dgjOxN+}WTn>w A9G0^k6xc )|2%ώt{:f[Ⱥk [r\v0<'ΤQWSxuVoO6(gnz \}?^x<> O;*dn2yLgv|gy k:߃ j- Gi͏3n2F'9^Wָ/:⎫mMCq\vV<Α}\ĚuR5VxD9GP}AVB+ Fܹ##ؚ( c듊ռs^OhzLrG^#YrH@ki!I'F*vPO'ScpB\F*7 3ukx^I C08GvU_J.M\ vYUw1O'ڧȹv=w |r~? j>8޶ڕV֖I| \;GyfcSFڟ<{{qM,Q豃gLPCUl䌜*UTٞ,m9{ gZNuNEVQE`>354 EeSpr|}h?~5X^9uUqGy<5{u$q̓lav8U++Þ:ƃ=+'kH)}hYr0L0-#+;10~%.'vO&?.$i O%Ux_L.X?4KTPҢh Upcq$'cҴM>%u%E*0CJYLXZDQ@Q@Nj5`fQwI RHjr~=+kh9 vc$G7ٷ-2F Fq`GRխo>ᜧm#3P $U oiwrI5mےK dnuwaXZh$-D^{#L%A>dbrFpܰ?]|Eݦ$ηi7wVZ[^HV;x{ Ыc4BUUAz^7e%~m7<ZFeZb00^@Q@Q@y$??!^^qFxP?SN)wwqO]QEQEQEQEQEQEQEqFMN]B4WOxg.ӯS",m99s?oc|H𮝪iVwII_v, H>.|5ҵOZ难ܒN@,b9S8;Mh>gR<`_i:\]Xdnq1`WVue([.{g?G];?Ky?1x/UwRod}OUI"*T0$>m|~l$ѯu1\H_aW| ʚfM>[Siw:fup$-l2/ d7 5?B3\z7W0备 [OҖ d׽rfU)*qqz^o~h2I.ӯLŠ(z֐^ۖV1\F$BGC,'|#j6]HwdJBU$v#h w_VU>/"xDYGnFðZ@3B15ߵ}'uZGō+wI b21 A$2HJUE#¿8N/ڧÚN/Y#hĵFI > A=׶hn ^3dV~:|9|wϳ|@n'5H_ˉY;aQܚ#ÿ =Dit-kPtf[[ F) XIe(bJ(!EP|Hh5]jRb@Ϙѷ[ӼaϥjHV( Ȯ-%'tegӞmWWT&L.Y=H*Џ+[Rƍ/|./5mO\.-"Ue{khF3e%3mLW'b'o# oڧ.v!HV0]qԃ _#u+kǚ_>tO6qE'Mʺa zw_?/Vgxnú]ߵksmmy3:VQ!EPEPd&C2=A9KE.ީۧO ϓny\UmGM;w \A?x_h񧉭d\.$5͠_2UR^N~R0I,NF2dcG𥝖#ж$o,̀0JR2:%]X};WJuB(n=Cl)рC((_7N]:[Ijy6fKJqk~#]x'`MFF l`_φ45_>-:uD/0X~ȻOC^7-GEխIueKxQVl. ck*Ӳ}e_ ᥔ)WvKu:\&XD/[)ZI$ٛeqH07m86_kV$NYC-EldeF[HOʣy8zGQ@Q@y$??!^^qFxP?SN)wwqO]QEQEQEQEQEQEQEqFMN]B4#;OV!DE%i# ,1MiN%OͣCK,͔v(K8S Q>H䕯;ghO L4iVzF(P~4;0Ek|<峓O'־P_E/ @Zu'Wn[K*>c(; W/ǟI~ éKs{q_LmQdoXVGmE+jV[-~B[o}ScYtxtj;%w\B TE29dQ u5_*Ҽ O k?Ecw_4QHH|0ps![}Ij8~pMب ]7_sne;//'2QYr i_;=$Cj),̤,g8ֺ~"> =7Qk;zZ|Kym-*/Q(*J|E\-X֢(|/cOxcF>)۷b'$۴[$2bEz7(?_N+[o"Y W>֝b>,440] #a2wj?*qpcLexkJUzW9| g'%~1?Ң6z,80!q!T;$Ԁ}E?? jk7=Fc UCWw?B8ʴQEQEx J.7ҸR!ҭ=d\KP ԏ]3V>(xH6w<{n.Z`M3EeƅeUnLSI F{噇 t68LIM6 ]:yuJ+mۓ3o'W7nBNZ/((((+'Fv-CTҵ JJAmiri!Tx5ƾst:$zF{ii)]f,RāNc+~ד~+I_^ZZ8mmE4<-rخUugnQ-M_>$OIf,KJ :]_Mt cKn{o3h%Xg,+$m5ۈ!RC!ڥS< Yͫ 5iV6ȣfo݂NYTZI|տ1JJ/f?@KM+ZO_sֺkj Z0-o"1M1m7`r6/> |J}7Ļ-tpt&w >)oyR*9'?K7I2e_6_o/|{=fjڞiuu9%h8<^[J<"2SSSlucoY[[LW1,U/˟֚vCxZ_=nʠJz( 9-|ixOdg(GXGDS0rq2i6hP\f=G ¾fx4?6ڑl٤&{.7JQ Oxsw>&Ҭ.~xG@/º.݇[JN+yC69'X5HmFw_U}C{K~3-ccGQ[Fa.L쭴6%%cQDE*`W^0(W~u[V) G]Z{8O!l$o(7c?0\OCujd.2wk~lAWuy3w4rQo(鷋"?v"6MĐ+bíD|_&//K%o\mA]ygSh;T_0;b? Nze/` N0R Vm?TaK7|f??5BKծ.&rI'clysz-r~ϕx5@F/c8us|rJɄ# ((k^^ )q]۵=b# i@A@s x5_hvc#wM6hdCu(mvss?hu jzeNnHf`B>gޑk֖S_]j)|Cf(-_PYͺq*6I;wq>&^)? "F ټD4>l͛А"nōܥj3IЏxKPֵZe֒¾Y|1PݻoJݹ>i_OFV+l_|rL|O/~'O马7((t`6D(dr*{ /m(_]XA[[)31P!݌7Omo ?xl UnN~EuI%Ğ,uuMCSޕa"-ƌJ&{kH]$w= s7 }e"NΕQ8g185iG7i6 ktu B6 B BHyQ=v^ A&tF=#V5h Jz> M.d]g+={_ioT3 ǔ6BJHiKU-7N?w{ETr3Uைn,~⇶@Ιو|mMs7EqmxdkNwK8+NJc's؋K8vs [NoRu[fwVS,C*$@=Es^=<-CktLdž)h=TP|$l랧_xV:_ywSe p~DrxWgJgc/ W~ε'-F)[ <'.?|tw>c~NܘrO=+(z"JѴM'KO.SQEᧄ?o-xXk˿~@5/^ඒ&:my7 6dpX|ZJo׮RsCxoڐȢo* Nq ))?M=O_|QI>eo߰5Ha(~pFNOz+7|Au*`\/oP;cį UcP>YwxkV?;VK$Y#l\y4c9]k$wu{v1'?N_ |XxYPL.ah!L3ۆiղ@qjR݂((+?h!1 z7HB=WwqO]sTӿ*(((((((N]B4juP%9Fc8ܠ#4;IfFv}=J(en͸8J_ĩxךRxxrRI4 F_(IͻQ̌[IJ$W 7ƒ+X`$FORjZ((?jo:_?cGS|H $6It nV>fSI{sOĒ|n>?qicp }0ݕlJ6j^o^ 藟_Nm|jwϚY[ͳ[ت( <%_ճN}[)<?|9uxCTƲ/k! v[.*+,)bVޭboŗ'ڭ|,%ZDHxgIBe$g[sk_ŷūK~/ŅQPPQEQ_.~ELЮlŻ:w-%uFx#'`*99*6m-o?{mkx2\]4b9\.wTu3<Ϳ)ŧO(~*\x-ލOn\"+f`F}gHuxJ+ɯa5ݥķ_,S$0$eqq hފ{]o^;<hu]k\,1#nG|p4D&쿯(EQ@E@|YռoA ߏ Xy+!]n`X <F3nkڟZcN T$Îw(~V|8v=n"tkն'7yITI5A渜VtҌt2攗4qM'XuDVk9ENTN]}Ŧh+4^55K7('$/'%Z-aJ0$ݪ߳75wΡxė%淗ԴmJ%m܀"X) ʑyi4 A% AF@f{+lQE 7HB?U"\SWE\4ʺ*(((((((SGЍ:_?i_>%?aIC ^qcu>a5ϐH9Ӝr9/l:(tHSĺkKRxoM>MU,A.Y(}M/ xC[񯃵}zxDI/%t &ط) cyKLoGju{vս pHV()_W~ߡw|H&Z\6:wgIm7 剓 `O5svfҮl5f=+I{|a7a#cۍ$}! ! K'_'$N=OP{[ݺHW} zVi\]?DŁ>{i":xXUOy]B˾1?G /+]ã[M%>+O9, 0s^KLԴˍ9 j:J]'[< _<3ϣZj^IIr0,{$4֏5nERq|`.pKŌZ:Z+˼AA[OhZI*~o,cơǫ[V6t٤E|d2c(߻<΀=^o |{Ş+tUJ ǽw!+dtXF܊`PEQE|H?Oռ9jR\Moĩ#ڬxǔ]~ JlO*Q-t F;l^ۡXb(T󟤨{[+#ط×kK?ZLKX,kmp?)ǾuK|]#A]6kS#ưf>q\EkhwmKD[EQo:>Vt/CJ )0ģgbo t#`p6*wz5qiak<ҘlՙbPFm^Q#kJѫԞ[I?oC7gEU$xoL$Xdrp8W9_ -F5۸..H4kḧJJH9W.6aI>/M#|Xy$$grs玾GcY6ؒxK,k4F)p3NJ|ÖG@.FNMH?{dF;?'ߪğiѿc_E26u[[]ZZXj]ZpgXcv!+q%+Y-1{~Ky>G?j_EJhZi.(I_5yPWs K>!h=oO ~ehޣ!Uy[$ x/~^4O鏮IIicb{ЫD u_AN'm_Zw]NFhm4*XjNlɵ- KwO_xlxDEҬĺgu-ں9++%9۹ڳ?eVî CwƟo:aA$p-]I Y?OgG:՝//aejiAo77qZ˷YbB 6y5;5Oߦm/GjսLk㿊-k^*Q{Kak{@̎쳨 YCW4 _zi?w֧V:M{1-#VqI95իB_6u-gvv=ĐJct:{aMi]S_zpkDžeCa>MΩs$6֋"7ق3cf$foNV_­^ӣԵF7Zf|SI&Bh帼U%vNekVKrۼ/e.?xWN`K| f,Q?mf?mnw$#*Mv- >%>9}CCihډ{(Jk2yPqȯ:(j履OAuO x?xws6ya6 ;JU'Hi#h lR|5@~!kZKR&,7"<@,2X?UzW_M'f%z%ō]Y&|"ά78"*w"+X+?h!1 z7HB~ȩUW;EM;򮊀 ( ( ( ( ( ( (8#NWuW'E59l[RYTݎN1\ΟV|,|9֞):mj+auV\YP3FXJv=f7Qkh(ͪ tȢK!#+$r8Weo }\l < F"FB9k]=F(aEP^uB7cҽAc0lz~^kO4]v$b'&(F#^:k>G5ϊ.|AM-֖/q$b`70'i$V:T 1"=+E  o N:nsqqj# w+E eRGCy=3> iW|wM IawEfVi  _*Zv ZG1Q#l`{Ÿ4q>-|5|OqXhz<驈ޭzgO jwA4im[6T88Tf)}Vu_O ZcGL Q@`m9Q~'?m?U|Wꚧוֹ0ik~)Xv@$k*Y 8ixhdYaCrd{+g `iZx/}KK\J7!, lxrT+}:+K ,j#A08Nw.}%oQE *77(Ң*XI?Р hOR5 iP:Ê(((((((?G8O(~ȩUW;EM;򮊀 ( ( ( ( ( ( (8#NWuxI<>iLMj:^ &4{kSr#]O]m7PYd(I"ڭ sO>4h_ DR5]S[6v Y# d ^kmmΟjW2j6mw؂}券12*;030e̞߯`\o>!'KZ$s̷{X؛ݐ6?Z P25~c <.kE hQrH Cs7i: #,EtIkw[Ca<<$nPPINEn^K漿]{Ex?kxUE]k6֞,༷XC ;J c>-ltV/>=B4IY@7qO-}-+9]oO-mt|2Ưf\e[r 7 ""9nd8rV~MR_ |m*NI[(A.,)`/98 'A x|m{='Q[gLj߳  v'AꮄCZ4 ߄c^6[|(Yg)[1mu{V "zѴM;ÚdnakYB$9$;(((((Oᧄ> >=osEKt7B5{PJTȍ.-c!< u*[|8Dz`fϒ 'etCYV1>#ȭqasďͫQ9:UđCtmg$dRC*n|s!隖=|=KPFe+~0遅={ᦙŗ/|86h,J3ǸvϨ%{ y,kH 9|OG4mNJa+Fpw\#{PQE@QEQEQEQEQEQEo׉Ѕz=y$??!@EM;򮊹߇*i?tTQEQEQEQEQEQEQEƧWu5:tӨo:] Epuwno$ҥlc $IㅼV=pA g*x⯅:%ZoWX]Æf8mH W''"{oO>,?aZi^ԼAL4tR̢xN{)xdx8ݻ|) iKbl]A"[}c=ޅ%j:$!eW(P:NMQ:;W28>Z.Fu x*~]\hZ O4'4M v _{?_Pm-٧_'?4I$*vA?Tsߦ ?Sy~)r%1Oiσ~.^"}BѼ7ѵ!<14[x`k{hXX936? o-g<Mmy^0V'O9ک<M[!E+|G~<xU–Դ;'"(PB̬T=zw?ک<Mj7S./BzoG9^j7Qo@?ک<Mj7P*9ک<MzoG9^j7Qo@ Wڃ-Pƈ6mheh3:*^2BկI'‚/inWy9&M>^Mz6rI%խ'A7IJxnm e6x>NfZuS|" ~+"=΢O`T(3c8j[_<%^c%0yf='gsy"a>M'NY2miEqy j5/ ^+iz0TS#p5j7UzoG9^j7Qo@?ک<Mj7P*9ک<MzoG9^j7Qo@?h!1 Sy~+?h[ֻ- K=HT׵z4ʺ*t^'RT(ÎEt?vEgmxQo@VݿvEgmxQo@VݿvEgmxQo@VݿvEgmxR6UchN]B4juPEPEPEPEPMVS:U-al!IO$W}~H Ĉe9rB<ŽC.a?e2ԩ~W̷v:q_x^&V'JILR"GW#B$#{o=PiM$im%ąq$8\+FZJOpYRw}DjY[ ʫ,FHEA2Im9rc z7[Aim`ѯϚ5MFk=.ݞ]۳g;q\:-WUlWV"iosxbLFLZ[cX42HxF@ $jE֝$Ha8O0V>j/W_iOK{is YhŅ(i uprp$.xxV[[;wڝf6:ux/6=óC=Ə_>8|l|Sυ%KltدQ<хp3ەm}[=vvO3H bxsTt6iŝٮuc%}@vt&|P.|-u2$\֑ܽFtʀHʓׯk(Cf#ly& t\cv\f+_@}~}zρPAK Aeoj4|H?8_=_~*wPEZ"y$fIH*W$cSm^$msx_]~;'!-5VT-fY$XRs2k^[M[Is^lo.#IcwE6Ϊ)_qN0O>hit/՗#_tmkɷ!6mhi|wOĿ×wO惪֐Q&[PM'¿??Ė۫5Nʸx;y'ӊ\˥ϰVUܒ\9}x .]=Ɛy:]>̗g3420 m*>zH-xk>ˤghw1wrv5[z֟m?4}xז, |=qBMGw6Yb2丏lj q${{mؔm}xXl}R>k⏶{9Om?5qG=ckƏg`GObQqG0XiƱ~(g,m}xXl}Q6hi_{>( _m?4}x/=ls>k⏶{9Om?5qG=ckƏg`GObQqG0XiƱ~(g,m}xXl}Q6\'+/7 *IWG$WCq\?4ZuSSGЍ:3 ( ( ( (*_C\{HrA1ڷFpA#J"L*Gǐvdw:kǥu>mǴ1˻z|TyI4B)I̱U*j7ѳ-mqd.Ϩ[TX$)Qз~tRq*λ,4O =O$*h``y>k0:]WҼuOxŏpqq]?'~֝hӿs' &VJ,HMnxkCѼ.iF [8q=NMhw i?ƏN/A;p9]7XxSG$*Tg)t> ##|3&˥,6Xtr:S ߅5?;G#Ix;D/aXY 7I6>aAKU$K/؋Vj62^H$-TT/EɯNӿ_ZwOuz**/۽Qw i?ƏN/A;o>?:>?: kN4w i?Əfv$ww?N/A; ߅5='۽G۽Qw i?ƏN/A;p]>?:>?: kN4w i?Əf:Iqq_ZwO kN4{79bO{Ώ{Σӿ_ZwOپλ}~t}~u'~֝hӿuؓ;G'~֝hosğnnG ߅5?;G}Ýv$ww?N/A; ߅5='۽G۽Qw i?ƏN/A;p]>?:>?: kN5{H״}]69q84{79b۽G۽[~Rq*ަ# J5Ӯ"~љ摴⇷KZS㧇~,_5ݞNdGwsSvuT&SĖ~oK^*=u0p M;^9 Zh3N#m!n YH&A!E#nw\ _}k^%כP흵jəV9 hAՊH8%+y'5=?|1eiKwneѤ|3q70/iPk5ׇ;km4aH5~hdt  (~>%[>g|25F++ ]_[{*Df'ԗOM5u !f;M[ðFI;u@ +<-MZί,ިiooQ2@,2I6?xƽxcZtˢMx{S.3淖229#h*fۿI|v6GKmm#S gO|]|n}؛3ƚͶ"qH>l8_[Kπ,4Ǎ.-e}:i%eh6cdb w'/>燡_ -a󂓘Tl#M@(ѺW޿u=K[G>! C˭F#'cJqʎq%<xN ]~|M&[Gy!૖x|㜌|AH-Ĺ|~1-xeX!4.Le<x< m[ᖓ[[E,iA({`h^SUM- ccWz|{ij=WDj:",?iڵmwg/Rv@E"x+oM auc]hȲjFF&1!Z\7 x$:zk -;xJY^<YglU0kWWx x63[qk ˧8b^p74_oibmu!·&>o[NjcR>; y9R[cj<1~iv;Ȩw|̬@=k/>?:P|yƱ>KW:q69f )I)TݶO{Wj/h~6h>.t}^WλX3R܀B8kx#Ka{XbXtc0\)ҾH/otl-+(_ Xn!_Ws(~)xg -qIe-Ehqbvū%L(>jQwնnz9i$i/K>gď |F#C۴DNpi83:𯄾6>|)?EGKijȱ\uj_mAETQEQEQEQEQEQEQEQEQEQEOIo*JoBE6ǥéͤfݮ(NTQ(__Dwui\r={n8}'7xRml=́r jvî]IVgy%s,}mcY>"ƗHvwz_$/Z 4g-++[5SzֻfXʔ{sxmywv|LG+=h}F7Ԥ+]Fgm{jd]B%Jо%P {3x~=aΤ5q'n nny[|wq\GiY{o,/EC)3B#bs@V#O;0_6o]Pz`7ߔ<އ;_įײ_h^ԧִUXo9lw72G4 .y2}㜉?A+y-Oa;.ψH5K>R3 \dZߛ@_||;G? jݵ 'OfL6IU$g ?V3Q*f_?i#N(((((2AQ mAPEPEPEd/|)&޷h-Q#{ʹ'ҵ*]Mc6ҼG,lUC:Fs@<x=ƘZsM,M:| ҷ9/ Z o~%kO=Pw|A垿JZѮ0^Yׯb3pCAT~=FSеݿN|KB֟vzށᧆEqP/4zpC"w淓7EuMSk WkPoN|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vz?9/ Z *? sTPON|KB֟vzƧ}[WlGF|fRI%=:Q ?u{Ǩ9Fd>qR}'i7SWzT~=@?7SWzT~=@?7SWzT~=@t*şZ6ZE fH+`A1Ua! ӫGЍ:_?iQEQEQEQEC[esoDVBBA(s\S$DY#u!%tџ$|:Ծ!NQ%=vxl7Z<)#ۓ6~}sv%IMzĚXY%Ē# #U+L1zI͏k3$:i) ĉj9泯> =mt{kxfFd3h9m1[U'}}i5/V׏o|SJxF#Omf.&IH EbJ0ğkT|8>(uM :Z oj $'u}{ڔzw~.Lm81X6*Ojׂ=sM@Ѳr DdcC#[~"|7Ikvm{[K)[wNN<חx㷊tPk? 5_X[Z@J/ˇihqW4NIԴ4Y^\[$Z_)%7HW;gK֟vŴwIn$˙屉į13Vih^NTҭ#Ig'*ŏ^!&wLO۞Y_㶺zw nS FvPAڭ#*Gsb^^?x]44gB [2g4Ìy,~EJ[8ku8?8ɤtD6ː>9tv{/[ݫ~j^(? sW>ÙmSN.LDO4+"Jcm?~<xKO:^\Țkc;iMf W+{ H7Ų~jAm<#GA?e_{|O[][Е]u#VnQM|[XΘu 95P{Q 8\=9 >xKԬ<5|KTn^>*ԗ5!-uo#T:[a.hX3:>\,m̽=>JO_zt]'?oG׹W_?> }K$|9oja[-ܰե"|S Yx7?GQӦ!BzFOӁBtAQ\z;~_B鶫OoCk='[7?]jך"(4Ff40΃k|Q!7O:VUլ Ѐ LԊt(N]B4 u?ڴ FwqT}+0gg('0gg0gg('0gg0gg('0gg0gg('0gg0ggu\6U +|CUE/a ?9ŸIɯZ22# iӥ&]0h2i 0~O2j_՘L Kӭׯ zMKYȂ Xn"YنUa0T럏uu{[x f4Iv86tḘsԨ*Nᯎ4 u=WTEK+KX%m yWPc 1o3No-t}#W|M3dupK&sy6/UJ'_{ a\>jZ~qw B̠Go^;[ G"ӼJu;},ymVIlyp 5K7Ef<-<Ͻ0+"UZ7.UOxőO%Z7#?2]P/ݩO]>`scXWor0۵bOi~m# Z7e? Z7ev MCTl4fLJu 澞[Xn]*j6Wq`+ռu%宙o{O[mjYRED 88FG t_2 _{_ 1F G 1F ^_y|An7SMst9af2'/tGFxoIm'\%iHŴi en`5 MVU+}o\6 Z7e? Z7e<(6|Sj-SRc⹮mh.ǞX}A1vSPg#}_ 9GT|Y=Ͷ[it>i. J>\\TX_K iD ot ouoSmSw:ΩK'lt jXL-.uio#HeP|FL댾WOW>=#{kC#xqo|]GE~ߊ_/q_ 1F G 1F _67:NqPxq[QXL!Jy,m0ݵ#Uú|Ai~Oa ŭ6L4p'R F1R]mo×}?Ku?5h!?5h!w^,Ms+ Rt /(7yHA^7;{ ~ן?5h!?5h!_ ~.4*wL֮;<56a#)%g"wIʑsIů|g4*@_AmCSi$53oWm/_գ_K_գ_K~0i?BPi_h76$3j ['pzWx ޯ/6 K%=GRu)cNk7Q[&Q߻wg檶&%s Z7e? Z7e&|O_YǤ]ͬꚅ kɓVʶmŁp9 >_յ-՝HB$ p*Z%+)>Ȥ%o/_գ_K_գ_K*/|>zc};Yz5љx`ފn%@|E@|HN^ӴZ]KkXX|#w2 _U#vAjhgs+~62]2]xixe;}}.)Pu&Gxdg,~o/ B ?~5#gO, o%ۙN17\JnIݯ~Y/jѿC/%/jѿC/%˾o_ߴGm𭏍4hWmH>F/##"< T|U6?fY]B+/e< fG 犌dݯv5CI$;|}BĞ4.vzޙ - bc tA|cCV.|gǟ%)cŨk?[M]$!ֱ45Sv~+]km^ɭ.`1|h]ٗ 6@\~'k~O="/jom^t x'v% ߱'ybi; O'"-G G"-G ^KkJbc~| sImPMƸ-h5@ pSgjË̿YYu{Τ5[ $ZUΖ,hߟ($|t7Vݥk_IãW>[,2On?<ʳj!U7}_RKEV[eyS\F>c;k] }szEݛQmyime&v1+,#)KrUA)Ub8>x9.e>Ru+WN'%ȐfP|Å rL5 ӿ.k&iG~>^}$ck%.-6Q v\2QPłb@X xV CN]^ IKUwbXO' H˥C uuDVZzF:UZj$WP$P33v>Z}صק,K&;H$qrv6C;rNJÿ~jqGqݘbӥ=R8 uE,; k%>@#Bo7bj߳?kK*/TS%UymAW+ZtY[Cm F*~odN}NrhKG|?hmne~4Xu{ht{k;Kmv+łS,5$tvX:ܥ/sWkoi%ֳy-ۛYgiL#` q-İku$Emfۦ2M.@rI/W?hKG|?v+"*6-NE2Oko6q-$1rJA=*/"ԣѯJ^s-",v⵿hKG|?Nop5Ǣyw)#LE wi/^5*{a[mJb$G"Y4%>@#OĒJȃ]ƓxE{}B=J;+fd0YLjD~Ϟ hr.s[(i\2,ce@b1UKG|?-Fe]T?eoz&[1_\yc][Lw7qH c+t=4+ =' vwj69-\_=rs&ny9KG|?-FIt[]"?e=ׁuH6i.H5ullzcJ"PKd[ºvuxDQԮ.nfKwC$3 2p99w"Z?h4-._o85/QFNϩjZZ}DQ/`B_D~[v{`,-w|$h4%>нd ]ͯD ؽ4IRBv:{` ^ ЯmLX&C8e#xhdRXցwzGbO|=iSD4wS2i[$Ɯ_s| Ѽ?뉮k>=I%[bc Ē{sD[G'(soOa5}[Ϛ#} 5ף@tZC)/IP3d(+U<XkjZ.%$rEy1(AcqVsoOτVѿ ?…_Q7Jn\^˥WzwAnĬR2'+ᯂoE6(Eƙ=ŇnoεPTepj+n@soOτVѿ ?b? (qdC?KxgA~o+ 6I[{H,פVQ |a! {-Q@Q@Q@Q@Q@5?k=kO@_0֚DA\=omu5Qܣ2jCKi~/5 vHnmB)'!×@{zkt+)w+4qx]%s/EgAQ+xw4{DtpHF_3+W #v0#]^Nχu m(mSv('jKW}=&MK봙“88ϡ |*ğ ç'#f/_V{E"Bݥ)C GW?hZhȮv'i"خ\afCO85VWg.~1,x¶,'$4958gD.TK.~S i_tv)kw֐ܾ7?4d|r0@9|8,yu֙Kwvlo4ɭ2+\rs +P@cv> to bӯRH}aqRp(~_iZ~g6{ 'ooBJɤG{R:;;S>/&/CϞ'7u kwG!l]x(#i[(>x!_ oq_5O K[UxViJB*d¼_ ^[kV;S]P[+XqGhoH\2I9ʩ[`:F|ִF+0[OzR:W5wK~S׿2t.`ۭ;Tnfi8wou /kQ_Yxf JGv( _%~R+kcþZG>-5ۈ,~Y `+y_2WSWMpךxD>Ez^ 47>}Z(9φV*.4K 2w _Ly5ā@i ej4O6YMfe;Sv;vk MOĐ9\S$߄|+l&T|[ByR#*eCUJd{ti~gV,">Nk ‰|ekWok6ֲ5ޔ9FVhfתTI;GM\*7-kqxgÃL0j7:uX AN]7X𮙥x r- :L"[f !ϗ ڸvc%u['OR[i4n$<ݍˆ 9;%A$~&SׂZ+ou[s xC oW w7_ e=B^񅥜H$i(m&T]Hxq,='_~6}kWl7-qs=8}*PWq1/c*r%|E+-Q~̒_FI0$4Eaeya 6ACG++< =X]M7O2M]Ϝ8l3MVyOO⫴b^((7آwU'/\υiwIB֟ۍwN#Զ[\+cNXDj0'~ xHGS^\5mP|nU #`4%ZxR[ſic|l jd⓺NburO7|mc3_ VkKKM`[_,h|Ɔ dR~pmh]fy ԣR{͠ .&Iy"Id PſJxr Ѵ=j+"m.c(wȻHYC'?@]Y"׆4ugik, 5%o++_vhzZvY ]M$,%+2+('{ ⥻];~Z%o;]==Xxƣr3C@wByV'kF޵RG4\4md' "UҴsH?|7txPcɼ}"xdyU:N}>GwGdImjCVryv\t*"iiW9}5s&>X鷋m+ie0]Khoa2\ȩ3GKY%JRϕug $0;W4CxHτ#\k~ɌUy!p8=| K mr"KlG['ܴkZwi6_jYDՐ[!kPA? š+9._7W6i O9VEt!!ddpk⾉xu,4-*[ nfcAw m!Q c|oB| 4Dڍfk?jWWR2yM0beWD7X1|GDk:v1Ѽau2C%VY*f T@POa<-Ym仿ӤZ‹3s. fs_:¯n{xg7m*9 M^MYD|HY0CSx|c; OL[E뷺D^\N<( ܭ(+F~%< ߯7c4<gx񬱁*-j_#5j.\i_y{IjwQW,:E||?ar~3I3[hE)Vv!^2^3׾x#Óx'\C%klܭBVqT@~BT_wwmȶrN#سElp 8漣Q2~o4]Og{Zx D1~YYWqsNF^+ۯ ˩ L:7~Tt7R\N8lqL~F~#TT\8^L_ʏ&?/@?iIK,<3c|҈fA$G˶9&-+GDY,ۤygw%'^=cTwt+뗝-fKf,I2 $pMA @??&ɏQ?Yh$ @??&ɏQ?Yh$'6t6]pq;/('œq#<חy1q*<z޵;}&UP${qd²=u)uOI;cTy1q*OVZ.4²=u)uOI;cTy1q*OVZ.4²=u)uOI;cTy1q*-#Z>t.mA -uzc 8}[B`hy+|t?e>Z+`fotoxx-18.01.1/images/mashup.jpg0000644000175000017500000014454613222767271015144 0ustar micomicoJFIFExifMM*JR(iZ0230E0100Fotoxx:mashup|trim_rotate| Fotoxx:mashup|trim_rotate| Fotoxx:resize| dhttp://ns.adobe.com/xap/1.0/ 1 2 3 0 8 1209 1689 1 2 2 C     C   /" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?N2(M7띗V=A83kEXeΎc鍨 m]} FuZ,/hvjN:Z5ivOpv',־7IK 춝 Z4"=5m%U!c?_8CV$FI98?i$lX>VW-xYUeltބW2em?+;\/ѿO˜~\K;eg<f{x@ғ#CW񍕵-ʒ@ ׽|իx|i#Á?5wy^FOeA95TVf?RZ>~',6438覹?NnGPv?JV"}KI,{=9&"?}w1!$|{dח_1K {YALSsTITЗLШ8*SW4XNH&@Uy%{]挶}I($zgLt~RNzbRا=rIJmowE~P0?Gq{h;2;%8EF޼P%&n1 ~cRjBHUK?Rկ.߃ C/y0X}AWN/݊G|=QhUJiX F;Ug]m{U Mv7We$XUIW]eHdo\^C㨕Pt5w^wbe>asLkf>,$ ]tks̙i?2G.QyG>e8=r6^>SexgҼKf,U8`'=|VmՒMRH]g}QUR3~_t}(_Z 4k_<h7)MJ?0ob]#˫%}IL|}k!fO80O֪Ytg&8Gf}|C5tuC)  rb5?>>MĢHכ_+53УOgӔVG|Ki;EĔ\]&=)ҙR0((( 0 xݏ:/5-V|[̨$ռ`SWĒj:{]49y<LFp{8N- W\h}%yz~cg \fB-:U8Q+{k_YtCcpszf.3FWeʼg3 }.5'7daArkueKn b||csOo]'oؚ؟W/FsԏoD.=וLxD\~nQ۸$uWþ;.5Ṍ%מAJ?e;VG_|=R ᜠc5R;!dVVcӒF3\4nNdXFd Y ߽yfn1|ϴd>$C{Y=>l,uŽJǽ6~cۋ4zWin ַvokssw<5ܕ):˭72՟`7lIgy0N(mGZb+PW.[apZ:W/_ug/-&qU.ͫI9uuӳg^<74Ksoۀ&a<[aIU$ rC_:.l!u;#PZ+7? ͎;֗|ˏ^˦:8~Ct[H<`ϵ|L) t>^A+_4xćRd_d}Z>/3׳G%$>\ORğt9Ğ)$o:POsp'Q_GZIq)tIy},wyzc5cq+-dwap?#Oe4tls/5kAk]+L t.š@a*`c)qvd2 ז&5čgp0op3q^38sWHersg9oSHwzιx/jQEx+9}k_Y SEN2 7* Ko>1H;+ ;;R9db2 M ^Z%-E}=(Ty}NxX'ސx%! -|SM:ټIKy%0yU& qlMu*^n n7knD4s$D=NAi=i7m7ס&lcܙ8;w.@>0[>ɥm5OelwyP r2I8H4^N[[5;gne7dnTuwvI{MTյ- OQ'"~++淬MxMY5y *r8䬄^㎽3Ŷ>+>6c+n$:ve;NX ֕T(%knt/WT&Gihݣ~Th/3R叇i/Yk=Cfi@p#.NՍ[-;w i_zkSo{;$Qm_%QLחѓU-8˛GR?8F2q_~ss0:^- !f +Y 2Oׇ01MwZ2J*Nt/YJ6Z^ּs_h]ΐou _2B7%m\~~'.emAgVP|s3\gXYo jZ֓ha"eq〸0@|'xV`eϴDV 1=JcZ.9JծxcNTkVrMsvۓM뻭՘YI#áO&JӴ;k|;[db[KDuڬNH 'kϓ E 8Hk3(]γtT?u[(O܂"ʃ3b)᩺Vڌ0ģ('5#Nr-OèB;h+eD^FCu`y#-?/z wPj{cj-qgh(02?zUЕq}|DM6T`-ׇ>iy1ډX,q)/$I 33>kM:[-JYF׆d=G'!n!%#]rvvJD[ \[2Go+]B8dLn<XU$m⮍ܱPԍ&'rumf ꠒ c^GN鿵Oyڥ֔^/TH8=H @=v_^eZ%މOU0\ۣ? |BNDP%!^0ޤ>Vn3 f}*r1ӥz%m% !/?}V_niTN_<дG:q[ sƽ3 ܗs;0LךJHE~ǗABIF)qN4k8=}h-̗B>'ZֹlX_^(bcֻ}:4Tv~?Ҭ[mn|Cc9eu_u&}YD Mw9[‰`.g] ;u?4i/FܫP - ^BVH WfU8S^fdݚMﶧW4!ydHך^uv-za^uk:g/W/h{Q @@:q[w? 1c5͗܈\h2;g}N*(hf zgΜgRRi' IZϚ {s^[ \KB9E_Z_A+w,:ͶЇ2,31#U;وP2H?D4%^DT3]DŽtxlh]/D*#fQ$Q^;6ŧ(⢔uowAx761qjQ5N>OZocN'=^%\3zƚ'JK|qGfӭ^Lob~?]rw=_ǯlƔk|eO)%~8 r+'a-Q[Hed.6|M~VmY`k‹kI1~B>ִibl|Hy*r_!8`1^7k%i+nwij÷Z]& XfQGR PmVouxt%pdFilj~k>$^VSp\ͨI$x32*08_(BlΝrx(^U̒0DAl.1]ͿҜn*M{*IS)%~h>i;r6|rk_xYg$a5{twVHcY sB bPpa\ռ;sKs4,AHO#b|?l5OƷز !V #Y}kv]koEmq<$$OΪAQWVx2qWN_g]*vӵP`F0Zᐠ<4ba哨lǿ־.h挞%G~,F>1e ўү>8(=cGyA!%tS_40JAoIk}u`҄9N_X:>o-_1ِf‚G^~gGRW{4 -OgWE̺mOxxBiɣ9۬FO$kŋca04(O5R:r[W_cŚ^ $WWm:0|2F}pAWb!ZTt>O[ס# Uˏ+u0W Uׄ[XI[ dpƝĚLڽեG2IVzp YJi$lI s5ǂ.<9\xW?:rR.ʀ$rHU/ݯ.g͆L} {wſ \k> ݝ'ex殒DFx&LEf7Ib3312}\Xk^u#mr08 YF5- ZKCW l|i魪ܼhN˒qђ{zңF7<)dzğI’ŭ P*|O8$~5|:c2Hs1\gҽfyi ("y$BSׯ͏^+iƞ%$l9_3y?jզmKGo1n=N3Nu9$k;+?=\- @囏׊[uVΗ}aUWP s;`0zt^W+VtUNd i\ 6SORFmQ!,d <ךR{VKui1d3Vm$ّҿ^Y02^f˿Ol.D.YJ#p{gW"4|",,g{t*S.tVשj=Op=Ȭm>/:uj7kfXg/< e=q^-lOo4z|<' 7 ٦՝~iޙ*G qZunxmZEqɹg67Z)3$8d0#,={ | g'vź5aFfIrENPIE5'%.k)4F5(+O+Mil$gW%UQ}KdֿE?'][YRd!ݣITWMAt \YiZ:O%e+Sr3RRTUJ]~z6 B6\^ZP$d r+^SSIZI>˸nVx}}E:[(avS E+g⹻y.<_m żv"vN 2.Ъ[' ot+'Y\O{-p$AYD=59%dn߅ȍuJuj{`+9FB]Ij26k%g{¾΅z>_3LK]1s\5LZZ1vjqەUզRM^U58o|a޵|H} u{HZt&GA, XAA\{P{້t=Cþljwx x8̑/?٧[ ͎|?$Sqbuk6\V\ 湯~˟|i;Xk/A̍#¾sxxD'/l"ʛRQ/nG%uK+$ڹI7▱&G 2Dkfx3^;b]&]ǪkQXF \͎2@=;׹PR%15".$$ušf|=ռi?+[MdrLyo;.FA\sWxJi]9VKe(0(NzfNiGc&))Sߣ^qiW'[q\kMvC㏏^ 2\('쭃Վ:QA_?xgᖟ{px_A%䑵חqn9ぷ}=EO<G$vUp\2H@>IhiNw'(bK$w`98<>SVOH_\_ ,hՍΧ-+];o\)p#[O{tQEx& >¾t~4ũ;5Ŷ6V~v+QA׸9&[[^26j6bvJ|ݹ?uiZ4'=,6;l'lUJ? w+Kmn?B~2ѿwlXu3\RX& 4$w9`Q*Jjv{EQ)27dm\<2s 3ĩDoF r8Qק 6O84[2!&zl.=G]D+%sܬy3TRV9)j>p?'`0u_ P O1}M͜-@ $?7W]|>tʑ(eo\ΜY-=%> 9Kz,Tz]Ძama~I=}\Nj}'/+rzN=/oWI1RA*gv;'-Ɲx|Wv146-199#t5$xTNM" `wnRKjV8nYLʞ3(xXqJd6xFz4\UT^/1{KIhշx[_ukQJv GvgQ~Pq\mu?k5Z0ya*޳5~,ߍelu dY0 +G|m6q厇#Dbxuar2qͺM{ZN |U{V.qJ+_+8i}os}[^v6lDa}(*%,i^t+y~m *1vVۓK}+Z&IZvOu*ac#_dQ yPk!By7)9_E:jq9ykǛXڃII$ -0_k 5}n'=A`q1"(SiZ\z_Z(>z2Fk?<7._9&>$;k[^wUnMei ql/aY牐9^O͎nb]]Պ"ђ9PG`;UٌTͺp_3' ;UKu;Xcf_ÿ #_43~ʌv~.Դ Vmrj l\S lq\V_|ID[,de2͎2p3>U: &]<S^~ QqmVue̓rNSOfk.}{Y,}&An #ii<o _Utti>inPP0 QKMnͭoR+&Hp0:.o\&2i&p!˦=@l5*rT*r[ՆaNOɵ;|r\VMJ7ψG|Iݥ_f"mmc`zψ=3ThcNRx;2s{Wh_ |;+^ȣU!ۀ+, KA4aF@%@K.›2nrOSNd{nz=SVigʿ53Q٤xF 6D(׭;u]Qkae\q-LOQ < s%i3ns5,):ױoYTTvm򓔝+=ms>hB$1 bOG"t,ghT<ήbXj4?EcN.\omVt=:ηbI2$`7=yg 1dȑ|-}W9=F yWHezɳ#MjqZc섢((>_6{uBGL (qן6e晩Zoa%WT FAڈw瓒~ЌxmI,2YH \VKV>8on! ~QVݗWH)rp|xúBs4V)$0H@s~]mBZttb4ː͖m,8m-#NnYC,jX|! WmO2ݣ>c%$g gP}Фq@W!u++Yⱊ8-)T1 $w8جcקYhX4/zXe$yIvҾF-ԞxYbDf\? kv,sDp݊B2:z?TLz]m\Ɵg0{";gWٞK7lPA[ƪ0j+k tf.\A婋tEw8cdsTg}gq C J('6ښ h n@).zStMo%T^\ѿN/u| |5uEAW n#6=Pk:R_9 &QҴT)%}*BqUc+З,˧;畷nm]&O Z {hKwK{˝Io&):E!uЗ|g+/CkKElH18Լ moZf>_#oM98 ^H]_ɞt1Q(SohK][W_FCtūY)ue*:*#6^XGd*j6<<66a`TӮ-=ʼ>c[۽V -*0PI":E5-?#,Ƽ\V׫i^-&mS>6Ϗ?xn`eGm?pZ?դ9H .G\޽Z;GW'Zr[׿5WW;3jq|QEQEL[GC%L,`eՙ # fJK67%Ҥ"T+wq*jKrKVoհrL~7yLJtOV-4qy^mNZ96HЂ>G|@|:n+}2/s}b`9m2H#$GR-.:-Pl z8HMtJG gSYRQ0̤s]k×*FmSy4 Jڹ9F67{dv=ytc8{iZKz&[^[i#[0wS x#%6+y5>/Ӵ9rs0bG?ź^8uPS0h`?_bUH.v'li?eZE?#jPǨ\[D5̱2qcXCs'"N@E~~ӓ_ŷA 0V9(@Ӓ+Ιzb$Ŋp3jt#RpqN9Qcׅמ%tHZۭ9$ɖUN|첾6Q|Io!g9 i~+GG𦚶nHcLnwYk9LOKm]Pq8#,YvlƦ&/o#8j>%$I 10i2=+ׇbZ9WPm"C~'~>ڱ_^5"b-Rn|;-IlB=0Ak*+U塅 t#y>'Ӯ`0Bo+# qO>,+ܫSA9[i?$R013}ݡIp2qZqzQF `@$N:z\ls0ޙe l#V|Bϯ[i?٣PYkB&O88s\n~}rswkg ?4xf;iIwW,nVLҿ,Jr5/c#Eш2e_ҩgo<K}?q HR1^gQtަ~'ڢǜu'j3433h4z8jɟKkR5|)QEQE_$iY}EDr,HƄ޼u؇Kյ6dIIqy ;`S)o?B]SP0k=ըo1]d8{|;e"o, 5Ũ(̘xrj>* LEp0SА}8m/ZBk뿱[[m{YrWvjqjg!q{N ۅr1l| ǪbEbO:NWy#$ֽ|-~waopٮZ*ar_ uIܠL#vpKFPm32vi1Oy 3q+ YBE|xnkm*ܛ$ ̅è*r~m7B+Sp .O 8M((hjtr}g,sK}zAG$*#rIŽ?z򯎚h Rb&ٲB;c^_(eUl8u䟴)n25!sbd1xĚ0 &T@ZLeO[c$8<{VM܍oNVԌ]g7ڷٟ[ԑǚr}_JM7'̵8NV:F.cR>X&u*6cANXomt[ĩtm?iwHGGFt1\qGccnP6cnиuNPj=X96ff!Tzr։yc oy'\Wxt9#ITG>6H-BI).8`O^M/Lr%lc^us۵FUE708<ܚ/žsK + ?w$jŐq è/y>UJ3rIaҵw 3y+T+m.&iܥ}X!Bؽ-aw8 I 9h:q~J$+ =g8;tm}5yx YIc[o"mxg>xvG>Y7:[#2/k҄r83{V; 8iX~UO[K<+L#*0xRmR[ 0M0zdӞn5uY pnQ?ҭS1o1t%`V|ehv0$8u%cT Hƶ5=jQW#X~vg2q1_N1GzhB޲]JT-3GrH$UAֶ0N+.f:BFbmbʁWj:*~kh0N_'3׵a0etӠ;wwAc֩W^:ԞY>yiR1O8c֟@o$TXkR>lml};6.t !&8` سLNA8W}A+.>IRןp?[ м= F^L|$\8=/o,cHTy%ʐO׃^6Qӈ',Ko ʦQjqzɤ;Z[yg2Iw>3V4GFI9]jY :]7G$Ҵ<5Iq d`m *g~5+h;sz{Yz-u.@CO%4Ve[ng25:Cpx\ϋ=ttsz{Vs9!1W&K[̑L,aԸ+rH{XnF&9GOA8y-Dj[V qC gPx~+kzE~ iYʁI$# ^y]BK?^+-?1ZadS;S#8l.g|AӭZm\N$%S d< 4 H"Rc^N1&N8fo]Iwۑ-7C Q'm;$5_ź|9g/[{4"q#hcAdzηVj6,Eo2PFNzmƷwwzmKc$H aP7n.W \ 5K'uă0v,OziJH[5O|?'$^I$}?'t,M(UxPV֮џzlA\ke%זʤ4"v{3ƽkVI&)|:GRAഃb$s8/i첇(myG$$@Z~?>P!@ͼ}(1 F{>6, Gxx4v=%E emWk>ɦ˞X#_ƸQvC9 f#M3[@W9x+k zםJ{1ZѕS:¢$L ]b$UPgxni~iYQ`e98׊lZsaS c^ '7RGx{ mG@;$ 0>5xa@Txti`#zuZǛ9] uGWcP2\\~Hʴ[Zxi7py*`ݘQ^57kLga؊iь3|÷=7QCB{~~ƽH֧Cr3V4?E)ġB:{jRl;SO aSEY Ք+z|-:s:EU'pY%dײ_i|t:N.e{xb0'oiK u|>`FA*S8MٓZ Ra&ۨZ>?iff2<֚h~j=JVa<<>9-kQ_O%NloIe#==J٬l\׎xK;ױI6j1Mt=Tw URY@|7U 6x׏{x;s|&oƼ=̞*.<覎If cIٶk-v,֗P-"rOQҭd7*d@GҺb-yΥ8#zNdUr¦_/R][YFP|G\ÈךՄ '<%kc"@8wYVBP H2qs /G$e9QGֽ o~_FV.%/|U&+[;y' U\77`E}ω9s@߈6@Lw8 }gvZe\Jж`:rGlW3~DK{X>QdszW|1`*^64EW͟DQE$[].d0Y$q:S/!P h@A'c@dt{4hu"gP!VwsЎ2dž|5.ayԾ SlA28{vis43/nОv21 6aCE ̥ל0ϯ@(x.{k٭"EXۢBζ`g+l8RGA%^Ϛ ,?X sp88ɯZKej[YKu?U.7.zΗj FiGW .p@@WFOh1 M>Q$.18A{hR9A)E}ēQ UOk5ޟ%VQ[άd ;m5=i]yҴbrA(RFONPY?fnVIVHG"7gsX~9PRY-4ь؁ܬo|xZzY[6fēc$:t !20zⰵjH$渒o39s&@00Fdei6̃r@+ h@@z0tPZmOH0FۉP٫t i՞3kKOQ2FFsR> cA^]zL>Dx=oH>Ht!~UitIb-5~u< ʞt*u#'s ۟Ě*0:zl>|t-B Y1ZPi^(A¹`&2!Y#"Ry1[͎  Eycҗ,=֊h(+%fV,k.nl~Q Yq1YPy7r:= b4r u'=ktGm܅[uvf/cuQ#8g\se h#U}|Qϧŧ[E?Wd{n3ך:mkY{e;](G WƏo 9]*0漋㮈2c:otj`Vb3h!;{+62y چ\.^!_JlJE Ignٕr2YA@p8a4II>a!o+lv#$^A^T5An^:'O^Mz&@eK-k+ |.$!,qPx- <8(ZSpM73?(_>?m0qJ+? <3xl$Y A_$6(RQ@Q@Q@Q@Q@PN#E`<}[MueW_hfT[FHMg)+S{ f0H85_j&˴w'q!Wi-5he^bA08uqj+ngs]HuT27*HؐGFO'2۬:I*L}Iſ/im!<`(r8<󎽫t/5-\\G6~Oߖ,HW}?kvgZTZd,3Ӯ@3ׅ.Xjn rk&n. Cp0y0to5;]3I:Irb9a&g=8|<| .r弤m^>O#Wnͻ,;³gb}?1EmFK[$&hċ3}?v>֩-#y]MIi[q00~q/B2dNSTm87M+S#) K*4^jo g'4/œSh ~!Ӭo͜,d=bƽnkȾ!Oil%gU[[q_}|n6CXӧRiYw#Y>gLѵ6cJZrʋ |Ǎu)-",1F7UoKC&_(c\۾b`sn: c^# u| o-ѵYjn W1EQ[+>. ˶Cku'a>>oE> ^5w6VRn.hӞ}:WS@xǏ \BIނ/ #U c! hgG WVmUYYO8nmlqrm>QC'͸JKtvךuuy"H-7.t'r3|(Ք^E&!| U^?t͍˥"79` ~FW$eFmG֗<$$%;ݷwrEM2;[Eͩ_,`5$ EzQ@Q@Q@ǚ8VMf4h''+~2{Fȹ WD2Gop#5.I{xjkg%mZʱ aOZGߴ ҅fG^XobsέQ>;ͫ g71bAZjKgiJ3|so ZL#$h[JާDiVq\-[RTmbs\]Kovc`P BWx_G5ϴ\^ /!vsw|jxsa7xF[d%uח,QN3_.&M+EtvVvw[[$ĨY!όr2=kj c1^[."W_46~ Ф'onZ,zϋokVX$^y~dN rwr>#LKgEm~Wl{Nyžq|.#l᫖-dgQ1 aok>%-7Hp#m؎@\ӧ\ 2}4i&n٣vlUV5Q;=c},rMx'Iյ >Kqq.4N>iA<`5LXR_O hχ,e }Okܛ#đmݴX~"W&~n曦un/RR>oc:|KwH9|@ t5|5?jϵ$?2;5~i7W$1B|?އuXbMRY]NHT ďU$`{ǿ |m''KxdXe /Tndǩ+=#};W '<7ݞΩ&7)PNHoS9@pH_}K^DTV(,$cz?E|}|kwz׈(bnV5R]njs@f[y* +8_)|`3o\đ{VG[nb0g O_ޝ5OznmR2v (|j+y3ѥ3kv >8Ëmz{O@E(Gq E;bV#ǛڮYoou& i3HUvNH8b~lc獾/|+>MSZ5]xZ+1YO7}d}_O-cT%E[fNJ%nx<3>z/ei'CSJK\^AzZS˚G2U ,ۻk?oNdQ֠Lz~[|$be+7mcys˦Z4n(c ? at],+Ŏv.rEttQE1Z^i56A 0|32P&}ϋO!},^ڕb Ww5IK)gXC1dMX 7LXDxDwi_vH0p9 VrborӝTN[d }Wv.ž"ƕcZ6Ș'y"7oxMmJ]CR[4?hPY <3XN:m)& 75:H*/ X:WO8I GbA!  _M|3Mx_4п׮@W=-)OoYh|qQ3ʸ])>N~۫=‘_Ljf'(;J+{/''TаI6@H&rytEJ7K݂cÉYm#ϫd?]#KױVKKgNQIvUu= yj6<yRJ ѭŧg| \@}#_[[(|x*Õq^eʎՏ58n8:_ܺ9^u\S$n&13J$dWך0BrMPQ4, x'VDP)^cQ=eO*%-9?$u:jv^g}utͼ+`6;sU܈qklMR|#k^1.ayJ%nj~9x?co?IoEC 2qֻ%NTcO~~' ߭x_  i*i6z0 =\5/7k,ȴmMw~x7T״ YIsaj%ڛC`4[L=h?o**W)vK|T% u~Uh/l< TF⤐2K>8-xx\ڧ4y,ܾS0of\ $Wdz]XxcQY'#)"hJ4R(ee A󯬣p(Vno.y\'ݗ iISh-|NzzUwץӦQ0)bp1NG_}t`%HAn^m>mg(2JKξF'.}<%y/~z5TMv"Kn1)aGF>s^-oMqbLyV0Ŀtc>Ou>?Xvxz&'UGU* 8 {潬uzE|?CP^M8$X޼v nY`/;!,\k ᯄt#xWu\*2qO{VTmF' ~bs7Gzt "= j >vn-[ٍpQzz_~~:LF}FbbԴ#t<"4;$#z^X*s5ʶ>ӡ[xԯ!G򯛣~xPlIW67KIw219 o$2]bE<k揈Ե(}#' 5ձkycsl3שc?cgH&|VҵGOXj;=Z[FTa BXckط *Xj wmjv{A"E9:plCdPVEoKj]tuvc;6@S'=_ោ|AK/^/&V,{Ҳc~Ӟվ-|[AG'a֖-H4ȸ vnM5?*.iI,( tPXcԜd(f{Gi:j|\D)F 2Mx/ٓCj2Ebݮ~蠍[ɼK_ pvtjuu%]3\sH{A-?7:[ֺ4ˇ<ܨbа/o 9Q|y z=!%.&81݅mƹ[|fo nĺׅ岵\G}'Ɇgryw8GG|-n|g75lmFn AF96.7F sQwtxoP5uM?*qsrP ෆ=͵Σ+nxՑ[`/1<#~:>!xk$w[y=ShEc5̲V'˄?W/]޳$69o+Dy8<+{-7z]] "gB) f@gFsŦ]2ZE4fm ~XMq$pH-g93^oOx }Y-&ReA#$8"fܽ#Qj3Ҽ%Mjmx5JIDV (bX gһ1~&WWgUK!w1yQ9!A]v8n$ r4O\ݑ+:<a9:7w6$d jJ6OsۧI*pQo{9$Ҁ8&KRt( nk?n~)3x.Ķ]t"aͮ6q&.p$aA`*D05S,]sVW{奴4糲kU|/mq^d6lAYgA>W<-w~kv;/s Y'U6pZx5_XP;I<jEEYȏJdr{ uንgf۱?mkcx⸎PVPC)^rs"xr5k=:7T=y͈qr<={ -^̤\%_FGbj:FuωvaSz_1<5x(+*;05gRKwzEFmF|gq*_Ǿƺ<%K_[@>J )g Eyi{Q3arG:+T "^uܭ3x ?j++>2X@3Ƕ^sjV=I̬ ӓ'z H| ck~t Ǜ[7oYpAW'_٦wLq53TVa&XX<D${ߋ /!1:} iZ ?5o&==k Eo|HӼWQXL>XcWg!OL?A_F5]i^o⢶ +[_V VnN݃?l~Ux}+8Rd K²!ϵ N;*վa5o>[v pf(OHdקJ,^%|^tut兗|ZP0zc[&7}5\=B ?ȸCd SgowL֗-YTgls~*x$𕆕B.^Oy(4I!O5>k⢗OgRVmY_KfI// 0F\Tť%c<)͎28t>H-;BbA⸟x_s\^[ۼM cLp=+JXz#QTh$44{ULpRHi?d{~x4 hTFb26`W۟&K^LNQWjRL@׍|}Ej^.c}`{;YjHկW)sD{Ws8!y7uЦw __E<-Lkt qT[ Iefֆu>l@& `.F3O'EWP|k<eZhӵ妹qw-nnl@ ͡䜴~5캾gWse \\R(K3=}o^_.{Ksul2CM".tiH$7rgha 5+m˂m$6]AcggK Qƃ =QEQE2:x}c8, +C/QP#)5mOڇJppx0<{kMĶW!?eY@ ys@krG ᧸g;F=YEfb~>^Qz}֓-zsqNj+d8(tM;˝xX%F $Z5w١H^C壷 gO]ԫӮCteI{ۙZG]\euԔ9 Nyc5"1G dSW5>)r-@eSk<j0ya+ET9*~yXcI3TPc'{a5c3?d7u:[=$/ZFk=ySc?lxWRE܏5U@<裦5h~|W9Xl!9'&c?'~eAog_(Jm >2G#s3J1b[TxnSȯs5#2r:gu?g? ϩʁnuG3\0 ō=+*NHN12:_ez 3ӥϮʣ OcBin՛s!888>0qRf8B0k}co^b^˘l2'RCgϟ?k uöxe#A8׷q]w5thr3;{H{ZY[j߭xď2M>>& ׊3q+cx&t/Q/e3n'ӏSmx3YtK+&R[\XA~VesOj!שxkM)ky"){qb|19 x\u߈5-NZ#3E0>5 Kp]w$.qn>Ii(-`OIWaʗ3sh$ӖiG&X,m4kþ3qlm /#6rW?09°< .*ެڨ|1ǷMyxĖTA5Lt1>N5励| iL* WWᇂr"-ۜ }m۬ҮJǖ*q|;xAMV..r, nS\(!y>!xIj}O"űȳCn,g9:_vZ6c"x[ؾp:ukei|@5hSڟ=)c0~gH[U ȠIlt->6OKiIڪx/-n]xavk;_Y*1`̫y牿jm?GkW3Ŧ鏧 [2$iU_v߉kxƶk:A7/ * ·Lx}w.F@+6{?u]>Deѣ bY~?~%+Ky"5(x̌d1QkӞF|gh ?5 ;VkaG*Ox &J{C7O EtDr dtً |"tx^0k~m׉~K:?"WKbYy;L:_٫>;ouK_Sow51G1PhvѼGYd#:>Q݉xGw63_] E!c*m,H%2xPs(p 089y.]iW[x[Vlr*cG:#w%E*ZIv]G+wC Q$~|?VֲCGz)q+,-!0vD|N5(TEU@(q@ RzӨ(fO5_%ݒx_fp^s_tS'#I.&a6RXL@kwWngh G\mcO~[q5qڽā;пm~9U $"~WWxl,/pYny Y=;,ĭ"Xg=C6oZ f휙6g5w]Ɵ51ڂ/}ʊ@ƾ-Gš H4U[iLIyX($R38#uWNٟ6^^If]P[zεxچ,dQp6K* ͹?V4ق{).m|*ݘf3f5x!r $yFZ5%r'qs&UNGm*t/vy|6cy>1 ?̸$ig ɤ eː\ϋ4 Sd|:Ggͥ-me4Z/)i\<ӻ I3ھ]&s=b(1U #5$#},_&wFڃk(Aԁ}I;Z`9ɞKYsmF'7{i*0^t4I(i~^1P1'jsŚnfHky-dh H5xQF}*󱇴iAjY4>iHG7WHq?zV{s6pVc{QKʩ!"_7e9Z#ڣU4.wp'Ci:{ԺI CaYt[Nv Ҷ=.+˯ڤfedE?Ft6Sߥt0ԱT:RaJJB4:EYQ-O2~ּkc庒7nA^;k$({m* 5j:%)y)9`~{?Ϛ4~S1Z6rkJ}W7=ƣr-$doōYD |@8BPhyneHCeNUWpGK>xyHYi_\R4X'~u{_ wX|1KKlPrIV,woq6>*Ǩ>:5M1mmNI|yцg*UFXѲ4Ydy/ HJ`B_|E;tMcSmK e,͒r}ߍ5H‰n3_7 ˹"IŸ38;͊kVo{MΡik yMYnƁR0~eGsҺxkdWfj`j7Eo!2d2G,~frA'h⧁,|C6]i`ٶtyn4KYY6ACP )gx#W_'%̫_.ꪋfIBG$+ %axWV:fEwua!`o:ZjMO!_j6%ͻr($vf|Xo-|Ek)e{4D;VR 7+8"n&P2s5kOZ Z\=Ώti#6X+R8 :ċŶ[eL<;/:e&UG9$?M_xƾ-]6T| au@ dap m#OEKygu]J{y$d *1WBT2 "0EǛ+!#8@xcj֍gkHԵ%.$,j {l9d$׏De>;K.xvc2H`X&hӾV?F\7ٴ}v&.sϯ5E.M%PhN(M:}hg":c|{kxL=!+"Rb{ʷds^x-ڎpXաGw(cԀ+"Skwrdkn4{I䶻q,SFvlA1e΋ꎚ])_ޥie;ixVq_=XڪAӀG#yWlm3WГLq%PjZhqٽ ~¹]׆$tդ`Q{e0YH+f׿ſiN[? 5-oLԵ8͒$P *0ԞH4FuU)oE$@q=q ='DK` ř\,9W~מ 4Oq^Dl3Gp}\QaBkBq -'ڴk9Ȟ[|WdKDrѺY 2}zE-j$Ls%e?U?lv>+M>j"ʉegMo8%(dD|=sxa32(Nr#8Niěc*~_ty!p9]_#}84--!Tzd;ס,eL;q+;]X(!KX-HG$[H~m~A-T"B,zI=5 -K XڤK>_P~?ΝU幓Y[p8`IeW3LthK#Q~K8\[ͫ]$ToKx?Fm^6u{gzڶsMcF?u{ZXZtn;^QL4>$vVv =Az_bs7^nrm}],3V?3i߂ QisB:m5LwyEA9|z\{OQ_P |(.5F[E,Wf퍁k\,v&_q*fU%5b}I5n 1w~:~uWL-4;!=q?¯x-?QPVnd _uc+4,M?kţz;$21˷O_t ik̼aG_K|;@[kuy0noI&G2ɯ~S])̥Rn>fí/Р%`> ՚Yvѭ1rk,4m.`bF0Y (Q@VI<0ƥI*I$|S % xwU $cm3ifpA`rFJ+/}eU-JFӮ#f F3 'ھ.9x\MK$}ڥDFHN#9?m+M!z] .qbY#fvomF"1#ڿM.&>1LhڜV1麭]֝y&+_'>6x^+W^=>i[]K7Hs+Y UTr-9,|E|@ܺyykcenS*|KFOPt²|֗|TeP(p4 ŽB* O~>{< -GiڔM3gq$R`$d_4Z>|)uÿkr{Uӡ|<l bI8σ_~3xU/:5PR^beIdsp פ|Buτ>|8O h3j( my&VXtߘ~\y>:uÕ]Epf[%Hc\˃reߌWZ^5]+g]Oܲ<8' m7WDm+ k(SiU @KW~ Zu奝ujIMw`0dF 1of HbB8v%DDe%cnU`#Q#1=GR|VxH5(mSimBXf-k'׈<5k >[axpnWHm ) ZI@f3SH+U2$fok7Y𶙯./-RF99'*!j,~(ufF-k6w0E~|JdfO"]6E*Q[GlA/gڧ&!K2T8 zƿw.b-|CK*dhF}U E|wSg"^]ɤ)${s_=[#֬븵7hr]YyMj~o5N2__n xOXZ--x!(Ws:"`U;^ O5R--mJy^swa+7+.TdzWYL-`Gy]}<ZxFcIt4Eey!3GONO݌n;qc3öe E W'poϰzVvg@QC;[w<'Y>աxӮ r+vk<1m~Zg_5>+AتMi!yۅqTԴ-vQaK{H_u9}}(^&;x>;RG͊Kyq9i0T,rې ƻiC]xN下c3,Ėls["O(LF3'%-8]rYN<x0|wFH9AdP?~?ELt:#xX:G,f)g7 #.Ǧxs:ψGDӴ;((6]H?w,bE_:yӼa;uk';L"V\ xx'čRVڽ\u FL0>ni1xN7izomC O>E-G'2{K:?xWKrˠZ]k~%3oȍb28e^>h'^y'QG*RpM~^oKqbc%I-U9YZ?0^)m]l Һ).+r_Mx^ќFT+ϵ_Е7'p7SijC>mƟwc=J0`?J`vѮt1u~uKB1l=?/?п׳|oh=.$;@TFk̢y8ʋJqkhzX,Rɩu:Mj+tS.u=pWL?<%?<+rڲ\\+g쑷 ӣ:\W!_{?=&9F931+w9rVIyX$ׅpZSLtQEwg^+0_S/|ծŎK}{cf[l@p+Ѽ3 y [{xbXcz*W<߄>iE%y* ( ( ( ((94Q@Q@Q@"zRMPv ۻXiA)q =EPcgufm-"EP0*ګ6bKS=\Awc^\$LiOƞ"5(ٵ{PX}ȓ;c(QWx~\ţAě@gvG9rX' n;(UUP3'ou;wpj:n2n$JI^Ŗ$u˅dc]:dNyRHClP\@?׾Gm>2؆ppT(h]UmLJ@L@((fotoxx-18.01.1/images/repair.png0000644000175000017500000000777713222767271015141 0ustar micomicoPNG  IHDRDD8sBIT|dIDATxy}?3=޻ծvչBdaERcS!6 VQp+vaH2I0NUR6ppdqK613֮V{;;t{cJZ \|zgx7  N3g(;պSDžA &㾇Kzѯێa T]4:.lFd㾇Q*kc,4JGqjH0 9`m"~/x qI{**x}l~1gRDH.G-Ub A,ONT!m:w@Lg5BS/.M%Ns?`8WOO§pɲ455;㾇-ז`$)|B\tYQPG4 eJR&H4xNu9$tNSBzm]-k׭g?cPB̸Ffod7Iі/g Ay(Bmv)M)$#Bq%s|EklK:*${jɲXSQ J> YiAR *F8Mēi{6B l &IBW}"Qwr$)!5hwX$?6#E*Y4.Cw&JE8nhm(%ˋR8AXI*儋j39ok3G8Uf¾(omN57'1R*x΢K^g}p2͜J7wnC*5e/,ؒDC)'{6 zXuy,!0Y\t iT4JdM$&~+î (-mviVnu!]⓲ω$"1:Ӕub)ۉǥܥjj%VƘ}|U`I6B&/QHW+9*qNvlBpt p~#4lӔD06#qxO>9&͒{8^IgSr` ac3_j`fW]EuxZfNcP͔Ӝ擏Zv;_ރS?+WçW\ 2i45LeʡYdQܦ=ؿ8+o$k9Ժ5@dRX"rjFuN ov־ П"&CYnC܄W}e;k}1zCDsEr$qSz{H uxi8t`prsYli%W},sp7ei ;Xwt o?yR%e&[S@Q~ߢjcOɵW:uBOMDFȫϞ4ir#!یQRJ'.:8EY ]T>R }pNq -sH\D@AR2qΈ^2Φ-t^ꇸw7W  I ENp#;7]lYO?O%Uw,3TȁU~S5HH )%U'.1Ds S_`!ti:523$4R4 !0u3^~6b9,׈$a@4.2PMSbR=B&wbρ71LAksYBG{sZ*& b)8 !2K4 !g`"B"r H&b ;-j|zBV,icAS))A8abӔ0ew1qf 7/'y;BW_(sjYy-UMӂ$+E < Na2.Dsc4xM~'?ЩB:/h)%J$aM 0{ݼÔԼSV~!6 .h]:-)VrsZxn^4q}Ca4rE50GRDB2-I}ݭB0.8y<8M1z]zˉR !Qش~9 62>@J$t'L̩sDIsi9g%U%֘MVܱy_ 4UpC'{[h]GK. "ztSfC{b0-GNTL~{ύqlZ[X<.M1I4e`3BGB(&?6/SW2 G:toiUE[h .ѳ :ߘABv{޿>6`t=Nc,tqykeqH42crQmKJΝlѳoTKrT{&,?фN,!˪N`ouuE"]뫷\)%ZH&#<+Y[z4IX E=);IFv$0 oh9x)wxa(Ix`EG;6 [,Ljv\?=+2#NV0M]׉DbM_M"E-4WXaמ:>@/'m4ҏ!(VCnRqI `o'X$|Ԥ?c47Oq0U@IENDB`fotoxx-18.01.1/images/add-text2.jpg0000644000175000017500000024714313222767271015440 0ustar micomicoJFIF ExifII*   (12i%>ttPanasonicDMC-FZ1000Ver.2.12016:01:17 09:49:18PrintIM0250d  ' ''''^''''$Zb"'00230j~  |ro$t3923923920100`@6t  }2016:01:17 09:49:182016:01:17 09:49:18Fotoxx:voodoo1|voodoo2|retouch_combo|tonemap|resize|trim_rotate| Fotoxx:tonemap| Fotoxx:sphere| Fotoxx:write_text|emboss| Fotoxx:zonal-flatten| Fotoxx:tonemap|cartoon| Fotoxx:retouch_combo| Fotoxx:mirror| Fotoxx:mirror| Fotoxx:add_text| Fotoxx:resize|trim_rotate|Panasonic1< !`D "$%Lk&0408'()}*+,-./0123\k456789:;<=>?@ACDDEFGHIK`L@MpkN*kOUX^akce@>lf@~lg@lhiljkHmlm NmnoXmpqmrtuvwxyz|HXnHnHnH0oHxooA@pHp Ppppxp`psP s s0148< y tDVEPDB AFr b uC f2 \\+ADD.`b9dfhWjln9prtvvxz|~@UU"$Ï&("8 0246 > )XXAEhjln r  2       *8U$>. "/0 :&@>H6 "$&*>.Z(,02<4FVZXDBD|u~JFuHNP^X@Bxz\dbfLT:6p -2- WB6 yf `de@B D7F14  hj0@l$& "4(*,.02HJprxz |~ tv  XZ8:^> L ~a\      y   6            <= FD` b @ B D F H J L N P R T V X Z \ ^ dST " Z $ ( * p. 0 2 4 6 8 : < > @  N P R T ` d h b f j p z | v V & X l n `bfhdjxlnz|~vr ES:      YCB   8 0 (           " $ & ( ^ * d, w. xg0 E#2 X Z \ ^ ` b d @@f @@h j l n p " $ & ( * , . 0 2 4 6               [?ATB< " $ & ( * , . 0 2 4 6 8 : > IA    A     DSFU8 C   ZIS`bdmrtvx{z|~}~~~v~+P#(.7&  (.RCN@B2D+FHIH "$&(*, .0ICM  $0p UDSCP'=e[?dEeuT-~`/S7wlWueUqU41eS1[e]vE DISV8888888888FFFFFFFFFF}{}yy<vxyxwyzyK0||4z|~CB}{>|xxrqq{~{xwtrnkm~}`PgaP\5]`R?"-7=JF@LYQ^VbKOZV510?/8:31*PA[GIsCdbw"()'+;7?RrRqeKyN:gnL2PA*N(*JL.>>MG?E=943>vK-Cd9~R|mxNmW]J\ASRSJ3^u1EV2:]1BB'-C.:Jm6>_01j'K'cM: *-OISG~~~~")~~~~~~~3@IG~~_~~~~A7-&~~J~~~~*&&'~~;~~~~ ""~-~~~~#'$~~~~~~~~#~~~~~~~ ~!&~~~~~+ ~~~~~~~~9&~~~~!~< ~~~~~~~? ~~~~~~~~~I-~~~~~~~~~~IH~~~~~~%~~9H~~~~(#%~.;~~~~&3~(7~~~v~-AO~&L~N~P~b~ig]O~~~~~1>IC~~(~~~~</~~~~}~n~"+5~~h~g~r~{~3=BD~~n~~~~C6(~~d~~~~~~Wy~}~~~~".0!~~?~~~~~~~~5~~~~~ ~~.~~~~~~(~~~~ ~~$AEBM>c@)+M6TD'0 #aElP$$2>SmF&uHt?W3C~p=r^E7s}i]Q8@njPPRSTu A_YGI]us15T{qu_=ޝWv]M?UuWwWQq[UuCQUG}u6RHKQ^PQs]MP]yG']_QUEU5qU>Z,Yu\uu}iD]mj]UDqQQVWuWVwF\yv=te۽UCP]T8QqUVQ6eUWV=Q E?1}UWct_ЕvБёq.vToWtQߝe|t]uY#^6]q)Y}7QůUE]TqqqqxGOW'KL7]P[]z5GWQWEcP{u5EtouIU-Wڗm[W5T}G}TUacSuuYeJO'U=iGTUEV f2uQjpQuQp9J W^x4 9V3tX:Q^=Ew_=^HGw_euzEy}Z.e257u-_] DC EVwu@qy^EK]}vGq5QWYuo_Y]OGO5St}UZwFwDt_}UWY]7QYF\.]ٝUd]U_U}z_U_UKB5_WTt][ U7EuDud!FCCV єm"qT7?v-}>U_gєĔ ē”Ĕ ~nJ:kWm?ɔޔ̔ۓtד@7V4{<GGT?'lanXGiZBr8[C}T6I([B5b -iHV.XKZF.x]u[y7)R+=)=S5C8 #sFCL-gAR%CL/@mOE1 lZcLdg[w^oj?[hGQwO:M{mJ.tJm D=HR}zcxo P(5  *$gK$$i>9 W0Cdh's-Fru^ix${H(Q2w:.:~n4*+dF_r&DBkbq@}\I;;:xyy#G{E0m0:9W5EhpS60^@51 \~L7:}ELak5.K>P6h|RxWF{BJF6~,@:Aa?'\ y,P>E8aC <i4XTPX@BR: _g<qX_XT3bi{sHTiVL|<,B68oM,[~W!$!) s?t  g^t]Gxlj|rponN7N*c=OK)5XTF>(5/UhH"v]$H#8^FAA4P? E-03Jzg]KZ7(fW()WW pfKZT5Vy\jex@O\bTPnf&N<H=`W<J-[P<DXU?m|&lK>c.6s]t2# +>Mbf1`R ;5S,,8 |N3.y.w* M )+^LL;jp`9GrtKQ(!QzeYGxz/XarW84}x.s3@1cID8CH8&,!)2= ^ 5[ATTKg m !}jLzry7H@hNdHsd4?+&O,1{nF y V+Y`cT y~yJ"= tJ+0\D;B &.QPzOr$c\_!?&Q35 WzZx~*vh{Q.9?Xl4ZG) *CF<-W([+y%{<1B5+l@\y[GB"b|k* $0;~jt)+mz̒uvAQ aa \ A#U-m?<RU*Cd.GtYQ`-^zY9sVPIFN(:r ZQ5aB$  vp(%,Kku-(DfsJON m*"c8\l$ Nf45BU{sszg[:K@:1O;[UfNXkIe.E?5sJ%kKS|(#(BWVk9)p mL$85lMcBy"USt 9nqqiF%Tp?uC22Ev&%.(nOpy*hV:)Co{#c'&?X =NK rj6)fs` v4j /a9Q^uSm >A6t >P[Y '5Y ;)5q!}{ VujSb(/ y>)| Wj2z;2GBz,)2X|u]v4qxw#cB*o\ DV' ` 5X+o2=(G*i ?=<1I<`0C(F\JXlm#R2c.;(H7e5SU%/OHW~dY`e05%E h| wK)IcLB BB?b/oi;%s'vAidJh~%@+L$Yv4^-wt\8Lt ) O7y8UGXiFfQbpXJef> D<* Ejg\WvZfH?tZsrq<QpiI5EXi _?j) Mx0B,EEf&k&5d|=\yYWET[Mgu}ZUydvڼ]\7@\5 PS}) DE._^KGOM=Y~q v\UqTP_T-^UЏgEKmAT v]?\Xr|up_|_wM_AUmQQE]UE۹u\V"uLU5UWU73]Q@ē]7?z !]}JYPSwW|urUWߖUM}__}u4w0R UVw5UqS]tTeo-<5S\G_4]W^EuxasywUu50XTNwu yY]|PߞUDUUGU}Ot%_M;^EuE$ t_qMX5U]U*2WmsuWõ%m7WQ5sYB_@+k]WvwQupUS{_, %Vg4=eQ6GW4+5%WiV_zq`]}XE~-AVUe!-W]߱UQyqt{tU_UQCUU9CcyatѧUtU'W @>Ga]4U~U7XV_sf[tUXRwUXݕUUUGO|?7MP{Vf-HSUUU5<U]W='U:OQp5Ќ7RDSWe[Post]մGC?GVF{5U57t'_7IuS4QqWRWJEU>/2\owTUZܕuUu@VUWŋUU DYYywF׏pg^T= }EEaMG7uAU4]|(q]\K] u_XuOyTn[zXE_y{QcuOkGRWOQWUDuYguIS DWb]?TU6V_75s]^U_5U555TuTU}5!H?MM_?UuEPٚ8$QpQ|KQ×WWU_\UՄVmuTdq$u/WG_^qQGg7GuWQUӑuRQUU^ ]U]^pSgj6X1\`w?u4_auQ3%׵WdQ_Uk[UwW \v,vw]SuywQUC}_o\;_UGCi)RDaZxYVUU_]]5]{OW]4%LqU5xQ}O/GwSPP]А7}x;D}5Oe]u]W{syI__ =^r6EBqT5Uu{VwU]uW\@w_w-? UUCPEtt\UP^u%_eE7uoVXQT}]3QrUUWSu_~u}vTr2}]WO8%wmE}[{FKDUQUwM]|Tig.GrW]U}[i}QY=uTM_WqeWgB Gх}PTEW_,WtUt eY{EuWuTY1EEU\A]TǕmq;Eut'Sř5]4uWu?TlET՛V՜SYUOVWuV]UUFB-uPE:U6?laUTM}qwtVjM6GURIU\xpQ_U7Da$1DEg(Qy9O7at:r_FTVoHye}UWG_P]_S^yVwEdŐA4UR oT}}FSWMU~<.pNUEW^[qܕ}wI1uWZ_' U|]yWqlu5U%Qt5qu[WHw)OEw1BMwq{UPv=E}_E^SUq֗Gmu.n)~Sy1cwZ`AwuW\YaWGUayu~qrEʹ|G'-QUqq7՚TPtݝ'_W[\ۘV~\QuNv6Vd7]5E5u}UTU$QY}uYW/Ϸf+*.+'&o T #(&&* +-*-+""'-##H$g(2u(&(DN$ %$-))'Z,# ")h(E$-1",& A!L%-.,++!&#?%w%r$ (&0g,!.(#a"%7 (#a0..3)&$&!J#*'~(0E0,+Z<>)Y ') %',D0h-0,s."~" .NjhU64fpx4 i "(8Z =zBAg\f$R$["uK&?< 3 9 9MXq!J iS# /9opZ]5!#/2uw(tVGAMC w-'"  w-'"  wuTF9615031000869999:99:99 00:00:00b9999:99:99 00:00:009999:99:99 00:00:009999:99:99 00:00:00GermanyFreisingddy y 2016:01:17 08:49:18d9999:99:99 00:00:00ASCIIno commentNtEt0 - tu(u !HH  """"&&&&"""""""""""""""""""""""""""""""""""""""""""""""""""""""x! }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?O5 ȹ?_DVrH?4 'gǑs xI x0u0/|3:s)Znɂ@]w,¿|Q< yqSdljb*%?˸_ vN!~U/"^+.ψw'$y9]cxW׆H7 ~&%Hn8oN^ܩ)xڵn g l,#W +Nml%V@#$IM35#7~?кd̞v] d-uFF ZV`g-#ZHIfx,큌.`a?cv1[6*ОƢ)=ZzC%XppGn]㑈+&B}*{7-%ڕİ_Xi1¨#vONǃޱ4>MUM[f' =q\ӊi{)kpӭDIy$t?cJ궬aUu92r:zSkq{Yl~4I+ʑ([13F::e+Ksv@Y_$`)s~J h;)&+؎$c槴NdLXzƞ_n^iEat|ҫIvC)$-\%krcS;Cc s|VͭKL `? ϩ-mFg,h;${5[X[],Oth Y\})++f1s~_Qr\d,J66܎8Sv&QAf6cyR8>+cSӐژÄxB;˞**=x+-ʢ36UH[ϲO K<ב֔CwK-1) 09{};e^K)8>:wO{ur/h^(-*'#a#55mUK1Ns׏Z"TꖣKKOTkJc'm07qRY_AE_6t>d01*pIG[V,ṖHIn{8! {ۥN?ggou}Mq6뛩db#_aH?بcǚ#S"IT=S0-*d~Uh4ueYp>Yqu zWL2ߩ^/1[LBF@a7qGP8:WMv <,m@?1ӮGR]gqZV*gMB:Lj[2iX?1N;fXwO۬2I:lT$n's֛~so ̒#gjxcg^HSe"sF8R!wOy3(v^^Z{*ƖVw92 'zp8QXd1f2ѱ0LTkЮnuM6WvYFM,d\ tKyn$(g˓N:RI=? CglI-#H$|~W2LbbÏUd+]8yP3D88~ul.5l5۫+NG. qZX<Qe\$㱳%fH'v$ q\`%%]I[ B>2; T%:KluKk-P+!19皊ؙ'K9fyD|+qv=52RQOxb`Qv#?n}*K[iu{mQ=R?4H靠;d9U,\crɿFxsC.k<@e BSӥ.muu$åDXi@E S_k^i5*` u+A)7˰wnCj컇^ߚYt닩W oڃ9PrqUQ욳&7SwRF.]ȉw3ۑ~eB;ENu<רq7,Cku5v8F< "!o,<~U{\>Sci%pwlZ)!G},FleA2Z03FŐ]#QzzVwu:\#]$Ĕ1򃻜`>ʼgZmX*LX.Gc$f5i#ԭY嶖Yov/'xP}Fk> hrA/NsB{ ~s~)K09zK֖#p?tv.eteg{$7fH"i$RĜ6޹?n]]7]ok1 GqsO8}6*V2b$fHHAcv9T;gDc qsS ݚk[Me m뵇$˸0zV%仂6VeJN.7_SM7PYE+oށ ӕ>&M[8.Qcd= 9(A6kIgime"@rCpO=J&vG#L9V-RA%f #G>$t jK,!f-(2k/iU5.2卍cTksjILlzLfu K佋NxZR3F1$֓[]}--Јlu=߰Yp+nY2i d01[N>'?n"ID*,F=G޸k{R֐@{> f yY:ևB_^:[۲9'Q-o5,DN8zi!^')s^iѶ9\ZI=ѡ 2޳̤iІ;$j)\@PO{ nC0l @ႁ zզJ{=)-wC y~7<Y#zc\K>帓~Í6SVfeѕ'ݩ6lq}?*abdRWZE es5E^D"]B8O<攟!Cc F܉ >S,8߽Kr}Hm|p%AJq1Q^GᄱAepIME#{- x 5 p Fj,vR N0}HMZ9A$4LR;fH'j;\j̉'kF񭤔Gh*TˌGac"TFOS\ߊVMRNٺ(m.-v% 0@nͱ: |bZl2K+wXб`*sO2x[Y9 1 irslQv-l9Gw3W2C+q z_TwwΟy+I['kcxW&7CÆLo{+C^Cc]@9;lѥ\!HqGg}[9[ôBwrLimbaŌ3䊺iv?iY J#rQGg*euo*uIpqsw̭lj]5+䲶kS!*+63t;zUFFȍ/E ߠǠi$"W^+y'M ysF8ue}FA4 Iu+1k[5\77 $@QޫgiUtBЭ2m#(lr-8Rs\Uj3=D< !܂?d]NIhڰh4)s]n}{Upw#lϋ[X̀èI⦃TbеiZno!yRSOwTg'#9e a*r˔%CZ3s+.*1VGj 1" i&NNxtJml:V~Z/K$+V%&b|xeѦIVVo Һ rGQֳ3 Ŋ{r $Y$zchړjhgW i*Ex設4tзEsL| 8qtSz[}Gn8T*7#h)-K2,.JZAnt 14f?H6~~8_P6ɷ8uaJғJOR&{u[ &\vz4-,co7]>}k D3I .c yDVbi wsYP[-`D,Xg=kJnB_]Ie-5.Bx?xFӞ.;W +"C?$q+HnUWS)$?AN,d1_,%93-j6ͤQZ\jﵫ{X˰ƚ<)k⽥#'TcKPȵ<`gpmq5hMoq%5 qVMMd 5ޥD6qOdHlz`iZ]ΡWyѴd<|ulVqN)̗w{3aM<y 1 2 3 0 2/1 False False 2 False 0 R98 Germany 0 8 6 3648 5472 2 2 1 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(((((( *@hSyV4sO?dx?fVd|_4$6v߾]˘g'##ݓ`'ǻk˭?7W^m66.`; %Mw> qkk/iz½71s Y#ilʠH Â\ln&JmJ}-m/h;QI9'i̸6r"VQZFtdJ2j>-M᝿t7fyÛX~?;$ao0 7K _itFdf$1w3V0Gh:XK-cyy3Rﴨ8c]Ķ}9#+ݹ3%J~VϰMK/>j~%>9k>,m`V4OG󧝈TAfU,A%POckzmx `Vg=0yȮ/IY.;!H Ac3k75_^'{ֱ\ 5H҅(U'Hk;X[}YIz|?U}q_iOK>$i|19V$Bxo~О=Юt|Tz#ΜQB*[QԖ?Na~?=jWZ]s$عq V;XA 8]gm7P}yaV|7Xe B[~?t|@ tӬFPricŸPϋbڶI=B*)ְߙTKXS:+/٫jkxS7f^W:K]0m+Om\x8`>QjqypжFX/'ha2Dc9q*@S]?|/3|I-4 M~%ICԆRqʱ@81C iăG7 ?o K¯i-ޥuq*BqeᲸnpX|h3|'חkvmElCEw`Jœ/?k?VyohbNl[f-`cx^P8'S"躕Nj5#)>Ұ*) DdfU**6|f9ce;wON὞+ 9uCa[&Ԝ^۲H@HeӞpt DO"B 9WY$;ϽsGvWsTQ矕Evjr'+jZ#EWr}c $%:+wh1W`?3W#ŧI@cϟY*KOSPcN&Qϒj? : [j2C=+ȫK\^%5 L6(y<ּoY߇Vjv [znM}M^[KK$Ӿuj߉u\^k^t4ܥdTw{r q tog>Zp013jZg|Gi k{ {_2v?O3\쫛b5tڟ4#LA^/o`lg0F3g#~2*Y%|Tեs}˷)Fݍ,cvN3{o$zrj99@ބ\y':ឋ,znVX~=;W;nM;s^&vyih  DN9pO3~%ƹkVPkrGc>g˷'zAm04dImrp\@Hk|YKzO5Ufn!H%{R}ϰ:f@z+{OO<׋]cZ|]G->qZiw1s{1XdBN!"J[Qx6$[@&wix9,JH~Y22~\ ׉jߴ?AfXMpeI,gbUL)$#PGe*_>|'=nPLGﵤvO  nMu|@$>ա1 =@_7wF/V,wEr_ïxƞ/UeΈv. IUXP}[_y<Ɏ}ZvKA2;9Cm*8*FA,:漺tsZm-*9ii0}3fk?C?$<=-i[b9'By !I.9Dh4wP7ORs`NzCZZ<ŞXM6öm<TJ rEs_G{-SN_9m'F h*gRqL> ~5Kˍ7ܖ:u2 &L(>_묟X]߬W\}D %%Q7ϨN*cE4m#;u#,3bGkC]եE,l^z׈hYusws m]q .:=Cm,xsk 8A.r ׺=K G7n\E*Ic߶Hmhxki:5P4R8E7>uL.$Fe LtLrTW3 @Nd?jkZrO3G$dCs WS*5nܠg^E9:iǀ< kWIdd"F?m?͸ihoAnʽl=)I܈ߴoCI4(܈h`vO~Oϊz^HXbVO'ť3Yxc_@;j "AǶw&ߎ8W%8bMF9m%~Gͫq'MCJ^I)&ݡ&rnWipxo6l"tM%Y/mWUuR'`cד sG~eHm ;b _Z4 |Zׄ-oy>L0I) UAY/ xYqq¦Sl9Iyxhc+sUvnQJ/j,kMɥNz4(l*I|mmz~ k%!Xu#%rQn2z+;?u 񧍼3+q\^[[#UAY69#ß4]yHaF R9gF_376(;G,Wsӯ;Gֻ3ڛ;k䏕kK\*>GN*JѕK;6%+9hߧ+hwkETՒo1:"DgnTݎ;|0~>)& [fGk;i:6{kmMqJ_-keLGyV>7~ NO-ŝy%s#N0lI =SRЬ5:e;>VXՐtm }|._}:xQ5INJ:JRkNm~OUS_>'K>'$Awneyxf`},Xq0W>|9gi9mU)-)EGSF~$1i˰֟8c1^6.t׵ꗞ 4s)y.];@$qYR%Ȝt&hx n<5Mcgsi)E~R `$|o ]‡AOrѽm1l<1E$d硯$x\x7NwksLBNWHƊ(P4|7sLVTbp3bO LJ5 {.d 1|:$g5]'OTG2f9գKr(2x)P)NQpMFI=c+ߚ:wg/bST]Z:e%ZWeT3+ /\Bu݀d`$ԓɚOohvgmS/ Xk^#xc}4bQ:CNHd\}ۏҦQUIp,[ڠڐļO5uv#<[Ķ>%IleY& =zk R=zR8ijI+E\z> .`qkQկ8{(3u%sV\xz+wIwF*3Q~VgXsW4}Y5 6;j -ĭ2t\.OjٟH|cyپ{ Oo++4m3%Iw>g9n`sԖؚƮO_3\ʮQQ[>[%Ju1u\fPM;sԝ'Swn ]߻GEl|EԢ 7`"UThE)x8Ry#1%A3y]dV`@^PxNik昣R&'sykϏn.0kki['_K`lT W՟4pb8YpN;(UJT^5S]Nw1gH{Q^H؆fa^}+d[f?$ø<9;_=?uwvS=11W fC%$6͸ z 59,x)qtHNMjYea.wp?jf'RS[J&RVGԟ? "՘F:+Fni*Hb22>|C%hCsѰDW$gg~.03Ű*q?h"I?橓aoYV}n[Rw҂o%Z](<>5+X.D5ŜFX2_S?ÏxQY%g)9}yO/kOڻ9'Eׄe%Lj j%9~S!]a]4ⓒZ Gm:u\JHR*JQ>YJu:).M%)JJ.M%g}>8*Z4wۭAwhRRFpx_LZ(=g' ۬6"P0:;W~Rh$n$6Q}I&>dWWoGi<fPI orw}.P!RĶQ^g}\))* G'{n{X΍IL)ҧGTcZ:išsVVnWC+S?xf (e+qtkYf6{%; H0?=^}:|_-][}I5}.V[x!*ʤw:TxJ^V+5cBkfQSKT}ֹ>jiʔ(b*j A\ ׏Ĭc~ fω:6{4OYsn@ FO1.GLu ~xKΗLew (dd0m|;ׯ5Ym=a@ۀeoNs״A/7fsXMC&WGH&ʚ.UO[h>fx FgiMZmpeڪ>"Fhst1&26Ցn<> y]x[S=<-eݒ&Va91TC2wqֵwmnnGx q8v|c]q[cټnv>'#&=$qe!yRDr Ļ6a]pExŞw>5Xj6|1LK$.ݽ4Y\j_i v ȷ#"; = %&|qjiy^Dn ]N <ҧRtZz .r(x |3Io*r)d8#QM6o6mU( L`@3H=,W_ u1acBa>ݘ篗ܚ߇|-9|@[-RGi*ZŖ *] y,#$qtqrĩRVwX&tUE>.,Vڏ#'}B;`3ym=3Hj-z׆iZޟfE 8+qk 9FYmgLX4+d6Y |05iNL5tE}gG=z ^ [o}N VS UiK`Ԇn }pwyy9NƺoڪC-&D_h!}Ͼ+;eye%V2ʁz~}w_xn=WI#W;. ,=ׯTԠDkTtRrr|.yƟHo>[G,}1 `t/&POYiw(]'uK8$>to[ۦ9+7phbZ,a{('+3o &sJEQ*yp2k#K֯ BB^x[ t_ $7?|Q4)hW9!`N|ihk>ɮ{y"-sӟ|&2Ά"4nw[cJ^Q6կ$ۜc)s6m>Gl/ zGcDŗ.Ae9`z75࿃/Aqma+\[*IH2ZGzv񆯭+o6D(v,qjp t]¯w Ks>+`$:&A ~V.~ ,6?Z]mWd(w*գ 8T8"MTIoy^OԵ\x]N m|bF卺:GN|5obF9? O_w9S֚ӬԈ!e 9{t 0*<::0$W|$|J-ZƇ900BGz|$xxMcLh2j6'G|BKtf |f\y{w_sO/FE[/0YچnSn:⟄OQ q5׉#mt{v˔FePܑȜٯyMZ=*7x;+kzςtmV8/ku[м3S29ۊ|%]N5K)%6*b*[$v;TqV> E׭|S\ZOA'ԇ$E@9^o_ 6zZga]>G}$CU>\Nv1F1Ӡs9FpRk_sVoEWמ0QE Sw/Xi257:v=G F5_K<1 .6ͱB_ʹ<3Aᇇ4zx5؞D@JYV0r~ݻ8Yh;mn)Qk(yu%҂Z"g>Xpn)w3|;EmYKDp;)9m^Jd(1FH&}Wqd i!w*R\Ɨ+옙{)ޯa6m/6wJŷxKSCX+y%k\W`g9jm͘is/v,]Md|WXb;c x]PjWRa+=Ji⯈7(rEiفsqO/6.~~֏'xcDN4QG -P>Bf,~*?>˥ZZ37(fRWQZ[=^gy%w/$ӻI$;zrIC՚ȍBܱ'MÏ0m-9 su R̈nUyX_ph_ 4?GRW'\O g!9#'$EKťRG,NlWW>*3Q˒jR??fx|kP)V2XR̭0A;ϋܞfh>)֥ܛ+Q2˅d q'Ú;<𪡐cwl漊 OZd$iO euUsך;Njbj753HahʔхGſuۓ*0FNC#ۃk?j->g$3 p}?@K-WXl˼65QPAaqW>=n1v_ð'ֲ vu*IvZmn/(Ռ)ҍڳ<@SxYDcX,cxNܣ+-gᾡwK5زFT'idz =GMeXf2#=~m985/ZuGiofa0q' WQG >yAN/F݊RqGNKg:}|Ft2]<] ;3qҹey.YR&E_|94$afEzr+,𶍩]ɣiMͮhy[$r+ YY唷iFfJlCNRjG1m[KSUI`Wsϑଯ__>#MhDxީ4ϋ/ջiX7h㉾PeuQfӻD$6QKƸe@ 0eЊ18b#yh*cII}iòj"4MqTu9?zBM^_싛Յb3t2DKb1ެ.vvȕ5+؝~=PƹRj5ng՟l٫qjΗq{h@>~&|O|!MWHi X]WW3"f /MSJMI2h<0Xͼzouk?~<ӅIh},3@s2FMtcjJӎ=WI=wG'ztV1@I]C+!8>z[GMXM4[ioE)B}lO,C_~w<fvl3+ߎYJJ w k"FyFcVѡSmTOEwc+|ًdz#h_icMѣy]^l<q*#U|ΆukRm+Jd%,>1Œ`8)oKNk0 T8<BX86G_cwvVDC/wdJgKԎ'ۮY{`~P2 l`1gVZ@ˀOMGJh:UFSD@$k72뿍?zsX+HH` ѵMWZXf#-G}kX4B> A;X̫bTp9tŚ[h Α,\N! mc%w(flmǚƟ?~7x{z7,u#W#ȘU',,l^ZWܢ憭 +"b 0Hj'DsrMG_x5k$$¶zf>hXzi4"czŏ_<}j0C䢇ҹ]'s閄D< JxΫ-O(A`q_ů{D'3h:/!}_|GԵK#2\_Xjy"}DHu{&,Ռ^hz <]82kZxCIռ-yȟF9˵c]WB>duO x^+UF<\F1.ɏe5e]G Kx/EnI!I#0(h gtxƵAOhûI'itڤW5MHZ FcV%|U{%ޡ K۵UFHd}I'|E,+o f}Xݤ[#:!^1xx면cүoΟw-:d>q.=#<JKh,<;|Q|.{ 顀p U=Ci|9e⟉ֵk}{ⷆ[ħIu\*OOsn~j$qic㑊Zr|o];LMvFAߎr]oBOKX (`c>X_]_m8FW'h$dY'xrɥc 3Y#$ۉC(;NHN{\|!|Ehp*Y:uO$K6(,A dxc5֡ERnai%VFm_d]g+o.#“nIƾm/xbw΁5 i氐ȥ Vy|0 J_wW:}߆k Wږ;)%La9'QW}'\Oh(X7$$}M~ECR^c"H;g1iv;k}_߉K2b ʫU;EcxQ8c>g  |rxsӞx;Q=;36][ 2=x^#Z/mnf7W2) e(JF9O8tq駴aO202ӄ=O^R\R*7L)K__6GIy9UY# vៀKanDSq"WP+jO i>/0[ŦB6 ȅXz rkwֵ} S:n$G%zG|cxɪYxYuH"2Kd#2#ESSV4TI/ې Twdxquz[Er_ x7WlZ)MPJ͞R=$?Ժ}M Z̑EϕmЎSA^;-:þ"y!&u÷`ZuuQo.+{ua, G坣I);&}7+1M^Y $941iDƲ̈=cb>^]d56Vvsx=dy? CųM՝$i-dK U<|?Ꟈ9gsڤ6STQLqc8sRrњUi+< B[6F{`$gGrwwWH]G[xm-򻣍4fE%|!4kiImd/\,@Ӟ:`WOžyc&Z0c ԋ&`k2#c^LJ7Nף8iҴyPghū$qN.fuD QA셈 +H#_cᅉԴ:W)nlH3Ytqx<%YK -h-yxҪ?*pMQ|{ܖ=sgB̷B0rۀ@ (ju)ݞj>*ki-|̒l dFӕ9л?#WRx.bRw=E{L0wa+;}3<-HWcˡ<5 kmM b6C\Ҧm}`8a-KQh&wp ?A~*PK#+k6Mnv2={5szΧΩ+(#$?SPQn'iƮ'09P_מ;ޣE#_8[9 2a0eJ;9/1Iheg \cב6}5U5u&GGmR:Wuɮot2Ɵd1a* y?|vxYzu<֑ƀe,q<ʞۮ~~6hu8hȊxfYN9=}}VԞ #MV_kC$faF1Ջ};uEO-ޠ%7:Sv`gzrir#sca-.?r\[MqXjHpǰ;4[#نAqO UxP4%Av/*N?l}#_J?GEE2Kw)6dsޤbWG П l_iW6;h0GB2rP@ūnxtOx/mN+RÝؒY$t鞵ol%i;HvDF$q䚧2_vZ Kyc.aQ[K4׭a,u<˗{xM9#~󤖌 g߇Pt6 t¤k{Ek{k-GpL 7m8(Ѡ=Ez[_֢AmJ+[E Īv䃝|?jh7~h -˨p tۑӀJ*ϞkXT-f;-sJ=~)as>2wcOSxuthE(HRT c)Pѵ|-W ~,?UDkkwqC0IT1GϪJjW?i]ZWD\EvB{op3U7S6Fv.<&18_) WV[ܘ7]2B79gq 2+ Q[ɓ|z U.=iu)QbD,[qǬk[V&.T|;ۤ0ԕX< `0G$G?ݯjo p47r̊NH3GwZ"E]nUhe$> $̽>gG3Z,ӵ"Cs/<urkqz/{bC#xͯ%؈/ç׵{cBߴ41YX73>cn'%ry_ g|%4\,tHLw;dύCe 5&I>Z^v|OM?؞!Yd͎rFc;_ t"y},=M;~.N>3յo#[XlUN易mld]VapܘIs^05qTrM-Œ^ BZc@ /Mǀ3rzܛ=۸2Բ Ո4y F-ZZ XOs*=ˍF!98PU>5Yli<Ş*$b+ׅ׭pKw/Y$>Pt>7_Jݻ~Ng|䏗#jp?J_E.bM'BC6XTv 嫻gLSzE֯=:&kWG[pͱHPH?{z> O C&pӯlW?vY 7`RN> | Ѽ [CրŴDa)!%d*gV.RIj7adHK|H sckP9^4x4u+ F|7@yH8Ŭ|EC&*>L']f_5[3Zf aЀxa_x7ֺŔ~ V[$b(UF?P!|-Kz^~0`33vpKmİaG,KIt:=ڼ9QgWCGlG''+7eogi$o]Ayn~O xŗZ!w8^*Ɍ cc ѬS*EoZ vVwj`3J\n^%+kmN{D@ 5RZmRo:Ʊ4k *4yT|Q|\ͭ ixKo]E;ut mq-돽2(ޥtxXOlg^IM:PExJQLm#8>误G4Aqxwڋ<]E\0?İƺj7bCsc5*^F1>M_ d6XM$<qF?vNy6O$5SñxI7qF5?߂­r;ǚoURU+8P (Vq^xoJgk zIu, ysY7asv+>۸l˛xkyqN?SV'y.5/xj{{-Z<2`A"%BpW;gܰҢ/b]:1uqUEs(>գcju{8 is%;ζUq\JA<9Yzl6roH!XXX޸O4z#MCQnXa8$WxJգ"j2R2=C`G7v=[⹝Ŗ!ۤ;p~}98_CS GD<ը|MQ.H0ue+1̻F7oQHo]O_TFҵ,cf.M߾Ϩ K/time{ L܎Xds&0|Q{h|@>wHh^R3o3t?Zl/amD!HOڿxs^I,Oỏa*AG'5b$sa4>jҮ[!NAڽ^}cNg2 UmNl$KJFtq_wm.qϥu%* $G ~XWYE;w~VIoMGwwq ӫɭ8cq]/qm˴I»2789g5࿵V jOTzg4!b8e8cY!Ab,_fE&FAS8= x6>x]wU_6U{˙݉ .AQſc8R[ Yr#\.19$Sp/NQsk`DVfqʌ\/|uĸ[f ǃ+8.c!z`Ƥ4FWot^;X p8?:si,7WOnwGJjր5u$/Py\ɭ/MD$DF$c]KF.hW[c/kOcTI@ ێS[ K RD]d6~ZcFHNOɵNkھ|T>Ӵֻ76^!{,1uy@P2ɩ wtYU}zם:N\q$ WڼzjG$"ڹrbp6f+~c5UXy#yܯ8=_-^B}9 rUߎ5Z43::ِi7Toʂ}vnNrQj1gZs‹۹pzud+š:m9!l' 9Bxk<@{uȆx@A |ٻ`&]K[xj] ,}zh:ybRMԂTwcz3ݙ|y5o Kkr}VX6HWhc$76ZڥŭCƱo`cWG?3XEg~V#e9fFʬ5r^ˣ:}+ \n9c=tdW5i*&c [G?4O1ڮqW5և~ɮ|R6!ztNJt>=iW:CfCO.,ܯ5Ldlp^c[fU͌%3ǭEk?uk,uGMU&CX l`n9#5Z}kz4V/Z]A,k 32do ppkO |+It*8]{_)˦^-8u8'WQu:tiyEydЈ.#Yb00*ߑ÷ <޼zG/4] M!Qӭ/7m;QڱNU qoc_mMoV@X(-ZN])]Նwu#~m_h-B٦eI6ʛ5%*k'V}jj6?4|#5:,m|Ot,#|c [I_mBq־v-uo[Pi֗2A3rXǀW wHuNQmN'go*D@*vsL[ޏs>eN>~|N|k{^ynۑv9R +?lsrisiBXImph=xx\|3[r˵] z@bqOaxp2Y'lc nJ4EN+E|+)fIcĒYrI ui0䲴M»=žo>J ⼕f[3k8@?xp۷Sq>]4 MA -!H' 5$Bx;+D۸qӧ6[k'Ŏat+=[M8wrjj~KV0y?68)[Bf-xPǤ\]mܥwHYW=kƺxf.WA F,$c޴5R-JGWzϊ~'\]Yx3Z$&5C*rQ׶}|&--Q,`P^M|+t(+ܞؿ_<1S*hgC4TgYo\BP\ 8Q|c|: H{dPjr+[+"s^5XsR7ӟ-?7mkqB56piѴpqr@O}JAo pxlqǩ2]d!0n feu$sğ]#^a6Zr#炊f^^ceoz%W1*]Y20TW^4ٕZ49߆w{gt73$w2\m#Sl)IՈF+V5}OMyhsc.Dop6Yї!64־|8ҵK h6 -09Yd.PSI:eww{ic#LY%bpžGlIfmiZ*_ lLj5mWS\_LZ[Hq!q-I_GᮗٿҮ dI!gbNy;9_?yxM2{񉢷Fmņ0 HiLWRXԀHgp:WQ8+r4JH E%>݂D0'3_ɦZ_Z6%-5'6 H97v ω<7x6hGBqYr:D %)0l|=\:MhsN)]w8t`UqhE X Uq{1n=Lou57OQ俘@LG ]$tҾj:D$.m.0qe9 \?驯>6/% V$+† rFKB\t9cd?+kz&'cGn4sCdo1MV1oR>%0мAcC`Qu%ȩ} ]˞Èw{JϹ߳'4 ~Оޯgah^-RSn ob3${k> >ԭ%ҡtX4ɣ1 F"q!>Zf*5kSgգ]J&WsI{ރW;4ˡ7f9Sp" #X8=UsSx@MI^YXnV9#y|jbZ/i[k$Bk$QLbݘ|i'߈^&,wּ7bj\08Ʌn0 ۯZPŦ|:@EkkunX3VX" r ƹb"qg3APZEu4%[ȅO,_{ _O srtF''b?{W<=xŠK-c@noʬr̗ wqSB8qU"ϧ~;[! eWD+6w55?ߌ|gMY$!$=k]Z%ҽ񵦍ulA/FI@kuO1tgRH)lr< w3_6MVqgUnjn>xOcoٷukFDU۽`?x 7ONIk ЎOjX/MR[}gOܜcۏ`QMVg8Dv#lxTwk顷mQ8;zKC;w;VV-ad,2N[ x'`u um4ݤ<#'W=tu ˕Y>$j_inokoK 3_MxF|B1LV\Etˀ%f)U@fqcï'5PͩDL,4 n]Fw0=R~׳o x~h{jyŭJl3Uz`5R(躱{NdS~OqlmiaǺ+ҐH#0 UH FoEgKj,LVmVSyo8 ;; wN֮ujUXf\C)ˌF1 -~i]-@dH cg>]fiRzĿڣ]|GxSZԚ%䰮L$1e-=+[gnj_ZiD<"$,3'WP6Y . ˒?r}kd4)ٓQ:UieB'1P>M_2ru9,3)8>;ue%nv Ful1zeNVA2x; ឩ]b OMHaO8 B۠99#sb a]ɴu^îoaf>e:k#ZּP\Z{Z# Ҝ<2;Fq8s_QAºt6w$+$RpsUG$ZZ}AYy}K N=7^>ʞ}}:M4MBh m7'#9c ^xwWd`a[*%c9 2qaeRu&U8CTMå67?H@L|']k7QYVu l$Hq XMya'Sv, ߲W{O1ԌQCt@>7^ti<5}= f5Cj[Ee۟P:fEZb D$ >'Ư4)".m'9\:?ړᾡn'M: -糒9_0>[هJ47q~-!L/WT?k~!j 񆓭hx]KC[pTrӽFzԏ4Z]Ok[IwZ>Zl/!Y-#vIp򀕸$` cgZӭnBK2[G+11Γ%GRװ.oi{i_^!JIBk}K)8a׊x⹒yaA_ d;7fdfsaiǖY#'{?//ZOy fSsmbQ,H&QF/: kVvof҃-$I+91&y g ep#8_P%oBMO>Ydq( 1O-wRվv>,2/4f7A.C`q\_qI>_waf/}Kk:fd]9&Ԧ^\UW`\`Gy5<+[=3=嶗]H!L o–͜f< //5vm^^2ϩc4ny$~{[}2 NRy/#L'vv5R^en0>#>$GMqRlm9^, $9f`rAYrj:FO|щ D#Z_4X[^ 29ۖ`jľq\YGo]}e'Iͯ%Y_Rs6 8z/h4{mG.5zO~wZbmmMbcM/DbIu51.*WZdѩ $u)ܯ,r8Wkp|9&/ܶ 9 `ג|[K۩ O$.@$_Lʲ|->)Q^Wis\|nއ-vv,ojmmeUм!p8C^⿆>'֯b]1Eᄘ{W|@4 3-ق{$(J`Xzc'?O.B$k!CHaLS]LwGWSi/(ȁ~svtּ}=r[$ 2 וoa[ɍjXpGl#Mմ}>(,FR(U/& ̢Z~1Ehzw W[5΍w~!XUL!Q\*jox k-vi7(Se,"7 n慈*%LjVhǘz~zEO>MCPrw|ӮY${x2bVc͍}Jo-k9>1x ?6VXi5Ob,̫Ibqv#/)_|4A6I,IDlY%rC8R|>6'\^ZjZ-ļۑ "F(a$}CWVq4ʚDo1SI'gy$QqmGzCR@Fa%4n0>x>'?^_k>і}]jQɺ(yOhbIukg= nox~xg@-mu ^qʛNİD2*yw#N-v, ZO~aJ[ |`;mjqtf-wp@ceߦu-OsvJ?8m듌%h2Uѫ}QQ|K5X7LKnEVE%wet?6h#ԗOf` @dgqvl&/>!Q"B3<ILmý3T\eVQ_Y|rIbJg`毎<)Ėz+f駉Lg~rqx۴tڷ5{ˉEĆ)w2` 7#ȨKG[[ֹu^ak5;hI1$pB3W__ PGzF-WUG+ ĎwF3{=W5ND`/h,1; o-oOj/MWZ y`7N8c}/->|mk%z!]"tkmEV# :Ҽ%&vCw[XVR"gOv%[rKg ar\< G4qhˑ=:kU<ΕɳzhmN/HXy5?iO^jw\ݴ߼7r@j߀zG} u*H Fgs5Tӡ2X5pi9qq׮y,K׼ٷNaB<μ{wZE"kHxF?[v72w#xӭ!ֵ=Gi M).N;`|_S Ir-X\k.'cRy{^6hR{(#%@趝_MV a9da'{M&ƟKvE }ܰ~s^8^4 G[Z5^7mXcŒn,{g,u΁[ϥ\\5jZA$4@x@8]xB=WJ}2Gyxټџg9SjwZsm4",i0)q*V2(g}Cog2E,i*E_0>X⯌}> B#nŀ >@EPJԵ'rbӿYk|mYj%Fi,vhr]FJ=*(%s;⚤upzp2k3_Jsx}}|Jmʛ $?<;5O<7gi`;cn z xn/M~p+W#x kaW&籹}wŷ::Hm>k +:+(=nǃ/|/ĖT3Q;rvH w\71l-E *0n8ՅMẍ>Q%}/BBK˛$neG=kh27ʪw ĀI$?|KcX+x ;y!q_JxqKᾥgxuhmiztFe_`lm,1r:\k?iA|ݡY E$a@3aזj6_Ԧ[%m#9t>/ҡԖ3% .Faf%sȯ?f7y.?o5) Z6In'*0|2P/~x?Wwfr X)Sxឃgqqkk3J3cvbP_K~^Ь-Eӣ`C>#HHc2 u'v? !#QÐ_?GId6Ba}ݥJ Gn{WG:|A߁3%axc̑' ,>Q3/\Ks^#妫=Bh k|#Q6ךu?jB]> R{LYXEi{ ]j#vLuZ^ 0"Xb2.nJ>Tdwi[k䓱 !'Ji䉛*#8cx$`EMeoKp8W$'$9s>u! 6DZtB{s|7Y llgen5R:Wbt#^yIos l0v֑]ivt}qņpO9>{%N݌u8]#] Er;8AqNx{MRSi-PA8xc>x{oďW>t^xBDƭk%ap0.H '9$ ވԼ)ne|90l{Ǿ,xRm 6=đ-h)TA,y^~zƋ8gAC2wo<`ǯ+k1 h7rwhp`sFX漊ӭ+dSGlV4-E#n8>^pd$,<+L㯊Z.#5;9.7J_#.>,nmrqs:uWo?>8\8~kڭeݭԆ?.rA6H?U>$jn(lgY\}NFC)OApd,{d=kۣQ|q}+O -na u|? ʓT1&oș8#++g?kkaw!g5+č1+ m2'X%ޡ  Xz{^Wp5gEWwIoGWpj:(߄ ~ Դ)KNĿ49R*GS뺺?|xu]cOԮx#A$G& &dd|OמX;w%H-f=jW௉->|y)X;ǖ="4&@wrH&ޣAׯ=j~ZF[_RsJ ctjV%ďȣ&Ш=}®A]zzw^ie嗈R8Ԗ/}RgI sgPyG=᦯E|k5mrnm..hK@!!W@=7~,Oyӯ gBI<p*2zq]76soý7ҵ >3[M8 G1uxH(;FR]+Нy~&?Ogik4 g 2 UP88:gO[AeDl=d8% 1ʅ=s$#Mx[_QӼUr0:2LHp[p'Uog-FO]iVRm%Uu!шC'r)a'dRUogJ>vss; AyEFIw1`Ax_>  ;kHKn5g8آTF1H!4w5?\h:sjk5 F8,y6Ȫrp0k 0 kύW{h !T-#H76kyFtgܜOg+nt˟ i>&ծ$Ӓ\[v@-Fg{'O"O|P]f]գQG%b$b`g͜Zk2Jr=d'%UʰF*@S$ڌO4[xlem۔;~p ?62Ixĺnluo/^)>s$Ȭ*;o>\hއv@ 7V&/gOxbJŦl,6w<GJw]z:ǼAOS"k3NK-H^H23[|c-#×+ڄe m{(PpH A޽v.e2= UMK_Y@`@S gѧaI/Iq?٬tx!J5;$c'h߈Vqioeɨ_\#7 H9 0'']Wҭ-dLtD drG߀_[;O]6gIQͳr܉ː6*2dx"36ɤIi,wFVi"eo4UQʫ5xs-|q}% lAZ8n79+ <𾊶hԦvQ T׏XxGTc%XsKl g]ZOѩ/yoqf:֓u-`w!8bC "ZBӠԯ䵵1YI38G׭FY':] ,\عʵ0H{¯G <3mL#NآtDQwo¯R9A?ŕcC^w_rru抹 |ZVtU;%tq2N0u->0xsPc]B(` X3{\zW˞5#\O}ʙu KV[O#r<[, > ڟZo[Ԍc]ԣSNPJ.Z=? 5;9>d{@xv8==3RxE1" Oym>7G>i%m"dT >R|}X8S-5xJpq"nKXgo JMv>qwG–v2[iʰE8… =pk2cgw,5xث  OG$m>oo2C2h;ַ(C#xJQ%}}$qacxxͶEWz!EPV,dÁߚij|(#x:Hl2s8W$1 *aJoҜ]S_t/->n{·qwcxw~0Zxr:'|Wq4zedhnR-欻rEɹg=J+Mq[rgA]k"XEa`QRCrYpA`fOf~1vwu;<ԎIຝQp_|WN'NJ[cѧ'Τ|aռOC`nЋp*cbEwA|猵Dn1hSRخ ce*2h \zCK%T|>kW-w@v_hެoqϓ,aF n*ks_ ZkiK.w #) ]c84QZ¼) q|<~ͺe.ՠ@fY#f īoQNryֿd-c^xG|- Eh!Ru ר(ިjԔV'FU5nQҮ#Jx,EX}ۇ$2n+& *=qo%[skU8a( Ҕvk? %[/O %[ɗNobF_'z>Rk*K:=F$Y\t`ټgh;AEuE)BJ:4擧YjzwmbԬ`I9"f'8\x\lj|{(|C/`.8ax_y+h(YE۽3E=+ű`NWzi.7neƒrx4o7vCl"A7pJ}T~EBM6њ=Ro?h[i-2YBeefh_~"Akۨ|81uk\cHn*7|9(qRO,lZhwݯٷ>.}h$mX_98!f 601 762 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?8JLjֹ{߈5SʌT.?R3VO5i0 1۞>GU3}OK-bQMnȇQ֏5"T.~F O{麢׏zޣj6$zeŰ@7\^<$@Os»p؅Z癏"T/9èC6## QL:2l%Nƫ}2Hj}ZY`8|9u)8 @ p:9OZxMc#ynjN ƺnpXfC*DJu?!^yyW{[⹑3'Pu9ֺSyQ7ҟOQt}CP/kKꚮ<nk;BC-9jO?z:eo+)fzJJW qi|>Sh q5ӴN-2="86eV`^C0Nx>Wuc7v:ui=fUK[\95~S}/+WV<Oχ!{2i\"Q[񶯫>WHV o0O#o?{ҩQb"E|2Zu߉2iugpO5`J+xoTst$EKo-ȣQu?_^O4-wXA8by>[03j.WU<;6 6:6,E5A|gr/Qu?Kȯ|ysczx/ &%e$e|NT'5ukqK4W7,90'4&K>T_OQȯu?#7tuX7HnT6׺2A}WU, kesmH&xUSϵEEj|7$dWߵ^hmZi, bKB97^9(Qu?Gڢ"=xm;XORN2[i^4 cXAš^IAkIcզD$r:3NĠ%ED,iSu ʗ> 5MF[,@/$FbǒO5 \]Fk庼--o_9n9=(rvik~l.,H jڢ"WkU7ynӈVv ,+~4Pkjiɥ-jwhȖ2 3,1(]l}mȣQu?\7|O%jqߦV'IJ9`Sz>G0XT_OQȬz9ڢ"EE`}ޏ7S}/+w`}/(T_OV}ocQu?Gڢ">G} j|>SzO{:EEj|)~G0XT_OQȬz9ڢ"EE`}ޏ7S}/+nsQu?Gڢ"{_7S}/+}ގ`ȣQu?XmsEEj|o'۽cT_OQȬz9ڢ"EE`}ޏ7S}/+}ގ`G,')~bH|Go mc5ޫJ!f"q:GSR{ k$XV\{W4=[Rkgۚ(TlT׵CQbҸC㧄xBF8 :3毅V7KՇ*&[kxcB%{"ܖ><.W3s Cx! # AǦM`} ?R'I1)/ֲUpKJrmzu ls$TN%F?ghTW9Bo#I , 23[uU660HIMs g9Gǝv8v19IZiOXvEv?j_ӿZzF5/-o=G}λ=-V/_ ia,sչ>J5W) s^ƥA;ǨgSOMo=G}#1?c-Dy/ܱ97.s477e|?ޛ}͡Ie}s˘YN%9=s^Φ?!kTPF_MewoE=yh}Ý/ɨ>ʬY *q뮦Ɯu4`r'ex|^z5Oicq\ƹɥ}wᯃOsOԓ,3|Y9^s BNQ$(rk,.4Vn,K+GgO3>ѯ/,tkk _Ȁ?ҳm~*g_ZzkS?}(}qoW%-NC\lK@T7?ex|>r>o#unGa1"G\G ֣(,??Q7}WsXȣG#G >]a1"r>pn(,??Q7}WsXȣG#G >]a1"r>pn(,??Q7}WsXȣG#G >]a1"r>pn(,??Q7}WsXȣG#G >]a1"r>pn(,??Q7}WsXȣG#G >]a1"r>pn(,??Q7}}(,??QG۫,??QXȣo#ug^^XȤ6c(}ßߍڠtyb E|SKSq7ky 9`<==+ϋi%6Hr+[Yׁ#JY>3 ׀k4' u|kVZ?\\h_jcx|Ʊ7P?ZEw8K@x8 51ԭ>#~>!bNm$60 w p7_9GvYGc-0Frs_)WPN+m3ڌUd_ 6Ձ6r:}Ky_,:m8Bd83:=ӑHqz`|I- cfCn\8:$x'zgMw[a;-O\>yo4ECoq4,l87rU2nx=S~VzG~iZ455bbV61={GmK[ k;KV]:)w\n4_uyy, %β(lblB*Z:nњLsu%cR?3ە0$iU >V4u6O"V w^kI>Ӡ; ;!=7hڵyV ipHBJ'ɌԌGqk_nuht+E죴`'ܶ["o2&_C=4Q f@<2wj5M4ꖰS#rn^H/v˦-\mbrd[s/3zӎW@߈|fM橨k6,Z9C f9o$Ӓm\އxOVmSKw"}ŝbybNjk:ݳm!m!\䓴zR76뛻=3TԤ*xgJ_VOc3Ds>>&si}AgdW?ջzB89 ?߉$3O:a VA ʡfij)=#ҾPTսy uݧjMaEd,Fr~f< 6F*Kt犟*[_ kmI{zW2ϸ$R~@&iV|)mͣ[*7Q4yLnN6M3V# rbmx$"ۊJ]ҮyÖԣT˩ixX y4tfqYnh0WډF[q# ax+㛛&{{?}MT.0'`^"bhB^w÷naq6y (F 0@#Ÿ AF\63,P[<>-?/&ZE\𾛣vYh0"'Q1Fiߴ殷rxJ{M}Yq7_=Dž4weJ䵴o3ϖ=cZ|&ޞ/ ( !ծ76cE,DSCO?m|rS\[7lZm"Sgi#VeVZ59;ʂcO&:~}tb15R4[1Qֽ.gm9 vHc1̻YsV&7 .m8ɼ6ՕFUnR didRĭGU=.I>MnܐYSF vxEzrg:P^Mb[JD ;YTH=uT"QHaEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPџW K49[jg$WYGSS.IR5'h==q_9Ox=XٗZ-N:VFំܲChZHcSHPp+kt쎚?~Peُ]RM_gXe'"o|GƟmbV@#RD^ǁǽ}3on_JV wn+:߀?B$o.t塖Ί~wR3[[dXT/<1ZH'ŗm${`(X猿>猿5\%25ql.ąbRU=Iq;;y'gpUQ@$+}!4}!5"9':I2AEIoa\?/?Ə/?ƼsoQGޣ?_ƋcA2ChA2Ck?4m?uh\?/?Ə/?ƼsoQGޣ?_ƋcA2ChA2Ck?4m?uh\?/?Ə/?Ƽ_,|!k+cDڎFR2}-ī+MZKPC.k)5Yr[,8(\/?Ə/?ƼMg`m=wm c;dacvŖ\&YO6~'EqIF j,=xx7os猿>猿Gz,=xx7os猿>猿G'ޣ?_ƋcA2ChA2Ck?4m?uh\?/?Ə/?ƼsoQY o;{x=̰Aq_#z gڕsݾ猿>猿ew|aioyڳyYoV,"jZixK% ue{pz6 vvA2ChA2Ck?'-0jY,u1p7`h?_h?_xޣ?_ƏG kE׷sLr&PqvP5~,</F"]]RIa"L|r6ZPHdгP3zוUԜynSr, Ρ't웾/X=s^>cK{wfHis}!' m]?_]?C3YoO,l-]rMa]@ T33%w)~^|E=C7Xok?jKm*ck:@ǹUr'p;7|vou‰%I5e[Ouy}NtgV9I mV%\CÏGe VohOi4O%̪QpX(?xF0Xg8oxQxzֵ66RyZg4O rT|4$q1_5+?CgږKiy*Op")6*@v,} xYc`̀O҄i ֊;_>o]6I~Ӧ%29b1Sb{O8.!$Ag[EPEPEP|AgmuBakIeF1;FOrk*YYt]Wַ׿aUI.t褎1J~Q" 7J|V=A"%uԮdb!Q #2)ko-.<wi%K,1γΑ(y$UXy1pR_'cΩmXi~$# [lC)`1[ּx[uG{7^ ::ܖpii_?šH߂$쀃>!knVyݥI@A(U/cֶO[+[ ti7G*449޹-Lj[AOn-/ZD17 s;i'`4h+)ws:l)PCo.+c\ͫZGŤ%͙.[F!(NAěv~F_֧+|:CMk_zĮ. &}3Y@HAڸQKI_k}[L{캤LF4jxSd[QR,|;p:êO祲|̪hMr֛{+TJ+Ƽ-ZQ8#8mJ5ϽrHmrqGo ke;[y]\qNºgGѶC U3Iֵ 9Gj'IB:׻Wc׵g'a-#; `F9R'zjqW>Q%ٹ^MXYG߶F>.r gbmSk]!K(dIw(xķ'Zլ5t{I㴍YVib@UbiHU{gd~ _2]GPP]bDN5@ӥȓm|׀yy Z\Lڏu u8H]m4`9(m?1z<{45FI/um'10β2QPTָ8Fմç&cuFn'h^_S^sT/|1ty֢Qj H#;q8z;ֻgl%6ӵ{[{5ɸIJ#D[;qTޭ]BROӹ{h ԴÇTMӮi$"s=F+w}BPLH̋w,[*# `bYE{$9tfg(m$X\qҹ7:=EɪaEi ,E  oKg` GQ^AqQm-;0M4lXYfQoAkou_ JӮud/ +O$M"4Aa_\ +A=C~ԯZ^mgĐ슭) GwJą`HҎw:$:g9qhﵽ{Q-.,ab Cn ^MEP Z=:=8-dKnk)QȭAb=uǿ}>@O}R-6D2< aе=j_s1X,b)hdxݏJCY6=&Wl5yXC$ViB@Kv |Nছ?\p1;bu?z? κ=Xí:jCndfDaeAKmv%;ÞEx!YᎧ%qYeۮtW?0Q8@$c}27Ƣ+)p-|-GN׍^%K8^;)`<'N5M{F#ZXn.R9_jTl$shh]cL_i:Un4r_j)r-kX | .iLC ⺨>8K'dBHtUJ_~1\ tҶ_7iV=RmS ]?xgɞ %V)R w6~?|WW..-ygsӚ6~v; +O-:k-2kZޓkPInNVhTHV6)*\烓^Wfً"y$G5.(:Ѭ,as#,oF:'<; 5? K旎,d- `Q -m9q_-g\۽o/t" ѓ]s!xJdJ)~5Z-4kz@_ 61,9ݻT!=s>4mks6ӆȞ  b}dpʲ y|%mQ5k㤶_G56A p,J#b>lv\iыOHϙ3 t]2yjSm4kz\Ui"-I{xrV0dV$gk>x=[VVY͛얲M$DJ|:meNy `Qo x:4s{`uX\8Aʮ䞕ᏄAe+iz4֜IiMa#*[<+Y0>UH_N%72#l!٘#WY_V\dCwv@I7:͜ `^jGx:WDM֠vYLdl$V|֮xnW>d֒DX䳍'87nO&uΧMV.%y5#nQij[#2C"~I6($Si_(okEjڵoZ+g$smj"Ž䟺>3k|AoX'5{[o+ZK(3'L<Ih_KGiW2 mmmthhX, V׈|9m#ݪj=ՠxL >`Ķ6$!_xwUм/_<0*ppX>VmK(4xD/2(Ffb1zV>_]-u=Fk}&_jwܴWV#BI#@ ,6qtq{YzԓPXԨ\I 84;WZhտ=3\<>R,>Oqa<P񿗀qN+J𿂴7DҢMrdVdcsו¢|;y5֥Gy< la sF@ۜ׶idӭm<cT2AUt/Ȟ?~%wJ[xrGDZG cqgpA95'!h9{g[r7V;$ Ã^s~w72UŚ1]AvKJg f|Oh_-"8mhFiI(ahiG[\oK ?+-JS~=vH/$crk}tB.Ӗ̛0!p%q=q+K>2, yvY!1n2!/m3y[b\l<ج[SZ,smZ;IrSřvd1R]i"=Ś t0 8ZZG`;úm]vb9mUfg(V0FrYg;kMZC 啞yQC|D[yp9jOv5fx_J:fo>%~R0cS0*Fwި5>ŷVe#=v $Rn2SOYiqVW6o4%NL',3zם5K,+j:%ԋዩRx'H\LeU޸B~lt8lx ޷O qYtBVȏ[K{U:t moQI,X,3Ky@x\ KeC;x_i}Ιr#3"6y³M!t]HOfڸ@P.6~j7OhV?ZJ;+oPk|M|WuHXYKw"ܟ8K%~S9o m!|?qa577@i,)uby#f8gGzeO=4#r~>a\boH҇?v@[+`Ŧ8ȖMF6~gps(迮՜I|-n|Uj֨^U=>H 3y`\u'nuvmG`<-8'5 9eiKX =2 t|F^eOm'ÅP~Uk IψֺU{T o%x4 ģtW|(|=iζt(..RT49PD%r H'K_}̫|0Z{m峘!$0=|x`Z}V;a}bDr`82>u`{ L,-%U[ՖTr>^5P$jZд{[qh./M#FtY|$ەgrӿ_[ʶ>$uk;-45KLsia]Wfls}JM#@f]]KhclqW_ Ie%֗$.ai18),p =uA<hauuenzy K!yُNB9 x#Ut{ѴJLɹ]D%w0;H&mw૿_sO|٣ز|nWSes%z-Ʊq%݃^M(CE ` gfT4 iwq0TȊs'v01No>yrizuܩke M%^\[r kO>'<agir `h/"Eb620;za=֟}AiXa4|VF…eR:פ|8=n2%ܫbK19$䚭ŪMs[I}祖Pts-so߁^6Ma}CNўԭDϗuf'#S7\|M+.a54I-!81 v>+Q;>, ]g|G:?Wœ̻ml乐8 0Xc/\Cc0gY4k^k \YZ^Hq= X:d#H'{68=K]G%<\GjyVV 6@j#.:]F^OKdvvGB?t[V#c-Zz,z%Qj#7 y`HCᯈu?2.o RSv g9|clZhLCcHE 02oZgx];^dđ-!,(|PgU*.:mmcBOEO'beVpʤ*z6ஷwڬ )Me$Sz:#ׄKo 4Zuͪ_Z_JsMEo⏇}a]^Eu5a#-)JblbWU\>k}Y԰4W$[!\@22G9oAF%݄&wشLig* 7'w/_,^4֦ʲ-mpVixI4LQgrpW9o v:j3u_}}<9j3_iY.ۘ/%y] *0c+?az? B[GT ݞ84gK] T}2KHe[fh8ʹZᯆZt+ej'Q}CPQIŁ s?O:I$ѯat?K~'|,`awxG_D*+[6{GhWb2̭qY4~ᩯ4+ t/>; 1j-{;J1% T 2|3~  xa֯nfOf cC⍭@ZX<5O{=-b+pP4T“;/3iͿL|ݻg^_M[mF_&K[el#T7~2prk hl6By~~ٝ{9B  KNg(@mm;m4Os'ڌ!MnG*3)['s7bYI' ]$ s=q?'e4  J^Cu(%eޟv<R ЃG1}*bT5|cYƼa-YcZ~ L#VHrpt,ZO:wgk{s1~k}8QB=iIK|5odXTVhUx?/NJ0 {|?:?C]w_h:}wo:ATpq[_k|%"^"C;gy?ȷId&Wg;' E1m/A<+jHE4WFpkӕ ,=&ybj{񋱻xSf鼟Ulady? 2[1d=IG{"kH4]_?~l?O*?ýBM3\Oipcu8 5*s75j7 ?#Rµ:O#N}9kol8}[GMG赯8rty=EBw+)v;`|VhirLO79=Ƨso6>pt>+fg|;z>5(0-# c?)# 7mfS`G9Ѭ翊XhYﴜȁ[R"[Ry'܌zaGJUrJ2էmAE;?C^xpϼg~ǿX۾XTZ6s.Sk4ͭ[ʟesF1g#+mVt9Imv\-kjwZWůgKssK*GEj,OLf+M9%kfhP?#g`׍X.kir `.#bR\OHG`&sx(P5fCuώ~2 WOVh[2)gjoK<)hzk]}men?_.js:.&wvmY@#=} L4;1ۢt#>^KvmBp@yz C U _qX۲Y\vڻ>5ӣX'$gl }3Þ'ڵ6Qɵ"+Os_zĸ^MZ0 kb"mʼnԚjˈ8-קŭvq(ա/J 3|nˋ)7ؐͅg8 H&O˻Hi/~oMr4;ĭYw:dWKkBqzEsN3;_Zk|Uǰzv}lͦm,@#>`|j^*ıⱾ.HzzK"E8j N:uT%l,;z}ĿռKɲ{Qon#ѥƖ5 wP~No+iJ)G2WRPĊ2o=|_1ğO&?& zm׀Mml?/ 5MPmi9~ %UsOAi``zTFGY}W ?J?}? rt3>?1#j|GwsS"/71lj$_M4x??&[V 5qeB_4CN[kBj%oR `-0~QqZ,6܌"󿽊|eQ1/]gsT/PB)A-t+]o&Ef…a(A'軣^;:)W`2}&z-ťK=R*A!~^#vMsG-#!*Hھ&𽅭:{M=6ZJ269n9b,9kl6 p֢eqh{ f?,}JxՃnKb#-¿|[ld3e{6;KeRTՐ0K{w:}Iu| V0?O8/NIZ>>|vF5tTGUFsauG^p˝'ǚnuk9mM~jٟVY73d}/U-BzO?cYoǂ̓r9+߁? z)K]iOk;c3W#4Yu]}1}cS*!hz!>xsSӵM.խ2Fzן ܛ;ҍ&6)_.~?_.o^.V|3ǁxi/4ne."񯑿moZ&W>"*:Hϒ6>{]UNjQdtm$ԵK؏ ?ehk1}+?#+[|$󔏽z/J񎻨j ka \ȇ.H ?~x^OO;}0T[=8SIEjv UҰtP90hly}Z!GMG赯hVqlZ8]%S~p>+31<Zgm.ζ0ՆJבIԕ?z$*J 2H <>Kxq$AE\ҭҦR$HozVq,zYq cǭs:w||BJ d!{>w*u8@>m*}KxCFvS8_SK hE.`|z~U完Z .D6Tz'z<k_xG(?Y,Tʧ^ol%wt"oup}Hši-3[*#ONOy mk-.Gi q=~-J{Noy!o o"eϫ6?.C*mcq֧z딿Gh2|qGLWu_Q3iYiYǓ3珥hOh! p:umKqs9Ikf1wI- Q5̷fD[Y,NJ?(+|Whz>I1'=yVm Nt~n&hr cYI w 6 |^ ksԼii ;.ъoL'KQk}4dYp$r=RKesneg8!OGtse?f/7?yO#۬1ǒ|O7Me&5rO1IlDRq/ʤz naA{h$W Z0\Sr=Kְ.x1zRd7 ;űelTdx}2y>Z6aW6QA񯢿o_h ಐ·B^:H<3Fua,adkC8 &OýjOG18o/=bHKa'vu@lo c㿭y}[LV4[鬭!g8{tɮ72o,A¯ڔo9*N88,/vrKtI绩G ˁMtNd8+9J+#wB[+{KmA1\ Fq|J|-.99BxVx?BLR+:k.\c;\NJ 'i/]]]ܝC 1F01ȯ g ?u5Q/Ѯ؆ a^!{ߥy$x7X ߒ>q^!MQ|{߈fӮ&!F;{.?l{_AuyX A< RӤfQddS†WIY~b#Ӂ &c[%mxTH-y7ÿ4h3?D^k0Oֿ/1 xUѮ,#>ڟ>0zݲ-ݽCl}k0y1 FzKJRnw0h>KE|mX7Ўd_tTysV3☭ #uU*+HcSsg5=u`~gs =YVAE?l>%?,Hx5K쫤]" NFz ʴ)Wn(?__ gWMYj".wdSz;>/Bnm. Tp R]ydicalv? SC|8ֲYFM4@5Fwd;)wg76wng +M8g eAlcNts0䕜bEH\,iȯTGz: zW??WtPKqbG|eAxǚv}=7MhX.~n::b%q^K7PK#Mis9Ǚ?fm8:#  s9K֕y\{:6'ї!r3|@t[FYtX!<0ﴃ\Q:xR\Mi.mO0>ENy&q t"Z?BD$ 6`w,\2=]oO$_2_[ͫAT HL(N}ҵhԫZNwD6]GlOj[:s]0RVp YS>?ūWR._NF#Dnc@?EzJ̈gQe[w2j/Tޭ #@2k^[jz}ƒE3tbx'#8?Ξ+5OQv}y?-{H]|^ ];o\ס/98FqV{OB5kIVIgjNi?cV檒Mc"D~r8\iy'j޽ sI53]"`uܧ>24Ӭ49aGmIorq\Wߍ$335+Nݦpa0Oz*0nHNSJ,I]XVr\DFRA^7ž4ѾXh))մi%B ) G9s$kzw*&X.\.H qٽx; IԮvM2Pg%MsilMlzo < oK,.4 PWy={Ś]=ͅf<1LpF\޺ Gh7躛,1NNs^!|31!X,6|"#(fh@gQK4M.>b^_ū˿ jKc;-$o9 Uv8z6Uǝ[Mo)06yݟN+c> Լ;<# QzRS1>'x³xϐm˷C_L,H5ï XQLz.JA\\;ong#_OmJq2?ׇU/u_+=`Y( iw(las 5/mlZf[>1o-V=mI*Ou.Z<_-76>,5MR:ir#iy01AdD<ᯉa%4bK¤k @$=^t=A.E{4nާ3GӐO5:wχz6Lִ ym-Jpsں+_xS^-}h)2kTc1Ϸ$5&%{ધ)I| @dX5CVvost-&;R1d~?SGHIw(ŗ'pqMDΫ^Xͯާ {-Ȍ9Wf2͎1Т 57IMnzφxz"Xyb<-&XhWR]2SMM4K4F'h*  m ڻ_5okW~%mMDqŨK[}`Pw1d'=juvyo=GVM0S"d%AUq]d>W sjPrm+{3ltKR>jp;DD '`֭"S|Ll@'9+ׯ5S|EgޝܰYىk"Q׭Y>i=BOk9r8r\/97kS5ө/Y[ZI-юGtrq~$^k[+ WDF{(prķ  u> w_[8}o ~P* r95ZxŶ6%̡I`%@A {uW>"SJ\wkҊZsWi0j٭>x@ s޹8[w7{ 1@ 3ɭ/o亰Ԣ |p2xl@%:k kCAqZ|yrz}+ȡ 'k mj=j80Hܤvd-Y5RT3 'WV1.K{d`UÖ9Qz(zؿo|7#9k+V&[\I&j3ϖ,韵^-e?wM${ǁ/>͡ʒq'$⺋mEOW~g;@L yi%gHq(M5Gj+wUU-|c3jύ#H G<Fv~ &'fI\á?5OZ\\+A("Pvy{Wѿ>*kW7cKszfZf*6 ׿5 T]nQOqL)! y_~".MH$ :0 .kw&}r7 ~3Ҽ:֝u#vAv_S+0]޾e x/Sғ@ԛV ]H1 9#%vv [SvvJ+Mϼ|*Kim:Xghdհ^!|5^_:q yI! NT>~IҮ4vQҘy!ukIxW%FxDZ8n4'֢)Iث=S8grn9vZwt{m>i# lך_PJc_)?$dctLpiǗG(wRO |iZÑo,҉<( zu ]6<{>FtCB$w=3@Ţ9Yr3>_m4s+(1N F%3)ҸE}y+m f@ilMF:ҽul8 eG'?wv*ŧˋxaQYPmc+9N=%}QaG/|>C$[0>^ {W{b,,YZ c ӓ^5otsp]g ׀xǚ犥]sU%L\`ڽvv/e ,g?Z_zMeh"Q^]R1ukT2Y q׺^ 0g|)Լ<={xf;Eʲ[\mLkWp2695bQGϛh6m*7`ھM?Mwy k;Syk3n29Wj_X.,ZA466>̅ 3J#!ۮx^T˭?Z~9zJܸDc}O B&yV*:{u=?C^/A8uuC_++<ӄө^##R|P|fnO6HidȎM,bkSZnZNc`;Gk?źό~xKVfUYˑ\Nx^\侗c{F~#MHKn 2HVF |җ&WXq^/Aз<%Q%5dGMڦԡ 51B||Z(ۯTɨs2\N|~}|݆}w 7M_7?lNְ k-dxsV%Guф!Cuo>"Q{/_GuMjxCY"ۉbCP/CAtf? ng)#v8lvC~hZmz^7]vBH]/i4d!]ٛz^VGFMThvZl:^tFYnlEҜƻ9!jNj.c\ѡ'-m(猕$' ~$ <'jG;Lu>o㹽^{T?mq8M>W9O{ԭmVDudP2s־>np2+9oxJՑW>:fP;dyl%}zξy}|Ss/RFcW?¹hS\l4]azQ|7Ƭg&HG ?_aM&;KB[ǝ$53Y~+98ظ^2ş%ْI(%pcy.9ߵ~w~ӳr噲rN{x&`mM`S5?I}^Wm~pcIie2ࣱd> ;z汫kKi,v)dV#{/o?o{?YkZCKe}ky ag.\+AN隼LLXBVIrXx_Ե=^K#m=je6s/ KGJH-rB0FG==ʭdI",Y=fA$%EͦiPM3Ix%3M)3_>ex? i:dipĭs5YI O9q:XK>Xo"^s6E8?>E lmo;~ss3E <~S|VYWp&Rs!=J_ Go;;0h%d}YI>a߂m7vvA?}px\Z^? *惱>}ω|MG?L{s)Qmip'i$ZׂW>yiV? t:7SF^>x4"ѕqi\MNK}Mc3̽K Z98OR=+53c z93_ HUcHGu#?VrJ[_*~J~~j wk) q^k2k~O Lnxb6G9E)P{5̇fυ=9Q;N2y>Ք)F5G83=*v\-녏 _=Ҽ_7uI$hG'x5&>hBG50jWj ?e?D.,Vؐ asτ$Zri=[l:~5 ?M }?ӡJ&r>3~4rOR+[ t5P~Tާh?пh_ƻQCdbv[k; Yl,|w޶ 'gU(,yӵ~ ϣ?пkh+h/d~^߳_ŽolkYC QNУaç$g>~(YkV7ezfR?пh_Ƴ[۾iҔ.X?^֖SMg H3^kx=Jg$œsUh_ƏA4/X.XzQuJדw.7e?EпaoQ*Ye0<=? *_ `xr_Q?R)vf !.m4f3|y ĶQPMo$IЕ#r̤3^uu'- QllףORќ0'EצwiT@.q4{[~ޙ#ˡ4-DūGqFԷF־=cV $v%*pie5'vdzt?a躯?{e YY5GoũG:nfʤmb! >z筼1╜hګ࢛7M @?=O}}#;i˭ZAgu+>m%m1?LXb xZUyUx~]1붽gTG;`j _?ks"}W5+o($9KI 58ށ\ƸqXlݷާ*8'sI({[}ϡs ?5_HX^]s:܎_k|%"^"0~!GǛtWMhmѕ=Wl▧ZȾƾ~Xsc$ 1}بW\Wg|@B."u\hmxQ=kkOJ+ D]5W~߆5ω|;X>˧\s[ɱ#ڿ5?e[ F5co\DY9#;~}gzxh$y!TquloD$~F.~2Gc*ռEs.пwQHzot,g*HΕlc >8捝Јmg9cryѻLzDd9c P*״";4sa8]ٷ{RMr} ivN1КQ"-3\滪[jiaeq$%dI[ v?{U+ Ayte/X%P:}v{h".A'ޮOvn&i{³{$4˛t(]FHϥK}c+PR[ tkg{rcVgOWln-. 3.ە`Ϲ.'mwU4ӎp_Zvw]$p"OJXuě :[K[TevFYI?Z{Cnmg fmed#8ݜ^n+.bW˅t ǥGx:]CBXycjIi#`0 OJ4x{Ķ&帵6C _꤂9X:|Y6[`n^Ao'([?Z-J;\5.dh2Smtح|Csv{u16sZ"Qai$J3[\w\*! sֆSZum+$jp`&̭m\v}v1Xd$}:ѥCo[ԣY-[6y“ZtGIb}K"#"; Ս±ilXDƥǩgn$,eVe\qGjO@JtGp6w:wimo2 'we[m"Lf_51f8 ϯho$HcB=;mty4U?4ަo`^i4\,]`~i>.y5}jh󨸬\̪֗~mm <<ʧqعQU?6622y\,\(*GE22y\,\(*GE22y\,\(*GE22y\,\(*GE4Q抧{..0U?6ͧpsATĦͥpRmޗ'\'f-aX?KsvBH'W洹qO-SpP:E Z. ((@QEj|aD?ڰK _>&5Kaayj|`Ԫ'mjV,(ފEx2Q%F|Ϧ1<=xu>dk'w_ ^5+SZ76k:@N E=^0Wb^˵vMקЭXc@Q^^'.՝YSP\nJWo$w+A ȩ׬0קSEM'cE}i_b"ZP>LœgߌglrWrdey>VBgBC$d΂L7Z[Ro0QmF>d_i uͯ>+?$q( ,`$ r}9fYj~+ԣӼ3ē  9zԛP674Ef :u_?][-{Xulzҥ%&Tbwg¯=F71̑Aq3yQFLa vghKω Ѽy^8 uZUyn>v^kȼ#1{&M귏vL̸1J"60J~:Vak*Ii'ĬHN$15kW4ŧxƯ㫟WW mH kKce]ٿs_eoxv׳kԼPjOw}l?F6ZtW+Wg~i> eɶ9%p:XgID~)x ^,AD$ٱ/%jnz_?/Vz|]ɾ0l,뱃(~]͖O6qEM ˻B@%mNMlsLPv֗l叕rm,2;\=2Q^\ZY !er!-Yq9+CnlǶw& oc1T8cϵu3~ϳ[xSOX;9.I_@t+>KHv<;yJc2Y=}+l_xW)k3`[aـm+m i멷_=KmJN$Cpms 4`!2Gt| >k DW3=Tx#{fc\io'EpXS\wgyNd ־𥵧|/n8m/u?T:o YX$$RcͪOmb?+{iI40;ʘ_N~ש$/1uYd "{Oo\xk4[O{a^7q3ϨG4{yqjvG*.RkX|ke\XXM^<iy .O=+ʼ {:ow+k j3MD4%{/o28G>k|y-%duD0ʲ\^ {-{Oh区ۯIq0Xăairn=ȥ'dWG %džM yVwO5#;;Wvo^gcv@_5ŶP\Z/cRz>Se:kٿ,?)YyJG|o_mIs4 :}$8$M4zĖkgxc|Gt`;;N\Dxf-oY=NiӑIu#FUHu`@PX#JCKoR[YYNO> KV~_'ϵ(=WUu>C_ Ev׈Y,qO_坽k?ҿ7~xYѿh6+u[Zޭt-bܶv>Đ@y?l?֭%'ψ^8#IVᛍwTsaQǚNVo6^'!{A'Ɋ+"oxġ64y(W gi Vƥ.Ğ%} t=+5: #UEٳ\j0iKr.Z_ĻrI<5)$m}[sºĚGlDw61rK˼o8EG*.'NmmHrbr7*{. O&Ɏ-3ұ~kl)kosmjmrC)v`rr>OY1JwLkʉmYGhkyhv/'l;"ֻWe&^]"HgQ @Xo0c}Qx[I ~WrΣyaBEnꟲνCxݼcZޮo`#yHu.Um5)_~'xKOiqٴXPgjm~Rɵ[[ЮzdRn3żz{?4^Zi:\\}"]4^Jr܍r0x[ #GԤ!W.,ż3,1$e)(|WO!.A֦mad)mɸKo=+}૯񖝥x+kL2Gexon(]-X onI+c#T\Hxڍ'Gx l'cԅ.WVg1X|㑎 \Gyg2Iblھ\[BX?Vk[|hʢ\Zz汫Dl={j#*Q/nQTnZgNtUwoFsakVp_-Es`RT] xA_k5|R{Mv)Fw ݌JMg'm%4*ee\2WCH"V} ٓ#0>F)E2sчG_ֿ]O5Wqr4&Y, ,@'cqN--\B\3g9fkG73?3i_yk'N޷hڔ/ܗᯈ#Z::׺̉㙑6.=|SV@7O/XfPH2!6kvO6{ >Xu4~:]Y Q`&@`' =OI֢y->7Ih23eRGyȭU~F2=Fxn N-Mm3FŶ L9zz#T ғO&[Y-!4X1b d9b@}tC$WZ>}jFK]ImÙg _=0*H/Nj=]7t,zirjql"L.lHlsU>.EZ͟mlķHR_7(|jzft;ź5miCoB6;]SY)*s=u=)k"]A>z\/|>~T$R$g$Xqe<2g=΁8|[--+búp6Ų+<3c2-;)YUP8${=xSׂnu^YuKYg(PОH0yuV־fOj7mCZWAϵw]6w:%Օw$/!bTA5/mJN2]I7ik$l0Llzgzq_[{㏆&2Qv:;!`;gnq]S܈샽y?]ah i\y 2naS,GLdz^5>&ծs5:^&!9vR,RFA)ˮ CzE$k{BѴLJCGƦ:&umAPqnɍM^OK_x⶟{#IwvV\$s4,cU '-+J?|-ÓX?ȭncX̄G%y tSEyguOtD*ѭ-l#hnHm̼ez-93^W<gky5ԞeSYnbS$ʸs_@ok PSӭmnkx?d rĚK⿓~hc3|Aeyh}mImM*-RQE[ifQۇq%]yP^s_APк̍8:Oa>)Eo=֡a% fhwGFD%cƾĶ9kwyΟmuPZ&^<gO&76z]Vf8,fgMd3WH$Hde!dӎ\R{ ny3Z5}SMJD[m6!"C,v '^sW[ֵ_\Yu=/g6r\^m>(x/A>]*;QK^Ws@tU+>jd o_N+9$e @Pis-Q@Q@Q@Q@<)x@ѵN(d%x(C eS^Go\#jomިp mHxPz}&J`>|7Q[K[+,lRS*̛/ p}k**T"bF)k3^ºޯ="ܽ"PDdnQTߊ^DŽ_jWwVZrX W#=FHCBۺgk&CХW6r60gkؾ|#ϨOt$^G%E 9x XΔqseRъX ]JҊg+^2NL^oj zͼ6Z&x49l[D6sKϺj׀uK^T6:|%C Ʀ[ nz _Ƈozci]BKy`ƌgꋁ0jޥ!SsS᷹``mF<Ӛ>waO dۈ`y$cSeQ#OS\|a~Z_lk[fNHXj{_gn=:g+j) }N,x69Pv J=Pվ9ixkXU/k!eRrHU{- ݞ]>:>0;Qv@)(FWy2gu[|F,,8ncYaRV5'U\;Fkj7In"R;VyդԞh͓b2l(d iv3j(³{;:qnjH!;v5VdGWQ>jmQ x$|eOzeyGxK$^oijF|bss8Z#cE 0:S,(2c>%Q+5M RPżjEa=ȼuq7;[n5;tʍETc+<)_ƣ]xK{-fs[W0ʑҾz=?x?ǖECG6&=ctKNHU=rO-Lr握{,PY$c#fvRrH==9=?x|{}q-ǟG}^Nk;%KS Bw8DBx!^U{׶^:~v^k%MKHbPj[-ߠ=gQEKJti>XHX? d|> ɼ$LN[2$txB.lm^+wx- 4QN:¯ Mi7:~$%#uuQE<6`;vLv7P rHh")M֡stdb;*|/^#!hzo5ѵ"5;rTXI"na޽g>4+v/Bd=d`>g>ciײ%( Qυ_JNS }.y^En[WaJݠ WhGҳeju>%Q@:E:AZ^QEQE$b_}!KFLGj͏ݏzAxcDյ)i\*w[ˏ?c@XneCr͌޼Z\/j;/%v%8?#/LѾ ؉,&*K9';q]e~{Swk776vKG1l_n|.}wWʠӳy6S)kb:5(G~OF̳_kxJo^({_Z7]kL"wm>c*N+> x-4uΘ4IԢ>["YB:zfvq C@?]jyr.>6)\8XmUQhO Dk2­rMsO'C_,/KMTT[Hф_)I&?.:Zg(^׷zCMYz\X͜Rkf<ᙹ'{ּMr /&[ii=Ůng +>4]iW>džG:Q4r0 ,3'n먒}Z bm6wa"ͮ~3x=%uGVH3Jbe @&==Eo|4wZ\Zf\Z^ܕKo;E0P ar=&_>0K {gYm4Ǚ.YKulH^2|3e&o3[Ae-Ne`GRyR~!dōGĿ[B[oEpbne$el`$NJKOy<3 4Yſz" dt_Q_u_DEcYou8txI. *-WF$ڷ"KkcGqw>>N𗇵c?.Cm-g#pG%FzsYW~ P44i5En-!n T.Ckm|Cmw+mzH kl2?9KK~¾.+}vgWqp@mb ½2z [PwIEi~c>LE"7\3W'ZA3Y- 2`^イ0oKŵ׼hZ9Q N9M{97maN.!MmwK@rnؒ.p;h [}2(EBLQV~G5oltOWx8ωxb}KO,|?4W֦J…ܼs3^o|n-dկӏbK Aqǔ#N@d)iL,Cx^Om?W>`*8YM; k^ۥ$ 8THKk];6ñxCI'6|vq*`mcО:B4QE<Ś4-594Fi'O".vJ +j>+I#ñAyR&&w_5z>1HL+Mt"ZH92qX_ )/jzcD gt43o N >RO_\JQN(:S5 X5K_C.0'" p~LW _ejMk /%a-t8Dy uՅrj:]qng6Mr0T-gᖳmcS{1q)+F HARo@_~x?4n`[UQlُqN>j$lHn6"E||=/+öZ&ch#1f9$$IZΟ^Yq5WM=\V3U'BZZ]x9Nխt46*V&+4l8+~<]?S@,ZĊ]Z,FO"x'}~> :EIj֥A7"`g$yA<[b:YJً;K3IkݛtQE>~#R^[6F sטpWQnxx[].=R;Y ߓ whv.Չcmyg|e{ይOң/k4c.Imt[\{WJgMiu;I巽$+FWsČ,:#m\09ZTLjCP}+1D~v,B "'ݫW\ ޭ]Bh^ ි+y ,$g`7Yx[:_c3Z̛Y6ʠ zFEk^Iwm7޿}gujJmD<ƛfFOq]T%v\[eAq/huQ\Wשbڋ{d0E,,9F WR2Fs^ŏ V5 n/ K)Ya ̍EWLRA<~_J@QEQEP3c5?OzI3 |]93{gᧄصgѽYTn2C6o.^EPmJ "?geF )]?=Z}@xgV^U[pu /.e- 6VYɩ J|9PL&gʝLn+JNy.>X۹l!dlD|1gWUC|72oF-ơdvaflH%7|%Z,ct FOy1eRGnZ<6ZZH[m8\2):LkǢ˻/^ϭ\j6z r,by "*!p$sb?.Ӵ}3Juo/J[ `h9|e`r2W=+n:xX:Umf}6-e-{ %v!dv1$qY/Tj7V^mGegs[N,47H]HTٵ.jm-[!g?, ⴶ~$_-ίdG;ɹFX:¼cw f~wicj7iBi$D @#t^~ςkNci6-m%;JX8C9_+kd&.u+t3iiR]< \ /.G,XN> 6#ew "xʾO`9뿋+šMݤ,۾hZm8ڻc (y=tBo#=ޑhZ}ޓqeFO)I|X@3?cM_^$5ݶ59#|[wU$ȑxZj}zG n㌳;c@Yϰ֍eVȸ#G<dgFOqI5󮅯_i;/.~ѧ\xrSE((icZmO[m@ՀelAo2o5X{KNW[kBbr1Sɮ}Ż^{w>?`lgHi"FmsrHЫzwFᦹ֢H *:b=7Ob"o6 =W'S²j~u-2HOK+L"CB ^@#jۻBZMt [ZM-GrLqYUcp^~4 -,Ft/ ${"Qm_-?}槪/=0 Zcqm[:)$tzJwWBboMvmk-g]D8 Pgm"=cX5yI4ksYݝ&x1!uYd O҆sYGSsk6-JlDyJMLwWyU|Oi.x \f˩}X1{"L)\ } }G4zܷ]dQ/_kv,Pi 2$  gU{$M Zv]K}p1y(O@;׉'X/|k5k[Fhrh6Ƭ$j%l.ː3>7WsxfkǚMEjM흻jZVsqn`T6.xRv!FL?~_GKs14!gfoƺ><] Qu]U \ڽeՊR;[+<2.'8}BZG]{QL]|1s:}ֺEĐgeM[*{F>2k4ņII-i"w.2I GXyV{-#D`n4$;nbnp8}#0\icwtC6n#(#)n/t:`||/sI{Y\fl/goH4lP=rzW~TԭtJ&i[LvȒHbѐ2#B ho{-^lOEDUM,)^G"RtJ[%&W)SF?烙+r.8e9i$dC%U,τݛo\sKcGN)Ɯ Z(;ol쥶I]丅P_Zxj: ]?Tc\$=?שBړ=?FU׈5gcڼFqi@ nDbUJU38:z~ܯP˧jLyu='bһO Ӏ5F&tEmV'u=g~%^Kjg~ygq C_&Sc#ndn_ByM^ȌUIӊpgG;#%x煤'8v6]_캈@#/ |UukK{9`B ~VzïOxuHy7Paq+ѩ7V38g1/!<g?ۇR-嵅*m$B.9Q]JY%(XӌbvӜ%fnEx'J?oJE5F1jr֒'4˝6ݭvERCIBEydDWZ6Əo^k4[QvU/ c{px'6^g}mxVKbw1B+&ھ_gn>eӼYuiswsm wygeE_3$ᘅU'"վ5ꚿZYvkgo0pp7aB ^Il5<mi>$~Go Fy߉|H~ ֳ.> xRbXN[iF&23H$0# A;UiO\k: I17( d!c OAx3VM.WşM:#Lamq֥mU~|"+;K׊`OtnJI,D]R+2l:S 2*縻Ғ +TA'9Opg>m/ms;M2BȬ#К髞݅/Ȧh-~铀=iHj%kríD𽭥$ L-/DǦ}멯!ӧxg]Dko}ۭhBBFqs^x}QKkk{P-,I,eQ4r)@y@ڼ|C&M3EҤln_8"±8Y\^/4g:0-f`ܤRT" \7ӡ&ڠ @Fym.iLWTm }xmdE%fɄ''Q+Zo-*QA%wL\ (h?[gcpUiH`;F桧i/"+JyU G'j]i6UF]z+Oƚnv݅HNv|kG^^'nl-"9ou]Z{[uA/`1Avє,( mVsgko;nY\Ȯa"~'Cjzq{&ks[7R;(B(WSti!6Yjk !Iv6q:nzm~ũRXO0I ~bgq\k۪w<\V.^Cy.m18M,bb0I99Gu=W«i-#;dRH*OCp+}Αo:]M8UlnijP)lpb~˶weݭ4ڻ^iIjַLQf%:x)#QiDJYDzW!B(!wlQʅ!9' =y8NOO6tBa7kAjO쿴?uM;-98'h ɧ<$Lr_J?wS@EiN޹IsW9u@(^>YԓH&u<R#w}B5ox}n|<`Ey?)xZ/x;;d q[u=N"1>qP>g?{Of)^-:TqIqC:kiO.I$yIZk3}ߌLTedI dbV|m ~߽'7ެ]fM^Q?z}&Ui/C堂IfS~+kb@ǘ~˶|ݦMN52~gcU5S`"|\J\:F ;|UqT-9QrG\+FJi!ܯ1%O:۝?졆 s;}9-*BAm7Ne,ЩdinވS~8;z״~ZQ>;if.q^Ě1h?r2G8 d)ʔ䗽fvaUirZ\zĺQ_=Fq&vj=&LK)--.of$*)+-ui/EmiWW[$'99gp-,](ZZj$s~<,N8Atuj{ԗ|M6FMw 2B,nj"ɜ5=+-F U5:#,ZO2PwH2NuTBw?mgi`v j3ɱaiR7F@5D&1yc&n6FT|Q߉t~( ݖqHѤpH$g#oZeX|Qq=h [ƚ|K>,kd(fc|-wMvF-,moM ѝ ‡$ 8) WW2A|<5Zl5HiYCjgbNOݴ!^+33jvEZɊSDgsHd;Vs:/M=GJӵ=uu5t$ $rFFsj]k}O4߯YRHӒ]&{Kq42XLq3NFX95[~ҵY-d{X_e x>kl;6et4D`(R 9;rO m/  %KmۥrL۝dZt(7{=mᴖTkxs qEzq |eqm>iw&bYBFF @`URдYkH!#JlûwኲkFt+)k{8 dY;Ik~īP$?xN1$ڛ뛝AX~sl(y[6;%Cj+&rғJL]7>兤֔EreIPhTAW  ^&=ͨZۣX@8xkټC^zċ -+v/DfIq"߃4\]agw%8B3x!iYc5.N2I5ᶿ].R&ii;Ě\.P2 zkFyjy7q43r0Rז~&o=wVb@Lw0|r:*;]u(>x|EjM VV:}pK&V>%rƹOkK}Oa,i$5I,]"2xQҺ%sZ6+y z8~lVcy[ ZE.{ZXKyuP1z4CSeKΑzJ` Qu=e>S[ͩq>r?\I{@zujhz~hV`FYA}kF5ANm]已KxV̪^+R*W{׺p`WK(U$Үxč/zIeٵEm?L?^|BҬ Nm qډ "\pqqu:TSXq]b! {dԭV4(Qhu-4c<Sv7ec3mĎX 6A(?ha&/i2iQ&eUr$hF9G.Փ||_o&nxߋWy IxxÑip\ePM4]c0)%gMEP3)>+1zrq6ܚ%P2x$g}k*U JlHnJfX1=!#ugDΗ]_Gm}?!k\Ok[ NW8fFd@1 dT`79E8-+Ĕ4,$jXŨ^]Y(iZj+hفHA"䶥Ap08%-*>KUW.sꮉ*$ݞڂooӞ8R$dpnj׉EѨ`V#RD\m{=&Gy `IV^Kӵn&degX5r!IHdQy_^}j-2Fxr`i&DTǿrUi+\oD]F#ZZZ%dy5iƎДY!^oP~WwH6(NA=hMg$IycR}?PLCoK#z*Iy|zّWU&Hn+#RzqNk3eqcieuާtuW[`ѝ@?kPC)i O"nhƉ:ۉfY3ȹhqޯhl5Y.gO [ڼ^XAA= > ~NiǍ tn0kq]ߌQi-s]v ֯riߡU.$!|͊s@S>خO? m7j]+J1f$6[+X)Y-9mQ9^ J1jQ,PyYXq;jGW|2\DِL6rGe^:;*KC_=r)etG|qVJso4vVVf_KBkV\HEܕ+|s~O xKvP7zlM\`C&;?/{ sE7RYo&&6)U8!iEkmu%?gދ(l- P>e2{U >,'[;˸Xbi-sI5\"^*ۢtW*M} ^}FմO:ŃLdԡYegHZRFd נ-7Mx"Y]r*,̬!F wm{yߊ x1t>Mo5W[l,`>-==qIn+k¯h c^#4 ;E+[5vy\v}cjj| Z&uM y[p'IP7 N#ki^Aʾ'ѭ|oMݒk L-TMpS6<㑑_UW^|g}Qކlm1d ؙ}~]c2zԾ+3<=oOn&i8"ų4R*Ĉʒw8}<,$Ji!ټ00zsqWIM:TSO;h-K,/*J+ `,8n3OGt;+.눖C+C 6}+K?EcojMKwkǕ9鞕 KX~xi;ZƞV0FX}( pq5U:A#Fc%W8{ 3~-k:UM[@L$UYMtzwN5vKWh6d[Id*X_ hiYAz -$_np3mV٫!J^UΛqj#xH,褪` +kK?gѯ|ƋoZ9"i1CPF }]k65[649} j ƶ܋MSWkya1Py3c%ݥp!fUPyc^E։.l мkm@ӯf!О9'ӲTB ̢ GxfwvrqqoiUC RPTJ_x׆-ƊvŦy&v\|<V8Ek9pX*pc! 1z5[V%4<toEyZxHR"BywrX_:Hn/hnd?7#q|\о%KHtK? N _?ՌdҬ0y$bhS ?_('xPKM]Zޙg PK"~TfP69߳5Ūi^"Ӵ4K+Ⱦ˪i x^Ré5Ꮛk?/}Z[k%ab#m|]-7=>*44 ^Ϯjvv+ 97+FF^Nӕ޾kiollm'%`GU 8n >yK 5Цն[BNe{ ^]iCEǛ>PsvS[?堞]z(Uu/|)\=Y' k+qW4c8P} {O\|>eo,6|@>}j8j,B2GJ18zu1[z5jZHT*˝8,S0gSw[jh V+ _ 9ɮGBxnKaiV)2{ei?Ps\kFT gk? 5i}2*3=+.Wsg6涂#+)b䌩 ~?tcO ֯> ~Q|i|?mc6Iu\2K6c>OZW/NMsb*ԥD9OhX JiK3;t[ QMVS]ݙ=H9+N]ӡi46b6;wnh^+Stۦni"Ð< (xC~ Xk#̖&򬁊lxQZ~P (l}rrw!B?imkQuhtV+8^mj$ҵKEQ0ٲ7.߄GH׊#O!H?6:J7 Oq\Xèq?ڰjuumIEHHgv$qL566:m,,w}=ėRev,Pw1 ⼒DJ:_g_*3jkK_>b/ o-O,3 䎦޾ΟzG~9xu lo.l,Yēo>bg6n9&g׌xFFhYjP2im>ٽc8 c {>y[!~-eҢl4r}LREdZߋS_ӵM&6v|FI3yyl ~^%m-"֭{)m/NsuLUWq dy8_|C5MCȻn;>;(V9L`(|K?S##&AoڥU-ge.7 ~Xmp&3<͟Ρ#xWi`&arckZBg`?¼M0| ڕ}-pa jna i~u2 :([k: nG!5꨻W$c&KOtWzViuYK+wIcWLh5.dRگ~l4K޸*izBc$H| 򌞘^E+5:ljM+O xM]5IT 尔H/`t^'g|n?.fRЮm:DN~̠Sk˗ɏeOs^.,CwS#sq6MF5UNnKrI> t.ZhT{X'+wm-4g\6:ޡ$vvN7-[*b_ub`+,2k>+|=m:OhO+u%UyT#j;`J{I-YNs`c{WʺD~uZ_i:W%Om]#찟#spFj,o/軴xkPotm R-K6;Ekex@J4s݂{ O5fj [(?oo7R\? Ѐ1zDlC4hMjOu)ZN7r(8F|0"}sZ񮕤k__e@նyZ^TMkvo)UG |;G<>X]i,wUa"'v7R.ײ>v߄km=m"7Xr:V7)&s>5AX$aHۖcH %uGy\$ZZYALacaKwcܭb4R I9to~ԃMO:ơvr%ܘO#']k ڥG X 7m 9}3m}WIu[-sO7ZuY\ȖC#|[tͮǟ|S8tsOcu6[9Y#gj<ӑUu?>"GX^,AekMe30xrv\d^_@:;ιMm+JTixȟ@Gw) :.qauj:Ɯ{66Atf _g~x{EAO(-ÝwY֥P+UNkX!6f 'v8 (?Q-kSQmJ\(4DݗY {W|M? [MULW}BW0ڰi#C55;kmAKU4j7v1a#F10@O/n䃃KH@ 3to~ ]{ĖYOikiaM+0 kGM$}Q"|=Wu1|(zn~Ei~lOV--u L|w|'KP\DʮTg= Ċ[9c&FIۅ<b[Ꮝ<1kvAuYuA$IˡCvI0C9kit{m^}DYD"VIFڀ8\ k?sZ]qvA$R@Q`oP7M{h_o4leVQ,[٘#c 8S֚7^Dw:( ~ߺƋw! p\}k5Ծxx"00pk/(> Pne>dr=xUOfަfQ2@YlKY'Hڷ q9=[.2_ڦv3wE >IU:tF4FR9\cviOF>H9TTLHU'E'M%"O2"y5/X j|#?Q饿/(G2rz\v*}k|Dhblֿ0z7|8>(B +Nd-) !=1_7|^eu RgWʐ7zWË*-샬. j;Tbݹ+oR~ծCk=D͌7R*XgO+ܼ;SR}-c/oA-yֵ쟤DWHS_@6f8TMezcs?7(=6c 9QCu1_ĭzM'XuE>FF0UF~ҩiq֮.Kv?F5%V~lJ3Ҽ!?⯈I!ћNԬⵆ,r|84xNđ,u;}-xQ5 %GRH:͏QWFO\%/Oje@ y5W?l53kVvŦk1BoʜA d012 槯WCxRN9]HdhRTpTQ\WO]|=lz=Hs<aojHΰEўà][%ik} r#"|ṛS\N;Z/B-?\s-% ڥ=+Y> ѕqqmwB!%UuT&SoMkwzGO ˶h] k|SQb=KMKMJG!a$*W|gks^2z[MB=<Ǫ"+qc!H; wN$tt}OJ5j13=bBXp8S+V'e5KD?- Zmnb yj.ڼDo%.Cƨg$Q`p8֏o.jdcp085B}-tQE>>{Pn| [(_1~όk/#E;` υ_JM+5$7<ψvj:(& z\_EjtRXHw!L<U:=ß7'F}*}V6>'̣ΈqA?o~)# _NO.b| C@H>x5P8FF(=}Om'k~_\ͧ$1o/G!q^ElFRL)@ij(Nu jbƽ7t+־c4H$W ,/Od*I_XWx-Lj55M6t P9Y}{ds_)euHmw3{+?sᵍ1Yj>l..o#=uȄvBƟRT<Ͷm_O9s+<$nV9{^6]f50tD&C\ev~7|+C`*J^^oVZ7ޢⱛuMY#X'&mOPJӖv @$^T}*CV|(dtf썬fpnϺ#o*dܹz$J솭Z{\֮fZqw2(RĀBJֳjgS5n C!Y$ͅlz>+kgk*b}L\xF=f7K˹ #(e;\jR_lI&]/z(&!} \kՍׇ<+/f\yeBʼn @95o%[}GK4Wdp-xfH$lvxV#ֻjٷ%[^07MHsri j-dDʖ6~(MYw,I64g$d.g4+J֢ٳfg4Ox7:ϪxN4j?^HJ i CzAR}0Hx~N "{F]"W <g9ek_64pxׂIu 8.u(gXwfSgkkK? e凇4H}!좵hu}e cj[dXRap2pAޥikkkj%Dz䙊+H2nB_R K91I8Wsp=y<<j^<$aEXX`݊t_CO|յ_wQzH"tLymym<קW+^G;K 6{Ah91ʥYHF uUlrOyj}))gi-#H̆OXyPHH+>xÞ,/w8>m+XK}jRzȾǦx;X5("b:P@by'o~!Y~>|%6q,M۷n?MQ}VI V.-w ": < MgĞ0lz^t ZkFi6ǤA]9g!aU$R#8=r(`x'Ֆu+h˫\Aż|o IJ]+S-?O}WWGoGvЛvƠY6ؾ|E <"YX:W;$6RH|o&?]6RwMcjRmق+mޠ:}-t=*Z+Uеm>8V#.J:9>gε|l MtrFIeI-V@IAfbKxjψz0xR}ry[C67v{eEPvapH$4o-|ci}o,~dSx=w&Eҵ}.T}B8cra:[\~vG0 xr {ghm/X8QAt׶jOS#ΞۮT8ݑ+q6,A_kC\eEIHV=jzȠfLZʺj㭏?#fᴜZ  6*@U|;"T֥ѵfݤI3hiPz<3kVۭf9vkkTp]HVڭ˺'%cտͨ~UḆwiܩʀ:Wz׈*_{/mO}6<:XRM_) AaԾ8F:Y ]P&1)0"`UHk7]q{q Xˢ,lYr0 X<'Tqiǁ/5k7I-:㞂~)O&}t6\xX8X~ȞZI|T+KO6m_$v>u{]'J̶VG["g&]Șp9c= zjvxji#P_K%yϐLdlq״[7zk-giWMTZǦ\o 0La۰g GS9u/Es-G%@) j^- [^it(Zյ9ͼ*J$VIݤ0dfυO 8}> yv v||o2bX_j"qjabXJFnD,)A`ko W/ 7זK3Wlj%Z}yχ=>;[ج/~q^^Kf+岀&YЫ0n8zeyǧW_ڭ{c{69+ICT;u-j43ƙze7VYGXp ;IӚDQx/ S~oyul,c p60!]xzFB2*vGeWuAJ֐Z{dm2V<F`2HR|fS׈;xfZMO&|ȩp=3jc4=4:2Lv,*OR?[ZƋIS?n<@`WQ`mD9&yBiR yf_m5+?G𞝵S)c<}g 7=O_9|@|%@4G6gkA gL_7y<Âw`.*o"3< K|M}gZ5$cj=_)oQ>br[1|-'X=oIy-l#*̪NJ8Uc^kյ-N{]SJ7dG 5)&pkT~VD+ shWoۻFxZiKјA N3}ggoksjm4O`\D%\G~ }w &JhA* +AsUuEmtx.Xa18G*=Ÿx_I NZimb bGvզyq% ,H۷h\^.SԶ2&"j_4+$)8'^5]rڭƫ-N?iڬM:V#>I!Iv'A`wmfݼǣx3~#]ҴU`VIx0 b.܂JESL-.[NZW 1Ѫzh%PuOhM4/en,뇊,ca)2Aֿ {hah6z6%Kvb$`r@'~в)5=3WRlf 9IO ET_\lKQEWY nqRo= ʼnwxuҮ! ź’nXآLdWMj.v<Q≼)u\6IlHwȡa9ԑt5B/xMνiz坬REV}2rٰ [ u߭ƙj*eM, p^iƫ36rnYfݪv܅+A[$]y?kK[[Rivwmq!STc#Gֽbsō'Ŷ>'o&ۻK#%#kM:H#u>񞩭iMe<#@2xcDJnNi3aɯ7ſX~0Լc>K[(D>Y /_%r]}h9Z9ծ/nmM53fH̾35$dCVw?ٿfc>Yt*t5[PS[fJ<ޔLf.-T]_Y:476˧h<Ĥ5>w#0+ʾyuySTƢ:pԮs>IdV=M^*%+^Ղ(QbP񁶹y=iGӀ j?v[F@+B/J *ɒ;ՕJuQ@Rih ( (:YJPM4VʛОTrz-eY"R"ġVr@{ mM9ZXԝ$ ѹĎAZ_4" H-C}y?qRXRֵc5q|e޿~%^OIq vFFT~Ϳ|X<_}]>F᧛da{k$XJzBq];]2O> qtQE.^VnnQYEf^tjM޿4ZԴ\Zc* m~|Q$ Z[Y$l8 Ǡ<.&ݴVӦkX䑼 NkORusEI}rr#@Uڇh<+on#S×7:5o>2KC*IE @9v'|(,O WFΎ` =i~Z;? ;χVMa$;V;)- o @=AK+~.lԮeu$2E9]qS^ڎЧ6x7жᄸք -No3vp\(9k%IxcL{6Qqyh &!^I Km|六">t h^ڤF{I-d-?𒯂= w6ci鶱YYYWjf,ך|(n:𝆩8KFP=Ϭ[!uaX>6Ү|/.bڎkdg09JުZ6]Զ\$dlenMLˍ[+h uFk4$ȖS'ך&sO=B0"ncʼnvgDԴ)ugnhۙE(~\orW5xZO.eȤ̫毸gQ\ šYi-ҧ+o -4Y9}+SB?ys 1y{߅ G)DKuFA'rkew_lmmV;ۏ-!lכ|L>+{}ufFz$k~6h "zڿuMNڐy$t{}Wᴿ""_MH8V"ˍQn:zZkG`Q]3Llo lG" 1~85]=X χV^.a[[\K"6 M1 $vȯ_=+ W?)JBXmZ9 BْN:)u/Ǟ ֍E Y]oltIĩ)~]sd 1MZ'#8X:Whڧiwv^3mnQZ}ʌ~HQzօ{De cO >eEʥK($c+|j6Xt5/Z>*=⑅,0@{|2'Ҿto~-OE}ᶵO>`BFٍ䃐3jW?QgA}qx~)˶S6B (KϙbEDP@ҾzoacCP\ڥamcICFnGs_A@$1bJ#?'q{u}4l2YnӼp۵AҰcxjLmK'L,27O~0=bRiF6a^\JPs*w٦Ygֶ"Xh?oQK{t"Ѵ[حi8ڻ1IjP3ɵCŻe[zZVY7>wW+1]x5մkMA/H.΍r+zyZ}nYiw:H!yb) #5i/_p2|9]+GԱsyߓZSdO1sp oj:N BI$ڼQ޲2,bMj Ez xσZ߅{^_٭ kz/xeb,G^!u`*i:}ogmM4*"Yպ#fOx Z S yu; VTRI-2ӭf{fDGAw?׵oVF%evK!wM8Rv`:nmjQE"0fb4>:<|6 (U8U(Ʀ*O@(QEQEQEQEtנ6 1qCqjtN텟W̆-:V|z:HE?z NrqbTci94f`i:jmfZٞv qw].\)񏈼=MN촲qZ8Xio0opu|{oŋyonDwo#wnȍpss_*}1+*tRϥC6 nߕ rqrx+ԶѝkĖOyu=ܤK}Nr:UCw ]JO 1[E5ʍBDEm+؛q T9,ti/s*Js*h%zu|I7TKo-e ,)$О*죽r񗅵Iþ[C[>qgr4iPo‹ˤ$WQ]}tb/$ٟc/k #)~&V4u1NᎦϫ(a$eğ|%o }o * S98e.vSڍA5đy&w;Q|Ռ$&hno2.3Q@BQ^ wc?^cQ wн{(ߨҿ]]_. +]OiwMjoqڻ)Tn#'G}zBf)\emF@ApMTW̗wtb.KR~UK g Kb3ڙ~ޞY4-*kI*Ge I'}c+承n [jSͥ]ZtU26L5zi!Us9h᯴tIz}E| GqsP/ZOojۅ."Ff`͵Y&dRx>mm kao-`xVRyJ #5vѮh?|RE U8=2hZ+{K wл{(ߨ]~ǩW(.Tmv6(?n _5 ]?hlvlwz5۫^Q-wxU~cFX]xpZ')$N4]MDaʺVR:+K5 x/=kXPkPpӿg3~?Ux?CzGeRష td۴3ow>"hL|E:}MiznwggH \Dx!oSJCjo ,Soa3bej)!@ǁGtƪQDAbH'"mǫ5ki~6^xogÚI-} JݏG3](]WK?. ?moxn]j-<$ Gw=QTz:}?oMqc:Vu-BhE2M۹!7>lc7~SomAa9TE,@M]?`]Xy4'f/?|uo|I'B«;,˕j,[ 2{]*x8#|+<[Stn]bpːo#iÂ:t#ۚ\(>DH=3RL֕X MbddO+ɑ]Q@j{M'x{>%&-rmۛbbV ]ZXXn dB`znuTUK]V ( T0 +ۨ"nQ~tQEQEQEwݩ! ˽ܧh@'}3\ϽX<6F= }@`A~3*_>Ë^x_beeuCEUծYʒ#8__>]ӵ NKHA(_㯪4?:6"$v1 fbcFZu<ܿ :-ס]u? bMktF8;6cv#ulx>.[i FK2}\gv8ͦho~4QnF?߁ִ|KcCvuɬi^0OXʟgxO=g cr:R|xK C"άW7Чc*S c#VWצu@g}5+CkYmō3}bI'[`d _|. t2IΦ.#_WtN|f8 |y#~WJU~saΡqc," yC2pvkյMgWtĚտ'tY~־~ԒOڡvZ-}>_gmF äi4.[9-gr>C0n+g>Ml&oܘUU$W[-I Ȟ@R\m [ >]YկuƷG\=Ύ;cG9NZ-{;}ॳrzSES,N[ؤo E52ne;~P5!y:mol͍YcGiS/ANKZEu4èFQfT aHa'~BEOj?s|Qsj˭j7W7k76&g%0*"1yM fx%81!\4M7Eb&eӼZʤg,2=:|#_WMS듙u+Z2$c(@ 5+oOj,On,.ngy̶0:*.ߟ;#C/:燼j5+bHIy~e]6T,8=ix ET}FS2]N+ͳtX&|]II\|JsLU632\\ZqlUܬb& @P2-+cxXMڴHy$ьĀ=W1?[կViRPUbH/*rhg]lj/,to?K s}s%05y!Yn Tޜdb]k@::\J3f7d:c.w;qgNss]jHQKk9..-VC_AiZ4Zew^@D&C7Β0+O9ݻ8Y5~6]7XXṚx XQv0C^qF?[;hoޝmM_tׂBcrA2}Mk|HE^`t%$4A05tًZKuڵ #G. 0O71oLukv7~⏶v+>.s|'.Ўk){  um)uM+˝J]=idnyz+KW}Vu9-yQ6#*Wv8kގszƣsiooui VZKo*ƁASjMwQfFԸVe&rdہ&wn14_z&㤶M톬,DrĮB!fc'kuC7kR[k_% <6x%/aw>1_TE-v֩k&,|PFdW7o:2EᅼF͸ʓBU ( "/ O$^}ww`i47`Odps^ Wg.tKs{lI!Wa9jm<-)t/R}|O`KuV1{C)8uoW+&UY4#:}'yK3 Ƨ p fxqPҴwt2xbU V)7*i't]}?f_xRdu &8'o-d1cxwv>A^nq?z5ZA5g[[!!LĎ='KS'm.,m]3kr-KҔ*U WsV5++[)/tmBI\мDv7Ϝ`G[1UXgmH\ 1$TBW`!o'=NkMN&Yg(z+zlg$kcs }HEp1"~p_^K߉|3i/緷>2XtJſX1cڸg.#-5}QLd-АTH jIꥭ|A$zj{sV²$۷rcllX%owߛ)hfo ,Bq'#l|-<ɢǨh#Ki۫ZXQʒ@Q$֟ŸKA }WT,gyuʄ !eNrAqEYZ&:w- mNI_%{|'y֠SMvɡ,H$_dx+Cf| 7*xt-.m.62*V^'1I 1%뚦=έy%[K`S~RHԹR h?|Q Ms4WV:%cP- ĮA.pi-,DmQkSVRx>o$6"QB\bq+'?hw.sSZ4 yU&j"d+fj~-uCRơc-lYNy5oxwNk MUlf!FynW9h^/s[~'(K_>u~ӊkzWyVM&ʽ"yTq^ 6yvOyrL{EE!Q@#ј5㿴u|!tӵym0 ßc^7 ]iaz'ŏ]x3Ķ:t{\Gsմ2Q.XקzJ6c[mu(/K$Yo@HkBD畷-nJM~ZtoA_rFq־|y!TKvq,O$0sN+0c:iE-V[6gA_k͛šEΫn6 g~7tzWo]%Zo5"LpӃYG͸Ͼ+yjIZl>BLy#wU]$ׯ7+g܎xm,v7v-i4Mfh} => jmM Cx!pmDŽhUV^|^y>~ҾU1蚫?cZ0X.Ngvm~v9Wk?=r Y-nEaTU#x-(mNmHXN1>^ ZePC)MYnz#v_s3#T5G-`"DoEg?<2ֶ͢iԖ&I9%/<]+Xi?ٴW_Qao1+,x2c-^5akImg#򑢉Tg;F<⮵15\t;ez8]LDZna95j:mƩ\dJpI9*'xEc`s)C NPRw<]Hͥm x<ka=^ 4GB(u[Y["Uذ%(ǵrl@I6Pin牮0 ԑTmjF֞ :tv26_KgӇJ>/}컗|'fޘ1j_3MixWa>lyfr3a[޺>͋|&ki o[C` ABm}Wfּ7s`leX)g-_ی{W/&vNu5[vGF#`t}0ث?n3Zvt"; b+/^Av`XbڃR.>S:n?"‘Td.8<թp/zOtndoK FmBlQYsjw<;3H.:,VqOz'˸[VT=\:z]%حv,:3z>#'rߵ/mcL=;1_6IIW$BTyzr.&jNOk۱qԔcd}aB-ҭQ[;T`eD_Q_m3A\_/[+fF XU,,1rmphpm-:ʘM:5,ޯ>߲>鐥{- qEʈBa z|*֋e;oi.#Y|k;Q4ǏWnWvC~ꖛ]kmck%t%ɣtI^%FxJ/ Xè,6JXM:U/v=.is[s"ZiRD}3_Mm%R#}]?8W3ڕ_e}_-~RTg;FҥױhqM¨MrJHmɌ/5|!^bPbaNRMTkK1)$HbUD\*:U/E4oO?o5oMk42V k6=pW}k0➶'a ݗ=n?|?/!E$~5cN炴{ExW$CmfѠ^^y A%.\l׫I&eV#M:G 5[ .EvWtgea^*4lt{M8U QE6[& }>^3k%hlÀ>Շx6RH6%opG#͵_D5.T!F);ˏ2Epρ9lS,8$ddSt5rGB''!pWs:Wz]W¶-<穆 tB= QEyXQEk!?'2:Y(ß |P|e7kݬ'sǾǾ==Ė6nduׄ؏IQIft߅Q|򛌖\㮦,a̟g R֑A62n( 3־hzUMh" $G̳ 죦:g"׮.evuZGrT ^qz8kUvZ+=<-)S߭T'ƈ&5XäF|oC_⧋!%:5Y7cmdI)#(rWjk=>尌tܤqޖC ʿ#U|qT,wQ)4 c? W0 ,UUnn a5vݍЃU4 w)af/ 0݆AT෸G5Ek}#z{y9,q_f?|Kދx0Pۓ"f|mo)15xGυ5)9 ;y_ hTFyPtgG) |M~^7ӏ{\F0RhDr. u$ӯ!nΎw*xǴ}zR5_:?7a {Lh<g8{vi@-44(%SksҚm<kx[Z6zΟ>6ոgD>r?*U+yd =XusPwE3mUBUBU}k04#zY|9g/COu˯+5Gw[^(n?Jļ=> lzoF:U2ĀW5\؞KU cuF di! *v,J, q.t>R>;ajвf 7zg2[sw})sr=W fi 0\r@pqJC+[[,;T#p9f8J^bq ms5T,1Z0~o֮y>7{rx_ZӖdV13|nFy-MO&nD[T#]W*8\W|Uso7K0y弦6b?21_YoS.dY'm|n+(ʥ[c̤ ug]xz{?ߨc浜qaNNzLדlw ou$c?}E:<u{!oxt.yix^dݛI<׷Oβ_\|H'~lpfX^ta+`MTI--Ex{*Oe?Xv؞y>=־&cz,F$][:Ut*+jN.~0%5| t|+a1\]yxSPb Og#~> F~F>F[oJ״/*Fu<9bvd"f]_ĵ&\DkI!㧭0[?JY] v[]2s,Ŏk=k&PB#W\n:'5'x"E6P2*W)-MG*}k(75)VL3w?d0iqd#XpcL~f;ёtWQExQEz]Ru?w:minI%sU`X'/%{U.4KM՜1%Ŝu.p\c85݃ ~FjO?86ֿ\m ?5A {]^wG 1ƾ3I*]jW7qtFS.9qP5ʑ{ޫrmV9n9ȳA9"BT!hǯ1,[5 F( FuqPƮ Qաh:z^=l38\IzI ʾ3JX&:5I_`=HB}}{4jo.̹ImJՈV o9WzN| n:~k?úßzϋvhO_2y ͅ챙yL[oN;`V)Ugғ 1Jt/RsS[^,.Fr×wyAG9uށm+3«66;i!¢gZj\HD2ld v1!kMQ8f GAZX$ʇ+t9z4)3ZLʊmr^w~_cβA׀zzkZj٣y-#nr3jA I(*TZepjtFSWVhqDCjvMg{2FMbu70=8?r? !>X+Ce_,WG,Ty&FT 'c717]Рe%N8?e A_^.<5UxJsm3FCPx#t'Md/rϯq׭zdD:;~zi$- >q1 ͑Lf#σ*]JK2‰PΊǮu~fm[sS^;%e0j$>XZ L|#ÍKu+\[E!+C5Ci?a Oq\;P3!|)&$Y bןxVlgr[JCQ{7~#߈I@S乕' إfOR/0?z nчFkYگl; m"_ xwJ,FF{2i$"m?_mz_A&e󢕊i:xOeNjVPOXyO{]L]\%E:sxgX<~.K4uk]nE1 +Uf}#K Ҿg߾x4pcy'O4qk}B2P $W}:m^/qE~ n.~B^?>vmt {~5_3j>v鈌\I ( .K0@5 xSUmé0iA|aqjeQga_oƖWpxr7Gko*=SMl}~'xeU<}H>-.5=R:2JzЂ2zWx_C޵&&m۫Xa#^ u0#5y\B(2QsUaǯZb+5jw;Q6dz+Nu~85)DARƼ Pcq {/985߈7 |TZA-TWSJ9Ӗ:zSwc9,*.Yv=zבxGuо-Λm" VU~Or3b[MCNݭ/H4<}}M>U+cҡñc$栝}ǡ\ږ {k{ VOݭĶ"x빈#R{j~wo$$!A^:+ JV-સk\yr@y9$ުEmsNV=BU,CF 5eoeZo-\忉xb3u)uRN5=SKԥ!*8a2+C+~kCt ZI#4LExȬztɦ\`(%4Z^;ӣO4g vS$LOjLfC1Z76/mcMem72! @'Qh_~%9N~*Qcۛ$sNk|+iM6ouygr H_,]{hu#=:LrƳc:*xvyͪQ F$,>G3ue8.̥c:MBѭƓe C,DsUﴄcO+@|eĽMRi]W@X%,/xyl&FO4dmU$s*@d`U+U?haQ붞 O_#&]g&+f6vT_\̞XMD|8s&~~,AWן$I <M8򮧡6+>3 xc?|Ij6y;[+)Pvģi| ǣG!&ff.|z @u}d!Q\Ht.%~Zx 1+Zzcn"E E@oʖf9sCbekT34`xm#lJ*Ѽ cunIJa=W55KrQ\ 7_6͞-E$>U9TsTVg ti eXwz]شA gKi&r4yO)-qҡ XNr[?i$gv`A= *O \1΋n۸ܱ?Jۓz.𫎡H*_ĀyEM lb?fn3$JׅNFɴ kzYaXL) CtؾyXrm4?>VKXc3q)?5U} t&hV#<Ƹ9g#}z3Yk<-b,-˸Yy'pL'ły>^@5YږBP{Xke) pug\^}Xj2l㴏/u39bÁ߭23Qx͖U{%#+#@Fjvm\+GRGP9Sz6+xO|k2+FLyœssֽ\VnX5yhNcJJ|OM1xbqYqG§0BA \ q11_GRϽ袊~QE ?:Ѽ9?_]:v]#hntӌ!CLHZ2>j>)mJ;}.㽍b>#quxt=ȺYͼFB*NӒzW-I7m؆gW pjp23帔4,J߳}m.`%fjђ8qZmg}#[ OK0jm~bjI~͞5\i!D{$qb3xv={k(I攈 7np'{M2MCP.-K/뱦$ܠAێ)hQh2Cv<uJ9ԦM s{4cS3'<S|y GӵOաRRkȄ/(KGj~!±F/6nec ho 5mOLU_I"(f@HE+ۓ~*aUrkߚ/S>C-x&2/";g(+kO j NsqpܡY2Ar#+¯#~ o5Rp,cWe Lg7‡@G.gZ;&Q#Bp\TCO8T:OiAm*j1,sM$r}ko GQ 댂21zf5߇ 1_r4ۘ;N1ߵcW ?នmhqݥ[0dTjd޵5}U%NM;73LI&p.W1ҭxVԾ+][1ps)铞I?_;}sD΃VF0l;kǼGx>xt i; >H99?6 TR/bqRVsv>|W4h+kb'}ygp}s<*,n' T_^: K65]Jz%[E +Hwͮ[#XGߘP\Gmu1[GJnLJJ29&K#N/gd&Мw_:W646KKL 7a$q޺#PvCJ׹[f0\<;lWL$k;xY"$BY@FkkA&1@$ :1H4V5a)bN\JJQ7q\Z(Š(K+|W7ጚ|u_ۋK*<[:6ӷkK_n|7L~ ݽݺ˾t ף׊V~.I^5xS:@Tg KOV,񻷩* 5Y\y:|2wncHm|5cѬ` l&B3ߡةJN5m{?u8(|F1ó F~8G3VY'&xU1Tݑ,%fG˰x[R+4\qKuy+0MGZe*Xe $J벑ўxA"س u0xG귷0ڽƊM;^f#Lck*kcχI?eO :3O#!Uiaϯ|ZapB[ЊcP 'ھ_W`0:ۆgR*x?V7z s>VZϯ3&m ;ỬSa ;o7.{Ƹq?]zχ4+ :~jIme[\$3Ǡ30SB1Fz,5Jn}O<7pf7 0c?qqeԆGr*?+~#ao3jax]i 9$4 9Ę?!ʢݬp90~Vwuj VyĠo⸝'ndR!Uѽ&mj/<!;U*Qn,sr5$r\[= L`urfख* L ۆ3W[l<ij1 Yc}>#T,w** ;z5a3W\2K8ݹ2ȑrWwT8~oxf&jd/#7"Yi#Ӛ+[70Ta ㊧<GPb&Eh~x;gV?ʣ6M2ZJ瀙lԢ.m8Uf;U*aȋ#Udg.2|x LNR$<,W2%wrߩYxnn^I³ZbdH(~T먬Pcqv'W'67~89~o-lKz%xU>9z/D|iS]xK-6n$y:_y߂>[* d 3U '2;SL Ӿ艧Gso?loV$c }U8U=P4j"e+.=rZ|˙Vs+o:,WE|7}c)_9a|O!WW2@W+:U$ou }`kD-~W@?x8-Ԕܚ(ǖ>[Eğ[=8_* g%Hϡ+Kz]q-FiAn:/-c8,WW~'O[,eq"v?SZF~>i^5fLJ$A8= 薧f b]>yW@Z1< A- 23ж qψ?t)JMjKnqkPrGڂ7 ӭ9 B;{w /fy_V+ ˩)'*]eAW|x~]BX劬T4$$85.<7O^_tדd2`)mDΙ=s&XǣJ/7o|:u%"*c_24c9x–1$*Z>q5sxI6(_Fd6 /ڮq#5 @pt%(;HR0=঺xo^S:H 2v#;B^NyǰKO.[.ך#vÐxm<7n{6k xsǜNgQ1ec05*j3].!cV(w`~yS$Klr8keQ~虞2ULe1 <>Mo95- J A Ҷt8ƕC=ډYtT ]Kgh$mŽ;\R+9EJm>' =!ۃrE1XwAw-Nĺ޵-Ν<F_&@H褀⽯Q-7Y&ʮ7|͞U)W$b}!ESQEd^kz/ ͦ_R?B^U氂59ʜqџ^ z/gXE/`~wDe@ ]]M9+nA#~ۋUŵ6]l>:um ?5Ǝr--(N(ZuQ@Q@Q@f[)&u:62NY eI8 ~xu\'e?g ?ÿ 8pL?_sᯅO+(6jMDŸ'1kOW@6Wa4Ÿ'/z|7i5P<g_~`}4?_?!-wtP#a 7=<1+8 ՋÚU zmpE@?+FwI =w!No>g4z3+3.Uإ6ZVZu66qg˂0$(dJԕ%=~eQ].(Ԟy!r9+xvb@F> $֭Kӧ5U?`xM2\ϧZ;$ udi{Ƴ11fV;sUW0մoQM,WLȱLN J%E6)fKhvlӊ;.yvaB&g 9NU 6wE_KfN|LܵѷNd{VY5|[ܷ^0{R7! 6=@=^}+Ykdkr *F脭3Ѿ_ye E1K"@8orp7qwOr/lvuPz^?xmص$á@; w~# ?Tđ;ZB-2OGUu  +TQ!\~iqDN }5|-L|G sUpkgp?~ @)c>lQE!;⋔4=?TH+-ݲHʄ$t8SO6/&Z*\b@q[/t_MnV+-"%@kQBV K")WϵxYw>5y-PU+r{?|U>#MY2̤gEy]H!Mڻz(mx?DoM 㵽a'JY鶶HUTgVn_ٖ*vG VQ$a;F3X+w^#4v;f- j68Wp8OJޢ-y蜺|;9%`A[7FVʷPtphBt~k\l"_4 pť!=3+Il:t_Mi|1ϴex{LӾo$~b`Q} N8{ f bS@;Q s|Y[|ccP~U(zUN]-!G 0 ?Z`)0?(1o"N[E)z@pޣާ).څ#DcvsJ ĘHrh((HtI`ƥُ@5:/QKFKWSif4:;sJ͡钰9L]]7pTC_Gc:3ߩy"QN[۶^.Vk(^1/<.K0ʭ0Z]jR[\Z2X\j: mh`'%<4Z!w~nS?.+XhbԺ*<Ո4kh}o]<+0ahrSK@ :өhhhZZ( ( ( ( ( ( ( ( ( ('IK|e|MZ$.!6⧡ϱYޭ%u .6|<3v ۆzz3|\v5 )d|rrGcҽ7oYlmcy[_CwwC- ŽKHd,ϥ|ZX|ki[m^.T,#9RzͲkڧM 巹ԠM1<#} hT^FInYBGc`'GZ/tN}$k @nD9$`m!b |?.D򢎆0 U+='Ef[nC̤H'YGK|hmKlD#27v&qu?g0!60;׾Vn$I#20PN9 l-i (((((((((((((((((fotoxx-18.01.1/images/batch-add-geotags.jpg0000644000175000017500000006007613222767271017100 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 203 374 0 C     C   Q" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&KdUU,G$iVSyo"g(kRA"|ć#zW^7 }EΞRe4gւͷ^;0R}>ShԔm>-u{2>~lK{$d[t\), g5?_o~VKv`)޽n[Jjbfy8q-Gy+ X*V8e]NSFN3C*m`B=4XbupqI#88`W6w7'N"3$Fcr0Kcq\G_ˠZ%L յT!kepP#2\`c5sͱj*-$G:H# yxǃG0X E|.D`Jb_ej/seIb6{0Dȣ6?W%|,TTkFLn- 3~MAhZI&1hXyR>x|>o #e֩䭿wn<.U'j:΂[Xl._z3 z }o:[._KԹiv/6?Q(aλU 6tڬݒV>rbWn>cƞ>"i_#md4or0!O*A_( rG< 'Y,t%]]Or]GA)%_ vzv~ɭDw{qt"j,=1^( r0Gh XҬ;yy#vf2p3ڳCţ0Nvfrc/*0ɯ] }x|9sY}Dvhm9g+5%0p[s` ?fvm]hrZ-wWѺ'"BpWx|>o~l0RE He FFGjA%GaMRI)n!bYȿ+GfƣiWZ">oH刃Ժmr7oc/ OhfhXV Az:ՀOu~6j:/ak-2iFbDAr3jįj5.f=.uLq]Kنeô=K`e{6?tl Qx24iw1a "\H?P? uY]mcķ"YӦ+7RIGv⸴ev,Xd >t;ߟVw:*(Ume+"1VS9O6RWv4诎/<{x>-uS[qkkzXm GQfFy>Z4^CB]F{˘ML!FmI!z t3մiz֣my>?ٯ#%cq+JgW|OS^౑#g4=A۸avy5|B~9u[dG ewLŅC0TVbw9Tm#+㻏^.e+[- 5{b)ňd69_|E?&}.g*n=J5M5nW7&8@q buK#j3 {j/rhIuDѳYĎ(Tr-b^ m5oGd^g !P F~z!u}ROOnfTA==*$_w"}E:DԾ"xy#ZYx z@XGbd9%ʆߐeXTω5mbPI-tbٖ;gSi*f935-/ZP])f8P2M|Ꮘܞ)? 4֗ܤtyr3u ŸN4[{wFmO2ġ8 f߻_-Ϫ? 7z%éiw ts!{Z7|w-?glOtZ>"?,+*HϬCORNx;Wum6H[dڈyK( @֎>K){s=3[[ /nD.ə@-cGUWu:Gi-[qiy.4Mx8)D-#q&X"z WE}s?bZj.kqmZְ k$i ?/2|?]+zyѵ }JH7u Zue^xƩ꺑$}_ cJ$QEHŠ(((((ǧhgAoR])OY,J( /5h1_?j?c@mZɠ!k(lm`Gʤ /=H#!!@5 /5h1_?j?cL +~/#=[d YB@ Qx;@m4=6 1*Eh I2aw\ Z' aOR_ 7m. I7z=n$+l07lP` /ßGYtTN)-7EA k +A$=O,բ*Y~D?U1Z涏i[-+O,p,Q¨π|0m$Im}-THYXB@'Zng%݅֯J#` ժe7Gb֛FY<jLn=^4fX]Zu6 kb|iڕoysiBxW{Sº$zh ̻$VïpOjբSxW0*X aDV WqҬ]OC5}ZFu[a Qz+zh j74[Ud}@Lo ga+¹8ݜdKyj:Eu:] x"##jk"CI#*F3RMsWT|U:RgMltx ;{̆;h5,y-js!CV?eC0TQ\,բ*Y~D?UtW7 /5h1_?j?c@% Z' aOPIEs!CV?eC0TQ\,բ*4h HbR1%*=HRq@QEitY.lŧ,<-ǥOj72:"u\jk;k7Žŷ>KxieWla$> >q|V{[Bz-?? 5mIٯo 26HX+p "S7ljm*}@g-r 0/3־v5^ZگËsGn+Y$VKmH,vBg9|5]wY?m|mئh1/͜{$]jj{v@h>6֋6>kHR˪e2]?fmrEzLńf[=o7%vr@5jr=U uM,h<^lX9 |{ROSP5KK+覕*$` ?׃|*mMKAO>4^nȸqfV9Xu$' 75x7w5M%EK/eo>^ xCcxԝy݀ޡ?imK+Xt G_Xn\ (I`/_!|LzJݎ AMVudn0+CT9{m~0n";mg#`a5fsd[oו~-m>޺;Ksj,JegTY-F]C uk64<)h2+׉c DM L;#xD_}(&M|cJO$ӴϢ\ o\sIlF8`@_u/<+3ip&yaDe cU=__ +Z zR^]~RF7$aGXȪ 8xW˥:Wt߇ԡIQX Q2@]'Í{G-w6|IM7VwhcfXZ7/tNr oWo(QEQEzO%yUOWQ%_63\eg%Gj:1k~Ծ#<>fD̡ :)BxcQ_m4fXu %vf ȥ9 IT$+  y}K_tZ|(M,<[y3Bo5UR.ܷG#q$.P_ƞKk=wL]QY U +f^Onߋt;[,mJvcll H Fudž|E?u.mkhZ|vjkkTb@Xdf č43ӵB[uK_!{^I> rѰ wC+=vUσ .QЃiIXPd#s >!V=ŝ ."RH #UN>~-q OH#"M s»'◆=[Z~2b `!m,Jȱ)Y&5AtqWIwk%Iٶ'>.Ŵ[jG,LJw~wl+k;8`m..f8r7;ɬV5C*%SX_k>? -4]CK,$BrOc x:um}E_ :nj=<HF5#i<&'*Frax×MZΜq]Ӟm;Ooh:Dž|%/Wj>& [O e |&H{ͧMXj>.dm,[[;dn Τ%ooKo{|[SWKKǞcyxdW(YR:Vl+'Q[:cTOQo㡯|C^W|9h _iyh/~ \`瓒k|OKŖz/u[o~ciO 2дGq(daJWSJ߁3F~V_sz w+x'ſٿ~156,H'[BE#p\+>*uOj>xoSF@-#6B`ϴ/%@odž5>61kGn; t>צYNrI %Gsӂ+npٿ>4oiBu+n딸F* :8G̱) YrOrMwMYؘQHŧ+m8vϛFG]p-Ot6:LQE Rtx~\5u]"1$ [ٔ€5誚eiVvV[j"VJAj3X n^w eı Ѱpj`t4QYZ+^tK`ua3X9J] V{y_i[\ifɹIXQǘ+JOmۃҪ7ᾓxCCei_jw;FM|[@ 5/ j^1JЮ͎{?.g2~H玵=/ u x:KHb^+P8T7 22< ?M|[@ 5/ j|/:AEtTBł7ߵr _RjᯈMn/~⫗>9njC:'%_jWp]vwm 9åΧ_EG&-y§п綵;G*} {k_<jM|[@ 5/ j B//Ѩ?5o4_LιYX6ϻs+d1}o>=^|w57us+q>m$r%%I9QHo~Ÿey^Kk-*XI q<1θ$+8}FF"^.biq`ӡFN1 #]êg>*>qx~^]Ư+ 69S1BBO1#G@gP ji:/;M=Vh_C ˖I&F]mo đ[J$IcT5 cPp/4jҖdUYE X ݘ+ MQ26j7gM |in%ˋ[>lZi$K"~u-ij,Ƨ! ZTVݵ'ʵxEWPwKχm۝HfNmԄ^Q|;dQ/6e^84|7am$7>τlw\p|^"jGº{fЮ/}+nI󥹹Ge K*Hpp8^!Wo#ᾣ<8K̸<6UX2 /ܱ1jzi7İ\^DFڎg('&h*}ּm+:c^&w_m~%wrVKjůZi^/th,%P~-1䍣.*I(!rF hIo/ DfYSŜ]-Ֆ{ˁ^FAb bKios.ͼtaiq!Oc'y42Nm[ K }IQ/eitP88ݜd։^wϗG߅ۿe֡o[\ˤ<] CmsKyZgؠX$񞔘5_ @S v8e`ҵ 5h#0~3'=85/Nכ+}>L5YX5|>t?i^Xr5ږs誑r}|4_Z,VG/˧ z%:lٰmょ s_Z>^-jn ~ʕ~쉺a؎EG oy^o|ϳǶh\ ZZI߁W w?|=o CP3[(kOb;:N<+`O5'1\Y|k thE*%H=#`oE(o.V{H0'fS:JxsQ-mOK̎+!tqE÷`kG'ۻ=8YǨj n|ˈb-+,˜9 K/'Z:(&0<|S^q; Q^SvPWc.nmu*lM l,pNYA͗ų[GQgLsqy>Y0a* ~loW.xL1z,҇}}b #mpRE鵸 ]ƋGNG—zg5=jAW6+qmb7ln#$_Bym.Uh{¿5 __iW{;Q 4J;\'9 +9_׆fA:{j 0@+2 k+ļIQh沷K}qtH- ZCs3y` HlW3ڧ&x)'\iZ :;Y» )PsL-_J>|yѓ֥{;{-5g[C424Rn3s\GgxbU`:n2vaz .UEݸfs7@ sç^u &U+Ym tl  .ܷ>_WWFg]ZxfOAuf1`6LHdc2P 'L/ iCZ״{;]"Xiya3ccׅ[~>& 6{$6۷fBsTz_%$1W 7xn֒߼\G .Ű܃5y| _g[k2i5S RC\$7]mZh!?=Dž=|7/xTt??NmY&7k rs׎R\+y/5I[-nKs-$O$eUO ,`jsh&uO,y+FT]$(lqo~_Bdҵi.m෺)!UP0洧MԜiH"_;+>"|H Ze}KRglNvzF]mHRKyI"ȐN03]OU$αTߎQ^acK],省̏#DAǑBhdfv>0o,en5 !)oXޑ>P m @*~8j+n+傻+˴j~F4a>pA, /1*Kg}ŅK-q29 0w$]⣪ތ+-0M;~E|o}ˍhN\?β(cp(((C)[Gޫ"u8xim"0pFo!A`+ :(<_@/(EީicQ8h-ြS*n'rsl~/ %(m5g7 9gOKyWNly.[m4jwAIh"gTWmT~Z-<_YY:6fm\dDH}dž럳nLj"vĹmu {`FF=>|6[p|D.nto^O>Ӻ!QG׫I%ߑx6Ν 6[5i*>^oĝw7vZmޛq!I.MWֽ*mo\i[c<-:x_Ie$: kjBI `8 1O?{mkZ-U|CZV;s#`,i*k/8>E Vh b#뺕,۸Ks,Y'mvbWy1q6Yj3x|H27j:E 4$=ȓKۉ ]Ecc̼?nJwڌl`6!HΡQAcߚf[=Ze;ukkh++r0A8*yKK^|:?m:ڴ79sp߇#"bYhyU]NUSmRCTMfMbyD,C#Z1A+VBO/z'];OOҙetHiP,*{/UaQ3|2;+y-u[yD[ܸ$7 AEk;Y|Qyu+-^]nAkh˝yѬ̪FG1 *m,r <7iƦuZfk&y>$ o^qy<;im^FNcO|Ţq(!V ꊨKJ]yX%%׈Uu[kcORŜq\5Ʌ?sʴO^=Y>:N& ,g"d`++c$G#o͟57PH5_KZj K 22P@$*MF=0񞗡F$xcPۆV_,H FHQVrJw#"J緖Κ[u> o^ѭu]- M;&1#}#Ԓ B%?u1(^8 W!OZ-ڕF>[ D5 `@\@݌X:P?DiP_+ ( ( (+[Ir#ЃaIVW_ uEfe]fuA7ZTPoUo?W_ uEfe]fuA7ZTPoUo?W_ uEfe]fuA7ZTPoUo?W_ uEfe]fuA7ZTPoUo?W_ t3h/[9pܨXg^Y)3E^jw1NuBR{3{|AʺN(/bIͺL1~y_isݟ^z|?3{|AʺM0*@eS(Uo?W_ u>5ߺEnFLA0=Tx'=-s]7Q@0}iuA7GUo?Qo iHMMT u+-Cԭ!C<㣩U#PUo?W_ uOg쌒K C#0C& [ # N1c\xoOuBKcI_X;UYU@۽tW_ te]f>xsV luпxL}q=\SK=L W_ te]f?E4:)T쫯 ?3{|A"_O^} BfiFⴄu uA7GUo?Sf֬̒K C#0E& [F98YiO5 K5[QK(b" 73{|Aʺ /újw~Y>SWz.e]fuA7TQSWz.e]fՋ;9m݂0eڋXrzPw}}z0Կе^se ׿x)A[)UFI&6W#hw6wZ^jWv"f^F6|go^T|Dc[k^5[]"Ran%mmIW TrNv$㏇=lj"YF;>(m륻hBݳ|&3_pSX!ua9&6ls$QKe߳?,Lge[YB |M)?Oa5hvĖ*ĭmpr[6dm9zᆩOWi?Eφ5o_nsŹhٷy(+}Ϣ=;R&(UrM%o%UXq|H6Au--H-{^Ox=tjn<]h'7z |v^.:D=`̪ .oWVkC^q*bwo\w(ok ѵ{!5}rI{+ᲆwo2,F?? $jG,-t5DgGE>ae$p{ <c9?#qj;Ƶpw|_FEy gHEkecc2En~g8]>?7mܘ!/C/t`DeIsjmzG>"%<8|N jWZ|+ײ[U"*$1aRx:WӒL]Ni%ϓ,sv%'z Oj:LՔ7s#q$A,2ƒe N݊'71WKln>f6fvy\y`mrNvg.52"PމVl(((((((((aK? #I'tY"I;h -G+ з_MP/ %NC-@|؉~XEnqLQEb? %ݎK5D|_x[#"hWoH?_x[#"h6m,షK{h#(*Y pzu;YܑE+ з_M-Bޑ4Q@+ з_M-Bޑ4Q@+ з_M-Bޑ4Q@6X[o ,Q UQ/<F.-6w9ifُ#&(WoH?_x[#"hWoH?_x[#"hWoHڶ8*PQEfotoxx-18.01.1/images/viewM-dots.png0000644000175000017500000002362013222767271015676 0ustar micomicoPNG  IHDRxx9d6 IDATx]xŽv{{E:UXrorU6I1xT֯~%eX{=Ȫ8RSO ,by``N6e({Z%2{ߪ?}{ꬤWj됱?I Ś!8˙Hmi*(5>ۡ;{K{%5ufAޓ5v.rN) "w/B>$IvWY tғ.zӢyeCP*E1UyLf{گ#B7/LeV6DZ`{ᮩ/N Ǜ/2*ညL^.=+V>, u76:P ].otP}lYMlAݼg F/yhyE֖9iubc(-p-L{Тf _#yY.@}\f?ԔS[pm߶-mݭZpwfzO>acpQ)! 6U#i>S)H?$iЧ{Hp}0uSj]bXS:mZSw~O f6Kdd]3BwuSP.cbցclX#Q.*OmeRN^ \lM-ON`um{SH3'X8(o˺˒vKn5џGrZ.'>n8H z^1da~nV69wJAಟ,:睘Z9Ƥ\ VnWLG*Hi&ܥ3hiΉBY(NIl7ýqTk$콁[6:HmgWl.awdFuℲ;mf:`$mͻ,w;n*,fNU\L发.θA\* pY n,(9Z9tYGuyɚIr2ȖfF~*vQ4*%,'}itMkqnsyxRuTop e<6po,y?5%rjU\46W<횏KfɭgԇfO{ 4{bms#S!zh6,z܄Rxucz ) :7zc4ca/^q poYٱ ;LGwqi jrCY5 \9AU"oKi5p$1R!V GJ_:66:ʚ 2[0/iTS՗C<7c%4nZ0DmKkA.gs:Z͚+D9L%3 wVL6JUԄAXk=|>q9$9<܅kG4XjzK"|->F*ZW3^Lf9u9Jb*"G_ =Sʧ]ިN(u߂\P{X.D”@@Zg;nmƟyM Pϗ1C~TZOu7h0F}ܑ`bsԟh`YAQf/ҋ>aB3P\0L05U Z_#Ҙyp\37,ж)H΍am̆H`YnK/r+̸xq-Qx 6<ϲ<'WaVuOq} j_% !yPbB愷Ϗh=ЌM4N?ԕ-~?7尙_\6Q-p fyd:8E6S3\3`Ȃ=^-L{ u톙`+wF^A܁TSL{x͑~8n& _b1j w 4F MpacAv9`{B 猻3jd?p/ 0D.|͍;>:dze8muM`u0(W)0͊k#V.H^ȦWAW\pbY3͉-P` )|u{c ih9j Ɨ<3.t3p>s+TiU|qd 9=2* z{[{5sOMX{/{gv&&\VTsO/3;\A5Z_77{Y@7b~]C?Huĵuk_ CMQ5cs'XKY7 fEO!i\(xmm-P%"k՘uзXy ͦ .h-ULajB]H$h@ u.h,cscdG/FZS&Nv_~sUZa*+giE$mnи>7Ww3Ti' -Y[5,53Y3K\5973F`@c `8DJAHpni if`J~-3Z3Ro^4e7@V].W:x̍2\Hj3o3!zzx˭=} y1|h&Ò,l]=A.DVRKq'hIhSZ c>VTOt2p`i 0E .KK`@9/(BsYKk/8Sb `YGϲBrkIzlKPNj_-0M@+Y1d K`0oDj$. 7 .hږOFsC$T%dQAV HN%:%P>v:B쏚hR`w &17AFYND@rZtGijCea A#9 Gt/>CɝKvx2}h>)c'T'pT&+wu^[+FnfQ@(O2?ftFh!\*,ڐ4AHpTG-pği}PZ־ӁG.f,sA4~~+j<0ZiT`h HdEe-9E\ؾ y]̝X&mL؝śYpa Zڨe|ObߌrD ʷ K K~peFPrlǴCRCQyT/IӀ炰:f货OFケ_ag;+Q~>$Q{֜#\n:H5" ^-]^0ShcȬ<2LҴ,Z<cpO7i9o+PϺ@08ܨM_?1'bx%^4_VYQ˅hA p4¦ME<)S\%`sHƳ^X|#}XƮ :ӎм?4Ùб"*A#Mkdr/J!brd\~̇x`~3e ߗPY#_ ༽`j8×@@k,u'j ,eD=YCgmɨ^[c*.% %KCZwB+1 .X`DK|H Fw]'Q8AL"§no>eL& XOCXOg N.Ѕd7Kp;?$ˬdcYpP 4=u\ `P3hݭ8ipWmQQO:Dm|Gඓ,6S`kDs\8g-0|G \\4#c{Hv[3 _Ho;pk1:~dLd=KM 4$tCXƼ!oF'^:yGkݑ0X%X5^i'Q5 2YڃMZ6K fJ\WRJ^,Ɛnna6GwG`a\3ݝԴP$ . YszvYXsSCO)U GDխTf̀J\3mtM_H4䛲mAV>>+ K!M՞N2耩DTv5V1op0ZPҫW i"k<Dtp_DpD@3*@\ڧW5a6jް^FU!X0PL F:NO!J.^z<->UޭbSɂF;˯12=k=FPԈHΈ h~0Uj`tGi[Ze XFɂF;k͇CNpן8OIQ*&2_2{@HEjr;*d [[h5kswh-hD~^M?Pҝ,C3c#3Tjlɩ@F5Oy{<ŀw>?Yª.RܰPapɤ0 /Jz PL_AMd^V q^$H՞jMdFu˗JJ-SE  oQj=^yNTbjd"}KnW|ey7GaJmeV~"QAioaIS7 >٘Fl9_[^;w|$2iь \-+ <te%ɾͭ8pMC>vNϪ^coN#tFovl.)/1 {Pm0$_|Hv&Y De~ը-#{^~RweX̀{ 2tS )myW_:4Ϣ% Fig|b=ɂ .שdo6>^\Lٳ&D(/iq4۴H{y?g|\d߁TRoM;u̸+ExdźYŞb+?~pFuv  -˛ד5jnaj8?`+l8/T؄G|#jT]-j-% 9e\avܛT<{I `"sWwd1EisbBMVd?Бim'*'IDAT\) 0XԄ:6"-)4Vl%I2Cu0f+y#0-_H2eP^`ڟ}a^reQ|T,] uǷC&]po\0aQ :-h ^ÄM`-y2h![{R_Ċ {XM,T,3Yj7S۹P6r F怇~i@kLLu> ߉cr<+{w.~BwH*G0d, }fi(Ս٣:ɿ :'kK,\k ߯G/qv.~kw$~z3缴d̀@2.̹FK(mFk˔`YG.GH&3qtYoK_%Ȋx0L:h3yYb$a*Laeas`:ITo[w_ռ_^KEs"QK`zѴxXgislD{!>,s?=;yAMˡ\'F~P~77l<9]96!ߌN,Q_,0ܝEUeko9Y(-b Ɵ::D1oFg#lS >:"QKb<ZzYa+ )ԵoOg,=dp:X0i3eC1JдȤeQ%͂gx3HY־odXRH*e NR-g^00ZKg.%Ai1%q& a b:XoZv%k.TOp'_YZa]5-mĤk۲G͡p(,Bѷpڱ4wʢʪݑ=ON˲#&q\!o9C#S=k bPɡTzBm[mݺ߾ꊓW}&#ZTM&L5rz.2rbp!0;̚gЁ1f@11RPD2}-?ɇ uBuUM9|\;[5URC?֝9<=QsdY>s,,zTXtRaw profile type APP1xO[ )z?JmԄ$Jۑ{lO?.hI*:fD.8<"hP(^082A9 1ɩ,Ko]Dn9>6 ɒc&8Ovaodcscq:Tr]Nڴv|+OBwiTXtXML:com.adobe.xmp 512 512 =cIENDB`fotoxx-18.01.1/images/slide-show.jpg0000644000175000017500000033044613222767271015721 0ustar micomicoJFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?>2XZV}qgkhHBبbwJKR@$rqM м.וK6:{(٩G5]6_* NxcYY`7Oߐ_F9 Hno +(5aǮ:zVUj*Psfl;UTԲ. *Ʒ7y.gUo'u a749kf򴠂A:cV]R{ he,܏zs͌hϏ\ ɚ׏|Cpu{Q %Q<պ5{k'{Wd1% TYj/\GP;% TYj/\G@X,5o sV<<G g?9lUxW9QP:?Kg@t㯨iZv[^%prd^8_DZK6/ΏPCgBAs7Z7ZPCgG(e P?2֯ >+SSip@>gy40U$lj ^&uu(NFं3+4x"OΞ5ď}h}kC/ m_6/ΧPϭϭ{_(e 2Ѩ)Ѽ׵†_:?C/ m_ϭϭ{_(e 2ѨOmZi % &Ivm~9?JҹԷMwhP]{k/2$E6w qӮ}2 cohp5Ǔ]BEf/OJ[k[A4˩'pyPCgG(e yF&C⛓znc*gg5.=Jwob,C d㑚PCgG(e .y5ƻy=y(R<-}I'ӌ9ڟu+i-ŽiI!9'jQXBBE$; zuO 1t†_:,kMWOltD%10b3Ȫgn [X^Yݰk[q4, kC/ m_6/΄ʗm ڄO$QDʱ4g(#ȸ|g]KmNZ\[mܠHBsf'2 co6y,m+AgOr r5k>"{մˣJ PCgG(e x|aϭ{_(e 2Ӱ\;-&2 a8{փ)ُcS^ co 1tj$pv}IU[2>]F,I<z{(e 2`<^2IA5eò?BLѩU.0}JPCgG(e +]Zg^W +K#EM=g!7[;su/NA?7|fۻZ%\ vmR=|ʌ`gtۙ_EZO0̟iL |GӻjJ'GAw%R(4khіӮڕ5DFw!K ƘwSQ\u ZľOKvia%Fdt9泬oxkK-`RDw$Q2n-#$( Ň}rn,t<2E+0pqqފׯVxTCMyo[ed`N.qヒ+?SemmkkSP,1N gqA28QM++u|&eAa-X:Ny  W|Y^h:Վ,z@58nGe XSr8`@xS-=CCCPc̊$$':^3֭$Öt:j[Lmdyg1*v f\T5*:m6^}7mEM< u,>5 w4Ԥb>a*%w"I:P;EfzT-ﮭciHoHddgOZГ L|LCV C%\t+YMm}JNOk)W/'ksxm.iՋS=J4LJ08&ωnyuf,pwD&I`W^OPx8E 2*5p 0QOC_Ԛx/wFRե^[dd6r%3`Hs'R?Կ#gǫqxJi6%핵.HVl.%*3 [C;@ZYXn.XĞxikT;kmRO\,G gyݾkYq&HX6v2S *M]n'MPO&#O./yTw gQsNl5ҋIƀi@s9\k`}dI^FLTit#yMkO;9",2,RA Oi {ZY'v#,KOA'3f׌jVg ׮!kiHtlB|9;IvK;),$m!2E01pl=y[\}lvZ+?Iɕo6&L9d]^jSC.F+#nʋsԼk/fjuU2H&z͂p2p3U=R4MFř.̥K)y-ݜl|G{g.ɧ:I720` ۽d̰xQPYMGJE$p)@Q^/Νl"M$`c8Eyi-l$$]{?͜?f^i]J3Jsޔ08 VGYed ʌp7CV:-Ϋ k">c1c;rF{tc?_xQp98<ƚvظ[I15feXopqT^[eVm {xeP'd .FHU Ac?_xQw0iihO4W 4ꒅ|dһ <nzojѠ wDޙqs2ANRp#:ޱExvSDqe !^;WaE @g/>- Phb|A''$Ǩfè[I >;8+8㼵Aˢ%w)*~@X[PsꩪdJ?ܽy8t%k\jJ-X $rǷuP MNZ.qn۸Hu#[P\쥁d]<_u(Nj~.:^0=2LwBmt(ğ~S_)6ykzŴocHc(dU]?E+-.}_ 7|fT>Xxݪی9xT|VUw3#yn((((( Mi1(]} aV¡KigwP`I'd+7}Ɠp0WRd<iZ7nu2;Ui(,qB4 bK[=j+Htb JT%]:Zb\wc##y@،2Hpr62`G=G-JNm^N]i%|=~VR][ yLA(|r4r@N zOBphhܤ0 Z( ={U]JP,mqs= (I-QX&ԭlwNf_!\' eGUF|* +,b$$Ѭ u`A 4Y@˂C we:Iv6n/qaP ;9JPWq( T2g9#}ii, A=S XFCbn{8k2^ӮA;6#i|)ؤv=suǦϨ*pKėPČsx~h$wjpnz}WvŢDH#*zGԭ,&Rt̑|sh'yXZֵ$Ume}yRvw199<MOpAh:[UWYfۈ3&zݍ֑]ZJʻz2ڛoiAhR1M4R*s? 3}K$+O_]x \VHc2 8=*$N^ȖQ [ F܇& Sـ ɭ? Zqs2D9'e8&uK]^:Mqjl2a?lga˜X*5~UOK94G12*G 3λ LUV?ݟdNrZ0+k2nf urs;Vgd0:ꥤۯ(((((((((((((??CSZ CXUqv|!׭k1gd`5Z6~:u.Y^p?dbrBsף;ie2!К uؿƍ^M?ב|\{&=Tг?)28~0+umR(%,o!nv#c>P ·^ẗ́`y%|o0FK"pVn4K⣬$+ GQ@x#h0^lV2-_K)s^D2ͤB8"-eG6c]mo5kM-Z-ZsM BK@։4{(4+-I5!%ܶ_laN8l `M ^r:XqYN1m[2|Z 㙢fRw7 0[`ҥOy8U4,<;hRj2i7 M.jFF U=I4}*xnq,vLi d {{Tմ=_NRKIlp>A=<l5sg4֚:D-~-nqH瞯 klE/浍 Fb<#<y/4eW*5ē&^F,p=К6hz@[T]=3taV@'x>ee}hã9h֚]@0`&#vw+ۋfm4*4 .E|N՝ð_z,Z+b Ub[(x+hsFd& h6Pns]QqQ@Q@Q@kSYDzMPUUA5f>?WRJdF]T▊BX# )9KE7bvݞOR斊ZjK$Ro (A'ur.r?鉪Ry6M#.9`sPտ&u~MS<}UV?oǪ+EQ=WyX(<QG_czPEc畏"@}UV?>Ǫ+EQ=WyX(<QG_czPEc畏"@}UV?>Ǫ+EU{fq $dHzjU5b-vL+H$8n"-QUw_r?FY(Wu,n"-T֟³ķ1 1IGhZ CXUq+V=ZyIr ?ch;Wlu j:Jj̲,n >[ PO#7-./kvPquqEQ#20 \E/<YT5+[u0y2m`#i7@g f^}a}kl@*`y25zyCDU Lbdd˨ 'ޗPvw:i$1j0%uS ?Ο&edA*- +zpH?J kd_)gV?30!B$d mTx-P=B(|>NC:Gbbz-n%_1qJKg^,vW mb9C,5gԯ-:x[o O0 0vA5M[]zuX~`Ox C*2r.w6Vpymy]TEYMtm{ P\m;Ox^s]Y|_j4Yoouf_*!Xq*l`_Gi}a.n 2daTg$;o NS"g_9rp8'.6oQ6@ z:םjѼNlL!͸GM )<=ԑ\ݭ#{f Q)AQQFbq,fd*7 u\OJZX0Yf}/߯eJy#k<=m767l=ݺ)-(ugBB$@<ұ+yW<] ֑+C$dv@z:<7AZI +39Ÿc[$NO85>"ܚgsodc݅ `EJ3(h=tӤf6wreD1#B(2mFN1ܜdSK[F隕3{-fILGxqS]ԷPܵ1c~WuҮ{V"A 1R)hf x#vtǩ_hW71^"X<قHV]@M i^&"y-[odr)s:V۴P(;aTz };:+%ΙEl Q3|RF 5cKX ]Z^i02!V0?]kk5kW5;.!a_Zv ]QHaEPEPEPEPZw{/EkS@/WG״&Em(p9j+u95XKi5c,"C۔u2^0\\G˲_.$[hn[$p?烎7 oGij7R=}l[;|b;Q?զԴV k~ѢU(:r# zғZŬLmܒ'Uެ 6qP\bΓ$aw– 7( fNUt(tZ9^bdC/)ܛϡ t .mIck7qmٔl]j?4W(p3d}iC(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEE_0ŪcaыUOnua1)m`I0 xQY׭䳖{$hZ4B "K9 WCiL~f⳦u˭:]O[,)aBnf2+'OJ4,-V{U&BY…*AN9O-64?݌pA=;e᫫k} Z+}CK6Byl = :t\OAxLl|܀;d9Hbۤf7 h̊h@>6H!ݣeG'Y Fzm-r3Ymߴ,H@',BwLSZX궰2$,lq)0f-NL-+Ml\c'ok~>cxW9a4O91p=+k _7 eZ!XӀA#{Q+ Յ🟦YQ٥݃Fq3sXM*GW&IJgBL6[p h@_ng(Uf \;303={TG/?h;wu Z>"zby7>tpڅRO̽T X"ҵh7kgFѬaI! :W>4ʫ3khxZ/8uҥլSRbe `m래z+B:oŏĂ/y?wo\u$u-B[?V;eg\^nGIne{Tq嘅<4uRG[FR0pO' +8`7>=mc׊aP[+fas1$^4Eei>#u{R*(juM*q$N ^:J+/hj D7@y֩xf1H ƻ )Q瞔TMJcGm!&@z}yaEPEPEPEPEPZw{/EkS@/Uz{OҳtQ[iIp32 q@(] 5UA$Gc[T}~ CF^(& zuqnh{\ +U.>֡Q"0A!vЊ}FԮIaW#$g@-M*+?LբXfZN?. 5a+lٴVNCiO }0l|A:EζdN%mNSsS " S,`d:@}A Me&q44w:zd~H`1q\_BoN_BoG}A MgEӭ|Im^V8FxOjMWI+ gݒwE`yu= o2߸<M~k^%"Ui($I k[nD9E P$/!7̾?&ɫ[&kqsQO$q^GGV8̾?&2߸ iuFyA":yP_BoYms4r0ڏ#ky"7qZ&D ]w/$nb2߸<M~iC`$d Դ2߸<M~iP|M~h/!7Q@ /!7̾?&E7̾?&2߸u2߸<M~iP|M~h/!7Q@ /!7̾?&E7̾?&2߸u2߸<M~iP|M~h/!7Q@\Eqr+؃%H p=@袀2otwQH6)#?n?7~+vw"h / ߸?n?7~+vɲ%Qr"=?GnZ qu} aP?//[,eg$8 mvPQi$E6"F9*@A6 rZL8o_߳MP]4o\V==!:D&inlv k7R[-H'eKz7OK-D$66d5 J A9Lg_Y}֫}j׶QǨ^A)d0 zGuoQ X9)V I u<8;qz (!~q E⦠,yc|ALI &l[[ߨG@5#K3{*~b;[ 昆Dr$rOjݏ9G;|dc3_1X21eW~n8U\Qh5Xob&)"Fd;I~+qN t K㓞=_7Wm-a\<{y'9]hp^~{KWfy-*1PoWW[OFFxVM:XȘ"PW$88=9 mUhM<22WGkV nm$E I_5Zh0js[Iĉ!dhYAlxdvwuZ+g^^${-רBVց| 7:hN[6oo#s Bm7IrqWE-d$nnm ,͓*2ъPg^ˈx$MJ6*pF8#}vG3[ɯ5Iº-qGzRѴ-NLAi =O\I'9$EQEQEQEQEQEQEQEQEQEQEQEQEQEQE5|jkO@kI5_)mXfoKLZ[7<1' NzoGR9Kqcxׄ45t =OK쎨<(W3A$#ہFHM/5Ōĺ?dIf[@szs7Q{8^%}MZ>wΙgl KK 3`q4ͮ~&hΓe$/ 1+\2sҵ5+NG>)(+H<=x##޼[+ew^hk>mvEʌe•ۑ9"tFTcPmdӽۙʀ{VGFAIL,.Öh@GR!0:wL[í,TtheILIq4kڴ.{ܤAiEN qWp#<3Znz.n4f涝 2>KHqzL4ma[86= 1ڀGb9x[]k.Ӵ,VHB4SyQmܪ|'x5&XtۋIYTy'PAV lSHW=Wp4Gy#u/|P5MQM:զeX~pjx{P]՜}o?dy#6nN1Jc=r`U%)f-ne7 |s9xy4[Qtۈ紽4Icf.bۊ6vy k{7׋LYO|!P ;j+׹qzu;◺u]kB2rӥܳK$pH[ޥ.5/Qt7%dTawKoCH`F8ԶtNߓab{#^MiHt?/BdeKeb C)KlCOsg,cFDytFEyϕN- 薷hn%IHp 89>YͩjMi&ډDG IU9ˀ@c=FAzeۭV"G˹Pv8q c}^Kgjr"iK>TnmȭK3M /ƻ<>5LIHJN ʯ^V#sXz,tiIm[g d85}Yl,uKy#)sF[+*#:]'nm$-jRxhB=A"9Kmm r)!tpȭ8#$w>DzMx曤ztImekJ.o=pvҽNeb(Gb>?r>#VmEs4/q-m"DBXr4騮cF5K[E頴W*"B>e =/x6ҳ'Hɐ#_:-h^:ijֲ-t/Rđq|o,PO-؅9e1Y:pAs@\R^-}M,0=4fd$n98⠲1i:D׶_Gsmb)P0H=3.vTW?o2WM>?nyZ6!ETIП$/#1ȸl|@u/UXn&%4raUmfDqinm UMW!Oq<{SxEQxEW>0[Ǝ-AShtg94-9cfel`FdWG.t[__si(S(ZJ& & }O^ 6 vZEYQJ$vy F\̓\YIF2n+*G^H]HPn;%ݤD}Íy:m_/?–$R(IcwGl;t?JZ5Q4I!]Kyⴭyo 9(B%ēo6+BrGӂ*sziԯ# heď J%b~E Ҁu<"(u<"+iKHGg^=H0䶂XaiC@}}_h FX2xMX?N%DCnc$np9 \@8Ғзlt%07\sێ@t__s7֯2մqsr,g3">aYzW'c-3Y5LdeY@.r@~}iwM *dd:zV 񆚰ҥ3FeUzv# ڔZ,$vx7VVA PlWഓ? 䞃u=7-o/MJLEi6w18+8kz滦vw%5գˇe NF{Ⅸ3u<"(u<"+֭ 'xX"QʉP˓ZhA]CD'ImEdc,:]_/?_/?¹6;Y{f;2vFx^1waktnlnW{TsE7 7 i\Gu:#,v'1T|kz Bi/cy=Ǚ, §x$b yAf6`N=ȲĒ!ʰ\4f V+"+Z/20~ch @^( ( ( ( ( ( >cxT55| w5[kd$yN#zQPߣQ{E~@QPߣQ{E~@RHH;X8$pGQ\hs=G c؛M2%V#HrYf'<օC{E~F˟?UMEC{E~F˟?UMEC{E~F˟?UCo[wZQDWTzc5rs=Ge*&s=Ge*&s=Ge*&s=Ge*&s=Ge*&s=Ge*&s=Ge*Pd)$n2O#Ҩh~t6^IdPBQֆ˟?U._T5˟?U._T5˟?U._T5˟?U._TSJl4adA,<#Hw.Ip1NeU7KPFqnS@/W'-#RX4V "6 r pGls/Uz:9?}es%$J^ZXK2{OM`iֱѯnfi 4m4(xA)>Eck3^M2Eg,W $k_U_o[Ֆxag(%*p!Ga= mE [Lך;̪ =KGω\V0[ oeB@˜`F(v Zt+[^xRhƑs#h^9oFa*x=[yR}kyu֒.k8c]VA];Ǩ+ _H[.LkX>fVIQԨ IeſuwŦ,cd. x⺟ow QWN{n|3'Mg_[ܔ\f' |0O5XڥWN{ҬGk{g#:+֬e<9iKpFBBdoNkK/Pk{ Xas֍)y/  Wmϝh|i#7.v|cRN.,s$sM*Bk++e Fy޺_ow QWN{.8DMl6,ݒy"*=;1L 6=3[C4-(nIQ]5jOow Qpc-վBmsiBg`>O໋m!s g]@ P2 ekӿ^zow P+K[.–q$LlT~i lˌy'{}֑Ko{udUI*¤F'=N9<`` Zt u3*}QEfѱ8ɑEݬk,\(TK7Va";&A$u^xn=GSץ[so4dc%dzU[Iӯd:Oi1TW%FF1ekӿ^zow PiR鷰^n^Y%;@R<'>ՌlN-"m׉ё 2 V=Ol /=GVA];Ǩ RTվN5g11y92F8÷3jOfKkK;ضH |" /=GVA];Ǩ {&җafROyFS>yy.י,HLz0"rA]7VA];Ǩ+ ,fxSG3@LʳY!䩓rk,-2_Xˤ.bP,]N=}~Qz(>!QE ( ( ( ( ( >cxT55| w5[k EPEPEPEPEPEPEPEPEPEPEPW-'JF\'Qle ڭ'!g1}O:Mq~>VK7\SlBRWRjIA]K e¾&Ҽ8laI]K9 Tg`#%{g5f_kpho,A Z= +wsԿ. }w>f2OʊAds\3xZwӢS.i1E@Vm9r OvW[]5 $e0:|dgu/߽ixmH^0q?ҙ)?+5NP?H?U.K.5ŴY~΂4`ɴı; 'u/ƨԿ8Mt=ZTI57x$e2œ1ݓ+B:ڵRL- RC#K2I ~`VVk@\'u/ƨԿ9?W h}'ϤUb??>'O}'V( h}'ϤUb??>'O}'V( h}'ϤUb??>'O}'V( h}'ϤUb??>'O}'V( h}'ϤUb??>'O}'V( h}'ϤUb??>'O}'V( h}'ϤUb??>'O}'V( h}'ϤUb??>'O}'V( h}'ϤUb??>'O}'V( h}'ϤUb??>'O}'V*]- hՊv$zFx >'O}'G$I'~>oIhoI>IQ??~>oIhߪϾϤT}OO*ߪϾ7df&D?Uh}'vIQRh_c}˒Hj}'mohb9呎~ 'O}'G$I'~>oIhoI>IQ??~>oIhߪϾϤUkOvyдm݌1VdfO#۷?_ҧ[N 3Fn?o*ݓ7ұ*R'MD@QR'MD@QR'MD@QR'MD@QR'MD@QR'MD@QR'MD@QR'MD@QR'MD@QR'MD@QR'MD@k;sΓah/l/ou$ TC!ֺo.0blk4rȜ9zt1Xl.gKWIndy5#ƹ8'[9#R[V,XrCYvO# cjGq㬍cM[^h[2Lr$(<) C.\-1-* F4 69Qu[ 6$2A0y_9T/;j)5ef(_1Xd Q>2 ҭi%F ApĒ@ '$jt-(dҵX5;t8i]ԲڬI,:P|c@Fxl@8Lg!sQSUޯ6mm .H BZԼ}K֣{jW$ΡTfʬkqy9@Ϳx^-ftKq)ix=kt\LJ4jR\ ^6T/0ڣhtM1!@(0((((((((((((((((((">?W&2iI#ɋy(bi|<"VߓOQ<Em14EL_?@VߓOQ<Em14EL_?@VߓOQ<Em14EL_?@VߓOQ<Em14EL_?@VߓOQ<Em14EL_?@VߓOQ<Em14EL_?@VߓOQ<Em14EL_?@VߓOQ<Em14EL_?@VߓOQ<Em14EL_?@>#I$7&1!Tl}{W[<y14Eqw??^컟]OQ<X?.Gs?bi|<"8컟G]׮ɋy(bi|-N Gw@;{ \vQ c 5y14EL_?@]ף.Ga<y14Eqw??^컟]OQ<.}qs[dI[Fq[>L_?GA#(((((((((((((((((((&K:Lk-ҳ*`6с{u+ſ4'o k?i۬h__Wy ?4'o hğտ2__̣̩ G&$xڷK5yO{VƏM9@^gdFH\c#ZW.uA4z%r t8E“c> [?4 TtLҵk 5Uֱ.~hԱl#џ:6+ k$m>n$M F-oi"Z14Ea;wY}ͨj Z$$Du,0ȩ$v5M&+9#‰eTP{`6?kxO+wM6u喧#Eo=u$2Fj[oKwkeq Oqs; 6q=z΃4G-oi"$yaqsil1s@7As23s:!#|wQp;Z1<EcAx?<<.kxO+(( # ?Q [?̢̣,4H`-QWVhF9Hڸ<(?^-^_݂r W?F-?R<2.>GNk"[ۗ (#2@v=qhh>.xboA?ʓ4 \./MmIe^FzvouӖRIB T g)rpWS~,F=*ŷůE2HɎKxDUfҴp,"Ӂ2( &'81z{t25%z8t)ϩmKhetִk|ç!nWz<)QEDu_ k<:,A{x06rt(#[x7!rɸcp88^ OeQ`/z9H~ȗ t)߽^Q.g$w^%쑤MU2`ҽ#41?CIClxS&^,+UC*m I s4|N-#a 3(lx'41?CICls˧c-XM/mo2F$qxln-#{ hHt/vڽk41?CICljX*tfkeׄ?<%^eͥCmۻhq׮y}\ayϙ^[ᎿvW\V$KY`sӓ r?) z"& Y_;- J~Ҽh,m" /[ uU8h$N0k!_nR%|c%)6+3i8 gXOμpy"y^ 2}:Wm CĿ?:9!dVm.,͸f (=7j:qsfiZ:(XDRBz<mXѱ3ǯ?CG!۸p'޼Jj~?sJ]t>]5\J |yY]#N5_ylu(UpT&M^gxwzʺMx5UgPUc?ɒ xhp[PE^\$b0$P20J?ac} sXN"$a0 rq"=aX2H*G&D;> \Kx-9N;gTrga6V躖iQqnsP+JHTr!X@a-1g\Om]i 7-g4ȶ T.>#9A"k@P䖞Sj3J[B yL3 g[Cm3۾Yb89ml+sQ@J2Hyzņ=K얡GO>eoĿ ls*uzS;y'9].3oOOG`76#ۖet١Q@Q@Q@Q@Q@Q@V?EKL C>UZWxmNִ-u]DZc}iQ\5Ε.^i4w݌OR,[0iǶPYϨz,'RijU?1s uW&|ol\V 3E8@ 0BqЃdwvOy_p,'>ٜ J9 B?x5+w lpG5rŀ ) VvwyuĂ(TKA"`\wI '먮>_i;y,^$Oۇ0UK)`c'җ&>aOou~ur"d)q9ڀ:+MyhUoـ'Elr9=&+ǫFQi]76)H0$8PN8+PKg BwBJSFd^FW@AVs׽rޝROCu T6?<2(cZO} ѿS};5OVblbtyee(`@JTtfF-w?dOHX]0m9'ߩϾrk]]אZLꛡg!pȽkH w6ݫ2FC#e>sB(((((((((((( ))((~}+TVQ%-k#_LVYSknLH}qےyǩN( ̨<1[S%»jOj拥Yhd~ vGI=I$Ijё*AӑY[]%#M Vo"]53,j$ܐNyYOC[M-fUpcgKm&4WvnR:HDa-|~8$^k]B5FK86@Fay:.tbP)Xoф([c| xOZ쨠V<~lS=ڋa< I\"wD]&6;^PIUN19]&0e=kKw4D A'?N**,q_-!e6)o-En 0pAǽmh|VgcutsGs4ʛ7Pw^Nu?~YyF G$r2s krm Q㦱u=FF͸u),m$2cE`r r+'9IYJ~na?^h7 <O{qv71i#j Ȏ%H#͡^X[66mjш<+"z?.%h [nVLKFU'v5KcȁQ.8W_OeYLB?'l,hQEQEQEQEQEQEQEQEQEQEQEQEo/E>o/E>Q@Q@گOs$XG%t:Od{g$ w`A~g5-ƺQӮe@mc- ̖ҨQc=:SqXޣ-A帝$ <#'^p(H!I3Z,%5+WPnsJH]X2[vG =gFuYӬSceP}pxϽ$zOzmF- @hT##YMH4yΡ fK 5NDc r~pXcGXӵƧ*[Wq"OY#XmJ93]O\ڪj*U8Kwh^IwX%*\e_i2Ii.9'MwUic#EIc(F҃dxN#qnl+"9l2IDOCe놅h{|̹ fOq;ʹu[V,RW 귚Gu An_Gݒ$rU< .== ʹtyW?.Cu8~:֥׎R@P1ޭYUԧNҭ>a3 TH%*A;*~c AsK*J1Q!Kgfq%~'xuD lԨ?;t\YD\+)R?vǐy5$[A ϸo3qiJ u#9Ma麌~gt2!\6 N(ͼd;xj>*&;iq@4W%mNYmҭ-Ix#, lBq`ڱ`ymAӧ-eAez4,n1Ahz=bM,X8oX d`Y`Mҳ4x^skdKHʶB[b h?ŵjoWp]HQC~>-u]2MTӟPx 5*hpA#J\h+T埊K[$f WD+/dnR!sie"<|cvpCH{EyƣsMӒAKyۭJ ,1g r:?d %r*A,g`^Yd`)y8¹^u)ɵEl90+Jr i~q+iG^[W0 G)76'ӚsEyƧQCHYܲ/$G;cp.젹TdYQ\+dsPOEP0((((((rD_}2D_}0 ( (3OC\ֽ+ X"L(-i.2v'#'tO7жOisjzS$uϖK.bu(˂ FGI,of0ae+,`Ge< ^KgΣEfյɶ6}Ǚ" 2:Um-l": Rjq@31@xD`;>P+GxjCMn[Ėˉ#6󞡚6@{yɮlio) *QcE @ʖ&vxqSČYv>׈d:̳Dx?y"AutU0zVXMwsmwiE7 +Zs(Ӧnv]-3EٚPı$|L/6-ky-YCR Ep$z_ /WO{&ټ42u}Ww`?mzѡt{mVX&/Y6-Ҝ/ 7Ph`mcymk9\B\;>1Mmm}ʮM )%=J3ӊ62DPJyQUmmcQ@{CsJ*U 0<֦@×_A+_B^Bcf8ϙ~{uY@qNIj, g4l=;P+&X^䆎AlC):TU IZ2< #\nrI 22OhO!]D\u|7u}]':uƔ-&VM>Py`Fr1I/úm}cZ\ƳE"¤:0B? Y tmvq3𮦩ČGtoX߅{Hl ZFU U;qӭpVWWS-;_k0sS͵X6ੵŇ>*9XXi:o%sFgA&7&Z2IwL写4Ox+y`˺6##.['<( oF,?箣T1a=u69kmogkqggE700C Y6=1ŷv(F`=FLbz??VOw#nf8鸟J4)x:l[Gci9c @s}cϧgNIf)# Nq[P?xVRMv;XmEeQFSy[ʨ\fZ'X2̘<z2::( _Zj7ח{Aup`tU6ˌFv჎ETv<1\yB#aR2 R3*q$Ojs:l% >kv7I[}(qS^ni⺻Y ѻ!%jA(v9OzX`v8e,*>mmu-FVOT! R$E&=㵟,]W4l"Ha@rQlGCIrJvhS\(l fta H'7Ln^V+Dei㏃vdFxC@&z\ZLA"H*{8#ޟ< 3G,Džax M~e}l0B#x)ێ: Vm-H2*(zl4UޖcA[ys[m <*di1kzs[ ؄ sW_U"֙$w@hؐH.+LEeu k-ժ_M%!w)P P#tJVK. Wԑ#"|XM{ese5]8p]$2dv k5OEnY݀@g T1,;u%Fc F_1p2rFIyk2+E$uUnḅ s^g8c'+k6n&̚+Tœ: cӾȌfc# \v͂@!>`8&ZzZj2i_1ʌTx~J }B džM\ ˜*`@Lvck}Ztcu*!`vON8mB\,ҼĹPn9'$) (':QRiЇ|y[_ByqzC\+i$c,hd\6J¬ztP~Q^)5+W$QCc%iJ#1 v©[ꖷHLllt'|Wj"d.Xm /4g1ArRIOz5 M5~3[>2[ prp !h6vWzuw>sH4%Krȳ!qo4;O @j|yY7G>֚BQ\xxHXKGexArG-gQEx-a֡ͧh`32nQIh1R8w3M*Bʫ#AziyifRpq'\_1po;'9 8$1]63rD_}2D_} ( (3_C^uc}kNMFy=Wd 躯OQY^Z5kxslmʃp0v`HFx;I];p6<kh!L\+mkh&x߅˭>,b-'پ$bW*9v] {4cRRg 4sDȢ4 q][Qxr;;W[i6hӵ˧.nv6 oU̷gNޝdwEJr˝pdWQELJæ4w:5ܞַ=Ktl딷,Lx֚4yx4k]/&0m[h0Jd8=kG֭VSD%OpˆVz~bOCX37$'$M,q:F';2;/&*,a˔MFlzM3H}f!(IA(2'sGcx柠Cy4Ԛog=ƱE/Ѥgr)^ ?A-.~۩8_+fLaA^EH#t xof?GKβiUP i݀~p}\\["V5}v/I NˈIwڽXZ[0UvAk O~o}PxHg6Mİ!6,Ay&Ь7A{`\cf(2.8,rkCC},RwxX{;;kwA$(f, p3A=djϚڷNk&TYڷI&pѲ4Eгc٣yu[cG|E2Map䬅 ̤G\d m[ɍD=@f]ikz׶:_˪=Ŷ<\m`W Ú^Qu'TX^*u*P1Pi671LΥNXo̖1LnVCsC\vztn-%ןڠbFYFIJ(zLjhR]jshϫ5GFy-n{R7d` `r+Ҿ,>[`SxH|#i_ U 뇕,q>,WiwkJ @ ƀ/QEBmԭbU؅vN@hj+#zׄ|9{}%̉lN#;\V#/P;.I ;dEA2Fȝ8Υ +̎~n`>%KVV I@@)e b 8 G,5%Z HbHErrp8(y/E̐Em%y8ީԎ:sdx"YOgfe7"H#`IG㻽V5TUH;S`8E;jC^#mR\ @"ĸ@98eh>4U ̽ xnq֥ݶ^yi>ȶ27)H/ e.ueEKؙ<Ճmmr3RR[kQr͵a2v^JniڪfixW6,Xv8=yWXx;Set*,%YQxȭ_iK5khVAT T0J x8.uǩ6MwJ]D8ڛ]ݞGyrVӯ.-m/'8VxPr:W}-nǧYchB3|hs8\goLs. H$)j3BɄe;;c9@O].l%Kg;%0 89>%бu3ŸiH1FGQyV^Ѥӥ@3 T[E'ִ|Gh#4Gn 5X=JZoNh3j:wGhyQ3*՛{NU_7īI$3Yȑfb qsO6 (8j 5+k`AOOڅQ?BUoA(u2[[<ƥ3s::\Xjlmsdp%w9@@M&NKE&˹ qw8D`t]We7ȷc<ǃCUTzHǢAijD3%ND 9Ci3XȶyIK"|z⼳Fiֲìuend@Hѣ͐a=O5r}*IZ],s ;]~V`X@ 碮=^`lݙV\2(m{G]55մӜKr$'ns\5Ω A)45nZtMʩk2]T>v@:GXx56./,-ql ʰU0v ηLvqaBMV!srRKc9dF88|9Z'oovò[uXHXdp}ktt4%(gXPICdl>rQHaEPEPEPEPկ{j+_R+PVj~Ҫu5[[m}o5JW =0px3Ft#TdׯVĭq򳷂20hx ; aG+ nƪJ777jȡ3p ]յ(knT67vHmjW?f. +DUdr99Ag 9_!D\:v}Z]^m[PDb72Gi8<nO`49tw1*qs=UƔ:9Ne"oE{m u=Ԍ2gQp"G'o5T^ g;3V4$] #\H@9@tZb?~Ϩ[Lubg@uSN|]~;߃pT+DmΜ~a^xf |_? 3HڵZblC jv:Mċ=V8/uM)Nٍ $a~`k4J!Es?W:Z1tRlAL3Ҧ5mbo[)Z*br~Bwi֞ xou!$0\- v[qSBwwFUIk1*/"*1:ݲ<͍Ou3#Ƌk\{_Ԫg>ƳxF+\Iqh浙tMZ\ 9-KkV41k圎,űPvI:5X{(_;K@8gڹφ֥uFR:r4hX(Fֺ]6Yz}PA88= PσnjS[&.-ktE sp8h[55C}S6Fv#t*3c?꽿ʭxWA57V{fѕE0.aupF@cpNsWC 5+3hzqA6rGroe~EQg8 L2$iքDG p9|'K'U5}amBKxSn mbI ]\w]ҿ/Wc\w]ҿ/P3QLQL(( WҨWҹOt_GZ|VdQ4vt]XRx3xx yq}[to7Z2,HA +rqҀ:j+Ɖgu43ItD$RfEVBu:֎kQ]ܣIMŻ"6ܣtrX.kQ\c/ƭn+, pO hzhc3q]\*<?먮zM,{L [YwU2]xp=+SKԡԅс'Ao;ۿLJy4X.]3LEU}2c%4ðd]KU__<1Uq+%/d5 $O ܭ9VVA޸/w>tYA5sѱ(Yw*U9<J-pJ)fAoʓE(ձ8gυug\i[3dGf;6ymo]y#I-d&xM6@/>?n][|,-ah,5khn 4Ď0zs[jjcm+NKK$VYn\ϦΪH\In m Qyʀn s3'z14WIy^EzbNGBP:+.ZKy<3-mgjqұ%ƚ8M>c+yX7pPX h薱?up[kYf$CMg3\ѣ:[H𧘪m CzgaM붚Ţ]G%9x  YY^js2I6z|F(G;#uU-Tִog;K)R <<`՛Y`D}d@QYI.oQŽ@ؘf=2I#=Q&Ag$[dGQ^sր4(wR}%ݶI2VٰpR9m4[Em!u/Dbk)<.IErq@^᥻{W5&7uƹxugZ]˅4iR[tuyQ`^ Upz;u/Z#ʨ R{K$1ʭE0Y~cS\?50+q>⍮g1t;jNA Oo9U&·_mV?94P5;%f9ңĮ|:?KrKr?3 MItFn(.-}f,V;ّrI'qZ_ؿR]KL[^ٻy,jx$ҠxOo:5u٤8p+taf?Z ' kDC?I_S]&aݽu@=kR@fM2X#D*SEEy>$I$ٱ֥ ?bKuNSEkrGoonh78l/\tG5xK24I=}%N ٱ֥ mv["4\ dx^>ѿc~V|6Kx{K$MY?(QLQL(( SןHu]{L{h4ˍoqw$M,qa@~}* qV>'ӇW~ {I-1d0 { ?Aͦ\% ԗm$f'B!$?!+:kѨ>zz^gi%݌$dŒ pIC^|F'FBĈCW#nNV<_Я %[{Wwcip_Pc݁`x-/XÖCktԙnjh#r6,@vwF~VKm&/?Lt;#Gf۟#>gn3ZUHG*N ) Ͷ:n숷<P($dLyzW!xQ|?qjQCkwa(&w)A/ks>ͮϖhZpS[3HYtѠɫ.n F&B}7@{X#1),s veks>ͮϖi[Kx+wz6e=햅Z.-U{۔(-zz P[YO1[ҸK\,ej0'hk;E%@'H<{=;ˑ$ H}붠c7-3Vo i~ke8Hfyʎwk(hhek.r^b0ACB !A⻚(m<'hfF&^-nE"Fr􍄯A\ZirH'6"٤r+ySQ#$tBussf$V#osځXZՅyX2dz,089<ƫxoRTw3mY[xY"NFp5}]=3mw|3Gd/JѴ3i|a3ϒŔ &y$N:k^[ݜ҃FF#Gٵ?9fL4ZfkBm6 Kj\;+@̍"d\(RR}:ۂK}=i 1Ld\A\ϥxo]jw6Uʹh`կO&6g(! HOn8l|\Z:6.&ehn-Cq>褉S )$l:gDg;Z֖LOi\o;Jd'Ox_T\p5khoWU`K|w/2N;v(4r>"tuĚLVou8bI לҳ<9+5ɗH]c6f`YWb9v``R{(^BC"V=F_짼:!<6XJIalg袄կ{j+_RJV?OUiKUAj.Ą[\%lL8ÞGCX-n`VP2swaT #lIcxgt$8eHԫ"6R xzNw%݄w-r|ˋge@sLJ| ޢk ij1HG,Σ,ޠ=冗kaw[ oE-SmE5v6 Um>ߦ]nnqTP7Zjz p*/\|A G 9~!ѤpJWJ>`֭S`uM^л$; uT4Q wB(1@^jk)x !C\R:]Z0IjT0[б?K_KB-(D??W?v$(t Kб?K}*]ntװQ4% .q.֮c6_H I,m 3p;N1Z׻< [<ۼ7ݏk(j\AwӒ6A痐J @Ͼk,m vH$P1y5=9Yuv[m.#x=|Si|S;&ʜ5{FOAknn1% R$rpq$PdE?7J "b]F^4^NXדۚЦAA DQO ?ˢUdigVVyڼGI4zgEc;+F mo$WqjZw/+,6i #FdK@MO㴬n OŖ-6p_kxu 3m,ps;ץǜͿdA/5?sKRKAlIʬ\ҦVs?ժP0 N:U}3QM[wү "hvg,1@zRZ_RIBmP#AZ! HI& X%GQ؜WW ;K ;Mw\w]ҿ/V$SR FvqpB}C:))((~}+cju9u-V M2T)@9>}>ZX׬83hC Ax2|> ^Ь촙#^n#݈$(xct=!;2DЇ,j;st皻' I/SRd ^& )nF88Uqťj׺|Meccqo%*;d6+o> OYowMoη*|q2lF'0,ru˝R]RP[+:q?(m0x3*ݬ[X.+k0,H3VƛމΥ:<` }5 R`iiis ch :*v?:Vω[zNJ{9kYW-48cq$dkQj+266cK22̀O8'$U/ \jw4wnt3[DۢPpm@O'9@gxK=uk[(&h:O(gl9FG}MOR[i5j(F.%E-6;d|+&=}oQD8.x-FݷnlZXWP$Q&г#nyC;S"Ųz%=#Iي]O1dPq^8]z3FGв|n}f&+,ae$*rܜ^K$U o;K1e|rY+=Ɍ)bPI,FNqTꗺ ܗrG{ssLѤk 5 .sgZ\mnv0*xbw!Ι5w3^M<]\\<ҹv^tQEexE]kmUj׋UֿV ]5],LaV pnyׇ5[4/5]bVrscZ)U|mcK5M6K\Ƞ`As^.,6 mHP@,ʠr9n}=l鯩n$u,I&hϓ dձmRU]C7׉wJ. Gͫjymu NUZJHNژSa/٤q $QR$;fr}^œ\^ip[?)k{iy )H*YH%3b8#EIy+7UƧk*Hdd sG|%\jPzi&/aɶdˎ)P"bsXI +ZwuiKPnb㊯? ˑZX5pF ,dP⣇~M%.e.?vSpozg<9q-w6,()P#>WToloqo;GPM>[Tydd$;è-HkF;B'YTE6e Cx'=(^`v³C3h:бY#vSBJ]hz o" %H@_>\F{PΒ9$a9N3܌ w)4Iڡmu&hي' W r}kD[?XŤ\[ZC$)OI<Ѡ_ Z'Qk!OdB|I%ƣLPʁ OStxH;u|*8@G-p2{ OŚ;L2mʝ3.`[zi oeH?9vF[C 3ڭ]"X rK.;sW&·_oUT6mgd.([gOx+\h[7,e'9Q O,nR/sF.}p.Nj:ŵlKje wD U+-8"@1u׬uMcC/G7Dx:C$($@s\E.m^=Q5YFH<@F'"-u[l. 0f[@99W7WVզմҨ9 @P@=:Ѳ{߶~"Yߪ- wpqo!쭸0=S%ϊotM]BK;>]yF`lƢI|C$\Crx= pt±1kYn^B:6%T]E>,Efd8!9l<=?7IٿtxNMvdbb0@FxH8[r,`-l F nͨs:@l?f͇{_lUr>B4~q$in"cw3mfsYqAmrbQrFYqyIqހ;tix{Ǎk#^LC1 `Ư c6:w\M.8y#<s1}Isc{c]^XZXũI:Ȉ)l)P+F)lhy\Qy|b=|4zݗ$A\(^_ogxmi`[ @w[B5Z+}bK5-Ԭ12%_CEUeI^B(Ao*H`c3_j \-kOm^UYo!XfIu IOHl`O5MKr7 kDG^ rl?f͇{_lU`xN{nὁf.aPH0k3Ś-̞["ʀg;cٿ?a7^_ujOoqgkus&gЉ}rsӚfW(~>`@ކr;[83+HZIB@'$cɫQ@Š(((( _RVVG*Z??PK1ªROVuƽ“\jBZ@pPp[AYw*[]xiF-sm>lr>}xt:Z*gۛF`'F'qֱ|t){=5¨#&A8;I Tft}J?o2sۦGJ.5..5+(L-O2t]n7<7ӟzEAgwm}mŕ762վ8#jFK q@kz[i`ߌ-V-"sqGw4h*#9.rA=N;c誖54e$,3{vZb? _O[4lZ?"u_ yIJ}/9=T#wgu#h3i6wWeqّơt-*5fE?23ߑЃ9ϱ?Z$kUtP {zt71Yo#w*.I$zi<kr.Kk_M]PG xP~ xFMSԵ^VYDBTr'ّơtfE?ИX+Wz 2>.CP@""(װD_}2D_}0 ( (3_J.jެNF+i̜߮{WOV}U;۫9faNua<;i6o-inwC̹f9vgz0(RJH-ZCdd}^NO5o5G#U-s~ P֥lɍ 1 ~w;gӨ}N;_7}Iӌ{ַ#U-OAWTDimmqi*os7*2`TH uYnuIlq * '$8tzŷ?j2ne{j{[BDČ6bBI{ƹ[-mkpUX3t>zŷ?j9 Q׮N[),U6#!@d ;EoC}0݂q=?_T G:5-}jmB)7d1ֽJ5*v-]nA\?5;WV  7U\w裉R)sJ?t?o'"A gj6Rws3`in-'s.`ct@a$2l{W閿bӭmw"Xdjaoտ׬u"wzW7p&2dSTcԳtO (u4kx%yQݺ:cN15zy0I++Xg>"iw~+0 oqʯ@jt_I44gqp#\ߖz:Ni.(=C 3o $ruKbGWKZ<:2%msu-*i Z9Qn猂:ux7PsC@fv?9]'1.kvǙ5̢F *my5/xk:yfo& YR)ˎt?/cEmp2z[<ߝo6׭tzzsD>)i]ZY_1 1+J+ Cn.#b0ݻw8}(?=i^i\CRΖkWֿi=T *?=i^iX$&&ӵ G-/67 t7.G = YjWz^ x.;i6"9A^N&[eڟ͗|o Isv;{TW^]BU]Zh%IVZ|9ot^Ԣ=fu{Ǻe۪l1y!mۃg[] Xhheð IF]ā-kWVyadMqu$qHg ,>֢K/[ k,DfʴkcY< ˡxJR85;x/n\E#ގU񟌮ᴻG![}Bɵ0C Kat?Ejmk"4Iz3hd.sڳuOkug7jݭ7$IRR<t'sM}忉o5;KiC"q'K*uI[E ( ( ( ( (6?EKQZǼZ}kPQr$qIWzŮXZ^KxbwyY]6wU- ,$Q{lj亍70*[A:{5C=սG<HՏ` 8זS/ R^{׌$v( "v' q5uO BYcK1! TZuc 5$\zO Cr/,Hb8-+(] g$^[n5MG>@{h[9zEu:ZK<D$BSȳפ kRqu|@Əw/l{eqF3"YAr=+ k'P(5 u[xHʴR ',@q7 rkyRPz}x}P=^_A3L#`|2qASk!OdB9s\>ke|ͨNw3JM>{KY"oé|cFj/*37A-#] cwȟ#]Q1$QXmU@_~9m\h~g8򮪹njG&UkYzof,oM@{.hIkX[+tG ӯ e?MT2u/ω%ۜpڀ:K))((~}*^~} p^?}oOSumK gIHbOѺd H`֮4MRGuB5ok q$t88msm#k6Sm-)XX"9ds@m'Y.5;8Vd*P-]>V yjc 05fiePdٷ!m8 Bi.՛N_Eoy1 lK9$ssiHf]-1!dhD>¬p0Hvζѵtۻl/`0ysir:1\xwMIuyy䷴-5ly6n}4zu> C6h )>;O'8dT֫nV!4ScG,1p(:IcQ9 X7\>81yvw+ѳ7+$7CYK TOou/[Cަs}^DJ ߼ݴ NNzg~%hĿs r^,fkp/LY[Acll`1pX`pxSX_^Y@4ZKdxQg]#[܀TI.u~n5Ĥj:vr_`L`ŁpXHoIkЯ"6lH[`u趁rS} ѿT}7$5cS} ѿT}7$5cS} ѿT}7$5S_׬t !wħZOOTzX+?oVZ{صf[d[y",p M7Dx:ݮܰME1jnt%#)>ߝ- 7)~0f8mKc5\͛$α8IѮNIPʺf็TӬ5XbQUm7쀅?!97Nƪ1iZMij@V4a"Pd+ m8zjjP]FK%L`A9#>&_V3< ep0mU hVŷWwV6SDnAY3EBz ZQǰhtE|M$dSXck j^ɧAr#W, ] HN5+HON3YA;l1\q][_д0FY*2I}VmEmh$i< 9m q3Vi9GQN5%Oe8|6;'վ =~[Wl6,x>g{~5]Yc`FC>gX6kju,𝰣ddE<;vcG+ο)pyҵj eJf,?*Iy@\yf@JAqҹO]|mƥk>kI$.mXFIȮ__?zú Z-޵q+ꗆM b&-Wsu_W7w^FrH*J8  諡iڤ0{̻pei^ǕsoL]Yo?G5&,iXp} ?Z$kRrzFFQPU> Gj$ ϓ9NKh7E/5[Y3 vW|A+VO\7y qF"#?t~xKZΡyjt,UEIз Ȼ׬_Ƹ Ȼ׬_g]o/E>o/E>Q@Q@گOs>.ִ IiZ2Hmp܁A_JH ;yOk-¤pXMg)ݺFCN=Ԡ!7a>!u2F`@8{(Nkeޡirak;䲴>u_Qꨢe[<[.G~"-pI/˱iB/uKCD9xP2=>ZEZ/Y\|#t*/!.Ch`K K;2wl4rƨqSA=x5VOXȷ~f4FVY6~R1t5DcXA847i ,?7X$r$z-sQx7LP[%B-x,r`Yؖ2l=[tZitVgRZ5Uu[S}]gϦZ`m;ukKK1e% }9ۚ~xZԣkm2{y#cm r=>45X]"(IW#2~ BP(2r;PtnAK<yb t u4x:N&X18Һk G[IV1['T*(uۗb7Gs"V- 1 B..ol,^YF80$v ΃z\U[ ;_6mAM諺8#xHćk.wϿo j^%ͭ;xsF"}ӃNr|Ky?/V-(aPFQA'Od _z @u"tS"tSQEQEVGm?U@W[ߵo~֬Q@i>h[ߵPZϥZ>i>j_o~֏ZϥZEW[ߵo~֬Q@i>h[ߵPZϥZ>i>j_o~֏ZϥZEW[ߵo~֬Q@i>h[ߵPZϥZ>i>j_o~֏ZϥZEW[ߵo~֬Q@i>h[ߵPZϥZ>i>jJ 6#%Vд犩ƕ}V.nwG#k*֐\_mW[ ҿJ/-aKo2+"6C=K?4G??W^[ [:!  ?5Bىo 1d ?EKQZǼZ}kPv6y^4Ҕ!dzfavzr+QHdRA#xF17q4s<i G>sEIEqz7Dג_ݙWQ+kWvd<`~qYol`J>Pvw&x?kMz$+m"fE{XG9؞Tt]VXvE ȅYN yĖq m+wt>) W=xpiB(DukUO|  ?[^.Va`n'a,d'&;orbOI)'vZb? ok??V_kǾ?ҼYy.-,#ߩ$'Fpr ː~}2I&I 5foy5 X! }1l%ؒ2wXOһ oeH?9ҭVBΗlQV³qU?k?ПC:}U>!WN]$ȇ)CcEgB<KgB4Y^3_Fm.;٭D X07$q^gpv\ET/ЌmigvR-)l't'RnGM?Τ??g^QstO +co{k=/6*!0Nv5@?qQ>y$Ae>ݸ@rP8,|1e-CNEη7D-*a6#6;P&pncО /N&m-*+յ&(uLv̩Uf@L1;U-#L,ɧ ;x6{u^A*ŧ @fN]gZ~ӿ!MΞd>nk3m $⽮InYFHd2 =;Vf/DHѤ;湖rTyJ^yI4\9/6Q_[ qoۙ"v-T(8y#hc995 c]{7mn !] >py3N5˸\@r CKuy"gҪ/,CG<7?P_ZƐ0 e=9LV2($hO P>iWWs uW@1i-a շ_XOB%+gf[Us#=ſQXkNz,`c6F`5??U2WX$i3$}jυ:\㛫^VrE^OA$iYg,:4oE,>0XYݪe žRw_7{6kZ[QZ{rp 1#m@G Tq[KuxkE+bkmMtY-=CooU@΢D_}2D_}0 ( (3_JW_JH((((((((((((((( 52m7^u%7,9dG*<:5$s[$rH6cm>\U1I k׻y]Ζl7saX^˭ŦEaG%(HX~Te:ӻA=fbU~%1)9@[??o~?ѯ=(6-/WQKp̺1Ib29 ^XPi3:? s-??g^QstO 佑Cy(%IP=qթ'Se."sHʬp BFHy|G[ke-wM,2ƌ>xP;AqiĀ5 BO6^ZͽMKM: (;^ljOio;U5L,cEexU&ú|WN^(r1,RIoJՠ cQ ?1o;X@?_cU Z݄[pcHU6WZĉi}*;mJxw/ \wjC>Ǫ"-NK߷8WEPǔͿ$l^5b,EAe{j#Y^5&`N*x {DSh9gM |=)_cQ=WxCZFVPQEG $#>9=}+e N}2+o3:L'oAPΗz*totK<]Q\w]ҿ/P3QLQL(( VwS*;׫Pk^@?;ף;zEP^kC;z?;׫Pk^@?;ף;zEP^kC;z?;׫Pk^@?;ף;zEP^kC;z?;׫Pk^@?;ף;zEP^kC;z?;׫Pk^@?;ף;zEGEb(+PVj~Ҫzy`.'$8\6ǭXKyU'*G>!aytgmRѧږFE $ U/m>=Onɑl v&3/}4\Mkzf3 ご"o'p*սG.4k wzw<^bމu%Ч7khT\2,K1B0qWMuSA^O#2,~M =Uz.hD$R0PX>\ZK<-+Dzl^@["7~6,F ,oFO-ےW ͿgYS2Bi,av9fg nj1=KQ{%V.uvWp&oN3UNOuP3&}[-Ʃ/?i|I.=Py{vE9v<` xNVVkg+E'yV'{ Y(sqE37-+f>>SaEW" 'g)Zb? xL5{9tkB[+1xPm-ۙI'k ҫ^|CЭXaoȄ@İcRH.FO/+^ iYfK&m@l%T*He`Vkv4Mu} {it~q1B4~S1И3EŏE`"'.Dh>A㚷2sBeH?9E{W ?gt Mco4m8mQW̔eA$d)gCy]hmo5ģRlQ3pāsIn~}P6u{5Z̀͹'l(cMvI u"dQj8HկzzCe-HRGqmTc8&[K6zm6 KoR3BQ#׭j 4ttO5׬u%sZ?- [Hnf8]`r}k09/[[ٙ#A)RFs`1bTx#OX-t4oO/,H%9UEr QY_^:\G%çE&PbF9by&m&=.Kyko3o؀6 ~Edr [u[S7}c0Ř}Ni~$u>y-f(4]wz^ixn!2=Z@r隴:VdLx 9Tֶ6^csګB?.2C.G Ik~e2,s4d#c0~v!{heey p@ MQ&GDerv1ں[[?uu[Sд-Mr|1\]m Un֖}QO֡`;i.eTAcWP o o7TϪusQ+J)z?=k_"/q}*H;qnHыx8%P#Iէ[$r5R7ݽ=j>nnmCJ;"OpN Q TһwzBIfT\@utjmm궧siwVڞ1\$o' aU<8?옿֠ SaHxɌ,{jI'n6ż),H;dȚtJ5Hv2rqװ_?⭌Wq.B6"!0I; g84 :uRw2o^Zi;[YICjF8/.(Cq nߺ@#;8e͗%|$pL>]Q #+Σ ze܄=9#KEFO͌{A\Wh-g:=+cc2Qs\Vwj1xkSK_$:I3U`P,{1Czx<ȢRŤuE@Xdho\pzK> OXKĻ@MCiP\}ow^-l/-纳ne`wq@{2e܄=9-q_ lVOkx jUD$N15`F|\jr/ 1LL.<JQLPtma]HTmCXlhmBv Gq_CCt/[L>  w)TeyCA"ҡ5 TQ*@0*iuy K<*X1X/jnn9!GI\*nXIfO @ѯ^UZ"gez& L,}+lKubᶷo7#PMc[$s;m&b[GlM{{v"r:/A+u'ҋ?YKW5 |_ O^ YC\I /; [5?5WwcC/a;Rgk`@C]5B흥PTkLUnQpx$0 SK,,`Ң{hge LfϕZ7χtO?d?¸ Ȼ׬_g]o/E>o/E>Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Z}kPVUmo]M[]m?TtZTq,Gr@ѳ-8 sD꺗&Im]џ,Unv[j]չ ۬9RFAj$t։tssͺ&9N_K5wjW /nC6& 7XVG.T0!#'$յ~KM 22D~l{ Һ /Htk{7[4kDY.T # F8I4-"]JBM*P\cP0>2' jc5 5fGy|@GA˹*o߅ GX^֧ r]BD8+8Rx{ER}FMNmBB ܛT2%B>M Ig, {n,<`2sNfeEW%MjT\Z˟.hmB18@!^^w8z`P3Hb k"Tm5*UGG]Tz#_Z -s9FASZ.f5d@1$_CڝnFlgC@G(oqc3?f sv=^h / .\yik$~`YUnm.HDBQ̪

+c:y G@ orҬgzj5OI"u䷕d*3'>Կ޷ڙ=P 6j[*V$r~tLX[I  HH9 Ìq֖61,2nWм Y#r~ ڒϧEGGT޷ڏ[mF?R_ȃ+/G{=&\olq6eɽm.3IJ@-e`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEej~ҪխC>UZ@Q\T.G6\fI~"\eI,UĄ`n4uǗ=Zmg˻h9?gYY}կ[TAwQT $PN(zi6u9H,tgQw&ɜF#p2x=P{xM7PKK3GpVE*+d`dwصhOMy%u :xr&r҈R΃3.#zrot[]hM0ݮ`!dO"ܷeSRkY(9'2J(8-{WzEջ鰼jQې :Бֳk;bvS@ ǷN:ON?TgggO}8KK;U;eΓiw\i;1ۍ(g4 /|k]_m)jQvXJgErPl#81x k*K(̖hH)+݀(8W}=WWI4nb0G*v85oƯ^^Z[";4tP@@푁 \- /ՄwE6T3,`>Lg!zwgÚΤdwG <'.. nl]끚/-u95Uɏ\6S}Ϙ}zNcK[AEvZ3mě7e:5v;uQE"g[6}MbrKϐdQq\Υ$jU`3aucЗvZj_An+<@ȎWcH#{cl+NL/|;47Wl yzcqhN)`̆KHsNGjBhc~Yyf,y@bVmRc%Gu## MwuAEc%-ą Hp9=k|5 笙<a K.[U.@U*ݢ4䂠9W2m{M_ei_6)CyB~Vpq^}WͿaohwjZG_J"I$I/ʨF%ʧc#!_7$djDV- 2?F![qxH+]^iQm,(mԤLy9#'Һo [^;Fu )n}t 9$N o鲷ԗWw Tyb,!U|u$<+Ozu-KT~̱G6"d'zS\w]ҿ/Wc\w]ҿ/R[O[OEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPVG*Z??U\m.;g_ަdSeMvvp08 O<גI|A5*$@#Ӵ1d^\#Ԟ/yıCFP9@:(և }#P8рYYq.FI8$s:Z(V}^F@yxVdur|Icռ;y2E*ʬDl%`dV5`TvNw$c {ֵQk!OdBz !E$I#4^]&oy \ 鸲3)#*T++8-m$0@ct?K(us2פOt?:e)c؝ R[o!I/kZ\O417!ےA 7Pg~4u굳 Ncb eAaUY?IEPEE-0M4qvH[ImZV! . 1P[UZJHR;_(K~.݁t{]œ }n%7%@ =Yb7`62;*m'PUR]wg:A XhߎKuq|oC=A;iD~{'$8h:>rc!Z'c,-DbH;WRCg5Wݼ2˔H!FRrߜd;cʹ2hwv貑WzWZxS]7M<6QG-ŕ_I;'ze0A4Urk똈!G^3R^Fx{S%/#HGrT}sHb,ZSj2XJM$_lp~ms5 /Hk--%̲3Oaߞⱼuw^K?^["9g2DWV; ~<+=uk[he<.caF:-1iQE-?ݓ fxVD}yT;Œ|yV?|_m}kQӴ?In/Trc pG k}i}Qo:Rsn܌\xUt^Tp]rGqDd-9#:ddaI 0`s^sV%He FXnjlj&_dݮieCoW'4jZEֳX̉}kph#v4s~2l/m4l|?jw4̈G B 9 qֽ;hn"9T^q? xqyt,|Ch-y3[FuPH 0rVgg_A JhOsXzGQ?Τ??g^QstO2J(<ÍirKa|d7߶ 0i)onlkK]iXrAٴ"0~l^E$ \(sCPu7hN̉2*G ;x+\ԯӼYxn4 -Wqs+nmZxu ǵCgv?hZᠢǥj5.`p~4XR5 ,lyt"yI{b.ZWK $YqEdcn$Wm]_$ F:>,/(ƥKj%EP2Ud$<mejυ"R #6zڏ;C-GUg 5*V$V=q}, ko*\T᜷#aHY5-R卥ͨ%Fl+|Tq{K:}\6֯n1r\B $WO>?oB+icr Ȼ׬_S_υ"gZA#r2@ΞD_}2D_}0 ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (2?OUj֡G*A;W$I+?i٬mu1 ~C@QEQYޯ,]\u:nb<@Ҵ >ԲHGPN?JhP cgY@4A-縄ݮpМQJMo(HSp9o¥SBK`8h__K~z^ ]5憗Eoi,gy Kgx~m/L [em5 Vw]@Mi4R巐Ȅr#ǿZ:~?GQR`9kܒ elA)}*^3RYéi+{y4p٬P.T8$'j}R^_?]kdw%{m5eKh!ǹ=5/F#M ZS;˞[ĸdtFvǷ*}'7vvk)o&%M4}qׯH ((ͬC6tdIBH 1yƊI>#xm?be}iןxG:%obq`/Q`="<-:dY-Eϓ >V^]6 QH څ!Lkm['ZPMֵ{}6E=@8 5kZ]Mzvr_RLEEee Xds{KY>Kf1e%IkZ+_[42HZ&' zg3S/Udk:Ə]l-լsI3I|[oiI;y$pjws»|.(Z5OS&I őإ9t5 yO\v&-JMCcO4$a^&c\ڥQU :LM:~f?V_?5? WjYԴQ6$YV# 8HtUش6$-wQxeYE =GWxNkY+|naFfpC@8$TdW~/=>8L 4 lAۏjk`S~_ ͿQ5qk%Q"LW;{#-zAH K6-)louQڬ/27GS|fQ!@,d5jsgjqz[jV*r f0K8?o%]k۽ݎYI1ynT9ȯ`y47hP?BXj7QD1cbf><McC/G7Dx:( e6^<(\Aw8~kŶkroۺmŬ@`rr 9NA3Zi]ve B!ߠx6BthV,'i q?Ŝp=_ٕݹ |`sz })J%1BVD\+<AJk A$Ē4JwSKnwM_4{J|?iAg W]m6(,Ucw8 .fЬwnbbT8pH= =*KknM8&= =^MVُQO&("76Kt׵M)Z5Kd#!,9:(Myscc5I3d;8yg''Vզ4 sK%t֡MKX짒hbӭ"r>{Ðyhկ/U4m#$c pxz& WFS,H23GBUM2$Uc FP1U~AIq8E9.خp³^xsZ-];LbN\V$ztHM^Rh?K[p -c jHVوBA}A߆|Ioj:6s6'!%$RAS\{c'=+{]/t>m3.*' ʘK?M\J#< QU 2PԆs-#i H<0q=*ׄW\i:5 /d2FaH AGY楪Z%Ԃ}NEk?2H I}mjm&LME>S)vg N3B*1?A\]ҿ/Wc\w]ҿ/Pu"tS"tSQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEJV?OUiW?w9o涟ORY!]ضGG]mg-"2@U1'd'cU?6W6XA ֖E8H>թEc#IΉ]ΙZ8 `qÀHz(}ask!!'b:֬Q@V~\zT+`ʝAgg{ĸnVDcqRIqZDtAǧ4Rx,|djzw3E P+SP :O_CyŢ_]f vIJMDELQ6>Zwij?5k]IgZw_Mk}1H$10buU4P+]GFTb9_J*xsTX/feI0=E-6,2J$ƣg Ojij=jo}yw(H ((Qln,Yn#1KgAnpjePFԴPEPEP{+k@$t`j{O׺C{w8Z(^kPisiVy*{E!A97t?'xEӡGwP\ٿ>T&duیCp7.au뗺d~IՒyDm#yfR\;Wi_mӭnIJ鑚|U1j0-o2##~uEP;L!?B?I~eУH (Ι_^Y}sLƥB@?jfMƥuyXG'̰ޤ /G5ju\Xޫ5U;pG +#Md--ơ%%Kָ>|e*amvcGCNuXB6]#@,,'ϝ=G`1 \Dvn|Ɔ8ȹ`ə%IǮN`-{V屟L;|'B b ln{cb}sAKȢgU%s L?–7ޤr+J(fmq0jصJѴ!F;qEP3m7Xf 6\d.HgW-{*^ $X㯦=봞V(3A!Llg`ABҨ|}eAscxdh@]/m y2K ~S9zVWk{8Y$MX Hj X]Ⱥ?$לqg'NҚk |tO;b/#V:}k>!-ѹh#rj? CO̗0M}wt,{ۃ3Jr=~g慣7?sojj<m00GU?C&`ktT#t*a }N+5 kCо.O6q{تnj W3s]7tF}>e}{scqEv]I9YDrH ;p2%4"iYױȤ 1|1?÷W7V_]]OB\Fڀd&Wt@t@ E-E((((((((((((((((((((((#R` Np'ܚ%MJH~G/o𩨠~G/o𩨠~G/o𩨠~G/o𩨠~G/o𩨠~ =0)Lc< ]2m=O 5?*J(/T*Y$m$ZVSIXEQ[[\f6ލ6V^pz13P{sVkoUG٭義W?UjQ@-㶂o5Z Pi0  f5Mx ݿo§!T~ߦ >¦!T~ߦ >¦!T~ߦ >¦!T~ߦ >¦!T~ߦ >¦!T~ߦ >¦&H#/!8rIr@?Ij_MDuT}_&K#<]3YX+izm?5DuT&@@AMAy?Ҏ}ac`6=8,Lq4Cg xx9n"UV@&qT&略LG$O+K>7_G%w 6???kCύ;Q?/CM& (QQBeB: )h|n*a&QF~~ Ao/E>o/E>Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Z}kPVQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@gk;][Yx!@浭tOK{LJnclO~Vu7 i, xoXmtSՆ,4lV(sYz6cGi9;Zl)`#tǥ mA<-HMN,$81d|SS$ q["(S\׆t=CKY.`ʬ#YO)sP ڹ<šZg%Z2b[x82ȱ==[uݡ#s򚮬_$}aX[)vKg ~!-/4y>!-/5}ΏA![?ju"U*;I{ȸcFi~nΝ< XV9JOGؠ|C>Z_h|C>Z_k?bi'i'Ptt}΀8?'?|f.bB9!r ?Ptt}΀%QMq.p)((fotoxx-18.01.1/images/print1.jpg0000644000175000017500000006351713222767271015062 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231%Ơ0100Fotoxx:resize|sharpen| Fotoxx:paint|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 226 435 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?@q!D;@2j/oιi"n9&dKGRRg/oΏ:_ߝr~SGRQ7u/EK=Oo_?<utѿ:<~uɘ[jZ>z<1.A<;]61k۟ƋK=Ηz7LK=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=Q@K=|Z֡wv|Q+JX"5( 52ٌVӹq԰*2Ηz7G/oο>.:xKk;q,do` ?9xk+ tѿ:<~u.?_ z>7xK-!i!{lYA gY_x 7f~yFu,lv߽~^%Go~(3Q֭3^iT+.U~0tjYx\A;ѨPg+>9?|xvSҨ|q[Eͧ_\yözKjMY8i R7NF_ҽojGכxۭ>W5?p4+ou WѶÎ&Gkڭ5;2-R}RKEK@VQ4X)n} Ϳ,- ћXԼU{ᛋ6X;ĩ\睯1_ K ?`Ty,@Д/ 04OzZjk^#[_/>m+"6⻰ lo-i|]yg;u^0QSs6V'YI,@3_>"XxKgmt%]R-&gyV䫬[JĮ]kCg[J}R Zd9c L)"?s3'-I5;K(ucEJ|9?5I[X\N(XXk7֗|դ|d^\N[1.?> >r8#ד^+j +@UjiE+dq"Ip7o FjkW#>[ibVBOwZ^[Z\$F'h-rx-j?ɭφ5;{&)6xqbu!r5 d(mx/J&06bVi!JSNH^k۾3~xM<= gmF.%I$9%9i$+CoWY?OcԧkkoZ*"mG]` {oG^/@Ŗ^i\\j,j!6мp2$w_&Pӭ?i.–ܱis1g!V$CsҶ|m'?ƺ-R=[NеFBF m S_J{y~ 7?jrYxb-o1bX `JP5ڇ:ׅ?gZ{-GGB}I`hcwMYxV 䟮*ݹ|/!3((((((.k\yF~,UF5} +ԫ6|O;SC>Cj>"XiimuFP\F G#y 3YuwNJԿ%#,Kn6=1\{CL,[]..ۓf<:+~'Zkֿ4T5=ZcnY%T0 _EW IԋZ'fzkRBJܩmkg]s:mơx+յ=& 5{ Q&($?-.9UVFoZۣ @tA A x8j5 ʚwZ_iy(nTʔ,U 5\5ޭ~Vĺ[yQDZr#4Qxu?zR~}o}4aIEknj(Я(*.Q^[ G1G%?WSԨ- ŠB+c ) TWQC E?!Q G1E*+(B? ŠBz|P_QOTQC E?!Qp=JJ>(Я(*?(B?Eyo%?WS|P_QOT\R+c ) J>(Я(*.Q^[ G1G%?WSԨ- ŠB+c ) TWQC E?!Q G1E*+(B? ŠBz|P_QOTQC E?!Qp=JJ>(Я(*?(B?Eyo%?WS|P_QOT\R+c ) J>(Я(*.Wx)q=jSk6RJ<6ZGf$OJ(B? ŠB8v)8߱Z4$E?Sk?^$ ğ6o+c ) J>(Я(*}p ğ6o;xQC E?!Q G1G/~Ka //ჾ׉?l^> Aخ^2fn=O G1G%?WSgSJ]5J\ЂO*+/h#3:NTyc23>mE QEQEQEQEQX1g<#_0[[爑cf +:-"b@mB˽Ĥs/(<9}Qpߜ `9ϥ? ;*N>_.?}?<] /Q\O~KcgyBA S\lg [nO8 3Ŀ_b֟ N,%f=7 53^1cSKMw&aٗcs(ipTֺ_?}?<] /QX5|Gi7ZpVyo+!5ϧG U?}?<]:yEQ#ϧG U?}?<]:yEQ#ϧG U?}?<]:yEQ#ϧG U?}?<]:yEQ#ϧG U?}?<]:yEQ#ϧG U?}?<]:yEQ#ϧG U?}?<]:yEQ#ϧG V=嵽R[F;N&v (+ |2I7KqW+a#r\=_^C\~Tw+RC)hXGBuKz/t&)]՘xwCs3WK^ZZQCck%>xz9ti,ҙ|eeژxK𝟍*ŖfkVxf1C"rO5Km{!մ~9V?HCLK PX.K\jf?O5i^LSK95;WӰ%P`u!I/|Aq֮ggiRM)TI|Vz _\å|MK[/_ȶKdÍϖPJ${7H]k qZjmAHeEbZ5$ jS}ZLk=Z!!_ιxڌ#gb68[";%<2" ;~V7>x>?@oķ~"V`!-BѺ^Ҿ񽷆n'~|cg_xnyoodpݒ8{W|O+"G喯Mnfmt haqqVպ▱* xB5|@mۥű؅r+zZto~ 'B=VW:G٧eo<~N3Չ}> |& i^hft{o&;TٴK\>ϥ|Wּ}xS:vn4MZAef36[F t&IۯDp?LڔKUX_lFKvD2q@'8#ׂ>!x)뚶xRldӤVa'Y$w5.K)5J䱗δ}N+m'2)px|CLV6 wV`7G"8 ԫeWO^|'xĺ?oPP&we#T_.2 EX2@.'uu++1A& (XҾt 6Zn&05X ܹAV x5Rc$Λxtth4#f!9&y&4k}ϥ}ύ'-K¿ϧj?do.5-#; ̲yL㬛 =yxG8|sއ0j'Pi'A-+o Gp쭌sSil<3Cխ>۫jK[o—.'Wi-у~Լ%[F+8pd"d2O@eeTYw`r4;ḳ̪ -yyWyy ^eeUhhיGUw7QU]-yyWyy ^eeUhhיGUw7QW) X7,/OU?<OEt7RoB; |vĿZZ}Eggd?nXOmXi57̺xFlɫEPTtM;Rb ǩF#*NOJET-n[-FQK{Hz`AxOCNO=5آZFb|բ1MzkG5F'<L "$Cn #DKw~oog?d3~%tkZ2Fudedy|1Uᤶj(:sŷ)*}A&I)ƫlV/^x^ #bHt"E1 j>9~:dW1"ҷe^g9ӆ->\hۛK#WHm)c?RyZTTK[۟I4 p+l(n˿F. o(մxrS˫VWzG6]J`qڼW/LJm|o}4Ziw:C^ZD,$D_g%k5xzt~sTmz8*mk 3Ͷw=.Qnl7LP: J֖N)|qWa&_%H}fym<7;wmg|OZ >,mY ,Sf*,K.>F_9*컳_ٱQϦ9 \Mc'$>q֗Xvw8 &񞙧Z!_yG$vF@hxg[k[>^ a^-ªUJ~{-ORE6lYߦfffv%zj~{':MedmCjw-2>QCy|G h%é%֯g{wxLmHlk❎4_~i-cuрKrQbP# )A#y㱰i:fX7O1X<|y-lF{֖}n{ڋ}kpۄ-~S<}޸oX? /# %cRz~G}c@1=_&5/G>}޸oX? /# %cRz~G}c@1=_&5/G>}޸oX? /# %cRz~G}c@1=_&5/G>}޸oX? /# %cRz.g}/-]my6Ei&HEP>Ѡ'$p3^@Q@WY<)x3Gņ}+,E{aiCnX+ua@zE^sj/Ҿ 9}Mh\]UݤsF>8 YA[]Eeqq:B FPSvF GwTWezjxʹ>UVbo s5R?Sc% ,gXfF;Xp =4 kN+-x;h,R۔#98k]h3ww7HCG;pF3Ҏ=>?UCٍBomm7Xw=S?-pEV xyb;6V9誣$B7xģL "Os:O hz<5s%iŨ^FaA>Ai.]mKUrlgi*<ejQ\[lQ'\i xM fm$%0e*PHCmUfOM&йw~-W˿ >t]K6uI<;hC*Ϊ͞o&/lRY7EI(H+Xij.]mKUrlExĺELgA$24me=~"J6WKUrlZ gןRB?_.6կ?G Z g\??VҀ*ij.]mKUrlZyJйw~-W˿ kQ(B?_.6կ?G Z g\??VҀ*ij.]mKUrlZyJйw~-W˿ kQ(B?_.6կ?G Z g\??VҀ*ij.]mKUrlZyJйw~-W˿ kQ(B?_.6ߊ4 ٭ޱV"'HP} ýgA7w7֐EA&z11wE,ՎYϿGT? #/^Ԭnn{E(#iZ3r<0[cI rg|Mi /|'>!-eXQR0gx9ZJ >&u.U (eT9I08e<i?i5c:ĽsP3VᵹUz>fїn~9_X_jmx=N[׆z-<$L v1J<׿3sOgci|TZ,,Fȭ[YTJʆ` xwEޗ ziڝ펝\.c6A#Kgm \nn}3EJ)(>񧊾]xCIkJ]})+xvFx]ɖ`NN3d_ |M4VuǨj7H;IQeʀŏ}%E4M_qOJ7r?VR}~ϵ[gZC8o;"[I$2PHyuŗ{xWڟfEqZ\Z--'iyQ1#'<}#Hxn q`q&/T/H? x?Yuj=g|QiFĘYH獣c,z+O7;[Ğ0-$ro^[&/T/H&/T/H\GoO Iw6;(p=/Gqo7 zEo7 zE!Gi}%e}%ez\wMB^v_GMB^v_@Gi}%e}%ez\wMB^v_GMB^v_@Gi}%e}%ez\wMB^v_GMB^v_@Gi}%e}%ez\wMB^v_GMB^v_@Gi}%e}%ez\wMB^v_GMB^v_@Gi}%e}%ez\wMB^v_GMB^v_@Gi}%e}%e=J~{V0jz]M ŭIA(',x+~tmj28E/enH"?u8}%e}%e+5kt뚦do4&_9aHs߾+0(((((((*:U߉= I[Cysh)Y6'R`g?)>koxj5ղ%vN1ڼ_w?~?LMZ-B$5I _= $i`ֺuCLrNBwӛɯǶ#~4m_6vo+s@< gyhGXow5 OM]Cbg6>21 p$/|YFJm9ˮ:'Phfo +$E#M/2KkC{}ihФut#{Uvy$Ʃx'g"CD,7'#rsן|m|WXBVմKMA +4P>ؤb'H$o3k&DX(]0"< g5VߧyOoZ_ntkw5m')"|gk28Z?uoxfkzT8kxdm{h8;B Eh+Zc|?j%ܮ𶸽SH7yqF8%FBwIW0R4Zεi6wb}GJdK00u OZׯ[|h^D-u -WLcbi 02\eH5;iVsHs$%A5V?K袊EQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@TW7P@M+U-CHZm/RԢSuA%Iuͥ㘭UF p=(Ts2M41w*I= (;cXP"0ee# :TizDSZYGn{1b$dz@SQE 2 N(  6޵eyAsnq/is\Oz:΅a 9RS5@Q@Q@Q@xƻ;O|Hku_ _]\-uu{B1=zWV/t}_M ::0!fRv"ahOh:_~<|9¶ioE&&0CGz<;#Oৎgu4[^OOߊv\kZXj~Ԡ 㷷m&XY`)WG= #s^0kox U%ebxUF; 9r{Oz5 )Y5V YsClR-OA^MOYt*}ٚ)g0Q,g1m[E9j_p՛GΗ5!>~\xsZ:o ˺G ƈ2BxRռi]m/7~ѮNyakqhY^-ʹc|W1:}5vi7slR+E/ Ux \z)XӯcmjG/13eepU6ߝēI/%a{G1n,h6^)BXA,ؙ (NsU߈Je׊}=> \]eXĖH ]E\`jgxxMUb,o]/+pg4eFj|z4:R钮"X>nMɔ3$yľgq+z_5-Z|Aq%1ɀ;y'<`C?moh'+/pZi1ب8(!G`+zfgkn1i%ŀlc̞yvB3H^,ݲӾ֬|I]Zs=ynۢ-JpFFGHOo¿i+Z1iᴅ[\C.F+'LҼa^MzIkgma]%<(/ArP.3szmgźZ#u5* j!c<BG׬VO˴l*C# 0 AI%Zt(-Z*UKMrm2&v0b4Yv <׋.]ur]jz4uy݂FFב@ 8sAH/_{M> m.|1\ͨKr?6YC$cFa` .;&ߵ;5[eImIDaR.zHft^ T|kTֵ hu{xg.-mUO g cThoCC˕3oxc1}K. WSZҧm#]J+{]Ue%Ɂ#crpzZ{6im9ge֯m2&0- qvw}r]D8ZC+( SOm$,Q(.Չ$rsRRQE?3$7ֶ2-E~~ƻH"HQhA\keōbKykЏpyYb揲M+X*^̋*oRm!\E@ X0qz< M.E慥XφfҮ.-FjTSbR#Ez&m|i gI6n“4r^Uf q(ğ__D r/#qaL%@ךD#F #['*/'ƏWF.m)5 m* ry~)φ{⟇V״~M?_=2'<wmo5 ߴ\'ٯXuZ/nl+GxLk _wžsCA֭I"0\<^bXU c΃Yx~naӌW<2ĥQ|AX6NA]]W>,|UBjZVhkm"+;S5-dHkJQTg'}F ᛉQiCP\+ +״gzU6+#_\Kkd҂$kkgnX GBj_z!wt(xsqqW~%x⏃|U}-wq #O$ElETHKW鿳tS!.R6jWWē4Ȥ"QB_RlZjWVv"o&TS9_p0oA%|sj'ּ'g_{A6Ɨp..nV@RC^(.xE7áj\(#p8,8*Aj=|?F}o"%WYy*5H!Pa~4|5~4[\ڴ7O$;@);ai_3> xK|.(Vխ/k%FbE )+ V#ۿv~-zμdmV naDHn \~óhؒKk6R޵p3 3gGlN>m"N\MYbW2`K|cBKMiDwI($Ox<]'Ӽ9"No.f -US}ωo5!&u.6mIB]Nn*g_xQo5鵕j0@+ۤT.v]UPA{[_|`CNam7 D;N 9 ;{m<2᧾乏ZKmY(Nl&F8*Dwu(,(((((((((}OSUF K\4өxV( IŒ~SYZh-V%Zdf"3ԧT4W?r?<R?[=\6b8;@ˌJw/ &Gt ;lM^+U'Ds}{⧂-Z~Bbq% c>V(OC㡣_|+xCP.NXw6dT 9uWx/߅3WCTMwvZ6ij }!ux+xI /'+ mpyNIoQ\͏jZ֝iPY^ Jĥ6T!>SxwYCu$nOԡe2QYHl`zC:j+? SӴ?hΡyi<͒6Ȉġ# ({*<0(ZG' z@jwuXhnZm2Bң3Е)A@TEbZƅ[TIԤW02U$A\2*s]f];ĺodUP$AT{ ?-?tP? ?ڗl_߄Q_#8~'xOGE5] "[=96oܬPP,#,0A S7uGH<(A7m&M;GGç4]v,u 3ndѝ<Ț"<8!we_a7ewXZ+|ھx|L2\j6CTgc2<ɿ1I`kI Ñ&u}R=41y!x6lg=il+hWLk7AL |J/|*MOxCյ\ ҴEā*Qѷcu=3ㆃq]WZ4,|7H!`ֺ ⧂O7ZJ N LDx)uQ\|ao8Y|WV6bN nj<oZBKmRA4I#ʻX OEs:.×%DΥ vrffniz[ju `dT,d{a--okk ^kӽsǰ #=ppkѼY>lZC QHvK 4mx+W|gZJ(|iy(V2Lpqސώcߍ:(~0Zɦk>Di1r : }; ?;:lM>;KZFC$YԎ6En7/o%%U^ צi~K7^&ڇRxY,|\։G#$1Nk*?$!k:97in .i2Me*C gf _ 9㟁4 ;Phob[Ihw=&3;Wմw E*p G#*7?v -+N-mQ(¨,IMN[~kு1*Z4XKfO*w)`ѸPX+J(rsǰN [FIeuozbAuUv kIe٭յrBvÏd`r{rg{-ًF\H za,M.OiK59P{<JTuk ^dVmZGmgv,q(U+tb;?}%AwGgO^ 4'oS>Fѿ] O.}}@çͿX zGQg W:'_TMM 6oG?8i|Cx>4 sĚeƍZe{ bHdBqJ5nyޛjEżUT/9$4/†|+ִQb[WC1b ?z|75]]NO,so,rn#O刕x[o8#Z,Bڌte2ecoo E6XV>:|}-7v'+9t/KM^*@g-B i1ԟ@𞮭}4! iɳWJ~o:4a&ITPՄC6׾7ѵr^&4 4_@7ZJ`7L+F2E}gJn-5%j   )t [O O/ %𾵧]gsF1WU2u9ˑ_|0mEtjFAbIc;1;m3;F2q־d{~'ȗZo4ݜiZ,n&nH"Q3IbĚMxCS=4M&9dUc+?3oZgbVWˬxBay:WD HA+TNyijzIO8|r8I[ 'z_Ƒ:;hחqdk9.-v{iO}yy OKp7,1ƑB8M5wwQE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@fotoxx-18.01.1/images/unbend vert curved.png0000644000175000017500000001762013222767271017330 0ustar micomicoPNG  IHDR]ٹ.IDATx]mfWUwXFNK "Em1D-hbli&`S5FԀ1R,Ж(UDP hhL&MʏB2g:23zZZk3*{쵞g9=vvYY oGO>zq4T777/:y-Y{+.SNwlg2XݻwmݻxN>n|neK.{-7?t;u9I; llllx/{+{Yض9=󖷾w[k_{~|1,5؎}nW`}}}*^rzNl-h@sxrl5)%PTB].1<{ۻG^*䰶ԾooEw[_%Ծxv1c/m[FvדX|6Rn:䀬)38_){'&b G738z۟r#ЗtصG~;o.xYil0D̜2 +m~lzq[O+j XpM^,IῈ`>Gm5{>rk_4K@̜[1ɐ;:oDhΕ"+Qp8n>ҋgD4=&\DqC/o~QT`:a^vOqsld351mc>aԬkڦ>2%G6]Ұ|lڍ:G8Ђ6]iYr孳1w,IJN 7,H(W^20daSs4 t6M9_1bLkr@ ||p8ǜ 5N2RԀXǝ\E=@jy].Nu~Vg|Sa:CEo yŘ= BI:q ЙyYZeFAV>Z|[5#;EVQgZm3+- X| 5XQ( rT 6<"qL ⵾UBO<"v2NQ{#S?g܁b+`}zL]Qu+"\~`de0,\yq5ɳd6bG-usSc>0}1 2&V'p 1,/&IGrd|T琻`lX=lAᝁC?V<#ķJfݿI9SE N0fDMtvB]$uK5O- AFȝEsX̯:=ưnjۘPF%upl'MQ#R\ uEW5%%lNn7SpɑM ('!(^!>)TG$O|nQԀY9B1a6HhkPM1$⧪ .fG"%b^!gm$s#GJ̯İE z<ߑ&ʋa>t%s.st@1{4 7b)K84䅯T3բ]+ڕB&Bj1PjϐGQp `$7K3u8r7LSbs噭OK6Ug? L6QWŕO~B( 9q"蕨-| D ʨMfZd[ɰ'^N7dK q7=wke;h>Xd8Q0 ڞ: ^Ύ 6qq=_H ͟#k>bIN,-:%ƘuFa.¨Jk3N?Jňq$a]8비\ \oAEVMJ͌~IviBɢ5dt+k̍}UQf6KE89x.d_D hW;Js+oFPYT5OB7<+q-Z#|Ĩ7e:"6×d涘ڨVy a!l~%&[ bp]UZnyݣ:`Þ8Z<Ӓs4Yڑ19YZ+3岯>)4:|ڴLj\uŜ@KzIaJ$ӢN"}jWV55'#U/uNǾ"q@ Yp 32^G3;;|Pag^.ٶ0(9F@e(v]cծbsG>TjC E£╯@|OU|jx>m4=-A sX!T*NҍF")ia5@k@eR1MayQWg+ՀsV4Q/2HR"/j_1ηz*SA#(2GPm)LqP)%j淏1r]wdq2=>c}#6Dͣ3+%=Q2p`Z~;"i^21i[7p"| Yev wBѨ>MkS PU(v1K4M*5"E/&=o0sَP0 8+'lr2OKkiu!i؜=5!NGeuS>+[vj;Q&U!càƸ* ͣ.;dX,uPT >5s8IζG݅k,pk\zwZB?zU;7eª@@0PF*Ɉ!Qgkaωj_ro)?O)S..˓ٷb}`0d޴F4Y9I%3D2g&7Lj1v<=Qq9!}c`qK :ՇOH U Ͱ"OER Ҏ 2:ᐫuy^ӭ|KOM#CĽPlUcޖj2-_hۆh 3NVHî>F=RhQo 5tF-!^*6TH1iE#8Ѓ#C펨YbVj0L!sP?*#PEB[g2k 8 )ƫM qN -of {V cڣGi=kcSx˴V.S|8s6b*ס ~ۼ}ƷXmT%pG3Xro)NkȨY}|y'm߆y-_QtTkXJk.7 Ljj ?2&#0!.59+A0aTcӀ>HkatDQ䭒˦8o~-\Y|lw3 Pm&.ꕴ2Q$ %[ 58Nj~#"4).Ig`0ɱk@Da"+Գ:w.euQM׷'MIy&Ѱ#FaIzH!QrD=q!h5 /[C')i|PR8fU^Q_@.dĈW$ɓF{Mjב.^ϣ>^cyN^/_}0!YL֤9I_si639NqXXUiUk.oKpKYb}p$Α9|L\Lh$#8%2SC JOiœzѱ&YY]aF' (C lz%P [cعOiHp['^5jd!yMȃ ƀȋ,D,8K/X\an`rrx 9|}?;vPiC;..$tD\>*)ybʚ/4H &b? n׷9%jʡ1dh+@:9YSkkO>ue"~ƻ_s+~pŴ m{|eY.?~đDv]ŧ<۟{Ͽ*={ۿ߅VqXk\0O:}/>MGWoWH=/E׾bcccscAa*i 7XzGOȋQ}{V;C=k%?#L׮ {26ɓ_$bԧwnȟb; |sov|<寸xӛpϻpYYYYYQqFzTXtRaw profile type APP1xePQ =Y],۲ ڴKEwoYhn@`$̠s!iDlF~;8"xt5CO}]x]!c5:"׆B+1g8;=K8c4$*Wؕ5*) $Q3WlZ}/]'|:iTXtXML:com.adobe.xmp 128 128 IENDB`fotoxx-18.01.1/images/tools.png0000644000175000017500000001274613222767271015007 0ustar micomicoPNG  IHDR@@iqIDATxZ xTU>^Ie_ YX d !{B@ADulGnuf[Dn[ݶ:PLHXCXC![%J%朗JBHf}}{9=] vmn#pFf8G(1 3fysRjYtOLpƽնoz3 y6/b٬f 1 _xI(((N_j;,-AnQ;T/*XSDHd?'U6yLLc_@uuMlDH7̎/ܣx[o[(.>\~!\`}@x_V=6 'vIH03+ )8[',X_ʫ/%ԋgۭ]Q12/8o)iBLlD).͚38C3w>坍o[q˺jމ ĸ9uE7cJP+TAb&$'p/o/ywnkkk9~x 7hht ƆE'W9 dRFm@߇Z]T"9u ﷜?FBH~~AS~.dX3߼K@l H"**j2+mcپ._.Cɧ---׋-f낼C% ú^zBsY1/gCCBHuU$%RqI}M&=3g@Z^fkw!C"%>:6%`h;R!'稥f0g۶8eɯt jYEnn R .IY*Ieۭ%:GӦSw:9{w%^m۶÷@Kj$q Olg!}w tveeԹuB| i{PxjS[S'eYvro!e oG^#=H^]K37dv.&\yE)ZcPX ᑾ}$l6.c9C1hdKR 8'  yi!Jx{wx8 ?)$jjݓ]F(_oB ~b?a|]]]Y$<È'L`wx޾camwfqgعkMM$A#޽KM q:5nӦqVe 2 ѩ\ tn0͡/+g OqS;|~Ʒzx8ꆫA4i"SSwLh^FD2=BMu\'wPWS(--_:"#0fLKY``޴|Dja2~7=5?WR&p]b;dP7#Y{VYd1L %ڵt۞8q2h/_3mZTt|ԄT̛7o'E 1rkLXnТVHO+`/)apiXYXqr5k&dgg3g -=Z3/r8f͒J4L )<GF5 Wn{HY& ς46BlqQH:A,I$eNjN >zPzMD^N#O^ f9ўޙ=&ݕ!3r| 8i1;҃q?7 >ݱ bqBĬGq (&-M",;sׄԄF:Ej+hiC|Xf)+i0h?Tb(qV2{ϜCn^a4"6D9{&B~A.[,}ZSY0b'J:Eb< gюur <(J%gҞ(> z@ @ Lܼlxfs&\T1j JpOF H<%qbR"o@ɡ!ŵu p4zk^`y$&AИ8ׯ5Q޳# ` bT]o>21xgw5…&pAР&9-0Ǝ Bs̚˕#-EޙvSy(Gr8z"s j`j49c:M&q*D̂A.WL sH|L14j`qݰu g<6?%bju4,:~* s]b,'o`hlv<%X `0;dUjms2DQwO%>> dAo0V-Zd4ԅ7!Yvk'-ćȊ x)Ba޼򄌌_➘YsjðPy1< ?xD58I*I3uw DYaV-

Ys53oo۱KH.N~ZwoJE Cb{8;lَ}mנBI0AVA]- NԘlu#M/'T-vZeK;}BIpqLx|3bAU==o%u9&/Ep;5ߓ< TZWy<-i=FS'@ܥdv6eہ9#W7X?l;3I9JB9lC`} qluC(25-S $:ϡ־uⰱL)y:P iv Fm#Fz6dvbxb=p5!C)GlZuԯ:FdhF&-!^U! + o#uOMj.MQW* KQ><W{3ӠFDW5M~ O4Wn(R:}Y{xIE?2y+d Pv&DJ#[nd'JP}ggm è'JG;fGG}}HD[O.:]͎,NѫУcA 0#B=6+ux 5T*6XFL@6xm7bQRTmYC"Fav^ Άpwґl,t4)BmGPp: O:%H)+(uuAcǁ_t.Ѽ_^vn}nNZd)W'\Wi`#{'}!TJ|߿p-ko}ߩϟ LbQiꃃ{{{z)Sԅ~nR,-e:z /wu>QM)Wh`xd[֎Dggf^exxd2k+rogfzL&2TM}뺁fBMJH)q)TgeXlyrrgyoJ%gŕ+SbӦ?O?GlcipI* j|>OzaQ/BHOttZFP ϲˋT*b(#M٫9r!y.&0Mp8D<%HLuٙ3E3I4r,GDcф^Poܰʝ۶?=y-^L?W\N&QogIR(zrl.Kf92|-[u|/  4!^opE2 aP.$@|-!x@k!uh4p(D6cr 0W.Oi=z,ۼu˲>^Vo^t>|x-lxH\6 KKr98X58(z݃!%R\7[)%B 4M'}Z>?Xfp8eed2* RFQc`hs \˄–w𾇿O:|:Rjݺ[Pf^EHA\ctt8}4O>$Hb!"w}RH)!_Kt]#N/!@Y) 4M`< ]07RM7K&8Cb-td?G>WJ^†b4)R)S,O~7ٺu+>?γ~p8L6AJIZ!JaYJT<(cccBJrY&&.Y"`iiJB>cM%\ץZQ*ƚ5c+\bMtwuqYb(kh/{~OBMRJ:\Qy{ 122뺜9sFr4 .hMh4_W;;(]]]o}l6c7Y\\` zg(|_@|_!$Hi&nݺʊ :ܵӧN395úu <|_""4 ];NT$R(m^'m W:yT {! >K! $0x |\ElOqIa׮]<={!4}y~MƄ!т<g=9qE6oބFA*bfi4$ .\=%SըT>}1B ܹbO͛yKLNNreQ(q0tww34<(XEX`U(C!x+/322JgAJɽ{׮6lP]ĈBobph;wzWy7r 'e%Ν?C#],H$oNZE)Ji.>͆mLMMQ*'\p^xp,F) &ab&eBb1پ};izzz0!(@ IZ'#<Iu8qb~% PôHъ)Fc_+C|011A4~CP?W_.\8Ilwri c\x?0??iu&O`MmS,T̰n:xLD, )+沘RRjĢ1Z|҄G"1))P-N_cjj )%h~_`F˿R^P)%CCD"QT}=:w_q\zcppD"A8^@)8V,//S(i6:}l6Ýَa+{xSרkw4Mb_M7<뺮Yե['8\qlסX,<ɓ'(+n߾\ n߇kੂ|Ӝ>})zz맿p8i躁ah2) Di7es377p~W? Bpa޽W, 4#ݟs: ]MZE<4M&q]JL:$IX޸a}+H[o+Wҏ^bdxdFA,#O`YDT*E<88zjJTRR1tH$L6ennz/)Jkʇ>b2n.4MAӅBub]] 088fET%%s1vۊ٫Rgnn1VdY2 BL6"33 4Mӟҥ 6l؀i޵;9z-~rjf]}ſ:Ѫe9mJ Jb+.b:5ݻÇ r׾UΝ;F|%iɭgyrY|=J%ZZK/'N|={BGr{T&X~=LMONWBJՄ@ɖ@>o61-|0J8CVcqi\x Mذa#l'Kf^E)H$t: Ɔ(wxp-}&}QAg"JWWX^^駟ĉ2>~+TGi&l͛F"1 %K 2Xyt5q\\[H&,..122JR _t$ B0QCy8m :CRo2o<¶N2Bu4K:8KRoYr{}li&TA Y ρW=aIQ({xT^˗'PJ1ɚ)Hc]p+'|v 8BfRTˌ GD"LOM#Q]oT@R7;ƀ |Vh == 9{u]  2u )}5a ‰F@3S7`wa2M\si"I|ߣ\E3uVVdzJV)Q~T BH֭P( $H~|tpE fR OPk:t'L¡JP.yWH$jnn~ls@VKRʗ-o+n[>_zu8zh&J)d2B!,-&&[&p$xp]dzQr0٦ATm}(&4 u9H) yx{^p}?l6}Kfn+BFFb7Tkx~@}'LrN p9V:Rl:yB,pe[~##Hg]蚎fI&! RRfddRhLOOٳHTG[v"D?]]]4m mg:L\s*KoLO7^|>_:u]3B"бh4J:l9V شqTK~6W_ [9W[]g?xǧ>i^x;wz@;[mQRJ"[la]XIwwz۷置ًs~ؑS|!u|w5M[|)Μ90qT^k׿pp(|i7/,-\~ t]~ip`hTvt$dlWLy.Z:JTQoѱ^/,-- Fܸq۷o˗BFAR!NcF~gRC2R`&CCCxGGG-[+WO?sq `7iCDcW<} ~z2 Z]7_x'&&cA1Gu4'Y^2sjA,h!::R*uC{{W_vXRDϵ6]Px`pp:007 S4r>tʕ>|Q_$((w| :P([v|}ݷo˖-;LJH$I!B)DZ\.NMM&&&߿gϼd\70 H$q2cJjPJH7~f[u-39Bk. rTT뵩t:]t]w۟5jqiOО";o=z.6Sky۱zwǻio/hUIENDB`fotoxx-18.01.1/images/netmap-locs2.jpg0000644000175000017500000003674613222767271016155 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 243 222 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{׹~>K+M7ƾ u8tuDVK[Ѻ+5J7Wi[s>}A# "ſ-oǷZpxo>3On-Aƻ%;sPo^/M%o="{ _LB$JG;"a[n+_z~}'?DWGSJ]ºm͢_hW:w,8I6 3 p/x_hzu|u5R%m,+?%ڥG1C\~WW>laOl>|IuCy{;z5ޱ3nNZm3m%[.Xtަx7Z48.oj~ _̷=SH8rH=:${Lk[y4y(aOu_Wηgcuɯ!$u]q")bQм?%hSBKpHR^L(ےO^U˯Bm'l>|?^ C]xUx[@K-<|957lגDj]ͬY;d.WL3+3SmGN_xébH)c=] >Ȗ3TСXdF6y>İI1|W65;L׬t䶆Y$YLUے@T᭼6.el>|?ꗿ <#}CQx]wTmBTżJB{8g4n絹6vet|Z vmoq:4ǝfc$βx^H%<-:mbp>As<05̟r" pOsK|oAiCy]5o·enoHT2"aެI[54)7Ι}4_ۄ+ć H=GY>]DB/$I<ZW:/lqB dp=JX -x[.N?f9 P^W/Ld[ a,^(xt9i}9^ߕ]>*cVڍvv%thZ^A  2MYFu3^ (Ԏ\^XJx'>}k9>%P]ZS:ߋl+i>'u?ot<ﺸ]Bp8lv=|oG6|FF=|oGW>+?ʚ)outmmn#AY"1/l4?㇍~"q,"ܭ(CDc1A ykat\~= }#8v}='?t#o|KmJ]C:%$e}ўVTu[D|DoIHbEch4 Dp L5Ql ozG;gߋ=;1]?|mxS cu0Ա89+KO"im#[nye9%Jc/18+ߋt\~=.],;s<X2xjVOm1B9Ȫn ;㷏4{U֭ Vhdv-  *f?fo"#A&ӌa_ö|m}#?>##[q}#f??tmj,,JSk}CO}@_mJr2AwZc͓ (G׼X('T߆foe#י0ɪvW{ 0)JY;>\]Osq'q3dE~Ἱ?:wjފcT<9U$7vgzY#:@mn|A;xsP-<9y:> R[ݴ)s C+HX6 ׭Ҭ'|||nW?-,<>"r{j1 :FrqKZMd@lG8[*&iY bQKgI+ rbQ@4{-,H gJK] Hlg-ZERűl2}v7:(lo.$HsF=*|NѬuO.6  xT{FxOxUʁ3B Z@IR'=.ʊ3KG[^3,|@~Ak7n$ YA۴H&KG[Y_7í?$:IOto|u Lfs31m ETto=b(G>-խn't-fv}&ؤ&A;lPɰ;m<gO"e-2oxo}v%6cm <ٍU6~_q8E^·GF ݛ'/w)?aER(((((((( Y QWDoׄ,ׅ[;?ǥQGc7U𾍣Z7-^h'G4!Q@\p8?ti=huZK ,f[/[]3i串1uPs(MK?%Aٯ_kWx{nim.<%jhX?gGŏ>}vg^ ?ek:H[asl 9>?U k?t-'\iSarʪ7''ɩ,GOmi]:;pC1$rI&uj U|ASZ7l47N]/G0Kq-[t2F@KG f]'7ğ+ƚ\Ißخy,4@nX"O.^`2!NW^4 sQu)RQ?رƗmf|^1vMۅ+;/a|<#S7Gٵ7@Уơ0,/?)Z|~Zz7?ܗmּA]^n5NJdVN2Dؠc r{W=It-2!)o KT* ZJ״voo{p*J ( ( ( ( ( (0<"7?k­GU??5V#t*?sTQ|**X/Gn|_G{] C<k6ԥO[O/ĺ%L]ˤeZ=̱:gZ?z*tڟ 15KtI] q{4-YWߋL|D(g׮.1KH!5oYMUa{{ci%kk ؼ+>Ƕ \<׿͗w^/iSW|98m4j_\C ֓3L7by\αhzlKpf i%6;I$g$жӭl渚%mIaZV2u8iZX)QEQEQEQEQEQEQE`xDoׄ,͚4fɅ\88_IDoׄ,1ןh%Sd6A+7TziWmx Ǩ2}KG[Lokcpڝ؆^P7e`G(l'~n(NUQu~imT~ ie+(FK^HDAAxD@Q|NJt]{_ i<jWS"Or[Ynôm>HRJ}>DI5[ZDvm˴#misHĒmS`rMm]D{v?<|yeE~ oCӯ{G\!QI 6wLm-#[ };S;ſ:&Nݣ* kj*[~QH(((((((#x'f!c_vDoׄ,_1ןiɑ,?(T1KG[_7$ox=db4H[yLJ b<4U',ǫx'^е{9J,$ K(DzN t*( uiY(yz،o[2\8|{m\ =S_Hssff-"Ti.1sjfV-L/+Ķn o((uOip5|CtrE3j^2{@GǽxiDچ.Ԉ0&;K-a´sj.Mx+W[⹸$Ī;eLO)W/a2i\Av6 [C\W\ׇAxC4/Ycca=hѨV:w5;"2VE_@tn,ȗyfKhpxƺugNcsꤘIvE.nc|2ٙ$>q*|ƗTӮ`AnlžJj*]{k_=¥綵=OP-~!ƟÚ=.k UYef]rW z.4m<}b6'*+_ 4? #}B-B8em%6 We.~i//<յ+9ᵨ5;m7 .:>!j𯏵Kx.UՌKx4ۄ{O.cHN<S_[\鶗=H4,T(HʒĊ@4{M:9P?%[v]+{]@owJ T2GF$/2_>]i>/4'צ[ˆF6|s0N{%?غ~|c\Pu]ύt/>}gVzm7QСb:yWF)`kuK >cFҡd-8E&y@ psLv1dXyv*~̘P0c(>oi7 rvR-q8{t"YF0h<=*s[+Wc 嶛m (|Oym\gvLHÚ4?(o?uCh?~cmeBҷޑ8cU}v^5ӯ!{C@dk절FRYtW o[߇瞷 3TgEq?<_?my 4\g-=of[~z/vtW o[߇瞷 3@AhgMS q?<_I>-h zH?4<? KEK[i{ӭ)D;hR;Vf`Ʌ?PqUOVk<"_{Efo+~y^EiQ@'(ߵW?ZTPo ?OUv'tKJvA%!eb*)?u?u_k<"_{[wSWU/HkF ꊽϳ8'g~y^E'*;߷E9یY]Բv_Te}n ?'(ߵW?Uu?u_?u?u_k<"_{[wSWU/HwSWU/H ?'(ߵW?Kjy;[X\!< ɍ.pH1Fr2=EAs[ۋk-*U{v ;[<9PIDx (o_{o+ۺ YEۺ YEYߵW?G ЫE$P5O<[Tϲ?q@vw+4k28>t/(R<0)HA( QEQEQEQErK]Y\GWSkξ~RJoWVa_4]}MMB8xnFaOih/ӼR S5$&sg$v:DE5ijFN-MG0ejzwi?A4֓3=qHcVM-2 āJ1'MfJ%qaqY4QLXe`F#䁷$W_)^xMMWM_4jy qvFZG v[^xPX!6H]HVr8+ݮ} mH u)tsF :,,}n+WNa'`#H\ #ֶ$hX(f66OmusѾ5~O <7Ki _Mrʑ3+P#5vF\s_BU_. 8M'h;"pr8/| w>&}GP>he ,W@"P Hߊ^P=ŪZm*6  x~ xĞ𶏛[xó.Wrc$aLd6m\\`z~x/W|eu<64cY@Yy<^7rr{RwozσIɉ(x\w4TmQEQEQEQE]gVWw GMg$LHbǽC?>o1^Ey/D*0,[?/fl~H+( _G"_ػ'ٷq[D$ }˟8zؾ1]b}tOW@}?>o1G/Dz x[Fm.վȒ㳑g,K2Tuki a47ʗLTj6F_5@}?>o1G/Dz ؾ1]D#6"cɯ@(hz{zU*ddRl{d*fotoxx-18.01.1/images/batch-report-metadata.jpg0000644000175000017500000003443413222767271020011 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 145 274 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&se䚣y ._-Va+3ZԤn1!^%xRsC,F٤8vAƼ,vaGw/|(ьJVmukC@߁#$[#8uE!O$lw/O5 еjN!Q]6 OB}]+_o/_=Uh.X4LO'WO r|=ak/|GMWJ1 {loݰ 8Fz*57NHSGvC߹YwF(Rj׿C/:<ru!_%EuNOK]1ᔖK짌Cn&*v =F+/5ɼ?Ú摬KlKIV1%H B;~a|Dru!_j?Au=#JaA{4"}BRl/S\|ޘvwY~rMX.|HĞ_>^r xOB_> ]Rf*ya/L=:?+_k'Ğ&4+[x7O|YdT!G9oSw3x?X}]Eޗ֛%ī,FQ _iR܌1W/_Q'WO< xoK}k÷>4BxQ YsbA-UC <;Kmyb! BopH b)Q+⟕sǔ| EEo]zF&fUm$˗-+71Eh^3<;jֲjk:Hb 2#~0vb)Q+⟕s˓R6{u{s485.DOތ)1xy^UV0ϮjTԮ"7w[qU 9[OʎGy:<߁|1z/BN8y̎^0wd`cToo ?4/b$rA8;;Emn $ד 9KV?*>ox 5cU^mqi|lɚ9o RZ,.d4.n[pۄef08kվoo׬OiޗJmSS>% qs#!Vs*qoOkgְ^Ȫ0hR|g?Y/ev*,!$|οԯ$^&G-TIHgcKxv)/!. jCR)KHAt5y'O&.$H/ݒ(G QNO` 7"!Qw=gwayrLzT ~!{{Fyxm,wqhf4WqLdKNKBN?=&隍r+o.`Ȩc5;ь4\da#O.״PMwijqwq.BysRƺDž?76wŪ5~axhYnbDcRT6X0 2IM˥Uv '61WIRa\7_~~$ŏ}V aӬf 'ؐ{p2}>Vv);#. X5+]#J]̱Cgb=h$J}DX۵ѵAeS#U 2x޻땅=Q,09r^)v-vMt0[Az`Y6)'Tٰ0hZu94>t~[anxzRFj-/Myjڕ$ 5D#t#._ȵ*\i纺ᧀ` bJʡ>lP8>lOKj<_{an5&mB{k˽*uyV!nG ؞ek Z&-yeu4RpǗ>:%)fuEPmkJ9GZȡVqISsZCk/CZwͨGiW|q(D*2DvzEvT]䗝C:|: WR^9s9rXᝁ :+t7FVu1Ga&YL6 `mD98~&@?|AxM/J\W -vAhV?0kzkM `Q_66n<_gxĚ{hQhm/W@,rl{A~z'kˏT~ LЯmvPgui,k8vvS&|$5_bM槩aY7W8cPK;1$.-!'H]H`{k[x[k:neO6#[[wg׹[X` #*$ k4]OJQӮ̂@ʾÂ+#-$E}/kZU^NKtrOy'gK>=p Z f1Ҫ"o_O=2((+ny#^խMFwbm羆'AEUOUCnM\qBs@7Bgin"%1M.]6ծr)lqzux5`0j'.|@$9's{'H?? GEP7O&O.n4,{sMo 1g9W`fU.54乙vJ$0I\.H`s;٘ISzUasalvo_̍d6Ayȫ?<w *[![f%pc1J1pWVWG-յlLW ;(E ӆAeYXXPxRaL+\eFi kU:>e^8]׷W/wBY#bF5R+þu/SwP^i-c(a-D (I滛]LїHӭ-[UGln81W{+la/2BwIPW1 /$$S<]ko])KR+(e P`{ ۢ/x >;W}>(v9vEI랴^oC$15scsn[P;ov}5d=>7&!xl'֤^쵙9ˬX"&@ w(#[S?v/$4ɫ-b׏˷y3Vm_]3XOtoKfR2.Fp:YE 0oM+Sm9Y(6=viiec i#lvѬqU­QL(\ ^;}]a5nZ3$U(尠p9O,߳d1o&潗Wsd Ԥ4=ÿĺ:,ҌK7;toSWSJZ͆q{_Y^p% yx}$е-?Wx]-3Lt{kx@J1lm봕yi>'4=rH|#&#Y&2Gp$ 6յD'gl<]oZ,ӽs5ba :8FgѬسxbrI97hZ-EeZZڸw@[<Ѷz6oцzAE'4];Ik"F溷NYDUEw>3[.u5F[Ҭм L]̸ E}m>޿}aԞH⺼628 2xkx})uM7PWZōV"ys3XH"Ϙϕ_C|qLY9 է7[$y0m$ld`1BzdSVȔxsĚ-棭lVEE$A&Jᗂ߉ o2aOٹ.씴z k?h:ɭ~ n6 5;RQ.^EV]){y_>?5]MtNU| go[d77nV$T-w+3ΜxO`cۥo;h[*oyqyfs;ko}/tu.tk:ƷlH.HwH*_(_kFtM+A,c%Ŭ K~ֱ&`0Y;qi8txIcigxz-GK:s,fX\c?|ȡ/0Oou{7ӼuoϦǦw:L=)!^NWp8=_+x@`7𥵻 '`@gV&[joUmtD>1Eki/xKX57W+ w#!\;k)>-ũVmcx?홢'F!np2sָgLcUկ^ 8fDlyQbOk:S[Z엱oyh3Y[Ӵ|]qymP0|j;ZR0n45gݴW‹lOϦj7'O@TJޣk o4^LvoP٤G ᶁ^|h:VqhF0A$;MBT.p&*̿13{3+6,>57w*38T/!Bm<"8Ԓ.OF.Ӡ3k3mQm<"8wtqcҵTWҮrd ?+Ь0Y}T@۶g(ݶW?@4Vw?mPm<"o{3Egn+vy^ EVtx[Sz& ᙢfCRϱDJ9 m<"o{3Egn+vy^ EhY۶g(ݶW?@4Vw?mPm<"o{3Egn+vy^ EhY۶g(ݶW?@4Vw?mPm<"o{3ECmrqy*6L&(((((((f3n(T* 3w%ժ?7IЩUۚ?5_O |iQ|#m!t˽FKL6yP|dҬOi-JY1`]w<1)Bdk_ߘ=?+Gnj)a4nj)a5YpeZWվl@4&NS3 @|?Iis_| /L̂9`ٶPOЧU$ЧU$-|xGfKMqTY=wm5R[#myTU HY~e瑖Z}=։jY{K=*87ƒ"arBU;'_ԣRx[UHeOd]pX'ڮϭC&k"#b2o Qܱe+^46Ҵ{gGxfd1xPR2!iĸWO4 CЩUۚ?5_O ifź_5Z_v^Oq;Gok\%* zt0AsjdԼ  c;e NsUOTIsUOTIB/鶷WcN 4ۤ7Q<KT36KuAxv̒Yjmk' |0m8nj)a41y|-ڗ}~>ÖdzKf5 w}}o.,>#^(-չ$G5M"A:h;xđJFR2 š(EkQEQEQEQEQEQEQEVHA?F;_Wg&;^6qsQj?// k΃:jqG9IxJ#"`Xsc5Q2i,t+B.Ra2BE3ǃ'9/{G?E V \D~ L>ēkC\הv?}?N{㫟i~m|;i7LXImмQ<)M!,kԿ_>/Mhh7eA=xWӯm4)kcj%4$4k$mb",0U-pFܛMtZ5wV_RZoä\;,1Бϵ{g?E/m5|ͻʭcxLӴl!L!&r~`A2_)a|^)F7_o(g]% &nZX^N@hʔ-Q8[oi-𝾟]Gi%5" F)0wugR}VQVU4?PH8̹x9 mt\oDz.xFI"h)cGй<םϖ/} t4bYTI#hV+{H=ֹ Ѩ!u5]r*ȁKOO5rVgMX4@vA D0F̲$"'!@YdK\{`iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-18.01.1/images/trim-rotate.jpg0000644000175000017500000010574713222767271016116 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 313 421 0 C     C   {" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|:^4Ju1oY#ln^zdr{ ot oiv}xj {gm08 ` 1J7J]_ђWE SKh}[ C~eZZ ѴЀ ;WzF(aOcޏ{ӰDQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EՓQ1PdvqISSV6[ T|6Xd."pCՉWx<`nY]yse~nzqsV(G1(b*pjZ;-14[#)U;gh<{K I(ەbJ.= =+յCf?],0G M(m"眜Fx,?x28>{IHѮ8$4BF =jTg|m&EB]^xNѭ ;vtAWT%xcsC*q9krGO~ø z?`_Z=.~|}_??0xwß}?; +_GU#Oi>sC*q9krGO~ø z?`_Z=.~|}_??0xwß}?; +_GU#Ot֟OcqBMḚ&Cinrc;pdA`???0xwßP 7q}IC$NC!k@\lqW.~.x]gs_1jXQ-JQq5; +_GU#fO_rw?>(]5uS%6@Q~U<_[1&k| L'޾q9kø z9-:?ƿ{ 7ķVZ!GZG_FoNNUG=*_u7[sC*q9km^i>}?; +_GU#as'֏Zq9kø z9X\I'ֿA`_Z=??0xV?>>}hIwß}kU#sC*as 7Zة.dG<ֽǏ䈭!%`B:G; +_GU#f?> hR1?Ic>֮>̲4gcby$3Ҿq9kø z,ݰYFӎ`+Ѿ,\ 4ط{Ҿq9kø z`hb^7ǭfؼ+{OσI  6r7ݫ~nsC*q|8xշ#pT+$rbu_.i=eQ]GQES& HD4(c|<>xZ-=u'ɦ Dc^~ۂK)qZ_askg>׋i#X 0Q͗syl>]8<㋂?i}F d9\Lb]7v89x߆ /¿l<#wkMFF%u! @V? R:Pݢ[OK]/SCO;=B`2sߵOgqsxyI /lg8>+䟅_t[kOxuX} p x3 Iaۯ>7ZAS:um?Ɛx+LR"V]z?~ i?y.%F:} "Udt\;1|u1T̹n*w`V_ھU%Ծ%?5RﴝnIi+R."1M3i:nn_jouBqjM-\EG 7)X/?篠7goi}J5<}lm_,|ZK?%u'LK}ﱮ% 4<` k?woz>!ҕ$sلf=zm┓U-nm4+&WrBvc"Bz=29~H.>n|;vmCS<5]29 /j*ԼIcYΉ?4ygGs ]"=fS_⬯ovK1_K['}Oo:g.C]6=Pl7DN5ps*Odx'Vvm.JZ2\^y kIYXlA6 _:~XuU'@>{y`=ɲPX:)1!dg<,-<)i1\Mix֫CmȌ FW?NϷl[c?1\ׄ~&~1.m,F>q%")&̧q px.<8"tB>wi =3:ԵO .nu 9,'ȴTj7. sNP]>7ܙw}sTJ #N,n { _TewqnM3ZikX qe2WZG Ѧ%6a‘z'ow/MJ亁.%&OLذ1cMuo5pe^wGUޞ.!xDy1Q%mmiC':5̴mJIa,.6I#|F1G5lh[y(xs 䃑e>⿈,WMWH%1$XQI0 nm_˫__i^&.El6ِȗ6@Ee- ~V4?M{tOMkN.bi8$dg<-i"6(i ==D[Q𔷞< &Cyp:CU):.}W~ SEH}O񲖅DeYvH#uJ)$zC$ṊKQi3P3M:Ξ[nC_9rvx_ 6,ү~k2Xk<)w#-#o,r [9P'¸oykĈZySHM=)6ϑ冐II~WԖ쿯?S b_[D8iī~PΟq(2 d(_#|@Wqk?[4x;˭?Fܬ>6m%%#~2֏_QCcWڍv8x^MEwuH^KJ]J_[\٤ qmBcN]Wa+R;/FWҙo\m p\ qB>e=9,ﭵ[b7 z֟0fSC$ܬ>tKx [o<{[,1=fo:upĄ22'-'/:޵:JyifCGPNIPXRK7M>Őu_,oE+6מXVO:IcSq|Iʁ0I&pxI|12 sqyhB8)7]aoxSMկu_p]xKF,4ؼ;s{h.f1OYrR@nם:S5vmn噅l v8ޯ#:r |U_Z6k[m?@:RQkeYf&{fIK{c#ae]xSf]H<@XB*Q'm/QE%Q@Q@W |pVk_oNզiܮ/s H:0 wtW].uaNmY;+IWDi>D=D~͜b|C/¶6w[]Ae >fD* bEr_ kƗW׺7wn,vW cȊrFIH^&ta%\Pxź_.5x4]&^+$^TU7|+Z~@lQYZ4{&xew-$g# Zt=ŚމHm- h2FYpNϰ tT7 K_x:Klj/,eԡ[&7Dy ڠ4>b Ѿ.v#ymI/KyьXah;3C<> >MVKBT[D ^o66O=_ᇌdmA<%^2Xxдp.U!xyWPUBjY7Fլ7CżRHPF ADDDhU`(Cy[h~, 12BQ9I'P̐7tfŠ(!a_^mK9 mIPrd]}`xz1jk0"7F 3%2'ȭM+SִMBKKhd,2 VB:袱_]P,ݝW|dwHض63bW ~iYΤp50i-%`ɴeQPk< җR ԭẈep1Jb{30Ql 5+~3x7D~֖ e6yFbcI}f r09((((((o_җ cK2ZxF]-` _miE,,:r(_ӳF|j> zϨC|m7vEdYQ(2 }cĚ6xc?so3Ѿ*4hZ['jPys̱'ۮ2>[f #|G?M𮭢YVm&gz lvJ&Ӣo'ern ƺ|`T[c?*x%X_ [lZu1Zy~]D|K$6ҒMö֞/dx^" #28,s}Yu|r_x_5.M]?KbT,oY_Dux٣pSFXs^1qq?; tV=č?HԤfݤڼײ[qeG(YA92s=?7keT ($ I ŭG$1,:޿%+u[_wiiz1>{Z^Y,( 1 _ xuߊVEm ZNxڎ}7`Y&[嘱P>o3n6ű&ۜzutm˦+z\P֭Цo%յˏ?q}wm=>}@825ˆe,ٌpE}1xĿ_ƗcV='P[8RD3IcF0N9";0|L۟82xo15սuEO'4X:^e4Zkvqpf`#e(>fݞ)h}_߱4}_߱5<+>Lơq5PKĒfT!=C|n. rLI%3NH0yX$ ;}_߱4ѩ\C OjXIOk xSO֬/5}3Oiiqe @8 llK,< AcY MH8(Q\j&'xX,Jd!  v]$KoHf'eG'e^W<=6vJɓR8fh.]S`ʅN gLg4 V ^xu;Y̻*ia8ڤ9htǩ}_߱4x'lbtd5' sXu?C[[m.@uS@F]VpZDŽ7jNr6W8?SM 4Φ(Q\-r@؞+=&e }s TJX gyYdxCiYu /S&DB(s?iItѥk-cP[pb2vw3Fp褴IvAk]wg| OxW÷>CejeG[w7׌T85^)Z!6])|a vc5.u'5K>kg,pZjwͩF4%$;tQ},^'hڇ>>~mt2F!T0FIC^^em43AE5yᑑF W /QKO^6/, 'D총ioclu!WʳF|@ kN=!Wm6-(,EB5zؾ >*>^!SAYk .* @E&D\6bc\;Wp6l mOҸ\<8Xk1]օ#FfB^(n.?׋5RUҮ`hc r~2z׌4VM8[h;8t!UdnBqrb@/ l?#?i]?MJ+Xژ-_̓)`\+!\|r+| Ώx6Zu/W=m-bʊPFG}7B_\__a~_g'*Fqx/GcCŖ^!w , e߃½%h:rڇm4=>ER3^$$|dk- {'ؾ >*>^!PV+k{-cN-&=.PJAfNX +#J$ Ay5xf]AŪ,SM*'l5-&C2}P݊2`05\8Rė݆݊^ >H6ʡA$8Uk'ۍIfں8ELH⡫7q{~}]GGoi:tFĄ31'47}QZ[xRQ:re< jۄd~R1/?? |A}qUM]>m'𯄼}>wT'aX_MkHw{yq 8$Wx{Göy>> IlRy7B/$-n=* 6ib]>Kw(" vJ/zQEqy<]GEo$}]W&z?-?O$k*2:F*FA|!I,<_ +}k5su4rf7=zW{gi p\N%BNWfq3k2LB[! P*G5o|ecǞ%/{o '9/j 6Q7)VS+_3rzhEOE>&vi'Y:{ym|6e퇙A;#`pinjmwuФyu^9Z8ʝwmkboVuxmv[iј.(9 @2kқ m`?-t7_7TƇSoNҍ$RH,nv ii7?Oȵ97ý.uk[V8zPI!4k\!ѭ6r[$6m`#'v=>&vij1,ЬVҘ'$[ha2 _>&w[ѵVK]<;pNet` N̎8oB}u^jjW˧YIp_n8,Ā=Hn|K7Emڬ857Vgxú|(|]7VK>/,jE idQ{ ߴ ZQs@F7E+f1?m@ExYxLHSI\˰EWKIH5|NJot$w[ofD֌ϛPN~cUk7>ݭS.v2㊩jp·)$џc98Q:QûD>j>IE Z>oh6kxs/&/Zn;\M;01._q=Z$4m3\O4ۙ",4[գbWsWv_&;CGfVMBNr=$(;^d5^&jI7/ΡثyA y2˒ĂUi'Ioon4ll m|aPd\ ldv2zЕ;Xդt[k`cyr"#`nI&71r!*o(N%C.m?";C^9ğxžƟ%/!Fז *Lq+6(t~&|Xf j?FԵm6MnA} قfem4y+Z_[vP-< #=+rscSsw5R>]ŦYΖHߵ[ )20Óg|M@-d\2ֵ[kiӽ4rxɮIhOy|;oNzk4M,pıHJ#0?x/`z'|M@-i7?OȵZknN|!Z0_,Y0evkl:FдgMuVXcu=9RU[i},2FFBI\CJ}_6$a'$k>cRKo%m#3X;]J?#>78v_Ԍ ω ;t/`Z4i7?OȵC񶶾7ς5siaZơ{m-m]O }/ƞ'Fexj&±;&?̤uv;ϴ Z>oh6kԾ;XYܵugVuՌzf &viIwzIMF ijM+HE^*6Momqg=ёQK9? 9ǨxTZo_g ?dkǫдX^ss;Yǎ~qXgFti3:?YJXms#I_;jx;:嗍5Z]bNJtI-L坚SI=?! -?zx3\ lwۧ W3ekmoX2;o|4ԘX&FŒ8U|Cr#C o YR3ׅ&nn_S$RLiظ'\Zԯ?iojRػ "0H$唑_C5-Zk0Zi[W^$-5; éMc#w -s6em*#8 ]c>e\$hx?Y&ʰUk&,ya56>2K\cAq.(o+֔C;@?+}xFQO#Y /c Z|\WrB21?k0Z? `iӶjڊk_&k/jLJt3JO3M>͛G˓*ڮj61v66b2|E^#5[mĺ0n/- %DVSc!^GGRߋtZj'd)Jظ @zf{oӉ?G! -?z&-sWQ<+I.wI4!U10KXnRd9?g^HI ԼN%&Z{ evRnph r8 VÝY 5=9Om$9c$tw _=G! -?z_sZ[՞K0xDx_LK]>$w_0䓖*]Ʃ%ص hĚ$༭3@. 1 coC5-Zk0Z|Z_c"\uUˣ=Wk}w8UiV/(\P=ُ^R0((()=!isk΢L'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V i|hL'V86sA&+_3Ϝg[_kW\Ai,3(҇FFB@8&5ѿ>G i|hL'T}o5ѿ>G i|hL'YZ[];DskBGzڠ i|hL'V i|hL'V i|hL'V i|hL'V q3:j 勯Y+|!7^O?Ǟux^E x"ZMW&Pϔrq8݅{x?fS{?ä^PQ[I'\<-'ïjVi]ZiZ}?RGbJwX*G'3ڵdžu#{vH)BDroqY^ .5mcW#-V{}<$A2%]B;֟gi\m3.kfUOk:\F{D""sqOr7__{NXhj.Y\iI]%ܼw46';'y/4kݥabk$a@$p y5_^#Fsxv9t}koV#p|#nr /?ďipjWFH ŭwXAwd= >_O5bB]Fk4H"WGjh{HVvX]+&h55uw]Na44Ųf^3qp5™7PwqRZ1|1ͣSm?"togl/C_&b SErYS­fd6P_w$5IhZuk/cխշѴpQ[8{ h7ԴexMaDgG`ṮtWĚTzۮc[{+`8 O}ѷr|95mǤiri<C5ܞk^ ["g/xX5%x sj׫4U-op݌54_.C[V[kG1)x!QX/9P{|/橮*%vW:Hwșٻ#=o>*|b)GWK"mmn#`hbYA8 mͮ,)_MmwO _D|Qan+YD{#*n7Tʊ Ȫr3jOGWzxy.?u(QGm&w{c$>NȒsp&JV˶f r]WƱIg?^;tAz.PJд(1P ĨWvX%_xR-Vmr{dV*C$ )s$(RZ]i'D.5 [+(;Y%A23V=* mS׆dqCZ&7ڞ60s1^Kğu+I[T. tɩ|ws4pF6sX 'H3vB5-iɴټM≴ok GuY(-']c"4ueF_|Fj:Uվ_̍ka[igۼW EqC֭C+fқ #[yo9 S^࿇|}-i['3Ct,$$OD`` vRkRm'mszg7KpiGN-F}&_Kai5%àA1Fbh’y}C}WRF?^=kjmm>+Y&גTo]UlN4+4 ]kJ EwZ% / 1+Jm[p_ٷĿ?1z>-g%i%NQUe 2+ DO%[Kmb66~ o|.I[u4&9!JyS:cەG=mkW_FC Ժ~5J\G*=ҡ77m]ė:PYxbm0_>E[k$M,iu6ْEo)нX׵$]Y5gIoDKHg H\,p09.E?%ɴY shzŝψ5MCHkI63)»*ݒ8ȯ>ˤ_k^I*E}NKv2:DP&z3nKCS7.2&hYawwٲY=36\m+FM5X7+)˕lwm?֫h]Pӛqq [ip%EAU]rpZ3}sHm|3W`H>|$$#9*~ :Q%NJku-w1[,h+2V3~jDžCughZs-=c̬#h*Ư Ş2%Oloawah-DwPܙ%à 9 [^_M~zWsICյ 4i\V@c^4Jh\1sT/iK; !["roAuV䕞K.Ғ6nk'W-~9uӒeh>R~ᙣq8$y)!Be쩩VZU͏/_)ߝnI|{OrY@SO!F˦0ـ'El]Ǥ7xlCš^fXZđX[b ջIoU+]JxU,>Ebgcx8Ϸ6;FwmƼ~\+_kIKm\{wZX]G,0 s0 )}nCt[mV)ƿ x4k<'w9վn7%kXndoffGظhԬ*[< ky{eIx~H5Cc6Ha!t!̨T0j;A6q\Z7h#Ӯ SX4tR~ :|'%OLFlژdHvx'6n O aybt2(]t_bfe}v_?iWſ/5ֿ7)+Gb5y pFqrM{y!n4 M|'hԍ;Lwdg^R^}aEP0((((^qaCN|`ǀt+uaZ'F|$ηq`Z^ HVI\uaZpri(e+ kzɧŤeYiSlj>ppO>+ 2-_Tiul9bGv7Drmixʶm?z}Ȱq?5 Kúu6˸3ѩo Gy|\^=mWM״-AKCyqs=ϕѻ.Ji p-]@nWDh4mN/ij"# !FHmiAkoM6ZinMP4m,,b].cxt翛K['|Q3+Ĭ#rFj_wu[+^Kk;[lDvzP<;,%AqXvyxOWmb[5#nQ$V#?mdž|E"&8WH,xʌ]/ ]i:/K|tW;J%_motJ"[?ξTi>J֬ld}CVnSzŭ;~h%-_#kUm^EMii] .HY~+]HGqq^EUؒKo8ó3Xxn%m᷶䷅@6V0Ua|+P\v3O%!UfDn.NH d–*3~=Ax[ĺ P"䤈s!N ּό(]x~lbH=\$+(kj)uXc]?w>!l=zh<ll[ m&V>Xe,=o 48C/J9,m\<*H7;M}ERvw-_3_ 4jzԑ,f}EiNl䚞'\ 1j]J).1b܅Wҵ75m+/tCG>Z+"_%ԵK";E|7e%I0# /`c1 mIYn[NQ̧, ˣ|_Ud[EĩIM>I^XV˶ 1.1xVg } @O r7p ۢ6S@_}^kGKnuv~!_ ֯c(_*$=zk8_[k04fd":FQ?ڴҼW-d6Ws]_[fj,j8, ˣ|_UH ?Q 2@SMlX^[Am" V((N{me"IPW? 2G, ˣ|_UtW? 2G, ˤ|_UtTv]A*M $lXz:ԔQEQEQEQEQEQEQE|-V_?6֋g}/a?.Y<Wñg~6xa{ :ψi5OѻۅI10y^,|K3_N>HT:H-ÈՃ3Y@Ul񣍛]RO 1=/JeH-Lcf'pNcP2x+g4[Aj{KK+ˉخ%&cfY[=i(y'_ׯE|$[x^*džЖD qu2O7hT!1*ݼ xx[u x:TOkkg w,¿,#a HUwmԭSo? t?&wE|ikl F {auoNdBw)FAB2޽TZ1&2T5F(g1VSÃRͭA\]%;HW?@0* _Xs?kj:ցL6!)&H?:GⰫQR*=l,K$S4Vƛm\\9[K'hc`dv2*ƣ)iKp-l-7;أbx0 L'g'o'pFRAu ^1jv^B[\Fc7 `W+E.d}Z}Woknfկ{Y_Ȁ_G`xHŷiq%3xWm[G0ҋr^|F3 :hM?j7m>O^,Am6%2/XOCWw~-6Դ;]KOp!]vIֵď'1G#"O\QY"U./m4U]_Z1d:k[k^}C:gӵK&YSSnY+4ڍ"W4uEkVQ_'_ޫxOx ]gUiV{Mn+kd0q:2@j"|_㯄ij'}kmP[\;NAY?+mbSW R?QRVQƑo\ Z+HqIMU#m|<O'-k&u֡{vZ݋sּY>+Fu7!ͧ鱶w1Y5Ou0u`6M> ̺.m+c.=XӞTꖳyɌyLDe +ߧ`N4{_dVϓRgU~o}o^x/<@iG$d?^HR1i}?N.4H xrꎀ~7KBg}4MzwN.'UԥUVתz0^6 B2IZrڙ?V|EPEPEPEPEPEP\>xgĞ3`/7\A1ˁ`Vs#dh<':6iq{UmR&*9R#F/-ʜ'׿g7WյO#]};I@XKnm9e䝠GQNVG:@n^soZt+Eo1 w|#];Q^%Ҵ'N?`iA"Y$ 8\=F/m<޸>/?$אhw bdY#} 8=8aY+G%Ƶhzl>R–ڍ#jC.lE!$TE :NaaÚㆲ1]Z$S"Kl Tk>h~՝} مS$i^L!@@^Nz+RYWt4&#uYm.r N|EkMHҖK9pT$c4mf:Ɩ},.es$\?_v]h Gw4d$*G/ҹnOvuT:$[$YVH~V&#-ڎq 4'8\gރM?&oouq>}e0;Lys"ʷ x5 Vpjj)m~o9/0 Wp3Xx?ΟT#[>O?uU {6뿙Υ(ѓcv};#U-G yxf5/ω6ւ 8i:]:You]]r~ S4鴧H@ۉFLc޺ınR:dbϚd~ ;ok7>qi}mq3Jd#>S)HZ20GתQGo |;\|O}@ځs|V.65НGČIZJ?Hֿ&iV8ilҧRUa-ݵ~x e,K{߳y+{3O4 |w\*Wb]5/_¶je(BG;/[HQ%?ríًnO|p#@VOɚ爡BܭޚvHx\q8(ӿ,V.ߦ|8szܱVPAmͥҴ < ďs[HQEQEQEQEQEQEWTi5'BPUn {}+Jպ5V׿r [^p?n {}+Jպ5V׿r [^p?n {}+Jպ5V׿r [^p?n {}+Jպ5V׿r [^p?n {}+Jպ5V׿r [^p?n {}+Jպ5V׿r [^p?n {}*+GTyWM(^^$v>ڼI@5Yպ5V׿r}ğ ѿk7#Q7F@n {}(պ5E՛T[m=I'*z*^ivS$FCъG8ߜP[^p?n {}*ڼI@5j'togV׿r[^pʭ7F@&Ftڬju@k9G?F#×:ᢷYg^uVE߉dizJ5IC-ПZպ5V׿r}ğ ѿk7#Q7F@n {}(պ5j'toԃV,v60K*B$yYʴI$ Ҁ-jQu@k9Q֗}W712qł" =}/x7 fju@k9G?VW?oF^$nY,jQu@k9UO5W?oF j7ʨ]*N А\HO*ei_F3G[2:dB,ZQEQEQEQEQEQEQEQEQEQEQEQEP6|QqG p@}^uw;R$^<n-4"ynY&%I \9R>)= aOWd$o|]ok^5,n,Ixn>q%&BwΤQ)^y6KoI>5}|Cqy{!D{v`)f2.$->:M=UtrKSvE[,[Hq)Qpz֍˨ u˒cp&[1/rO)<)z%ׁmi'T\ Pʇ0yz_=\έ멧VFvH4arFI p=+ouj*VJ2ÿ_Ee4j`$C 9mvIP jEBh;du\j3iD6h.Ἆ?Ѻ&WW c f'ID+0A Ȣ}r͸M6:jSk7͒ZE *F9$ ߥwOZVizf׿lHVeq"Jpq[/[Fմy,.ȅ:2T+ofg=xC_W&m=ZXq"MQg|WxޏkWfIӴjwS&uď $b7mn_f1cĹ}rNYl4e ΦE$QO{F֮lHZ@ԣ]LѬ+lnךxlմKMOXMkgwg.,n@ykyeP+վjj+uN0"pX9$n0zu\mỜYZ*=IhlTpŸ  Qp<[—ͥi5x#׬|Yn@ǜRCeܣ#9uGg]KGpdeGrV6UA`sOٺ[+=M[b G`Ek@mݷ$;__s'=?dW3SdWMHERQEQEQEQEQEQEQEQEQEQEQEjԮ5 4^(mfl6$"wS3_1X୿vPgb[?  V?+o?];[!ylarp22XZ|f̍]U:WkEq+c@G!"mG|DO>#|im][JV<79?s_x_ c?R&?ފ?  V?+o?G!"m뷢8C 118 250 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((u" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?VR(UW8֛-.RGPèT-S0*Tx⩭G(,TM[AS\*g~wGG4rx2 kƍԪF\\/5gyᔆ+パK좍Lw/&\9;A^ю'9t4#93A\I%qǢn'5Giq\*LѱsUks]cg2?޴xcv׌u!X]]4C3*I#担Wo_W\,|K ꯢix-?0>\V=xBxBoIsm ISX*6Xt-{7>:,$XE&c4\,z>&y7_yiYYjڭϗ#!#l+SKcI捭GX7cǣ bMh?^=ע.S,.lE-@;z\]ywaa-M$7'HK?z??^1}mvj#G9NJqt.,Y-,-$DyFȮ8c|?z,.r E^nO{]M%8@+%M5_D $?^=ע.> 6K?&kqmu#YL1 ~^+wKГN ;y`(0(lt8c|?z,ts+7]v!kR%XbzlЄE^8c|?z,ty~]8Q`QŎ+2/9lgU|4M3VГ Lgk+q^?gb9 K\eYYjv+uaՎNS>÷~#Ճ[LD`B.J(\?.qں=ף8c| Yk3M$wq3SRջ/6`g43>by8c|?z,tax_K]F[3So92k[?^=עWDggO?gatg;sފP,OY<݅q4V,s\TRkdW9uGN$,,5SA*''[*Q-+CW΃{} uD,f,@^ |ԾFbVI#gvb,VSGe=y%bB u6CwIo?Joj ]8E,S ?{i}BЇM9iL36_'MEX#G}cKM_4~?ѽMEX#G}cKM_4~?ѽMEX#G}cKM_4~?ѽMEX#G}cKM_4~?ѽMEX#G}cKM!bz_3?1\{ Z( ( ( ( ( ( ( ( d`N8FI= iqhjYE0 [I*]}wi,̸B۞vOҠ~!6Y&xcJ@IcP#Cn>VM[I*Nr#9Xݔ >,9MM&Ǫ 9d)|maqJZWX)PTp{1I#8I_;Tqӹ=':{۟QƲHbm?Eu/UzK=K̪H" FjNkY3r#eFX &mԿOFK|$ t襒yqw_`2="xJkkyU@Wsg`Ns[y CeaʐpET_Mr4fW KGap1~ZArZ(Q@Q@Q@Q@Q@Q@Q@WKX!kr@C#r8 zRmKԴ-'6q9#g}. U]Jݬnu "8ǚSl`tڷg`taFW|l1s5K%乂8ܱ2cvxgZr|Aqp'o2u cG\ߪύ7~>6?wⶖ-d> ӳkQr&y/1"@Aש~)oagk6L}i ƛjwDvڄ;̭@IS>wWz77I\I.aFGPTÞI-o7FW|l1ab}6C4Gm=xYH8+>Ɵdnbv*6#zd둀ݪύ7v>6_n&8C.b{O-Nn)*}[nyc?Z &w2;*d QE ((((((()h њ(4f(3Fh њ(4QE%Q@fotoxx-18.01.1/images/process.png0000644000175000017500000005712213222767271015322 0ustar micomicoPNG  IHDRƽ pHYsu0u03r +iCCPicmxwTSϽ7Bz M"%"EBH0$4!#*8PdbaPldPQDEZk޼sk](>BQ:@H"vgFFE3Dpy*_O3u_l&'yb &|A&BS%2*Y63ˮ2槉(ΜO˸ YRHEYBA6ʷPO Q~+Mp3PdvqX((_qW,Hdg䊅I1τifS +KLNzZW ?KIV[&Zd;k;;WSҫ==_l^{/]@>Ѿ^> ټ$I$BBV??z,azT SV7^zjTL֟9C @FS&%a)&ð?ivQ?zPFȯ=C#$f/ѷb8F#sYпgKeK0s'4ɓfd%t0,`p0Hi@ A>X @ v*PAh@8 ΁*n` `!*D m2l 6P( !$uP TUAP=t:]{ 4aMٰKDx9[J>mx~O"!# Da!l!H"FV!HR4!H7r@ƑwabX'f1YYٌ´b071 ',5:b}Dl6[=m^cp8gEq+pq{p͸>nǫ </woo d6E& ai a@4 :|b.XO ^#I$#3)LZK$5.^d]9,$!W/(JS CRPRRQ^QTC+5*n6PSS,|rZnȽ'Ȼ/ϓ?.M~\`Q*RV8Я0HSV RLSܬxX^PST_ 84mv6Lэdz ;z/}BYIyrrr)0d2Rc;**n*M*M*7TT稺 TUUoWcymSkS{Q7UQV߫~A}|}ޜ9׀5L5B5Vhјܥy^s\媕UuZkL@[]}FSLeV2::>:RZ^i]#źͺHzlrN }m@|FDANn)C# mFFFyFF.ˍoL&)&{L¦Iզ`3;3>sȼμEaXA EEE K}hmݖlRX+YYXwXncjó5:ks_3'w][mNۏvvb&1{}8l:;};G;G1ߜXN)NF̯?uuX\`߂KW=W7d#n/ܭ-SGJYۣأSsgc/]DF o[g}>>|}5}y ~~+):@,-l AAۃ/1R4:4?{mQËބ=XlX3\><&!|*#,b 2re((aT{4>:<@%; Yj4geRE q'}wO8|W~9L,($8'%&:'nOKrIHrU—>5S)A)SfR#Riqi'DJQWVzNz_YFQr;O2̥:3#5f-Ȫz} .]xۭ%K'/;^>q}۞ljmf}o8w[^x;wcK~kb?RxTXq&?7 yɃ!_20\bD{afgK ?x>=^⯻_7z&"'__ګ |&T[ޱux?2ǎOΤ |V IDATxdWy'z+vuuUW4'(2xE@&-0 fYl}`ŀ6 $Pf4ɹsssϭ[թfFtߺ{Iwn|*cu5 4Ǎ-{^:#<F :xC{ oNÿgq sa.K޹0u'k}]%M5o*ģnhl=wkrtE}kf46m)K~.7Ni5S,tj MݬOD‡EwTPA7<@qDI[GBY̬q {-]5Q# 2z#_HZK%#-r5@>i \DUzĽ? P]?m8 vՀIwq ^2D}Q;\ SW~8 `8MpM ]L XSi\&uӦT"3^(W5r UbjMXY&% H$ޤ7~ct9oɤR/{}PVxW|p6(t\mQGN2E}9MPxۍ΍wgMmuyOyyW9\&k U %H7H{إ^O*2Z:&<;Z(d!>bZȯr0w *%=EwGG)@?Y j(PB\A؎W?l(IQԨ(S"`2@V+Moi%Y t!ѩ,<(e;ur6ў64v$Dۃdґm5\N6/I_|bW"1S_oܜ;܀. ځe }p<]/Cw8d">nXBɮ"Jx*I糩ޏ)=O:] #1p3ӹi#O5W| }+^2*4ں6975ڭyP<[ρ'+{t =^_Px;= ǴIeD-+D-rX .s彊/Pu/gb79?rLBAkj VpWy:[xM@r7]ۮAՔk$y׼i[W;[ xWҥ(@6{kyWӹd#Vg$S3k dl\èUX|g6=w+846-Ͷ *<3p4]}6;LDet6<:+w@{]ɖ}w.T;N7f';H+{6V B^B|&@hDC>r8Daz >s!/.OrΤÁLjN4fcSps t88 E̳}/7. fiָ{w#M}tjx2/ ⎢{Ɲߝ}ygyqtr[z=j>tk\py?0 )I'c#ixo &C :.UA\.f(ϷhhsB;IO $_F_3T-{@q?oe0My :& z.:` MI#nvo^]S7r}9)60trv+}zWMb+E<9뒉?ń{N|./5ewqhg[Zگsȣl\nO p..*>M,вϒ9G(A`xm1DpsTjcpV(G?Pe @RW2҈\On矂k-UMk/=\ާY:Tr}wEçx[6[l7mfIDVm*^by~{l'41qWYL|Ж?IxcUZq 6V@$8-׼Qd; > h]Uʪy%^SgV 5nMJ,e] X^ne0{06/?ow8jѮgW+DϚDjR%'@ZX ?(S:ML Jk &K ‚C*w\'A`ܔ*Z =׉f`1 ™NʷWSgkK<^ǏG~p"6rKʜ. maXY<Ug}{,UR WS#%Ͱ" 'cWmA"~eSQq1 4uP3mj)@禟7f>y `Q_u]k~?.˜:|M|' 2ItA\ɥΈ>Ǎa#̦Ю&ΕZ݂B7*̜xNh3TZz:+Ƶx T]s! "@5V\ҩIzNEܴZ5Š+9*2`ꉬ(m Ԟ9ǘϱȹֆGEU|WmIxo'bƘy~qUj._˯<UJ[udLEϊzP9Z@M\#opLCkt ?0C͖Uyq:b!T2?zߴw;ЈdGV[~-55w>Lh{S ʟy+*kõ(vIY%GR^vYAfҫ&!JN7YʊoE99v ֬11t\&m^[ ˥o5R &`lyޫC)vx$La~5 ±eBU݇8LpTc-L+BU0\8ۍ@pq9wO30^g Ix\SJ8A8JRAerAr<)TL3O7U7D掠 S81>tnl^|>@Y4Kܔb. 0~4b?G/}&e5!0-kHe*_y ;khV6Hɛ.\ 4rGR\WQ(oѵ"`VC!jq )X9Pr`zp gO`؋r;zs0-V*'O,GEq"o ϳrўxWb^i%1o\2!'qcrUfVJ~ hoc(HF`H/M軿5PdǍq Q7q9DwPub~zkx=Kk)aQ0m͈OLkv^3tюR#T6:*"W]fyi :熦'>ߗf&^7ŌkOA05P6s{^UB2Ʈ5 0ڵӪzmĂbU]@4*@ZC;P$vQAe/#\fpfvL9C1b(5)L<<PpBޠpDS3k ׫B.|@_h!0-Pogu]lwBkT(@&Zż*\x xTX*w]¡$0&eޅoɼ$@6˓H뻯&Tx_d+srŢ븙97$G*!RN\¡fv^ 1j6 6~njY7(gm򀅦 ˛v|3-{{ ӓ \G ]=PTQ }X`46ā4Ɩ]5}F4o~!pge= Wg+0JgM HKG߹`kOe{HFrL_Z|PEws~9MmaOn7?J-KNTbљuf]T#H,2Rd=Eh{ѥ69'v@rp2P Hs吠(@D7~ BEd:%1.dZƱa?9R"'nQ,Iކncݖ훙8}XQԺSϺ] W1I^LVZ < 3 `Rզ0n- pn\5t82Gsar( !;  G2hb4t{|NL64F Lqr %KJ2 6u35migWqQ tMOMcJuuA/8%c)ޅ v%b'Z:XAi\@F]|$AGa;5luh&km^,^̋uq;ͪ1Ž3N'F(w4Q!r9BkW=1?=z΋dx!_캰 2%z_c`o@K\yP\9-|Gajۂm`+Rxfdz罧׵u; %* yF i3(M\:D>"gRk?ɺFG2!uB6J L#k45zwrĩ٩#g71L>]򇺁DZ3[ֲ.]L̮RyhV*ֽ͢ˮE<zNz cݦwV .Qy_}l I7T ĉe=o9ֹڹ.Nn'n IK4<HC?NW~ !֒*{6H-NC !goFǠPW*^_[z1c+VĞuxnm*x+SFUIo“Ε`3$*w#GbnC|ob]~헿-!Y Rh?`;#_8t)؊qZJ4U9 I ^&vr@U1,y(9ȲUWԀِJ} h<:tB }L26ػnϊGE:ؚl6~{ob9 :WVMm"xfOwCz1cZb]z4$\<0z 7'ںwe ~j~B0eݷ87gRjz?N&`(]]c~B?=Hqlծ+cJ(ٓ᪡ꎊ_&tIoqdbHL'Y}f藴/ >)J\K z;(֦<ھ(Y~Q~mtnjJdd#$RYO'҃. KjDZ3JB=isv8wAV7w`5\6tzꝐ 4Ga|;"&,XT>4: IDAT۰6vX (sy UTJU.5'/;Apƿ&T"g}ށu"rkcGW{[|Sg?Lt5Qoe^8 xxeI_P2&* c:HSɌ Mm|gLvJLPANxo%mYm8Yĥg:ԕ "+ dv~%pYyOz^HCT$eЅN ,JH6rwK@Ĝ"o*fC2bV4}T@D`4,Z.]q%b srƐӅQYߺ@ݙoMJ[.ѿ YǾ]]fʭxHcHv{) hx||ל1rȣD+;8^-}m18-ZCD.(yk6ˤAF#xtv*%M/۪vG"蕇%Ovns95 |65ݎlɛUxf1υl`xU!^NŞA4|Ke.LDgÓ#C~|& +c,D@ Ӽ՘ց9 }۞~_kg0&f);%4Vuah*Id畕룀- 0\ |v2j9mn~8G a年,dl>ʉ@ms"iMg5<ӷagb A`\td(YWƫk앃 b phv~2 % *m u`QJ:h k8.{Z$> MqF \^2'\MqokxJfZhtE [.:Aprd<2XT"6{椟Ϧj!<몭vtf3/3r>U ;Ҭ3E dUn^Tu6q[U)LQ99JI'p kB@4Vx"r'_ ʌ+?'X֑@PQ˼O>Jԅ0ffQE4#lq*YN6||rZv pe `kWDW5bRpۊr0bI@ڊ :=TzZJ~%.Ʉ :@}J; 4Lp9'GRZmbQq16V#>85c%>_<л~[i2bX9?\}N"Aˬ4J]+{^r94\o nۥ:T ,cQRio] )b.%FL (?DDpY #cBՑKd 0->ɤ3lpբ?>NZIcծ.L:E(0tҴ2TJ_G)Igԩ& gS9N7E=I 8+bs&rp>.):]ʃ$J8ke)NcN'&'WX6;se^M5S [1̪|`_/7TLK߬vO C}V^ |t\2<x Ӓ%!>9]$=ds(!7WدK8  |hĚ=O ޫ]5HnͦZoX^$3;`WN Mn8D?Q8T%cI<^v_PAW+5M*"1%!=Ns*}#;y,É" fJy1:NoAAzTҒOZYk/Jg֏#@V_~FO9nd߇hj E2+W#ȩ? Z BџN ;O765t@ y +{{rʂ3lr(,fe~9̤,{*U/UW@²j$h\o]_I(_/&z@haЊ7Wpˌ}p+ԇ'x*͈>3BwеĊ!Q2<ǂZ"'1-6 t*u"wd@ 7`f"]U/T ,0o<؇'b@.)vZK <- sLaYљ[>ORJ^NQ0N\T$$*a8 $&?3~n~/5}%ʳ=y:s:szv媁xmA.v $ 3d^3:U/nR`e,P;€RmZʢ$B '::P; ,u\$ͪ \FmdS6܎E]"myN( L\sHQ;m@*'糚Tc, ffq(tw_~tT_"?+4n-H+u,ZɌXt٣2B!}1j܃A*Q+,c z+uB "aҍ~dȼ<<|.$^=B)'qr#Gp#̮d2}H`׈c|d 4\ 5-i t}Ҙ89(#\0Ħ\%ˣ}vbv ;Z%>ܶSEmUE:ܜۨ{f :E_y|춖;^buOe{yX;dL-6]uZ>1B˿wU_`~w~A|ƨrV岙Qwk32,(VMKRS5u| Ā/3B*p9gTsܭԙ&\^?ϊȂXGWl+:?@Cӈ ZѰvR-$ݼ}Cw:.Y)]NFkj1,<1ڮd4Ys5Us2#C>J @B(=m~͌D2 )R2`"B \75n u^=F2R0l3rp"r?7섘%*q٬|z*BxFnW,J1:٩>Z$+TKSnYiJ}6 wSpc;*]-<n*u\or^ ɱcMA?w@W7rxF;'HFɱ3s΃i@(l}f'Gx}7ⓟ7/558nV~2_bE=̳,eb487A@e:z~č MZ3` }"BEUݲ8YËL: wb+RQ^QdGaxT])wT(Js]"Ȇ`*-SGǞKca OCDk0vJ#7V!¶*XЙxLcF<37=낱jp-AlQ́6zZ*pF_T&}YjD~U:#u8XKv30MO/\S/*>1qk1]8X?]B8o|mI@=@LS> e0<ڹfoþdSm@clΥܔ_VW\#ѷ)XxLxvc 7Zo_gr#pܲWVr,U jp\$@>s6tbf3xd4IVqFˮ*%c37BTg49Y[)VJ)l  OEFϟI4<G`@kf㎛ͭ]Ln >"5=Ko.)Z:LƝәT|:<3_M{wcS$`rqt=Eiʨn+)BԎD7O| |7w]kQL&ޚIM %Y\&Fφ{RTm>sv{q|Euogﶩ-Xzi? :e܊3} bChPC%jmc@:Tp)3W9`-`&׫gBM?;}{'xkCIJ.ٍbAG=߹=M[468EHEY6^]+% r[Z;ҵvWf?q>dn93d K4&pJh s4F֎Ib*.zkVfۻc)ҺͿ>tHx|~I_u#w@JZ`?Rk`\jzⱻ6O"`B'2w馋Hd?К%Ȕ2>e@W>I1uiGʼn<53"Qk@V6UqW:42tuWA#E#_lhM;>.H,΀cWv䡵OMuveY<>ṡ[;}`ߕ=mәU*?]D[hno _w<0M$(#bwa%9h<VYafͦk CSPԅ;ޝ*`CVilG堄P\sa{Mk&<{u S8> .gt~ٍM܆c@ kgǧcm=Q'8`8>1ÙFhZX "(}^[—r 5,"@uB%]1=qE'3%-o7Ν&9V!%GbPNmϠXr$XsG69msӧ>w. {=V(w^suuqj*]E(wa~E$n/&.)vTkEIhr0JG{>GcWV >Ztn[`"D'óCȈT~]6rq%nOol! uY%2g9a[\?x#ǿmh>~b,^u];7h[imލ-bDb0{+KGaXL$Ub\Q:sNWNG3tBBz|;l^‰,RC׊[rTJ^g${ciR6x.qƻȌcl{/ oŒ(iH-Sgv%?E> 0vwdI_7kxpԕ%zM=AJ9ϟH$XeW:_l{Hm~fI#!*qoS/=e9l(ߡ3_m ow4S/- (v-|5o05syJ8~/fM2H!#&[Ox(􃻰]IG;YC|2JMDzL.GW>H?#ߨEĜCT&:+:6ϾFY-K<O==*OO}q'erMR2Y|0 )ݘ9T追:0pZyМK/jhOR"mfhAS`%8G?fkMjq h^j]Vr#.twɚ'{*]S68/_hu^,{wsum^@Kc2ļLt@]b+nNNDZN.^{ˇ!:}oXbov[%( G-Yx փ 3VL%ީH՚g MN`sڹScGӟgE`aw[XRQ3Q?Ԓ="#Ɨ?&ZqխoE_}s[w?5ON<oG!9U m)6k$BTv{(9=Վl1 JՖ[8/W/Yl晃^;o~]XbƮ+?`/2+ʹ*>۽{ebfyqMƻy׿<-E3ap P@B'bS۸x-zԨͻzgb\37y]´-5T5Tq 1zt*|rV%E/PA,y49E7jTl d;\@0gПg֮=7|ş<Vjn쯶ŪsQ 4v*A'PA9Hf0͟ KfPFzD8r;򚀪ט,Q$yC™6h%y&7[~e@`̓"^Hr" QLtpubc;S|{fbv3'c6E۹2vncJI㰇וJ\"_Dܜ%uj4Q1/ 3kiA` RMX<6Ca@#}EKg Rw,Z](O?MPBUsn̑8+~)twN>'Tf wV7ՀcK_fXW:x(Ǿɝ,]>@LJŔLXyay؉ϫѿW4 lҖ{jtx&{ jEhb0Ø;S Zų_35pL^]0:r,`s]/ (.V#->{eA釂'GcE?`n,`*>[ʏO.*N|gšTܚ !.Ec ƹ xޫ_iZ.yJȕPĠCF Q:o0&8~dފd%f^ |_g}TgrryFF3?p]Z"+ k5 `ukV"4+|Ox6wN.gHE?la*`xX[P&ӌO嵖ήAo<6D2'GFYU(`N=}?[r\i9nCbT(?u+_[ehAF'-[zNLЅ:;`c"23R"2`ۍ8cq3qc& ]7gk8S%{01gRP!sӇ@#FKXWLE |uvզPP gjnUw:QlbՋ V*w'FWao;WA1W䌙~*2:oaKto&B8&f. )~m>sگ6Boag'@4}#s$$?3Sξ@:0a89E3}֙|` Zya5R[׏P3Esilq-w%Ĉ]Lθ]s1Er461=+8OunعTcS,z ںzc33Ǿwp(gL=6Ã#EB/acnȎ ~lnjo]Cő^}k:cR)lTB N}qx0Dc@K9d0F< "oɉB- fřщ?JDcivͿ⟶{C`8\dq@pFE6aVV˹_ x.Ο9Wg6,QV. :5MOlz ڑCn]٠S?.K˃Z?}||bQ}֬BD(SXyh8|Yc'wT66ݡu4<Jqtx$TDNI@\'LDsSǦT"8aeh $h|yeg|CڍۺC̈Mžwɔ/n Gy`tBSM'>pg_~it.|QgN\bzlCm=MPG/Nasv]p9jǣٙg-G 17m޽%L'vXlmܚ>s8O玽 6ۯm]=M6ʤy=^xI;N76\}EןW6ͭ09 t쑧~631%OjnS36rGBkGع5r富 ۂmo}+z?v`"܎AD:[B|tP?8bd™#g:zbgMۺLRO,吗ODnO?zoK;1 .~ =u!X*T>딺nNNO|KCvCS.#<(6ߖcEs߸8*Nm=P{g8 ?731p  Ewl{K x$J7E S"a _Cw;4XFІ羶yוGͭEM2-Vw3L!JDx'=rö7nc:HDޗ\3ͭPZbd3L >щ_x[7ں7vy_D er)@OϝzKg )7]/\gBO >#]PVG>GPu){# ح=7w"e7 pZf/)Lk3'gciKj9[d|Z ":-"!66_uhؿR>c@4B+)P'bpL<2\sko%%VwA M ij?w\kE|)yv$J/,Г5iyT65v~Z:AV 8s[gӼ/a+ "0+<M(~L|yΩ[MbB2qd*\ ʳd`Bz:,+nVV؇EYW`U[7V_]'/mv:{#[1U<9l4|-b~Eg~v\_рy)P,T:J\߆P˾傑^᥁0#d^/EfOq)veO|% l/Cj&!ASX&=sc%-CI__oZz !7arOEgog8%9߮\̫u&G"`kNhm4`p O,n٢YpX0m:W9جܢǓIi$i0%ˬ6DJu33"}ұL:Eٳf_V T--7b_dj'r}:R* 'J~k-ĠWZt0D-4 ̀^nދϥVlY%_. e1RW/a2>n 7p64HbCk Jb5|o8>;ڹs 3rV6`0 W{ڢk2==9̵/`h̶\̸];zpau^;{,L|~ַaaU‹n71W WY14xWrKvٯӟ31XaN s%.癓D3/{])Vutނxk7]JpVZ27f܊W+Ks{-l r^ޖ1x%zD>^g!\8 b7ұv _J+i׿ܫ|`(~W [mP9P=!P叠pӉaZuBC 0:wM jNX˻ ǎ<-)Evt,KӲ=(Enl ɥLӕd#:nf:)?{y%:Yߟن ;v+׬(,|q߬ 6?tr'HKh*^c{*:ffí\>eSX/V[5]/}56`zTXtRaw profile type APP1xeNI < BjPM#:/e[齽XԀJ·DE/a Y@̝pֿnp @j|:RmW>]#2cfP *jtFsBga~-RL#IUU܇,܎WK@UiTXtXML:com.adobe.xmp sebastien.durel EwIENDB`fotoxx-18.01.1/images/viewW-check.png0000644000175000017500000002446613222767271016025 0ustar micomicoPNG  IHDR@@iqIDATx[ypyZ@xSDY%Ye[v:G״[wiڙq̤nI;N&M83bΦ4"Gɖ-I& @!v?.o߾}{0s3_Oc2_IO}~p ?#V09< i`0j`4λ ?y}n|~L04UA^ˢ@"AFV bdD)BA- 𺿐/@52DPx.7p_|ϵ=c@U72rDbݪ\NDmu>'N3LBA"kB:|dO/ Cx6xB}$S? n|x hjlM}BShfU5>M ^x.80ν5D:ف S`5`ˆ&.bpP[15xSNHhZ Y ځ{WxW 3 7p]JWGMa@4z8v>P^x'mW RGO"`iS7[tQͳ[8 mWt /m w#[kCC"Aa'P32B hE7F"oeo~8n#H.Эr5hBw~8J $jمI:Hi"fHbc{~6*sгCP없Du#9ai &4(-η VVxf[\﮿>TsjؼL JUWf5eũqryϞC&?iB,,j+`1) =ž?zG5#h2hJj/? }PUJiLzmM aWzvɀJ% Չκ;zǙɗȋ F&3Pa"_bEC_eoyz Nds>y5 71ň_ҟf4)wߡ670~zT9kMΠj+nm47/!m,tn :zq(&gO FWɘa,!$X~PnCS j@ 2J-uY4TVQ"h:5~m2aUвFbVnޱI[c&(F\ "9Dj/aҕE(!+a!<}Bd ((}Ԡ@-!^f,fvZ _(;fd79:3Ls:2x;7ȄoF JsSsK#UY"&&pnU\:NϜl< &9:ҌBC+սiAU̙HdB#|8B{3;QvμdZ >w7E9i׵Rď0+u]> 2j k++2@=ѕr`n: !t5(r>d&T{ ` x+QZ 6UJso؄.dr W۶tcffzg9 2'(Y3 JʨllkV5 L=<~C֖{(ۑA%?Y[7} Üjb8$lNfaR:^w;'oA;07`* -C#Lp(e}c3'um؏XpUSCθoT^Y8hL6M$FT~4GXpr|K`9$z2S w9V6ٯbKB#x} Iϵ " F!NjXL2ӿו Tͬ w+<U|6d}֤|_%N shwa,xvs (^=V^q'BEKcezc=?}m?Sᜡ jZ0{MŠySo >|:T 5 !+'[0^ҘOd`v`o C^\MoڿSt{t&&}t>O1R?9!Lܸ%7¡LE+fh"(mR9X#RTjCAOB؟JpGx>#~! rNwzuC_M\Z &9gt(2Htf)БU>/ YKTYr?T Q+04*gC͌({F#I:O?)]uSmx% V}*=TN#]0T_=tYdV+Ճv Yt6C8LEREw2ń!G5TpKuVl0K*NQ5RquR$Uͥ /i^2֯YS'?O"R2 ܮv:B#Ry&}%u pf%[I `u5 x_GܖeL01ַ5Ѷ #,oKϋK n=?~]xJ~V^|_'Npz\LI`dž=DX%Ә ] Mf)92J5@1TgO>U\dtWhSTg]_n9H;>9o@%; hP,NzLGSy23G>c'`͏ζ{qbeN'P7*=w"Ĭ* [.Ee ;飖 d=Ji沲O;v+JmJ E<&(1yQ~ O_%esY>Qt-G)ז?&?[8m@Lhi}:lטEnCh#~F[*P*x^2ay!gU_JMJM9^`XBU{Fp%9K LCDNE-6rulM#! QڵVfGMoJe~&@``ί3aW?II*ZaE2x:R,exY gc vJ 5/uJ|!+D:#t[HŎW.2<+Zɂ]E ы}֜a}o|9Dq>+D` ۷>cۜB^աVl:n yD_@-/A&I&>EgbBxHN C(5,U|(H/L;#9خV+a4NQ@2%&+3pG[~NJ.W=3zpE?ʝx|\X4M\H9QuBT'okhq 5LRmmԘʯR.Jf+^C냃g(dXИ|h.֎f /G|:u4€Woq|ZyQP,0A_Ҙ).+~0X Wf~KzG#8Εiy俐UL' PbVE6Je+^W8zXZd|&P/a ֐x e,5VNT\?r&%Q_e%%VHRHS%"wa -8/=uN&XĿz"I&]' I&K]HT ;M]tp b1(:קd1|X_'㱘d9X:A6'L_sLsZ1_QUw{;foww @ԨD,a9܅-)bL0F488tSCW}M酂|-,gY(C_OxbEhƌRiqi5v&&=6;:w|8>;?κ^+mNCqe6>A4O;pgDpH],d׎yE:(2|m.[!#@o'N7t!+]UdOʢQn>)rR%QYCFzPw'/Ur^=kCdlãmE'(.E3fgٙEZ&ۍݏR' $?GoǛ*XlgVeWdMp݂a,4d+L\f#)wR#lf\.w}_h^٪QKğD搤׼\{8݅]m"?+DZ/V*jd ݬ72ARޣV71!A-/Ybrcsj3λβʔGB!fcI\*De;J5p9\UzJ3ړdrpHY5r-f[\@O65nX6.\%җ~( b I][J'MEB|fJ*-i:.0Rq*аCc`Im2Zw-sԄqFL>VjrE \va{oyOK%O)noö`o$C4Ѡ 6Iz=Uqᄚq &CR05\] )wȗ㬕fhx4ٙڣBgM=Kd1Er&U ػ19ҳkXx8+U=uխVmf%h l|3i"'D3|5L w>fVbԵiҺ42Ɖ;je+)+.~k雤Rs䂋8ͲlČU d)QEkpr kn.B/%;zn+znL$\c;Pײ1F#>Sb1TVˤR){LHr+{Qק E]6n c.*>*QSӄVJ+[S3s`RS XUL_Ƭtkl?en_Hr]-0> &)(y9*\>ܴ]\.:r"aS n Xז55%{kU k~d2 'V>/='g`ngQZJ~䮱$'90dYb([aY^|UUb<2ǐVUk`bсF,vNo .Rvᶃވ9EI+aH8Q1u>S?[B׺ Et8 ֙iȠI$ BDIKX0V/=*ϭv_%ōq''Ke>OTn2ѿ H@u19:\|(@&C6tB 0*Ak_ز,'FL.tr1E\7᥎7{Cd9)ꋬ0BTUQ\z}]; ٽ)&|cs:M 3_;5ٻb0B `!Orڢ(\f_Qi7!1?sE+<,r~jUO,Y "WY7r "U^v]34lBLr!F)%v.M;NI|P!LE&Ĝ$lg=P2'\n1Rـ11 q޺VEqqēfIuHuס2ܨI,f,痌> (0=emg f}#j#'='|Rt-Li9KYL UX] X~]i8l^tIME 4eXIfII*  n|(2i%R|gThumb 3.2.8HH2015:06:03 09:22:21022170100@@2015:06:04 09:33:32Fotoxx:paste| Fotoxx:paint_clone| Fotoxx:paint_transp|NE(HHJFIFC  !"$"$C@@" 6!1AQa"q#$2BR'!1qAQ"a ?L$F"f6 F#"f6u' Mҥ.u*j4Hj42 81ڳdJiiϥ001׼zY^~V)#፯F){iizxjiY<{l.L N vHHb#Pir%yaa99$%Xi)Ze5u53WV5.h7c>"Z tgK7;bK%,STWf*$ř>ִʼn$ܜbcOO0yaar/o uc8NdmJZ|f\ڮ-ϫ8kRĊLe ES4Z{YcuEᏛM˘f3c }ɷ u{JKMW 6/`H*tiXڇ|뉰{c_CVR_W&޷CFx*C#K ϶/}$]2#c@1uozW5L-\t§ixgB7XsOPqsr 1 2 3 0 6 64 64 1 0 ,sW`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-18.01.1/images/map-click.jpg0000644000175000017500000020575513222767271015507 0ustar micomicoJFIFNExifMM*V^(if%0230TȠ01002015:06:10 15:42:31Fotoxx:trim_rotate|write_line|write_line| Fotoxx:trim_rotate|write_line|write_line|NE http://ns.adobe.com/xap/1.0/ 8 500 1000 2 2 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?P((((((((((( Q53躆-./ͥCM6-0H0 G"RI`GH&'Y9OБR/Nz,NC_3|4y솕tI 7g}p?Uj~,ueJ.?z7(|C~B{5+\갃p%[gzWG4f8ZLY?x*ʤξU&f֞G} ǰ|Y?A^ajDK9oQq=y߉+\5[-6)x-)D@,HcGGpoǐqJ?"=u&MfR&pA1ƪ7M.K/*Nֹ v2O洷i6cʎ.<&Ћѿ!G\}+ O@eյ}Nc97>;v+0Du$OR_T>5Ɖ}(k>]g|~K||O-ks!Fqځ{7jFx:m<ch:"ۧ pAU,#[4mn _[yjI(H#(e`g'89"Ћ8gn7u`i:B0\hAb?-<0mTgMSK~,"9$UrBx 6< B#9Q3+>+^hKkNkkHw G)vqn!PsPxZywjoo]Cfۧb(p|`w8rLhEߐB,o^{Ri2f`QyA5 ¤$8p+Wofd2 6ZC #3 d ÒǪhEߐB/FrڿRj{9uz7(Ћѿ!\F_uz7(Ћѿ!\F_uz7(Ћѿ!\F_uz7(Ћѿ!\1uz7(Ћѿ!\TjG8r^ ?"oW!ڿQ_~BjGTs)hEߐB/FrڿQuz7(Ћѿ!\TjG8r^ ?"oW!ڿQ_~BjGTs)hEߐB/FrڿQuz7(Ћѿ!\TjG8r^ ?"oW!ڿQ_~BjGTs)hEߐB/FrڿQuz7(Ћѿ!\TjG8r^ ?"oW!ڿQ^uSn#d ~ls9⮏wiwxG*+8֢+B(((((((((((((((((((( $f_<^]H|@'Wj?sm]ƙe$s6-=E'pP)'4:Wӟ2Pv>~!XѶ]zT^kie36 8ݞg|i#I|?n4|58]{ 7I gu(-E$bW St^Y߂oXZiNӯ%F~s By,P{VQ;ۏOZn1|4nԽ70LH8}9K}s5\3ѴhItGJy(sGGK4iD쭳+8h8f߿q*@#_qrxU`A' *8ǹIqqr?:k?ZAxk:Jd ۖ 7Η@!v̼oM2-_/qi+0x:` rr?3 ƫ/"^T\:C]G,{LJ,J,V_)wV>vXź=F̚=nipYw`!-G8"Ȏ˸ynH<m/%I,0ʙ'HP궺Vgަ[Y.q$ǖCў+xφ+5dy` * I;C u%GדGIx4M.-# (lrۯW0YKCkK!/EovDuXC?gwH@rr9ztkPqF|3$$Bxl\K*cü]g I=9"|D~+#5)/)f,@ǵ9D *.x\g|_ߢٓG#iOwik5C $TzzT' '??Gm {8k[U8K ˓pwEEfO#6t^K '6gS!9cSjͽ& sN*(vlfDZ,QD,v䚓\Ư<њ>c?_3Ye3?@3ѐ34s 53Fk,8,~?GrAfjf_sqׯ5 ~Gh 3RúUEqwmo-l%pFPO'?O,<њ>K%O,EH ;PI'x2-3YHeXn$~XI5[мGMSKԢ=3G$4,Od|kV_i|wy0fko/ů5¢^k@K+pW+y>Z0uX4R6nܱ!Pč@ 1ЌcAv\~#vM>I$I ܯNV!UrFH ]/'ZͿ՝mזٿʲa~\,+a>: ?˲#5WIjUTqŝ8ipȑ7y8P"8ir3s kwĩOԘD\0[xvn,hG˻vcnt[|-fE Ƿ]F\^[#|Et= [&7 Dq|Ĵiyw~r =']GƗ޻U+uX! l7b1m\0]gKkո+! XHm# 8e iw%kLi Z*0ەF]JR" ||?e,&h]mq>7Ck.,m"$+Ȋv'{ᮣKo]pm,yM 1o.m-%Y E2 }mX=͕#U(7O]M@,5GVԱh243% dJj$br:@# cO]Myݎ-~ ;8GQ/F1gkWYh^8 Jc9*~~NƠ|'9y(jI@#9pU١Y$u<=2g\uP gb֯#o֖v:tQjy+X3.bC\+ᶡKs]\;uH nhb%L֤,㪠cŲK_$s$@q!H6b%%d*6]Γ{/BN HuONKk $y>k(ʫ7=W2^/4KU&u,$O+gUƟ5\HɄۻf$*ɟL3u`wB%ť#/N͈$:pǁZhDᆃRg'~cy]p(i&˴TqW퇠BhWp۳2C<2quA|W@t ۮ-wÞFW>#oSVU_S4ԥxg ^Q }ڱ~+0|N->IS[̐)(!0,rFs'*>+7_X 5`Ԧ)uҟ!1D1cylnʓNGExNKԔdl57vwys49`zֳ8ܒW2xk&݄W]ZYsn"NDD4iV|ۦbd"]@h#YV&?9|Hx1xsKm[\O1 S+6 85u}7I./ 4m #?7o(Y- 5kUtcV~!ukY4(KM-J-XAld23*T>ږ?ںSr^96l#ìҳ(trcZS^,^yEpТHŔK$[ J`k7^.iKx^dA#ܥMO4s7 KȼEǣ{ yu,^:вDB%|(u_nUY@\,-26T5FMƅ|'oztYPI:ķq1ȽP 6Ϥz[7P6r}T%>l0gB-累Zņpi 4q|cA,lQ@`%M |F4+m9eK)ƶ5N8%6D 9haBulU3d+ nΞ@9~Qiuxbo,/E7߻|F`Ht ΛY;椈Oqx~_9yx-#\qm##FU>i";w20RW^"㰆Oo.f>K xƿ߈KšvmXne&1C[yg!ZE<zNxNCZ{&ӪKo4P=YoF J\9MXTK3MM׵hgLKfe1'$}͹Gʪ3SS-Keub:Ƥ<3IqrQ>؝NXWrGsiuxWLky:\rA)\f(`~aŢm{(ltm:FP I6*ʱ߂Gp:d KNuK]ȵ-rQ 8Oo1E#}RȁƶU:{`5)縁LƉPKG1?u_\/MU3>^ >Rs??“cRHz _I)/,wːy{i(@$q暇r_I!isX\ð6-*5}C|:}QÙ:?GGbkH53Oqb`6伄6S EkeXVw8e@25_G}C|̏OMg~ HՒM.K|.Zv!͖B>f*Ā_zֻ hqwG4r O H@Ic澂ϵhgϴ? 9[ />!-~d y:g2΁00`BeKgGMc~3{εhֿ?ŽVxQ$gK{Hlewy̤%A#ڨOw&S/H]>+w=FwH O$y|m +O_G}C|s#+#n,(ϵhK2>nOc>)yEձTr*6ڐ@B>{F >R"\~r+/Ogϴ?Ù;]<B_N \opbExW6!0${m;n]^xaї|N1_Ggϴ? ?9уG֮uyIBn_UFy 2BZ}6߂T&Xx#[䳒7x'h8,"s-%cYXFaKrv@8O@pז&`w႕ +O5YwSrGҪ^+"I+8l; ĕ Ò@\݉󗅟cPjvQ4bUu I#¹FJZSFl&>/ .r\n_#k")kZ妝9M- A[N45nݥIw̻h Dm7㷄5[Xo` Mi{,)vb6k`Z|Qwp7tŚuڻX09# LTeFPJRVOo?C?G}V۝~>oʫw}?"uC 9 1֏h뿵F_ )ti$meYNC} ʎXGz?kl\@(wЩi{D]βR-])xW_? =Եm5{HS_A*F'`;wk"O|'wƙ|K֙&Zbwb4(0e<Uu%NԥfzdSxO3~2||o"]קּXUK:o1޴@cD~#i^+`٫K&1 x9wO/im&+Y<\Cl 5 }"ٱomeY.agq~:| UWvKd{y\ F,弦oOYuϨ~ 497YrO,F5h AC3HnFOٶ GO-ۦG=5p ~7-66W=#ZtgK;" p#|~(xݯ*oc=.sI/ѨO> d-*!``ׁ>?:&agZ;QDwy4(bi] @R P )?M?5x|Kqnɬū6vr:]6IHvR.ܡ+$|T OY6%9BGᅧ~IGYж%_FQ4%DM P6B7T=^նH|Ѿ/\M#g~(?73o׿i?@s#\ &,BXiͧ mZiJ." 19@H ޻c$<;W_s6; "EتT"䪁>){sT⧍?p׿g?@s#/|-}]G{jڋHaBP. SC3n0k~񿁯t4[<\a,,v͂>)|_Χ7__Vc]߷Aky%9n=hw=tWWk=Τ@>n2̟jܤC5>?SA7Q/p{IM"h9&HY3)?ݮ*?4'FxN*t~hn<<<@־*'մ,kVpZ(E3.LL.b#s- Tz wj%ljaa0Vƫ=x<æc4L l1APNH?TiUmFf Kb%['j5-O*xGM /̻8yߍSƻUU~ltxo<"4/ U>q۲8$};z.)ђn͗I^2M]yIZ{y?h~"tZ}km3ϷE@4ΞqJ?౿gAa{^h2yigX^g' 'Ͻ^3Fsρ;s ^|g>#K/uM&(*c~33A?A?F<?e|"s3xky ޽>4| ͅ%YAv}Y'<>c&ɤ?Xߌ>}:`|$x*6}3ǁ1 o,on&?y|!&+|gƁ< Ͻ?Xύȳ3pӛ@6E?x\Z Rgݟߌ<3\'&;8Q cŅǧGlR0J}?౿_ҏ|o| _Zv}Bǁ?Xߍg{u.ϻ3"ǁ1`K?Xߌ8ρ? G=j5ݜPۻ[Ï2URUs':o~4бO]!|h}%lyZn0sAYph#E%Bρ?4v}Bρ? K?Xߍg/NL.ϻO;@gg@|h?,x{u9f}Bρ?hg{u1ALjL%?౿1"ǁ?R%Bρ?.3ȠiSë>=sy4\ɀp76zzo?.P+`LWlBT.]~^(lzLn#DV57ܷqx׷PX`tzb!`Y2rUIbr0;kّe0b=}v N3uz~&ې S)M0]n$qڠF$1,N}JSG n#!F+8PȣapRqY:ǼL+c S&r<ڣ{ԑV298s /cbC9AL0!A8:#if; <JK"IaQ^9JͦzdBI 銹{|~*CH99?ZeiX@ 9Oֲe/i"!YPIŰI8)rjHyc,rNXn/nbfNly"Lð:V=֭iOj G[Xh&Cl[XwRT1PN]T|:џR'0֟q Ó8ed' C;1*P)A8ɽ;mI.m5hVW!+,KSm.< $;ml#\`mC 㣷#-yCmG%V `㺍+ȿhxڶ, f ='R=LM᠔\ש-MӼ`sM?ս9港?ićJJkyl3Wd F:ry iG'' SZxzދC6qDz`qB؏Pp1KSE5]dc~{acEe vP1ԑ{~ÑbL}tvU,:؇Nb^,?>;WPν}^/6Vㄎe8U&Ne r*v)}[m[&b%\2.{aףG0es_}N_/Pa7,W>ռ۞_ b/>;}OOϳ]Za # 0Xq=5ukK;V[&v.\nl {_!K|E&M?QFPc$30o>E:˗e}3 έog%'׉!:LnocMuf pO@p?Zvor`mdB<~+K s [5"Z4Jdx|&6Rp 0;;hvԺu垩Z;$6p rpO])A\t(7higC)]%Tmw$q_rCk\fx ůj -55(&.Ფ<^5s]N N"ff}^H08MhG,MH?s^NJ>U$DѸ2OO;T{qWVۜF?I/ |BPQ @L2?rxXGHggF/?{4D>Ah:qz׮+㖑g|[|6:jinbrdbˆI*OꌴQǞn`=N+_? (hE>G H n>هhW[[liק\\oa*/.U+/ yh?r)s;_9~.6 8  )^Sk6v>Za60r@^c xk4w۴RDJ63>nk(RG}c d{@M'^ Ѭcۙ4B ؊^f$rxZfUPG5pgYJc\?t~:=+Ӷ[sÐ%W]0G07dg9uG B3NoS'*l8mY%lP`ovy& hw77x Nc̳<ăX?X+ab?ֺ߆Kl绊H.ϖds+Vj?4 W@tt;2H  #kdg3\x+WkCws,!򁓜ceϑAa\[BQΙVJ aO{kпf?kE\ioL1]n\m}s~~ I~m4m2OcYl%V)|t1xdi" \n~V8'>!5 %-ʹKhL)!;snWW M7"&,ضN">[ҹ)Ԅ(;> j)JKG~YIm?7Ud]9aZ IoCڪ{8GtBx¸9⻯#ʒ@b3H4l`S'9sFGBT `SwZ .3E&SԔQP P3ABM1h.F(ϱ 4!)(qh} 5izb}#Tֆeq击.8sd9_g#A|?nsHFprO<;W_{9 =Squa8.y"Wо Z[E%M~W $h2ǐӉJcb;ԍ==\{o>[Gg˵KFG!?Sq9vP9#+<(<7tigm)]rK[(<;H#K_~9W̦ĬU"Nb5֨dbvc4һӌ彼93e;եh*}iNm밬1u[Tly/oⷖ{{o5@@bn##U Ũ’xZHV8̌r˟`ܶ EW#) R++9MM.ǩKJ8I$|U/'hI> "*{\ٱ nzqFV<.ݫ!qx/bGxp:޵odJplo<+'\{W~bZl\ۼkq.Z2$ uR8?;^Gsr]i9wuGo)gW RI=kVt%ʼn M' [˹/D2o&%G`2G$^.|G&k )H- ;pҀI!pH}Z5+_x],lֆfy'$}yE|Ccm4^iy1.2:6 $HWMLU ۿo#`N4'n7 q\7;քiHۛte\2{w_CJxRl5{]6OI Aϖ}Al[_[^'+ 48&13G⯈AAqx" ̗2\@oH [Zۉaʹ[q.-HX99bkJʠ'4yog[.dJJ/P>pۜsߏ\_"'|U}z R rqZÚd.Y 67UۻIN>S$\Ou2#%'ڳE|ıBw&%q0ABD;}O_$.:y[ yD1\:AK>5xnF/cVV}$c_Fy1bu~:$zk*8ZVpUխm%,1^ǩ|=дItܗ֒#D)M峏qX{ Q%¨o!9$WNϫ6l6P41d=Sx 1›SMd..hڳy WHf\-#})<~;xSO%>\W ` *z`g|'e6wK6kg "Kgiِ< @ǵ/t?725<1goj.4gv@z3z鸿slE9:YƯ2X$oEv}51qk;wM4j|C4kh<ZwuKhz7CǞ^,~12izn3Bv)}:_)U ԛdZ$"pugBہlr۩_iU\f^ 5茡OBzR ^kOڼM^47E ap,'ON+]AJmؼ@zry-3FF[->< z5歩j:6Ĵ6N*,Dܠݖq^+)k{Y5X5+v(" vIN? Y~G#:2 ?˚'M*^h8''I77Fp6֐~![Cuv:iy4̊DN.F'MK|D>-E糳=˕'>R(%,\ynص@T_(r q:zbVRU4Ts,LQݪ)F_xž/ԯgKլ."2o`7*(aGgwxOX-I#F##50x62HkcEòUxƟgGxv[^ Hp `)l)ldU^MeuKD>T` ɯUIewEJV/]InO _>+폥LMH%|%J| u߀kd-^OKO{:Ō-:(%6*rFCR*Wk/ȧ,Q'R/ 5p?Oc|%q(~#M4٢E4`ݔdzds^nN?ߕoTƒsOmҺ>R?)/LaEз|mc$X\'k xhմKk{y5H"iU2,>R0+=O h|P^YcBO_n.|ARF`u c8\FpsӾ)}rV}bҟ >t7{lCI<.ofHmbI1Ec=K_ !jqU4ZoϸokrC.>|!72no_H̬yǽ;g& -Vkx!T?BO?1UKRQ]Ùͥs/-υࣺ},]5 .Zi 5y?f4丆{x ?'i'ᯓ9i'JοQ*&je?N>kFeޗmDZ=C] lTr•[qfZ2RGIzR銠FnqҔ61@3T+nHU*a1Y|[}+<˧wZ=PIv蠟ƅC[~#}F ' 2;W! {WI[u}B}i$ucI"rMiґZt 1(P%FKv((t" {QE?{ u {a$rX1_5X{-'H6.m ?ͺ;N0=C /O7nto]6V6Nxׄ7_A}oⷊ I$|H %[AL2KlAYiV2+6e2;5YYC;:irs…yTtcY-k8lm8ļNU#W{g/zi.n-lGTe@$59Ӿ:/R^A1R=9Qדֹ4fo~3 yԠ t-ڦ_w}NaV=== ?>':-@Llmfr)޹nxfy#+nL2C@Ne^xp~(󐋩@@c*)O>/⪕H24ϙ/*`G r⧂3 N;s@?!?_ # y M!^7E:QڹZ bI>H<{s]3_r] ʫ{3tjD|Hӽy=z֋%Ai! ϱA-Xi='[Re\<- qRAvvji9jubfRKK--ݞ]Ksup#\Hp=;לkP%:=ZagIf&M*$]*pGai|oj2!ajv8'?+ºW4+ _[jq&MO[s;0Z:N_$cjВ ~%RC-` Vp>׼(v6\c%ѓ\v Q/*X)X2x^5at23Pb)< |Sžhhm 28C[E`*Orp>^:iŸ & Q*X8.=?KjSHi\Cǃ<5ˋ 8'p</iRZO_;I (h8dMtXn[=HO5)¬_  i`!}?jc/#-?"o~XG:5s}_<<5i]!W pHP2}F\jf߄sygI8xżaNcq55Mp <jCA!~k(a% a#_B|:R"6/M FюGj][/ux{Pڴ$𮲑Qh ogawݯï œ鞡X #4?v+IkVix;Tf_BD~~&(|VN˱|$Fp;;eXmVdrʄM1d+]Kok0M̐Gw2A^ | "tUY >08?/ӆt-7?;HH'ێ*4T]S]+E𾮑Mv[r< ,y@F@-4m-gD:d"%i۔R?tOn)<1&3?*#wH3V!Z%!ų?[Ѣ_~!%eI!>[c|MvЊêR%o?Nuh8C}-]vYj:sh:~+$+  ;PJI6ct6m՛jKJ=]Sᖦ^@]4 -B%[<92lvL+ѾH8# kGW5/ZƓ-az¦U%qKͷoCqKZ*4k%ouuVwZZé]%# \񚬣(Δ dG'$~zUH "}|ǰB;/xK'Y#EZ=O,v3Wg;/ׅ5Yk=Vٞ$ AҼux= ி&_firnʼn :W#;W?th|=MgM {DX˻%IA^tF0A֛p?*qṥ `n0N "A "w/,[DӖXy(?.ۊN۝02ie2WQp)8ma1~3v8R[[$hَIIy4!Ic{$݄ cM^mMdm k|IdX/kpۅz+Լ=.ݯ,G}QR9IaixylM{v=Z5MjB2{f⸏xVZJ+;HQM\8@= 3^}V Ⱦ/#ou}hܫXaJFRPFFh@4E8 q(4d&;4u""E &3M?}ooYWmk!/" .5X$Id%(iT}\| u[YU+1[g~>&ꚕ#:,>Vݐ0լRUVwMoi_[N֚L\VVSLepoĸ',XrC`7>{v|9,e#\վ'M8Hm9\/DHwˈF}ScNuJU2IC5e@P7/T=~UN0 'J:pF9La#yUc!n#8GnPF Ꮊ^UM1TJvPBD'yytrSMٴBKKIJc Vt z<<"7]G⥱qM>Q#*IӚfG w~Ïhu-Kp^HrsQl9/ ֳh{刼q,$Azkb,c;TƭYgiS/kDzژ办65IY_s131xlEZfk, qcɯWhtQoZ"es29=Jz}|Em_?<Tf<'jշ{txk Vn䈺1z{4GE!<`tңQGǝWO;_ًp?kQ uxoHʶc19#ڽ=NOJZL'>SPzrW>94oXKHWdUIes'Y.cx#bWw|<{T9J>iRmR<|ڛjʬmCWahdžzq?r114Hs+Fx#Z}^/oÝ=>WO%,e H$05Jqy 4*W7-߃0uWI,YJ7c,Г=T5!O1d=;okqq$ML?n@~Ѻ{tIT<8!+Bzw5:t[\ə"ZtO(.}OzW?W8Jm$n5  ʋ>T`FVXG eI63lWʮڿOw^2sm}EүKmN< ARG}m&uKսL}@lKNy}+7<m\]^Y*b-'iRFl`qxNjBxGmtWytJѩ")%3gڼƵl>u0Z"ӫQ5BIkEͤlIտGL֟~$i"[-g~c])9+VfC4̳` ^44q9U>xL1)vo7#FPs\WnPXl3X 9}|@= ; 4_ͷr?/=HH6R~^%(rXaOCϝ#8'Җ]N6ڦ302N8'LW-"|(gcׇۚY #,dTA$ 9BRI}( }yq6+FS\Nodc 0q\`ЊI#~5!=7RּK_8!C8kHhψqt17"_ j&+Lfuu͎Yz懏|_{jZWTU'@o^;=/oc1d0@2Ā$msF!WWV]ˁyFK,N?J#7wf_Kw0͒)A'GVJQp3&`XG>EqqZ488b;M8 +tW,/fW{$WN[) 07g jFm;>è@+6rDݚ[@'KCa&=Gq5Ջ죈`^65N MπբvVS=6O-̆yYõvߓ^HJprƸ2߂GҲb*Χ$m*TiS#xJMM}9Y8BCPЖ9)*A;@]KYr*2FοA~ξŽPp1=j-IQxX:{ikc"Šv W|0]^|9D}CO7d^.Ƞʒp=+MZ7"yaeOsDͼ%:i:א̳+"8_@Oz&#xkH4=.ό-]g߆Ɲi?zXb?*ei$D).$і%R iO>Am Xg$^v}GZzA}u\\XMIY1k'ڳ:tDy )M4m+16P9ϧjAx57u8 9  A2sҁiwqJ -4M&j??Z4ЙTL[7gq@}@ {);tՑK <w>WOЏ˭y<.^ٯ[B3+fv"inzW91*2|3b6#D$צڥӗ…!z`uXkջHǹK6r=Эhjuԁ 6ʁlcXsq#ZB8ZEtexdZ`pQCWq,4}rWĄ0hCi,QTF8>yYHZKhw!vZzZ X]`GNVxჰ@;gҤ=XU]N7͡FE18vOia;?;Ӓ>_N*_&+$ Rڃ1<-dW7rn,F#!{ww5[Fcq :mO}cp <]w)Gmou p\ѰGTI2GRa"6=9ֺ I.$b`N:LS@֕31xLWUu-UHVfbmUUXW [jRiz^ݪ a2W!D x/9)QٙHRpc/܊lkf%NLrŎs-$NrNkT[Auf_閶iM2IfW.r9qB2^\i݄K2!n#1瞣 kd#!DQ6=1VR+uCH}Vt- Dy[WSe`҂a@S{5mFLkHG'y,to>[[[$ӝm$~wݻ]V5m<<Ux$dN:t浥X N ˫}YX[1WYFGoRFK]e]sw"pb}p.@Eu*̡DȮ{Nӵ- W>,5cy.w<d.] %rPAòXXZ_t.-DV"&U;:TR9{.*d7l[_2pndHueeMYW 4YP37 "I- 0APJx9[^k9k Yd"% HO, iu(-m*DmXnb1 w.pHnt+mB8;S(-)U/w.8RF5[4.n$qnYu&=I%SCǤ4ٯپJDVѨBa؛%z<; f>cvϽrJ!ux~ĞbR5K##7lBZh}LF 4ƶ%ݞ} y~]Õ9DzI%oGϩcFGpMHa|4ǐk:;5X@ЈtVED1!bJ1ȫ>+u?^jPGYۅ%DN(9< *$Kaaeɨ}(UPlf Pafyt}nƷ2XxöLV&eDބO2to\Z5+ShbuK/\[Ou5Z6UFwYt6t|<Ӿx^EaH7;7V=9vn9NzoWSڶ]6s Y 9#GҔhB(q7J+n1}{PKl?"YS%:q-|Oc^5ׂsi-GhG=A1Į9֢PԷ| Ԍ$wn [\TnۚG+/ĨSO'V{6>kiO-uHsVj6Ja$dLR7zZ<6Ƙn}ebɘﴳ*(9RV V"4sL+;{5Pwh檿u+Yj }e4ZΡrHܔ@ {~|2FkҤ7 k+Iq6n:zqURLgIK/Qa,:qֺ84X5($S9w&`ʑS$C۵cQi*G3xb'sߢX^FHrp; Psb9$֦f|c5,S$T8{=ZTGD$[|ÃҺ^ǹYPyY" .VA Aԕ⤎58YE}?!\G0h ck@)6FPb?bҕ T,;㊜niyr><1I W\4=}O7<#kKu;f-f%,JcX5u)ѧ`Dqź`~k|%ɯUk3̊voA5JCc|{j~-٥ $WnW#'n5hRĽܛnU|wlPYx]t-1Υ[fP͸$UNޯ>#~~6ֵbi3{4@=(1޲% O߀{TSNUJ3B?*=<7nC~9ì7%kI<`Oϸ`=+IkfN1QJ\],<Տk 4?쎐ޗŒ\?| (*:{Z[Z7=W5ëfgdg @sG't:N(vfuۊ6WV*$5w3ֆ1q+MSCR4gqN#r֥6xn|dX #}k76za;hws[ɟ*UEV'ލV zW_o|_Ś=GTЮ쭝HD0#״Eu Gմ;ۘHm"+Dݙ&rgt5>AŚx{P[éehi嶛 1p=7T`0DYpp9!}kO-OZۗ67^o"kem~^*k,iZD@o^p2:V,S&=FM Ó3HcK[Tft2yOT®Os(-;N qζn59_CXU{L>1|2OENHN\Rӌqeegcha`hWm%*Kx+[  z{Z]m K)ث+d)Oqk{Q-qS@'N@@ϧmte Pb6Bg y㎖ڍȌȅToVOq}a)~Qj!H$e\݉"]/RO5ϗ“ֺ4cr꬧oz[5WkɄr~ Q葯aFG ={TYCty۫4d NxN>4SͪGe^/*P:A">=zi\rnt:o/m BC?ǎIϽRLrж <+Kg4久r#ns5-~-7k2:BuTt(vȄ8bKD:sǥi"&TsZC=+#gp?ww9֭js(~6 Gt8Զ``S#P OGs qϧRPp]A<;ٵN"xJ">S:SOE]1^)]\(vr? >!]}L&Yy{sx:?߬_P۸PAD5xê[ֶ׷~lo% qy=o.wg=mQ PxKO>ɔ]OQ/oz/ 6Y%Jk'̓t IלKol&KRʦocޮ5>ZN EQ 98_l#N8!:?YQ s@Ypk+S/o,u|Mkspvڲnc($M|)Ho`%# 唻aҽ qxMٝ$ˈ%>QB[f*AA?Kÿx~}#X>UCU$9ڻXž6|Aqca˩PNcm7.'qxhb%98/ 4XI4<(EQJ]ːj,kG0 <*͍M! u榳.oC4(A(]tK;K4M LzmG'l>|D:'s=8qii^72\>dA[ @>¤kjШU 򦧦k^E=zߐ8Xi]wc;@$xPqtS`-7kX7oeU$mbs*'mxKxQ9_4Wc)os{n(,AP+;r: sLRS+pI@95RV%IȿzgR%pl*&ԡxh\dz|=[ǷctV#C6S!c;?d\ ]NhF$$l!?88ScϪY=y>lBc9 =xNh4xH<2O鑆#q3Vdg4('txNjW/k۳O>zօ9!`X԰峖"tKÑ>Νa-W~v ]ć SoJŦ]_ix7;1 g\$1* =rq]xw.YJ >>@IBi <:px_>h6΋dy=yBK$5cv7Aۚ[~͚7,VmҖ>Ad:~|e:pthI{3W[@cpѮ;8rvWE)}wZ :NmpDFB  okc`7H H *#q߱m4p.-;_g'$y!QVA8$!\k@FO܃l&{g'/IK 7OӛMKO) `D^0 'x-oFǎ$,̬8ב q`%ObpGhzuIul3T+tx"T:^,Wwk;䑜 kEsE}⽆oښ8a1U=@FFx^;7ƭj6U& 8l(T/sGu3ciqˤ\$/sEwcWV M=LK!ok]!mSF <~Y/^^^hw2Z_ bRG \9omIwl`A5M/j2:"1&%$%]n;scZv2RU6-S/wQ1qy?ҾmW}.#[*(U.= f;☯LYi-ۦ6UNnñ=3 ~x:Euh/$0A4lA6`{7=X.E9F7+3|q-^ EZ(k(KvCr5OM.%6%ߞr(Ė*pN8p2k?@RB)N/VfXHRfk U y q^ǁ}O࠺7J? 3WC;7駏s_AAu[_ JYE'O}xE UNйSRprkjv:Tc qT# ܌)r׿h⯄-/~*idn?QgL]xÁJ NE]R+L|﹁.e+־JtxkX'QN1:yjĸy9uKDy?v׾+jr4QuqDP~ Oc◍!8:<1 1ݽ0I+;M;_ܭ+3np0=ti/þ>ȦG&PGOUӫNFzѭ_yl<1jN֗e |Bz{W-q P}+o}n!od]0뜜0חfIe[6vF`-^V6LNtGumysh|}"BwvĜgS[sAhyq_μz8{h"KF@WZ7,s.lh۾1K"irq ~.ggC~LTy{R$⾯WO]C5f Ol9㊷],KP@\c<+dݓ:eIJM|GW0G*f@?iu2%)rI521OB&FAj\Qlj]#t'cHtA/S8ZXM&і4HQY$%DD{z;UCRzKei͌JYTlK.e8t#$6ڤr2ono.P]?eB~̪R=]]t(Fޟ0C"#8 # <Ұnޔ42P7qXLkHR$*s'sWF`E!E7Jn[ /@=ӯ+* ^i;9 z4 ( dSbxΕ$ r.W8`#ިIs $U\ .=`q` z3S+7ݑ[i* 27hi Y'HM'gHv9#w9TKVHX:sױCW3V |؞sXޫ,b-ma YBsdcP3=+a-YV;KdCI'= $Vn„gs~_~~8ѭte^/&W|iR r~U=뿞$E#ۊyp33ZgN3ufv8«e$rlTF>a׷bnG)6VkU 8;dןƚ`AxW5[VKavqm#u/cWc멠ĐF_*F<zVa.F\$+,FӌgK-xwv`FOEm%Ԥ>7|{]rw¹.$;N[9ݟNk'lh`fC`WaJLkZ+H^yFdߟ\F)~`?#wVs=8ݬd2[mn`WZƥjjYl~|_l~%5y hIm&#=O ׈IZJDGE}:fSwm]rK;[[[\=s&C&~d?ypP ~s笳5(W3>'Ԓ:k9拙JYts:>$n%vPV>#iAԃ\pzݭ$ED\L7 E2+|'ҼOtOI[- ֌,1@*C33|ҿ2w ^k,_ϦkzFe԰wA FrNC/ϜcȭBu*GH6U8j Hf FWN%>Vz[iڵ,TD0D'6Đ׻)B~%ĨT&ny`|^|O?|)njiu{kv$.Aého+-DRjZ hm c5ƪT!UӒR:_=D76TZ[ِRq23gAķz+ި< 3\c5~z]^i~'4;DuNbU{qkPO|o:Sb$kէ8҅NZW>wx[[M)$<a*ʩ€p_O?: ͔eGao"]*9Omݫ[r2|S^ϳ.PV}T<;.n46;#" cj# H}kIoӕ)r=~~l4߄z 6LwS1u\~M3* 75k_x)5/DRZy 6U*\e8<i&/+O=RKX I! Ss_9| 𭿋?xBQ5@w) VT`}s)I[*Ism'_ o>? AfTEV ln 9=Z:_ 7vri>EfCg~k7-Y_A:VX/%9Rc+6v{ItIag_ݷ["h'*-K_RjKўo%|Gq>m了+VY&`A đst8߇>}5_K4~aa#{; P;ⷉ͝*;@A#q}^ao[nj4&kt%FDv̱Wv2TWe&6V>dm,ǥ\X,cr/_wBF N MekCohOQs#[E$pN`࿀'C>9fԱmd.cb[ryJF u z7[þiy*P2F7沍O{q|R nxPq;N.w|@DRE|72<'Ƥo1}&Eu}:Fr _lgDX3)-K)r8@ե)*J744&9-bHzOϡTFyD,{ ~Yk_4=3JFxːO*A;O/P[d,KtQ_^^sidwg)Ҝ]ɯk3RIcE,W8\23f3Lխ5 r PA Pk}y:ͳ;ˆS OL[&e @Pdqְ˰+M5t>:7?#٧Ǽg0}x634cnNq.k#9ݓotyI}> UF)2=PhFbd81TtzO@|9;l.;kx䓜ϥVTŎ:sjQHm+]ISrjV &ubrȏ# 8a c[| j.FÎm$&{ǪHȆ]J{׭ecǿIRH#evݡ',=͓Yˬq=3Q?1rN1#:ք++cXp+rvkQψVfM'@66s^/[FM6tѠ.8_r|ޘť:2pYAjTPTׅYڌ~z?qz/ oK,a-G^ -1u|)Dq|G]zR"C]iP<+|?B%m_OJ<{ǃ5 a{!Id 's~4i_4H.u8,5hWA$6u^; n|:5s'Y G#Dy];[DaMJl‰DӴ}>渌I}kX}VqmH|?Z(fϱ5wj=283Gyt9bg]C\1KgБf[xnU:Cˇ|l>fJOּ:^-0) q8n&$ I&ѮG0+ڼw70"|m`Gz9:nsJ*{vY2͎:-lYhz](=kKJ0˞Ê/3N#ױ%?3:q~57/#%6FX=J Fzգ.ok\*ΥN+,lb=)QRj,LFp J4;Fޱ+F3J$fjQg qosmBLvp ]|D\M"c>YӽQ[k2Yb2隻k̾Dkc>YWgfz[Z\ߴWhw@xֲk @#eg cRp9'WHIW1UsFYr(7m_kmV'nxa>,]TFz(YA$xR38ds@l M(Afb:xsO^(QlV(=Z\gⓍB3~sቒv=w 9ox?915\kk>m>ՙYɁظAְJJ;=ϩiImRL_KP1O,I :ʮ6Z>w%%PPkI--Z Klݞ)? "B{n {b\p3Upc"R!{ow]3'z4dBU]K[%*|9rs@aW949Di.siT7P=Oi{oaFTR}1I-9g!}{t MXE=Q-);Ucp{5-SB$u]$~U^ç۴Ӿ_v>Ԛtox9V5C+#jDem߿|gW|?-Nrܱ"IOLc'Ą~&M뼏zgۭiè@BНt#؎Ƽ F><⬵wNW&.6C'`toUp.&2/N< p|͍$$GYT2խku=Gj?rk7jۼ燎n: ;T`NJkHn)L:WV⥫GK1xH3mj0*UWЧٗF6Á[+cdy|4)&Eر7ub=9~Uw|'Iqj ԉ?_)=woia%̞;>"*>2l> +ԃ__CQJgR; 4MZ[gIbH9`GNWv?r,Eda;.9\ _5U0xM^)]Nz6{giu5x|:c8b,־%ԗgRfX>KaݕHwlke*a[qT߈]CTdB{#;7_17G#<Q^ZiR$N9$uܹr׃~'p}|R -vt}ij NxO־.0x~+ q]%`XwwQ9+P/]ҿgu;t!'b?Yk>k+Xbmg;T{Wz|%LP.ZM-p2Ar>1NNoFtc*~9vW:}FƓToۤT}嵫ψz*Y"p7B~ &I4^ NasK7Y u>}#:+M~h2/KY8RÃyJρ>#+'W$KJ]k.Z?i=KZ$~{}SFfbu+A'85kOUOB\ܾTzWyM͌{xB7)m83D/.4u?|]V6d H[R<ӽ{?|c6ZtzU(2+/? L} J|L9RHVh;[ctG+y7]?(~,4v?E/兇G5_P?IxCY7yHQWߎfU\(QYU%RN7iӗ҂M37ו wt |rٝbBG5izo]t٦ 4)t:I_JQ];mj^=Jڊ:K|]obk)gUA3dcӲ}[P*2p2zYx{oRXikRgRB +M[Sј|}is m% rQPO-Xx]<G$]I ,tgj9l5jܬ{L}?]ƛZE $ #o> xsW]BNi.}ؠ0k'=Yu 3=+ TiFY, !g< (b=,qjh<-WojSۮfSlG ;v:}GbW k},tOyo˞ ϨZ,HhtZX0=z\cS'#?*' xϽu 2`<_{xo rf0uڬC6lwK,sŏ՟=*Rr`"3*WKo$L' Uuϗ稯M#֤*the r+fM=tB7 CYz"кK6q͜>N83W]ߕşQJ/V3᷸.Q `syof[+I|:Wm*̇8==m.hbpT }N<~kmj-Rs"OYIuiTPkpsu!zv^AE4Xi r ÷|esMcz#-7 uaQFNW)rb1=uveW6 Sz!wPWnki3 ]UΚ;f":Z~ WLE #![ fڵ]#Kb+ַ>s?i`o9k=/V44cMkbֹi<)3X'(l)EKsaCIk3 xhNJI䀧 _|TfuH9s!L*!orZ58`uۨm.ehE -1Or;Ff;EY-ia{Io EGu!ܹܖW,6$nP{_xĚ9ԬO5rdXhC:A`GBEr0Z%l/JMҕӠX8!@2Qԟd} ƖZEOB ÿ_^JVN[#stjh-Z &vvm*Sbm-V1<|dt+mq TW7)0xw7Llgj1=j2Y_]Dn+5lRRϗˍ1$Z,.q3{sRNr 8`⹉ Tfk|*)x^@>/y#<ֲÚk.#VV_ :`Mhq<.U02OLdBњ3Z4F)X?RGT岊R7O*/)bĜI-C8 F>^ޱN2Q%bI[ p zFmktACp6rUkp)o)==9)%(R2C*673Pf#7o?:[DK`S?;I>qY=L0:(- A9 B?I.pl#tO1}VՀVlu?/oݧ_ҫx665 f- g >eoHj̰1y.֗G?*/#j gƾnx?'GgW  /_Ǚ휖+<so"3:Wo[Y?C>h&4_Gr`{)%;|}PFe huKcݐd6i|Au="ê2 \G5 :W]J5x& `dyOmq?xn!+6\H ʞ81W>s0ulq ~{yJf'q >i>{U;|aVszssݪWRcdelr@w\ .=N2 1=@WwDkqxF1X=I8i9-Jmhϛi]:IBo'rryhrkߍZhvV5}y6 I/a5׮%=*7P8 İl]yj:WRy+m#rE.Vj@Q隓oX$B\~ 6-R2kE.5WIEke>QIw'xbEstKcZ8pww4}#SVM'm݌brQҭ]\41R>`N3W>H(xw/~ߨG0-LUIz]׀5^[۸(n߳K{;v^(QRY:NEoCNY/<%GulpFG׹#(*)@4SKA-SEITt*t?B??zO#Cǫ9onn&r$F'NVzwo> 4ˇi-g"W6bG*=2TsW&F,meް;F iXT;nz SVP m$=Ze/c'R[e&[n}I#rH#2yJq# jDj_nQ 3*}>\1D XYtr~Y QYHTƑTU3=OFuܙm%#?_>Njpx0\{ַJrHq\H. dlx=Ƴn`w} F?2^ک-Q!Rf|y8<O}J Jc YL[mT 84u!,7@zoLI+== .:vpg\"\$2hJ==jO" |R8% =AkaBڵEjyW0Psnf5d:Z8OW7wYZR$- R,$r¸͍[?aZK/mf- Qcq _dQd z1IJt ;9yS;F׸b6~ԈOt ffU}x'qWq$`ѹV<[$i :c!xo0@sA-H줊oA;F푨{ԓ^jhm$y;dr[:siVWn˟o`d(2rrPܥosv&~Q t{9Iu$Ƞ'uc{ ;9k#C ;2R?0ku uSXRvfǡ[oTOI|3$~)E7`o'i!$!#d`x +71[hӤ+}:]YgkY;Ab_ "YgR⼃ú<MwCT5ki,oK_9%-n9!|khk9ӵmMaM"ݶD*K ( cúM>%ëjL:XKpH$VIi~*]:k뿈:tOHT60D?TerF<;y)+S!0ߊtXY֗&\J$Q\K ,+IZuF6oi+ g 8(sr{lW1Z#m5!=̆'7- M;1NKƤoSyzXz<|K\ycb%R73ڀnx[0x^[?xY5NOK J;CI<2T!# k˩:#OGo =W3,k'Mr^"|6‘/x=LJF|@tvP)s4?nh,P00\;/ZLlic,ƥ$g +PDlYuVm Ah$*`𸻶WU.SԶˌ|UXgc1 ?}e/<6ZEwuGJ\#\p`u޴:ׇ755ԭKKMLH-"K09 ``6 lо*y໥l4di$-]Vec>A@:Exu&`~-֡ϰGp>5|3( bA;@/,WE]Qim6M$*yh鏗!/-{n@G~ķi0B-.Ӣդ0;!0AIW`/ٛmtkcP"i㸐 0 FH i/#"kGmBH"-,eF)Jpb'JMfXƗĚ ƕl^bc]F9DFU0 3 +5mmAc$6ixd\]8iX+}( A+ɾ!{㙮ƺo1|.1: &Sz T 4(A >c[i:.5s9ݰ)bgרv#Lq")]"Ǟp !ItSGIңg{^YH#E?!xX%pxW3ߌZ>|l%<ѳ>:zpqx~>Qs<񇉼)i?WCk!.uEvnF2Tvh9fi֚MHo2F$3|9Hm6o/4[)լ^7yJ#r(?-/>Ѭ]WSIìB`>iB .-I}&]+u>0d|AoҷPmhݞ/ˢxC:u-H>cM{_f +uu!XϿ WϿ4<+*kdFYr}k؋Ǒm4AyfCrbܽq=S5yV+(tP>?xY!qM:)u(y(>Y>M7JM_8bg9p=ƩhϗmC0;E*v7Y%|eAɯIhĥ=5*"?[՗Mu1mfH.,: _9gش:tx׼_q3P"Yh;d/X(VO$ZP-DJ4-$*gޫ\Ku"X,8̧ˁһ- |1.ՊGipiko2jHA^V7{lX(Ċ iČO^Ny]9. |2j0N]>:ln=zzZRxWC| QTs֬n"*qQSs[gk7'}[FRկg6B;}9HB .]|8ir[}RWx8sXqGBsZ޲XoНk>X=N^h [\OrGLEO&:pj_ڷ\v;C#.QR9kY7c-NMR-ԥOyc4YuQ'ig21+;H6c\Ceo8Ʒqor$6OƼvϽviV4Miv2~ZjiV8*2灞չ难p`dO| ?/ӧTolC9(=I94ggwI6)s“ܤB%mۣkm0fxZ@-0d#ڵ|5ujiCAsRGAE}B2uKn?)f,+$ pΨ µ*>kUYxF71PpH\S0ֽ8rF@+yOo}6˓Ws$3{v;W9o'ց)M cjO#CǭB??z8Jo67rcSr\MԎFd\؏ȮD{krs"I ׁCQad:}<;Gd}j(獿}HG/ڮI#KÖl^}Ƶ&Bd-N@'8@P18օ]>8GJ\GM7'Ь3jY%*ZuS.f7CKswN3V--)Qy1_?l|cImcPQUh@_1$z6p]0y-r$6gr)hp;OunzaI 8$tXNy~h.-MR<ۇ dm=s!Ɓ3w˜yޟRàYXHϩ&ݜX6s鐋'i;a\ 㧿j{Ys vpmR:8 5R7j0ƞ`Kb64/1x j63W{+i5si 0Xz^r{ v9eS{g K`|ș`U< E84{O =mBm;3Ѳ:hOO>Ս$K0lܑ'q5Vi/"=_"009NzgI_7EsơNIh6fu>$&6n]&TgW,W~2Ojĺ^uMI Qmx۱ յ RS}=}+kxSphZKo,JŤĶLm]zg+[S*+wh%w $c`A]YH(! 8:noYkm% {/[xF⭢.qݣf0{O A[u=Gn*&dFsU(9*xR\'QA~$lad{"KblKm3'./3ef#**ǖUT?,ԷOH\{W^4]Gs4,TFqӚ\ܭS',4[)5-g I  of᷆4Fm|o^)*<'8ݬB ,|dx_γ8e%|2.:rG&􏋺M')xZ^Le .lm#5w+G~Q[/NRm#T+p#۴qm mĀWNfnju)Էxvn]EV.Domr;bM\[ WRiICkXqgڼiu-> lܪ[ 3\8IsG{~ifV:Sxfj9d^2I>}w7̷(9nO6ۈ@p!f':e6 M]0Jn1zEj+ ~a\۽v*J&<7>xjVWRS.[^$VB"edٷfW//5G;wkeɹX ];ɹ#¼Oo՞LiFs_ҽY4Ut wa*Pnǝ}Zҵ{|M4&;R%C)_,|S. >]E+,'"k,r$3][t[cl ;cC{8&E/f/&63y6{+0ylP4}fCѢvmPOިuz"Z,uRXiqmK[L,:0qI] ii'2°K\us]oACč,Lgj37'v X|7/ϋ 2\[oⷻ,# ~{2~l^閗,|jۣ>ȸR:3 e4̜^4RO[si>Eu fP.]A+#˓~kO׏b!V?1sI=:p=Q|%Cgt+d&vO2K`da'x{Ko_bַͬh!F:qu#澦uf-oǚЇt}(x9 [)#}'M߶G#$ھp)5ҳcԎ&Yx 3ꚮ=֓kvwZ(/'񽦧gqsy\[2I"y(lmC/[tݞwK08yOaYUnegAf/xR 5i N?jVLyLTg'uaŽծ]7Oww z~eps|fCD R9(/$z{q]WJI\0x+F2v8oګׇ iƩݔ$]_nD1x̴xv##?AX!Ү|+NVY|Uv~Oyfu1tj5(1 i>$F+m:5%dja ,_+Gͧj'pA ,y|=GUV)p\U[]&PbF_G&$|W$ΦMҭ#[ְ$7~;A70IhYJaaw v0RN>G$"u8ȫ<])3m9njZ{qlhv.6V9vԓXԶ+H˒ Ʊ{ڬNH`o.+m%FAZ5ڌwNz^B=fgOvgj7-$ZBU62x⎟.ZJVp 6f;S!forJ}zVe@ݳ)sZbn 8OfջyS,O3I#C+K θ?QOoQQ]ǴC^3vD:yY-paiIeoapx9+1Z!i0P\,V4KXfRN;`Nԥ97 zdB4yd nZkZfQag|6DRmFe9b{}J>:k^/Ҭ_(+On/3V%k}kڹZcu{.}64^3O#dޤ9\x=@~k+0*&D6ڄ\L2v!5uq~xSE}'}u(4`Jk,&O*)|S'.pOˀE|.jVr2;Gc6Nqڿ?WK=yo~s&n[\3EhA:ub[9S4nJX!J--ϥ_lI.lM峜y|/0O/[9y:|-Y 唈b'ddݭ_/d^xwgk^kkpґCJ儀@^MǕF-yn -SڼUZ0Gm)8Mn}{xBc3z=叉^ִ޽ZL3F 㧵}I_ۿyxW5yr(6Hޓ4Ο~_4|SZ? 'yIUW3{+ON1J,vD$FI!c`=>\(XLxo|/k]!S"i-o15_GMg?Aܓoq(MC7Bo즸b[4kO3;{?i/1|nqJ}5%#¿тbiO"Jr4R(5#[C7W~p~zs^?gV+[*_n$W$^#?gRM_OG-+Wڜu_<7i:캒mW *bDnG`mxMkK9JH\Ñ YQȵC\v}W\v0Ż <3FBwE| #'B?>fJ7D ,̬ dnj5~co\??eaK [ϤuψQ7Ph&Mi`8XEPvFGN u[{;a{ˑ @ Ϡ+~B*?e,z_+Zӊ9M#ٿn D?.ۆq<3h#J G no _|"]yy)"e)xVx7>Z31BXHak[M}㯊#$}g?c_H9jom?I}]+_?Ndxzt$5kXy%';3Z-涵EPZ7%@3*W㱯76?gm֧:C[rHn >Mv 0k}i_/|r~iM,$p4k~Q(O灑[W|8ȯyӿm_4KEa'Ν::d(Gtq(ѿ!SuOoӾ|&|]۫=oMkj ?6%Uzץ}ៃ>͔^;KCPkym/y?rNG_.|4_AȴX=,F#g4[xG3q/́aӸTk!WyڟwawK5lJh{MMx݄[KtH?gw XQ>ng+to^OF/fOkgWw6y+K]ծ<™=Xs=)\薌4*'Vē#aU&MғJQ~‚S78k/PCB'a3g5^!C\ڏ?O| RR+>hxK{ۣdvbK?!\'o6'8~g^~x~.|#`-4t9hnRs7_f#+s_&㉚GM<|Q~t(+.E]kQR~¾ۛGb"'h]M̦'^ô@Ќ)#V84 o=| mMF)RKZ yo񌑑6[]<A85e-?dx#Z7xZׅﮮt=Es Z>^VRhkQ8| ?]e}AWFW5d}c]gyM;J[WGp2duֲ4F@Eƌ9߷Ƕku?FR/jڢ908|/2$L%pǧ|4|+_7͞ ߈??OCڔy@z L<SQ"fotoxx-18.01.1/images/montage.jpg0000644000175000017500000043273413222767271015300 0ustar micomicoJFIFHHExifMM*V^(if%HH0231+0100Fotoxx:trim_rotate| Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?CK>\޴K$3l6GnWKg`ڽM-xC?>/uj^}ަvGd|^mu[o5^z>EY {[Td|^mx?ڽWad{6GnQ {[UjŨUS}"m$ 5QK.Mڿ>/ujl:-z7/흟,:ŶؖW[_)sc%2@#КG-?4W7VDmo3Nu 09L4;o|IM-īYAPIr>CV)MLgKՔ8 ?:ZW:l:-#_ mƫ,59?Md!W>ϚVsӧk=4}7vZkj.c_\bZ-!w>bPJݬ=\l:-#_ mƫNJi<5-7\5=†Q 0nE>/'f5+GMnl*kqHĆx+Du?u[o5G6GnWzïj &0]YC+. 3X?jRcj {[U^dʷ?ڮ8dlK4 3P3^_xWq/i%G2F&X^O/|ory[[2iO Gb2ƟA]_D:ZHcmyN3A[|O,,omEq5 S $ѯqhz ^7OG*lJMB: #qU+ïj f{! Zۣ1'>Ԑ+nyONZZ\O!ª3I<;Lyii/L.cK@6F-",~Rݺ\.#|o{Ɖ\.q#sqY7f`jڟmHH׉¬qr_J2n{4l-F cWHq_ˢI$W ~"ھ6]}YT1K+C89yYh5㷰?ƾ-s i5DUA>Oӓ]Oÿyt)ʓ($.d&zqVZ]+/ak G Ht{cN~meFAb- ! %s|!PyU,sneS և|3?3jij^X¤QXɑ=7IYu)ib +~g 9__Gׁ\L|5SU6^$Bf0How4,{2{WtܺU SOSqOZT\ m#7a@rx_5[ h|Wm(JLV#cs~-}K?h `3f:ٔ]QFVG|gE(D%vnǨHZ|BF8s_5mQR`ݸ9qO==*I>3xxKL[큆 d K3]˜/<.6v̀3jx>,icӀ{kZ]ׂG5_~u=gx\T ӊ㿋:kh:{ŝCNx殮a:*O Gp~ "Os>n#]ͽ㜞+V4Ee@3ox}xΥFUF2q*ōÊh\qv㩬ifdgTd~xWĶ.f-y my:Ep5ij}EJ&AF #~#Z\X"V nn8ڼㆋ߈W#a):QR~yҿ~n| Ym?PQMMfv.f$PT%$rq°|E4)*2Fbʹ<;%F߫kSX%{msFO‘I 5iؔ̇ŒqZ_i |O8H渹FtDexMsiZI%Fe@VCn`_ 5m3ޯg6&POǚDqy?Z+9VxZ4^_+ x<^Z۶7Z͜m$$ &ؤ\Gǃ?]?շdVK[F Je+h4߁^;%G^"7)H#!NpD8~0B00H( gg?,<,mF>U p85JP橯SZ[# 3ׇuX^*1Q/ɽתq}+SXD:V~Jv(W+;2*4r$!I c+BsJtq6q_oֻ= > #on.PDWϰ*ʸ[_mg+\X@+0) R|q1}'||Q{8X1`&ay66[=x ~&Zմg\vym s 5Rµm3R~VݤkדfMYʠ >7-<{ǫY[ZɷDb9 #~ wF/Y|;g7 2]FX1ӌ ~=?ٯJ'...ΎivX븜>0{.zU-; u[/7VJ(rrw b9e5Fڹ:uWYӾ&22I@d/-|:msl"1!m@m#լBJcFR~UIhĻnhR߇l?}Rr޿&4l_8R[N:Wq6^\7-w Os;eG V-&LkxEzf%Զ76>calo$gǃE>6K *` dQЎq"N]3M9r{d00f1iv[hdK@b%тu8@'9ɳ|f7ֵ[ۭ qWNgO RiFؔZt_TGk $ ܩ5]iBv+uʧsj-a% vk.9}OM #|B<crjVqFebO'Ó_V-Z-gw!J!'%R-˟(?&1P[_ZkGֿBk?3?[CcK&EiK P2qx;2su-j+._Qù<}C/?𢡊7G;2st˿j>}_O ?asߵZ>}k/w'eqrx_Wn0ڏ\ѵ:uDn+}8mqc885;2stù<}C/?𢡊7M^.NYO|oM\!{Oל.+~pyoiSO[4ּGe2f -%XK[lgz;2stù<}C/?𢡊7I+Z?Kn'[ ]oHuNP4mIV2P01|O<3xWQ$Ռ.;|*Q=/;2stù<}C/?𢡊7E ͞/⥗r-ټ?jO! H.szO4EY%BPi|Թ3chڽ|9}\?ܞ>ß&w}GO\j^{{qpZ;ֺ&BC8 LjsxF<)Mâ72Dѳ=G cg5O ?M ׆< ~O\!kr  A@ld1=W=A :gVA׬JO^a;ɸ2Ag|9}\?ܞ>ß& [ލrNiƗG+5~\wہ!d  n=I N]?G]$wкӤcnK0>L1ܞ>ß|9}\,HW~+4^#׮5 {9ty,v|P 9cҲ~}k/w'eqrx_WnϖUՃz0 ]+Y' w'eqrx_Wns?xo Q)KXXzu|GXZM{sdcliތ8tܞ>ß|9}\,W"+ԂQ!cPb؊ ۱ ~Ht7  F} ?O6' U'JѝulM.>3țƞ-Ώl- ,@rppsϠWr]~V,;6ғ[Oש߱>s犵 gB-.!a6DVLǃ+H7.-mslrƤceew>+>t((((7 7&gu_Y4?&Ꮓ-֚}7ZƧdڄHY6 $R<¾=CfI7lF<(2HHї#'enaq\wM&I.nft cd2m+n_|BO^ԯt $Kmn+)8=Ϩxxh5cA+-xg]IX3mp2 7<q[)][~gRS0H,#bC[ϟwd_5?cSs P^: 6e%YaEHcܮ{Z0G<֭;MpWxٜL*>ei#?0+eDb\Ap:q0Ѣ$GϋֿF` '_ӇO#^]fg_->ۻvSey8dN]&Vdq@H$_ ~"ѿn/݆qF# "CyďjQZrDt>j&E (!!)Լ^>8Bߑ:nLL|͎Ltko hׁ[RudGT#twֽL4};T7-qjDІl=[p8ix__yM#U\>F:rp&ռ%j7# +#fskkE,<`Gҿ:mdv&zOߊ7wN +5چ>uoF%XRcajF9媱:lSEm?Q;*]KRcm }9)|BNf\iV|1Qg xVl5WSӣa1.X| s^~l1RZnQ>/OMB {TF Q@F6  1^nRk{ "iɲL.GSZxͶouei(bv_ŷS ]HCw*UXaѱO==YR= ǖv-> d7:2lr9RNW+S}u(P\VM({Tux*p7cu&OVWRS&^qoOCW& {/{؞jcdּC\kZ`6j݃2AdM|1Ps W~E߃;:7i5[䱁2 3+Ӫֺ __A2ijqraM$m< /6ڹs$vLn_3$1A^KJ\GBS^^*s=zR*w?Vdmٷ:{/o>Y%~t?yĻ\=J+H,zيGnp8\n?)7_N59 VJ0xګWX[=}8?k4oۣØ!Y8V_9EH6ܨ>Y}9n;__hggtfbI5䕒F*T(~U*/hOBz#ͷb~JXJ,ݽ?#ADk7G@S|Eҭu_˫ui  w1ܿ*Kk;4JkS˖ mb9^+ZNᱡCF K#<_%̖?{pdBu 22#&<;wxYK4mHAPA`p¾4)5yG~;Ӿҿ?hSO]8{b;ҝZ-mt#? ~_e͕%ϤiH03ՈN+V|s[ixcTؒ+xီ V`w;:kwCQnuGcp (氡 %MIֽ=?k{S>7xhi^A{tI]Fis׌w?P i~&Ú'54t>J`0merHLwixX&hoK!*~)'nNk)b5 sk` e\Ȅk'80,M8k;~ҕ=yfNU_W'vWTT;}7QJфzŢevl噃+dpk׌q&4vVy%Y:'vz?#i-?.u]6w$e$gsRIoOZ1.4=4;hfS=pX0Rv'xʧa`kk[JΑT dw5ibKkBKӄZTJ#qR$g8=3_O(ٗEI2|G!y d8˓2ӛP.DŽ~|eWJKm&&Gp|<:X)woy303[⽯ hǿsV^V=s82O-i+mET, sa]@Oo7>JHn/Oj[hTlgY"ᯉS><埒WG##mH 8~/xfns4(# \n@7Dͤ|@b:cھ8cp]*GG5%wKqYìŪmobi$hj6fֺ(܋+\xl|죖I@$9}CW."HBo-u=Fӎi2{WSTu;Y#3T HȠ7lcekJnCs\ܾ R(H:֍onfN+0hc7( @s_3o  [j~֔gb'B#k n_5U QrÍxB#kg,ёVb'~kC?\x.WĐ_ƶmu)-hN-ԁv̠4/$ֵAiׯۍVҦXlcmLeG2G ]/1^͞ ??mquyjZ5oXI[H,ǴoA,Yߴ~smi>_NMV4{xYvwE3,!/9w'KFO@G?6:~|I|kZM坆y67SPX)Xw݆"?$>]ci:xNd0gΪASZi] s?mqty'~kCѾ9Mayj:}mtKcmcT8#NX;mJh8?|AcŬVKD"\c0H' k>I|!HyR&;(ՋV2B)\f:]Ϋ_ jM:"I2xk00+3"zU խ4}+JUK,%ug!pZPX j&= ~ҚW}Sd:sG}on } ȭÚfNhn׼5Oţ\kO[hc,qM$2$DcOEy]3ب?Νsf-9u(;{RӣʑDbu(RFdgwfxsZ).4Y_.@Lr+[,lk twTWvuuxoh$ZӬ.,til{4`gH9 Xkcab->8ڥh|H #FbwI| Ey\m<+I){ u`_#ĨOng--/[X&y }ݝ\#IF"YJ]+oh+6/-ńr"F DB"͂F9 ~wzOmcԴk˨M17A% vH~`{5h4?xF[+MFVb 6Wd'a;*HSF}sRՏu 3KӬ%7B5aGC"vEuk]yuMhjQ6eo4Է;\oPlJ#k+qD D;=+m8VoޭK=4A47`nH "yPpLƳg5ޗ_jv2hb0\R3) qU#9o\*5MWڸӴGpđ`i.ىrwTi5$8;MgL?dY$MVvmr bC62Wk母*м9$Ι:Y;Iƽ?oK &~ 7pً͖ b8|5/X,_|';k6MZO*)ZIXJTIxkHlN[aԨ??ʱ~|RhYQ>k]Oz=o +F^e]aӭ5?V.hȦAnnn"vH| wv\,nf2ܴrmYrp~e3#m6w ?g-ԭ`sշ۝']VTah8:8BJL1%c!$09}s+>%=hm,ąfaH]<^4#(ߡWC%{= %%ĿFr+K#nf>t=IʿGj_x3ྵi)Ŵ̑Ð=UK06G_ANP' 5[&DR'u,vߡ+W~&4hu=K2(pN9Eϡ7d1_*$9Rx5hW> UK{2Մ+t]0@i8=Ŗ%XZ_%Ah\UPe8F)'gPpS>;OZ̖W4ws.Ic 5mb=N("F2cdKᶾI\ҼKn&o%]@@l29+5x7K} p/B>狡+$:2.!O=@kL`Si;@$JtGSUuoC#~$.`XpcrK=ZϊZHgZN.sǧTnk]%fDiO^YׯdY.l%-!{E@.Էڈ4UpN`w=o}-Kp;}5R=-.@3d< t}7;|\7LJ(/o'GdcwW$v@S*z{I~$zIm-Jہ\q^ 5ǜgʴS$D 󞟍}AKytit*^_*:`+d1 ek xi.zQnk~5koR]k(|2p~٪_=]pŮliٞKk`1U(G0<2:%j2QFpcQAiLJ| *k=q5iڦs4Vڥ9DTE8@%[ |7 .+uJ;1q45KNo"EχuM6m:.R Ag02Hk =axk 00coverkxf%x\quuY WW TR^҂( tώ+xñƲ[_[kq@+9q\>5~cGV!" +Mi~+wb&%՞F]zm.ǠgKk^Ԧkg&x #@pc'v|{^7RյjQr=[Լa+B}֟*:ǔ @Cg9v̿=d>X#ۖ`Im@5;hgNe7i'_''ZѼ+_xk[fOfFYAs*1WRI8Wۢ俯-Og/L<4W!Obl#!dlϯA#ZhoM cv@+?ex66hi^Lܲ3nbӾ8~%x/ On⾑m0IN$ukK*UZ$n5"֨Ĩl'q>i~ժ;Sb8\]%B]K>-5%`'ʁP8|'9⯆z-KU--h>]ܒ91$p5 54 dՖp ߎH'3|.# Njpq zmctR.s>I{i{w,m"G?!;[d8 n ȯ/'$7콣$шZۘU)!U8}#EM6Gӵqw+Ey7iBrC$~·ZK[?lX0!Ps]\gG*2OmG_[ Ө$|k;t!mZ,+k-CQ[H/M/t-(n!ʣ1w$ gum$0im0L:zGxN`FDmH@;Tך,**[E=SqG?MC/@OuU6啙H,z#YǬCڷ#ƺŽ6Y[G+ t ^cjIKYܝ5uфQF4=i-VܥLNךPtcIҖ}oOA pܖO-?6YU'=p#{xXkx_Ϊ޹%/&/u&8RsjՎWzVl)]&diXjI^zd''_uM.7%< ?!V\{oKfe#'G O"ğ\6uƂXoӮ" hQ8ʐdʵ$k? '^;o2ܴnm٤8Ul-j?ownZ7wQzHs#0 gtnA,A@o-G(Nµ!gn5-u]P]_Zyf#2񺍮O> s?藗:TVq>A HF29 oϥ}Ej?45Ih`x/$b;ԥ2,)yYy`OZᗅ'۝.RT2WJ!'iY^6SHavPx')¯Gm~8񍟊.4 ;zQw^d ep "N:`~ j|^5e _dmM1<M so1?by:wʚk15tg Htj#ڕ|]urCu? /5?fxFuԵ/\64[4rDΖ"Fa$X`?CKSmߑ]ooOGO?7dڷa.Avi䠐$!>n[P;o iѦٙ.+(DJx%תݶq!)].g1Xk>R]v{-_ɧSZj {ᘮh~^ fZ٭f3.a0[e9 FO䳩ڧo_Ώ1?goa݆cegJt{Β"@[*gj5<5s,ziLJMxxqU*S +ty, 1CczDב0jcٿqTU9?L?,nC-ܠ?o k-'>М:PȲG5~y òF]^K)* e=@W`_q}v8F X kKgb5d2ҺC[M5 _,>\x,?g'+sfd8*_*voB/^y~ؾkzoZ {m%$d9#U⬷YPoArwe̥OgAW~9hvAcZEgn%r#.<2OZYGGX`\oʿB ]> ZuVVbaxfof5~ BV?xTj:qndڪ€e<~,Ua98ƿ' ?4K/F-康&Bb8ݏ¹[~+xKT5-*6{FǤ m&F+s.&sϥUo'rODº)'6Rɖic~g^x؏ E?.do ECГI9M-TCf}oAx%_?ƅn*H:0`~'_ξO׿&¶.C"fx ; !{uOEH۶a |CNnM+t?B>ȟ U'Y{ӚUvV;^z}MiZz'}~{>L0[pL"D죞9 ~|@6j5iZh_b*XrNx÷B?o/~$Bc]Kqo2_2HFBUsA5GZ[E=J?,Lvmim:3L͟߆->$|]ԭ }.O/sxDT3e`gk =''y65K8wJ1}k'Dֳj8a7`~<׹Jȣk_->"||[#N}>Z7ȭROy-ߞ9FڬW аiRTi/~fXNֵ /Y.ܱ8]{7X\ ,-k$g Ԣ[>7xoi$ʖgs7> Y\-mRȓ\-֖~86jqCٶ&L p\Tp8m;1t&kFqGG FSE7[ ]E2O%ԁB ªl/Ssk~ O ? xip" A8gg5{M=j^u/yn|g -|22z_Qr 'cݣkϑс?ƶl`:tDu#-8WFw@3-jUGpw4{]c}OJV?F_?U, xb&ҌAv3 !`¶š`<'$=O5ѧU7F7bR4,jF27 {&u|ZԴ{;-E;,,O;NO3\ ĚGRXԓ\ߥ{wߍAvvwykh;Hys)N֣\螥63j_YN%I{o7W`0Iʫ|]~m:>g݀ pH# #ھ!>-U;|uwyq)U R*sQ^EH<=@0+(`-H ˶XGzt_|[n֮ዼc|?/]"[xRYa>=~e*ㅍ7|3 "#89&[ش-25%c^2}!X/+'G=L'4V!ׂ~֯ P_xJ&/WQȨ@+#>^ᧄtLm#ixtQZi?6Pk>+K]ǎ5Fb>Mt?KfKyٚ_~FnI}.O;[Z4 ٽV!HP* }~S^~F|jJo/ }x]_zƽy?yrT6`{IƦTUٵ*쑧Je6^!ƧgmKvk#qn$zW1_ZRT͝v\p}>>Olm9$d=ɧ_|m1u3xzti-Na+u;{ R$/,mxJg;|:f],6H(CzϿlQJ폚6Q/qj5V2g'-^;ODATY=}8w 9|I5<{q[j:v5Q=ܼ@t'8<j?=*67Zmi\g'鑞k ui4JO\?Oρd2.?O?>OG/n<s%]\<6+y;A-5OۻiTZ_X^}`G*̀h&7 qntSMWGh K }Vn#Edz8׾¿k|?x-qlhUNC*R:{rr$pZ|p>7𭗃|MRcdHgU$q$pZ^6Sĭe/x>F4MOF˝FIRT:)n5+Y%0z+[qGY3iw$ YMQna8GuV{_#ZA^x#KM7[ѻpQ-̻)#g/L|]B{{,Id1 ^_Co?py}_6R\m+(閚E֎ZRִXlfkV!P*K :b#\mK p-۹'n~$`VVojmٽ\ɜr)G]mmJhzkV7/ja{#QE$[y2rr2 c ]M gi0rX@9QxYu_ n׼kk꺤>􋴊Y6;"d>qg<>"miww6{F餷 $dcZ#Lo/;3Fp ghko|8[gdNU|VZy=a:[J7ySY@dB"T;d_Jd.vm7dk`['&hm>ux1N3zL,֑:5 6_tZ|Kyn>ً;[P#[TE@~ЪTVڝRKqs(8g4$䜦ݖV۸r9;{ e76z&kc#h3 {Wxv-_SP][;x[P4&$9vLj+`ncmqWkp\YK7C}FkŮ憐P7F=uD%h}Llcצ2w;Fp~5EZ7:qW?_ )_`vy4k' 3fT] K謤q1&,zWןfş-~LPTXB!5h? tM*NӴ[xɍ- ǹ2IcOjiP-q/,~(h>֠m+MVVDieGnH,|jͻzއ=O׾ldDO弑2H-GErqvVwќ+~_|-ַ&erNǷoz~Y⯉j@[yP FvӶNoK;xsPtM~R*~`Î}tz j? hxJӓ\Rh˾+˓;s_V=RMϜ./Rr,u]fB}ցr0sAiUOmH Xx!/ֺNX&R ;03K_;jVυWB݉m?oxb=kXHZ.Up@=Ec)?W#v_x~' 6cODA-AׄG`2mugB@YW9x b)P$4*/ V+xaZM$&sYojVhMVDɆ _L!(0Q sUCEX;Hn 8oj(uFo+wZy: ?ξJӢ)u;ZO~'xu C`sWm?f>/^--Y_ xSp"/ ?.N{?f/mٷXҠYZ ț(0K1Gj^qM)@tB*<zR4or i2ڋx"< '^t.y4xTCRIM4ܖkw!JWAO4KO[ 'Nivfi9$<{ F~ݥəDm3h@$q8xտu;#M3̓0qp#:c9%U^*Ty vR$ַ'Чi1>pEơGq̍{5^5C%Wr$^g25OSDK_!#|qs;c7>X?=,S] \5gݴmqU>Vva騻TcJ$y'$zZ,y ;`pAo/׎=nNNiH>SW>~)k֚NxRrdfm%tE9GDqԲ:|Xޙ}:SHV\ռuRxm|B_N~2Tgt ZBH"h'Y"P8<Ilo5=]RǨ^DZ"_fp@9'|70GKVMx!}&@n,O.kUV b@.:9fu'El,mh'o;ΕR=H,WM|s FT/}gοo/CM7>ibh*Hb.@8"Ttɪh4;YzEYXP=8Ŀ <5yplhyfT>rCvݪ[$c9ȯ+Ѿ>o(1ZE;$~6#T*W9{p4ӝ-wg<7}=HTVk_:͸$$d{_Oo1y˳'iGW{i_ğYUO9ieV+}.=MY-ű1>Xpw lxJnkh ^Dүލ>ou<[ɮΞʌ.7z j+"/7ikzt 6wHU&qUf3'5&}\ukBيOJ0)%O֐tW/|=4lK-;" !FN1#:~OLק_}3ڭk:\#xJ)iCcW4=uӎֱwsqy$KpFN22+R[c-j"d ][-PH=7 ڬ wz&%Z}qv#?Sn"*~5s go&p7)#>եON{xQ- JY$qS*#?;vv'xVĚn26[Z[J!UXhPclbL`%vnl|R׭_߈|7j6/:֧(:F1,IW}/ȪxWƓd,Jo!;A;\֬4X<_?GI|VzncOq_6Ur:FyucNJ|b; QIVHp|xKkׯyKnm{<9Fɦ{?M /%t1^$,Sx T@t%W,P=b[ Oo3LJ_U ħF>FY'^uWzZq03X+fFE^MdgKZ/ѿ F?;Q|^mVփ^>*4K)!@;*(*2OV u}#Io =dz ^Ź(EXӵ 0Ɏ*]RzOzn4mf M-?, ġ,Jr7\n1_C%hG) +97:j)q5Eq"JsNPNdW[ 5'R*qΜrJԦeth^X%^:P057WxM̬ Lr ,7w뜀yg|sIwe[\]JfsmyX-3:N+Ķ"J~"F eKz~̍6?|]]KEʌ-2GpvwT0H99k7Ѯ!o2mhȌ(ijc*޵_cV{]f9;MIRU`HfG׍)-4l`dğ?͓jVݴcG\xb_jM:1ZAq hsķנ'6 wrk;k%KiR<}!+1!Tˀ-F%x.lm{(|m'E@r~Vqbݚ.gch~,d`"~\gluVޠ@:W~JX-is$-l$6 v'#Z_(&/ۧ΢S ;z5 ]=:_xN+o8]d\vf=I5K+[Fź?.@ȯ+ V\ͪ@n5f@`5g W":>位.&HLxb3+<^&Xx S&v$ hNE7un`Wr@#gMwi66%;{]]ヌwe~ [J}KͅcUub5mkNDݲa!J䑅p}=9~!𗀯@veXVT3K`7ߡ|c=:+59d-\TDrA85Qtҫ$[^ޛ4쟗}ߙ\jSATDH޽}B xb#7?W6K\ؑ22,)ʂY1 <פxcTS̒r d p8T8ޓaΚOկko>8[ #% A_R/2YEH\g>O^Og"CF\ "+ǹJGZR2FC`1q~mc=\Oj$TI],A M{OzF. Lj ߺXy81qOO ^ Ji㸬\vkiCeuf6g K27-&5$瞚[ :gG"ad`|xT>xtgO}2-F;{U[es%g*,r@%x&x_QX幹Annm gӎ=wOk:m>e%ZΞQf9%hR ++ aJt_]i)b5>Gq5]?B&+h܄3M,SKqrF~sZ>(foH,7mEt-cyMw*`5ǒ9LiO'u'Նq ~`n#fWEfЕ,3t>xfN jXX;dcUnXg:URX+n~'MjXN40W=KڇeuyX$Q(嘓ПAWxK⿂bj 5<sCW|D|5?2)n|b\eHݞ~ ZsH2qgz9~< ZyW}Ӡ-m:֔n!F2w=_t$M[{u<$1<u o+0:-܋wq" 䪄78:|<9lEYG?¾k36NX\pHTPpvvx`iRҴ[列Vwgw |^eK (XnN~Y/%Op`Ai* JI!_>3jl 2[yLWTG!'GU I'/PY-dTڧӧoFVGLGQ|B5-羸D![UI]ITdU\W4Q}/&$>M9?/խf2,]])2!Y pTn Wuhfj?S374}/Z08`{,y]u48⟍5?FOh^&k{ntYmKѕ#x~%_IaiqMa0W77Z~ӵt/ReaR>XU?/A]7>giOPc#WqhEm4mAFȪԂ)s_QQJe[5;֋?ơqy Ɣm\CʅEx }GJ,9S r(?J5OVGo9e3Alx4_Ubk'-w<.={VzO.]v D&d+; qF(8hWqOMȔqOMȕ6ַL,zx%x%M( 'VdJkkF\b I?tuj@|>~íZGp?]Lv2d<qXi>׵ o IqCMIq šZۋxe#HI'gu G*#­VOx·IoD-k9?tL{L`Qg|ѓ$Oֵ(5ΙA_I=޺Fw-P^Z^"\HcGn#`W"23TF~Ӿ0񦕩[CPi$|K5 >1w$V  ]xi͢=Οhe+p#y2 ڽ>-<(-<*l*ߗmO߳O뿅_;}i0A:;)&ܲdpvGq^^{ Nj[?y7"Q Nj[?y7"V45-a|o3wW+Ghi)d:HyB:r:/ҍwfk)ԉm\+qHδ?)KRHԈ5 t7`?2u-P{ ]#kے׊_‹ t]HX]pny,c8#{KcXύ_ycz9hcOQ8#iΙ}(,HO~|%gWl w筅_QCIHƀ* 6aӳF?1?iXC|Kamյaw5qOBψ~i{mRɌ3 *`\ҾO_=aakuߤȷL~>l$+d/O-v{[[ed P1ZP<۲>b4$,Ӆ`7(瞀Λop˶C$mhHd\O$Uo-ؓUu=F_i|SYt7FTYx0 `a?.Rk_񭴃O5,ͤ[Ɂ VFEyհU`Cg\Ib$ T+bHLHRҀ7 x z;9cxͱx8 ]bw`ׯ|C^IC<򭭺$Q@Gӹ5Go_PcC;r"`4n;Wf䡻ӻjԭS_c&h캝$o1X>5]"'{^k,pjF9X;,wt8)AK\_/)vm +nhMFᯅ<=sY60yΌFr WE,5kmj<o4uޙw6&vK'5W{|M1#;IOluCGBRIichx4ӗ/⷏?jxGޯck ][A3|w=zx< è~vGU A>|IǕ~_gvÝv5v. 2~g#SQV* ~Lg<ة][i5>5P^NX ܧjB,A FWx=bI/|_K~#*ّg0W9>:*p+V߿_xhZ(XjkYY?doS^!%FT$Bf?_ kO h:LuC}Y'{IJ1ϑOʹ`GwVRQ 'רc&}k_1K#^/-U86xD n;Vݛ{D i?xK?c&T{{(v=>w,[EA+In϶2!@3-a|}.Ӿ.W͘#66$ ߾Ny=_TMa{wKxB0 X\¿xC¾,ZޤbI $${S|z|XTrI?i.ei*7JUAcm|7qz.g &mH[Ȥ21u~>;ǂt!p(# .wLIY>(uǍfw"'J" 7}Fn0< !XPbMGVS3d׵5Eh҆5Gb &3+ x%J$mݍM~-w[Z@;{gE,|_?<'|7> б`) %q~>~> ס\0`kp$~k[VWvV!gT4r) @ "JyUp̽(I*pG^?#?]?"?ֲ?#?W(??ES̏zCّ[P??ES̏zCّ[P??ES̏zCّ[P??\Sk<1} RGJ.Z$>fG=n!Ə?ψ_ ~"4><99`M.;AB1uW42?s4fG=n!Ƣҿm|#JuƐfG=n!Ə?_W5B:͆p|K%%cd(޹#L O?J|Z\)D&˭v:ֶm!1]^IW()fG=n!Ə?1x_SмC MI4ˤT>WE:?L #?Gdj/+?4['i/dkg05QY|.ŏC:;lʺKY?yk)H9V*=zcW2]Z;G4쨢 ( ( ( (9n-^XA$lA1^ yu{Q_8Zߴ?GT hy!Npzr>Z9}_xrIK̏}N3~FS;sX|9]!c渟x|-ho,1NAI<}Ӟk<A¾WP[;Ehi(BJSƉqm3ĭU{$JY*DŽq9[lFƚz]$oO,?lݭӿ 'z7[Uc,2n'FNj$%[\|u6#DX$&ȍRB 0+ߴDz17VnHeQ(=yՄϝzN)I.*oɨ?W=> *?j?R3k|_I~C-/g,yxTKKNHaf-ªA!:1TtM4"KN*?}rn/5NIŽs޿*V}2و=Mp%((ޞ]aCRo}:n}]j 唾D8~^iW>Qϧ$M #&nGW|;Z>+km$[h$q_~նJp+>іv>?f>CZ&ٛm®N|QÿX}NJ'{y0|@=f -:)pE؜wt"X:Ɠ5{zg2%ُF8QCnYA`JS{ҾNCn g3X.9Lq'ҾQN>×kŠY}Udr '=k]A$'=-oUH2>s_SMӆ"F9?kSMoGqeݴ\6"oBscFwYb+q^?{qD~{di_U1yUyUs~W, .kTO{Oy&Ok_b5kO` 18u5??dR q Mf :na־cn+iz]@+NkM6iwGQ[k&pF@k6^Jޞgh|~JZռ2[:v ' WkkUEoiҦ6[$0d.y9ν_]?v:ǖSޘW⿅%wq"0lc+^/A*I-Q'1ͬFKer P2JdLk 2[[$RVSAӋh#M$;m[܉P1c!5_}?7uC=aP/+ya> ѩ>Dow6x?fo?1_2HȥNOPk{ ;&mFCG|Jҵ۝j;/:eŲI33HL W[↛,WV@dQ^Az~u|K6X †XX5=G s^+x/GtTF`<O߂ih&[WfNs:~٢xu-ͻ*6: uEeHҟ\5 QmHc#:6.6m-^4*Ws oOVЕ|N)RסKZ_kGt; z֟b kO/lKEG:g ?-~_EBzӕ8_#S)[|j@̄*/j@U k<)Lӣ,[?oƺq;Fşk~0ѭ.^kDž'q֯kC__`WgtSJ)iNP;/O'GU|QH((((((3 )boL#iՇ: qzW׏~(}GBPnytfXv3Lrʰ8@Ŀ CUW]V-bj>=eZAim-hm'8 @c?]B~3<+ V|}w,|1=E˗yTE0}i'/j~*ԟ:Lg$+ˢۍ8|*[=߅kx$ lRAN~j __ l5;J[v9@C>=ɨl|mMk-OWm#n-eV94IٯR}t6ezzM~)k7z-u5?e4&ܒ(!IW6/ .aa,7M$R)u,pָ ;G4mJ:tFX[Zj(8s$;^^K?ėw )ź}}N ۈ峚IaE.rG lt*6U|>Ky|C ? m< ,^K'qBb5Iy%+5y|Yyjv=,D}6VMq.70rk/ď=;H4ho /! iJ}spbHv77zώ>_Z^Z PO5K<U')~W_e\ZS7Q]3]%|euOu&͋a#`*WUJ\٫ 0i *쨢((((n=~ÿ4'H#[Y 1p *ONTIQaX_-8*nG6kL6l0ݘgJbAbbq'J)=kFVs|'iNx%dug=2?v ղEI˸ʤcMݓ\j\"QFAkg&QIadE*ik^>>X>]r~nT<=A [<ݷ=O#hҵ_h6.YkQ;G%񞭮][ZZl8yyA}pqQCk-j ga@$XrOӭ{0L!U:=[ZjZZݠF=~q]䰐*yZ0v|mc{nm-w{Ic 䍕>oÞ5Ld-yKֿ4u2-+Ε8mZC6'oq|5i\tϷדTS䏋?T._NM)(sO|DkimZ[ 9|+ŴW9NԺXq;{W84}rI..fIT=GȚJ 1vw| iBP-2 {Vw6HQ.en$ږP/x+[/-_—5 `V 0>e<$s?Za  \%VB#6VČrzjgtϜ$'4Y|Pn鴩3C4'Teg#ԃ/]WKj1m ʂ/-mT? l?=N \YݛX+5f,h:1%|2:v_vWa[X rͷxDq;y' Mc' [߳k0:"|3[j#גݣa%rKIT9+@<Ɔ&bg{cS1ngڄQ0DRji4An%1ҹ dDkT㺱_{/ZP|,o,4vVҞMaEHǁ'ھKxFطW$52rx~m&'66 [GK$i!}j:zl&ukUԖkS"*3*Ga}iy9LGW%y]\A wmkh8ϗNֽl."4pޔl|Vo4v譣FfQ$DXܟgӏWj?IGl~#3e N9=Y7~ ޗ^%^b\uWg;_j2\BѬue%WIsYEtu??QOt=*d- [y#?k?eߊ_/^_n!j4Zlu{u6yԗ,[?B,|ws~heƿfpf Z|Moa~us6뇃xVa>NA4t/oķvj}Gs2Րp}OZ]gW|Capj*&9,A;zgl jMI8(bsl2XtXoc9XK9U$ǰy|dYC=\9  ޮNP( 9vHϭz4unaġFzt*NNmz3T- VtW{P\KH'$@9<ץ76ZZnoHgB 8-A8#=?uzΓ+1ɩҬ=ci kK=)MxÛVQ_QgVS,MxϷW.OjڬY2#BVV1'551>ʻ{iM E)u|+kso˷q5񝅏ӵi#UX`gB[a сi階ot.U9R}:w<3(By e{Á?*2#VN8{![̄GgNQ#Wb˥y'$ԭ~Ӿ_i\]gqO~5|㏁Ii^چګ˔I@?}IS̿y8rB `}sW.oLܓxҩ7w֮Q߬Z(#w~_j?WSEn?G#w~_jhr}n?]MX[X}7w֣Xu4Q`9oFc7w֮,-߬ZFcEQ߬Z(#w~_j?WSEn?G#w~_jhr}n?]MX[X}7w֣Xu4Q`9oFckLѮC܎oE(`QEQEQEQE|(P r͕ÌHn#i|<¼VYB'\N&5((>?VaYc i5q/ fbXee#+DPbL}p a̻[ ˆiʢmv?0tȣL\D_9#qWGZGorPά$088kDQPa_ʽcZ{v7%ʲ+ֵO|EZA,wP:9H߇q_WPʓC6m"K]LJA F0:z Pt}=xՌc#sZm^+8խʻCDҍ6 -L6zk"!ntv,`9ܡ+3vڏGun Í `3>Pk ۋ&PTu.Kثz+ :x+k#:kmrEcb]h;S:OOwҺ/f&upef #Uq_=ZVMN÷/q8tv愦;˻ʆp[q":tXԲj0ǗNȋ`*RpF՝י[Q|iZKRmُvŤ;˱wκ//[-{.u5fH9"J!p^Z4^"ҴKMw7Eċ-Ժ5EaMI בE [olES2$4eDJ-KcO MтdPPݳ?hk'nO²Ik!t`>yf9 <?gde*xpq͆6<s5LR:u>TᇠeyI|oyY&kd4^2GbH_Ix/$zu% q״n|kQIT7fF(~Dy3fӡV CWKHF2 *v ֠P'Đ SRsq:AI6I23_LCxU}iY}"*d~zǀn#ִHL=8|N#GIۦp8.iݞlxR^𽍼-2xCIAgmڡA9=z~;7/|(E Jn<_.x?tI:>Ai=$fm :?axAkxJ4i-|*F@dwwpiYF~g-JHլs2l͸$OyV-{o`r;=r$ 3!BA+\qZ/beOP0K{G"凫w5TH_<]ťϢvUh^Wr50[]'\-$95?aGYҤq +Hɭmk9 D[BݏJ5~ޱ>g 2C'l ?ggSu>Y0 qɟ-3I{pIm[: biF09QUZ5'A]R7R47R5i 7 U=sY֚lث8#{pO#Ğa^;Ek7 ߩ?C!R5|cgpB\)$9'UOj)n粎vd^qq@ +eX%TkY._ 3_'H~=x ?k[ ,V"gdz{ވ42yvC #1'@^IkI8 TYvC2Ԫj3gfԟM\Ѿ1?: yrH'$:\zV{[]^VhWIsȯP||;mQbNwƳ`XUCXf8FH%w{xs/|RzE Aeln1&Fk^b5hzLJ{=FwҬZHϱ5! iw>nFHB7s\y 0gv9^=ðww/%Q}D&1;^9嵋~mwOYmgAD20 }ŭ?z]KC}%c!:@erUVO3^~fM֗p&yB !w, b ⨴ _eoj֓48N85VkS_A,m KEh\*Jfr# xvwEYVX+`+r3@[ZYz~F֍ xOׅd׼+xPqZX}㰁Byʣ It5f 2_i6-|H#l f#iOVwzW> i<~6KB3IC(wI8Uk6?ޫoi;. gk3 ===Mxٵvf߮?z7 tuulFldz; ]?0aO~o쟧x⿂uu f亴kN>;xᅦxj]&TIf܁/.ley9O m7{\į>}j0KCH9Css+>}hQO#Q G'0QGր$?H9j>&>$h>-of?h,y!wِFnm%s#Q G'f^#<1xsORZ;|:n9P-o޹9jZou{Y֬4u9[]Q7w]R czQծ虳 JA#r>yo*V~#"oT uD KZ|]m&pVBl!oXëxaԊ`|ȮP1)8 D%>=bzNڥmj>#a9Y-ׄZF+7i4a1#;yg_SѠvy6S@{}ǦS9(Eͽ*r8ӂմ;xh_G|]r"8;UEXՍ7]R`jJ]j M#O`#TPpVQ0T9$geimŤQ|ZȡK+Rq^8"iKg~3,uSӷ2~ϯj~|5xK\%7t3J.+ f6>|?>=^_R>(`QEQEQEQEQEQEQEQE|u7UΨ+{-w#88'g+Ei"X񍆽`?vy7?޿S?Wv?l/,-uiV|o2tQԲO lY03҉sAܬjwGA'O*Oޤ//{?hq_ @ź>ѬI5`BN (ɯ~ R[c\Z74ApHr1Zfwpͧ $k#`Qe:BW{uUTSQ{5˱͊ҽWƾ~l|Oșwܨ+I?)Jp6j|Z~ym?5[c|WxUQFnu-`RۄFNZUHJ\xwsk(Yb/H "ʀ1}0g!=jVRQMb[jjtAmH/{.Co1=cyB^bHϕ\nOKS 'BO'* 4'YG|inHPcN"Xȿ&>_yhG},S:+^;MĹ,g kAhMm4qca ҬxƋKOΑ{n.6-ф#8^SGa?Uk4`|4c_]ϴ>OPm)|l m&XeVI4ٝԌ O:v[F0, 2rN:8#P EGLh,jjBq@|}9?dY";LΈ,l&$`Fc9?>LjAs‚=a>uXTyr`g=3OLjj(y[²<?~l7WxD][[i|Mŷqg~iO /폫~!ͤ=W?+TRt`2a3[QG QU{W6B)710srA涴 K+[ B,VK+\2y+~)3qn4$*OsY_%&mj?LK].*pFx,z+ĸ =>iskySZs=—"`$ں5߽s[Ys9eur:M-M[ .tҋɼEv*(p84빬|Jm.ķ7SyX ܀9Լue} Y_Ė֦12vH_/.!Ԭrn˕)0%S5ŕl!= 1FXק7⯉/öR7圴)# Ҽ?5[m=M.-"uѭt1Igu2yg&O ҬR[Y,mo{_#Iu>3(E4[B9zW/M>Vj A \tf3~&>#Ѯ WH`hbON1]|-)l.YhchtL~a8%l<~9|ouc]Uhcگx`R5l@"kM!-58ʫG8J1S3{t?6Hz&\ĭ9 9|F~+^?ZoN6^yi0׮R֯q"RI?³[cHRKYjcwLA+zIG<ѡ)c7Rdy΃~4 _sߍ.]"9t2m&6981O?߄ q / TTD#ܓV?hڏQ;KVͭ5&/7{NFU»Vֶ)cVڟٛMjMY~ʾ a6wΌ3Y&Ӂ%wceBcϵc_m%r ҄OLמGOV.xGņVw6w p[É ` ^_:{^-ѴK}$cz~U=H_$>VMZX.Sas"QWNJ@Mi.Y-&cǀô&"?\;\F77?IOI }_fô&"?G;Bo))#4YόkC)|)k:UYV3,c<HIOI7i4gξ }^XPY!Bo M;A𥾃qq*jOl#潏7?IOIt+);;]?6C vXkbQHm&a ` d1)h_\M=xo.߷g;^/\IOI7vw-oYn\n4촕E5hf9~jy9]xGx;TE΢n%n',dcw5_;Bo))#4ô&"?KK]?}ou6|ѯ|bNLJ/jkg,Hݪ'pd&q+|S6ƖVUO̍=AIOI7vw"2Y$f*TsIf⹷ *bO>5G;Bo))#4ô&"?E\=kJ(!^bp68B}VQ}A x,BtP?\Z'7?IOIufTdԢdtҷy%L9$O_@/Y)X!'z|ϭ}kЛJM 褧$׏0Zښwwtq&?0}V\lmu~cl~]AR7 xfKԛJk% 0͎1e^>T(`QEQEQEQEQEQEQEQE|{+v}#o6kW­b}=-u_]Km.Xad~Iu,fŝw Smo*f5i<,.o4=Fk˵9*T9PúT0x.FVRЯE.%taH`͟ᮿ+5 tyoeq4s3|O$jqs;Kk45F~11qWTpRr]m-:k{|TQQnWzg}%Ϋ!vM7PHc{b@X]7^ m&apVF)!f.T1 5ZhKs-ӕM!rKyXP"++%.mT'տ JF5ѭUa􌞅1wiz,"ō qw*[# _LxoVI^lʸ"D;$`g|~;-/,K}|{ZsԢMn|C:?hIdǨ6^T&"jO$5YZZۣc>\5hƷ.w+_ &*~> ySХ-ccG_eF>VVn)+ cZ9«\5jaڂ? >mм0l}[oMԢg\ 0<}GSzQ(uI>u:e#>o-}dC{+Ѯ;#>;:7!C$9֔fZ<6309Ahzgx&MJHA.9*'|A}h5魊A2q ;ҴcJúʱ`7JoN(φRiBt9'#'ӽfR!8F:M4kO}[Ե|ggr1}Cj&Y"Ok7uh+gО?½PUvC8j{Aq$f %fX] ?<\hI3D_8  7r Vx=냲[Ӄ.rl8n>:z;E$sXҀ4,(PNX}:Y"koj*/'ҩP(:t*)$g@Z}wqg2$8eoׂ hU"ﭤOx>Z@to'O?tFkxtp~ Uc]d~eA-4ύyt[3r!z9% }M;JN|=y"xEo>IhDF|=0xZze'}|-q=ӓ_ށwE&~^tD 1w >ʡh¬$ܩejuqsߎWF-+Mabhl'z> ΋\iŬQDn`Tw9a8;vw~Mq7c#ɸ@E@F=+xmc>5i`csךj {tXMs=50zuşoJ,aX7 ÔrIa/ux㶒)b\3nnEѿm|AՕ<28S$;5?t{7PduYtp@@$q{XBՓtQNEӋ7W]{ǂNn+ yDm럕 ZJ=5 אO/݀b"lSu6Sp"Kv #$N]~:Mukmk+]imd`~r2yS]ƚ[3W$I(8]l;G@OG@O_͗4o(~#G4o(~#LG>/Uk8,H<}5_2~.ALuMkS+J񥻾p,Ĝ@W 7_|q_[`/5hJKG1K{u)jƇs[uTq#qJ"_iUܺ-YeV5dKml6mg*NFI4EwџyK}\m"\>iI 'ɒ-t'.5[ ,R``Hۑywe|qKq_^8܂ EK_e\<* tr̺,Vkr"hXRz}fY^F qAjа_I1Jchm U{Xíʀ@F +~%I+o <39تҽLr+ _/5R[=nb_`*oarC ~ZÈ<96KӋnވK:t|smWSmĪKE|1`w~U_߳'Q}ٚÅd8Tɑq 0uS_?jw{{ĔhP<:u;k-IyqeoHZ :;ƾmi:eroIL/ ‰?'z_ ~&B=G]}G;wk4#[cUq<:|% Ě$vml۞7U?|_CzGq4F_u{) `T.V[k?xoU7|1GQh(K2 JAAvg.(`yUύn,o-mxK;%еԤisq,/& ɺ0I@w#⦏=rV_ԞQbCgu.:##cNf__M[e/qm3j7IypʡKcgMV}:O/;Www`-NљHVQT%pąokx* 6};=GG{?%ݕ%5a9Sӎzmcu{#Ú= vz%Ntn*@ޝ_5J_ǢO@"\Ҧ/b#M؂IZ&1ypHr4:m;k׼'ҒSݭI#1yK hd—dOwra^uou-NζB%٨9fu ;y"%e4+C nu(|Ji?7?t쿭}J;!\%]]iP[\CG#]qF[[ݭ΅} Bv+ ؃ W> |L|9>}RydC}HSyns_yۯxV7YZm/;LohJFrUŭQG~t_xbv/03os4.pŹ|og{oYh뚬h% %$1p#;׫SΫgf5ŬB9D.P qǡ2'j> $6qj&06nNvd ~Y[}--[7[K~3#jӾjs7@KtI񯂼kxv%y.{kKmeѣBLe]s|W:|3<72i7:6/GssWk.e/ՎZWWMgQKIoaZk&or#9]|Lj"m:gk 涭=NeKm`L|es2 A+]?־Ho4;S[g3<#<TvO%I6bíjI V7j5vd; 20YQ]K_z;}n' ,4B_.n!V"q*;O\V97^!MGhd7Zeլ7FKyeRuV 0ßѯ4vZ4#5%c؊?1_CǞ΢NKKnmtf];Y fv$tKgh&OS/A Ut[*Q@Q@Q@Q@Q@Q@Q@Q@Q@Rcᗙ#|]dn`FMs=,22:g5!>QmE>em,r!JeI_ۿψ~9|%t iꚅR9F\2 /I< }u4^XƖ6X'pqzÚB2W<~N|'t0V{JfSzPUKOCkfo#|^[\ԟ}->h˘X~#/u;yOv |+};2 >dkf`V N759Ӧ#wIƣd;?|F=6 R!;K杁M[8)5>}gEЯ'-n' +J0:zJgv3kБK,Gmg>5uoxz8ei[i" ,H'FDUmW]ZٟE: EF5#dmtk_SOC_so X~D"<aVojKs6lڋ̠H瓒;viOGⷍ|{S]: ;Uc=˵K\n+x;Kui<"l@ܱUΝzM>Vo~'͹h|-c%H&{YI cv`z_ c'h{D~sPWㆇ|^kX4@k׀.4v mmxevV7X2Fe$UBG5 6ܜeH[F| _<^MkcwWn#s H^G]ݰӾ0:мQo3nǣ)]+:Wci|yk]\\mαI`g&lw=eND2 \5:5x+lC{׾bk6a7~,G.e ˍ|ǒ:\4g d̸ܱ9Nlaۥ;d$if p5hڝug$HU,N63~~P~ Y_ďC R.F:pZ)]luWTleuD]7+`R>@~'hv&"E  r/$?\1xwm-m9(('6dN9ӥp?lt}Q b tH6alq_*%kxqqqmmu, |;p_xvl**+ힵ_C]iQ[Iky/pdHg 39 >> ֍g[["eX!G~b;drcߤ:~!e0ƊbqvҀ*O >2 Ozָ#8~ipC %I{~]dZN-ίmD>_ 6=<=mW7Pvg ؊1r`|8_J]Y> K{=C^i F28޵?`H R@WTV*sf}tx !cĖMU3*F:wy},rO벼g)vӌddq}=E\U\KSRQW>f~Z]’HVYeI$|۶x/隆4b]V@~dp}pwfC^ɯ|RzEn'I/?QqpѻH Jd|R?g+\ǙtE ,Lz>2zU iCZm,C>>!VBw<#W_|5FVo:1!ie~?o -mt*KWW:Ziox^٣yu y(q 897e[=BNѤ0%˝DZ(%UFz־8a} "Y#t0L#sDby9rC|n?}j4 -õK?.~k)VrWn]z=iF3c?ݴ&$ VO(3\{oſIK-svޱʞGGWd<5mz.,P6c%8ֿCbg:ui7o_?#ǩM)5 Rx?fo?1_2ȥI 0A56#yNsD𯉴 fmD,܅~|$i]zPߵցI-Y>wbrKLGMs?ď F]-)2:_N2 7)wI sj!(߿xTeiFӼSz4ZZķv"&&7l`ßV-KD՚}:qwumXcvi3gH FNFG#D,*$' ' ׅZQk:FC! q9^ld$۱츹+%sӊ4ĞK;+"V}3n{+~xč{$&K[fCʓJ2pJxѵkMZYL7ַCyJ{7yܦZiIZ]FZiY˴xHJrxχm狵hY-n9vtQq?+kO-M͎1ܤmsؑϸ>)xhjC:g8Ef§r{>eFr.O + \]xG?6VfM6C! §E+fILw3T;n;sxyTv=9ML|SMr}@A+O?5P)yڇ[A(U)K?Gϥ"K 'sAcW>ΧrKxD-r"᳐1yL o;PK?Ev>}CZ]|{ߊY/ߋpC| qiڇ[A(Vdannm6?})ߓ_aS(,YViլN,SDD#*GAWXbzHH\+ΚUecs}1W0>Lgo?ݮ 63F` Nl`}jW5hM ڊp<(ocW!?խ,n$D}+XI'n\giĺw5 |(%#ʗro=/F~(R;uz2pxXvKş兡==Z&)=Iҹ/ML+Čw1Y,e)Oz^j}3%=2>wmt1-;VKu'U!dg1b}y5f?+=:JR#gï}"Zi6˫BB@_tuJ[ Qv@a>pu̖ztыI _sG_;|+Ժ7}=yy]);Ubo}ƞ Ჹգzu̔,vEy$)i5O m17[l*m=y8=Ji㛻 _ >YD-*9$`ҽ F7PU8ω>3=ū9Jq$%s&UOk<%\q}MB24vwM6Ҭk9{m5o.QlPpFP yysR];=Uto'jQEQEQEQEyy&Kkqj[8I p6}_ѿy&KkTٍ;jzf#4-kA,\!crzľt鋦5vr,arX$~Ξ1oKiۇ09n`d#5|x6x-hI#O@65ԔpTQNk+5y ^\_XvP&cA՛G'WG5Z':)Y( b:ٿU_zwa ?icc}X} 'x^C7C# m_/ahVQ~涟&W;(Ṓw'uFq^A{|r!E`Ax5k7d/u.ycS¨嘌+o~>++M_n\ =O5O=bm}5pM4+;~Afʎ!6-^޽:o N|:v7)wnMmV=2{wLA+7V,tZ[)i#fM6#yzj}>~ZɞNH-x^03̂G,rq^!:~;q^kqG<2P9s<`g}l~'xXM'G5]r>~;w߂6ўˍZ'`Hc$Ae9hpErss#=PhjKoZ'+&msA r8-V~-xC2Yæe"о <0')j[N)%=/Kk_sHy'N{;+A$Jaz'$;35)&iJtڕGUotۇӧoX9"xIM>L{(=)[Z'1(rCHu5߳ F)5O_Lv:MM/^Ge![k{f_!˓ #~'yu<1tfL[V;]D(γBU 0}@$M*~/Zլ¬Fe9铃ׁ_7/5 VB 5kb\ۄ8} Z k}E{2\4؅ٹ d v5WJJDrSo[Cqw3mB6  )=:5~ex'SᶇިZA=Ӻ Sy8_+SMܤs)]+o?t%S((@A{s6U/χV4MND[k=^Hb2}'j.|1-[UoQQ3E&ǍN$`SЂyCx7,MXSZK _Nn%[Vo,}3?[躮.D3}nI*v3mbqH]k~$Slמ#֭,ΩqZmq©~~rI-!'=p1T7ۮnOkVI%S$e*|4N_֤z@?xzNhwwڥ̐L0)\So&e1"6],C9'錌1~K#4} jMn lD]Fysd ko&@%HF],c<LexA_~Kؖ}An[Dќ|]xzS5듳S׊X%8X1ɕק"7g.Mʍ5 p{CSYXuF:\YICCm*Q末v˿?mkOC[fk+s anH淙78A&cF"&:mɂ-l$_Mz4k%Ӭ |E+:.ƕ KSmGH&)*@'鵿iV=7dw|ᄷ$anC#Q1=Λhv>24 k2NJ+X31lXx߀}]NU(X9 x8c i!0>R6Эu}|MZN^Xi[gYaCcw0DAixMxoRҼ9yfΎȱ,v g!8u{M+HӛU7ouZL?}G+OG=>kyn bK  MJ_ZokBB5uHl55oFMm< \.Ln~WA (b?-p|:5j5.Qlج6Ȏ+u|F\@l5 Gd̍g?(ʏ4!U$1N^R<ӣmim=3Kp|4bGn1AO{//LZm[i,֧,22rO^ \~&|?Qf/xu)H f.]JAp0񱸊$ahҫGn`k1hƂ4s%x-Rnm*;b!6^ sNm˒+G~+ӵx̗plUUguܽۃҸ9緆HmPQ̑@Y2pz5m-#B|\uW,Jl>A=~*&h9-2#5 o[|rO{ſn+'>xQf< i^3_ZzڞYamUa2sK#׷9T2m qWWʙhL? x}iO[[kZg%$vWtVRJ[S ɒ#]>ⳟ ฮľ PIkp^ 5FP6ʩEu X+dh\(ϭfkL߆llcP⾽M||?ľw/ oSk 4mnl z9_@'!3 \h |!q? rftg EUA=HN&KDuzboiIgφEZ֣med%A/|Ly7c'>?_ß { h&ٵH?s{tk1?_]{}=H&kGf9,ēӽ!L3]i8jP1ʐG`gqSaMZ?4*$m !x k>xu.)9fȁ\Fqz~&K˩#բ\}ݛo2AG@%zxp !+Joĭ^u ii/U! yPqޫMjC6qc=䳓4A )/#=@㩯#/i > _`{ - K{Vv}h>>/E%G&(E>b};fοe:7:<]{*ϑ3?#c÷Pk;b~z/gOE2[Ijw$J9`d (;=U4Y- #S$9j@Q@Q@Q@Q@E^ɯ|RzEmH?׾)ؽy6>8]Tyַ:M$ 1|Wׅ6j6-·< ,"0Flޤ.q_RU QZ4JTȯ!ޡYՍU;Z-[/꣊.žhO}RIX%L"3!d*rAȯ?hOB1X2;+i 2"޻@ws+@`%ʳ+ڊ\{\;8|bËM2UgeYcv}9;y_QDai`9En*N32+Š%A>OU`8J+QW`8'PUT\ {RVUkM>]s %9ۓXz׽iQ`>{[vxP]JN*I\ք#iB RӴ W}$WОZu*ǟu>lH⼸-k 8T8rI?.Os^WOJxV .uSDypeKn#b&L8AYzOG_ʋzVw=xi^WOEF[wS)oźD#'<kĞ$Zroaev,n-?Zu*,³_OJ?Y/%{_ʏ-?Y/%zOG_ʋz i<;ۯK.|1h(OzO-?Zu*,m%閬2*0@QEQEQEQEQEQEQEQEQEy9WS=?Ú^jqu$eƑJPx>> >#xK#[ %جcѸ*_i\0|_-jX4zf-mi#`/$RpJ1Ӝ~-Ԯ-c,FO`.7#"`_pOS-9'tυ'u=J>8n59o'}F]=Fiv3yl.[w'2/4m?QLwC_P93#χ?xA9E~qÓ>?QLwC_P93#}3~^qX[\qj$A;dQEQEQEQEQEsmnerd9$_dk?57y{o/?ƹEx}* B{l{w #&)$gXGb y{o/?ƹ(/$_9!5VnM+í`mL5R7-G^J4& $yYY.#i#=?KGԯ5: =KW\LI*lcbƯnl0Q3QZbE"1 Qg=1RC=[YF'R jE=CH8M7l;w<=Q{k r-me)+ kg޻j /{t֗:'qrE @]<čV@ ׇЦдɴ8R=2K8~,^`h混kHdKd ,r)HQIݻyZ}Ig?h6^'{0\۬nð4hZcei~ 0"ռTt.5bf\412 7&H {ߓ~\U]ۇG/'vw{f~Yׇ|Yu='7f}Z[ՖX-TcZ1o5:-/>tk ?oI;&X:.ȃ˷͹E{ݯ;Ht}&akwd#iV~籆I[S`UF瞔)7ēx|O|1[Zv,qHa|1GF2cc9όt{M{^.֌PU`2,riV\ AI9.|R;+ۅZО٬sۛQD;OZ5EUjLx&%‡%UB$׷w_֟|y+{ѡ@C4 ]wPżKhon`I$wZiծ{{2NEN%-f"BŶI`!}c֐c,bg8m9KÚݽ徣. WVQȳ) U8QC],`|8ŸjPkrw\"f! wU 6CJTz8564kc?g髙I\PQE0 ( ( ( ( ( ( ( (>&?M itIKv~pON8?xZ^%t{ ʡ1Z߶wƟi7"Y\`#["? ƙ5喫\G洒e~ 8#ztcGZם n<-M;Gu (&V$J5HpC13Ꮔ,4biyx!u+[{-rԅ+S ^{r"~Wvjۭicg-xn?\4sۉmehPɰmHlyniL iU_Kt&]A[ ^*i}Jo>_>8??jTs1~-Y[tb9YRHFjl2xUVNpGq^RJGϲ|Sߋt;+i5 *j 2=5+CH.Gb~o|_ZmB9{hdudPJ95=ICkD4ETc2~3?|b nAK(aO@gD')zxCn|ǧpOZ? ?x]^{hdxG|#hEB81$OZs ʖ]N.E*2*):O>Iak?<3uz2D"f ?£>3s+i5e_˿׷?QUh-.׿(ʟ?#?/gD'_h?~3e*Wc~2<)wY.'Ug#C}kO"O_?0'%{/){'EU$@'gA[UAmb|XO1TFd0 w$g+2#h ,22]X/AvbspXrLRFvi%[ 97 Uԕ֖mS|6<{s;z.nGqqdv0`m8~Z541%*l%ʔ1;@>G.&a,kܣ \c&[?@lkw$PlukDүI $M@>m|3d%K̀wgR+֕뭾I&rƓi~,z#2ِI^&A4Z灬B EqrA"|# 3t<ÿ'xGLj|3kzZknhmC2#c_Viu2oqm}$L1#2C.*S狺}^G+ݎuR췖3,AYf|`0'>+k^,m1ӵ6«Y 6SdO, ßߟZ&`uNy݀.9#8 1~NkTO<3!iVF KQT>gْ"%6D\ I^]9yg\v1C); x+:Ƥ7ڥM#c9E~I]K8xVEyѰ7ʰ J2W8ϸ7]a`R_oO5-FKmΜ 9eAT3*5)CkN\],bqf‘#q++r<>| ??MfI@Rh$X㉝@ԟ?aI"G.2֖ \g$uU< |"vAE%9pAu,|;td7/ϕWU|Y|=1'QԪ} `5+y<9k&ey66E,CJS#P:5υ3 \<[yT3JUYr$|g3?|b ?G,_Y?d[)wx^ix&ݢ5-xm5OK諤D " I7=q_m}:ɠg!RBW>x4?j:*YCMly5 OUknφ5j#~ E]GoYM%žvld!(#IfgJp8l(4_ YqnI֯hK5_n 8Ĺ~Nk)Q^_?>=.$?4Km>8M_|k),@Sv$v^!n|GYkiwciGw$@Rdٻl3ӵK_ϐi\[]l5'jqtd5V.gR!KoUSV~Mj3^xv A DQK3Z1@5+Eo}=]~xYKq㶏r&'th Fim$\@ӘlRUD ٮ>g7iw֡x宫y l_ʒFT;e|dg=+ )|^i<5~'<[q隄S]"΃O1ڤJXÐ ^E_ap) (I\W3+@QE0 ( ( ( ( ( ( ( (>N>^w[,mj=̥"4 a 䎀O|Vmok :O1%-:#'>2xBZtwi'ЭhywE*I* Rsb|7h7Yߥi%8a,Fl^x|23uMWV0Knƿ_mj/NFI1 "=mOZ>"unܯ&bdpͳ\ϊ~ 7\%ޛ<7vY-!Sq~LEyLJ)xw\|? I>2HL@SnS=MZܺ >IxhhAH~7/Īo$F` gǿxCX-b}-"Ixp\rČ_3=Iq7-Gkc"i̐%/jZ^ke+4V098ž"N4/VXyTdd+VףYr^%b6K'񇄮X,;'1ȋb/9x  xɥI`R8vTPj/LK$c2W~iPIԞrSp29ی׊A?+~6/Z٣HLc־&F:5RXIKz>0n fk 4 9@[sӊ&x[M{$]+.^if ۂ~^F88E~-|E+jfk3:ĆH9Fd#w-3/4 ix_Zne$p-եĪ:LX:(>\AKOGʬ4Zv]'xٓKu!XPă ˷$I5kǺxVyu.lI2 IQ,-ÏbuѴ+dmY+K23-0v 3T˫9̲0AIߌ9bqq3^޿kᲪyl-ROа>Q_?퇊|Mkm]u2?0l Bqe}s-Bĝjm[NHd935#7Rs_=ƃ%Y|f-'i^(]3%mI-KUcmx&RvAy=+?iCD,%8;$`pA(9'&EG VE-$_E;dJC*MT+HP;dWO׷6n Goy{g!TT 8+pߦtkpqg;iڦ^jwVF#-!5t'r0kP|R)΍jt㾊и$3n*͌| 6w:oipZ$ ncjo>PE}9_?"V❢𥙴bXhf ,=)3 4^Vqg!xҬ?'֮K\ . NNvOXjmJI/3“y_`폆#8{C㟈~ễIhqS<Ӵ [:X8n]<̯$>,3ĭ<һDE)k&4W]C-\^ZIRwx٭R3?k1|CuxST]*}G%!qaow6.~U/aa+F0$%1k?%k=ݼF9f"r>nF1]YUG:}t_eqD?EuZkej_jhûw3!60gbS҈]wF"ߞҹOx@֯#ل!FeV k[[|)u@^,;SHd 'ڞNƼWWabE^6;\啔nݪ9w^F5K-#{9Wz{Q5GO6WQ- d'9ko۾2m}fO1D0z{F~5X@fO[3ga#@KD-{֩hx@So>?Mչ ~ζ:%ψƋY8IUϹ2S'5GS5/W 3Dq"O|')A0>ּAm>7Oۭ$IdFO88:\OOr>[s݈z]usez_Q<ʅm8A\ׄg_peVbwĥG$|' :rUyF*5#lwҾ s/wg Zɇ,YbQCۧW/ՁH[S7,G8d`y:ihrW#>` ~O/_8&gioQdxXLdp=AcfAK/"u OU(fAK/")w|U)C6 ]1fAK/"@?gGlb*C6 ]1fAK/"@?gGlb*C6 ]1fAK/"@?gGlb+U[K_]qa%y M3c;PP `:OݭidQ֒j8Tq֘lb(͟_Erc}Z?N=7:-ׄ{F;i#)y0WKy$Kd22fAK/")w|T!&H쭼cb}Y 28'V*}^M)u+#LCķe)IcgGlb+M3a__ ismK b9٦uKg #7 c=5ֳ k'k1%T,ȶkmr>FcE=?Q[/^ #[@~{4OK}2f)"f<^qlg+ups1bI$y]SH{=-˿bk)EI+d+sc w yI+d eh ) + WoH͏ʾ7vyu/|Њ?|^ɷ{끥@(5bE&ò@-&nY&0e|d0A_,| ⏁7Šes.l,#ʧk0^H㇌<ןW.|忲hI؆ 7z\LD51h(>|Ek({qk0'yov2IO>8?ym,sNWfUVгxᯏC=Ӭk6l pPNzuG.xGd;[KjLFI6Wn,#zWljʤ┚wƥ(k}{//3WqhkZ3·\ەO)ܺ9iE-mNmhwB&f\'^"tiaq1D\+p[s@54_Nkl4}bt..Yh˂lBpعa*zͯx:r}'jַ?uI4!Q<"dpWvvCZ_-| K[eXXH\}1pcP{tcj:D/`ɼbϹ #(*V0:kh[rKɠ(E,2FLaNCk "կ*J#HZ?o$fls4%k&+>%=>5x/<@ʶzqkl,m%̍ߒG@xΰu1a&SK~YwJڎN |Niu?SK-&&܀+,*-doBb|Q}oi ;D]U =ǦzuK_zćMVhЯ@N:H϶̬F^3Pm]g^+xDW:p8 ĤdsӦ+>.|.߉ m嶍4uLN O Erä\]L GnY>#lf xO_]/M{PZ!Bqg_(aRwZzmt%ʉh> 42:4xA茨t[?tum//ڪHݥĀi߾"{ķ'P<^ZFl{XFUkŨ&Iݓ'P+X9ʣR_({H%Ӿɬڤ)NB g w* ;;K ]CFْ&,hY~yh.F{W<~?7:Egv5EoUdPq+kWIJx:nx4{y,Wh[)za050JmO_~uiaJv>Yk]V?LHd,c`Z6CT2ڻ ej5Z)BL{i& l56KϐȮVy e9O_,xv& O.n^iXEqpx}^"In=ˀ^FT0 J&ԭƣ}V֐[NO:ƀ>mwh_ swe2vbLY.6| 0tK~(lIis,洳Xѐ0* 203[@O{[Qae]߀#,NFq|M" ~)w?o%8]b%ɖ(XepF~t; @TH$ A`+JO1,}=? _jVq)(:6D*KD-{TO׼/(_o[46:爤&i2\XJiS6)rNaM`G >úmﬠխ>wqg9COa 94T%sڔ_Mlg J4[JؕC#ӟZxU5I~yr1 $#7vn|!$Z߂o.%.͵(G`)*VUMCT׈,uHKҎU|͞pNHZݷToF.}-:K%DS΃1zMGaC4}N|cn/uo?b% V 62ͳwoa\Ӝgl'sZԴR`5}n(3Zz.ψZQk :a" >]8<^Aey}ѭeG[a"}q?`|Vk&Xi&a%ߚıaKtZe 9TWGG<8}܊6?` ~'Ik?5{[QQ)QEQEQEQEQEzσ;5$ϙU20A^3LyJؐ X^MJψ4KOyM%&vH-㷓"H|Xþ#uXֵ-jJt/%dShD6"EPVHѵ-{ac o@0qҖ[ h~} f0"! #V<b(p*"Cω zF|9x:ϭ4269.Wszj-qbuk RJDԷxb`vp8`Ui<%]E@Ě=K`^ᱺR7 ^+rAҴmD}"ÅEzS//&o+g's?WMB((`QEQEQEQEQEQEQEQE|bxA~:sIyR%^e_G~|]ʼnэ_"x&5c·K%”<׊ UDx8x7JK4]_ ]YE+Q}I"f^赯FZ9GAkK}?ᕝGi "g4sq̞%-N$%s%d9lmp #5#OxzCukbOm˯~(.u&Ggcz_GڛTo!$Vc|7+\{;`0))AǨ"yZ=*y{(?Xƺ{S_TG?# W7>*[m5 񼤗N9Lv=Uh{ GмK:σ Mi|6g tG)3rk+m+m:9 QsB%l=YBƖ=K]1n|CvU^&cyy88xbZvC@:y+,T|#k iI:?=sǫZ $GܬdGPAZ_tn[k n$mILq>f<-dڟ5AnDZD.P8p9^x~*کn#Ycyc̓nHQׇi(p>ûYk]VUZ_~yx l%Ԓ? B S5KKy4]46cHVA=z䎃('%ֿ[i6ssɱLv{ҼwZ! isGw.%#wf5lgsHMxOׁSX5̺H%^{dHʹc^͟Ҵm6! 1.%Σ;HW5͆>c2Kc}OO;IYa& #UAE]Z'l??P?Ou}p_ύ4MY3[;Hy7sk7@Xa#xs^ROI5un>Ѹnڙ UqpH/KN<8[Z=FLI`cf䜟¿3;|\ŭQYdb [x}ON3hO3ƿYinark'/u\QZMޡ<Xm%VC]m%{B9ΨIiG@OQEV)æjC* "(2[z SHi~һ(/ *>/y-:7E@EvQ`0=6hFFr8UnE0 ( ( ( ( ( ( ( ( (<k_MF -rgy391G~;|9gǤxu9m-5]F+y΢@&7dW<k*@?']kVTdFƝWUnϟ?f66s (%;<"aM2;w]707dW8&]|^ _[t7wATo]BT.ӑ{GSxwPڝ7*,)1ݢmH +~ ^0X>,6f=kt_[⿃##[ ЍT\~+|NjGK;4772}ϻD>Ey#o祿1^?kw ?۶]*~׿`snǠ걍)-;#<8c(יkݞOO8߉;}yn1H 'p {ie [ ׁMK[-'G+˩VI%F *fbAt?#]]L[ɄS);#74.f88 GOw'*IDMٔ&SYmJO=epbOïV͗/eMTH =GO0+M?h N2v}ƒ׼<^;i-g4Nv[m(1 FwG8ZPhB8ciay\-_l8|GҾ#C˥Gtc8CA9# 9{7KrwWM#0Y@8nAӎČ|WO՟Άg{,Gπq^ VǏ?ׅUEM{TJxGUҞ~_,QYSJxGUFׄ}Iܶ|ۚY p¦Y:Z_Nֲ {u%_hf6#mfglhӄT뷮+>-Ҽ׏ב|{>/17CX Q쀍݈|hҾ k>mi62^kPGoUJc<I0<|u7&G X439f>li]+Cѯ=/\a:.ޫL ?yXt^6l5< 0>`r9Z?Lm>dhp9\论2D ]pz$*G Ҥ7\jQELR Gbqׇ~^y\I8g@O G>Kuk4֎d]I&<խgIϘ2m;y'OgscM~Eo]$)R~:Bg|u[ujF5I]Fid?fCˍV1u@rqp_"$"i{[ &%X׿!cyW奔?^+_E_y<%S·xn[qvmОm_o;ի:v>"J 3FURwHW\w_j}3"_ /7G<+U||4+?Rݝ4h$HAPHGrFnuM^_Z[I=>G!;ʑ dUȼWB}MQ"_ /7_#xú߂F|C躂ArP`T`}A5 XY_YxUD.cܢG-:IJM]0NKE_o*yW奔^nj|Yq{ jSxwϦ*(%!l `? |qÖ%K=֚(& ,*I`H4>x}E_o*qx>%˦xDӾt{O:(HՌݐyf wѼ'}fܛ;m,8P<sɸzQ,CyW奔?^+_E |2-%u Z)Zˍ< w w7^2|-vrx}E_o*`7!=ͻA|!G62FHn*]g?iWWOlnnF>oe|{[MȼWB}MQ"_ /7_+C/ RxoaMQ|𻷔\ځ(vYZ/ЯSTȼWB}MW_iQG3Gٿ/ЯSTȼWB}MW_i|H" &$"iw #yW奔?^+_Eދmh{ v2E?F1\7`Y%bemc8piw<+U/ЯSUt{I,JU7Śys_JvؼWf\GX::NX]FEpE_o*yW奔k煭aw}q4:=4[̙0 pxWoyx-9r-u&\R)BvH@6qh{)7hOp3AWdɻ9[Wï>:oVzog5ʙH!w`2 @}_|EAx dȾ$={T̮|ZIyJ![N/ odPlŹqjb׺,]A*0y8 M*q];=Ϙ>κTv#UA#;@T0q!`/0?J)k>JbVmB7yЎī{;mU?gTx#0X<<롉WFZ>hN]wE&W ')F>b+?Qy >,/9d0?jNǥDx6U#n3[¾n 6Gqte/.<h Ź^6t= KmPb vY~|Og 6.3*G|tt[ ]R SE0+*0Ln-j/cO2Uד^nu%9>z\#ܜ`TWNlİZ _Y'Mi`.FB.pGI8WxMJimТIQO¹cV:QG*j%AGqItmw-izt;Bpz)?OR*=wto-IB>OZ߅6|%7|.-Յo1W2^1d|:n`e[ T7DB+Y_qRm4ӱ$qm(=#pֹw/k?%[;HmfkY7K QJ }M;iI%9A1t>Dok%[=em)GrFy t5cWdE~?/uiP};QD\ʰ D0%fݍK.)+}S[gb&7aߌ7I}^g k˶Wm1d"S@<t @U dvRݺzEF!J=g{tT?I󕯁~;w$Â_-p .͆8V}=  iqnkix;Npy(T%-o|i&L1qzWQcL|j^;BapHan>lwm,/ireǞN!I5>[|mhwPj.b-BЁ >#NZk_CiRM__#|%̱ŷIbF Xb_Vz>8$13'<w;N>v>9xkO^}-N.  9`G9v>>h_YYdI.|<4l/BwY$3R37g#W%񞽦ȃ͑=ۀnr^x_-n~emci,"$=WU'RIKOY 1!p3zfS⟇eb񘃎X<3"(I2coCב_>%x_._q,188%K1!IֽN&]O|c9=k|A_ LV|e.G'hrCdxĩ|M'75ֻ2Ār8MtiCXD8POjxuOX˻Np'qZQܿ .~I&ǽ_Uƞ*ak4ba#i;_TƟgㆲ1b*я^O) tOS uR%8Nw5鿴u@E'<ך͛A5Gy%4ߟָ߃5O:H2Fc 79ϥg eYǚ@.| >uzխݾKgJc n93^tYQ6Lȡ'k#'5 [x" AsfW2\1^,GշϿ [|1oG۵wks*'6nCriӚ}F+YEC~gzc'ԓ}jW]rS`mbK{Q1&[FGC_~O1  IT'>)k}1-ψD Gsʬ$̳Whk0 /C39*fֽ^[=scN򢳻Hdq"Bִ4_M f[=OKWM"hf2\>W)떺gggoX칸Ӟ'XInpJƋ![Y􈎵eӮo- )#o%GW< ]({jnj}w9iM^Σ>W[iæ]EPDNBu~ &V5HY%s~('iHRB.!RB.! \|%i%Ӧ/u :9CKwnF$|ײOگO}RK[WoBhʜHp~3KK]βo|=s?O%ݷu+Nj@hR( c&?o#S>|3s-`K$|Ha ]CC ]CCJߏ6y2*ZoZL[Kpt4ՅC U:X8 mh'e ?fہ!4=;RB.!RB.!Y_'wV.cL5'_|_aMQ K烵B; >iQ; 3vڱ#_q0C5 ]CC ]CCe߂_E^گho3xsD:m-xoצ|<=ayyA?IqAa[eǜ\2pWi7KK2wvioGE/|!q$W-6_ \̶|.Y}Fp3ĺ?Ə C ڑ#<%_Z a. )|_Pt)|_PtޮOG~$c#w Ky&V_ٸ2dlsֳ6x;Lf CUxbMϽ3-#%CHyᔾ/Ћ5c-ȣ3⯌|OiiWy_eC$#q%>ENr0St'GUI%Ss9Ld7ce/".e/".W~?O,;?;Km{̡~Yy1)T՜qں_ |J8ҵ GEn59` 8>YXX9(O2uG2uMio!=[}<= sZwuE b1\`8ai_1a6 }mIc` `e/".e/"._INOGYyúaq4 V/Y&DV_tG ``1^ ]CC ]CC?zNOv *+i>_E??]_E??]+7O#]mDG#IRB.!RB.!\:F[\] b+",y1iNyĒrORkK)zwg(^P%.=fmwB#6RY#ھ# _[l@Iڪd?Uwkǣ<'g?d 'z+?z,WOC}NmVİud*IWSo.L9R3h1q~/A&Adn3 UxG_7!꩸nϧ}]u08XENP$jh[5'?i\1%Ljm#2 ɯ/nRMuhXC3# ڽN2qڿYkB*+d|5IʤާFZ*Iv?d?u1sE,Ǧ.4j#ĜPk[>*x6~5l=x>o?Eі-Òo(!TP3XVS^z5yX|tZzηX֡ 1rstw@I_Ƌ&;Yᶷk{CۺVl?-u=[NғO|!Ϧj4_٬!̀)$i*ڧյzMcR' ;V4=\vi"bKFrx5u5SMԧI^H5C6Mo$p#Y1b0`pNr+mBOF/,Q%I1H;s231MsƏ?3>wx[P{{xV]il`0Y9V'5O\kvMl㹒4nhi?;aG&ichIu#4ud&Y-%xIB0PSz_(xTu+Rr-ғ0lzʽA]G^ ;Uke1|}?O}<'Tۖ@mRI.y=]ezKwsqRS|(mo?ioB3s4Gi;'+Q?.?ɿإbɦ5M*EvMn=k[TK_A5s.}N#&~3hڅϖhXֻ- ]WQdWΚ|U # 2"fm'%q Rt עNɞ|iΛwGC!YCnӒ䌈!c;Vw5ƻ EHQ@OWQ\++,w C66JFÂUO--o-绔OCHzc_Uo.VͥM%}(ig5y\ZC&Bqv7ӯ%3UԍAO aP2v~s(f{mgW)G35Rc}8>_oT%$9 u@yuxWuIm\ŜI,=kb ,u)es"՟7 Cyr=,G>7<[i2}ƽp!pyjk}1둯:? XY1~>WqxJ9a"jqMrn\.2+š-mqfco%k ^K1vH)ƧYlIW[ϧiEw{oC1YLu }+|9sotnE0EnEЗ1 r5ex^Se_ ]xao%['w#?Nz)T4#έey,(x/ 1j&lA wQXy$֍|Iosvw[(U}1bk]La '[U 䟞Bx}?ZF35)@.c$yN-8GBw7h]i?4nuMJKi&m׬r#gh&Qcךj7 +MmިNv.qNII9]0, ]Ip(R¬k,_~tZޡ\gu )3&EFo( r3_Mqֿ?8)!y>&{ 0Wlٯ}Χ,ma8݃q$W!WIs<Reg/01{~Uy5(%tyxYihϥ~'0x @aR7`Y@1A?.IIOJM;6Ě%$J<5音ux/,"CnI>-Y T1_lc0zHOb=e |m]Ks݈L#?7R/=Z2g./j,.0Πs|uCxvZefqrsqc/xZ5ndYDCw=*imw kq ŭf~urP_'+4}/8wm$1P <עG'MlpmDt9z:Ej6Z5{GĪzAg8} ?GƐҙPicqFIaSV]:\9b|n lҩ 8sugHd> +ud#Z%h&Kx,dg'p;~||%IBdTGzYd{4?:jKm;wkm74&(~p~=YmhbeĒ`m?wA^hjS}$yN8+>OC񎑥 5 6b,1J>f 7副a҆"RC'ZQ`׼)^A8#N#yXbXY\9$EoAxZcWK{0Ezg!~f|Hj~2Zv00?÷ 9(WK=M`kJkCQCo[n0b}:ˎOźq|kqea7/6{,Wb ,B?ʭot-:v F\cm;.̱*^A3 $>Ÿi^{4ꑬa!Tx3ipj#bb2.dpz P0Uwڳ;\5iQ߲.ǻx7Qngo$+y *kD4_ (Omnb̎Ӆ'&FxK^)ABA cLxOZ5htNQpZ>lj<9<kΣaЎW(JOK]o^J՜,oEo"O0qb1-msyk 8a0a;JkzEFg$s>$,٧[ip6cpHJ̏MYOzfdC#b?++) Ȭ~'RjH^+;^#~~Rhz$P]łAi5x p)#Nͨ.r#&sSxKXYؑNEԀ Ze4m:inkXq<(.zm$߅ 4ټK[ޥw"ĖpFvB$rqIo} ZkKv+4ܣG9s@tVZFdq.хbΏ?9J`b;mLVo#N|7CUk 0ҮqszI[s SDb6#pÂl?Q`< lUo +c}(Y φ`ҿ6?*N|7CUcxG| >X,0i_G'>J?<#cxG| ,s4  lUz<#?9 WQ φ`ҿ6?*OpGo +c0i_^8?pEN|7CTs4 S?Q8?yg'>J?9 WW(?Q`< lUo +c}(Y φ`ҿ6?*N|7CUcxG| >X,0i_G'>J?<#cxG| ,s4  lUz<#?9 WQ φ`ҿ6?*OpGxzSmq]yQSTUP5K0?5z 4XW-'/}RJq5D>De7 R;yC__c?5K>l0Mľ U?۟Mk ԘӭaRUY?_巎׌ lJ8ڼ\Rh*qsg.m"y]W6lX.1ڸ]%8*`EV mؤ#'=+Bo'O %$Vi_NyiN߄[k#q vjN8?*ey(jGr* 0X54VǯsֿZng_kpXGUn1sޙ'_ԏ#}wtTG>qoZAC9wl>$xJ±[æxɮ.aBtzqBï Wrzbg焼iik:΅{TR]H!pH<ڝ5 n{Ҕctu;iI$_F"1,/501xozBT{v{c`PA__ٖ|AOݯQg>߱Njc]3V޻Y$.P tֳ1tVqfpG}xjDdN]>?m #XYEf|\ )9}˦Evc\|4ė"$^Dw\(dE|~?SMutkW9K<8|Hm`~umme7&qӓ?j]:NE F)Mm8A]q(<0_^"\ěQ Ll@'ԗKMu? ]~TWVH_FR0G>C':? n3OQ8˗gL15`w]#߳MP^XtH vŚ<Y`?W\<LtK잧j'Me* ,0}=MB.";3W7'*m:T$oCL_b 5=Vqipr*%` 'jZbsc&eAW#='',QS>N\7?=gھ֮''#yr3!jN=+Χ6FGDl!ݑ? 3O46`Sc;G?1qG ھuÖPJ.M$珛t/Nn iju 6nP~B* oeJSϓcJY#@SokHL3/ >wmy(r'k{%߉XAғnIΘy=ׯOZ6r~"ŷ1}1͏ʊt*sE!՝Soj z6 \v8ja5ͯFr:_b׫l;c|muB\񦏬^fĚz!eL kI)[ ]S/|9J,/—C_zܥ%ϗ;>L6q+o|/˳⿇-;:zl& ݘȒ#*[j}͍+sgsC4.2#OM{|mo/hVuk-LMYm$4;yo'~fjO&.}Ʃ/ե_}t#07 !$}c6ziZDoF0cnr"-Ⱦ x4x5$Kje60Ă2ϡ"7o,}KGuxڴ3EiC21@gQYKz73N><־xߎ/ꗚ׆4;{eMvXD\nЌg lQ[ahڭ-={K{KfؕWTU6䨥SuO UM1%xmmK5|,NY}EV<C~o/֏t S𦃬C̟ͬ*LZ0e Ntx;*+8 :'f:?8 :'f:.Q^{ NjY?{7! NjY?{7!p= N<_BΉٿN<_BΉٿTWqtOtqtOt\BгoCгoCzxxШ=,=,=Ey'/gDG'/gDE*+8 :'f:?8 :'f:.Q^{ NjY?{7! NjY?{7!p= N<_BΉٿN<_BΉٿTWqtOtqtOt\BгoCгoC;j zMB-vf| Q +@xAx3I^ 3+-|@3ak:8TjrKgtNŚջ[Lv bV_^$Y8ᾹwglF< L!)ϛ r< i9+/tw47p E<lI@b,o&W/2H' 6o<%2=~4mtI"I$Ѳ!IF`t|´I>m/p]Z$|P)x[Nd Ɖo< |G<浡}jrk7*sd* 2$R:YԜn%zkv?WtxWѐqߞG#z8L\KAǟF9'gbFqU't'p8SYͦ0%H'>y4>IT0f7W'Z1=|P|Oi&5"$ љ'kݜld`"jy9Ւ=M҄_^<[}SXJ㶟Y+a2!b d`}6obD6eDmcpa2 ϦG,Bj0n3,:Ŭtl8`A$ {v5;ˑ/YGkTQw};8$&TQ]gzzǓ_qV.4jƛduԩvsxR@$V^uKƻK-ͽ1%aIB+ m4=RC1޾9Oݜ[vxgsukmqNHݎC[NX)ppvc?Dؽ4u 1Rѽ ~|8I^nmGUd!H*x q~7Dow]ʦ˒?^i_R8voЋ8?J~ҴVȈ0_dziH"la'&{#);G떟C=_i,K [ |-ιqX f+!i,#TR73zլ-*@fAP^jKy E+8QD ;VWP9q0*;SRB| 4 ddhh!SӐS#|#f=wϊW=2:ׁ&LגRF؝Hf9F@$J9t5kti..u(%o:]p^W{ X>mi^-ʤ bT"{ tia/!eQIFG'ӧ9%*qݎ-4Ʒ[ifXB)Fjta_L.qѨrǎ9~2t–֗PJ "_.FU$S\gwk < [䔬m 238ܙJwzN[٤_K#◃TmٮTt'N1V {'kZt$sL׍lF`ȓ*1}ImvSt?zlg𷉚јO*] dg|_E~>)BZG~ddO/5ޕ{{AsjΗjuz)}el.oz¿'ڝ-qd$6S8JJx&CeC7h#sŹuR7LLq*" 'dҮ-i$iY-!|VNt͹^I]k3q\&IٔEi7G, c2(!) *힇mcq5Ƨ$.( bfOC(,cs&eY`ґ/(t  &CsV<}^xWEդ{_u YY>irq`0[kNW?na}mmnvqCVgRX\rzz8Rz+zޕS]v4vi8ftf<|h,IMUh7؏"O_?0'%n[%- KK 3bڰ?E~ Lc %q> h$ڍ9"8:ݍ(_>!xS±Z{-Ȍgk5£nQ=e[FkmquX6Pڻ1PH]J}FUfΑG7,1H5x~KӭVEIeYܪ K!.T'' jo{>%><5h y86go$u݁WŻtyM.Xvk$Ҽ3OZ{Z,T2͸M"HRwyj-E܍;Flg8ϵzXbZTϠei4`T"+eTm;sEAlGsY# oH2c$Wh/ϔcrq}. 26hm_,] q^^.={=I Cng_~}.$\LB6¾Fn4>c1݅ <{_YY]Ux4]Qu;{kb[2eFӵIRFB>G\%K*6$cy$94K)q_9뷿4Go4$FE$%F:s޿V;W{&e]'eGs9$* *s#Vtvpg%s0*IʒP\סzohzF8C*#d߁*x=t=.MBZDk&7@|l:O i.RkcpmaˠmϷwnF>+ϮQXhZ? E_ No|=@*,-I<`v瑎_Qs꺖TM4AY̛q2U(gÖz}Rj <n6JSuxk;j:lMtzQyvW('FaRjSe་JӼwXW[1;L>e rqۭznj]bWwZ[YF2Y) nRpy8wֵ߆zNJKdq請Ϙ VAJC-uo]E񾫣KjEzD:6Q7S_3U*pu+9Bҕ:ok#sMCPM^_QKkth @Fz++>~8յ h4)b71Gi%0W_#x[ }?Gοj,lc\vm=Gό5u-{嵌2$eYpsO%Nю*]&z}\=t75F 7 K&gabʾ}$6Zzi66#`s5GŭUOt fMw\X-܌zt^ᯆ>:N}ɥMb [nHyhܣ++GúV`=ɎԊ?ƞ] E:n^)61O%9;Y٘1Yp]gV=vՂSNVzj:h-.5 l2ƯpWZwa۩VM}ϒH>d'w t6? ޴74$ Dv'n_KX<]) gl:ZZյ JVa}kƮd-/fMp#8^W&-tw)ѠgO9nLڪlUcȡnLy|qFMod6_C(B*n;4'C$qSG5hXd\Ǥ]?W"hJVW>TnfS?f jT嵚 J11(D;Tn!+"5|Fg|ɗ)kqqSi3`Q٢ʹwc`#d y~:|Hi!|-ms ?-˫4B'͸ȸsdo|xmK!_.j|ŊH`A푸xc"zФIm'NxӓWm~k'F-g𽇈n4{g!F1,+n$u,;xix{Cn4o jPi-̫s< BGWgCËEk;hխo ʅ*Tʕ 1\O>xWNa`K:MvXsm,[s|nFmܹkYy!kˮ?ic^QK _xxk3ZڜYs筹 e. l}žnRM]UG \0@98/ AVUk$[W]fӈ%n(nk~ߌKquc0<&e$9lp0x9{+Z}_w W5^N56!{ym'**7T1Z7.iCh֧hqZh..mh d7)ף9VEt5촍ZNY`P|{XJ#zs\ْ=sBm7SukTѢ&Ե [[MFpUWˑcYB{-$b_7+~;Z+xH"ӓKӼn.He21) u+mdۣ~kQ,ym[%a.6m`E*{_?ěx"FO6Ĉ݌3pu=sx[vx*2Xk BӤ?5ᘾ}ñ:!VI9]Uaoq<^yzԜ1V}ghzƿ;^Ӣ5kZ=pA51Oׯm+ ?VjmVUA Urr3fA5H^}2(x]TzQE?uT}de',5K@eʙN9r#EGG:bQVGAxIԬN>~eϸ;+r@7n>ȭOF3\k@h.chti螺6pW,ҋiN i>gތ90~ǟ(&ὮG:6:˜Bw8՗oYuy-q^mi׋oژ DqO-`|AbŐٞpK31ZwK3[P/3ohh7::] lל|E?i?iҡhi}wǝ),?9v3ջ&y֕fQ%U7|*F9=f$c2U_-Sx/fuKï~<J㻶1G2MH*I`AʺoW?Kx7M֝dM芊G9 sQ|_j_~2g@Ga1_zxSPƌ[e&UmedgS3o3^k 'Xpywnɒu:'΍Y^7M0it(rqӞWsxnz|WG~}O#HP$Io<0s-Xi#/#?S%gĽ[Q?((DÀ!>'jʞ$[*Os=I=eIBjһCMxf@L}_q7|+io;E%58T Qctw_GҰMQؙ?¶l??mR/趗q!l)*zq>²atZЖ>2"|7yj߫ۼ:rF2yq;~uyxI?O_~φ಻Vszw#JY윓sZSZM=ۛФ"c8YҢ?*$k#}{^M RKyKHa{;I/Y7F(r5Opx5r^ѿdxB $. O<2c@1m0ktº2ywN\ C`cG=hn汨~f/}ƒ{ :S-[OakPcHPqaig4h:` d@wZ2)rF~uѵpgok!PK+SӨjuFi/~y8?~Q9PG㟉c f;<2< Xw #6;Z$||(Մ ҀI3axGzN4/?4HK+Xɴ8=d_Mo崺b/vʟֳ1Ii@'ncʥvq%zeI:N->u3O$o>SKShdaXc7 4 ?߉N߈+m(a'_Ab3OfdVsQZh7[g\1Qݟҵ"Xv0E}Ixgnl~_Y@A//WJy}.Ķ+1߇g;Iͻ&Ɖ$8'/z ~3DZnegu`#GO#^JnN*\[?)5J_ckmx" =~t>kk2}=k2;dQ < O#&A'/viC>'_V h*oGa|sWVS53JNȧDI'vp^FcYʂA׭jJ'= SiYK]+n KtɬN5B{rJ=7Ʒ:!)%"8:{=_:=UI,# a(Pes]Jv=n(,DZ4y J:Ί:qrϹsYhf/y֡so2EKs&]qbcͮ:40!Poy |c6zPџC&lCc]>_ mżb|oHΒ_歾-6)ʡĜ2j_Z^| 76sc? ;0{|fK?GkcR{ɞDʏixK_G_vt#!)Zi5gRBFl &#arK5>l'-_c+MoW_I u!dq׭Txdʺ*|5_e<}OkxCG-oc+,dմ]RFcPӧ]ZJfl\m'P';Hԭ5(mY]n;TQ05(S[kW2фjNI>^⤹}#QQ[g6 qGr>;CVi ,D'ݥAim~ ϡ>?[Fk=J8Lɀ/&>YJ8)>Q֝8; VUƤ;? /&$N߅Y_(O?I rȑfbk3O ǥu> gTe І+] 4x]I|GbKzVj~.2U Kmȗ8=yU=\K.&O8;A# j%V0ѽKgO4S.'k)6±`:j_ DdR-ېzW~Esx`YAϾe D2V?GbMK<@ s~?-n&.'2B$=ky Q#tUr5_ P֡=؊E 82wݏRx gxWŚXAOʡ_jnh >"~*G7-ՂQ dR[~/ßS^<] ]n[XcybIeMfb_hpf OѾYtuVI"1#ɰldgcڶ5?SLJ->BN9xPЂQ_uXŷ̐1gv9R1{ך|M_kZW5o Vđ2Ken>C+wgW[{)U/~N.zׇ|*XxK?/֡kk'ǯQG^>ڏl.h_}qiqwI%Vie3n38L6,wd)O>oo^9nDÆ5!lnKIN>h5xmJ:А%Su"u5- S[G F>Kʭ:iE6#z#z4?Y.`zk?­ +Ai*IYGRC_e &8WRާtFnae$תF+ta >WkQ-ҤԓcihSǽgx{3⹧Iz,%~vKވeGTIvUW 3.Tu@Oh6k'p0OrP*JT|(mboqȐaMzEú^\}J-b"F0ʰ8 @ŚڽΡvчQqҿ|?g{wA!%ƘIȵG*s̾"~>*|GR1B׮NIw{F%CiK)[udUPF~"ƽz"]TSzV_FOYmWzTF/;jy> 'PLr,cWk7ohUY:=t po8WGmwO`3_uR)t;͊08$MdCB6BJߛߌaL/G/YTQ~5rZ&ĴJI?yo&q ) 0c$089xCVz3kî7Wgt-[V6%uTԣnd[pqܐ6W>UaUa9xDdoգ*k;9ckC'V!*a_`iXBE]Dd&2 bsҁ\@]@chY%H8$~UxG?i*u;[-jz]ŚBI J$QGSЊtfi|_@Yj7Vt[LHw)29Uauψl4x-i7?dt$gE(!Gpjo|A<]|K6MwYZC&osBC 0=gHq%ʳ)ڻZ<;!]43><4urLkB ]y댂3^e?*W fx=J ~6x%)`#d7Sdc 4[-v1 :K8Nsҽy0NQo\ ^ ֩%xbYgU>\;' flr8|7/:yXݔ8X6&KHsGJQB(-&UC'񾧧m3Hu9Dq#݂zus_zz\kG>!xQEBbXaLX,ā' vzd2pj;~7xUԵ>sZphv ,3Vͯ,[kkH5 # x='v4&o.Y jz>Gg1$s⸛_i7ߖzzM+|Quŭމ1ȝFUr cQxs7+K+Ӗ2 n9}7YqZaãϨAjZъ[٪mu<mVOfBݬֵ=L(j%dRaҺhSYQYkmO`/ě;ƫ 'GKQW m@ nbŰy?LC%|sok٤Kpwc3 ~mwCүbQ_̟O.+(UcH^i%,If&"f*R=F2N_3['wo%u}=}UjΠ® $~n<+2Iwtdģgw[!]J1u\4P-\+Q=ͼW%: TBU&9fmDL 2iq> wL @ LNpWwI,\Xx|;O5612H &6{^J\ۙJK;KP'B4%;S$8[+ܕ-W:xU4X # ɶM† 0<WS}7Lܺ6ak=Cu*wBѡU)r3 x;Cuxuy4vʯDm'=꾣x:r^_?5%[KH5HK|I>llc<ׇOV.].o,ʥ 1J3tVku-6# . \d `_z_N$`}B|u8a OW JV80u26" HBXtp7TҼC4ڎnҋ}0@ gasu'ee^7;[˱XxM$KƸR5d\A SAO ypmcmiJ-w caڷ>+[.}LװI (=r2'>go9b|3Y9S+e.LgWͮj2ŦT2>+-`ek*NX^G^f⮏!x,A ͤj~IZ,m;$+z1z ^9khhvd#8r^M@95Gl7biK"3KAr4PChW+p< Q*v7@&r禺Y\W?=V%-F-8 ʱ tO?$A?}XuK$,Odea^46Xjj}m:$4Q_t+?cNMFO[il+ԀO]LuZTf )rL髪W0H:O_YI jwzl)c#]*qN0'~Rxη. _}XCJA~ƫ+Q*ndyl 5bLHD~in>xmG<{Wh_ PiWzۈf. HY|}͂Y <3cxNqscA壐<#'x#9 qxS+aGh `~@ \a@==:Fwuc,X,J*K_yi]k ]#7o,u_˫,vo=\0[W%'< c/xOG /6P72cz`sӮk\τO^^N$6V8Ak%O0 aF2WR_Xn+*>^;w?^ *Sm([[ ;%eqN&TDӮ?"kgsko($S"0~^4_͜ <)ϩ$*# tgR~$A+MݵQJ>:wQݧ}Tw)]펩m{bTF##9=Os/̯xsNvLhɯgյ Ԇɔ8$\?~~14@`_ ?c澂暩?yCx_3zcƏ-l"41 60@`^1ڂG6}gxb0"4yr>~cXo/D#^,U>O| dז6BM֒D*vdgf9 qzfZez*jI7_]~њ?JӞ-ux4x qO|#y-GHm( := 5h(䌔Y8h5: HĆI$rĂw@QXU=JJ}5uu)R(_]{}1\CvuńkL| ;JuQ_Jt9fӁ&B]H`ux牮"<Zl0]Fwi7+}K7W-"]ݼhI}~;푊g9*+hpRNn#ڗQԮ4I^b.o$_)]؂F0EI;\{JϔD#RJ/98=)|aVΫm0+Fe 1Gv 8ۜskmCl ,c8hw$]]"UW\[BW+*)ڜ}?|\GIjTh(-9N+r F@8S ]|o@8t˘U##kcQ1RwiYsT dpTQEIQEQEQEQEQEQEQEQEQEQEQEQEQEkr^. xό.{ x@ԥkmk_[Q#ct݈΋.G> ̖:Vm¯V6sBoC692_xGvi> ]+Km6gbQ|^d_dK>4fЍrmsev>uYR?-YH\zd{IlyG<u/*}*MSg2FʪCltԕ92F+^8/ Fе{m#Bo賤|FT(?,xPƏ,* -҂qBoC6922+4Ib\{|=sB&ӵ;ᇋ|An 6=dXbMo_x[p{`W|O[}|{BsZ<1] N+$G@@ r:? h czlJj n! VSIgR>NzF6ywv~elqB;:58.zqVk ~Zz~֯-Ebݳ@v/yJaF +h*0P)p'keoY+w9ubu(ݰqjni4h#zXMrbU,R ;G~ xX|ꚴzr'kѦ-΂Ju B\>&k ͋۬4FȯRXJMay61u ( 89KjMl$Q䐹U]a6&ĩ`+V۴Z8[^%,nNko$ROM%ᯎ`,mɄpJE܆4 /%Nsc?^9ޮ&xU#c\_|%uhM&@Ѕ}]LVEer~(}FV Ycg8Sү|^umúuz6Jlx[وP&w my#-A=kĚE\Cf|‡`2B/>¾+|Os$Ƨhgյ;Z򮤘]9ȫ_Ϥ^htnkR"MOק3QWEDGGuMR֮ol 6Zᾊ *eL!+k|EYiz*wyG⸎XU^P),2׀iAkrd#Yʻnާnh "{k &ULkȆ%fDF`?&Mm%nw_V4TyoxaB[]E,IqFy}5sܘ͔ۑ=|}]@Ҙ3Ќ׼xS7αlzb9,3Џ֡ASQڿoGWS9˻: ȉ#ʤ8'8}A4^⿍{M EoB]rEԉǎ 0 i8MsbV JJ|m.|u]R$Qa!Yp2~Q=]wsA wo9yiyG8'v_ĺޣ7aFOws)u;!Xey, c S_5:һIz\wn _ֵKnƷ_d'gZxHm6[<RF˜s)Ar- N 8D ,@{OmU$gYܺMj1i,"Kdr.C֑Xm,p+boYah*IGdiRI :5ω~. -"׭~- lcJ5UR@9%OQiia?实 1mC]8V.WoyJR\ZZڃUynmTG,2985WzI%ƙ! t 6Կ6Z{InDּ4RYTכh&-]*XF3[sv3+zЭOu_\5(ӍXGWvZm6>4E ndBc&1{֚Ȱku#=^W|XդIt${dx¾x!>Ve݋0JNCzk~{*jusU)^]Zg i_l|mhQXCi/yc$v? 뭵5(P7%FU9郸3{\j`ҒͰVh6s܏`} W:n~gƲ>U3OQEnq١I|>OQEfy'(4??E}ȣ$EPh"C<Q@١I|>OQEfy'(4??E}ȣ$EPh"C<Q@١I|>OQEfy'(4??E}ȣ$EPh"C<Q@١I|>OQEfy'(4??E}ȣ$EPh"C<Q@١I|>OQEfy'(4??E}ȣ$EPhr#jJ(j?Z֌t8iYð2GgftbIޣs^+__=kƨIa 1;I|IcؒZ- #Ó۩8t/'4 6D|ܼNK1OTUy*cq oOE">EmZ,f-ԓڊ+`Jj 9Pvw kxVa, F2 bpr3ҁms,V@Tڊ+X**Z#_ߘ3hΕk?xIBmgh/Hc;T6ۃ~ As c  #(}E[WԪSmk|G5;m-۲,2p 'Ԛ7| -5˄ˊih 드E] 5/cGHp4ԃXh?Դ-[Mi{Mm/%o›dj!v7DǣdxAd}dו럳u맸QYk"L,LH:hPդKFR/دI~jLg4$ぞ’oأ$kK#?,QEsRȈT WkO8seb=Gj'-JIDžU*E}:~xL]>mc'oL2?(RVKC/k5%C9ԭ/"[ʲcnCFF8</ hkXd.]_.5MT ۜ(EGc6ܝ8~|EγvR5Yio#a%UQ]g|}L r@}zET}sGFt^ 3iz?7 Sr6+_«yCŧj1(m$d!s(*Х^NUb1I~N﹧o|8K=Ccf !8#~D9 uJYUQ ;%oBZ#4M{ȖwѼD)zY_KՓ'?f_QO]7&5']o/xOԢԒ -\jL ʮQ 袪1QVHRfotoxx-18.01.1/images/replace-album-file.jpg0000644000175000017500000010003113222767271017252 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 239 547 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G+a;NW(wl-u׼xJPHОGs P5($V6p}ph&$ӳ5[ŵ[EBFG+ԣdΉ'M;{kK+$,AUK_Yu7Y>5ٯ`em(UTbOcl w?~}G+x@ 3m奄p_k6Ӊ%wyLx8\d\nA^>a8}VM/ᾯx4Z\T-Yuג2ס@wXT6@*φ$7$?%yU0T_K*AE._J߉Ƚ$dQ 0o<w:|KjOownבMb Kyl{;UO |;믄osw5}OQ%{$LP) Q}O)G/,\Ȗت q$p;\*񮢺qAFu!{k9")է_0oD8uXdY$6~X(-ZHؤuIbӂ3^&OZv߲xbmzm]Y`*G5|@/_um~ӭnobir/(+9 1Jg'?DVĽ/[S_Z \pfR<W-zKQ(aOcޏ{ӰDQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`5fԖx r}j֟kCNQ|=h%S\́#^Q:}yT| e 4s̓QYcYѰ`=jk$5Ksqa^>|PFfVVo7Q,c3'ъw6w:j[ x/)lGW| snԲ 4ZmvrD2~sK뗷tRX$ЩT> sY(׆udu{1 ԛ@37q^4K}( fYy$'Js oԣUw-h}.IU%R\M}vX w5c.wP,pMyh% /Y Yr}W;|WvG;|WvE\I'ֿB ; z? ; z,Oi>oA]#oA]#f?=~}hIЯw7w70GO~ùwmùwmY_Z>}k++oG+oG.~z__o_=o_=as'֏Z s|7.s|7. p vOW_[_Ⱥxdk00 )l* ; z? ; z, >(~7xBnijAgS8.\ZZDqٟ~skMa|Q \Gھ7￿<s|7.s|7.I Y ?|GRluS /`ɇnNU#74ЎaK R :(xT͐;bs|7.s|7.I'5O!%Zj첺82hjީǚֹk7#i]2)[ H1*HBw;|WvG;|WvK.|=Z^&zt)t ڸ% b$k3¾3<| iR[If,Q˺xȑXa>;|WvG;|WvMŽ]|xVt-kE@ڍ$$u,H:~9IE⛴l~Bwv7CzW+oG+oGZ_גSMcƻ~ږ:rGUFcUPVOO~ùwmùwmm}_Z>}k++oG+oGf?=~}hIЯw7w70GO~ùwmùwmY_Z>}k++oG+oG.~z__o_=o_=as'֏Z s|7.s|7. i>}W;|WvG;|WvE\ԧ㺶p #tbZ_Vq_Ҿs|7.s|7. q+$;Q : }*VwN_vwoHKc'#=}oA]#oA]#f?>n/XP0 ?!WM៉7:6WmbVD^Wo_=o_=qtutzY~e*l,^^i xw^'-KgbZFAng=Z s|7.?NOǮ z>F6Fxv#0e'Y.5ZMY]]|ki+Eo&ztfd)܁ƒܝ3]٢uXDDZVes$fA?QO@G6:DYZëCSĚ&`lzgNnax3"6;j;>8d𧈗o[E[n#25aR( <?@!x ?Rx7ڃXh ΝPfY# XI f/%݃ڗ1܈tMZ^wO@_8{w~WtoI]9%=-l6ܹ,q]V@^hnh:Nխҡ bq.2dI6/_̷=?\w돶ۧ]ZsǤun5ehv^#-n#-eFkѿj ohuum"5M Tc3 w+ǫy'~kCXUN-f!6WO:2"Kz^WEj+>o+"[h#$o2|+!P1^f:tÆ#2WL?Zq˨YY0qӍft|K'hzͭѤX截 =p$_W\yw-DŽMidڴz*kGdҡ2d" <XoxV-zi%ixBȧP\.&k?EŶǡ\80mi=;TΑkZ֢oh6pl\ Bm\ +ǮiYlDω|Siq7Igl<@$Ymj֠|?A!x ?izgGqN}oe`qY-kykn9\[(4nSQE5vp=?=?\wk|859Y;[u+,q u9~'x>iz?Xxk[O(d*0$RR 2W3<?@!x ?wuIiofV*вTbIs|?;Zҵk}ዿƉBd7J~a[]GO@G6:s5dףxudhl7 r3!+'9W¾Կlk7G+Kt^zeqo]ֵK׌,aXh [4iQL ;7 .fu;=B-Z|8.4Ȏ(nݞpq^ R//~e{2߳}݌ϥCmEwqb-M,Z$~s2.Ѐǟ  l si),u3@mʫ鎿Ce[?_j_6_5-o~ͺn.|9Gu wPI2| D##঻7vI{i뤟yk(a?CbHckԿ_j_6_5Ce[;O}h/1p=A+w]м[xZiTGOk8aid%ݤfc8tgϗ^?Im~ ~ ͨmC4eNq5<>vw2[I=Efv&U!0 tRZh-Wu>sֿeu<K25.CI7O(IA$YT;KR+W;SvN /6MEo }0khכb,x7?gmsmuƭuḄZAleG,~]!<l<>") OhFN`)sgz{ZE'|?_ZuSzVQHZbСWcYq!\Q$Zi~4ۘt2G3i+YຉR"~`A> R//д>#:]ׅԯJuk"Tsl^9?BCƅEږkO5Hy.OFҠҽ;Ce[?_j_6_5VOC4O6z6xTy&\K9 a381n Qr8Wxj Vv~hnH ou>lm `&9Wڗ o}n>y~^x__xj/sjdS/ѿ>c|.09=^|")IIiDh *(gjJNz+Kt¾Կlk7N|++X_յ)=c[+yRKٵ m<ʪa@z֤0((((((((((((((յkt:,XeyGq8?~*5 T~*5 /_T=_ a#ƨW?dOEAX?U@k5@Pe2?jGPT~*5 /_T=_ a#ƨW?dOEAX?U@k5@Pe2?jGPT~*5 /_T=_ a#ƨW?dOEAX?U@k5@Pe2?jGPT~*5 /_T=_ a#ƨW?dOEAX?U@k5@Pe2?jGPT~*5 /_T=_ a#ƨW?dOEAX?U@k5@Pe2?jGPT~*5 /_T=_ a#ƨW?dOEAX4T4n W24%]ub9gƝcRy?`05(?[u aƨRXfIW^ɡx1ڥkbu+})3 @nƺ?gxEZ滩f [jz9FE. `m~μ@MCl%S(GĄy^Wڍ][ G%iQ[igmZՎuZ~rP#v / ڛy47W;Otm.,LFA f}C:?ŏ-u;-NMK F-*-6xHH:;ym37 Mm gN&iqޛʍl +GdEVmw{QS*lP˯ɤko,"fw<# 8m-Kߖ-mg%4|>nl^}Aoy!YUPɅBrs*LjlkkM.T߁uizfm R{xR)3E 3A!H89󶿰m埌K[+뻿 Eu2Gu{qtbA U!)s>JZ=k~/o4kscoecħl" &ƨuZOMIsy~_Gk߷ׄE%=wφZ8\lt?Ai(4wq:dbyZEdH xCi5wg/5'-m-#9?; j;ٯ&o%˳?ķ7F009~ʳh^3Ѯ%񌷞 A{}+GNTx/nDs3q;"R mGx|[?{ iJ;z|_Ꮏ7Ɵ>,_힅-U,&PŖPg$%?uxGvYIVoA (^$9$c9/ |2_ɵVPG`- _&O0;ÿ 4xj[CjgN^fH|r qRz_K~)oѺ7ǯkcO/eHwH[^3_otφ{jv>;o,nm-9-$HĪ7(? 5?:k(z|o'Z ;Ke5;3YOme(:dV%ZcIBtۻ$_73i|E Awإ㧆QA0תbh7%atbQ㟏OOs0hB=!l" {pQ!|Hj?ٻG/Z -KyBʷ;] 08ݑ>>o? ;?4t巖kK{hp"/*6 ߽7_M[uZ˥MK޹em|,n^K4rȓ,!c 1> |g ⴟWueMD1d_ހ9 ^!G|?-Oխ^_FLj<$_8;+.Q蟲o|Mϋ&NaṞ[&oKK[ݨQEDŽj{xv"|'W?u▱oZf x IҞxJ$e 9 sާG xgͯXjƳs{/X5XcqC wMwĺ~4^ y d,\N06\9Ux:=ޒEݮ oxmKu `9-l7oRO/_?z-~ZB\_&fdi!C TN d{ &/Idyqm6,H|2=LuyukU_ϸc\,*XdUsetki~DWl: 1X*GW=?~,~6w$FVH.HQ> 듍x$RgnOORM]'($Nz XFG5k_=cE׈E{ո:Rjgڊvr&*I?m\.4Bm<1Sؗ r妬;<$wk.>tx0C{SCvWOR0(((">_Lwt#t5k_?K7%uU]Ie5ơci%'*YsL 8΁Y.g@,Vn5"[kVT2껙ɶIM5{/l|}΂Hn-Ɵ$r)؂k: f?qA]__uC~?U_:h 8΁Y.g@,WW߫Eo^3i59'ӚT(E8΁Y.g@,V->h MuyN-#QD[GaH_ѩv`$RN(qA]8΁Y.¯_VH QK7P2I?@ %tt7PkBSDoEDl,V{{U_:h 8΁Y.g@,WW߫E}Z5?3oK_?K7%U_:j ck0Kr8#iҶ mPmcN`h 8΁Y.g@,WW߫EZh&?tT6-w5?g@,G?3oKuՇ";ækZ1d󎜛6mdR8΁Y.g@,Q{W& Ϸ+]<_ VĀEԇNXNm#Zz?3oK_?K7%~ah}ś+UZ[[.9c 2ۂj^ &j[[eI4Ғ@1T>ey8΁Y.g@,L3hK_K,iϷ_Nmͧ5yRhWOtafog@,G?3oK E=B <)]65 |mLc8A6|,ϊzF[\iQH Vܜ˓=h 8΁Y.g@,Kym:.k{vHb:Sm**uaȴjg@,G?3oK|36w񾼈3G`?fgZ !|J+-QFC %tu}Z?W߫EP(k: f?qA]__uC~?U_:h 8΁Y.g@,RjDN V_::42T:, Ne d}65Iϵg@,G?3oK2^e# #hK7~ ύlP"ttk: f互k!jؐ ;9$cNԼ=58/qsh `$ƍ@_?K7% % %Ete=G5/*uaȴjg@,T.<9%M>[1ÅYDbBǀӭ>Hׯ{K>wKNJKeR(xRKҖH[d[Z%<" 3g+d[yw Z]f`?@súK[Wn B#*G*о$Wq=UlyWhxŨnvYѭ3-f<[0(~`Y~nE{ YG`褎rϾ a[ j.U'{yL!SR{yV_t1~ |OgIh&ӋGmi徭u Y!2\PDX7cExҿ޲<Ay}{_$p3_\;#+1 8ֿL֕N_f/ZiZwt+A(!(d0%n #@ ~ x#\s_vOisw%Qn7c(1}ќ֩..?O{m־!k|7+ &skbGt߻KxnF0*UeX+>ŞoxG_nou=O>䦥`&0h̎XbCyaxM3jKl#)Vykct}7ޑhȍ1`Y4o_4z_֟O <=4<<=ؼscmpo)罚twIXhV C G+z?[ >x[ēsjzDwɤjImŢ+ .m̒t5^eEY V0nid'eu'YI$דOv[Oi}$hx.am}BŤ#LM+ðAA#@B_exUִ O_Y*_X0y>V >_\}I&\O /i4-=eN>s&@<R޻v+o;0xGZU^m|&YIuh֒\{]3K0`E2 |ۏJߴ/3~y?xGY qf!f<;j-}iewzwj 7Oq$1)0ӿgZ^xKO:NewYZL,p\D:Q"_j%g>_i{^Kht[+)me>j1bli Hj^'ZxsrVuC{hni*C~&eX(</5[_z75۽=Nf'`%6*y߱,06^׬|C i:vK(a22e^YY2JZ}W ;?kkߌ χ'T\3^Z FzX%EICO,ybD!OxM  \gXHkWKhYɻʒ-ԓ$ _ 4c56g;](o g$qZ)'x?U^"l(RDLcA]Ե_R7k̟!|b\?`Oi ݡن*ZK9LWȓc&?};_ xƺM_^U!5_ks붺Jg{HPsIZFTQg#_ⓨxv kmgg?m蠑3sYSj/PUq,&\ e @lr(oÇK>oC^|Nƙ>.7N-y&y$>bE &4XJ6IgV?Cy>ZjwzeiWyuO4N1"2nB=VM;&ITX/l.%Fʑ鯾CQ=f,Dž & *%1$[BD}֚o?%oAIs&kw_?Ï xUº׌mx|Ŕ/yidvFlcb6 yōHּAuh---x^BI$WWim-d}];@?[]?QlNm(t/rWKv_  Caxv]Դm+MӬ-FKn,vh%f4ޭ| ^f.E1-PUH%=ӎ~Q_Z= ǟW5u3Is $Pk!<2X6@OKφn|Q<56ԺNkI<-<F=^iw6WեVlݲ"^1k><?zf $PKwm_Coڧljwm߄QhN.fGN9fH4k sW|EY⟈<t xk%b-n ؖ%hI#oܺW -}-𦑮izNoea\OwC.bWrI$YV?hZikm?V`V qDqڌTq-li3_ɫ~[_BѼ-6o jv"n5neikP;טxS?| !;}wI%fbK{9m,%\L$ZíS0xL(&T*1= (<85bP9i4>JKҋX%ʒ1OEi;n&7"#ӛ^}O> ^GNvmu=J}G>dP|8~+|Pwu-\. FDKy0m9 * ?"kMx^5ya<%cpx` 1:{it| [cQx.kM:@ȷ[Iyr%b UG7,"O ⮵w׬uf<24˘44˫e\v,Wa`#C|Iu%l|?,6x{,m-%6] ry=kc{Br7G$&]O$ߎ7c>Rwi]n (~уo71i^K"= a?,c rf<֢ X\[Jt:ۑ21%|g5ӿgoY}mA ʐUee_|'kO1kC JLpE:Фck y➑>k蚆{g-۶0,рێC㜃!/D9!wZ}Jai{O ͥ[]Ҽ3K"3?PWvu {~i~==|2ȶf]@ZOXUv @z3VuǏEGu RQ繲mk*;o$I68R)|?5kxtCLhfB"ʁXUK  NsSּq%? Կu`@vڞr_ +R5OЁouRLvy 䪐I@I5k[R,kfH a!U# kk7Z妡M#Z-ʰv[.>./K7mN% +;#1[$m4I??ʹ)+/=i_G4jZ6ei@& , /0䩮6?ڟSKz^o $(K'5~!q+ӴτKmo|. iu)䳷Md[{Mj :*(ӮJ [9f7>_p;O}V7w53-6B-N,Okz [Ym;\*a={ói+?C?sThhE* F 9K&& 55ԧ P=)l_PZß|Ok:uػY+ByVO𻔄@08S^bi;->t$$RR-m(((oZ{/r20t`TezEd/e=ocq(^zV{/9ZPG"_7rE n?k@[?1㕯Ed/e=ocq(^zV{/9ZPG"_7rE n?k@[?1㕯Ed/e=ocq(^zV{/9ZPG"_7rE n?k@[?1㕯Ed/e=ocq(^zV{/9ZPG"_7rE n?k@[?1㕯Ed/e=ocq(^zV{/9ZPG"_7rE n?k@[?1㕯Ed/e=ocq(^zV{/9ZPmoV1< DOs,U{dgn B[kh%Rt`zC/'G '[P 'G '[P 'G '[P]7L-K_RrOMZ*G,48#\Uok,7es=UXdzx`ͮόfm ^f. + 2:VR=C? K m?7o r;yS۵zzc9|KjuOE_jw=eY%NosD*UݏR1 +DnqddW~ׁgIC;D#7 M5iH&xg\tpxL kۈ.M֠`2 &Ä۵`I5oiEO}>$oosփt,|?cc ]oij., $eO[[[!&sZ+x(sφs6i<1uz|{UfD-!ql>tcx52~#ĺ}msn- d ZX"`R}J~>2"f;xҡxNp̠sW4 rs>.Aq,:$ m>ZA TUV2@!H/kCG^ՇFOu;&Ar?.lGaW|WK_xkR/c[=>-!QWh߳?97nRNE?Oo>c;BO[{B^j&,~ 2kwLc7a.qFp@ 'vNvY"ewxԾѱ~ mݻ;3c렯> sqO~xb[Ѯ4f.ry:or&j|6 *J ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( dGs ,k,N 8ʰ=AP/_pd'\f ckUTQ Ŷvg n_YC\\BAƞYm #y(((((zcgZyk'ފd 5rtυ~mo%5 ٮE!t QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWTGw5$ܦeb֕sA7G]/?iQ@w? teb֕sA7G]/?iQ@w? teb֕sA7G]/?iQ@w? teb֕sA7G]/?iQ@w? teb֕sA7G]/?iQ@w? teb֕sA7G]/?iQ@w? teb֕sA7G]/?iQ@w? teb֕sA7G]/?iQ@w? teb֕sA7G]/?iQ@w? teb֕sA7G]/?iQ@w? teb֕sA7G]/?iQ@w? teb֕sA7G]/?Wu:.[[_,zp}k>O(KZFϯ\#ahc.n컟 ^o" Fhe^=jONڧ.0-ebsA7U?ӿ綩;G"zw?qwh]/?w? uS=;{j8㵕fZUܷz|r>=,3e=˹.nEI"йG-#&5}hZb+j7.3- 5?1{|A˹I}.TWKMцC.vA=O]`[˹.n'mSvzM/4Z0 Oҹm4[z u}^+zDA0HzQ?fX>?uHNv9O(Z7U<(DDPH0+G[H= Fnm.1 ✟4W,R (((((((((((((((((((((((FFȊ* X'I$kG$64Q@ ¿,G+ г_MP ¿,G+ г_MP ¿,Sx+m\Zh:eYƎB"(RPmx@ b¼ :?EE<+BΏ4¼ :?EE<+BΏ4¼ :?EEhi^t&vt-=aD[ ֍Pfotoxx-18.01.1/images/alien-colors2.jpg0000644000175000017500000005462013222767271016311 0ustar micomicoJFIFHHjExifMM*V^(if%8HH0230x0100Fotoxx:zonal-flatten|cartoon|retouch_combo|tonemap|tonemap|paint_clone| Fotoxx:alien_colors| Fotoxx:trim_rotate|resize|NE6Photoshop 3.08BIM art, fotoxx,  http://ns.adobe.com/xap/1.0/ 8 1280 1920 2 2 5 XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmC     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?_: 'FIkmQYYd/;&vD/82`mo-S=;347H'93JWBFRx>"Z=ygqb"c$F4<OW^n0KZt*s|# ZNAk !J9]>PTlExoItY'&&yp$+Y؅bMe˖qdzѩW9ɸFomntXP1gWw i%ܼd^i-\[?9W'/uVBW1Ǵ~k]I54%ƩHRi݈8I➚^h\I1|"y77Jl d -cr@I?z IisQGSc265I9#ڹGV? mV }5MK-W8BI?3Mi|_𖉦1y{k*Hn '$"? |Gm|!wṢt%3Zqg, 0Rr;s}uJ!ȭva\ө|">tZ2-đv:WśjiPi Ӓh^XqorF#hz/<ҺJL-,{]'Cwxآٲ"A!Ebq=lq}gLWً.O/ }CI,8XH.;ݜrә{k{?_%^u[9k%wcq RPv~Q:W ) d琪r\>n;Ft*su/UkֹCݵ3نs=?Ӓ[AϦÒY p,,*?wiD8o} N> ;Xo]x1X J#їX,.ıbęAzm9Ku^N-u=b] 7Ztѥ$)K*yL71eه NZihiŞCgy$|(|ԟJ%xx Bp{)5װnE.ԙN38hjl\ʭ5XZ8vWg'>O,eTVvX##.s9b|S&XiVAf~ )끔D~>osE3mi 3 WrUR_ƽVG&xXR+;PG~wTV_.mh-#7w*'"6 rI/I⽥:|cMwm ,xBU$4FO$?:VnH`h@8csu5~!lLbkdd"Y(F@zyXgRIk;}LJ>",y6G1XBYA<ïW;ؙ˦W/À\gw!XpWtZV}GImx$ai)#/ 7O2> u;~_w԰GkPa;n ^⿅%sh6ZMe+1E~\ ;c`'HBCjs׮yX@_Zk[+}%BjvwBXePJ6)cWjZ牙ZP-UClj=z.\7_V#t]_v~g|;^oSiAt?9Gӆn e~_U׍FEGK,Uq}< -l5}B{v^fIsϸ=k~4K6|0\Ŷ _]?PYL3mئuې2C/~_#]O<5hpt8Åq fOҬMa(ª biV2{%Qu<A;j Y6)pZ-Cn1Im|&֗*S?`kVs$w7-Q~+/|IÚM|_2E.mA99W=Ε\zFYݣs' <^iݙ\$lc-V!9"D&Ֆ> g%⵼yXHTӣHc!cAUy֍MOx-fh◉"}ǡ٧^Pў$G~(| ް^Es>(yH{ '( 2QJY[-Uk="ŭ2c=kϡoZ|]c$f#9rG]'M<[[X2Ԟy q9޶mWMDyn r}gX Nɻ|ghpp#p>rwg,Oc~/>/S:ha>&پIVP>}inpXp Ox &ʰqvGV\OTYT%QMzҶJx_#t:xkʪ{$qSvGJN[{cUێkWJL;wc%c5>"jSj8EN/txwU?ަF O6NT1$4v3p{ת|#֡J[8BAltX]$0%/i-.ºuQGuKeUs2>ZλЬhv7jVbO<5-3NԴPĪ~9r;`@<o¾މHFi-, g990=O/܃i>S:)Vݬ{{cOq\h b5KfP OdOrIWwc>4(vgQi" $~\wi_aA,PQ;S #,*UH#FGZexX8*={mWp01dvn`E1Hb0GZo[gQɈD۞M*Oҽǿmk4/oczim*w ;>~^5Ƈir"2[4 Q=uTzSוa[]>G w׊:MBC֌rv@G؛[ƚn[>MLypWT'I D>xm[Aca%(U@30SCo=N61Vq]{|A s[ n̉eT5$g|z<R-"ApD5UG0kwFr+|w\Zjw IWjش$z?0P~v >4Osڽu+ZΧg3yj-k6DбOGEiڴ ?85F]YK_ʽ;9AJ楮?SݤŐVAcPPy8?*f6+l)j3,iP՝ۺA`rr[H͍b}kGMGitD:kLjstxY >9_mc/{(v dms FrEe\_Z̦eS[z=sFS ,8BtFG֞_6j6uPVφe:ͻXFktc3xAJEK}3QT8G=/0sV{j.q'͖6KFl/<eqKji Hķqʂ3X~/φ+dO ^]/cJ ҐBOه}ft#R2|l}aW?kxIoǙg% Sk'QqZKv+luMZ!~'ҔyNc |>1>>Zt&7DYNG}ٶ&L:_*T)UK/'M76V?  .}y9bxH QxBGҦlne[F\3=+[sE m@4'vyчVG//Sx|bl?z7X澓kNq_kO@> mSJ3EXžvgODd~LIID*kė8k֛#Zq,1& ]_z(q[-NDח*(C,(\DgԵ}: $ī,c'{L!-mg#?3~^]\L]JUp;s5Q.Y}&k 1L<1J[~Kg 1{>dURA|Ѩ[ie%"ce8@s6fu"~aO݁_?%W%guJ%.?S:^E%ٶ!9nx&/x—7:W6xx.RG:<iyzϖ;%̋' o$wu F#{i,0k^5#QGwxLFKxTrWDX.~u'O+ enN|ėhؕsD2n|˜gp\x5[,0(}n>7Ф]՘aeїLw{; [jΰB$C}}+4Ohk%OF>>Y'#JZ4`Yy\[ic~oXo+gq}wok.閬lq0+b@'n=:GZ/$X˸'4/5^4#TV;d!h% ƝjއZ4n g~#ԢIVJaR,Zy<Ǐǖ0T7n/c_'|{֣-|?6MIVyIDGǩ$L_+Ƨz>71i⥬>vؾ!<C4?DM.9?|i-#|w@/9cY2 7 { H#-طI.-m"iϦڻ ?֧{lxWY 4#z4CRO#`hh#l>a$ZkŽ5ymX.eI[lkLO*=Hp^2~mƿ3죽m7LO&4kV,Q! $F ;OW]|~-ľ hHo$MQ4(CfۍɌ9T`%N/.Nfa$KOE$],J1 ~WSc?ihfנh.@/lcP)*.?د}k^|=\{i}i<9cu : ς:6(V9-,3q_?a}aׄ}ĬSX Uz^>,=kҾ\{K[e/pFyRQB&I)d?W5  }HS%FE4]7RẒcsDZ?kZ"U篭rR]@-$t?,*^I݂k̨3rU&ubxD[ipDfb*6yM9e| {^՗B;nzq[]r+1TlSD7:M8\JloJo]Ŷc ~e'?Z޽nye_r jV6ȑ#\zќ{rR$M3"FU߇Hkm08$H$(-zk]{SV0A ƾQ?MӬHfQH1=IEeؔC˞1#LmwQ6$p±8# w:jkz$|[rUx?3g49S^ݕFrPFqx|?vk<ۮi€[9]}Wzu'nS5 # 6SRpxӅH]G*M bDT״^Py{);0T Ԝu!WxC/ Fi^2 /mƭ^OzG6W'#RBI%q%}2T[3#UM7W'5JvkeSxv~ώb Sֶ mi#eC 2|Y^ԥ<]IzfشWŴ7*ǂv`d}c$gtV{;{Kg6٢QjXFDR6ꯈ>Ed&y?tWHY`ol*Ǧq__,=Ohݿ<<0e wNZ_\톁k tg'w-c,e̱R*[z|ĕb!.՚9Y&H-x*a9??\wڜMx^jOKZ_x"RFt6g[A 8]ƛsy*QR4V6W+q#G9?2xZԴW=.0؈]~]ϓwoD׮,=ơ#ZY2/'18?Y|]:= s!ԉIU'R02x8֚h) A%08ƚMώ]ns}wa_QFMF*'LUOq7<=?/l/u%Ni@"Qs~{7}BgV4lUYt`O)|'y}(iROhc\fpYv%@rI)~9S=}Fc,Q[oHPWi#Z{/P7yyjDI 6ú v`uY4oZn^4Y[p1Y{_:E_cOK"8V3<T)~jk⣪:ZFq Es sK,C2Q;d'ңZq< |-;hσ~[}SMl&S[.w )NsB8]eO|Z|o}!/EmH%?gXDs’B e5KMc@OePbeF yz_|qK e𷅼axOFd}JkS)2++@8A^)[揍QnU"ו?<ž׼/QԴF,(tudtdlC ~~ÿ/|u62XHOrX,RVLWڇ_^/[\y-[oo""3EyBHY[Ǿ3/M{-Q-S}2S`U4R>S͖7U;1_=a^}v5yiW_<_:{[۫!I4Ma9[7;u\p㝲H:+_ 5[aoHw<ZkQHzWaTޑpk ?<TuAr= rFq\Nn5y`Y]6`?S=ij1(X@!baΉq%VR_Fq˞t"ݽL6L̏ۑطzVEH3U_\]+]B#9pT}kU`U@dgN-4_-E$*I(]۝ϡm]p2zp:Ǡ^+FUn|>+*u%d`|F|Gq*^Aq EXIe0#"`<ǡPmuMBʹll.i3Bı >{ULƶ sӺ+L |rzk^NGRJ4s)4]2T]I܇QX3&=74mb˴䝣Z5>R]Z/Z.^L/d;JyH|ީx$F2BW'G3)LU;?3?*P}^'ӧhL5:CMaw2~xھ9<a$c_6 E`YHX⽌p_>'N41U!Mhv6l ļf#'z̻3Weg_Qdgk30WGJ% 5ml̏fotoxx-18.01.1/images/smart-erase.jpg0000644000175000017500000004110613222767271016056 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02310100Fotoxx:resize|sharpen| 5http://ns.adobe.com/xap/1.0/ 195 314 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}XjCSc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Yvq Mw/p̙`gxgR׼ aZ[J.ޛ&lW-ҷ{ ?DQ 'i=#?}'?½_F#^>Xj:..wOx"$r.6#H|1ؾx[]x7Qi76$2Ewm`a1NvIMZ1'?DWxW4;-25{đ K^vr Tea:gg@վ MoZj7GR0#T;qg8Y^~6%aOl>|𭏉? KMš:ޕ}fdUya:kWJԦl}'?DV?ڽW;aOl>|{z,FE HjGAϠdipM͍…ԩ;UpYMFPm$ECՉWt<`Y]seX {qs$DQcTQWUWGiyO*KOO"|Ici4rSiE +5mpx;Yb>ݼCGiKmm<;AsRY!o Rw:19Ȩ_no@%iA5vO5P1(~E{OfBMrxfPZa ^ڻi-MĶ0p#k5.\gA)'񯢾.^ŬcDեJ&>nVtp7`d5wu-c(Xt#7^Uxv~L'>}k/E/EN~}}_/1x-/1x-as'֏Zq|=?m(q|=?m( _i>}/; ^)o1G; ^)o1E\I'ֿA_bO[?_bO[,Oi> {C"Q{C"Qf??u)nO.x\I Zc~?ӿ-nv$nGqKn5W; ^)o1G; ^)o1E\Ti5EкX.،rWZ=F[MuUK.i2Kpyo" ePxZ+/E/E]_כ>~#mƟ_jPU"(Ls,"7zv#YD<660n&s$.^YqvWܿ/1x-/1x-;6/uoR^M%:B9X’2+^<.9WUX-8cBRz//E/E.|1: R=G_KԈB%0lHԞՃ_/1x-/1x-%l s'֏Zq|=?m(q|=?m)مϯZ>}k/E/E.|w:=\[mH8>dZ>?"Lc̕ W/1x-/1x-as mgxe+q!=s뜜jlo;Y*$~; ^)o1G; ^)o1E\T{K(qUvz7ŋ+X- 쑦Ÿf}H_høøËFom[.ͱyUGS >[?>'.O4>limit97çbֿB?_bO[Ow Ao1[MRɊ֕|DgQEuAEPEPEPs\5jvjdw2%1sX_ $<.|A KkHf(6|P' uDpExomjۇ<;ikNsRE3G2Qno/ 藯'~^nʹ-[^Z +ˆ`mx#Ɲ-GYuO ZAvURU7G2H8 hWmLiexnHҮo{ G nN=p aC@>.g>@#J;3482$(yA˟A.:yE'm|AO[n|ˏQ4MYJc<?*zn6] y,vnZHbT,x!ِZxGf/|;z%Jak}lmgdVRJYi~IOB>.aK~@܏Έ YhIUC$SБ^=c^5Xu \NkDdmAmUXP0%ևÏ /|)}x:xtm.d>H TGXWٿ_ﭿ}-B$s5 XLaCP=7zg޾>'SJ xvJk==-0j ں+$pG6dU;Rqo_Yx B@XLL"bNIbFUp0۵LIcI5[( H%iT+c񏋬<MK:~\MPu >8:O|Z.k0^ vE9IO1T ]=_߇ҵ _Vìiڝw`#B"dwC\ZDMrQqf%(C`gVnಈq4pD:Gkozᶷyil:~NZAk2`a%gI<=@m>Kxjs ][MuXlUy4n[Llg7+n47RSoVpG]Ar}i9>(wVM--Ɉ[$RGD_l:o.4#W@ȒF4?LC0iVy`*̻p:/x366WOM5$ }u;M6hf;.f+~`^ _0VڋmI!sĵy㻿 DڥZ1H̩1:sgm6z{"?熖I!HX.DS/mO^$ѬcIZGͩ^ HaxHr) LВݿtTv x8I.FpA}J((((((((cG~B_Kh#qƹ e  exe߈Ux[]~#m`{oS[Ω"HgԂ; {]7]x?F\ )"2Ws#Em323^oQ𥾯7$;_s5e # ZcēN:R{OF; i^ ԼC6 ƫ IA.cqD÷Qot:'<=Lj#n£<HU~|=BJ.vՖVd5Ķ!Td# nqPKqR8pu-HЮM1Eg-Kυ><״k6^ӭu{dNq'X]Eғ0 ʌnnI/M?6|% x{ xH.}Fv%]ureOqrVvܑCi!#y }9ڰosI-Zzї&[F5 $X(8$8E߆=5{I3Z\Y\2[A34lTvl2 k}(! _$xQZ'C,\l88 Ğ}Ww\\jWKe`ucLs,ĒJZA,jx⧉~'3> Ŷoq>0,o ߷y|3  EU5;xuFU/-he,NPT9kĿ:5{f^f CG{co23dCyו kY4K[_ G̖5OkufCUX-1;/>+x'~14vaxơAuFɴpj}gO|;i.Lu Naf < ˞^iot6M6Kz_)3X|UmSw4*s~!ן[iw:dztq1aO 2iכ_ğO>m^ź>)~DRo[ m=:VO_ɧj~loWNU䀬B|;U,9/_NSgKl" :+[hU3YSm_ |M4=O:-ߊ572Gl^i9c,QUl;=O~D|UxayTQESҼw7>(K-#تO4EWϨ\XGXITX~͐ \qcˢǥZj:4ٳo7ur^O~在Q`;:)tZ?WaȴXQYZMΛ}ƧsGi>R"۵j$ԛNĚBj6k脡U)9jY\\CwL*n]W#j([zV_O5DT8߄5l:čSJgײibp-lI b \Ƶgq [M$.%I)IAf!I$օW<:v.w WӢ%NHQbq8 sw~>wm4mޗq$HąfT$A>[ySWaȵ_x[IQŚ%MV^[1FVzhQE ( ( ( ( ( k'^M~5C5nB dˌKWԵxSH5]cRXt rȂ4-BJMhտ ֟_n[o Rk mGYZk?!T"`K>)~6h,oh[rX}9\OTkrZ%O="X=@QqW'dr_C<=v5Nj,+vgX3*&XXXqzxcH_k[ jV_736ٶmT*|̶@mWuEGy5,vK6A>8բbFq1 .QF>c&w?QJge_&XG2\ 2Y(!9 _ǖ_%aCGUURxs^WRԵ>^10\>6'8'8ѥ)h>l8j{mP..#Pu).ԛ{Hغ J1Ut? xSe{{;]2K=8*qk$z`ҶZ;:mx~-_\(u;uJƲsP5G4iyo8bq,dMk|yNh5~%~?{#C\-OѤdG-OѤdLf;*ؿ Y:^ex}7^Dzx:6-`py~R hK+? x_UfuMWPn:v@CTnuoxo2C ^?&}&UI %NF0ɯ]g~$֯5}W.I\ګ.O`jjm.;#+-3B6)!q"R~s_4?o^Ѽ_U|;|]iem|'\[^'󬐰F`<[iweV^F^i* OUO|ckg'mmdQ)DOЕ#85.qAGm??M04ۏ ߈?n?7y~!hJ͸6w&4ۏ ߈fy.m+yX]}˷o Q;HҐVnw,S[E/rfP~\sGm??M04ۏ ߈?n?7y~!hJ͸Z΁o׊VF̑d7 (G Hw61FLdvn?7y~!h͸qAUv\/zV܉%U).ހ6A櫩 lB)t@-\# ʓqAGm??M04ۏ ߈?nG#Z'h!ㅠ **|ږit5rLU']M hHWg.(v@9㜎qAGm??M04ۏ ߈?n?7y~!hJ/, ; V+!m qb ?[W~ѐkڎ:ΙIm#DEuR0v{W< kicٵr|Kx{S.4KXUF;RH0uƺ,S FoRX Ę̈́ni??fqxi-SNSu8>s,HS0DL:[]࿅/xևMYY7op@ʌs`i?m^ o'WkFZ:4ZΡd./;@| t OZIx0hIɈ(V #@͜06Ҷm..5kғ40vKYWHK.|m_Q%ec+wCƾ,Ѵ)SUeIt+c !L'&uH|ac4{k2jqz$!<d-,t!L 4|#ѫ¾-^Ma=x{yo FT}>wp+ pFz u)Լ3i?k~'Y^% ,~P-h3>>߈ ,(sJHaSr2a(x|1ڬV>MJMKYkˊ GaSOB{&E"i MdNۀG&>fN|Gaikܽ5϶<t:pئMw"V?O'BMA PZ=-}ww)y1$6F,#̭.u6z]J̖A9?|//4颷LRkQl;U +4xLH۵-?J4&) ?L?sCĿ-ͧxWTuh fxJKiͫρu{Ik msOcnaLGThp.D| gMgG\!D}CrZ:_|DӴ6C5(煢vdH "\2$: VjW^(//4畭.mhBy*\~vռB~[9dwiUEGdH\s9};5+cFӭo-Vui;q5 O %a9?~.k>:-SìZ#RJ|f_ F?_v9S> ›R?Loz mK+57yY?Loz mK+o~|&򾞷m,+K{qv;=F?_v#W/;@."xPe==EqOoNć#"=tF?_v#W/;@) _ME|7=|A6Y<_ s|7=|A6ohys&~`5yY<_ ](!A1DA_~,h-xrK+$#|.GqZ57yY?Loz mK(3)/Hjeoh3)/Hφ篈?Կ"#W/;Gx{:OInne1Vf<95T SYTwhgTeb=QEQEQEQEQEQEQEQEQEQEQEQEQEQEV'o緛GgmF5j(Yr\+Vs^/8ռ$}5G+ e?uCF|5xF㟎9Xxt}<6o43 BׁX >!xXf.t &_>[FN/~5tI5Hˋko 귒Qy f-gʎ\;kDZsN#.u(u"FQHhs[FoOjJ ( ( ( ( ( ( ( ( ( ( ( ( ( ( lJ]xnNɩ1'Z*vOi{J*rAAphuCS #k3ZE"F@Qn~zǚׄ.X 4l:F5b92͖Yhw_rVZGI_ mqokLZլQ,㉢H1 kZԺ:5)xv;rG;FsYhw_rVZw{v'hXrUe ++DƝ/ax\oyfy] kVfotoxx-18.01.1/images/plugins.png0000644000175000017500000000161413222767271015320 0ustar micomicoPNG  IHDRJLSIDATx^LuwQn4bg$in*VUB*ݴ1t5-tHAPtvKAP@KvrǏgkw5/;}_ߏqK)ê/R=F\ze3䍷3R.~hæ Qp!GrŮ.*m?DN|୦6OXV12tMC,)iDY¨<{; >eLJ%H|0 ^ևؿRHugr5i!_pc\W3O}g{ ikз ڗù4h{<}P:]bBB~΁hЭd %Qř'= ږBBנ36nNƷXL<`x'\yCK2+d BFYX>l{ĮRpq i(I\-~uZ0ӻDFZ?I$mT 5Й?>dCLZ+̸0r|..ı#7LڽB@CoG\h0[ TXH>$K q˽Ѧ'9oo]A,Arc<359=R_o}.e>H[I@H Z%ǘlLGs-\F ;?b0pT+#J3'7d" 125 242 0 C     C   q" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? t{ h<>}XjCSc?}'?±Gڽl>|?z>EaOl>|{z,(aO]k;<<9o7*2 r(5~y7IaauI=uH61Y@>]ݿM]_31K2 j5Κ6*\bلd,Ī'nC:Vo:Oͪ3r!m|q rVO/}m?}'?»|=s y9%D4#.P|0}f5[x7 ֵ]RSK5Kc4W 7dx˭Vl>|?M}?K<#Mowɩh鍥 :j>PjƳ_tE͕8 }]GJe4B?i6u}Myb`Ew8Mh,Gԛ]ySBBc?UdM1PF]jd,ڛ/Z zzߥW𭵯'0Dv<+rP=S[uuxX<gܝ(9Yn-Eg@e=Nx]S ,@GP3U Co PKw:j!3G*3CW/;Ccie G>%Bz-{j"vgwj`8>ס,hsw5s0aX 8\ƾ{Vxnu(hnjή ?08;5ՀŬeknqkʱOՓOəsK+Om+ӳ<;i>Q_?m(pOo1E\I}pOo1G;2x[,O'W;2x[?>Qf>ߌ_Ou]K䵑R:d29XxW<;hViZKuG!wBH R康hgoxBZt;1o5FSTvY|QxI&]_I;{+x1QW8981_mø|C'(ů|J6GZ7bۻƛ\Ow?d?ø|C'm>, "cG[ Kx FS{lx$'.M4>dikiopzd\v=~n  'bw"1fIY#*sIϬhbI=< טiJNMSP%[^hW(eQr$[r@znz29ohHV{i5k ȧˊC3@nQT4-nĺ-X³# Qe`QPx{EּU<E%#(z+|kV&t^ ui %}F+fZF\LK&? xcM3_]٪]Y< EuJ?ѿUhO*PjExKfS+$H8$O@Wꟴt[N=>ۻuΘ$4 Z3w󷒸i.ݣo8?=O67\dWIi5 Gw3\Ckx9"]w0)%pBoWi-WSp&o5im:isB6=qqE'ʍ dj Y~|IԼkuoC:]ouTI} '+8ѧG>5Ե]i3>xTOi팚nGimO{6(mI<"^`}gKWumT J[9fD!BTF3'4=MT;;xY]Bm8SQI~jro;+|ף??^_6'Z1 na+6.AS3⭿ōNݦkڍ\\()*Hϰ)wky3/"<9|M񎯠DB. h>U0KWܬ>< ^#ڴwziI|["2JXKncFeC ~* HBʃSZ?n^ $OM^j#I%iRh%S+D·5lUDɭW/4 hS%̑NYYVuYu#8J+ L\ ӽ? m_g-aImG}'NjZWGRK^+'-FQ|H|!eixg2]Gp[ı%+"Y庯/<}xSG-1l[y9>Vpkc"} ~"I+a| E_֜=[m5ȝ5wg>Z ݟjE9[J~`wW+9mSCk-igI. q$_δiQP]*(l%U_TѢW|-~>vj4h?U;? 5Gϝ +;QgoƨF_Ty[ѢW|-~>vj4h?U;? 5Gϝ +;QgoƨF_Ty[ѢW|-~>vj4h?U;? 5Gϝ +;QgoƨF_Ty[Ѣ.Ҩ#r@1ίPEPEPEPEPWS[i : 2A UteC$2:oJnJt۱L^1»8u{OvXg h>4Iɔ}?0i"++ZgR֖$ψ|5v\/ Q8,%xȭ[V÷jvziֺIn<2p5k"(pHgnG7s4Iɔ /.ON"gw>?J/|n`G<7mɪbSzah&̠mzWWrZYEgxں\%yDUn"qZkn𵮱@hHٸ N}*3eIbH~3SM2@Z(I>PkQw>O(JݏR`E'&QOL~%~* $:ܚ:!nԘey>:@ yyw0rHO:wMdr {;a!N#m:)WTʂ'iݷ=[>!KKul/ -B@{VCrf7N}kuNk- L,漻m`\Kl4|i`(U+T"x[%Ӄ-DYʹ$3G:\/U:c=C${u @@Lçz>!KPoƚgl_2޼{_aҼU(|meo&D-V4Iɕ7nG3Cof\Ifnji)Gc `ao[JYi585X_QhxZ)G0+6ϤmUtBkkfVUedi$kYȫ0(((( ד֕>O {h5Ŵs[_H4IN3ҽyH20A+ou^!ď UDfCR " #N,7M>O]8ۥ")O>)Ρ[FVOǔpX yCrivgO)^0̷Å$1Qp>ڥ\'?lj^;v3}y~_vmCך^n_:O=GKhv0FLzohoS}[ws> mmoDHm漳ݍS]MrUѕJe5i}.+&vyt1%$·Z]<}Ԛ.8A5 uuc[ƙ:^,(Y] F\i庒Oxpasse4Y=(pQ)p;v_V1_տ'rӵ ٞ3&!([C7»ii5_Zxhc r06 1LzohoS}[wb.F$x%( qCFpf 0= <nbd'lWe4riG *1QcR&=+mұI8O/ZDݪIfS)8 K>ɿ^kO]wp?RMxRmpv֦bS ,ҊOXz'/w tk_m?I=(RM%26O*;eYK$u}〝>UBmsy%+"/K2' DuCMR~M'8fv+CE<6Jo{]/ DThsqTe'6-rMʹh@yԡN-?Dr/S/g-=1 i\8@yb-WmoY@W;k< aud["+{NF;mO!xL=\GsEO+t(8rgFWo]@tfMJ8=] I _> ,;QMZb| -Ti9r::FŜoD]v2Œ1X*ҧ.sFVXXaѼhzT\߷9QDh{pS>}u̮''ي^;+6^`ܭZ1^zOU@k޾eG]5Jhm3Ղ_9 ;S9:EB8!,wUuB;HnicpL[b-Q;z.'$u򋕭B/5 *~*1[W}qmn-iXo1]"V]|h xŶދV]̼n Fa=R™bNAK5 308,Qq[wMڻu/ 38-W"sz?SnԲb8 QjR'er)NY\ +p.eo?۞D˾ٲj.: i?wZTEԸ8#N^QjȘ}X &PSz&RU|,pqcU|[)?>'?ݨNbM.i3qxe'>dW9:7vbnƾ; \T/0L\ VIevIiT>e"|4l'􂴿f׮}Gs_y@v8gnƉ9C{!& +koڧoS<1gތ|+,eux 7 t4\.T)PqW²|&c)*̶`'ۼGB.Lqꛔ4h^e-5vcU'K.w5 MCah3+MV/\ҦA'_-JHxoLFV%0T!tlHһ&+;}2Iۺ -$sHrBiF4©!iNf|, JٺQ =@YJܾin>Tʪ)xLiwVdR8+DM_rVքݴ4eVL~ ċde'XK|-VL+1mdFncSgm˭}<0I` #EK5ocSBh˽kT&٦uX*Ta3XqC-s^:\wdϳxhgzݛӷz~ߕdIf$>}*Vo,R͈3NmOS=k"<;LH/See౬v8}<#J5T-d-i.=LN[\i)L]C7ѡq?|+dܫ|!b*=`VO -ib @% k"FBTТQoպsvs.JkzFDk?u,kĝ/;n-2Ș!pV`1d䵝B/Gl7(pEv2[ם i2ܒ7>}uOvWˏf`VT̥r_ږvGor|ks{6 =_m|gg|w}GP.8uQB&eG<{ dR2X.kj7}"\sm~9 !,oCiӕbEڴ Un 8@Au_PǥL΂>&Q^^=BµrdMJz'Ċ֚6d%2bg:"o@~+6F('\+e7#YwH.$]~OڃҦy%޿i&"^nCEFh*cBrye>k>QQѓ*v DxI˾P!c>|| >qjاonG(3u Rh{Ubv9w4 }CW |_{3Uǯ?m{ C&հKt;)ݖ&I%F&9WP 5|~Huk=uk  S5EtEXtSoftwareAdobe ImageReadyqe<zTXtRaw profile type APP1xmP[0 )8_ڬV 8$-H_c{Yr?n©@g+fED88 z,HeÓb&aےJu|cP2flQ-FRᩗILm`P!weHs߃(X:Q 0kZ={Ŋװv`ՃZbui߬ Bon1qiTXtXML:com.adobe.xmp 150 150 U(IENDB`fotoxx-18.01.1/images/unbend3.jpg0000644000175000017500000006441413222767271015200 0ustar micomicoJFIFtExifMM*JR(1Zgnome-screenshot 5http://ns.adobe.com/xap/1.0/ 309 593 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5Q" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?/LUC6Teuj(5?'>ы=?ޭ3OIVWK?U*%[>2~_jG}(٫89ǣQo\j2@zxUo']v(Ž:_jߎ;wWz)B;~&}w\[ɿƣ>.h8>"A]& ~41F@\?2W KQ e:_*gm -i.{ e:_*LUC6UԿ4]1CLnjeu/ͫtUm?ܹ}@x dfo&^*W?3W KU]7UՉgu/˫tUBՓH?ʫG% f:_*LUC.U~EG@?]__2/XP&~*W?3W KV5 e:_*LUC.UElgVaA+f]\K2ݓp\YS񽍪[Cș(~NygRǯsD_o +^} 06] rtPY/?ukvFw knE?Zʏb'E :v5@iI0[o|L⯌UfCO:(p 'I=}xҹ;wH#yr+ˌgEäŪxoz_*$)?aEGmfKOXVw)٫jvVvKV]F>!/,+yEVԘ})mS?֪QL /ƩA+T"ƫA+GƩA+ThƩA+IJ:Zb-l4l5L3\GѠeֵacSIk^* G$__PX3~\j$TMLUC.Tguj*J63W KQXPEPqa:+s-J*8 AK@(!E%8R) xÎzg`@<*.-7bIJLSE7b@ (Toim/#oSQ@hz\zx[H]\آ9frISsUdDK]]%qrx&VO H֮HFͮۮߑB_$)bxmRVF';lTy_ہVP["SF- J@qդ|ЯRIK@ EPERRp4fEZ))iRRKIK@ E%-1Rb4fE;4M-Q,W";FZۋsWQu=|Mc; #SNܥc\Qwjl9Fŭv.Gl`W\ۙvb?ZLzE(( *o+TfT$R!8TR=4I86*u}k3c4݊6ѷpM$)ux5oq[=E"N2+P A%?[5km!\ cR: SM0HM)NkARC^ZWծ롩cE:(Š((9s롮=ƓɧDO44NcZcS8k.iN9Z2:NXmYQN}(ޔJ~IP!ғi4)&@ pZ6U8b҈X1>F zXeCrMO6muv PmQhb )qF(QP(P<-iiB(д*Q(0(([cGokxqVM1,Kִ[.zD4s/fٺ[z_^?t(*3^c*} yu[LOYKO'<*;fxAxNn"1̇؎v@Щc:r] (V9C۫d:Jȯ)UAO(쁜I=#&Xı +"|B{ xmVDہMuzLO$u #ƹ[[Hb-F@(J`r\H@jHcugoα5,MӁ(1)!i -!ԔJ(AA& LE-P1RCDJ+P 4H: CDJ+P 4ѵskIKH)jEP})v6//ҊO-}()€䯽8BNhS"< jR>*Ul/u%wqVBIŵ8[ijWڜ jxڋ<[Uڞ p@[Sſ_{S\ ڤtCNWUmF'#5^ZhT3qDELBko-9؂r+%eX(ʓm(i[˄H!rjm_6n~FiX1M4\sI@<Pj=ԅ@.(׬] S_gb;vlR W/*% FsH<]k9p3G1~}S;X^Go$eg(=jU֤Z]ME'W~egdhfRڦ[3ZFOas0YJc;X<@x9╂jK9 .i3¬bĆ7p?ZNr8&M3D>e5!AQSyȥt2O&pS dUJ^?*Fv)\ # 9MX\vwC607F((GAX1sֵu j'J+2m0?cKr+XqF$j-gFGf+7&4uy]G~#XM6/'u &ŚX;L9QS;\(uAu#ӮEB{ӓ IN9kq&&qcp=#?ʬ?vqU4k]Y ݚ6?[]bXX׵o{%̸\g<檹OE$rHB5!0SSU\FiJ(PiJ(G֐үQjKҸ&o'=>K\;\H#<44\[14p@jՏkHN늗V8{}_ʋ2D+XA-*]!3**Dn)a`3S{̦B[oϺjllvҹ{-bQ,Elӯ_.7]GRf(Q0KsUAȀ Yc ySdu) d5-]O.Q[?^*GX 9..Ѥik'$sTwm˺vl'E6.5$/h*"}Gr 4`Q=)p.Th+= q*FvH8N'b'Qsk vQdqZ)^g]AJWV"0*~kk_IIzR`(P:~Cd c5^;ŐѺ¢VjiqA4E+}A(nQfJy\GYEr?ʒg)`V=lx5[g}/4 AaxcS{*ӇL|V'ƹٗ+ ٸ\!9M MC- KIK@(:?=ӹЖ5+?{7R2s,[@[ad_?m!IET;Q@E ((; =Ҙ ~ngk5s4oWt.[+߾O~RH`@,H n (@9法Ix[Sס4iŸi^ӯK_ĸ2X%z&6sQقpյݶ3TڼҽeץcJw? \ #?:qV_6 + lmguG7sNO]Q|W}=l PV$S8TV36iE346Ӄҽeץa]٢SyX,L|uf޺ 4o4~zuk]x}TV:I*+ 'gǕ7(9vF-O[.-;>FW2%[}ZI|c]%H`krK}p *1qzU:tݹO[.-;І "hS5`?RE0$֎OO,v;sM6Bri^mqoĊW4$H)/ 2 *+C &[* o{2E]wZu{_̤-vy?*~>U X*#uP+(WgX~@٫rzSy{˯K_ñF)C<|ufxfo? lmP|W}=l#+;UZ7L|ufj~[w::my{˯K_7%̑I(dzT9Y˨ImɕI =C%'̋lb(7}+eץc:JzWC Gη|qT5}?"J/J吝^-Gη+ʖH#[>rrj~C(>[+߾]zZv!yvz|qQo$|qPWii^|8{ k4:Ɏf,aڽ6^}:Թ=tiEi^g>&p:څ'9iHrz7V*9:˿t?mjCge~[?\Զii^GjK'7Xٟ3f3}W/L`tkZ7WٻFY|{0:.#N/J吝^ gkrbSu훼e1cΩx}2G~`+ێO==hMN/J吝^ ՕQSmS# Y*mSSp1* 3*pQm?\9SҿO~k$s\tpfp=8oIo#E6>g -:ӌ/4{qT[Eh6ʋY_?8»*'F_-O[.-;QEF!EPUZjZJkT-F8E*Ō|­ܮXU|®N>q!Ďi@d$NOIxG_YŰ7WM&L*EsIA<kj2ܶ`&:~, ,mۑvwic skRCƞڦîcmqvI }*I$&0UEOrrl?TўfNl/v0{F~?vSBh?x~iVmm8&6 t恣YJs; Yy?ZC[.@fͽ~!TmJBI(ImRGK;>:ԻzRE*qmy[{{zʬNFT5&VE 98:envru`4K$-u ] z~^kWS^đ TzVgh^[6C6ʎyR}n{+LOs]9O}+ !城2BBmc%ytvL[SoQ7c"̬nh'ҝ:.q(~,*NcOܹj,X?dUc+ٜmIqᣧ,238i`=kN0 iNoGI<䴂$,aǪ1f@91RW{:]B|sJ<7פ[GBUK,&W Ch_ #g Efoc,7@r"d3NG-LDҠd 6*:WNeHܱzQ?sf;:_lc0ZwuP8{#-1߸f^iO_V_?M (N˩a#PNO+&]{?rPq*zbGS7kmaԱ,WLUḓ?C^w3^9 ~Z=p>uz+ à?/'%%cҊ ŒQEMQE @ ?Yo>Vu7^-1BNJr@$gZJ71su^\1NjCޮ_Qj&6gXGOmM[{u;rzOkbW>Ǎ:{KS>EUQEQEuSZsYBkK5rOgDv P ;Q+|roTbW $(${cd "֍cT+Зcq]Ws~KdN.l"{y0K;sOȲI>^IrbژXt7 6IXE4;Ijٸ x5mcXm&bB.Ob?¹D){bR|1''QYh፽\RD#2mt6y8D24tsk:_]ݬ2Zf/)@bH{z4Q=J#Q- 8]wHɸ}E`F85^]5CxofpkbsqjXR80*|&bhX"4!;O^1{PPC(%uUw:zhWJda5!)ҙR +@;-Y|crIZ=O f$˞_VR٬&ƎOK"{苎S TDWz\⨙ḈPX#<[Cg:Za q"8LIx YCŒFznXNd?sdĢXARzWOmk@6vrH…+y`yE8קx-4[aNqްJ,qλs2)Q60]0FvPtu#*Aes=jјە eW `3ִ۸<{G"u9SSQw;~PH.'9皅_-ź3+(g J˪ȨQww;IP2#=kW\:Q3]  Ye*Y/[Hm k`pye;3Hbԭ'EzVqZ:t5vj˙\Ǖfn+HoVVքQxkXfu!_T1PRULEbҵd0;h S-+R䊔-;m; iC,LPppнaGtq^ɔ#ּ^%1ʎc*Gpz5mErM P$FQ1ÁM>kaBF@!횏>(Jd.D?1I5[N478H9d1|?\\I(~!_cֲ|{saܳmdUAr֘P**L+rJGM4tcrq#xr8TAnG#bgd-b[w\>iL39C=>2~zٍue6D(ފ٨"ojf9PP?+3Px'hX!Ru*!ki{my-1\g5>nE2ȠV"׎K1x ׯ_VSaKf̖I\WR9ߑ^[CsՃrp2k_CZJ K;W-C>ۉюUq@m4Pc?[Z}{q\M_-:0v땺gI5s?:,.c)0BN62EsM]mFFKq\3"qۊԶ ݔ09`bY m.5Y<:~v18#Ftj)IB#˚,"%9 }.Yhh˅;gjgy†֜Z7Xx{˜*Ğ+kw4]`)8$Զxpn6?Iu3Z)kZƣim ϻp<Ru+He#9?9Gɚ㩹 O EVMv=fgֺյ%5$1-8@G0+vJʚc$kxLڗGUWCc_SQwDk;X,?Zַk7󧷳]O3DyQ[Q@Q@?O:5CCmր湪|L LZS 9&³4ZZ*X'ҡiDp ;&}w c" v޲mPk笵Qy6vJa ڴQ6,Skkv'^CMscb?EHDg J0+ML띹Q-B^yؓSUjj6ѕ5i:ZrQI$Ev8QH9["2G5iW ua.:zzκ)w.Tj.,dK%QN"DDSJԤR@mJE&(iiSLG%rV|zןO0q'=okOc\ny<ՁW-JRnsDVS=K[o4̇%Ǧskj^l"6 7>t[#lH5XM0gIjoV$^[!$]谻NN'֪# {S ,VҨRan /"6ϙ'[}#k[G `I)Ҫ fs㸫7ZMݕҡB>T'M6O:vjQ#4"*IWN_w9gE$aj6Issi$H?,1$V]Ĩ7 WdP: tnB?\&pHݼhvR;-$k 9 JkDH{ŜtΌfQOX,VАQ1 X D asTh mm;:- wݻ{ԭ!@))]Ȯ)uPi9 @ބt[; Xi]?jt$W𩷥Gןpnf2* u[]fBk** 5Sϳ=#QYS-o^{̒GyeqTxHx +*5)Qt68aӽ\Q1G(柨vZEk468S"59;Rjr~YoiIn,k-Z`l@e SWntA/"fÏ[F`UEcrqYޛm^"kpEMWH$ı1QsQ\G~Z )>S"Kd Rݡc,dR@B8vhkIPEq޲,t>Qmqi$!bnKm<% q H:nBtf юmah7q^M'0$=Zۤy{~5}ΧdǢZkB^AX5CD:}@ۊ 褽Ԍ*|G{Y>(eЧUeeFԊC'¶J2oC̥bhQTo|$u9"ܜ8;zⵝ rՊ5Dm̺]sk)yڹ߉%f۸?w60Ǿ#sHiBT9\(((F4;_Oæj1upW=_(b)SOZ԰8>Z3?1%C,-9?Ln`2Uk)h$)r[. =r g {X#9%OJϸW8z=O'+k7cZZ(F̕wkvۺub â5L޵FsH85?$5a Ya@=kN4 qj*J+D:q{VMI$' tTɐd$;՟~1WYfUo?J\!;Q#8#޻gnI>=sV^BY$]A;]sɭ~~#?U3$N .nsi*\SHDXR8 P("yZ%I^+Xa~pXڛGx9Lۃ(7nyyxxfM3×3])BsEYtm4E;%2 ?ɮ>Iԗrı*I,PM"2#@URLB@ǘr9=>^YjHb2ʬ u棔2bB ™PWF}蕕vEڬāIv]{]o]"=I=@8hhuCBm%=MmSHX~`7krS¼?yW8&?)4yj>3ݨŞIp8 (ǩRtkyy<h+ZbZE7z1]?n ʵ|qk/t_WODg IC@5AXop]ֻQ\Ck++XxH`nced Zi݌k"*e[pu.Pz%1+9%{_/I܇T%y^(PEGF_T]RkU=u3ꉶ=A&北0VFFEd%KɅf!9#OJ%ݥ[G d8nv|w5rZct߻}/L[[|,ʃ{=&}nݹ3VrY8#3dT흛rgs_o#Y}]1a¸+q=6QjՖճo@YGMCwA5މ$0[Kyֵ=x8 uERcNwŻMȊ2<kt;v] ouք[̍)l]`rMj:2VyvL 2Q%"FUxhmOj };[;\ҭݳ3s+#@(Ph6?VM/Vs\~#ZS_;kZؒ>sU4Vi2ۑXLrP1$ֲ èYYUH߄ŊmT ,0OZ{6wkJ+sp1u5vٌ!'GIm'۹Xi.08Nb"'N]|=}+rwm *HS|*ɮ!}Vl٣N\ ا;l-V5f ~lVxH(#`Vbb-{իI>Wn8zp0U3Wξ `kTs+I!}?Ƹm^#BhnM,~smڣ<sX)"yYzTT%3ajtPkDK[kPjub.)GP@ N N"G:Hj dÊb9b%p;{:5^Lȭw>xCC^o%G8"K5̙ G##aPǖqǥE,,a SU(:+9MHVO"#"A g++j%F1W.ogKyNVVj$ tiwŮۮߐ(xhcHu#[pG#+ʗF? *sTHϽ3[ebFQQCfeAe랣Ko5 1$wU@'<&-jLH<!ID'!lVGmdlHJ+6l}gjh, H:ȩ qߊ;(m!$8~ C^ڨ2}Љ-IHK q&q\Lr? سwTQ-t~VMf[9)?Ӛe%Z4 pVn#QOJHasHpj,U5fD]j=+D:pqި MM+/jĘs k|e]JWge*q:O5-j|k~|W~QqN<z,港еe଱4&༏;wyN T- zիW>Sj 3i Pz< Lá4jVIJڹk2F9 =6r$r9[dKxV%ۘdK+mf'.zLsWs1((( ;i3lPYxsLCO-ްp~qHO"3J[%V~EVA&z E!m#~U^\\\ '殻W5od2NkXULt(i{7ZʡFGu4ns,LO!+77vm24,ݍPԣShX/O]-\mpQIܙNWAhUyXOdaJ7?|?hcY:4i"5"+9m'W 1ʰ|'i:&p}Yצb QƸ *xr2YjwD~w?qu퍱?*x?l أ:?q4Yӗsew Te?@bk:w%/ޜ:/ky{`S ݅?lQ61N6~9m#8zS*{q[I}'xndATE>U98n m<ϩJ+F ~_vXVD ly;M:Ue\Je1S#>c$R 5>jKJAh&AT5b2_$~a}wu\W3pWiq4ڵKk%jU|(@MX&k?:>[K Ilt_}N]u,`T~Q\#-[jx4RR݊PQ;OF$PThZڪe1fz[-IKS3 q`C]WWviڣq)99.iSy1f9⹤+3ˏcC I*U%H~ʟVFYT2==1㧥UO2=Q֠J '!S>'ʭkagKm۲uyNJmu(zNNxU+1jIDW[>[YX-i%n+m k۩e O3G5g df;ʁ^|472Lރ4SFMZC6Vv1 |qV m3GP -f *WɍdtjlR<֊( ( (;cGc֛7 1ZZOBpmNhVZbKϽ&gbbXNn!Q7^8I㚩,.3vWbcV'iYt #{v;JKؿ,Ci5 .htʸSQ՘wTJ""/z?:Đ*Rt&pME˰j/.ume=AsO/H \V@\9=.i;勱!{ 7m d$ sni;=mrC!N$tv\AI@4=)(o«ȥL0'/i'PԮ5H) H@Nvs W H#9AZ9ɪe&Z)7Tol1U75E,l&xUDE?qXR0G,n9ǘxm=[wMn~uc[1!e6{ԑ"ٝ [/hR>qWlM= 0zT`fQJi<яdVGjNsiN<9}0[.8RY QVOk Қ9Z"|TrjsbZ#'ӁsY=6!U4߳CG Psp }uVyz'ߖC]踘ʥ5h3pOď^a@ԣ<`~֭c Ñ-òFk>VX}[ ?ƼIQΘrGPxݖ=:8?ʲ. |{WU6c(6z"0i$ynz(*Zg:r{8"? 8/󮭎|?♗Dt+s(((괨tLĐYxsUsz{S:i[Pfa ߒ+t޶-2(;X+81޲U\W*T[:zUFv.{W5)e -6d(v9l5U #N\Womin"   i/ǵwg2F ,'We($rՓr*91X>*9[^@Q]>8]p'Q]OA]=Bw"- ,]G ^+mIs6#6d/'\ڟdVƫ-q,՛;@733#qӚcHri"y q'wC'I9-裒=C)%B"?+4Q#Pk"pb7Pr3ToHM,hJb03⡷t7w",Fy\Hg%W*ם_vƫ~>/ҍZԇtUY]uR f\r͢ 5WW?=zm±E13yukRy%G-vm3~%H~gtw R: stScT$Z#":;~tFӷְZ7Ki݌?JJ+ u OS.0QHqs#~9rNja/&qA՛"&4\qzm! @T"FFʒyYTJ>eeoP8\t9/ ۺyfLz)c,71IsZq(mͩ<їBV 9TmWi"\= ;q(R1%O~ eEutO:|Eon#Rf9]UҖEIya<`g1s`ýe@CLF8ʗUszke<uͤMkkQ^B5]Jasg?sJ(Tuj r Wj]s31c(ˀ⦱]ېzEc#T"N*$p$;T7$QRDE< j^HE2\ZoKۨ@Uķ5EkLA7 ꊅ}BOw&=3EvFwd upzieIb(((((( = UFhUKPD2h#CEVQEQEQEQEQEQEQEQEQEQERh%+#:zTɨrs/$ fvEh<ꗆ%,0_㢊 IGfh,մ~8 z.AX90)EdKWv8B]J6+X7ډHNEE(AEPfotoxx-18.01.1/images/right-arrow.png0000644000175000017500000000275713222767271016115 0ustar micomicoPNG  IHDRĴl;(IDAT8mHSQǟ{ܜLS*|ɔ̐h~H(>d֗>!^/AT`!fkF+o|sݻ9w,m? -DcKQX֫x7)  )%$QԛԞ/L82 p6<"KHkH/B/'OZʺ ?G XEvj_x̍Ia> @6L4@zWUTW VeWB_to)|v JE]騹PY|X̭;2E/:j_]5AqJ8-#V$H;"^ MVya_M=`c ɸIhWϭeaqk z6QK{ve8?Aӳ'}B1XᅧVeAAI6 Ӱk@0*Bw[F{mLW 1ѺzTXtRaw profile type APP1xeP ߧ,&˶?V@Kʕ^/s~2S CE5&D-<e?u렜eLr7!UŻb7O=׽YPDn\=7m{~1\PYD)ۯN:Yx~=(QU>iTXtXML:com.adobe.xmp 26 26 0$IENDB`fotoxx-18.01.1/images/embossing.jpg0000644000175000017500000002732613222767271015631 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 111 292 0 C     C   d" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ HeU]+U*Cmu𵿊-m$űXR/%hڲh`&4V6 WK/Z[u]RTE܌aEU;iFklPZ xOdEl>|H|!'ߊ-,g4DSYAHY">F7/ ߉Ot+:_ kz]aWi.JM,@#GPjz'mZ~O(aOz[ x3 INWexkty\ $7T0J/[.Lod܌b^_ɒiy4y'8GNдZ_7SkM`?_Ο:ĜW91|kO-hZ9ܢ_X mտ_?i >}^A%uk_,kʭ:S7_ 3{s+P֤񍆠ZA5 ]AmV1>`+9nSVWc#!Å JCR}'?½kH퇋~iKom(*HϜ~s(zjX)|[5-_6{g6v/ȱ'u~1aOl>|ↁQ];:2Nsđ1v]UR;g\ TXƈK"D)PItgAco:vq"H"]s fI{DצH4Xlnz~/jnyR[G?B}? vB ж @'R)nnPOD?цA>34־6Ν.<^YFW>L2#)U:.*֦]HԵ[R4k%XCE)CiAQd9|dqFim[T WM/\ЭTЯ4B3B=X;Gb $<>Jze? |)CP-Z%ڜRE,zG0[qyJau_o ZFR+ߡ>7) L w(:r3E:fh.Y"@8(>x)Gg:5%J[ŵBsGO~ø z?`_Z=m.~|}_??0xwß}?; +_GU#Oi>sC*q9krG4x͗ͅæZ\J&#gmpaD81Uo2Mi:[3]BHd*188?`_Z=??0x;ZZĒ_|W7/\\cYOۃ% >Z?x>SѼG-ɼ?eeHU W???0xwß8iϏ|W Ze[{v9A(.M}sC*q9kYuO),%8ZeYR0rNMdx.TQPt-`0I Eykwß1|f>/IKbI|V3 0#1m_lø z?`_Z=;;ZZwik{;ič#F 3SY?>)>;;;X5* M2M--K) !c QNKd}MoxOWZ|'Ok <8ֶQFYOLx 8$r:??0xwßpppk? -j~2խ+qƅ1` 5&EhN0\ۤ4@YA ykΙm_`>V [)g@ l{;2ֺnu<{^ÜyE[ -o=yg@oak_ᴋNueO'wq$ Qn;FUXIY]ߛ/GZzhaeK4-44@,}p7|>mV g=w@v?W˿|Ox_m-H{ ƱMʖ1f?vmRi[o>"C#F3RM|/K}/ ]kAxS7}˲Hi#V e2Onk2LG;_z ;2X;in?}2BmV汾'k?cZ]it7#k+=Ҭ)UfBf dg#ɾ _?g|=i |j~];QH捋0$W !H+DW3kR²[6_G++eEpy񎷏eỉ4n&kcxL3(zy_/~]DWWQY[Mqq"(dG8UP2I>V"2iy)|r}\G_|s߅߀'eR2\2@Tu=sJR!"՝F7wcp:dURN(..pi7}2y9x>iMZxf3N[kxGӵOMխ,#Y~`l-ĭC$X 1)u:\0` Њ k 2#h،t8 _4S|>m+ ?͛QDy(VXman8-~ nFn_I./^g5G9۽9"Wm.b[is+;Q :6Zt?PcFv)3mn(%\#::)wwmv (±i>6v jDQ#=1phjz]\lpi}U%?+e 4i-;]5m3FXn@Fx.?<  X2f>2HIP| v?%?+e 4u^^ݤ oYn m]Aq60tH( &цv! ҺIt B.+ qyhQQP[~MUxkz΃XEog43 YE+dVG"%?+e 4KVhq61h~꺞mcAu4;?jv I3@kW7 .A[/O]#_NtC8?mx-pUKVhH'Ҹ x{O'YcxT#9|.y EciM0dǘ t?%?+e 4ls|hZ}oqidࠆ)7p6$Et .A[/O]#_@cJ i㸋8) (((((((((( G]HD2ZZpzdjsU?.=gj[c`ay!x<;/o ZxZ[{V;yVSч86ƽu=ubM7h.ZI91RGr3FGHʟ4YϞ[5\t?}WIcckmghMs;Ɗ$ ܐ$i=߉%z o?YϞ[5Ttω^ִѨi' gVڄRDevoV#v8xG\<"I6F>qɻnrq YϞ[5G% x5HPrMF=Y0&C֛o\]IS XK=Hd"[SB|YB( !Yrr4XW:eu 2A+'?)h3^((((((((((JI<j[ŗ??G-v;Zkv{d,TFvxzu-?NSY8e?-`~vPAO~1Yj kǨӨ1'j8/.TV~21b]/eyBvcM?Js%ҵ kwQ%K-F8摆x2My+{'ڼEBZ>//*Q/yY[xk7=<:4sj0ٗmml%I2(Z ~;פ(WT_=]gt{~s<+ü>΀ ŽN\ۂV<|5Zij{F=?TR<Kl _ {FFBKE'[Yq,e B 5Vzgb`KnmȪ6H*oxK +A׵=KXK woVvp"[fh 8fێHKWѾ"k!_]j5dcሽO[19Um,9f#$W_*_U^"R;k^||6'GCcgiBO>;A(ebe.B}[߇o *S372xOUѲPHWYЩVz7se-nsBq쬆xUvcHppQVOiıE!h((((((((((((((((fotoxx-18.01.1/images/search.png0000644000175000017500000000546613222767271015115 0ustar micomicoPNG  IHDR0(yqbKGD pHYs  tIME#+O IDATXml\Uz9e̝g2~$'q^@R ++D B[i$"DVBlUjC]8،x<{{LEQ<===&t]磲I,---a\cǎ'صkmm-|sU6NdFӑD9 $LLN"ώ(O?^|o:tZ6]! +,PT]!]uFln 1M *j&kkON}8!"ޓֱyfj埝={vfG۹sٹ~|n.j|{Է232$H"R(ij 'ޔ>5oޠz;`Dc(Jdžߵ H|n[V1:J}.RHA(dH@ ( r$@1F7}#^RC&Ӎxo<;c_$A4ս!&QJCA R#G`1xA('-)E {pH@U?[i:::9kPjMvP~JO B:!IGHrH%I.pr*c!"/Ϯq\d2(uhDTtd|Ռ(@JII!&RR.9KVtܚ/סi*⯽ڶb,ka@qE4+ >gqI  gB HQ 0Ym3<0Ca$mD4` [ u=gErCEgKH`!Wx@!B%/S5GL $)>FW 8G:c+/mUa! !1P1q3ֽ+|v":KH%NI*<DTF"c rHg6z ' P L>|Fk$aJ@s{c FLr2gY"1u`{ OS/ӫs]IgJιRVu` .t $D8 H&׬Fk_2TS +5T*Go9 O Ğ={vD@K3S$K:ch'ߘ%25(JT.n;8߿ (nLh7h>-}6S#1a )*Eq%wݤoͷm[PǑξ'u///߯(JYgKqap-9]Bsp*򎏮>rW3ڱ ۆ(b؞/#{饗Db{i:-/gfgoC>WR N(X=񫯾4{ڽ{78le rK*xDAk6w4#h0;;|>l6QXjj5}zr]coڴ鏷nݚd2H& H1@Pq…i<v4MC 4 rrgΜyΙ3gjԼ-X aREq\t W\JODvx]a&VVV`2Lo噙u={ꩧbob}s,X'Z~oࡇTU۷ 9((JCUULLLXpg 9zhsDDJo=|p0'8j "a(MMM~~rܹGUEQ:288Z0 FQT.Z[[p[_nUd>?y^ۗd ]a6JlLfKXܗH$/..W) %(x/J5R)ضh4t:l61;555.Vbk@4 )%c( 3Öe/4Nwm~o AuH)98X]]Eﶫ*'ggg; س{nZwyg?~maa)EzF8IENDB`fotoxx-18.01.1/images/manage-tags.jpg0000644000175000017500000011360513222767271016023 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 291 606 0 C     C   !" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x}.޹ MEEj>|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>EQ_QȬ{z,ڏ"EEd}ޏ{`5}}/+#^}ދȣGYj^X EEj>|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>EQ_QȬ{z,ڏ"EEbvV8!N+?%g曩ig~K8cHVHب3;Oz.+el|ȣG^]Kx^+HNxd N@#ළl_S_f}}/+|S7^ K 4/l1BgIuH⼗Dl4$.u{1uk(ݍY8i`qj>|>}^?Rx?C 7OUlS,^r `zV&OÞ|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>EQ_QȬ{z,ڏ"EEd}ޏ{`5}}/+#^}ދȣGYj^X EEj>|Wjk/)q}p {.=O/ڽ|A^Xy>d _`(N.ɖ+9,o&CueEM9ڨ5!E_AqGZuV zWmk:[ W$Tol-2==Z>gTkЊY$+5)MJy)XN{ =@\olvY%'X R4)J*^,f"xha߈.,>{%2$G_\iek}mմ$8+ܮ&6#M93j 7x;c?ǧ5>~n6I2'x8#W~YRmuuo߇!F+յz ߘG֊Kᬌrp9'ֿBܿ ; z?ܿ ; z2n~z____=__=as'֏Z r7.r7. i>}W;|WvG;|WvE\I'ֿBܿ ; z?ܿ ; z,Oi>oA]#oA]#f?=~}hIЯw/w/0GO~ù~wmù~wmY_Z>}k++oG+oG.~z____=__=as'֏Z r7.r7. i>}W;|WvG;|WvE\I'ֿBܿ ; z?ܿ ; z,Oi>oA]#oA]#f?=M#߈}Q/]V{@"ݲfPU8vܿ ; z?ܿ ; z,VuKFgl!B|*޳$YvmL$o0=~}oA]#oA]#gik]#ώ6ϧx_PnH!_TI7u Q=`dSN=w/w/]- ?5x–wzFv7rZv'qQ*6sq\yfrbwֿB?ܿ ; z?ܿ ; z\sH>2x2x-qZZIr-ĐFfx;>1Uk=ڤ)6*; Wֿ__=__=U[GxRxӝ CAr\c,dFy뚝֣]=ԍ4r99$;|WvG;|WvRb~z____=__=;1\I'ֿBܿ ; z?ܿ ; z,Oi>oA]#oA]#f?=~}hIЯw/w/0GO~ù~wmù~wmY_Z>}k++oG+oG.~z____=__=as'֏Z r7.r7. i>}W;|WvG;|WvE\I'ֿBܿ ; z?ܿ ; z,Oi)0[X&pTܿ ; z?ܿ ; z,uKvU!n',ڳuit] $r.ak +oG+oG.|(HkM>{%*>^1ͳ4ȮO,N~ oA]#oA]# iJ %8;5~gƑ_am;7OqZsj]8i/P:___=__=y\ Y{y{Ɩ*Y-{n~xފ q|6.Nn}WE+j!$,t 6/%h!T">B(] A-⯭Rz\+ n}0K:m:t$n+Jҭյ ҆)wjrH$W>;Zɴ_k&Ahm.H HVGojuF5&Q^wWӾ_h^%Ű󤍟3 me 2;J+ĵ?z֍+; _EUIne2Vd1V8vjm?׋/o-G_ xW}:Ops1tmIsY~_ݯ~_gƱbi&-*ERFr:[ _m_V9+i!!?nB/tS!?nB/tQ`9B/ _s _??!uE??!t1}|C맢1}|C bOE bA,1AA7]=XcA7G!?nz(!?nB/tQ`9B/ _s _I4'u 2njyR6~U9Q]s>8?~- {3dhD΀ぐ@އ]Mj'Ҥ ?e?{e?{xލ/A=c5]IQHu GKOeIowe?xsIMQAvAiVul{O7GO7^)eİ[jSm[X^ֱH\/!r{ -`dV&;#/졽um9>ss1RH0A!LSwVϡ 5_niTy78mAּEٯ:oMIZUU$hhr<'"F\Ӭc6cB= > U'xLVIb^Hg]8& ^|/v M} %Yye]fGPF7@-W>))]_:o5}6 ?UjOoF"Pwt ֝~3Լ+g4`SM"1eVl nrSn# lfy.1^(Dum4XXj1)zdVIRe `F$8c}s V05袊C ~ˍN+ne[4e@ WqEy3zd&'# XvHau*(F2֐q,ɚ;VX)}Wq=z5dx_j$utmD%Д{pe`ܼ`q]KywEE[q_+滪(3“5 aM2uR;#H9EMAQ誣~B煛? |gᴸo[pɼDeW#8ݜdgUN2sA>g=.&O*z=K >?s2T\G>?s2Tg=.&O*Wg=.&O*Qp=Q?.^q???'Ez+??'G|d⨸^{|?j>?s2U-v,rĒI$zZ_,|e]w!CNQ áZRΦsMi$B%ʰa($s\CWǺU[i:f6pUd ͒85TU&Ӻ$&M]͕z[տJZx17! 9 {>?s2Tz=K >?s2T^q???'Ez+??'G|d⨸Ey|dϏz\LU8Ϗz\LUK z=K >?s2T\G>?s2Tg=.&O*Wg=.&O*Qp=Q?.^q???'Ez+??'G|d⨸\ώ>_DK\|dWOeBev|ɝ8#8$$~4EteoMKJ%d؂tWZKqp$ ~n8Zei_[FMkQJ0HM}%DG:X:im';5cA5K AzP ;'C-'F#qæGj/A4OM}%݊~xzXP ͸Eϩ&?ζ֜shG=%i ϣDUhh:m!%Ԓ۸$M/!}i^2o/O>=@$8(#ᧆJ[R_m௙gvvCtB@?*??"_*ῆPb7C*ϷIy=1G?.HQ_İM-J#SUrrF;&>T ϣDU;8?W:ߴwm 'xM3JD'?YǪOA4O/Dё(9$E±z(0\^<Ʒj76Y-VxR6Ĭj><snxZG%VOXCG_`/!mS\XækV6+-Vm"Ucݖ |•W}D_k{͉tMGhڿ2-m7DDb1Y獡`~_vGm=m^$W܊F >V>]͊m( G;>xdon[zֿ?5/|)Sžv [5DI>9k[I|QM>(Fm5uok:}}/ B>ӳ%ImmߚWտݒwI&}u ߆MDž#im4 8`22GLiC o K/kZl֞o#;ʹW:}sڧ+~uGZ^j&V[{yaTy{"ZyToϹMJNWöŵЂ9g?#`*xo©zmȶ£q{??|ABeց;-=iz}-KZg<;?V{m-O g6w#vJ 7M䝛Km?Oxx MzGs>3ZE恬>S&#Pi T;dc5«'87uim}y .TV3w$ʒy ⡻]e~7-+wgnF;kREfd'y\ kφoqiA%% t#!#;W? |I+z~i׿7b/ƺkO 7MH]*}[GDQ۲Iѫ~jd^ݤh cjV3 ug28YAJ_$^)fo'xQ]Rj_ ka,ieIoyk4}amrнMb&FvqEO|og|fྲྀϫjImg-ɻ9AkZ\ @1Nұ46o I]E4Eqv%&gw2$UMck_hq^+Sg`50G7^r7e6^yv,cgnPx!:+F+ϭͣGd!O2M=D܊=cPo|fk}(oSCSа\=s/~ƷltK&ԭ, dxV+V1h,/6TDb9,I5wDm? xvV/?>=jD[:[Y#04.*=,oJ >_Ckǐ0$FpBoKxNo[x euU+*ƛwhҚR-q&|7v:=бMM!2ڷIQs_??7'>!|?a,7cmxH\\xn(QD'8%_$CT}O_?|Y[ӵuWJ.=[Wlr r*Ij$}7/2~-RL-ɠ'F_$\\z֟?As}: hnc+;?tbHڦݶq}%46uB '5αsAj̒iG'$wCAl T# ?L][bd b+~4/Kxx}3Y,d,3\ud/ғnxQQ~\r6?XLJ{^x2:5vIo V@Z-1~\K ͤXg7Il}=Oz4xj?=n5B[WFߧݳl% dfO5ɵvMQݽ_#kϹxcW#|ו?bku;T>x7tCl-+~Qu Inr ]gMgHnѴ9[dsrW#Jm/?e__tX{>=+{\-D3V4'q{h~?5$J-{Kux1yv匧mRqOݨwZߩڋ|=, xjNIVq PE;W_#zoN_/<&n:g|⿁~ tp|Euac]ѺuY+&RPYT3WU Gqkgߟ/Uxx3AECx7ʤMH°"xB}2y=vI-paH=si(&7A-hk]?^=glf+ GFX`x3NA @V8Mw]m:?MQp*xx3jUQhy; g1Ō*6e'WUׂ5OAxJG׮'ծ%h.#= / >q7w0=o013O Yk63 HJ*tʐqz?߁Z>n'aio+}ZkDC8Qڤ׭j| k:o.ukxfQ6I4G*@'D(+$nDZ Q_$RxS\x_H$L{HX m7 զ}MzoG Vc Q_$[4P7!~zoGEc Q_$[4P7!~zoGEc Q_$[4P7!~zoGEc Q_$[4P'55Iy[8 dSKjXQHᇋǟ.pu`fc8[?:>04.2|4=ǿ|%~C|DK}\Z,*?8NBo>zo:V 'Fal Ǔg+χ_W|d5i^4&.b)dP9 @`:9?5wÿxvo_AiivgF;pmvF Ul[JuWf1jJ/&U}"tLuo6#W2@Y: w|~q};EռiXY)iOt)_x'\o u<g]W%/~AkDr-:ǿeǺq=/+N}6O4sc& zZ.-wk_/ kvxJuGo\=뤗L>"AQMjh; p\/\q?+y+/\ noOba|pWd:W'j:]Ι5/i^dR-U3'Ue|/77k_yR?63ߴw'JSNMRﯴߵygm\$9.R|G1ij" [e@,=3ֲi;.kW}Q@jub0SlJ96w>#Zkhn]Xay+:yX JkcxAE-'*YK=n@ʳu x |Ğ-{mVxuHP)&o4mmsg]UʥセMVdwq]I}otxFM2>cv+KOᥗơϦXu+BK ;=1ռ9VE|!vRYݧٌ>t/Dm;#`q\{~͖we؈7pYr6n늹[k~w]L~H{kgס>,?ZU-h~;־&x_د[H5viC&@SvkQ=uM4Qww8UP2I=q: Bm/zmV}颛>TYzs¿ztWIBBD}I({c~ YjBEXFF\2 WmgK)_*+Ե3Ӽ?]J:iowx;[4RG0#pe.8Q ڥi:}|>: ޢrtHa2ee~)_Mmwu_m? hkVڭ4yt'uU/^𮁩i!gkkZ,p|w6بiJI>a^ Ey4o;g!bfKv6eݯ_Bn} =]jY r}7xZh~/ҵ;%<'jfAK _ 6V$А2̄ߍ.t~o֑>q^M!-;X3Y]%o7DqW:k,g p #A{__5<h]ǧ4mp)SGz׃sxKÖ: :I^xɪ꺭s_[֖C* &MKwm-uMVR4y4*xGF`pPM=u] '^ O]WĺUźy *H#wJ׿hxkv[OImhPa[$q2?5[x5 QKa7a2e~s^+|7φ~"ƿ۝ R&Vu";X#դےKnݗ碿 GkLs]E|{OvV𾅩heַsZͦ_@"(̶䙒s%C^:+z$V5vU,1m1ةO0ޒkx_Ӽ+^:veOsw;m(dW%zy_\XKKI"V,_;㷅u|񆁤ۭ֥[Ψ`@wxlh^;-AVg7˼FFH'R_ckMos_/ty5n4/Y?wv<o8M/Х.;|#q d:b^<^]X.4.XMމSH|z־FӾ!kVN{-nuy. REu }+h4I{XaW8`G^Qg^ _ouA-IekvpTR*6įSuZ~_z7_;*dGs 4k,N6:==iPJ( ( ( ( ( ( ( ( (9O j'Zͭ/ j'Zͩ{QE+>(м:ω-Mis$ |G-?j)< conwc8Q>nW˹p2'kq#7_jvmr!id8l"yk+۝.HmT}ۈE2fO2ZOXx]n4kȺ!Z/W↗?|;/.}{B'I ~4g+`֦Kܚz8s|^=g>xOw: vw#kW;HTs9cb| j׌[:k!xzv Tk ĖڍŸC"rKbe7Ǖ?3߇4 OoxWbI|@'k᎛u]Hn$mKɈ&/)J3Qy~U'.~4;]埴?i> x.-BO/n#WwF껥:ݐb0q{gRvw+Hl5LpTE8#xRM^"1U2 qNQj\kԘ5sWYk \i~'6ˤ]X$f1iwD ȅOڴi^r[q0+kingCHYSxލIM ^NUe6]jܫٸڱ>0# |#al.u=Xs40#Ѳ\ҜNcVw߉jO zlaK pWi0koƭK<%y_,#ۻGGQ+l'T5zn'u=bMrRn\DjFHAŚ=χ ɫ[_52X@ٖX<ҬxDoe}lM_~,h$ci9d8C<$C#.j[d{뛍gti$vz}V'hh-ȯ%|T!t_~"6/xc.wi]^^mٸ}6yvF1M$k0#(Ұ_YY\dk=~ 3a Io" M'i]eI#QT%X%}ǹWuZ׊R=:Iŭ4T cG1 Ԛwx>{M h.u${H).ЃެT?k [Ζqe 4Hn^tQx2Ěm{ K;ɥ*lvNUBӯ>=W-ņ+eSʄ[f]U.̮ӜbK𿂮]FM7,IMfm/"0d8nl`1|'mxcCs_ƂڍZJLKܺ4fvb ֎~u2c}=z!|=Of3xrðP^.u$>b*j'+hO~#C&}h`\ LWg&Gk#Jռ4*9ݖ;4Jgï׏XK'ӾѮf|$Aی(TKj7;mzd|ou ɹ[/ ] HF_qs襈H|c/n4tG:ŰxMIcf6-Nd@<)?-vxzoCqD-t /,)# B-Oyn~m~VƇ?hOQ^K-լ=d&IVXԎ^2z~P|8- cOZU(G<@f#c'הٿڷƅK/>ӥYnVtUB*Ƥ-O5j6#XV%止4*~p߾dA`R@4J{+o^v~}_I|GhV^v$R]ı"S/ 9by!x*-6YKкFwҦWpr\~ڿ.-]jxu1 [[.Mh漣/ > d>? _"kve?%&]ue8"0z?v _:(Եk Pe3:iǚgQ?c; ׌GxHKDwg뜚M~_mwlݾ/ wҼ?]+ ~+iN/-Gif1If_kr[t?N^ؖ&#7N rNjo7=EkX⿊Nsr;Dl'}AFo⟇z1Ȍ0C8 {_u5i_|K|Uӵ7\Zj&J3& W,r1= s^<:4ӏ:[y'aV Ŀ@rÊoito Cj=ƭme馴yO;0 `:xwC׭cmZ)fvP{ b'qKGe;rp+ˢXK=SCnu(h #r>WyWO #c.CU+ ~|׆?:$}IrEw2G'gi]/}=?4rvAxdZQM}k ɷ(A#8Xooƨu4n.ƌJTlHr->]jY/|-Wh&6Rdb!O|;>x~>5} n\jpiM<:~3kZ_KkS:z{<;Hh/ޱxoڛ[\E iF.YefP@6GR 0=W~ 6.}.NS+sK%_b]CF֡]>I4Z䱻"ԉ?07 @ȭڟ*Sk m9VHlk_p\/ݔM|D⻵=l<7é}lG$hmW.6uclhYwvZOV88vZf[׵ $@C98#}E%tMuuGƍ/_uމĖ:V襞3!}!J OsV5m2jW7[6󜑜qP};?K~ioo?C=b+eYCwwva"m2lw@`LJ{/]mఃV_"I-(C.XFIZCy?ZV>𞩤hzu_7ª 9?Z?OJyīխZӴ6f̋yҞU`ӺJO_KWvr=77X|_=н¶p^3[BUe'>ѸB=kşn,k(t-h~+_ո]yIj(B0#R- *[>ob^XC#`>0;:.3}JoTZX_:֓iٛl0G u>Xha=Otx]u4{GDPO. M3NU[ g1>.aѷΠ5MSX#U] |%7ng~7Hk|`w(=C@ִ h,YghXi#y UJ ^^`~޶F=iv[AݑͶgo]?$:s@|=]/Rk`48q(&,H.@9`Gp*[7^.HzVvA;u9*Bsk}jQ h*饣Zk@5NH].2 :Ɲkk=lVӚ ݌^tWS+9^95zi~w#]}zo=w-Bڝ}KI۪Ogwy1 85NiMņWz핮F֒SEPH;Ad⹛>%k[&Cpŋ@Jw#ENk:k׮>i:7ڍ%ij,7_7T߮RP\2ʝvșT:_|'|84oqֱۛpz^W+]ke}x_>,x zob5 䵳.T#ݹ$@ _?Îk?hk-BOT. ViaB.9N+xxo\xFmA?v~zc;^+7>Ϋyi[ݼ,aebvA<$=*a?;N-*[ɯ+?gC\xw"-'×7k6|m+ syNnh^#JTOU\D$?.xqէ%xfQ>/7\F Hҳ4>tQ>cyeh+hbs)> vWg&ݯuտ :|'>k֍wxoO}n%́|ѦasqǺg]&Tфiu-7 XrwFX|Iu/ |7ZZ"eHdg_2cիMa̞LOX_Am6o,6an*# #kɎZj_{>c-7ᶉCwx71X n.s'1U~\@0[jj7&h$"$*'=t9/'2~ ; TB VFh$uI[;\k[ɿnAi"޳[Mƙkud U@v2[q2+ƽE+s.Fau98O8gI#kiR\xY:?Q#Fپaa($ !E}NυQk"M,ƃ &*sh_%_xߋ^l|?GXtw${- q zs[vAeg^v'EdY Em+3xVMu;MDFFc<=q+ `q6W 9.6V BZB #(h[ͯۿIQ@}]Y ݡ-0R2@$J xRWTQ'cH"Ĭ{һ_ ƫ 6Zţ͕H'ʠhWV'>ZW-߃~^(/t2"nP02:W%շ|?zSA_Ӵ}B]:{H$+$ U`7o-,&IB@qv u ޑ}M'lX!IBH;i TMm ㍅}ƺۯ8 4yv8V0ng5WǭOtX^QOKx\$Dg'˵2k/_LIui4#JҰWRYڽɼ'Uj8[[V'(Ukmx㽇-4}ɬaZ[C=@ 8 ⛮~^]:W4*k&diTW$t ?1|a:櫢L=;ŐUټRmь A]GᱴV[/h%eC\H>zP=Z/b_~ǟi־!{]NC{4nZ-C,SG}6ފJgkuZǞo=;Hq% X䑰8n|]7a,,l,3 s.A<kT{o[쨢C (9O j'Zͭ/ j'Zͩ{QE*o+S컂$q{T5On?66tIYl2O1ЌE[7>9xPB[xQ|AkˣY Q 1 ֚55ֵ>ì]PB%aC|uq\x_^Q\~,յ|@6)Eu G;-R%R9< 3&덯jL[WwR/uV<|"|cd^!6&yme&I%:q)Ҽ>ËM#B$XdmyP2I5`NO[[~Wx'ں/TG5=Q,X 6h@DXzm~"^ktowm^h2V !h.x\y˿gX1]-_U}z[f%Ķyc#;p>~k xƚlvZ>&l3 2|mnߋ9UrmSZOt+zZӬqgB BT1q57}#i>v/YKyr(>;ԇZfZPZT^o+9w[8Y>>Ožseo=+F#!l_A-ƟxᾷOxNauG[lAw,ANv8$0*=ux `%QZvysD 8 ?g?7O 귓_Rtn]³1J.TUfۉ)KZzZAf D$nnyj_o|g~M7}QJ̌m _"^lI4?kT^Q/|>m΅Ǵ-4Jcoa B)oN)o`9l<6FWFOύiJyNߍ5\~ :'íCIгi:6ܚS;fa$3T]Z?9iZ]F$EYW) <+?&mYYi:7TD_*S `rm< ע4cz+5:nwO-Ċb|8q㊽9>_} 4ovqᘣ.:{DYR[G4B@<h-|O\xbN%P..#ڦRPh߳֏} U_4k6?z.a7WŒrzK1[?_553sItBLuȼ@cVn[ ̓ ƃ^+D85+{1,afױW7< ɪ/FEf`}+Ҿh7ԗUW^DŶ )L/8}eOLJ,;{PoM,Sa[|Q&wm>kHMRysDu;Gfx ?š5hz,{ɴ{9~m4_] ռQ yP`s[hsV_s3ե}" u[/mAa4:(,ilI 8>a\F X{+y?ß k_M{KiP{emDGNUQFG9Լu? oPwgswq k6bnO<]$֭uum.mH42S3;y|戾jÛxk4p[åK6M+OPv94t{/k[W+|]aishRm;=1/SVkDL&c g"kέ/]:i7(w&Gc+pc[4RW"EIٴHĖ,XCY֡}i":~ q&Xcx1 w)?/JH[]kxTG&cioI`ԾGڋj/s[F$\ǿ>#:MK]2V0٭0wZc;@ w;E`Z+欽]h5{-aIi#]<\fxg&/[FkF2 (_6T:nT%u6sE}Vkkm$.@@v5>j?6H5Hm5 +]PmYKN-t;FՎssFm]jW\ZH :)ٮ(>6n vw$"W]gWx4(oմGQaBMyLk]m{pxj4쾱" mh7wK?vQEIAEPEP)SD _ыYSD _ыY/p (Z] c·UZAn?*(((((((((((((((((((((((((((((((>*ȁ1k6*ȁ1k6ER_ VV3XvtQEPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@O5F-f֗O5F-fԽ(@kw5 1UjkbΊ(((((((((((((((((((((((((((((((("Ŭ"ŬڗQH|.fa[*di_h=>O=ԗ OǦA=RΓ~$?O*z5?/'IQğUѨ9?O*Γ~$⨸EyITox~t'Ej+_?:O{߉?.Q^sğU/'IQp=Γ~$?O*Wox~t'GIT\F{߉??_?:Oz5?/'IQğUѨ9?O*Γ~$⨸EyITox~t'Ej+_?:O{߉?.Q^sğU/'IQp=Γ~$?O*Wox~t'GIT\F{߉??_?:Oz5?/'IQğUѨ9?O*Γ~$⨸EyITox~t'Ej+_?:O{߉?.Q^sğU/'IQp=Γ~$?O*Wox~t'GIT\F{߉??_?:Oz5?/'IQğUѨ9?O*Γ~$⨸EyITox~t'Ej+_?:O{߉?.Q^sğU/'IQp=Γ~$?O*Wox~t'GIT\ j'Zͬ}vOIӮ1 r}+bQHIbbʣޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏOEA8/qz_S@}?Khޗ?Pc>5=ƏfpZC4O3+ cqz_Gƫ+3OU4Z[lnٙ¤"44}?Kjج?/Q+ cqz_GƳm+{&!faBPdsV>a>€,}?Khޗ?Xυ_^8mu "@<א(ƏRb&48$] 3W)kG4u-!!dws3F6>4}?Kjج?/Q+ cqz_O忙b OI0LF#rmS@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEy'ĭrHN{鴻io;^ݟῇwluORU%S|l%H|F=r+d쬣{bGLKx),#4U8o}m']^OZ7u@Z01ӊ}C:dΡȷ@~8QHX$ `_ZN]f.^kj.I~5mAHL013#9Yҵi>"5=KPյ 6-o$j-~_@CDnӏ4FFK#tpQ|=7c8iĚ:{EŐks='sTu _)[?O~(_hS]kfl50>IE*čۜfBmo>'@J`m,C%s#ĊՈ?u?Op4Бg4JI섕((((((((((((((((((((((((((((((((((((fotoxx-18.01.1/images/batch-convert.jpg0000644000175000017500000014657613222767271016413 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 386 545 0 C     C   \" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&KdUU,G$iVSyo"g(oRA"|ć#zW^7 }EΞRe)?c:mZ#~G֣X͒;{Iodn>lk.Ȳd|{{׭wB[izAmLL^E-!,[sW^tUTsp//'{~= ď- =gbc p=]'~$gW0u;_xR#i1ܼal⸏-@#|Adfl< Y[ڍT !  ;O>wߘ}k=VQ V(+è8V$񎭥4K30w(Q9`%<]Z&]]A3Nq׿h&K+ G0X G0X #]֨aXocƾ ],i#mYr7V<]mȣ6?QÝS|otRX绎{ˉI#Qüt*Agdž#oEI.W,,#Ga1"G++6.i4~+ͬ, #nW2~ ; Gww!q>ǥ^̲r~mG }x|9s~MӴKm*+mRV_[c!c._9?+92w_GM!-~}gz?Y,6 ߋLnǬ6[xkVcsdjU (Gxl{k=/ P݋mէFiCH3 {wX+5& Gg?G;f0]I5 ɼ1{?x h~/ b7t]I,D墲Y-U!20&%+ \2jwZWLEBB') 1rz>必dkSy1r`o^xlr0kմw(;X$W j] _%W㖉c Fżm2IUoYs"'̤FF@'"\'ʮ}qE|&fh]樚Vm^8W2p zqU5|/wJVjrrx/&@Ы>!1?16ݢ+mJJ7Ou[FA=oĺ'? .uxzVLԵ9ny&h$Db,C]E:trz_`bŶxď.͞DRFPB9۸Urd]F^I⇈_h=wz^^<{q-F胗berDgy޳PH'4MXh67izVè$i!kUIZQW,N\t}CIV+[U "&z f+M?N|?ax; Ly!EʲneV4Y?B=o^EMAoʞ{ ȋoȲm^8*#dz5߇}vJt-OIwY ݢhDwdI[m-u_6>$gp,#;7$ǒ+v^CV;QR0(((((((((((((((4mN75Xzu1] G.K6y% 9s=2B8Ҁ8 ~Kqm[hֵ9o#9d%wu#>RyZg;?Y_v~^Y_+;/o, !)}!Y!#τ⵵_m?V nY 6d-!,Y'"Vz<kuhIlVcI`[[ 6'HXʁ WgX74᧳Q3.2{f`=vlJ٢ ={M6zGksi q0XY&hˮ>f訥'~ :׿k;>oa2Nr8X1k5{]T/@ ZOߗ;MwSZl&?g/ iwͲ:4XX,tGR?+1^sv:e+MO 7v"4s°PKx|9f8o||VmOP# ]AN{$wc'ץ{]궚ān1o )5QE #,laXNLkK ^Z+S H&  Z?gojWts[j@ 1s( #Z\zna 7l LhpG~ ύ5K-2Kh,-5)$ pyH$A$WF"і{_{^t"yt =b>dp/N7=>9ᘵ:{Y..-U<38Z"E-4Hf,h,qY5/_ivvH5-nmL,R#d0TKN~um]lPSG}G)z]6(<60~M𿉼U#~3ޝƘ,nt͎9%by-%ۭGBoWG*ЛW~$##ҡŭÞ"(գOtI }3] yq@ <;\xFFb+ujT6-#nJzi WtM{~:a§G \EyĿ>tk߼FMO) j_-񖥩-4K֕v[IXαKW=q\oyx7ݵ&15ޓ[1I]oU'k?Z*:^,>?,m,)ớKhgMDae!yAS#"5Nhg{'B{h&kqkbPL:۲IrB=B^ < ֺyM62tfda]Cym.u5;_g } &p_ !-7Hbo M/3apͯ|<ƺ4 _BKҭpt(Â0A&?T#Ma45~~vtݷnkI)~:g\-Ρ$}ŧp7~d B,TWn0 "YocZ};|隔;u"gH&F(͸-I;mm}'n¶['kk>¿ CxNjEi5}UHGI 6f™~|b& h҈/,4hbIA* +iҟ_[ҵ% DXHcl1 6'^}Bjtx~t okMucodT(hzfѤR=HK?-A?[>ing~~\t:ᦧ7Y4^WҴ}Ny_Gy=Rd`coÒx[ŇM6z7|'uͶO T[D .R=y&-mmoޟBoWG*ЛW|8/ <wA^ ɼì0FP0mP=c%Z_O]ydt-giໟX5oVtK"vT ,nfMm~z}vzZGχg]i-n5gOI$ȥ*=??M +/n%}a x'͔^TY&I")F3R G k1Ҽ7SY]:a&y}U  j]lѫMǥ &p§G \E|ZxQ[ :Kk&> -PTTׄUE$en9 jՌӹ§G \EBoW]U9_T7+??M +9_T7+??M +9_T7+??M +9_T7+??M +9_T7+??M +9_T7+sJi;M> H$ Kj8!=_dQE QEQEQEQEQEQEQ^Yx\Ҵ*RElZ1&%FA"5b2;@E`h95he[Gug<~VJҺ z,ʬ9"rDhu Pql[\yG(ִ_T z(Ys= .Tmu6VPH/-,HHVT#H<~4vWͫx[X.l0NfB?SW,u+MR6 H0k|#GǎtBRW>RtXprpx`zo,+#oꮛvi1\otOx _52Oac:2ecaE*[n8Nl3?>!eP Ս䱬Jsk]Aԭbfu[hlc=vz{jtcKa|J'EVRꑼpE A s!ae;zZ|;K;P ƛP>I$6fi$r;< I$[7>7kۋYN4֮dG̴LTe>wk0HUYx7ivyqs$&yT)l FYb#Up؍Y7(Q &[ꊊW/,uoiǪT/<ù_˖[6]C) +-͕j:p60!2n[5|A:؁~ОV}7gF,uoiǨyB?eO .+88WI8?I> 3_[C41G$ʭ dFX~'m]u ^Am.coY">ArӀr3֪AN8b҄qFVӀP'<?/H<5b m?JL-$W8P $)}lmElmEri,x=NWׯm|&ـS,b1^koZs62 #PFAQBW@vg w$Q w$WgZϬ?Qi>߱FydO@5m;*Ku He cWMi֣J"|c}@{8g#o-{M\llAe;cM :՝}i^_[Z4̨[dxZg-kw,XH0qdG,uoiǨyB?y.i {9fk{[4"l/ x2Nq(MGv?Ӵ( m$r@&*_(V~}Iuk xAquu>.t$e%m>h_" d?: h?.?Bݿy,]G? CS q#?KtĒF $[vKnȊC!a M6ͥcPsQGsQG_O4mGQ^O ſ(# ſ(#ҸM{\A5tAw񩮭&_13 #Q2kd$F+xd9w(Iomg[麅N=/Rm,w?q\/|#&E4Mm[GM9 mY'Y0)Wxog4u[8`iG~+~}W|qua 8a|RBPA٫OYPm?^70iiq~$$Obw$x LƏN7A3}ȈǕ[g|6i X{:b:Y@#"ӯqJ: k/_c'Ng ,|E:<Α^4Rx WC jgAJtG3g}6 F2ʒ!aZ:mcQ.4j^/oc&,A@α ܨeFޮmoRX (+y>WnK#C#Fqcr6Ğ74k>&O[ZCk Z¬UDM k?_φԭ|Is>=ma#32̥ ·5|%t[4]FM&p.aTIxn.[mYݯwW}CtooYjnESڦ-.MѼzKHS@7۹O5ς4xtۯ>"{{ MmjmHndŢf h\N="PCK n/MbwIMF̦HJyspW9+͠oUw}F "@ң>CnzZǎ<'wj tFfrGxX (ʝO i2,Re'O+T/kD{]F?>ywv 8;Ib"I85C_|Q֟}k=-^M-5KXm>2\˴KX#ֹ׼k㻙GѼ;k4Iv jXC O^*Z7oHV5K}i|H$}zKKF6>Ӿ/XҮ;xvM ՠdvfbrIum}-V+q&p-,-jٍ傈w(2r@5ֿx|+^-|?Khj"ViȞL0,A!mO!u70IYv}^lużg:0lCc#ZWJ;E_U>(6On' Vitak lx;W|:e1_xw7izpܵ,o"Awƅ xC?|xzV_#:wD:e.hc\L&X'(< h|A$_s|RO}6-β!{h32io "-}VKFuϣ|H/]ѭ/h>Q-.QaImpN.#*(*ÓNGk;Y<'glY7kK(̙S.!ygm4όzg<$o_ Fy'܆H}~>)-,d߄,R#e&4(H[_g}3UN;~05-zHntMN n7ĬYv8<_Sg@nb5(n^K1+^17}߇tMs&g[Y Yl2Hn]÷h#x\-?xP|4O{kM=얐)/`3/$ZhTM'oo{ÿoCpԮ5ɴ/׾"iey>ZO H~:֥Jeii-Ҩb$W%ĕO. kб])fwkrMQZAf<#Z|WC 1' і&6im܀(kOKQߋ}Ֆ_w4vĐƸ䪌Ȫ_?&ڃ-Vx eMvMpp2+q#|=xv| h:r4m3LX!1g}Ϝ(%/|u <֣o9_OK=异=FaG) HBf蠒We=ƿ |1>c:]jko+mڬwN+_|;/^Fkd!6cYgP>FG _/j_qxRT;[GX1Zn㷎lLF]$%wrW=^?^g?"է R[o(\p0, 1!^qWOWcŷ-<=i,ZͲ_k:']F_oj~14jo]Eq&m0c`s_ X|g5<[uqK Mg\ R\ItmՒ57ez _<+≭=[뛙f<;?Tumr\Ҫ\<"#rbGU2֪ҕ-ݥoV>#@OD/c}=\~\哴>˞G=k]мE?D&}A=Rk#.mX^6Gc-8jz/E(((((((((( Ri?^_bl,~WkԿOה=${':ΉaxK Y(:$I|=|{i?i,~\[FeaTQWcoR^ŧG |ǘ!\ c?n5/\6Ɣz{,6^ygQn7,FRi-lOuocךSH3|qcFoi <-=Dž ,:r(]ZI2q!5CGQ:oη_Sga#FahU#\Mw_5i;xz=$E}XmW1%ī4 ycҔUnǾ;8=*< -@Ԍ(4s߉~eqai \LbW7zAv@:+_ľ>-ꗑ&$HHS WIw-OKǺ4dX馻 /?('fyU]sN/,-no5ͬS+Kq 9]Ñyg:c:OnJDj6+fhe}`C#9v a-C⋍m2M4[魩y5弰bh<5h!Kb/t}JV$=M#nREahQ^%Ŷ>$* v]f[ ][N $qVl⣐ F =u1lW&p[nҎ7*#s,2\yWPH9*uY"/%-Q?/v]#qjZ6Ɩ&EoBTj9 Uυ_*PԵ\[g a ‚8RId'6~oKWx:MKTӵ*tll䵞Y$$+GYQ]20aЊ.'6~oKWϗ_K^_xY\ۣ&duP񓟔dsZ~֖zO$׼3hޏqmj dۈLr26%9 _ט ͷ_ߥ|/U^y;@j{"h&dR̅>HVpqz?υ_*Nm/Xjw0\_Xhm2IjH]Y᰻#qn sJ;Nm/G'6~V籋Qvڍ)K62` 3Oi<xPQSĩ8F :sLc9ϵznj,o-}.x%RE$F #`xONGdxCi:UǍܓ׋jvYh:M1ou=I.?9 Upww 5ۋSKdm٭jo򅊮"Yzt[ o,n2F 6D']|/Tsm>M3]_xB\^|Q\]O$X#3yğVYAj5 +/.8a J\\t]LNm/G'6~j*nQ ͷ_ߥ|/UW&}cA ֥}J)P8VRBIev9 M6I:ŎO*Awh$c;7Avu ͷ_ߥ|/U>+<8qk~DM>/nݫ*|=e6mgq{v['c!' O|/Tsm> {fhEһ}I\4ps(y ͷ_ߥ|/U7|2zHKlųy$q/<}:/.G.&#hm =3|/Tsm>|ab^ZwqS'e񧇡t5,kܦ31usm>?9 UcQE|/Tsm>Ƣ ͷ_ߥ|/UEbӯY tUd \W.OҮm%a+[ôHq<Һz(u5ԟۺ KZ)_ۺ K_cMk%z%_ۺ K_`̩D1|a6i6 &y6qEW w?5[('5-Oּ;4nXCcr>"l3p+)o>xĸ4+.n5+o:'4πz^ ދIA-ta:kshK q!$N OٔRVI%xǢqSW4ĺ0 ^k7uiYPPkH[ d++g5U7w+_pVZ}oED-.O]K ,\ʎAG5|!yx6X[K;s;"ҭ^_P36ފh|/tB_?X\o-%ey*n!WxtG6O֯LOjv/LZvY~Ǯ;OΙmbWв/d`fFrORs_{wk>o[~-#mCxZR3䟲BN;Y:a^+i=+ ;/X=a?6U5RǧYikp[AIF/@M ]7Z]5$br>/R5=wt5 ]7Uu%-C.۬r2AaZLr+K`^B0Lx\ou|5|IM.H2ދeޠS#8ch~53KZV/(nz? i\n|kd>35s-ō?ee˸נxޏix.Wlmv CQ}Sˈ۔2XX&L;/{DQ#`Te#JN }~!k>Mc#ԵXͳ,= `A9c_DE^;W~? #k+:WpR#8#b:khYAxDц4 /x?x 51[_{ssBswT|oOziỨ(gm$,Uc1\;Ilma 5I=BE!)]70\—TwGr˱Z]1=:3{nOV](,|C{[÷} W1O+M=ʳ?=v WKZLVd12~yOP ϨsZχ[5;/!&@~uMiZu# Oր<~ o+:>i%Ķd0\JB IdI>Ewxޣ=&Zv(km䘤a失 p1?k L?e^ɵ[JƓUF. ːFcZ< VY|4AЧLip>-`TĻ_KQW9y!rVSZޝm{xt |_q{ *^On٠'!<8iLr %~ |-mGo ^$Z.{F:,6|/ WմT[DX~?|>t ,-#K̆a#V 8|`<k7vZ](}nLǶŰ,P7KnмrϹ+ڝO &tPI $MuqFƑơUU=[}ͿԞ]I~V0R;I"FId*z(i[@) ((((((+5+iwƛ|mK6Io0R"ma.?UXm{ƺWmr.-mbGWGMaFFsB .'֌!vnuv5/샦 ?j1g12;0TU/WK 3]X}KĨAG;q\~yEm:V$:i]iqv̞]HCaҰr+fꚖgiw-]IpfGh>Hv`g p_|}>uohzζq VO#tn) ?t/(6nxNI,Ap vdI%dc+w:OѬl:mOm1FIJ> ~{d&o=fqMvft=]/ک$Eߩ]LZQKg&l|YnzEimľ!H}&P$㶝B$T<77Jqs2j%n$[8+y!(BF|$F/ VSk+l0%RUHH^_"=m'( [z_氱t[ll4-BKǖ̈#)-دeZwkEZmݭ=,2^mk7607J]} `x cvr: ~|uv}OEHZ!*Tq Z[K [w7o|UyceqpZ+8s$P*ÿ<)_ XsEx|[-}P֢{[xXAXEv,2|W|e㿄~4ޒmƩvC"YadR.Xd'y^4om|9Ojú32ᢝ rBMn,`u~RyJ}-i?:5=eZ߈-q$m0x FT k;Uд3:mZiMK/|FcL$BORjT4ȴ]&O>E yT([.W6GA׿jbBuz)dմFAأS㮴UǍBok<"qn̓Ϻ{'ڻCDŽ e4mN1 dIILk5FB[z88a^^?e|fek<#Td#ܳɃ+#gg?]fMޭxQԧu;Ĉ`hŤc˸1;y9OmKpjqrkaoyZ+Y9x;rG5oSM̷ۙ @7DG w=J.xkNUƚ͵Yu.V;K H2sq)VN਴fȽ;1LqrR_7gؖ]࿍⮽{[Y-tKR; ʥ3ʕ|l}^eo #K{x3=е{geauR ]gW|_߇,mJ]"_ id[_Juj,f6|?{_UM-I~%GᏌmx0HSL,4KݦG<;|pIdv,J;yxgZB=o +R5,Z5YJZm7GtԿxn.3Zd~QLON| ۳&l= }xğzW+Yu;kVm|R{"(2zR+E|Ojo4T_K+ۉh[&dGu);N@s]e&m/:4fQWmRгm`s5˯ZCuv$EX?3g>o Mu;kz<>*am.;SU-[NԳ!j0:jֶG< x 1##+H\A.u#5 4jkku'=XhV w,W 0ff8Su?2{D<'Xh+t}b q)X&Y 6v3D 1cښbE?5 5zgusT[U,|3umRI3j{P`-n6]]>TI K䑶k{D[{ۖibTW=S__GAXƾmy+JM?RmB$mi@(m6V)'d$t>mq[|wZPa,2< +/cV>٫dftq}sX?ogҼ!Bvէ 3Ynu+!@$*c5}$G/>;g+F:D)An]X%V'qun@ [K`ݦ(;38ʒϱ\`,cߦڠ&=ؽ'h |!@|+|)'qwgwy㎵*Kf^6Ǫ[ǰspm+HWJӿlm֟ɧZswg5uȂhQbXay4vF&ⲩKe'n |7s'u]d.^ON>rv\7޼ ic¯ k+|~RխBvdUD(]rN3?:xOFlj`5G $2dEwp3z6 ;=k*8̖ÁW޳oFZjw1Au=/l3`}r;?cxJ櫠Ii^P%̘ N2y T9ێk?ǯi!|ALڎsj[Y D:U[1 g]sV %ޟҤ;Q^090zֹGĿ-%m59.JTPvcGq(jB/PF~%k>_xB_ҡbo'j.IO_v¾%xT U5{i5M9n"ۛf5[*xes`MT?ŏ g]/kz֗iw%c o|9+_\?+5?a7Ū 4{1M)> ^U?c fxQ:AImkwo$Fk" 1rj Vj{󏊾;-3iZ4mKKKYuHEY:գuN>(>Aqj:f'ކ9 #mv Jק{QH((((((((((&9XVPbZ7'vD5VN7C`S;. \qXՈh#E|!프 ||E_4/=|h %˜`rzW\>!hEvJU䘰+#XdPrA<u<]s;Ӭ;(t)Fh.Lğ319}W^9ZIcK{XV.[umd1)DzQִ#ZxgKD%"M2ĆBU0Us|99>ttcZض%iq2f%y"S1X̏Lr24Qpj_ wh&si;9/o. ̧' v`\'wzBxW'Ѵ`Ӛaq,M&tv(@JEz_[ЛZ_xGkOmfۿt#kǂ7yi ^kշmF+&),VXܽ/63" (ZZlODƀ|@'!K,$bn,zͼ${,O^"𛙮NVpL Y~_Sd/O8CI]+IWQTV?$oz^2 +f2PTsN$y-l ^6uA/`D YԅxxB~ έw^=}>^ayM9/ o Kr+ BmdX\l6 (1:WV<>d&i y@]`8V V$9&>gy)2e`8 ^_ ? ?$Rqk0>֟֩ŗYhL$}:GTK.҅R@ I7~1|(n5 S:AgZiKY]7$$\81T-Ǻ*HOSE[mo *g? ~xľ-~'Qo!Eoi r,ZL,8eeK\H1~|-C6'HEu]8: yOm $2>Y^|>:|֒$V- (11޵*o%}Š(PQEQEQEQEQEQEQEQEQEQEW?{ W'|SyxrLܖ?%`>q: `zwI"o5oMmtB=6}%]-kF !CFwq_o xf%'Ʒp}aEZ? 4_ᭇT$2^0dVRX2AI4|;Jg~ hx+Vsx:=nѵXFdUU (1隿ſ&-4Jk ^]=HYEX_6QZ~-eضqs뗷w l̬G,Ƥ+_?#˫ۭb[k;Sqo}5-5Dn#CqNoog|z}u Rkx? y^X$2s^,iF =C6֋cpZ<.< +®A=3Emx595uuQ=ޯupd -g$mbՉal[]:t-THDFlGV0 0h[_xkA>75),%4Ch) 1ssſ>+[x_ 6}/FMQn<wQnnMwcHڷrls.2xI#ob]T94/參-,mb#73/.F km鶿Ҋ(Q]zԵ+KwU$~097dxm\)+?sr~񿂴~5K'KvAix[i#8# t/ɣɩ$gkGFvnTAGe#Z z_x*Y)𽱽mψ('o웷[#O;y0^~_|_X4 x~XFY{%(dY@j,'ϴ?|5hvQɤXt9> 'zb+H@MYG̡nmQj6'co<]_#O7 AquWo6GuQk߳ǂxbOmɼE -䫋eYCy8+JZ7]4^XGϑ%O6pU̓\ CdaJ>\ơ.mOh6hu,-AL(?3]e w@Y)bN@zMůx};¾ᡎW2^ǴLD*y@nlM_ |\|s iWkmze& 6Tb7V-K]C|kcMf6.ۚo*EcN761揊g~1QMmkeuokooy9IJIqEwnvI蕽.zw5].˛Hfq; ,?2ka]/K]NT( $^?Z~*\xE+s*8'?I4hQME&GEVE0~пo-NYgIBHެ7>֫/W|ɯ|/qW尚fe}R 0"o-U me`ki7ӼY4RoOQ;Xeeg<$^H+"/~HK?b[+s{$;!*dvmtүhL1g,n:h#R񄸱%I4Dڛ#VMY4]$z mNKXh 8YIKH[aB0>N_^æhi7]>n\J ː:V}Y"MOOm,\/4f+$9VID[{6HS݇g _~ j 46~#=z :xY" wY .rzoi04=+qh7KNڜe'ՃYc16_ q'ox~ 2[m#_ Q^(XUW ~@%p[ww-">w"[{_3yE;Ki&}?k^/4յ}BZ9W;xV9c p\q^o?#lR u{֭j6Y=G=uo+# 2F;%~cu#|4x͎]mӳi"{H xl_ǎ /]Kuo{m`! g+ >('>'|?MuAs"wC.%P|M>x^5Wsv֭0K$DI2U:Iw3. V>ftcBKvnۙ*$IUŠ_^wTֺlo.y.Iew&UQXU?lշwq%ec 6y֡ Fp`*Š(EPEPEPEPEPEPEPEPEPEP{˙-Lv]pD%]̵WV׿r4sNoHw7V׿r[^p'uD񯇝*O]|r,d2=h?V׿r[^pҢ3n {}(պ5*(7V׿r[^pҢ3n {}(պ5*(7V׿r[^pҢ3n {}(պ5*ʸKFxc& 6]Xc0u@k9G?CwC~m1j_P=^]ƁseBBƓ}:k5 kk{\ev80 [t}/kgi"xQу+,v u@fjQu@k9ZTPo?ڷ_㕥EfjQu@k9ZUG/4UKj$*f":`Gu@k9G?jqCpɵ~S$Zў6"fjU{~ު^U&%t/lQ O _ _)+_.ZMS̶Ub$$(;Qs9@s`K!Zt}/kGE`[oi2W;d-= ]պ5*(7V׿r[^pҢ3n {}(պ5*(7V׿r[^pҢ3n {}(պ5*(7V׿r[^pҢ3n {}(պ5*(7V׿r[^pҢ3n {}(պ5*(7V׿r[^pҢ3n {}(պ5*(IJ6w #$/ EV<~)t7i̷?*߮;St_k:=Ms/3| `zc Qtar $׮өjv57p7*Z+隥YmX,&`t}kEi)|y$Ӵ]Hηh..Ele]s##&^"uD𮝭i#M\]LMGv #21yIpOm|Annu5k-jG*qqkC(pscW &ԼGiD)v 2<1Wm*I%uq^ ⯉Zn:\o VWks Nwp=aUV[ __[ڞc=qko&ӋbLPz.9贛QRY[ Ho*O2x$0du٪hݾ>B+ey0$dKUy/~ .M]_Z<3۳ tpH9Q=ex*BZ:cImO-1DQ+o?. <>=3|AWZSuX5lh8ˏ!J;$<ΛjvE,^ҼIrJ,0c8sp=kodӕ} mIMė_,r9X!qT/~oO>]^Ee4Myc;9.bU݀p@ޭOG}Wķu-~>Σ`buK i`v;[wܨ't%]׆YS,wlB-jԮSPm;"gz,QqVSᆅdrh5<@ιK5 W1;z]]]i[wx[? j<:_2v6k_]]Ag?|Sw5xg(j>K=1ttIWnCdN}8[%wk?U|cS+Ɠeic;ԮMM>go)ev$xgiZ׋5a,-ept%}YpqF=e-`׬"5jVbAq ܻr89V-~=Z-CW%ōЇ y7۳Fppxlvf&E~7|e ƏagxsUm%t]uʒpA;zv^I]_N.ޠ;w-}0mEv}XxOY>i-ė>!!] YZb 'c Sm L]Vع9$9€2ZY+likyn^hf_Kske'إKfx$ESXU[k)$0=5x7Lmk&`Zs\pÔb :r^7wl˱SsTP t׌_v>L}[M>)%_Mg ILΡr Cq+~xM'LQicd-eE^N@忊1~xcAD%ڭΗ3J\ <[ᏅZ]͇tְcqq$2q!ois,8Si$Vmw~:(MԿ5|X~KZ[QEQEQEQEQEQEQEQEQEQEQEQEQER7?JY}^A_\jZ~}4S,6#T-x;һ 䖿5δ]<-& ~(&hk3@@A'̥>+{K]մzv"6'RF m#܇$$ xKUt>>n߭G⋔ ֧i4NIK顟"zet^炵 hW^!E[j6"F"6C}mock>+n'Lc>a\w~-7]v:/,bx]nmgTu1;`*#β ߴ}=,9?6K/O&?/757V|Tv1]ERFRႰ1r#О'Asٯt)$28%@W%ΪRc`tTB_L+K@[ w$II&g3PkLfeѶzY~r5t5oNv1EAA_4Gwhޟuu ݿ/H)=JW>/O&Oi:NhڝxAn-CȨL8ɬφ[FR3!5,z ~A΅r?5oMj2!K[(O=K J:~$$}ds^w.H yT9)7a\TW?V_ jTK-DXp~u}+叄G𶻧kyu \,;+6cٷN>uiJKGX58]q''zY~r5.FzJ=T.v*G$޾y7WG96](VؒF hn>}=,9?4POr-{mfܦY48#>ٯiԩv Mz&U|'F*-)'p5ײ QgO}=,9?5D8jω^8>Y޻~N n1Ti%PO,ß|mK/O&k&=v7]:7n/~eMkS F%l*~9 j~ X#Xỻ%X׵$Dbtۢ??#"5%_ͺa 3`Oi:D[:k¼{Ji[i٘/|~<~~ kTӦ#G^;RNWujח⦝ \IxP`Ff8hԽ9$zϛPy&͠%'FR{ynzD-8CyYܻ;嘖f$y5omk|?ߝKgd.T%X05<¹R_g}gȅ4hY#? [xi s6(X9k^伵_H`4Mʛ܃i*om_Mo祗'^4Q#EAN~u<¹:Ωs7d&Xyj 1^N[cxyfFyp95Ab9Ā*dp=|_q&q}jK/O&'oM4!՛o;d~UIys۶~}KRͥ(#tXA6TԅғZ7 w nϧ'5x(nDoJoEQEQEQEQEQEQEQE R#q7/7u{=+Sl%#8;  9xcE0%@]mli'f5e\0(Gͻ+WZ57ZWxV,z2ܪF1D\jۖ( ^\,}R)cD͒ڕ;yoaU;/R֗=/ɫj2xUь4Y%#6\:mKcš̑2 vǚw":a-5A~4E =m!ky0ZWGmȖT_r[>1omo,vJ/pJ[`[%;+Qn sd".+ԃvT3j3K  3YC߈?Ɵȴ°?{b,/YݬZ@0' }8_֛?/hߕ?P°OZ d_rR!@ `c5k6[IrUطckc~Uxzy6I POZM\V14oviq5>YQ 9<ٷ]V~ "|$|=$O_k7Qs(4^N3x%a͢jLo{3 Z0TNkTlnc0\FIIbEe$p@G^}{$fĕё2z$ UXj=iCW˟ k 4ϰ5Y=.RpdnE9C߈?ƟȵoKs-'xumc,*O1 X,]&?yω<?:5 i0i}ᯇzeg'&>gl1^5A~4EC߈?Ɵȴ+_4B#ÝWfkeavQJ\7ql1΋9?3%b(_ hjnfXU$c$6OALfK~B~ ZY`]oR"ѿ_KS??@h[?oR"*jFGu L.7(`ؚH(((([M*5-b5 K-:H It>Zs]UpS?-BSQ젆]7NK%bJ'4?[xCt (۳ƅY} ?>5n5i:߆uQeӒI,FW۱ |49*i'g' _('bkW_ළ6n5 'i2kibInsi=m ]r¿ 4vk{B_46Ҡ iP@ z^O mdž|Go`z䶰fn$CJ%x|bF}kP %Sgc6lFeڄ+xSo٧\-8C{ %ȗp̠28{zjm"5hmORKtd8d!DB#9o~к7u/L]\SYG#X5(P)-3UF*ヌ~k*{ R!-AX]VIdE# HIpgK߇>!/.9sgY5i~vWW`An?n>5;?FG#4KXM bW`Ic)~ZNJ ~Hү!?[}ġ %8#*e`6z׾xPIaa;?xRS4{D%HgI #<(kZ~_֟M+m]-^#N:}f[x>S ) 1ih8a@pۂ(n>ׇ<4/XG"qvPA#|Ԭ|9o&l3L}覾QmaI*:z*ѭѯt.l^VsΜ#眎rî?w#WBm[,8a:oh!vhvɿP<6$4uC WI]_k oAxb#+&Qoq:6%<=ysGhAy즂8T%5Xf1PW> %Lz2H0rT6p28ѿ.KKOGؿ;~kt{-5MR0ڢyF c,8GUOƣi^0uW6M77[E挬$U} ]cykmCR綴1Mj.dyJu9AdWXѴt& ذR|wo[^bYb-"kts+_.=\m<+7_thRS&k w47O*WpZƫOD޹]M~oo70nY<5zտ4xÚn-;49:‘oYL)2C/OjXb>y$m/ۚQ [}>8k7oGi"H't} N]JF1sDPHݛmŽJx]R]O kfm~Ԋg1,2:;//zns}5Ң$I Z)bwEdBdps+̧}uW?fkq/E]GPŪ}ELIy.@ HcV?gDY|;o<:>]_B g 4&u*N_h߀sk/m#YΥkK[as-7d Az S45&OEDaA5ܵkx@Ӯ@4 (b?of*7vY~?j:VgQuۏih]y澿8Y kF rf d~3dGGS:x>K<7w!YGxz|G>kiVVi[{ Ynrӌ,_5|~>?5 ˠ%ŤR#ʫ'?!Iau ~?_E/Z^s>EdI{ @wEjֽxG[Uu4GkYn a  e>N -o _ų(IO( i|9x+[_o%J;4)/;j7q/;׃<=;[75 34ZEC8$z%#Eum^"u~XmѬ[åy""TJN\(;Blv3$/O*5:VsǥA[{H..m-+qify+_BI x5tC{ gwj+ y/G` |dmEK?~.n]RX`xlB6\i I/߮6 ׭tf8+C9-#9#.-7/wlz4X{r3*9yl`mۜy?f(ty/5 ~\T^] i9J b;+^OO'>?Ki7^jӴۭJ wN٭euC;NJxfþ+??Z}VS5\f&7>`\տf{seuj*U|5ŧyȎEmB2 sGiX}Z _ u5 \0ۻZ̰2ZijC64i[?ԖW_@=3ElMNwy:WD s9]kfaѦKI$fxə2Qr:= (SLj|KXkm֣c[ 6!Ot:W3 ^0aх"JЩT-&JI=4-̏>7kNӮ.5+CfA " v?7'<=f^]l|9&us,PG ;tHRLϻ|sq7^*\鯮X25~ VfÉA5=,4q<2Yc*ez nl[oXz o?[uMRW/>)CnoȪHIt BK&yd=7yhs m?%Tg]#_G$Gl!?ƫ[g>z o?Ϟ[5@%?+e 4KVj%Q m?%Tr {LbQV8TIԒ}5~iծʀ3Eum%=N8U(((((((((((((((((((((((/NyU3X3ۥyh[SKJ爧~m8\$DG9U7 kwUgy$UFI?f` +῁VZܚomp,5mr Kj3D*v(?^]' zLvQRA FbwaAe` #>koMN[فD־y^>v kkZݮbdC7i^4=Ɖ&\ሢih`As9\;4-OOÞmuS_>owy{QLH6@'N|w [-{LӤ Z%qp:g5mxcK].4f^\ʪ+?2}ĺVٞ0ņ6rA%ś@@ _~!}/}%Ή 6漴z*cDcq8i-ѽ66nnsjkx Z~$-CZvf IFx+BY>)Y4!׏ҋh+\/RL>ȣIZÓ\$_ k/[7sGkLаT$s"K.[Qi뫏1y'Nky_o ӼIl-[Z˥G2'eq7{ ?ݯaf71i^Ʒ/[qzԖ_|5ji!Ү)gU$3 PkWwQb#i+ ]mm2;kX"gyo- ɏLΗWa%ӵhҼmזZnjGVq$$|ꤕ:&WEpk kǨ_V_=]}?+_M? jW:ɦ6q,nϽϵuP~w5.YKA4HQ egR_V_=]}?+_M~!ZmwPЭ}6zk @-uM 8sO6먢fotoxx-18.01.1/images/fix-perspective.jpg0000644000175000017500000006743713222767271016767 0ustar micomicoJFIFExifMM*V^(if%02310100Fotoxx:trim_rotate|resize|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 600 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?QNH)EaNӅ74u-74fJ 34f4֕ehX4f=uOӚxWኗ8(7 Hǫ+&-#SWBv5Dk̮ iː.g"+&6~hz9"[ךzʧ1u8 8+ħ.~mCi?κχ:ަ=dptqJRWa ;&z2i-iVՆb:O#QאW´K'G;H½{ß>AZi>6jsX=TfmEsa'(o#ߋ\jpϕ?3LW#EJ 88IrJ*miw޶$3]MgPwu~=:,?f }o qމFdd*v]mo.8gPZx8⫚:r嶦Pݴ?ߧb?@'ݲyAqޘcĦKoAWly|Kuzua\ke-׮oҬ} U# &by].TݴttIgnPzP1ZӠ-EQ;fK9fX?:ُJk` {W+O/ BY\N5ԩ.XFYZ*b\& |+f֖}F911A<7Xʕ%|CztKӐ}d?Au6"=, qX-Tl:)ӣSE%~q8;֛k>(gaS~?yZ!n;_A;Im\ɞY 2ѓY5aHÚ_TG;O>'0JO_OI[҂32+mq־w@sO^E[OֹSPjǫļD\%'R;v [6IisXs>w/|cgC^[ƣ.{6v[ $=̮f2[O+V5ϏbE1v 9CKv{@e_ÚXG߅xķ1!>p@z`}W-Ffю Ԉ|\cޱ^QbpU֋E̅-,O l*˅5gJP]Rl玟֬Mo#Ű0d$qW|Rjҩ ..(8?һ/j\=. u(pb7lWO-ҹXۓ=VIm֕n%Rt~N? xW#?boA5^=%l:?>4k% dFN{מ]\Wۅ`<:_[ܳ֕?3aOx #c62}೷'ʔqJ%= 7cs0F <J(݈1=⹏Ff#c?+׮XaS~lgλ0g+2n!!IrNq5hp0w`6DN {{gHOY| / 2테F$';mY$*kRf#:IF:cQsصlwXW[ٷ"WW~ i4yXB[Kܱm4dហgߦ=u\y0J|WS1Rm?fID fndzg`YTXC-1[q(9݇uKcU;~ڽ]8+ vΤ.v<Q"ZXOl (ȩg\#bDmIr X:_,v;4LZgkˋd<ͻ~4{B Aےsz عx1+! f/[ͻ6Jf7j)Y.>2OAZb>b}QXLkoMsy! $5q#V8-sULl ((4튓cU:<늱 \ajvt\ pA['yQci6ӎoƺ]MQr1x$1ZG8˱i/V8e,y?oiP3Qpqm30+;;k{NUb9,7qWs(إ}6aj0zѩ9}\GFKiȺܩ$}*O֫4Z7$+u*Gjn8coJG2 d N|օ> |,{®<)e^#^'xC98}M{w }Fq_>%VȦ-d^Gk&[Rǡ ovo:|˰8P=J϶HVXXuJ;Oߋ=:x~lUyF#:*m>+Q4$Gi'a٭-)G@ajyaqM`G*-V++6rBMbՑT$r;Z=6Fbi'"[2kw3xqҵ+ɪI?xֱV{T3;k7 \0>JԛJ <|>Sb?k  ?Ӭ% ^Տ7wcҬ-wdq`xP+[hlQcO+-R@wD0̄0<ETbR,g̙}@ ,B) [j&K8,b+z@[>«jd N{z*<W4փ&.DዮsU<*91'\sqj]>}:u5%Z~՞kҵ%Nn0e( = C^38'^_^/!;mj®L?45 M6܎\g-sJ w dcp =xr9˱ɥ1_Es2ߜӥ!>(Eۑ+Ge o@ԡF -JzV0gc\OGQWygK"B<{ט_B4\}0H%I?SSJv2jtQ/R>E*rd{g9xx3o& Oҵ{'dh[ .A<bD11*?Z֚0]1=^;[() }Zչ`m\5Vg4r`Ⱇs qfH-\:dj p3Xҗ2TU=^Hm?«FE  v9\^ȕp }]/X>l%hɬwO*F>zT_⥾. ޭ͔bۺ7;+)}p?.Mx;$%Nܨ{"mWLbMYouە|Gm$車&" ji\-8JcڍEcޣ ƸяN6i <qjEq&2#X;W\$sRһMFZG ϽE>%L(ϵA1)pI&4 ?JcéMvg\3{>h;M8m^GGqwBI着 Tj7GdݎvRM9'C{IBa^ev댚ͿKGh'1rg~5ڢG.'W M>b4)6R"4ʣؐG5E(a8"YNz]TJ$R˵Fk 3PW2; C[`Sƹў:gt3}rb^9|yje0cQ J{Qÿ K{5׶wAWC׍?:O!:?V~$k^*D}@^g\qfV WKJ329kdPqxg *yvQkJǩAP"h֤"_}[X %9lzu!b{n_ЍgMXR=N8+eUv'\|:[<_ 93HwU*;rܮ05vb_H-F z _궈%`p9ڔ9gX#02qm5:&Az0Wu ]`<"B8}EvSi7\P,mL}5 F<7i\#F&[1OJxrGqbc)\+>Zʽ2k^OYWO6;!* It'k׾[Gq]_dTI2ls\οČdSM&gR-ֺ8{Q;*Ckg|~tIrT9ȿoovs0¨!k&.u_ňZg@$ǥC Jrߌ/q\7eWO`}+Ebe sڜm3bIK}~G}n?2F$V[f;ׅ`yZ3\3yV֩&E%=xv)db]淸<N3N<{Vi$K.4۫bF1;}Syy .dQݑ<#:[LZE]_/G+p<q:e>0%/\ȊM7bH=W-ܕcG^:%&f[R. qMg[wTޔ;1ӕ)_[;ƶ\ xxT"X$I#O_=M=u;8uOn`T֢ӿ/n=2QJ*:.R'DV;؏jo.$n랶y>PxS X٤qsrHzrzuPY#QcV춆vWG$ݍ%B-&wR irj9B3}3QFHRF= m%$6f/'^$>lHLbZ^qy&͞Q]/3&u c15j*9\;P>/",t$~ 8#'r6:U :e#^)X*:½NGR TCq19Oݯ>OUTu~^5K'{uHWKhj`s?s7U8VVo 3 6oA0v%nu + n XiVۆz}qwrvb<ʋ&r6mAtY+[]پBOUxvMRv,By^epA,j[!@%Y!U^=kkrG=F97{gr"z sTl4%팍q!,xVo` 㚃NomvFq=tePAAK2ѝ&H WxY: S$uEfiָ*En[TIcۧ]oy95Z09EP2;HszQ.&JO"#\= cpQuJؒkݾc_5~Þ R=#X$s^OYzf^SuCs݈w$gUda,Q)/g$!jӐ%u_^l:nr ~6kЮfb}x^{%<=k퍜T{WQާc|R2 T7Jv ֹ;h[ҵ_ +õŤ]6ܞ銩EI+Nr uoES w$}r4˹ d}=kc8~lҴ1J]88+TMM3o.a=d _[hA*Z F`3kn#GrRÝ-qJM$vщ^V.KY EdO~31!}ֶve?',8=멱;*7ecU$2;zM3Oz>v⺤5Vw@r)2DSngh^q"H$zfXѦ.SZIj?֢c(x%I>°&\N++\'8xD?^٤dfS@>ʢRe'{[s;O 08~ '?figv3ﮕumn#JN"\p$EgVJPzhQzr%Wjzsh}E0=_¼/)?TZ|LLnkWR-j_4Ӌ/e|9j匍8%w9R3YIIKh?Msϼ5P$@#r th պfYH )N.L[OnqszijqZVBG\w' =`>bOJ–̗9:qZ4{mʮc½ - t;$iA'Vor2ֶM2 :{sV̀7zɄV: XiULgZn1>3 9O7VKowrZW~IU IRzȮ6SI&ga3mْ@=־_ze[&0v)=Shs21B;hmR4=gI9?m-#DU׬$qQZ}? xW]5_nz]ͣT38D-JJuc"u6 /ˌv3&`~ԃ^v yHeaJoqgu) ꣰inE/ }+ާGC͛>3zV't-b}~o%EH`(.w+V._> R*Տ+& @ÅݝzzV#ʹo¼$mxo2& Q^&i.YS-c Cn0G!6$! \52#2dWђк+8==ygt8λ؏$CS͕y:5͗s7VYԐ0sp:~4 PY9if.ݺ~W,4}M{"j-OugtƦWIa;Vrl?Y*<ڣ;J..<~F1.U^wN,Xϥixj%U8ڻOx ]߁Q->ua=SݍyjmlZk\k@O(]/B*8Qn? X|FѸ{҅ 3UDD:1UL" dU)u9xJ]F8m!u:0kGߧy#zÏ?מP.H^EN?Gu9\+:QփtMV R=qBZ >[>!V8#{ZHnǰ:epH6gsb*rF[iQ5SS"xc?{*[Mf-$>;uk6j^>W] +b;b{foP%f)=OzҰho.w/ R樜M#WOc.EeL {W_O :eob5P;>\q}~fmOX{x᷏bjG=f9mCa+a-NG~ڹZyqu!0[xbO?6:¤ҼAl.ȏs[plVƇneQY~;:%(=n:.+, п:sn l9Ii5;Xt-C͆$kIX~LOoJBʉ2 ~]gl,ѧd"$$\?ƵD+]r$i/"fQ-qCl G=p=himdG^~GF?ھ49nGKEx{ACׄ|`8t_ )#L'S5>$m彌A=7v8EuZS-ܟzȺrsz봫䀳HrV,RFzb6$'p;wzV ۪yb>guus*% ͎+{-r_3Vf85Em9ѳG%Kr0 *ޢܩ__s\w[JuHUu&(@)t^t@KkeHk + idUQGvچ$\ gY'V 5 溊ŝxJ8°.L〽uFvMuqrʾ?_\l}s]M+s unޑgjMf̗LN!>~=`mYzi&[ۙC&y'QJDMʥ&U_ζ촻{C^hLhWƀclkG֭;"v~?SWV<ښrWda"Fx?פ:rGy*=ϯ޽wx6yeIUMWQV9#>>w}S;fqC|S u6jGkHX-,gJ7t1Smkoow XVVk R 8?. u7;eRd[oY\,M,>vN Z+;S0r]C #+sLCIj"*WY w-1ȍG<w)F 2Z/78xl ב#{ [m,%,8 c4\k7fgfy<>կ$[č .BiuzO ȍ=C^k xNwK0G^&q۸9]jq^ DL?#Ϲ;f$dž Lij9?~un|2Y89^*IÙ.HhAwn1PG]Mm%+!0L"? {v+;|͐3֮+^)1 shM_4ynQBk63}J3'IRqSϥr+9wxmVV A{*)i;#Ч5k3v=Þ1hk\ulz5i[^fܴ~o*jqeݢ5ks܎(5xACׁg49?:2_\^&Xv5_V\\~˰I`1Rjoß^9i!g鼸J҄Եg )-'u0n9RUbVH^g.m9E L֬:Ea$*ؐODy|7F[ Fm=w,73rZm˻R]\lWxv`}5(=XX[4}I5SeF}8miVƐ>OTZ$6dto0|3I <HC>q+'ҩ\_56w{xBL֒Ʈ7lO5z4-e8yNHl%Tjvs]JQ69li_X۩嵇|,rIyknU[+é #>B}w V//<3LJ ;QQ*Q8ksh{mнCF" HNхciLsj5ǚ !vOau vf$U#\F5!awE7B ˸! `~kjь6\V<`-!$$nI# HO_nfi^Li,aza52We,⽯*hU]ҩ]\5N?Ү[1+d0K}U_m$˨0V1? OX)εxc:]y=&km677dž9uH#>[N[5&XngAx6 cݣ_GKZQKC7t {x^X$nˎ8Z<7-\ yP8sJTRyX,#=]%YcxPȈHaƦ&kCsZ|JztvuNI~=(I2}+׼5guo.xf#2!"$.aQ= ]-K[A]f=kO?v&oVp<'(SP RrBdzҲ#E.ݳ'nI[sXD= J}Mjd'K5/27_}^oHO!_}^ pgܦ?筹=%@sk;`f kxpVa=J1̊Ӿ$ZNUIտRGY K_^[I⣹o|O!_cx38=3񯳧?cפֿB[Awi5 A*>HeKHs;Eҽ Q9[$v1*'Vks+X[GJvqJui8siZFѼ:WLd, g=p? j03?,@@:^ev#cf3#s}X ipڢ[nG\c)u5^ǟZh7P><2.F7āQ$Ӗg`XN/$XƓ6(X hdZ>/zJwWN0qKD*t'5{ʮOMȹzk\"k+VWMMU.~ Y5VҵJXbO)cANI8ז[js.??һ$jڅgo_`V FtψӸz}uQ%'+j3&70Come,F0aӏ_Z V^!M פ뷖:uąH?^Q6?W'˯U`c)Mw=r 2PeQBr=zeӬ Ą-0I#V >+yvIA D^2sWg);ryH HAJ')h}I%ϿVè15q|t;cUrN~ QsW={BVzоxBǡSr:ZJZ?}? x7Ńtb?:/Avi;NCjzlκ1SohGdmV0kBmc¶y^=ֈKCJ}:ՠvA-0IktZH>Q;VOSOv1:}+^y/us$HqdId⬑( ݎ}4ٯ&g+AP-["~7aϷuӪEB[԰f67 b\D`Xc [_&`Lt?Ub๵N&E>SLcb}`"('c+;>i[ [ 9v.HuU{_]&Ih<&1~ia5pry4jL^Tۛezڷ;{+YUI^O\zGuznDHS\n'M1˱ m$Re'Кn,mLdס@^_Pjm'$ƽC~?UA{3cU:7Ҭ1wA+qG+6 )+o+tYFfx`sPkS]yl9Px>Ơ4f?4t뢗ݚE!vI1b)о~|Բ%i"+C}bxwPOZ_jVX4`uvVVp[N{S\/ q:ŋw|`}Z$FmuQtڈ?vNtZ*$XV40%B`A+#Oo/P#|Wi-ɶkrz:WViͥIT{l;RylI0Gc%j7Er$}hڨg /`{qҩ]عq#|º)hѥ/}lb]?[o]v=KI[/A ug~}? |ĻO'\~$jsOGiIk1c<ܷ&bB/8.͞O߽w J\FkOm3ꥭ+|Op9=+^k^"K`t>ir׵tjK![K%rMg3J"4O'Mƻ5R^&v:]>`}dC!޹j $CL00\' ׬dp _l?μ..2Gl C+ JmmȦ{~Rwob`_^6o\X:>r0Gqӧy֙l/E@HYLoOhFܹ}NGfMlUIFͳg,$IQQyL ȏ!mHF#8U'@gxcM_Rٔ.s[!9۞ZCf`B@U`N#RC)Y&Ƈ+ܢyT#1lvg^#ANGYǚn3ue]YXLG#)d OoI5rzt$8mBRpF(,Ufʋvbp>WS1=[TI v0OJ u'6cf!g܀91ڵ+OYBĠ9Uu=@ k'a;{wvJn34kW&[݌x!FeNǥfiM^WHׯ^6*3щ#oWIJ'pg=o­hX̬uK n?>lt=Ԗ#|D2xU=[ #NsXL&%SF$g*W=O~u)p{xԚ襄R܉ԦW$VH]P9k{Bg`oߙPޛ2]3Cv9=Xw%Bcm s]TKD_گ<5UV-aGQ]s}fFH܃N3V׈VY- 4'I[s䗵r ga`;>5Jywuk`E,2y*汩E1ک LbLbHK6CSt+1O_֡U=@5fުw웶1`L֥(RT85]fl piNv3gBTْ&]*nRԃ'ROg"X׬x., ^ ϤuPzxFQK֤=i עjyŅEɫT'auic M"Mv5EZ[;c$1*Bk?P< o3=+F-sry35`ÅZ JK؂1onz62[`w CUk&F,:gRsM[:keu,jh$lbN1湽f6cY I#k$qyv[дi"i"K}rr^f/(&*imK&$Q:^{I7K۠in$'Ώ,3d%i)1ORIZg,.)!@)"I'NQ]nԛv_8<=- 5³|U^qrD^ls: X(1?Ĺ&Y^Tl7SV3u֐6qk޾ȵפ|npWk/xnӤtVGK5"n֧W+Jr{Q*?,+UE<->H]L4(>p|+g.?v Z^3.~isǻihswʽ`ǥ/gُuG|8s}@}1k:gx%_C57P~kY/1KK^=f~sf͎5"m0`DwM{PGP :%Y2[HKN2kj\)m~X?m$6P)6hN$ytyYjrһXH0LL܏Xj'wV]{&Q>9b9ls5qA>E?ʳo`aq'{YBj,8OW&🉠eMmޓD?Jӑwh8O|>rtHer:+sWGc;(=+s{A]5ƏizB]5ƏeOXTѴ~p2Leqq.XYl*A< # '=o ;~]A*Z*8㚚[W8qn=?ʶHɁ'' T4yyA[@(軉`z<} 3A3[:v13Ygzп? yYkj|<.Y&Ίqrᶷ)(L7>\dz*|?mI X@lCSjbw<ާ[멦Z 8_R<"fGk] Y?4 OWxIE}PSbNN*JB?oiO]74KXv胁STN y>M^y*DrA&$1u8# OEaYiB.jgZ3_SVZxso0 QIu=RD\/xEysOF+˟V-r'*8&,1¯\II.sܨ?ν?A$1>_WMaM=VW-%ɓ HA XFgNkOq!<൬CN 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?O# לxG>jjoZפD傹0BFx=EzA_|]d.#MknrZ.u,ͣJ59RUIvIB4E qw'DιUF7$aCpǰ<ť7KgxchCuFp3DqL#grPJ{2=MQź%жW^6+sccV^`t KHIc \ikWfD#t;@W?چ{ gZ0=Zٝ V"N,I=KAnUM>۶Fi9si`(ݎY-ZAN (Q@Q@Q@Q(FZ)7QMnuhFZ)7QMnuhFZ)7QMnuhFZ)7QMnPO#ھz񾝪ߋ۝:<_ZijGP. k5UpU /Zi`w&@#\y Y]"-jm"ķRIj?f Pk?R:ǝ~I|-BţKo-~@}xt}xts_ }п1h[C_l??l??.v4t/ Z?᤾bGO) % i/>_Ô[CG4t/ Zlt}\9Oi/>_I|-Bů5{2CpCNˏzTNm'/Gmb#$Oi/>_I|-BůٴZ]:+;vO] 2H 8ÖM4s?_᤾b % "-Ὰ]NѲ4;U\IbnioiYJ9E8u8$qG;SKo-_ }п1kݿ1ǩ*y^jXϥٲ-Q3E N3֗a~I|-BţKo-~@ ?{E'_9Oi/>_I|-BůΏΎv4t/ Z?᤾bGO) % i/>_! [:gKZխ(oLO bv?Y᤾b % !/vgt%SЏ󊹢׉: Jpn.p Z9~I|-BţKo-~GkVEXC |Șs@дS4gHǫڦUT4Fδ)p[C]O> xoQI'b8βmҿK~5,uɛT z|uA!(c<ÃSK wF%l,+Ep{WxívIu{Kh.KKX$m$C*Npd+±褏I<_1|/+v; >}:ފV"}z3ɸo X 으PyG'C5O>ѭkFEJx&E43w% +<3ThR7cΗY4+NIш>!e,ڪpMg}2Hv&-d5gوl^INmK.KĚ^B[\kZe,+{msd-Io"DeRQ<;mKS]7ѕ f?gm(>u{.m$evGJ\B,2nerWkX7GZZ0z6]MF&Uڃ2& p7_+"xkRJd:5: AE$ȓJp(F0?j޻v j~&o Mt|),PIc_2Ou;M_Z ^'մOHIybeg{dc=}Ou?f#C1xO&C˫~#tngSaslh+/=Kj_lJT=F5Kϡ(xk@Dwe׎!CY֒v0t /qxoSl9p#MMF.b٘pl"8^1y|r|bF|Ǟ4˽fPpwҰʼnjRezGGZ>?ӱ&ڇj֏~X.l\C9GZ{jpRk|-#ׇ#*!p0;_^U;*z?4zm~2CEi2ǘLx޼Rs5Ļ#׮+o1- })j:>Teykr]C Cn|,ՒЉo$Van1^Mjmȼ/Z%ٸdnuV⼼ h)QxVMM5\.o ih.OD'+0:c(8gUo 4%E9~SÓwVk^t}]|9w:E6GۙVAlnW̾!|-  (nԷWyufgXD}23P2vKֻa⋟i:/x嶎->DDCkQ;`*xA=8ҿhs@:^4,Sҁ.o!F#qyk Qmi4z;^K ٞ8=(j1&Px?uKNH}\koju;<^Z-+G LKD $un?'x-}؉L^Q8]cI"<.shV}|Ju|Siׂ4"KuᾷDT PRU񶣪znkkV:(lL_oa-n4kqctWͶ)9#bUGtuR VMAkĂXSP0KxO573¿4ƽ@fw,1|,#L[hbWBF|רzsG~Б-41|+#L[k:K@<g gG34ƽChMF evV#,w̡=]wiKu!-kxzhMo4M Y%`UL+/+m*?xoQEaմCmu wx0 NO{$)W_PE5|-:='Pt 2G`#KVn;1KL,?g|.Y)Afz1"OֲM`ѡ 7<`⽎ <WnmItVqGk{ao5]>c K2Ԁ,zѭZƕ⏈WZ蹴$ҡ9VJIE*YUsP+;~ʺn.$ВnZXYNaV.Ac4G4_QFx?6.aq 5x̫sn 1?=^sk֫}մk-$lw)R8 4]b_FԴي$ 4w]UrAn9E[ bH4H\3BX|Yf> Es^O=vL=*C8:5GO`߾`Cn+7+x;š !]Os0[;*&=iV3?{1xOZW'G |7{_b&H㚩j1iZy}kwuX%Ω*9`AץQ_k$1Z:k^Zm@ܧ8?}B+}J8tqI ,?i] j7^SigYTT0SC`>l}kgƽFhaėOi%ͼÃb;+?5 x{A|=&h7GYf >R0A|8wWT5 oYfD84.;("U$ 3;Kc1B5F{@[p|QmG:_OX<^`ϥ FXK jk3Ggk$nm-oKsqmqP >!<;xjZF%;(Y&}Q;y 2s~7]p XU.IfN/̤Eq:UGVG_iXO= ix{S4bf!џ22Y3y({U1&MAEYcH[h A,98Ɨ^zwZяoŒ͒q^eG}<7_IxGLuó%"̒ٸ2dPo7V>_)iZbAEP0(((([Idžo-$v'"yߏ|};ޭ| ccjH835ோҼ+q}̈M4vn Gg'|wk ^ՍT򢾚g$d.#&4_0S}'5|C#_wŽh(u-#R2rct}J|_['_𕾓㖶BIh%ma+[afJ8h|*㯆?CȬw n!Y|01t R9#Ze݋oyڕr=0 +.Z<'~IlԶ]šRܶt-2k4n=؛]aG-gwcn9{6KePVZ.ՌK%ÃSlX8@[kH47+ِ}nKt9_ͦu=V}:}'}yq,VLG$0`|A'~(_.oy2OBLH!VTD/|;hzK]ѼaS5s u@0HLf'ߴ j>&?Ē-e4cXmf^++$[[Mؠc֔`iaV-Q@Š((((֊QE0 E(($H]$c1΀)}4;-2 [&6Ṙ2)[Zp{*=n~m熫"n<5_e?Qc?i,)GF>|:Ӽ!c⫿x~WRVP-!N-qkѼyxlL4GA\jVI%ԙ־|𶹧ҷ>=Դ˟ Xà<:Yj1yI[@Ŷ;x_Oj?] #;'|<*G_9#!~%?tMCP|_ol%-ėv.zV$!,95tZm}JO\jфyafI]-]WJ1Ex'k }a>;X};6?%J[`FA3sICtu_f2\g8SMi.d:"Цm;̟mf-$c[i9sĿFMb</kpD4A$6i`Ke}i ]QM$_[$N74.qIUuh\N*BG R }[ @5:O.e47[캶 E$ A Aaxƭ4GNѠ[3XLq͛v`cČ6mSx;Vom=\os5@l3~kZe&WPV(k?mw,鐤NdWF+FtrIYwG_{Z}+ ;u\""v 4TIMfڛ;3P)i%QگJ5 ɩh0 _Jת1 @:7څ-͵ql-:ĭK^ou6d$ؖf]$^3rGPZǟft_1 i Vq71nd1HV_gQ _L}$ݣM S! ZWv%}W㇅ xf\UHsge5Ğd]nv`Gh0Ni$ŦCjm7JKn.?.dpN&}SYbxWm_>a/$ҸQgv0 x݅/xN𝦗ycy$/Meг_\#ɑ$;bO!QBrKMX]x{Wyc:]q$pܩⴿn|iU`懣C [O[i7(2_dC1r\oU5a"6>6m?|m\SQύ_Q`)cim~W(U(+cim~W*X ߕ ?+r,?m?|mߕ E6>6m?|m\lx5<-:rgGl"2\+݀%'nXh^}i奬*Dm lT#&<[EpH5>ۻ{+m.1heA3new0<4mԗKm-_EӁZiEoG2~n8o^8t7YM6ɈY}1,k[YGooI%Kh^fH,Fp2@Z"ڏ}K#7z Ɔ_~]w]ݍȻpDpHh袊c ( ( ( (8#֎ڢ@W*GԱ*?SߖO<@=)Qodך3ҫ}5'yO~ڀ,@ ASߦA?jGcv< ;A?O7jƕmKN/t}f)%- :@UO#V(m @Ə1]$WԒI=5dj?Wy~.c+OuᘿΕay&y|^C<ʮh/*?&:o7G&t o7TWy~sߦ ڌ 'O[ӿ2sh5^]V]V9Q @? @?sߦ_?j @? @?S߶E&Zo7G&Zo7TWy~sߦ k 9k 9Q_?j?Wy~/e+Ote+OuGE?yO~ڀ/eto^ߟSߦ4hsߖ^j:޵ZZSF->*͆q|G id7.ѻ5GNdd╁h~wx@fueKeVFY-yvF=LnY2ƺ>z]i5~(MK{QI4rcnVfK';ny j۹1\>&> >ι}jo^M"6DI1eER1sZ>Mαi>3CmD6ik3eA|Y_ㄒ_izM-|!sqsۺyZJ!FDFN$~y#=Z-nE Sȩ EQEQEQEpGTQEQEQEQEQEQEUkHr$I N/<ƣ@q'۳'א~_jV6tiZ~5LJ9iӽơc2]W(n^(7GceFlMSi!G 4/ %Ipӿjq:7<:cK54.yrX{ϬX^鑧J Bhp 8 jɵ?.!%5ePONeuOa;[Za6ϳ\׈if/Pr`: 9j8l|vz+D}ˋ~۰2l\yk6`]D6b>_ЉezZö%s}5x^keJBCC9Lsa\{vMfOc\_'מ^|ogt^Eqh%,LU~AҴRT+,ee0S2~ ~w}iWR9mA*q:kѾ.xW"mEFw-ew*:Fߩk$t hqb]Z< F~OOJx?al̟^q'Z[_i+hځA{ !̱27z"֏q^Y\^GpSR1$ʐNvBT7cyu ~;x PӵWmvhInʰR>8CsE51bf{g }`yDc*\nH@>[m/:fVj"]K5#eB$o ״{ڵdž.mczZv][S\*g(W^Kc lHɸQs@ \Y(ycrYT+`7CG-cW?x6@/ofx[Q9Ha`aT*}O­kDsMW=<فx[i]Fhcv schv} :ZE(((=h((((((oK=)ۀ }?3bfq`9.C|mC.9.<h >_~gChtq]۰29erE{/qrW5oeG7%[DdK۹|0}IՁ+k+:>åhoRVSGʘIUX+\߈>c\hKIe<1,S 8f9IWԟzmh\wI3NqnҫxA-y~x5A+?$jq`,Z2#)5ˁWh~F1{V:2Si6|OCڊ(6JP֊)@;p}84]6"HAȎp8E2Ka@ P}1RE)QEQEQEI48QmbUQO?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/O?m?/ 2ˆ#rpq(fotoxx-18.01.1/images/edit.png0000644000175000017500000002007213222767271014563 0ustar micomicoPNG  IHDR@@iqzTXtRaw profile type exifxڍF3 AV)Ν7o%H^g0Ͽ/ܬ>QK+%Y&{_??|}>@RHNz':fH@x4i(6w{m';@%,EF˿c{x|@? ~<;z~gͿ~+/upǃ2Y` X+}'5/I8ػ+_WN`9,nK98[{_[7W(ݢ~̛kڼd fy/~E+mܾm=L^@weGLdeKF?/@4A|cT8 p=4:h86ɍ^wa*'E8#S̑]MeL&,Lt$Mj7pZ.b1K|#M=] I|&5nt~r{%R8HAt62ټlѡ6Z"aV~I3;`@0ѻPf/ ie2;'-pK*;nR#zx+qeC!'N!飲Z ~rMun;RMzmv19rNָB;\w`n]ɣ"fGWz쇆Ѐ1LHH/Qó/H&SQpxsUi{ i{%BfA<Ps8Peޥ,yaaWƄBAReu0xZ4~X9wENWNK>(BEoemRB#([QqJL`xcC @vp.ģ9ŶԒ5!eB~KmnZ@z f"/>͇ic̀/0:FAsBud4X3<,V(\w+,faQ/Yp5\p+ta ~H'[Y(x"H|= Fw#], IB:KRqsBIT|d IDATx՛Y]}}{ݷ`v`A C "Ų(ʖdV*XPGOUr"Hr\(y +vlGd %(@l 3wtw̭/sNs2>>߿BF"euuUٔ-5?~,*7䟓+PW|\~쫯8Q(%Mu gO,f:Q$$. 4p`_]~B`l[4P%gԄoDLSMPt_5_5v< E -ˊB@-ˊp=S@?ayD)mS1"7Ы:I2L CNr< UrSUna&aoix.A9ZY1[8^^cF*g 1> (YI%h@!s\@|iFT:jGWgr#̚ dlH"\^oh^:R$T H"A=Im^ٛ{QaJM}$B=2]Uǥ77S'"T$ϣ0Ї-` i;8X4OAJp|H3&5EY|n.0F?M!2>`*>*Yq#fAjwvM|fD GHftTQ@8=Sy\w]y>K [p]OT('%8$fI3}ĀW8Ic]LL_tcHd3PUTQx|Ee,:uX-pßK! !6ɱ&ZFIHr0j&D>SKŖ.F_f8ZP 1DB31@QqFTa\mcuzGc1@2 16>>HJL:C*A(CBpf x ה>ql)ᣀh}BŤ M- m90p:NMaQTϮ t]7@)L46"@< %hP~K;j6AR٧PB"!F"f36kkG*ѱmvVt9o>*`.z<=8;3#G(!p]]U;Odv4$@@..TBh"Ŭ_gr\ ہjʆmT{~LZ „~R0"4O1\oQ!t]Aopa3:h %ϳH$ZMkCxP!^"IW轢qS^ͨޱJnT'"QLFbW] 0hRs1"C14 8xY,ptؽGluuU& |#J"HeNxLUh@dG*, !qA@ Y-pã}L[S-=7տNxZA& F, WI> v "T<0.vMC4:hY}{`Ѵo{j6Ma2}*PT lhZhGi%穜s/oeUu{UEr1JP۪"qHǣn ko݋e:OgPHJ;pc,ڸU%xyo(qKI vTRyX"tzT":&:1蜓]W(T(3䝁TE(d{t5z jQ:H)p)$B*F7aկ~b(V_zx|c3M]`)ߺ֍P ^~PMJiR{_B? @a-3e_IR^j, CUQZ @tu5ܫa ^UӦwqnJ)ȅ:P4TYC!›wBٞ¡) ^4MbMMm ྋ XE*yok]/ ْjAQUaC b >h  E~<ߒUUQ WMBpM1< &T  0;׆|~B߲/vʹ;_ٴ>ڴ/AS\KN%ږ%Ked<߲/߷<1LnL&R %@E(ܐTkn Cj c?Q"R GZ[${=571ԍ4G*lQpm1JxBs?|`J 's,wpĻ=U ޽;1ܐn_(o#1|#_ER`;?/HM`"yܽ{wӟ7)FMrImTrTİ>(Ɛ/za * m+5WvŸnJܳ=裟~fxd3G_&6Gѓ !P`dĨDp ԗ @!D<C<GhImξxx;imߊ) >ݲetbv"ͿBԴ8!A!!DA:h64hxAq C}˶۽w|駵4<<|x 9xq PLA9/PnkxK =}BlbeejA<Btܲ,vrPUUzJ'OlܸEQvMlfYXPGK~hXC:LD?: \?˲纟=DzK/\ۿ;22R> Q}m6K,q7hP"xV :/[ dY5Kt+ @4躎fj ۶B뺊mwmS zw?96:1R.ahnp(LA8 ҕW lґ]H Vw R""3AmۨV}!Jn]KxXgOlݺc1 <ϻ9iSH8^UF6‘U7Ĺ l6 !\EрH&us[^Vc[euI;w7222t 86u](pKk{SZuKqP'@utuu1qt8,emK;~ٶܵk188۶a !8|EB KA+Ħfbeϛg䤔DSUds9qX˲9G<$hԳ,^p}y+Wwu40 !yn8O(S|^2lΡev؋3sg.cLi6[GuZ-h4z48w=EQ.q[kkmzݢEQnGGG?3::6d4ױs:j?h ҵxb:vw&T 1Ei:n7jѨm)P\+at]U']}hA׿1BX,p Hv?ڳgBK:Bx\rk+++?:uOoEIg5UwwZ[hlzvD"xWUkB@zUUQ,h LbÆ uyol߾ .///G_cb6 ah4zExqP:oqW9ЫeA81( CeYm}KKKX |ڶnHtV}aƍO)UN>m۶4 1)9}ߟ55ijuXe5WfggcRzӠ8+mkݧ뺚H$:|߃㸐rHmCJ py;J҂T^D *e !6 ٱ#HZJr|}uݻhi"BJ ᅅ  w{!MB/*dTXEpA (v#*,fC]XDP@"R!wF@P~)wfwι疹Qt2 {/ l̀Gǧ],Wa`DD %`du>}֧wg^(nFSPY/FU5.4SNCu(S5VQt"i%/AhtODi-,u1ܽΠɧΗ/秐*Ͻy%gP%?͑(Ӽg7PJ6AL`B¬x+0&&b ?i anR"`ttKϿ቞kaaTJ{ta]>: )pz6Mt.D'\ ?AA)X*h /{6 AvĿWSt(F4v\1OwXw]gG;WM8lٲk3|F.$"=)H7HY=;;ZYo'hҟ4FΛ(U~4pv~2!\\6[,!76%g/r_Z, 8WF""X_6͚͉l"%]mKc\%o(ꎨ'~Jp⇂ޚ3jt easqC|s#n1퓾>GzZ—Yj/uOt3n-B H@5u3.}WS.Wx5,Vo~6,^\hZ\B`Is^G۪^(U ƤozVE=aXxrΆh}{wUhsGu6 d37!>y_]s0VuoôXƍ{ErorEFyLs<ٍ`d߁XJQ:~]НS1 w 3p >|#=/_KyЛj `eLKтoSLw/,utȋnc7RϻmcZ X.ہwc'xoHkoޥz5/'W-xGNg$oo,t[pa_nχ&{Zɸeyftn[DxN;U]}bʈ?wr Q q;RI?.TS:p6U*5҃9?A "Ypf㫭 /HIotfӔ@>rBӜxÖ}̪.ZznO2A%\jh 쫧NWT/x`~nRK>²/iH 6݄97uyͰ,OPUIMߋǸ8/_ :O%sɨ?7CpLàmmd-]ʉRкfa*`M组ŵK @̝g$jN,tLl;qn]?ؠ[(^vp%~ 8,r|k93플 b|-_{jXnM$~u['-?tL;R0Yn^T~t/˶z +Q,|VBOLEN.uiԅrv$+8v* !qIYg4U0l}23<8wR2 s].S3-_h6L^B.UDHm/Og|<_x~}=ˎ-EJI'ȳTgN'G \Vҥ^>Qk 4u~s%P,Ux6nO_.R /^=P ]V( JzNeX!yc%e\W+Ct< D,:+ {P<K#>j^)e~':W{'/=i1 Gw:T9~"9If.M(rMYNgZ LNc(<Oџf > X7de_ O° xk VVA9cl|Kvp@{;ocRߋC)P~/n #yy~ƥؿ~y9nrd/Ƣ]\lc 1gm^|LP\Z(fM_4 =eL _#npǟ u)xYRK:߸~C-!_i]Ɵg˩L%ti>(wM p80,k;!xפ@O[4Oo/Io& vE@ٶ0YܱSvmwZ>YS u&9Oۄ*8qڽ y%?&2c@'I阋K,4'Nq-_>1E~9M@)*Wj`?uZz:ܾOsi˯bp@wLp‡[ofnmEۧ vg` oӉy<+|Gayݍ=+YELBI ڇowS&=-DO4^֖ߞ 5=\:@xdV ky_l&/Y=;3gIdd W4'7z?&;r~ADG%C^yQ;]~h}T|H={Js2HV(@ЗzÓs1y?Gkp?]uDj5x#n'8'0UBȋ"Ȭ`1}M, #i [SNžlkˇ!vh楒f,dz eWp?!+n߯zX^|a4xڂUE#? &iD oщnDܬf >J|(rnN2OPMs1G̋A/H,Ov@f;ɲbkʣؗ9Št+lki7 <|ۿyUwLrv ù.?'56"֡Gpt_A^cs^jֿrb7|odZ4& B&LX>ҴGpgg(&Vx-b|)_5]û%aM!ܥ=ph[ҵ؆q=^=o>~=\ Ng}s':#S$,pH.kwbNJɎ}|"`V-huaϔA+NvET9 d2Ltei5 rk`q} O CgP|7&,ŵ|"l5x6h<~}=bGeO Aa* I}ڏhǾe0~D,us8]0b͉j܉FtPyr6 #Įl=5 QY"l$um(;݅w)wRoUZ_GLSw*ZJG@g|2u=+p=|yW*ѽ߿<7YR\}~[ǤuOU޸ w-^3lHcof0b~4gb.I O|@jps^LRLrZEwP! <6^_?sCq8K[K Y"~.Bx *R76oܬ%/ a*ťyP$Б+G$Ѡsmֶ&{? 7+Z0r-~:&tRrX2Vu0*Fo:TL+ri<+ߡx2D~D/7pgҌ, `yG7}Nj0]䅭Y L }LEGGY n/. %"jzxkKo\ optܥS7_s7(Н7UI[_ i͂{HKp XiZ#PSzG~ 4J|\ yHr.r[4,LAZ\q=ZBahMp Gu m ^i]4ADE)),QC|AsBWHQL=(x'e4Z@/ u3Iݮ8xy:ߣFnY0e[ U u9؋sq6"pdȝs3QtXuǏ#;VfgoòI3z | >j E>G.c`Su2/@OؒMst&wڵSѮ}7ޓcVwsvnNU0>9knSP,ȣw rӕϧ@xEt3~v.,ا+4 ZLFk1 6IVluۛ{n1f¬E0#w#UE+ҷC K)nMT3dB@ 7NSsiyl[t`26Y{`OY]c5zpH(8أaa82cKgeBu Q:7_aH٧IDATg(j{G7TwvrO*oѼr`e aádC%@Xlwļm[1}O%) q(8\{̴{X]3dt.h!O ܵTpxFteMzTXtRaw profile type APP1xP |W >).&˶+ƹ(g{ϯqz*( QDmt./#BG".r"'~1*:+|<%8!nbF&`fLnfH- fϿFl^㏆|"z[Q#gEqE}0Vɺ ӅTXzHQ˒4)"|Fs?ciTXtXML:com.adobe.xmp 132 134 CIENDB`fotoxx-18.01.1/images/warp.png0000644000175000017500000001673113222767271014616 0ustar micomicoPNG  IHDR@@iqzIDATxś Nϼ3mHcBLK¤"*ZHѢ}/Ѣ=*Q!! I c13ITν<ٷ{%-^8#)))DQTlKgr8|O딿:q{KDޯ{~Y,3Cgc$(6i ?u$"Bܚ5kO?͛,YOKMM;VvePVPfM#$o n_gH"ż0a„ا~j Dx SĤTa<]sפ;Onذ-O>cgd eH'|@}$)|G4'ge=G3ėf/hv\Dzmݳ|KIIۢE:u߲eP\PPP`ZaÆ/?€ĽT:wwD֭ s1)z36˦z#G]f.BWdj c"^bEX|$%avI˗7fLy˩)L+_{iAwq3=f)< x4F/nڷf\!kfhӦM^z_Œ3Lmׯw_=՜\ZZF `L SL 4h4i,X^~e/fk֬СC/Ԝ-f@\znxg/!El/i Txᇇ͏4OniӦF W_}u裏?cGaذaᤓNyh1j(:w-ZyiΚ"9,4O=E8IIq_ጊsïA3θobn_399*{a}r)A&xkǏ?03={v@ݿ ֭ .4_z%3¥h"s2y ֻwIz &[yZ^9~yy2S?p zIorUV'0<Мk׆J*K/nfDsn޼yX~={k+V "4_ "vJ81#8?}>}M̀V W\qE+rUU+WN z2{FRw}wyh9\`R"q9L6 pED '+}].p6!C/?MC3о}{Rg}fT8}HY?rcP]Q tAaS_ΐ8.]YfHC9saOhϙ0sAhr%E)zVvBDx޶m۽)[>uARY2O{Z(c*dN䯿6("$ ca}(wyLBE y$H'NMW1 $H=/ 2Ȕw!2E?q&lu / FbztQGERHERHڵ+Dlt5P$M߬CUb:=Q*U"1 ⷲHIH#U$SD 0A*з $JU|$B~޳gy$M{Crnez:p_~<.Kð͋G=>N6Kyo/{0 `96n_(n0Qqqts{^q2ݻw7O/ $> 393" Yfv$S Ty5j|M9U3g~߈=7 qzڵAZp+입A5:q4ANKc,*puum L9 I S3QV^ms`ʕ+Wꪫ,"@?A&r&-1 IwA@1$# uG8@0\~m78<+9| }Vm.0:A+`d?F` !>-[^0 lWBy?]u< ǧC.@LFfcBƑr҈gB$GVa[ $Ko=pk~5 fJI4/T`!D(jGHvEvOXKpKdžG}`8~#=gpMűd K1`qS !0L/G|ђ4^9kǑB4YiZRIbH6M;wvRo91%>/ ;3<Ӯm۶ᢋ.2Y3|(C-М @8#^iȓRL ZR#q%WcҜd}iLQsh8ើ~f]fT'\wuh뮻a @\{Cl#C];֑41b{Y qa>E#9 Fu8@.pA؇k3LGl]v*$E|Q?,3".$ɘ$D ͱQH0D"ʶ̱'|y\ 9=уe˖Q$O M?B [r0Q~t/kg1̃9άFZc =$}Z㩵Q+EzcBHt`G#hN<%є)S,Ta#m z&M2{LJ„ #\|o!Y-{:-p$HMHaX/fXޭ ]xᅑRkH5}we|ಇp!HE" j8ݦL\M6mbܢeTPob&gK3JUGF+ bkuAGC6|$s% 8|H$)fGgO:,!tYHyK3mn28<+ q8 NΟR]CF+6}[ڦֈĬ- JPJckZ^Mƒ}9)'Ub3Xw)z^%E<{1'.ɸS$MN4a*}?nqNیPZW?Ԕ$S,Lu֫l[G~N-ÉuIi`YOBIƴ&L(LrJ}) qFZ&?K3BVVÙIo'Ols5ubN][,[<{;,$Jq0pHw\`#X{*3\N ;e"^Kg?^nIQ|V 4` T)f&@1qGB >yoz*ᦐI]xuMXU ټIrs@½n"&%$ C'PѾCX/D)뭷JZ7OuAgL.xWe#<+s܃ 7^ށG<$@&ةB |^bq9?+㌝m ՗3Y{Vo # 5$&nHG,Q"ԃ5q)W^-bB$vs&oO?5CxBϹgw%a MRoTD桱h#of"MQ;26ė.lc# t} q'KGI=H8ׯe Ns0q4&xQR/OpV8_ {RZ8MۇE @(PoT0G6#6a!MJg2O*D7?_< Q $ MܹLXu'c>̃ybjnl@X BD`h?adI$Qk!ғ@#|xF)iMTH hиt|mI,R1黻UVM֦DK+B"'>K udf:#(91?|PTRf>GcEQU~59R'%Bꔆr_1 @3 p=\!C_rQң%?<s/3˓D^ZJޏ}C v-%yǤ1h`L_RW#aġnoW+VC:B{&z Z7Cu$%0+ur r. kMxzH!"?>Ssݚ!i>'LRV6kjx<EcQvnm `߬Y7FZ%LAP%oDu!2-7..!}DBCݷz앐00N MH{G@iowqhސ!X. 7ZOﺡj$&ty_BFzu+gE!;9@u1 @[hө aHv5@҃&0A=bOٍ7,h A<ߖQ `呫J+H?edTf^a4NJ)"%ƻC8M=A8R*+WҘ<=BMRRe#s 6B5H1^RU31'ԝxtFE@ ='5k"L Tפ4Yd|*s{ f5@n?cPļkU",Iq)Jv\vQsbhpb 0Xy Dutz%^),'ֻ8 zW* [I$K&)*TU^F99+oUpt)y(xn0T.26J%)Wg3!\Obq޺ҩ !9 Z4Y=s963)n;Ŷ$}eXIfMM*V^(if%f0230Ƞ01002015:05:27 07:50:58Fotoxx:paint_clone|paint_clone|warp-curved|paint_clone|trim_rotate| Fotoxx:paint_clone| Fotoxx:paint_clone|resize|resize| Fotoxx:resize| Fotoxx:paint_transp|NEV6iTXtXML:com.adobe.xmp 8 1000 1000 2 2 0 4W`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-18.01.1/images/edit-any-metadata.jpg0000644000175000017500000010622713222767271017131 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 330 600 0 C     C   )" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DREҽGxoB'gj?F\KǾ5p2A9uB1ՅKx7JAkĊ!fLD0 !{ܾ̎owYvk:ZUO#?_Bx;OUluC {X/-{gO*v%&R2>qjsɠ-V dMB mZT(Ǡ$R _l>|?O>C 5{Fd/GZ~|,<3/?c}iY]R`!v kpT՛] ;G}'?DWx-:֭i≢u"Fv.EeQ״x5:.I_ȑb@тKu$^W/vm_(aO{%׃-SOUu/xNƒo yg[ VvڋM -+U#a8vTWs>v ,mϦqR:EzOi׮_ZBmؖ * |?z>EaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjj˩s I13AZ}mLgku8FX~"A/dF7?; pƽ?4"l/HUNے[?CNW<5$bD1T=jk$5;sqc^Z>|PB=WqWl \FF8P 2}hsgqkj_ xmR O*&rzAsQw ׎HFR2֭vrNqK hWjB sY'׆udu{- ԛBFj?v$Q* fYy$'Js oԣUw-h}.IU%R\MWo_zzmYj62`gȺ)e{k_sE.o~΀I*d<~UOK4 TFy#g :%략_7;YfcuMK2JIei>oA]#QqA]#Wf|oZ>}k',_F,_F.~{}_?Og5Og5as'֏Z r|8?.r|8?. i>}O;YvG;YvE\I'ֿBܟ?? j?ܟ?? j,Oi>qA]#QqA]#Qf?=}hIПw'Ïw'Ï0GO~ù>şkù>şkYoZ>}k',_F,_F.~{}_?Og5Og5as'֏Z r|8?.r|8?. i>}O;YvG;YvE\şkù>şkY},|5}&꯮D_߸v㓑*?|Hu5ښQLFłqƨFbr%s,_F,_FO qDw̟#+C2"SMkkŖRiOmc\j;e[[fE= vr|8?.r|8?. !CgMо~&hNs}q؆vERbB~y/I6O}i%X6ul"r|8?.r|8?.rkzHGIfXFҳ&aQ `@؊O^7O /M>=݉^3(^|?ܟ?? j?ܟ?? jn-;l|G7/xKi:\Ocn"5sYySrx5g^^!2=6Gui|##I=_iù>şkù>şkYMu5"Ms] S 1ĻAݜejZƓG]CIiZkk+[aa J9_hù>şkù>şkYm[Sĭ[qXF"& Ur#^rY/ y!H̍T~WOg5Og5.PGO~ù>şkù>şkOi>qA]#QqA]#Qf?=}hIПw'Ïw'Ï0GO~ù>şkù>şkYoZ>}k',_F,_F.~{}_?Og5Og5as'֏Z r|8?.r|8?. i>}O;YvG;YvE\I'ֿBܟ?? j?ܟ?? j,զ>yճ^z2Oʷ_ ɋ k}YJ,_F,_F.~zs,LM˿fL }*VwN_vwwo8NFz_}ù>şkù>şkYϫFK#i816 $H?~C?nt (l-m.ܟ?? j?ܟ?? j`c!FUY򽻦>GO[V[y ޹9_Og5?{z>F6Fxv#0e'Y.( ( ( ( ( !ZisŚ֓_-$V#dzbp]y>$|Om/:KNaX] %˕9#ʜu_#O^Q׵+ q gKPx70}3<.x.=O?-"e׭-y\^$R^U*$Pj&uxuo\jڣ_smմ54H2ێB&/xO_MoMN6k}㶺iu,ǼUÚO.k;z~xǑv:Fm3d4Ad8G~wj>GRKM#L-eTyK ˤm3j>x;]u K.tCnQ\6N¼G ]nI02qXݫy"]t,\ tּS~|?)sksea1JAy#n-[_%%u{qẌF wH=BhC4w$:rBzkނ]τ\Zh;WDu0DBUpI A>0xj} ?aGeko+\G"ya@9%i+6o3z+fi/ bSaS9*ffp-ۍndQ!sPthۿ73|[Z7I|k#O|.O+hV|nrĎMzg<3U//dݤoq뫜[էqDU}/Ԗߗ{L3o!LUHQNcu%ίaoq7-+FFA9W[]J z"mkxi1mVI5;d#'yO~5ִ_}>*Ⱥl" T4H$m AK_ߗmy_#-㻂x浖1*OGB2092Qmŕ7 dqWw|,!Elz6Hc\CHmjpҼ#᧌GO|@w4+}2{WÃ]guhV8ݗIprI8lkm/Z+=5oH/|bpZfOT+%\@"(o+?VO Q!VSLL$Ȇ+$yaCzlk_pN7}Smo3E*U`He# 6|'l~&i_J<SMh$SǶ[k. p {~v1ji#[kQLrys#f8j?V̞O=&RUUv i'<[BNOws %DHr 2x$vc񜷿6 Ki{m漸< ZF>]j!Apal5 CR7<QGƋh/Ps lϺ0n{P FF85WJ׵&nnVf ,8G=j)I%h>4\!ԖfH|Q,mg2;/эd\=i>mB!+-n@R#WiY4{-{Hl'mT31Q܇þ'6.drjޕvo: !8$(`qҶk  _x 1G& b*\|0ߋ"< N$K.ց|Gq}]mb:E knݖmY0HzT?Z3?N?0g& ZUƫcUȼi q Y'__MY,۲`]b#zak@;(!vPG_5iu/4Xn[^6S3up;q:c5Zx]z iJW<~v `2ry?L-'oZ3?N >V𵅴H U {QK[Gw>)Dڭ3|(~U鎕  ]_w:.m]jGevB:F"](Ĝvrz_nm}53ijoS7wɸx;ן.m?[n<7xkSVW$+2 CG2"¼~:j7#LZno]Ii1LN!Ԫ>g ߇G(,ot1ǥĒ*A !!+Ói[Ϡ( ( ( ( ( ( ( ( ( ( ( Qi?W =GM|A>i?uGQGH?P!niiW@GM R,)&d@2I=;V? x/ OmM=KKKo# N@x'x~ z?,5wy;]UG y~y6qkG&aK&iZ"nbom]}xrx~ sC"E$5#m~æR:'[[%dz)ae^@}=xViЮW+p-dTEm2 #G߽c(Q@Q@Q@Q@Q@Q@Q@Q@A7ͿOP_Ǎso@|Jz.]sÚNA}<6EA\v$7w)G|T3jϥYy-=$*]b{xk@~#9b{y22S*:xN/>kzTךA؛>\+$9-sšUz%M3R`sjbڎR6`\UI}/AuoHtI7 FQB ǥXxK}R=2Oxt^WR`!I?Zu?]^tK..af AАH=yt Sw7_G`/4KCq4P8 amoZ+jƋ5/fZDA=݊O_Z=ap*$ M]Ok B-KS5a!tk'ce5|Do覬zW<- j>+4]CF$iHXٗrdqзKFz4Z,#k؍B;GG ,АZԓgm_z]kqy\  OhYѢaku-m +/g +үum:k z|6d&UUݸGl|x}ׇ|E4ֿaOLNMB-\~3|K<]>ҵ |m]`EAo/[؉4PD8y,dא7Gjޟ{ $mΚH)ԎPJccV?X:NYEݛ?SyKÑ9˿gG|ËKOhZ֥u KtnȐirK&-Z?;[ ŠNhkE9J?3Oc}+K+^_+՗{QRPQEQEQEQEQEQEQEQEQEU{@z{ITu?'h? {I>ȏc^&7'?'򎾀"=zA siCVFATdozl@Ѵɝ6m[]Y'j((ޑ-R>MBuDFVy e?1b6+F45_Qi>Fvn] j`nj {BA_GO>Ghlf@  WU?i/϶5, ?".X-^D]UlfGO>Gh _/Z??`^еyuW϶??i/P-j {BA_GO>Ghlf@  WU?i/϶5, ?".X-^D]UlfGO>Gh _/Z??`^еyuW϶??i/P-j {BA_GO>Ghlf@ O?G/zɸ#϶??i/P9=$MV )E0 l&VF-ޮ?w} H/?/Q_? _϶??i/`8/bEwQu12^{p^kt<,дY! )ku:;_:k/Ӽ83- ks RwqE!Rs?F>6ܧ KF@NA>/!i} tF~xr۶M_[~Vw!4O>O"n6?`X<w!qEQn|a$Mm-d&t͔=^ c{&j&αoGM>($ވ?0x ++Xw0Ii%y/[X"F\HBАHv۳{Ew]tn+|٫㖕/0{O<8ncxes^0|m~)Vxm[ֶכ#E8C V؃yb?*;G_ 6Z/;k㕸5}CO$ tV]FUc,W/??2s‘dž'bt=NpN/&rMZpNN)KwM[Gj>e6zK5a&{ќ\8Nyį|S1ǪG;[I,hUg#k|Gsz݇[bX:;_IC>e Lx#=֯ & ^D&s VG],q T2./ tQE ( ( ( ( ( ( ( ( Qi?W =GM|A>i?uGQGH?P!np|qgmL潬떱~IE$o!]"vpK j)a鶚&}ǬGQ`q&1+7^<񞳠75_"[-P1)vA8'5ʵ f 6W 0Icw8(?hB=/mQԯty,MiSEky_EQEQEQEQEQEQEQEQEQEQEQEQEywZj/EgR԰9OM`sO+x¾ ׆)VosxO~,mmm`$YH5 ĖS$ MU'5mŏ|FR^fs%w2KruxWǑEj |=ҭ~"Gq?ͥǪz5ꩥD嗚"pHAǀ577YȣPn89x*8,v#d\_/|f /}ƞ Út̚,fl[9Ad⁥Zjڂ/.IM0?=#7st}_wW)PҴm3X>{5KxfY'u@f,T;, 5/ˢ[Ov\[E1.h('1s:,rP@d?J%-1İ{qwa |UH|/Yiچwiͮ?kd/%8W}Υ7P+/M?'cm;O-&mSVJ}R+Y9~r#I o3eE;}E2|Rо$;<sYctt{kAѩw$$HQ%O犼!~E熯dkVc\r>\woxf>%іP5fyk&ExBɂC#9[D_/_C袊 ( ( ( ( ( ( ( ( ( Qi?W =GM|A>i?uGQGH?P!nzmQPXQEQEUyd{ T~@?-?'EPĴƏKOjC?-?'PKOhĴƯ@?-?'EPĴƏKOjC?-?'PKOhĴƯ@?-?'EPĴƏKOjC?-?'PKOhĴƯ@?-?'ES--8чgf!IOrU({'.E5b|Z ßx/L%W3G$o$I$.4rPo|Iu>.ߋ>&/WMkmCk)nˇDTtLeT˛N_?Q_/_BoG65F׉5)/|=8th]BTp*$l[܈ WYyFhK!Uŵuyu[,63L~Xb6=@'bSx?W~xË۴PG#[zm_J쵯^ j$=E,r*_k(Q8ݜo^oO?sAiºO|S?m7T4䱰UH"lWƎH2F 6>#+~'5QjW@mm!Y]͒]_H,#?+ZGk1Zc9V@]b&Acᾳ=CUX]]x]WUG "4?g~[k%@kf7-upϡYGugYGr#s$s&MD<xu;JX/എ{971t(S^e⯃4_Ʋ(ž4l+GZ -[["2k4pیug5<7jA[O]>Xy-C=IyH WF!c;||ZힷMŚuYi66^4es2^x#O|uۍ6 QPإB^Xę3dFF @nSҾJҼ ~GiW"[KhlC a!+rp8MR[? o-3~r^nwiecw09V+fI˖7l'}{0~!x7CF#ˤ6P"fT 8_ŭxOէ5[;H׭ L-o5;wFBFBN2IS _ۨn LBJD]A84+io袊c ( ( ( ( ( ( ( Qi?W =GM|)kúx^]>{ %`B컂`v{ Aip'u= sl?"٩t%nuQEIAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP=Iu.|@wFeyj|AnРDe,0^5IuP9}oož3xC3Zߵ5 2nRcB TrFA?>(ӗxK/ƔiCZO Ld S]3Ş. &V_-b9RAO3S@`Np3<{'k tEK/&lʶC+oE  W+~~*w/Z]{.m S!Yu1ɐ*0~j:k]ލ[o >$~^ *6vljѣ[[5&OWo_>K/~6|T|GmAuo{՟N'dK";y'z;0F9;?xsŶFu隶w}P4aL"wRJ095|էōsϏ-R^Lt_ lB.jEGZr.DBf8 v?h_j)| xYkL"uvdzɓ~ぷ9#4xBz)m6;AVc*)fyBkƤ/^#ß/5|QOWmY;\\kP#̖7*!x 팖 [#R+Ww WCOsᵟ|V|+ KN:sM,br` tunϿ@ǟhc6xz-|uH 0gx^w >HmgꟵn~[5k2ݙfD%dHOaumqGpxf[)Hcp8#qw)jIel|["Y|YlZ_ k|kKMZX$I'ZȌ"1,HqT5i_N𕦍okul\AXA+::}*޳5]x\t/xrkh5KO]=,i'`s$l2T^5roᡚNy"d|`g/L-5-b[f;'b-/)`oژ4=CP(dOFKmRqVYk.lZwyH좞m7DvƯyͽߞG\$2xR`IԵkϙ~xv-~7xƓk7_Pi@͂' $]&F}ă^{4Maigqm4:#)VGI1#A_ >>,m֛wۭB?[}儤K&P|یg%xenT|CS=:6.PybN[ wnRi]\t_K~֟4]9Β5I͵<n>*$SȫQ~ݚx~ٮC.6dތC;VOZs>ߌf꺜܏bӘf+e!cx,Fpk-?gK(u[񗊵m_Ju=2;FY%_2Vp2c # 'ϣ[ᾉxZ *#/$k@&G[9[vv,irQ|Յ |r  RV0"?7SV/~&uA{sN!ox]K#\S$ϿȨ_o;:q xvTյ7chFr,8]xđ~?%4ڂ,4ymh22;c $LQA;qiz|GM|%KlWEg(C`I Ou/Ǫ?ֵFҮRK8)d} dnn4x1|u']&V.ӮnԩhyLb皟_|CIi..`֥}o7K*Icm7mwfalF!DݎyxF֟TПQ.H̴rP,s"x!Ӹm|C v2q=v +lJsUZl\_~~<7lZ~GVX#MxXoH~c)*qLoCR+Nķ(J]x^i!=,*;i_w11Uߑ_Oz4M:i:ͬIseuyn.ķr䑻qL?<}7_ ŴvWim%"ibW*X?gxV_ɤ]X LҬ]~ė 2ȞK'$ixgGtH#r HP2ZvM(Q@Q@Q@Q@Q@Q@Q@Q׿״ G^^&>j a\Ax_\. [lj] GYETQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEs?w_)j ]xgI_3\.@[nxR1P$ M]Ʃj^*мQ X]@ g >'f:kqoiռ ݰk"bpN Ư0gxoX˧GE,Kyp6v%'&zǎoYHѣvr8zjoEs ?া#鍬%'6*yvpn΂*X:e֥um5ܫPƣ,@UI tWeo'ʱVАH`A ( Qi?W =GM|ա'+?]@?]׀Ժ((((((((((((((((((("?7SWжHt-WD[;Xd:o ޫ#i$6N0rzO'&]j_ L柣\܆bnl $ MY4ãGn~k>#ѼE*.5mɤZj$۫5Ey d,5/K'<6o7(FQq(l[6Ѱ.+/~ύa;ѡx o úBvom 4=*կj"wngzO?܏iϢxK !vќJʵ W蟵ƿjjm֟N=CI ˀa%Aǖކ)'3= 1 %-r9cXG<9j/oy~m/I-%x@G&9ֈUo +oH?g߉RJiE`[IIzO?mOixEH>m b)t_ZPB IPhLJ8,8GjӷѴFfe(LpPp:R[5߅;וbjڦ"o(t6Py[EJ9")%I,0H>?z=kMcI!;죚 I`Ti%͒bP󟷣lbETEW*Jaч}>+n~t9| fymxm̚Dāy`=)k}?Y[5+*$[3Vw&W7EA3sƱ@w0n@Z>"~+լ_Vo9 r=I(p|$?u?vmAG1;q:93D_1Ffg WK{ XEm=-mBvTNϻm-֝%{~oΫM͔3Cn{51I`A|B:~o4˯4몋h;y&enfAjJ+JPo<),h@ `tzog3]۳\rW_Q.k~kz~j־3 'G_Bk+x/L;cWUAP[ <76nc22c#kdiqkx68'&B" $ҵ/m|ϭ2~0~6X{h0]Z|Vuڼ,eC9;AYW@$vwZ=]KS^/:%%h7WN5n"O,~`BIKw[;uZ!c1Um8[XU#%Dc H 1Ҕ\❿8KZX>!>$x_L5.=ڹmGȗ<"?_5}&Rh,ִeկf^H!Wgie,z1^t2H0iZ0:z?Rm q Iv..=IR}knev[CW|CI|M`i;i-sJ @Mޫpvp|aqD?ִ跾φu)l-HwC&Gb$(gm}ɏvyF3MkKxtYeK_{~ /H?oYa )巕c S*giP%~xo |c+][ ]o)R-!\ I9 f,i|qb鶈k #.q>i|^V߉t}!A/|E໛2]ʓZ4t;>兆yl>ѼA5/ o_= BtV6N->PX9$B6_2X"#א;ӠIa)df֚=wI袊G^^&U{@z{IC-pOWu?~5pl?"٩t%eQRPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|Do覯>$4/xwıx]g4ktok<ij4HI#l?đܪܘu_ 7Ecծu;v +I&5;Yg>ֆ>(hW-_OhRxඟYY5[j"{edGRK&睟VQ߈uK zDx&x F6"yCFcN񵎩i6_&)4tR2oi^[_ZoY-|7rI֏cJ_ y8+dS?yY]_ |-_J|K >֛Vhs6 dn]]sl6cww\pFkwCD`\%e6SWן7CoW6͖h:Va,lL $GO'I) i# o> 3Z.xZvsCi ' i<ږpʛd{'#K NqF[vMkﯠ5w{ZxjOh>QjE$/_bƮ:~~~%i蚡4mVM> kqѵ$d^q..=ԗ2 f-UU)8xof[]Z:V:5Il<߭9Y?Í|% xs8K:~;+[{;;!KvV$9H"p2 89-h|0Oz)o|gKLzytܥnr!IPf?!_qvbMrCB(m85j:bI?m@qӕb`6O_s//*xľ jt~qZXm(ǜ%fBAD+%7]98&]kY-q">HmI}QAaqGl6cww\?~k_בzkAyh j&x8F&Aq*H ?"Vuo<$Wp6B̠ˆyEvGbBOhFΗ]m WlF$KY.}d ~&vxx/ѭdGՕHhT5j 1Q`NG 8in,㸑[$6$> U?l+cw-u/-#W?$1 U ;ZSWh㹄!U mo<$Wp6B̠€4hyEvGbBOh]m WlF$KY.}d ~&4j =GM2]8+kkY&QeRU|Ue^yKO;AǶzl?"٫Э:%`O2G;WmPRKp#l0#J:(((((((((((((((((("af&¶jύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?*(7QeƫJTycҢ3|_|l1~>6_j ?W/ 5Gύ?3kyf`JƅNkcQfMOLI0%)GҀ-c<_|l1U'5_I!֗&-yc<_|l1U'5_I!֗&-yc<_|l1U'5_Iuc5ԯ & Y˃xO?W/ 5Gύ?Q}OTb[g76;H*`bU'5_I ^~>6_j?W/ 5U~ i)h'5_I ^~>6_j?W/ 5UĬU ew=QQ-5X" BtVD c2qeƨUW!Η&xZ_ eeƨUW!֗&xZ_ eņ+0$Z5楧jVե^;Dmj`V0q5|nVѮ}9]@^n*` <qּk/֭1\^bf@\V@St> پ(K/O+ 6(fHHK97ɤ~i:2\Ԓ]GWX!-Q qC=iyy_>|e[:%*hHO58Q$k[WMteY ?ғ@^g ռCV.mh"S,,J 3zV?~+C:5d$ 5{˙aFD.ˏOPJѾt=?M|w g`/`FH1k@;A~b=F\+xGTg*0!*Aƻ ,Qb|?l$o#(y qc>·k5{jQE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ t#+ {|CD8XeQ DOMvtP Ѯ~vbVgs@W+3CF?Y5WkEq_ m+kþ3Bi@S-! Ҷk\<:Πz, EB!ѹG`=+?\vP Ѯ~vbVgs@Z3RG=nh6x0Pm-.M>E)nyg Ob Eq_ozf Ѯ~vbZ(fh?;o1G+3CFhwA\ZڟSԥ U>3Ԁ>=MtTQ@fotoxx-18.01.1/images/leverage-edits.jpg0000644000175000017500000005010213222767271016527 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 346 273 0 C     C   8" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj:,~"kZ-/O2Fw` ;h`caOl>|B_UưtxfRv!#\1WH7׺gNF˥#Bm,ISr|v扺y_(aOzLJ~x_ z|K&e RKmI u9*vh<4Iծ֩zwpGj%`;B̒S&_b#aOl>|fiΝYwSmu4c(GRI?,k)umF/Zr8yqh#6p~QoЮ?g}'?DW] RE$W\Ҡbwr?q Ge修ݓ}oD?G+ß|Cn5 /L &V{Af [p#ƌ 1q3NJ-*K -eMFX1ô!>Go2]'?ƒz@$+^%Ũeͬ*єO };سTԭŎuyj9B7X{j>n+aYdzx[؎c<ӟҼR=m $ ppӲ֒^js:}ڄËx,pikrm^ 7b=Gb=5جMny&$=@Ls$on̷'ү6W7k)4A?n=h \Ouu]-pVu{-_?}KY %+nR@'Б]g­F?/Y緶Pw@c%Q[;zv5h;ǚvgZonbXM"2}kM#wC&K3;?i>}7; >*_GM#f?>~}hIowÿ|U6G; >*_G.~|_?0mwÿ|U6E\I<[/S:{ V"]2&{bq;_kø z,t|ueD|Dӵ}SSDjnݟZNƌhʈ *V&O^LMv7R)D\#X` <3; >*_GM#/a+w~iN~ 4cՐM'\f&ַq+ t:KKakO'.CdpI8aW=?0m|sw&?n V\D3ᝏ8ɮ|)Zd%&j~$k5QU}8`}d}AwC&q;_k8[}_|U2çx\:2 ^7D] bs8/9x֓6n; >i|MV\q;_kø z,yh|}OU4=֫p//O9(hjQ*YC⟊F5V.Ư 6o}wC&q;_k$d s~ ]5V-3l!!ó۝5x:_XPԮ̞\nNaW=?0m(\Ii!:duAC_?0mwÿ|U6N.|3 ld}>=?Zu^ErbF/>; >*_GM#f>X[oACn iQY_ݸjZCXkA >a M#wC& xgW^u5ݎ>lcr\c sΣk$ɥ]q#z(SmNS47X4Y=7ALiɦO0wA2y`0#iV~ ~?Լ>.o:֚2Z?$gIѨgBm?7MIjְX$3ElgxݠW q]>RѼ7Fx 6BQ~o~; gýW5hzLJ&`k2i$gH!*:*Viݿt۽FL%]kWV 8t똭.n2` jMMi3X:7yo@igG r<3_ G,5gPf+jw\|/+t9W?A'>ǡxOl|Yl*f :߳}e4W,zdEĮ{ i|ͧx\sWkGu{W֋$I0낞nIuqυU~; 6[h,.$y۟ "]únoxGTU4=N?싻q#eei*sCL=c_x t[wM7F=tps4[?`Vl.?gk>!O~-,sq-$6~JvPE&v+I)b,8 ~ΔlGĖ]hHMugp8 8u_ߧg5imy5,/ӵZ_OrFU$A.֒4V)r3|)ok6i]0ZKEf9#H;Nq#?f; kŪo'OkY|3jgDEKfRFq99Ï xb[Gm#Sa $3z GOERQEQEQEYiHW'*Fi.2o.֛kJ2?.2o.EtK z(#]?&WOɿע2?.2o.EtK z(#]?&WOɿע2?.2o.EtK z(#]?&WOɿע2?.2o.EtK z(#]?&WOɿע2?.2o.EtK z(#]?&WOɿע2?.2o.EtK z(#]?&WOɿע2?.2o.EtK z(#]?&GAr}]kQ@]4 0Rb)|= ޓCj(֛kJo?5/ZҠ(((((((((((((( &/ڊ<= ޓCj(֛kJo?5/ZҠ(((((((((((((( &/ڊ<= ޓCj(֛kJ|7>,HK%bϴ(;A#'hS?/]ozul ; ?6w0icbʳ#:@/ץgb;IbH`G ǧQ~<- %֕o,ϒN^BX9 iKmQ 3 F|7}_ŒoD¿%+8Q xWE>> m(g'-G(φO+[o"(KmQ 3 F|7}_ŒoD¿%+8Q xWEq^~ Sŗ!&{seq2(KmQ 3 F|7}_ŒoD¿%+8Q xWE>> m(g'-G(φO+[o"(KmQ 3 F|7}_ŒoD¿%+8Q xWEqcň| XelI0p3I8{E+X|;ek"(ěNbOsEKQ@eKZUy!7֕QEQEQEQEQEQEQEQEQEQEQEQEQEQEf{A1'QQ@eKZUy!7֕QEQEQEQEQEQEQEQEQEQEQEQEQEQEf{A1'QQ@eKZU.Ϩ[XAQLpe,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3G,o ϣ|_UtTW; 3@B@G/*4<= ޓCj); aeʺAۡPr?<g6q QeSfFBnTeb1']E|⟅Ʃ Z>(β 5_119慬K>ZjqCڬ6TTGp^4O\{^5MQX<y3''|3]NJoqxn{KjD f8 >~$*lt8 [b$h@f@ prFe6vo{~Z/OZ^ nRHe[95q$ia3/Q`ll^M}/ĺ>Rb֗J.㈁+ŵB˸@3^ ;I*[rdo3WLwY#Î8'Yoi~&OjӞe }ui-WC .&P0EK1 @9^s⟅ڥ[_ 2OCkq Q#eoqfg09*Ny\P߿kn_QWD У-SuZo,?>9]bÆ%Tuk?jwmb|O{{ucY[DlB,NQQ 7AJ$ jVv#bEjViw2ZqWoO[{YknkV<)r O T ^> [[[ӼK_)%k@,28&#AQG/#Nu8i5[N{G cӌx?Ok_zma%jqi#,4JO˒8yn쟗/-oxvXZ٤ՠQ8_b.>񍱝^φ_qoxJo"ӯckg#ko |d<]exNO[zoʗuO~s8o> =_RMhV0ETDyǀzo/\׷E%Cww 2FI*&M{˓V!2l@ /="I A|uo>uMo#77%DҬH6XbsSUJNߗi+}?jZq^[dU(uff in;;+{qwIJqG_|^wk5J5y`lm"9RF$/nbz!S@5wCk#]> ?"q94Yߕ[e}eE|Ǻ\&wY{ƺF>]Rc;n01'#o7]Q-4=T6A <X%P1N*LF .㙿 k{k2ID6u`W%LOIh>U&VWPC)+俌j4l_KL Fxp )m;Cg$?>!RGVMIeL^AQoa$QWbsrmZvÚ+GVE&p]-u8t~"LƯi7mlj!n\FZ5pA IxO9_kkvr +N L"tvh#<\ u47c(((((Zb mt`rWj .!kEd9{Mb.OFt`:i귟g˭?kڼZ՗<.Kp#ᇃu5} RܖK)c$``պ;kiue%Κ-E!%|M'865xG㶅?)=ZSb 'o-Wi`;a^ɶѵM6*=YAc{+AkZB\/`8*FFG' ⏍h'4?#@˱.dyRNY@Q|8Iy8'ϵG͙ sSW P-1ţ>_A#HzcjG:"R3F Xkvf k/GILNᕖE(AF+~JKh/H g":tzxëc=Km,UZ|ix?EѮ1Nq# vG5:DՖ&R#'nxjKMFPE mEC[!д^O"^Y(f`p9 /\Ѽ}3-nqIm8YC`Hk~:`O>og-n'm@ 䟗f;}OūϠ{)P[om-iT [{BXc]FAc'>P}Ҷ v=Uhϑ?߅-Ԣԣ}:komNW ̰~vb1t]V4W>ۿ-rDjjz{@XW֟Hq_ |~dѵuI51l68ߏ8溪B=A,6WVkw 2[2,?z\--O7J6f_^gv"䗈dFIN?Jb c"*&i u KO&3jb/s@,%7I(8Î>_(N2x?6kI.cZ40 :n+5%g Ab{}2d*HSlypۏZ]o6h3JVqx#{whжHB8Mҭ4{UI'$I$պ:Io>χuIn4_?bSYdrwm 1%g3I/<_,3> %# } V;]gGy%BiB;sӏh˥|A2C*M%QKBc&l<3WWH@ ARQ$![ٗp\m#5^5w:ܘmN"UA.TSóJd[\ ؞NS$550F=UUǃ|#d񝦧iZo̍qb,ˆ;q W[3Y} \<-a5ύ,c6Z\WIKR9 ,Ks_XClBZOa5ḼS4rGLҬ kw"C<2 Gf[F}6Fcw,\E\wUR}HP=jFקQRPQEx%" o>!l9Gi:q,@ZA3O'dfiJ I/.{~Ũ>Ujw> ֓j4m;sSn=xK_%Ҽ"u$҉2]#hLɣFݿl+/E|[QuJEm5h7ME̦Iůnt9hbQrYq>'|8/ 3=I͸mH␩ (z& jzE%˨~>{KO*6+c,)x/[Aj#484ij!!b9*["q%JTW|1ωVVWQv 6+mFKMUb֫<1[i N9MXi/$9FVC&@FnO xꇈ[fyDI^7ck:펛N'NӴ=bMk8.yZV†;@|,JoC7?:>7?:B OW)/kM0׊H1[i<W;[ҮA:_^-no3Qr(\6O@EgկAk]ϥj\F㽸6%TWZ1Jōz 3uhxZ-|7X䎙?ƅ 5VDNJ ,|~Ʌ]?ZͼX,i <iV v'2q_],%w#$:)o~g^7uܚvg('di`2B$[Oه6ikiijF6/g9\:6㕯` ->> m7 7~ڴΊ_&@Q"0WE9Ba.?JKGs[jV _]Ơ#(bMp+X_q$)Wz\) oME <ſ|>Ӽ|5[57yYکuC>|5Um42QM{R6gKSFJ 2~$XLdRO$󢤢 ( ( ( ( ( ( ( ( ( ( ( ( m_g#Rhmx\VPCTpz_CW 3?<9C VwH}[LDP_`_t3~|EO ՖLF&.żpKo\3Z_?^kO\iCwdZk}<,~R8RN:o ]."uiuXT湸UFl&9;krU[NY%{b3$ƥ'5KrZzuO~,ft kxM u 'EK/L$gX99?:jz֕sPYRMml ,08$f>gW.ѷ5A5At-|N( (WysW  u$V evfYij4 otQE!Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@s? gk{yo"\v9Q@° .P _ruEqA=`rr?J((fotoxx-18.01.1/images/panorama2.jpg0000644000175000017500000005705113222767271015521 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 324 280 0 C     C   $" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|QtߌtK2:]%̭r2R8 ‹?KZu.}h/^8##3gn6i_סl>| >OdE{i(|9躶k"1(m(s=zp|Mfu]Z%K^4vZ,q,1F>a߯_[_8/?  O@N0 /+?u_—><ҭ!-ݯ`2#e($ @Ƥ5+_[عr.!3eAVZj7_hvF'*9H4 JN\(s^;J߆.c }Sz<^dӮQ<ɞI9]\džv7>{;[#D0pXbK7mr]dž}')qʖq?æ躔. ZAa!I1N]}<ͷmꖷi4EDѕwnۅs${.VL}[k#E98dQ ktvmՎxgMil緊A%ܫ4F#R$/ G'o i>%\l?vue;KR2 ONvДI_y(aOzoşO=B}RwFTh@,v$I|>yc ox]fkKH FEG})YA0s +6] >}Z_|;ω ҵ 2+x}ǎhGF 끜Wt.[9=RĚfVB.UaewO„oy(crv1铌 ?#뛄:t>/_ ].s]ϵ.c1zʼn]Wĭ -}G+7]xZGuyu촏]UK}"bzrj5'44[ Cg($#Oq'nn߭nېGPePl>|b#|pl廴f{ Ӏfy%Uqp+ğ +9k}?|;DTRt①Tz'L9$*t_O<]H@`|OOXZshZc>s[*2:׃3hX N{+mFg&3 &%TmQI֦FU 6 e 4h) #! SY&ۛ" BXz7qٿ? \FF8PY>Tdk5}WTLj5[ x/6)j'aw9vz=K!ԼhGC єz]\jËhT,pg5NK xg\A.u(S3!ݞ5Pxx7CbWh{EfRPI,I^Nacʮm2JKIj<ƲleϑuR;0י]`ߢ8Ufc_D]_|/@M3@9X9n :%략_7;YfcuMK2JIei>uC&q:kY=s/jvhvKV5$ۊI$m#Y'˦5MY'28. 1:¾q:kø z,Ŧ:Ǐm7^.gmo'TmYY2n~;xv&ijoZ۵W[D,w4lGRI$2CM#uC&C67_+/x`>{ll/݌Pqy5; +_GM#cm —,o$-;OPUc /UU.bmѠ;,19?1{??_0xmwï&^ hu @-F ) Hv/ q ?`_=?_0xmO_r _ש~~&ҭ4[rVusۖbϔ'B8{D|D5 6=Ɯf6lm42IKD]XM}uC&q:kYυ"kikou@y}uC&q:kq_-ݶq-ψajʱo#¶ c'pGlToj>χlrd#M.1`|}wï};y;KyIc'<; +_GM#f??.)/X(P0 ʺo Jlm"^x8}uC&q:kNJC֍e++{wMy|/x49i9=Z q:køTZxZjq³4d#xAi WbЕNh16$0FV+9Q~ i]{}_h I|3F S IUF|D*wc$vWtcFҵK{w+2e ?#M)mB=6Ƒ4o%6Yu g[ PU&&M 95kk]C{ o4svH2_XH{NT$BL9^G-U1o5?J՞k}n =b(ȖEXu H,@݊Cs&&=ܬ F#8&4s׺i|E|EtaKqg t>=z*"RT;[a៊?~$50 q'9'u*[mel[Z Ġv\8AC@/뺵ŬvM24p sĪi1ՙ#vj klK7}d&Gp՝m ~YF Ϳ 2׶gcڷ/>"6:p}R"}!=TrI8jZrPHAhV*ˑב0kB|iR^mocV?*psޤEPEPEPEPEPEPEPEPEPE|6VῈ)[e=ѭ#$'jSPcּ'?hNEnn e2 6BC yZ]YQ_1[| s=&E55+{lD LB$,|kg/$,Y,I'IN[ͪ_OO ^0ntn OL %]`qbFilE?3,0 q<+vEQH uxsi'ݝRq-y?MJ}[u+__YIN yq3[8 r=fcHktHNs8|xgM"1,>/m4^ N[9 &xnYdۆ@?G|- ZϯC^&e0 Qmk[Y X[sQmS=_jn]Ķv֒>&yYx(]U̬9_㷊˽r K]Hm3M*]ʗR[ʩ n@*ǝB[[]Q9$w=(PQEQEQ_)HT;ıtqZH0%\(wyjOtzo°xsZ5C>tZ%|$bь8D]o>C'S'eҠ6~-$m\M$AVs$u%BS.k+l ŅݤwI]enM6WR2F 7TQE!Q@Q@s ¨[ĺ:8 ?tk3kʰ.HT4-ǁ/x[0Mۘac199-\B|,koemk:Dm6_ /0m+Cҿk? /ֺ6ƫwOkT}Yy_iQAM?k8?g_^;^ mm>p$E1׊k~+]?q~/YڝhNb~hVױ3"ʪ*F}z i֩&D5;` 3a]o//.uMiZC=LUъS A;0V9˽!+BkTb4ǖ@I۫>{ ]5Su&]2Gb{U,DR ]w65xog9;ĖַsW۴Ouu]UyND?I^45-u;P0M䝵iTQE@\lnTyRA x0d^vS:狵o\|;uEpiŊC"D }9~oa >^iRi8YK[+!9f$ ^77VO֜Ż j*cc$/?Zɨ^/\&ѵ; *4q:0 EOWv~o f]Fkeס\7p$ѰTr2۰ rsxn[e}qypw7S0@|$~$xoQC[Zj[n%$,aw0pT^'i߀iKזF5+=Eb 9eOL'+BjRiv O]U̠((?`~df[赬Ʒ xsKCѮ;a;j"ݷL4|-w(\[xvMgJs4ʖ5r aʂ^Yr@'#g׍"iOKlL 6zM[U ;K)z![nn.] rYM8ߌtZQEsO>r:5OxVHo9%3Anx7ڻG綹=2h\FcmǂlL\+& WA"5aZ֚Xv\PI澔.h;(.h;((noWN/gG+CNֵ_> jMil4U;et v` ^^{ \?w7"Q \?w7"U]Y%~le#O?(ImZu[ߋfQPOk;L(ȄPEPR|4?'u^:~j6څ\2[ q*8?LZZL|C6V>;* Ka \-trќkI x.OSBS%%+1hf/2Zo4OȔo4OȔIi|_ 7/E}?|Cmm_,t [$RJm ѷddWus%s%hml_Vu_),e[!#u}RŮ- ;bŚ87NPPQE+:_0]=w(Z@pAhǾ&ߌ~%wK/Ěr]9-b.1*|t.\on*E)vUI')a;Xeet9Vu:MV ]Y<|7ӵ5}*)o^{{6IURa~nWM2|?fxIқIrmm%W$*6*.J}?ɟOޫN618`:B=)oEsAoxGVӵcg}I2e}MiS`G˴*z?Opjf m,OJ/'#imb, Q+0Čp]suk>Vo_G=Cz/tK.N1O+Os\^]/ü&ƿG5+3ᕋJWy7X%ycE_,.pһh PVNԴ x/3|[9Qߵxϊ>0kSm:Ri\X],o'-`vs' *E{itL6|6;^9ԿR482҂FxL\\Ue^yڮ1~ji\'ƝC/_gƗ6qǨ3N4 T?x r L̹Y^woOV{.|% 2g'nmn?m} 3=[J$:|,n齰zcߨm,sMmnf uЋ+/;|ϛ3ګh )hjnkYY>Stcku\uR %]|)-G J#xY%XIV u[ |l*"ǕA06)k kw-5FY`!̌\X3޷oGTzѪe[};EϮ"'YլY͵)4E2Umo>-Ŕjظ[Xkw2>-B֏4¾ Z?Ep=PmnYtb.-b9ܹ"|u-B֏4¾ Z?Ep:xJIxՉmY\GqoipOIbYt 5@OidTreeaAA<33rm*mO?t'ʋehqj "~_eKVg+Х&U<o%͞tke)veٿ ڣu; 6N[hjK!˕;xWӴ3.exeRL`c6?xwoA|'B"4e9.+3_ ЫgNPos]+΍ yRdR78S@G#O?G4:f ?t[/4L/b($ 1 ȠGNhsI]Q@/O(;'v_G#O?´h G4:i? t Ѣ3e9.+FsI]Q@/O(;'v_G#O?´h t5`rLVP^C)ς# OKjvl.cd]yXWd?tdq^^i.cx H/n(3+,.Pf_.Rnah C A[ZFͩ+\VUx8ѭU2V#9(xJ @H@8+oރ=,R$6߳dcz};~$[K]<ᇍ ljOWsG?j\i٦w-dĥ:DQS;QozD?uOVFchMjYy/,y!Qw"޸oWi#8*7u`8ɤ Tw۳m>#CJי9dգVe$$)׺6WCy{䕥*Y[$81h3C_QwwKϥjw+tT1eTFd S(>wc84K(V2rm4--}އ| ּqh?WŎ\_6hk|10\^მlg \V>~KEVXnZDޥIU3U4۾^(4*TKk5 [O ~w Ɵy2Cce@zĝ$.I1C*(j¸61 Jr=gͺtF>2ƻ>{УI R'8>"?5}B-Vv2.'"3*g8} †5ݟi)rmqSNJT~И[]h ][kxIa5Lr,%! qpcV/-%ki̒1'!TWW%֟zUQRPQEQEQEQEQEQEQEyF@&x~-֠ǩ_iIYV #fNPNJz+m^'Ѭ_:ٿ[]IV[?|@rs]O%\Sc2psa&Ū*l#OCR62(O@ 85? ]Z:]ت}R9 ;ʌ8Hg\ ͢6|u{it/ w 9>m@t6[ Am ;Ìr  r+Xh>(DFK:/XV.uۋ8mx E:7 ˻!m~cY$D9omg 1Id9U"?Q+ېpFc?*MTVjzZ[M4veә#g2n<6'Ev4_iG$ZgC}Wq-nO[H[9o"kt#.*g>\])$?iLMO, ;ר)cIwwZfE g?vV28#Kج x<=Hn^+[= @U31$؜ 7i|4-"Ik%$FY]9Z]+_*t$ReMA^5mp@>Z*Z߆4e$R[A Bە $W- Vr,d2*,4̞h7c=3ޫxmVM 4(((+iQ "j>k}q gąċG`S~q(pl>/|=j[ױ=YX?jE!s<c l/u_'u˟ I{˯>p%|!XT[A$`[5}E;ߝro+~>5=n_|>յXxIcq&ZKEBV6c&U+׾ 2=Sm>t;"NI⍙F HdRb7ωeγ?q-!ۏZ&l{<͌#+Ǐ>VZVRyj ǽ q4byV/voxw<O5ψӮn~4ֶ<B%%#O->%i5uٛ`kpʓ:$%pdc׼YOXik ^lKqsk-(U wέTơ1[޻{dZ>d`r`^֟'z x/ýKGT$,#PKg+I n ݷè]K=vXn4ilc[FkRmI!E.,>ϮQ_x;4Ծigpv,˙>c$zjj:ީe=׀C%cR2T-aO k~Vz"]V'k*,+zKpen8!cyn}b4#ֱi#9lq/|zg,Ӵ i٢UsE6$iQE%Q@Q@Q@WtM76ZuwQIt$|S$`q8$Z:P0A >_|^sk:|yJϵ2I{PQ^7~_[{[K]r/jS2^#RC'ΣM?*_ jZ.D+P(e&ސT ]/]Wk_Bյ ";MS—+No},~ҿ6vj?ex>\ռ=>뤵V]R.Bo%Ucn`A$S~bו#h7uNmc^^i,2iA9ibh\6q5ŏC{MH"H?s781Ӛ6_?6{"-xKR'nb <(A$PHʶ3oJ^Cf^ 43[]䶬PcV,~?-}&{]=44CCֻOWeJʲF$Ex x*Aʆ0)QEQEQEQEQEQEQEQEWqwujڛiww# r 3JhEb Xwf'Mr|0~x'C=6FN{[snJw85Qz%70NV o̧$#2tw6Z u/r[EzE,Io,f LsNXj~SFy4o^xoJO㶖ݒbKy6@WWtռ?i0Koo-U)bδ[2+XJUy[^?F O[sqv-A(W+c >$ ih >c;NZh qxE#{3n?>8|gX4FwYơ%ۗFfR{½KKy~$ ^6q?>^OQc>!.#;OEQȡ-Tn<%|ڊ)(@QEQEQEQEQEQEQEQE[⿃oi:tcRVylGU1`:a_#^ZM<^'(둌A#|Cx> B s]K [RE4񼚩4_jqgX^Jമ_tS}WY}B8^Ed‚p ORiz柭ѧCxl^BἙlta_]u Cֵ?qx\YҠq%*t9lHlcg<xHoW_MUJ,. 3K9?_>Ǫx_Gukȴ6 R5I=|*| j>7}>I,^k{FJT@.rC0ƚ߱f7%]W3hy*?g#5-.__.Sߡ.!XR'ĭGV g>mN=J4[չ94"T(/a|{7|co4Mu-C^kI%hhcs]J, Yu!ϻk/u}kJ5Q绺Y%U@oEGcK XT U*+/?~t-Km[ii^Us$M& I0e㯂5|;a]RoIՙs΍|؋ oLTe'WxC3+߃>7*|Ys7|+ Mq-ĭlː100>U6gs+/3]_w?[?º*KUm8;FzSW:譣&i N-XŚuYpI {MᧆMúBivVz|,@E +[]#_G$Gl!?ƅ4' h>Iױj(L* b*OIt B?l#pTG: ⫛H' .A[/Ot|C8?l#pUKVhH'p#u+Mb]kh-Fʌd0$AjH' .A[/O/]ŧi18eY.yğ]Ar xI.-p+ӿ%?+e 4KVh.>^_>+kcqd4fO>hWR9jsI<ɬ|:>8Mqm{ =?wS +m$%[%q^ .A[/O]#_Ga5seK-}/Kk,.Of6G;QG gYCe펟ɳIJHą'B??5m1u+7sMawoq,8ln;s`\|^t~,i^-F1 $.`p2Qk>/~}M }M5q˻h98i5n\&y^}gqc#sk(9VRA: CR*DgwcUTk;OIyƪئj7ū;7<1TKi40lN#] 3Ӝde[i>Z /?ϖK5\מcS)y47̓$ Qn1ʱj>$tZD%QwE0 w)EKm?Y%OIyƪ9mhkz|:-:K[ N8=cXŝJѼOueX7L#إ2J ^c:[Zj[& yg5p:wc#kJ8uGMo m7Zv88uڳwWAERQEQEQEQEQEQEQEQEW?Ե/ JүuK"KM=1DѼC2OR3_@WZxBN7Z"76dUU28#:M0j&HӞ5f9c-,!C˅@btɬVu-j^%-Hdk;Hb6rFHLc +~?/*QЩ}Vzv-^gx5fFHC\=hcF̒)7r9fŸ uK5K9.KmbkGErNYm`$,rN On^#R/ W_\C7$U8IؤVڻSͯ|:]Vºto,fbi#A#U#8+OϬkjziogI`jVx^m|%uOͫii5Ңyeuظ"*S^#Rj kǩ_4)-F[8 Q]qu{yt-83MRaJo r(0(((((((((((((((fotoxx-18.01.1/images/albums3.jpg0000644000175000017500000017572613222767271015221 0ustar micomicoJFIFRExifMM*bj(1ri% gnome-screenshot0231YƠ0100Fotoxx:trim_rotate|trim_rotate|trim_rotate|paint|trim_rotate|trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1600 2560 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((c" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?fI1'O#{zR*ſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22yy\,[(*GEſ22y|XԼG!~*LfG2P '`jBF3(:׃{ Z7{ DE)kr~nI cqXO22X<`4uXd{cn[쟭9}Wʏ|nM;6uzX22?5[[It[|@ƒ) 3YCwB O-e%A?R`qz\\|a(+ļO]^4]ifi|a $I# :ޟOymaix-v$ݳ׽;ǵySUl o+"yd8UEyEψv{ )vT621ҹ1̲F* R~,N/N'gRf#+c%iuɧX<6t6gYv2iRme`3Pb;,B[ R&̲ئXmP(psJv AE@z׆xvNֵ?Pl'Dtلq c,>C]J-n5>k/YJ[ W:֝kq\6\*2N &kiweky +R6݄oj񩴑}xUy d彤2[>vR`B[G` K AUwV`q JZ][QWgĞ$}Fx_L{kd% Ą:-fF WSdY#8Brv4\09T#|<ʩ{{ҸXQUO3ޏ3ދ~eeT==XQUO3ޏ3ދ~eeT==XQUO3ޏ3ދ~eO̢K'9KZmg[,-i BƼ~!@ ?ŽV?0Z}1=?t?š#\G—+xZ<^^R$4)e/e FGN/?TXs"Ϙ}hV/?Tyc9Xs"Ϙ}hV/?T"o$WpDLhn]89UG+dMZy֬G4}G4rEo0Z=7=7ÙG}j`G`G+dVa?aa9[>Rk+)ྚKXh0zn>թ=7=7ütٮ&n%A, DHx[ð4qhZR$m]/`G`G+dc^B}OU*;}H[,b[Vp(As[aa.{{P H@RX[Z֑[mmE EƏƎVFfigYkݫZxj;;Ob=B8Qf\/# h hǎMGGҵ8Ql⋄IW qO4۫l-&J: =7=7ÓFd4'Q=4 VYv qoUiHλV_߭nG4}G4r2+R'R#PhUImBD`TQp8'z h ha̎rO xzX`MKhDcI4?'hsH lDn9 ƏƎVG%O ZjFkb}f[Uvp 27p02xheC٤%DUd>;5=7=7Ù7zyiޑm)-ђ5(AfKt=Y ⷾ>9X]|z]JI&]6Mp&qAa9ƏƎV rZO@:SGnd9]% 0pH h hatb_"a/7k. 뿏zX2)-:%o* q6) Ҷ~>9X]^7F-NCt9 T i~GYZV%V`G`G+ }hY h håa>g0z?o0z?o񣕇2+y֏0՟ƏƎVȭZ9Xs"}hY h håa>g0z?o0z?o񣕇2*UeѢVGxso>hbx_[=5PL[琒p;VX4@'h^[jXs۝Ė9$3YjJ73*]"EPĔIK8_\W>qe %u<8P3j[4i`}^?f9Ο)OuJ's>k|5.cPWK?}~Z)-,5*X q?:uc_9Jsŭg{Y`[L{$9eMlU/BK,洙Xnel2n6Cu_jf}1pbpI6Ԗ>uu#"E }\2GCMыmV"KBR?nA!0AZ'RUH[7pǏpi4_hfVn29M!BdۿW<ȡm]Ƶ}GKHb̶/Y]32y栋ƑYvVA$6wZm}9(%'kkEGM+$qܴ$ee,vpxXڇonBKZtI3GP{/޼7i}WZw67>npK5~!icܶZp̓*D"rmwOf^PMl[ۋo2ɤEsǘ7r +ӢZ=ke2y[?2h;tF6Evl#":nAWb9fljSwkf,nQX-jDhT3#,>quxKwXVn$l ̾0֫gYOcIl{e7*wrAshcD-JC*[޺[{Qsv+8s,KD\M>mW%Fp}v Q=(BICs% l̪6KxP:&`PYO-9!C')@qZعo.Z`vj#$: - N7gM;A֯.% 1$A 쎬Z@79t \Yˡs2۶w|wry;W;xs^FVR-Sidhwq󞼊C/Z?^#^Ejl-#Gg9*  ;ی- H濒 ln匂R';_7mimװ>Db$` lVkI^J2oy{dd{|FL(.PvM2F+O֬&4xnh~SutU{-Q0J0 k>mZ\ !i 9Xǧcdz2=EsLOw'V;@ 0ܺ}8y@>G#W0vgA~T`h#=tQ\Ot88w "Dmœ$cS(MTi~FGϞq6(oYӞ`:r=E{IIAf=;Re :}?2ǮhdzRaltC?&p:cڄԘ۳>h&@r8z2=Es6`h#=2Sw':mCm;u9Q\Zɦ٬Y8`rH?Smmqހ:QFGZmM`!l< =t:_֒Ŏzv9@FG#W1>tGr~eo\ړ `˧ ͏@>G#W0vg 9ӜJ[}Hoi~l:lQFG^Sw':mCm;$Zɦ٬Y8`rOz2=ErSmmq60oY#Q+ԝ N,vcӱzY&Xç;+| z:|QFGa&O3ן҄Ԙ۳>h&>PO(E}O_?[jnMc?#Q+Ss1Y45'Iփ668q@NG#W-60oYמ0-ΤWuv{Q(O2Ɔ>ܟ[c462~>Bp:cPO(&ݙA0AҖR,>3tQ\Ot88w H73MHpN1hdz厦i4?#?<ǯjnfe>`:r=EZ(5XyUAnjxUwZfy5e lc*cWJcU=7o&q$ʃ~JtB9y4-tmnhee˻*q.hq%첟-˫! =FqWY🄵(u<̍$FdH8^>K_wMRyo-i 6?*\֊ԉ:fyƝE۴V\z3IS1|yF>#LlJck8:f|=ZtEe q M̯ϥQ A \\L#Cb)"QikIoRUc-̛y1@zSo .3%Q܂39z/%kZRmDjO$?/,Jl$z(tZz3/WMΥ`}-2[<9\j8apA7Ļˆ)!]J#yCFu,5r3W?=>QUaoKB`7ub@,@=S.oIg ˪i[OP}\)w9ki&5wv턌|W1o˫]_f/ae*-LA@q]u0W-QHaEݽє[\E1ryncqPQEQEOWM7J;-iN=jeMV~ƒC)#;0,h`V_iŮƼkI-onccUQrJA?rmhbG)`Y6ppOr3Yz6]BM; V=a.U0V8uމ·%Y7scF/x}2g.["'8Q^KA+>Q9 [gWJE 4.(dhC 9\8>)G:Y\Ǩ@-爤B"¬ޅr 0j[5kKSFϴ: itC&sc =^O%21ʷ]%rПIQך\<_0DD $eu4 (&x`xbDQc=QP\in I+U Zۈ 0'qE/Ǣz*T7wVps[®I S$I#?yo <*,Ppf'>k?Ě,bW'(X)Rdr8TK2O yΡQw%#rI⹭[7zzM̖h}uUq V 0̓5m7Cj0]y0żx bK& )ڠK,H$1;@;p3@++I,a1Z *RTǞA9V0bSjqyFf8vE]"̕[Ůes*H8 'T7Nh:5F]>X{ǘH`.>t5䚆iy5[<9)Wi ս^mOJ 2;SPE [ԖI'vxVu/ AΦ1.ﹸ*t;8`H!UD`HAB{WҼqwiiiss/%+v(&@xl;q{DR:T0YݣLYF2W,ok=2%t%FV9ڠpTcV)tkS[KJXg ve^@uTQP][$(#fW 2{ځEQEQEQEx2i:QAi=(5)j3д M4&aa[v"k'N3=><<3xE-c, bb]xN5Y5ˋئXk l[ ۃ߭%p1A+0_h_fUuR2&F c5ιo Ŝ$ ?uK{c&O{+P^nI% 7UC€#\``V~j qiv [(_"W*XdJomgs3E_[iwwجu8xgiÀ7nWq짧Jޤ΃aIl֦B͸sW/_ y!x{g B 8dܜc9UBU(v EP0(τ W VI 088%"nE|l>P=v"2CgEn뀡FGJg֮V'sWỿwzͤ׭yR`,aF->e>abqn{q¶j v?KdliPB7yy8\6H_jd N k=eL%Z11_>ap?yqֽNi}3ցw7g*:^)Gi,-{6[đJĬɵAO͒v iIOskSZkzG J;; aT 'H ͼltڤyq8$p6R6֏i2Ŭ )X#h2{zW}L/o/ls 9 k6v:rSsr-1:fTLӮ(A'lj5j=.@p$+7č/-_20>^wڻCO{WnRޝKg,I;͞&=z|{w,gxHI}VFkd'L qMz?E̚,*.*g%@' h T Ͻ...L8 x~⁜ռi\YA+djNw/jX/N=ԷZ C$}ĕ^oH(aI0 O,i9vX_tZ!7)%@BoR?.ȥvyiIvSo^>SS/7y|n[ˍ- -.ٮ@bYrT#ZcgĚh.)l-bUv2mep0;*扨+mo$&)Oosnatb AןM& .k(Ɛext6Ӽ/ue/:(Cy@ p %Ԛz2ďn٤a(P1$WkJ,,omgxn1[6FMl_kgeuuy奼qfvTd󸖧2ЙƷEmn'hn,T3yeS?˴_H{X.4Ik}omb(vȐA#6I _iwF%2˲B2*sڶȵyeK@[iKc ?k 96Xu g xg#o# s+.6{Kb(HRFM 2Ě 8;kwm])Im٣c MKai b/ C$Kb˸Usk[L6w| fqʃr>[?5}HO*4B)8vA^/Ihm--6bH( d&0ך؟^bΗ{<وAdV.9=1VM77P$peCTP2Ouwe>b${]GUԠ{2xWéۿ9sWQNm6$%mu{~=\\3L>t93v /4R!Bk/e:9縺 J]L< Ke3;%|Pֺ|R5!7wcqYv:^A\jKi-햝@yCÂQH+=[}pڅ,HiQs0={JV;aIJ0#,)ju;3[nbӓMtV#M˝'К ;&C jd#zMCD^)][e(8B3v3˝.M{fr]&;|DpDw;Wm-[:E71BN2nAy0xZY,uY ?go$$~Ei$DkkCL[r#*))NajvCpV3y Ecm?sh,to@$>ܧOz7zJlY.縖3IYX3p@`Wk条_Aqm8T$ܲf70yoi.+*˵dHm%cZG ɥ+izƇuilrC 1, -LիsR_572Vȣp+,49.8.;( ,Ih0֤hcm`d$:r`sM$r3n]}tY O7,wwftYL6Yyⷙsh2X[Aox$HNFpYr78VL1ڜ[5{y'Lq9j*O 1Úo޵se5ߙk-5#hR3VMwI ˹F9RO wҢ$}ɕW(,־&wtVۗm6]P_;DwXճOI\ww2J?tmR[gw%:e7+`qG5r}7wiC̗İ_0g]9w NzW%OxN6gox)Ȍ4(J4xLf< QiZmh2+X^O.d.<8Ȭ~c>*JtbxGMRmD*U\quǟV.ǛKSG 9k7o?c2`PS=Eiai]_K~)LOP#xe5i|'u81ߡIA㒲h-uMgtݞPmK\>V~ˬk1\EV: w>Jph>ǣH>iVi#[p%%$by;T&#\|2xj2.?H)mw-mF}?Z[ !k6ܣ D~Vd@ "4k0^ꚹdYo!l0B#%I<xq>,`͂PS8-ui~#8'?OVlԊ==GWi> jcl1Vˋ]*m._*~X`YW+fzNe+mċ9==[K~kE4@6pA`!H8>V\d) Um_[jWhGFRQv; A8zf[*s{+%h#R\)wkR9`?UO2wUяYJI_cXlǬ_4϶0Z<rO0ONqZZksya-ͤ>a vB;*u_^?~,([iӿZ|E3 O則SXa'5 I}CVŝYt\ݷۀN@$gn3W|-PZjhV]vI{TWP  <bY?,d}Wwm1^m%vT~Zeiڽ֢jyBw0ri1ҵi?PjYyr(p%v~\_R;Rj-B,ԝ+sW~5??aX…W{ vx0בE nG Gݬ7G4eӵ 9W0NҠ7=93 inڌ RcLL-,+]1Iz[w6'R۬Q44}w\gW5F` ;ySu(`U|){·jn5͜3H`dv׃Nڶ_ZcVΝ?*`7X:c;u5 hGBڑTGM(muY6ݟR63T{ B{Y5DdDUі6 =rs^ewB6$1\ŏƲ9\"3z<${J[Ϫ5HmZB6%$2Nq5Kd_Okv 2z2{gG%F+*quv F}4iBm͒f  Py$W᱿Ե[ywul#xD,'+]^7u??W%YZFfԅ6eP/ UmBՠ]LFЭ#"EP * [v93ּ,j SOƿA.Vu{KX61[YdAV>bBq~ڴBh4i$u}._+8`?W| ?\khuU z1gj2Aax"1E5-OU$ɍ玕0rOC.?Ous_ʺG7Cpyڕ>m7'oGt9a4^G%T֒=M4LUmgiҠ4Lh1Jس=rp0j%]ܒc!4/VյmZ ml_%WO=9¯=EPBR=01Mi_,x12>m:WgwOh&$`+F~X-'cpcgW<g 73de_kG S}VERi&̒B;8[G[sY޳Yl6QE)!\qZ:ޗy5&Ԣ&66IXwh o 8B\AoֶVug\Hf*LsR̛d¯?9]3X{-g)ܺ|sƲ$s*,jO8McHo5⺸{ȖWi<$sӌksETC1O?čGN;KȚ5?!1[<#x,շsҤrx}ƳdcgsY$irg;Oq)hg$}28ϥ{DŽnuu;\O5H.H;G)'q2MWPM94۝.^hUZ7ծonͷm<0Tf%s@GQT xZf]Q'_3],;h5m wbc֩G/ayh`[~¹=9Zk>%[25rqdqɮúw^W5sgӌ :!S~Y_Z.AukuuIiwvRc]UX"#ޱ-/YH[ziEsCedTM 'i<[WXI' FF$znT F1*b5 `̈<#Vw1- kd1brTE^Ef`Vɀ7`p9ޙƭO9^ uZ$장ebE4f+xoBp{$Gf[SarK͐rֺ BO5NG85\V9KRU6D mzq^7t̎ N?3]$e;J3 (tV܀qڽC%aFT cb2&;ASAPǺ3՗}jk0ݫkY浻[G$ȇ# 9[j!;ͤMk V%4o4*)z{P#c-}nt(S/-5 TkաR"$]f䚑׳g5qd730}A *>cgc6֛|ma0u2|CLJqrvGjYx9VҬ )x7ʽҹeR/ S]C_1CmTc=+~]*ƻ[ޱ;cApj7885dGq(pyRy^}I$=Z1n+Bqu//3+_8>wx~]Ny6+E+rŸ\3#FF@=GNk&_465&b0;#jq D2o^~ʸIxUdu0ksm%Ndg1ScB 8C9S!An_U`\y5q{w:+nUp9z켤|A$ѱW^2ψ7 IJNRJ-Ax2#Ȯэ@'Mya5„dS ޽ opSo)ԃ\ˇ^LrH?΢>ݦ%[ޚ;d) /0\9z"VgL}ϛi-K75#`$̙<6qf3۴ec%r =YL6Zwȡ87OtQR!g$^BF2g'?uasfeW,ӕ= ڙ5{i~"̢UYOnH=1{Cj0Aj$z^_hVfR;ćiF| kC \{g6l>׉jw3ùS:34 " P38L.:sVNh݄^^ P,ފ:yⷉrBD*ThjtqIZ]Jzg~ ñ8OSB#q=YC(y;{|)! ?bTy%cѻxos^ZtʷK % A8㙦_;#,R T8?JNG;铰ٿ[*oIչN O׮,%xI.p*yS]%WkrS|OְE6X爴by-bB*p>ꝍ'C)jO -FDyD#:Uě&ce.S;$֍4m~"wj;dl rMyvc꫃&2U [yYtp۟; KV qd䕂$a+÷:M-N@RĖr2~X"iުp@8ޝ4fSmHȹHg.+cʈsv^ | 2=UE_0 s+9s^w9qudD\jtEAK+2+RMmY1=}j5p?gAj?1^z \\ƲF򑑝E겉\n7ҺIvCaQŞTC/<`@QQ7 m?r1˂3}+*KF۽Ϊ3MŤ+ <995Bh b-٫Li$ƒsU,#W][_3$mM/lX?! :cjĻTEWSVH>6ǘ5//إbSltV΁4s۝c5i7GVCO~ʐnL*ɫ.O< kV+zR23S\G^ 3@}Z&w?Vo5Jr=kO^ L%ԑC.>_3'ЃϮ*}pZݶ??nIPu9O|ɈebIM0;*GQ`'9TW/cf:nxZmr9-EiYN'"}2C]8ZrqZӊ˿~Ƕy5L/ %wWV%سI'$׬Eoicdv 3[ƚ:Nֲ=mr|ϼ+[UOW$ gɨ¶|yaD_"Lr䓓¥:ϰt$2 QC=zZ-o]-$5oc" wu^|I:+kє/(7=Aj#eRInx9VV+զ,l+wԪ#h8q_6˪Ǧj:/Z6uE2~05Nƶa=HY#e\Hb9^Ux_osNi(o-V6,q1߮:Wjw!2+ b?|qIͣF&EwW OSO-kIyo5[[̍JJ8 ^h54TI4Fqe$.N˝J(fίou;Sv;F|Ns#p_Ki:<^NW<q^upׯ + ,])= '6N(kM>ko2+/2x B?kՙͽKbƫ $;k?VZh% ]]tԹd̖t99sPEe@Iq欳Z*$<*e-oITۜ'+5-v#i\}I߇?WYd 㢵O`gݔcl^1x9fpx^jeg@\eIA y]BtIcGخ78\FM |@ .bu ȪA䞞OViG^sN>KX(Fh픨۴3"{ەh.J6ߧ^&<($ 8C+;͎FUʌ8: ˤvO<ш;<bk;+c s֊qZMAyM4 Hx* ҚIc-oJ.vbU# s;_CͦS ֣>Y%uͻ#c%qTMiB.O1㷽T) Ao q?2y$֧NGGGQ.GL\r^^ e#4^:zԶ\”hTne'sSQQwSOCt=ZI Sq9R8&-GY3s*N 87ޡbQkeVgRx8ݫ+in$vb3+FĶXk{u1y֬V׉hFkF/ę'Sfqb 2ʁ}h{g&pX'?vOڮh:P5ܗm&`#rsnþۤ%o>0~a]&bM^{c#D|봃~k{]|`{&O5z=Rh[q\4hق~UNMBv*+Sq^kDz|j Ω4V66_̋{8`ܕ;zU3|=,RT wYX7}MmiS;hx{SI25m5-\i]ؿO/^;$~Դ EI6%1Fz֭H4Um%6Ga̅"A8 ׭7)-?Zq*I-oGlV 5fuM^8IhMY=?ڥ.>jچ+sl҅1`W\c><`ݏW4'%y/ >QԥђKD1i IAiǓhֳgiyew,ʹ~\⎶ O/^ixzN}I(X&nfi1$bC 8G]WR#Uˎd&Bf*rH ҅y_ʍP1Zq*-} m+s*8$ON1|%ۿ)X(Tps}5 z^+ln\ۅ]wgFyVW (WFǏg$D\)A8*V\;K p~8[n٥iDڗe7<< mGiۜt$^}*߇5m&NSuUd:Q] k&c aЎ5f-f̡x;N{jjK==+:8n&ո}KXM9hYH*kY+k 7JCq=qS""psu<~BH~kFPgx;{}Fi%pې!?^ڧC{%`l2|,tѽ5ĽQoI Lq0ȫ'3 r1'6> tY 3H4B31YH6$+il37Ohy ޿&[]1u/Н2Vv +9nfF±S:_Qlf w+*SLAYAiVV55f.B $s\xGm&ЭT[ԃ6fX|8ȭCZ4H. E$\>®[Oռsۺ z0= CоzJ[Y=f u,d c) }нV׾M<:J7gM3I 3vrĜn9}$!Y\Q@$C+ib7@ٱLK$O#0231 6>,umncyUČ7M38;bIІuEWlG wt$~u" ( tx+AͶ䴷8GR$N{Ef:](Ϩn-ºɓw|kYhXh^ qS8x |O!k*o}q;F",Ud/8<`LZIoDfygrY'Zea :\ZLUCym=Ŵ3FDѩɏpvE (((((((V:*;mn#+s<ʖh^eeǷt? ]) W/)u(-KN,F3ϵuSq'-5)TQmJN6 w@3Si1jW-.Ȝm#pBWG< ZIR ߵpzWC_+&P8w>s{%Ν[& dx v8&lfi XYW{'y߮mWЪ*}FqM.:t*Rܶ*3SEYf*[nԚ׃{ULdc5ieżCc$W;.dE"X[ JEsSQ(zͦ^\T Dv5/oPٴئ IY$u+^Ew/E{/޼EmNrjZN1s.Xƞh,ZD @!k4 ⹆X'K8J0QA<>T_|b-&D?ּx{$BKK^ga}=*cʷ $; yc Ud7^\H$W ӯS=L>h_4 G1hW7qLУD䲂@9-ŊIݫ10ONZWfIMҋ-e)Y{H,ub|PJ ;M$MLlb;g_Oݥ̧̬s^ c]m紹SNs\W|q-UY:МfuT] e(dres]isRܥoi{ssVr&9iRqǨphVz΂S,6*ct(dKoWAn *70el){wgK n}ڧ$xYFjJ8p㎍mXO"8'Eu7qw<,33˰4VNn:Uf|'5+NKOfأž"iu\*v5] jѪBPv*"$ר?2ɯخG"(Jԗ +ll67s⾙E.u>jQ=Y\uAS߽jb[կtqH<(hh4O:2\FppA?3RfuFbKrF{N}٠P6`=1θ𿉄d[B=+Z){$W_ZZ,Wjr3N;û=2I+cTV*3t>gE}T۳LA:``bsˢTck@xoX/m[xCRa]SP:**NI$s1<}CE & M;NV:.vof k*2$sץw?K_:ڦn*h-'@̍H55q1\]$ze̓x>>pŹ@x\v5Xm6Z6+CnhV2,1ײ>5 K}C-s(>~>n=ideCgldv%cN8Jy=KJ֮>ѩ|9u-ΠH ޺弳/ɩM-ҦV˝ c'? %lfkF &-(_ҥXc{v]&PT:b hxge F&R9ǐ-@?qדZN,ږmmAion,!a99k[KZԙHL+8x}TchIrְVSޣWxpqͥj6~΃PXԐJ'w^c|B6e4h%e9 @8<ϣ46'۠S?xiLhRC6 '%O9K<N׮~XaE&[svm0fm+g$㨽[ kGy%֡qb  ̍WpM=6kR! rzL2fmxB`? kZ]tmaXd11l~ψF kywu=1+ 75R'muiɧ9HRSgAP=McjZ]E׆;(D[N-+ 8י+VKa&9ͼoTWgZs'yG##?tB6<7HWo,Ҵkх`xnQX$ZޱnO]sl8!T灞2e56 {uD;qy^H϶g2FQJXǯ>,3_Z \̻ry92.Q\}&FSiqrU 9b%6dKȦ@ (H(Z;97wVm*hehX]deu9 Bz}Q@Q@Q@Q@Sdq3¨$juciזVIsis5ܡvqSM XkE/l+Qk<p ,1 1g5i}ݺdQ瞆:/ :_bxA24O۠QI>}^Y uE ĩs\WVvw{ YK/-o s瞻P׎_1\x[IIp+ 4v?JנxAeiV51ZI`*X@N~U))XxT9'KH{gǜwO9k(h7pgW:}嶜L5yFa1c9!x:f/R-B)ƦH # NPj/+> 2OKXNB`pO#֭φ<@]x7qXKWgbΌIFߗnw׺&k+To#;R5 žL^EX}nQEQEQEQEQEQEbAq_zAI(aXwnnx(z( ( ( ( ( *mecg;0as{;QN֦4d猞\xETv[=)]* mݢO" ,~JrZ倷#6xcIo<704M^6 P2Z( (<4dVo5k]RW+8=K\\28b*jNYA;]I)Y#ҳFkϿ4 wC5k'%$G->UViC+[׮SB#rcpo i)FJ鮫s^&TŽ^X\:ė2"n 30vvkwmKik,k,F L~a0o݌qEtεk6ih:^ۻy̬8;_jųKa.A5Z.7n6 `:^?ֺ4U}>RT@CQ,~R~;ګ;Xd#{kud6k rK&$ Sj fQz3O [#bqюy_h אƒQd8aj:/q[Vq.ҾiEmiy}lt]C`RzG;[h@DT` S/5_}: qaH&*[x Cx hV-Q(& -H!x\i-kEq>#ltѢ{Ǧ]HEE;)CZί4>Vc5l&c $BpBM::+pXj ot?9mp (luiE7QỨ-{d0-VbR/j"soY ZOkij-ekq-ƥIiuC(BV`?#JsZuⴼd >LK(ල(R,d!OCaTzej_5oshZ,0Esmf*0<س΍4w֏m-ݵĢ21ƻEW, ^@uREKuK׵[;6T̑Pff~sY۽2l8,[}1#ev qTv ^亗}—V,IwGVu908Mx.t., 4/RC8ݵ[ UK?Rf/4׌o#Zi f3L~c~085s-! 0iڛ^Z,mXy3000qH5k *CY$WR$~]/ÖRUXr]YUe˶uW8E^C[iS2$ t[2#([_p{ xJ ,~D@ `08%O@Uj2j\O00$HG!JwdqBAErW:γki$7gF 罏G/./'l@⠼XvOU4'Ve"I lڙ,GsY0Ҵd{_Be>iQ\Υ4.LԂ,ov!alTzd݁ɭ;b-SOw|*Ĝ@8fV=sEn̍' iHRG#sЌhIOČR;(Y7IXI/{mle>yYtMͤOZͅB &p* #O\WY_XiXX]_$6XE 1PX;.sC)H?JHtmffXe"H6.SH*_ r7V#tq m8D@@IWZpgPhaW|| !I#sZMWGN-JUBmaON*? xk{k5C;ʽJk9:𼖐[ZD̠,J~It4]LȴaBz>;\j7'!F+= 1#a.X1S/+_H m VEm!91;^ BuEfxM${lk6*A r}kGÚ:o[CqSnw +㧯(EPP6β5km*:6=[\&}W^wkm}CU k҈O/RGW}>O-CmGwm#7BMGBM7Wm}MI}ഷd!$'%k)! ?bwn_1nBD,0P|.=ؼ)! ?bW2uOKfKWZ嶽}c*K DxU H=:POڴb/m5-R⥆qV. ׉J:o$H, 2]K*XzFM̺ܚww_ک,R\O!Cv}s׈h:{ /Y,ۈ-2,R@5DVOhڭ3~oR[呎C(ߊ[={8ne_5Mw8Lmo|7qks{&MwzeY03mSWk?:s{iREvoq+ƻS `gROZl~jCz_%u&ݘ1)Ey4uiP\s2H2V7*Ep lvQi \K O& f=2c=ӵ8MJŝrIf÷vMѾw i˦f0s~(T?^[jZuƠb<T-=odGrۜ26#֔2],lNJ?xYd43!zc {Ep ڻLZX#ueKi?+g? Kw (Š(((((s:RLH)kҮt߶?t-1!$.2$^IҺߋwv>'ӡkF1y`l::dW\Znǥw 3 @3l8~a~GQxeDu Rѿ-V;K*r7?aʮg.L q,cﺓOu i" z-,JNHlQj V-I"۽mA#ę=Nv_q_KK FSХPv@+m*zW_ lݭn!T'^ dsm>#YIcEsu4M̪8*$6s R=FE7v4+ q#&|i}^\ [xJtlqU~ZXZ%d"dc3c_QmRٯGKK[R (RX!.(RIe6`fQiHLM4ndک%f&_7{n-A(nF~\rAFEKPz5Յv -<3=i5[jS'Noowy \, 9;ʪ7);Gw)cmVN]ZWAiujOms5q.r\%Aяgo:d7.eJIq+ 7H #X\H Νݳ"m.AdFxk=/Liog<d <6FJTOP-KX"`DQ8_ݼhXMS `]䐪*o#kNҼ%߆[f,Aa<E MQEyW4N*7kӵOr4ۍlhrJ .?t*AN >%pk+FL#]r0$/MD?0e1䬘[Co7/_.WiLjEkkfR}^OI\$Gc%tQU[%nTcVԧjFD$ēIegOòjk>3*F|=7X קOQ`0SgSR2iIh/=ŴtA c3Ϲ/|=O= tAl,G'z=R_SGת `ӡ9Y3:^+szo.tk93e9a;EceS/:MGV5;VԬو&+D$tUn0CwZIl58ʨ#z M)OtЭuZgg1LzU k XMck[e6|xc=w(5RRo#0Zåxb[D :9T徣.wIrʲa+)L)Gyo#R𵡻0iPDo&;4_<>>rz~Ka[r1. Ǐ $Y6R5U= :择k5o!cT3X#:vuh̡xĊy汒2ƤڻKa[Օ3-YKZ=6kD128i/(F vycǥs%FKtG|8O@&Kcs'bڼt򮪹,|믯.Tf=zRsdE  Ө49k/閒څPĖS\w y8 vfM`s<̶ܟ&ݝJd2sHWCMɧèI$eQWz8m+2h5Mk 0 Uv[~W^A$IvǶMoQNɯto{p!l;EA+=X-@\籮xn⸶9C$l]O 8 Ԕ ''4[3[q2+ͭ|I+X.-tĆ fs]|W4XCR#JN=G'5+D3Ğct,EH`Xc$ d kòj_iTr-##w^w^ef3qq7{u[w1ZJl7+vIgSI.4.];hf 'Ƣյ_^MԬ#x[tm5ɰrʼF͚c(=1/SFѭnn7ffk ^y~ wH#'6 e&gz-m$1i6eXvl[xPqj˗}:6DmdCFҋq+Pc)B@rnG)H\ae v]WZuP#(ROx ̃/^W2xoLr0Z?\؊19=N ǟ|xm7K]_م $Q|Nq"çxMg`7 遊o#J\K^k\|]m.&7[Nˑr01jd_s-:/q.2U" '99־c> ,E-hc(8\t> /}%P>8 @bs=1_0Q@Xw߃5?U|deRj?ٷ[)ي߃5?UX#kyC0>(!QFI=I5ix3q+Ȱ~ 8ZFqŒ0+,3kK"|^O»v85$3Sv>gf~ЅjG\ʃ✷:/՚)L/=c`qQoxK}/nO20H\9nZJV, A9yb WRQKK_zʣPLk裳ȟ7#uo|t=_Ÿ+ҥYJI =~as3-KGfta:wy.ҫMJI5Db }*=Zs\= )v-܎}h8X%=SqzqZGNT1}GPEj1uFc[ȣb2h_sV77c$cPHf[$wS?OUtfI(\7QXs)8cY ykE= 5(TRzެI^*FM kn 6ݖHmqQjHIuOB5|1:!)jsBe$VO_f]TڔC9aX$OvVOQjѲRz&cx3L{DP4#g#Ε'4kZ=*rNGՍ^ .lQ#;.YJ^G[BPfrPG4utۘAZ% ~W&xͣ8Q5p7@$HHuRݍRW |뭮k.7 :}k"=* Mam%0m;c5EdՍQxFž+xd5ǞӟqOrs޲`׵l4=JOI:gBЈ$4x]+ Ss^gU}NFӠ\kd$;A/xg4>S{W;%ny9k{KăMҚͨ]Tjɒ%W7(wn?0#}SUo왝b Popv庒8T4WBM$XQK%T0'[Qh- 0S7k+ygB 7W:ݘFhfTع`;eI WgRW{wq*:%uPq:  Aֹn淹д`cs$ol).AZ]>tӧ bc0 #v;Txش+EakxZ[?[-/F Bѳ2aBNB^{Vuޣ/ iżݮ?xՌgֻ Okaib(Y?jO<;jBjUȹ(fW vG84ۻ)YXtoYԮmgwnf X8Sߚ˲75&Uf5 բZʰG6  {mXZGb$OGqWbI:N*}/:L>K=XE4]l]ѓ2x@Z_Hܶ;$p⛫/t<9yk3_:[`\`&= z#(DWhTx[@nӴ]6u),"3 h}At\s3}}K{NI-2G,}z֕V[Y.B@PQE ( ( ( (<㔆/m >qYiw6ruǚ+,6 gJw!BrIk-4So 3CaIQRz]3HA=N[O]"kš7A6 ?_ޥor1ַ==i'=F~LdW94-$<峔GŐ3+o "M/oWi`;hWal6ѕWR<ekI,R^X!@ԺA=$ZK҃6q2H8EkmvSHbܖ.ib(ڠrI^k8@ֳoeԐuf5FUDxU\;Zy9`ݼ)".DRB|}/OKY1>bH489"USl#J\K^k^DV?)kk P2p:ЪY<^4S5_+cճ?E5y(.'GFҼ7evpL,wylN1I潶+(RF-ueTDTX=s60=OihɐZCgzVΩ>c[ 1ۜļҵAoyq!146Ƽ ֺCi[Nh=YUI4ӣKT ї ctKsEGjBI8EWtG}tvܯHd}&Eqk  4nX?Y~j$<{=zWe:vsjHfֱ}:Anw\SR6`ehr2J9MWⶕEw'×xa!]I'Ǧ*a)6EEJiь]݇ם[K}g\'2F1\9"**pwQ:08؟Pf'Rیm#WզFWQZcluh4$_ Qr].z$'LIgs}Hhaɰ` 㹮/DT6JҏUԬ$y涕&ic2d#q$@3JuHHc? $k會'$֣ܰ_ZYE-49$Z2ԪPms\w 䜨?.GJ܌0:{U}UKͫ8"2PF`%b9 y9Wڼ£wp1’RCn)E<95Z{kPVHwk $Q[yݜ;U.KɖQ#Hωn!m,_  *%~P0}ѢuL#NTxUbXH[v"8Wi>JѴM-/2ӦkLcsT2;Nf{F)4:23r%;zlN'6zU%YXE +{h-ts=Cv;펑ys] Qqm]<<&8ȝ#ٝ158/gxoUf1F\'Q&ѓHf's[þ&u.bk eH$@J8|Y;=P^ͧm<-2t[c峄%P2|0ZSfРɹ84X^1-v=))[>6 \vveS%j^9nj?>lr9h!$ ϩ^Ʋ\=XƶSq7Sqțܧ/}ǾQLx?4Mq2q8 Q@Q@Q@Q@Q@}1+},3UI9˓a9ҽmdvu"dq"2$BOՍOڞw]xZ'IP*vֱ'VBq Y &q9+N$LR%S>sZʖ"t8ZHHA8#V{hXu ?W.EkT")&uSlg[XG )C&61 fh No53,RH¥44˶6dy냻8 }S.oy3l0zTڜ%e sdPKQv]X>V(FZ-K[8Dàʞ׮X3Cei2*-tK-3:UfZR.TS| sFYnƽ Jm;LA#\pv) NdtN1\U9o|yDV?)kkҿh*ڧsE-yh ~&4=[OWVbal؉I2nK @V WE4D;d;~ZI`H-IS\9.)ju,:}d.$sK{IS55 ǃ* >W}Kp!UvNz;v$,WOUdmkȁG[ӟ"tG9\-Ic` ֖LF1@"_:q8={WCj0䒹b.dY>.19TWujėP";thB';+Ί$i*UC NʱB$ª4Jj N H^M>0Ǯ̯g072:! 6 S'"j>'r^.QڲݍO:m؛%$G+kŸ+r*񞯤E=b+9by??YJ1bMWezE3KY,d.q'hnY%#p;zrяTBCnX.L7_CٴSX׏aqN{NIl ޶"vf2'Ohնܫ<+V3rn5BFU>ka8ަ]--0{Axm$rT_ *I6[9Grxƙb3cnNWCc 飗Ͷ '+o·-vI`-=Z?S^nrDyd籫8;i-%Q#5KMV:K"?ITVS0oỳD7d|cmÔc$Jn p5߼z6$\.;`/eb֚uZ>0x,&{ͨZه3 Ar3=xe`(yd,)ϭydιR>gutvi#< ;qkU@&acUrXsOLoT9v I#-k8ڱ9NQ^h͇CW{^g==Ȼ;w\"Q.mj\gn=7ZCKdHd]T'O}{ho.kMw4zb)$g鶷aCfD|vq@T*0-fRj{jqک_MӴ5Ky"H"EDw9p $(zl<_lw4SuXXS x W|MεCcpvs%,rEbp עgǬjw~6=)-Pg9I|AC&6Ǫ?ݳk73TEdjZ捧uZ_3ibn;q'5m>"{&v9 +ŀ4l]]8N>%Ѕ嵡ִwr,*0ʲrA:CL^t̐Emَ: g("f{{)n*2Np3@gSNBL\JuE|?p,뢗q g|9 +<]E_ivzz{-ʻ@v!sʌ He FAh<8nx>9|{u. .O|wW? FCe8.5PhxMS?*@$k ޒ: ][L*X1z SEMKۘ6o_zlAD Ry|ZQk P ouWjⵃSh%+ ~oִIf t1W !t=j[d2ܼ,C/ +Zml\[Ay{mlt7w+͞Ԧ{vGC.ٻާW5Ր{/ene Zסi#?z@Ss' GGGmSҟU񽰉<ƁL}qsWh*ڧsE-K2M7V:@cN5vgQ'kZG#c#ۻ98H3ov[pڼ򵁝nO=®iU~e`~:cG֯,Жy#܁TeesuD6:W ⤬+Z5y9{gl"qbZ}+?k^lMj4Z:h6'< $Mbǯ}j(Q/9C9C?*[*E"Y FrޙpW̟8IM-S>mc,c6+)a{+8m'6Bwn;gv5jnXm7WWg M>HyOC0+nSp1kW P~̈́0wu?D?fBKB9]Z[ hAK+[5\8$ sW# 4&k[[ZTeO/iڥ_¹mDVHVt+yʹ0!>o,FAǰQMJV+֨p\Զv\Em5̓22eEH+>X]i,bܖ읂~S85tT ;<=74a.vʪܓHľChǸE'uWinu / yh-"mV=Nm*MJ1{t3(|duⶖX^\xvPKdmZͷ/IQU* B1tPILxcưW%#.(6#0uǵ2z̚춓ij-P:cf޻B@5TQⶖ9ok)'k  b*2> 趷jI k@ҳ8l ƽf}y拨rv"3I+$i'c (Jf}ZOkRaxn-cUaE)(/c2j~ckMILĞxNh~ִ}uaeq)x噝'$`gm>P+.mẅ9n$P~Ԁ0,xyO kg'@фcn/hZf4J,<=qf]OnP"2S[=5 ׶H0LчIyJuɦYo`"w*cx\!}"Ҭ(Q{Kk{xe6WEt H 5UėQEyXOE.U>`^&~ncs3I-k>,A3(mˌH^aaugeN ]Z(8= SO@nwpЙ,rF ȹO70^pZukL{&tH* ǧS^=`` sdWJBQЌj),0F CDAg_E3V{i[.2TxZ) fF{9t5sMʮH_8ڏ,$,J*CW?-2 1Tz#)tq+ncV?!'r?V([^1^!qPȴE|5}qyn%3Hq/ǿ** o>_**Q1D]=~%A F3OuL7>_FvtU]G^gފV@pGLBr>|Կw{|'IFy|KԟW'%s<2 #+1=Sd瑌{Ui>-{&c{l,MHpG\䌊ؓ:\wlv[cqvVRpLd]alZ4̲yQ{,ED-<=~YJ̸ :y@M]i1hC&[jA${5B&uom Ul2ÀK/#ߑ|OjslV$F Dpw8+B<>%{I?tF_yQBrzD\if8Vf8ӵoi:]3%o,v ,-u$rD(=ˇ,;>BkMFDxd"fidvBBw1!pg歍FRy^e%68ĞX֊6IfO2 0V[iV3CG*̐sjhg Pv,2Ӣ~Li3@$al0P;?x[{l =7m[Qd|(gbd9&=_|E]Mx/oY0|r! sSyMa 6F?\&Eܣ9f$dg zf[7O WOnݧXpTF3v̍(,")'c#FHvߟ k֗ZMc,7 /L%.S 3}swmggogd#y<aC|ǜp8f몋f۰zg/)G×bi/l2đp!l݌gtw> եE}:Ry a*V,) l8 WQkcJoݔz qnCymܣO`k Rn|I 't Xc#'a{H:Q@c&i}-uX LUex (Aku. "Ѕc(8gs<\</%w28QEs}qgr?G 6-ϟ&vk|?CbzĪ?W e:?euQa\@Az8u+D4p0.ttQ`Οi%J6Tkz[iz}f+XG-GAITPGmS))RD8e ^DV?)khH}n2m^[p[\c4 yR9Q^W.`NfADZzz</ P p;׏\=g w9ydz֛(b"4{p={M_"M"%qې~Oc^z^w\ Rhpy>^=n`lf4srƂvUy}=.-$K5C us|T0\_Yi =J|tv^)'Hg=7= G`~4+D(2(N۸׵t)c'xkE:1Fvj@r=.ȟgSDiQP2M%u~ T{d? 0Ƭ%>ncσgK& U4[/< :gp?(ܬsG9ĩ]<ʶ|)! ?bxXDC*#GV)! ?b ^8jzEQ@Q@Q@Q@Q@Q@Q@Q@Q@t'?mPEG^,-!;O Aݕƕ݋Wh:,~MRu+\n>" ̣np6sWG|M[Ȳ+:F.ry \zWxT𭯘#Q61I9?0.vڅ:F s_כ%@,#a\(@;+O~z*\iYs4Q*CpgFHXpTP+W;/Rc|Y@s9\&}cӵu|Og%ԥiuDGsv c-l +o.iaKez ;k5"jfK5ZpFuCS~A9n=Ƶ]VݓΎCcQEQEQEQEQEbAq_zw!XQEQEQEQEQE%[Tp襯5J?jW &\6b}Dze{<(Bjں&x+gᤋ\F?LVך&.^)bgtNF+]Դ;>w,)Gz]@A{Z[h睧@XW-7ZKFMw%!!H) h|Z[)'>\|r^1V?i|;OƍcZIKMcSy]jp3|VrZ[h睧@΢?i|;Oƀ:+睧G֧ {EF=#Nd &w'Q@}5[h+KZJۤREi+UQEJSdL)! ?b*utQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-18.01.1/images/mashup2.jpg0000644000175000017500000005025313222767271015215 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 247 262 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wz/i!eE ͅ)4Sp(gQ݄ݕK?}'?¾g6jszvx{[CoYɧB$ $_ =[o{x? mXoaV -y'l}=>V˯ O77@R R8*рAq^x76Ýw7>!:֓m-Բ|[ۂݐU~,4:͔j^;}V]R C$3BѨ$Mj%̭+x(aOwr|8|co𬪗JAlxi'G/4O^xNXk\r?4&UnjE[[]ɞ >}Xj^(aOcޏ{`5eD I'ڬmi;m19zE;'fcY[?; p^_+:} r@|yvBI #) SYGjvG|@=WqW쬟ǣO]:71pUJGF;CW5$Vi*.XA&kO<鷍e˧݁"=5^/9X7ag@$Y~lz?*'Zpo{ h >F2At%략_7;YfcuMK2JKs,\hIпw7_ x-w7_ x-k3~z}_oW[=oW[=as'֏Z s|5׊2s|5׊2 i>}_;UG;UE\I'ֿB 5 z? 5 z,OlxWς5ahW'/lˆRA>;UG;UE\GCMCNҵytإtQQ#Y\p@^kRO,'Z [Ym_BFEC Ҿs|5׊2s|5׊2i_nk{i$Ӯm-bIǓ#|L yV%OjrZ\jIo&ȰćϜP0l=w7_ x-w7_ x-;KA~~o_\z6jOn#4Nd 9Q8o㗈~'ZO_ት)l4˨0#;WPoeV۹w=kw7_ x-w7_ x-OzGO~ùk_oùk_oOi>kAe#kAe#f?>-5), єSʷ_ ы k_VqZ^*G^*G.~ys,JMø8! >y+y;+˷(Kc'#=w7_ x-w7_ x-0u}$m"qF"!ʺo I,Ѝ"^x tҾs|5׊2s|5׊2fXϕ5$\\G>|))6Fm t vB$ -rÙ[ :H%ӯ"W]|aҴo Xkj}iWצ-ʈNO4޻Mȩ/`m ./ǮVeibehr`rdv̨(5Eo{ :H%ӯ"W̐~~7|6o5u}qsॢ %2r#'>3rE`l/b I .mSKf6.@w<=EY4&.c ^A >kr bί+mRtN3P\]UBɴ+%ˀK*J=|uxKG4nTou; oY @[Isc|~ ¾F5 {lvN__a z֫6ЬRGbf `[5>/De#,O o+y#h x [jq]/D{O u߆1Z\ZKqwykY H?vc ,aɮK_}oYn}KKte1pgTRwms:+SYo fQG"WD6N6@X>#%^ j˧ڃkL3ʰO&GPk>lgqjN)w$+_3W'ůGv<i u}n^#b<G&91_'%Wڕۤ$Kr>XJ\q&ooCN_Ϩ wGDEdᷓ9U_ ; s$ }/q';~һ@/?пekqw—W_*w\܋ bRn Z7?(W {;9<)o.a\[AmkڕGȱ<,u.p8b+?пB_?{Ƈ}y-m]qioE m <(d^[8xBk m,W7Q a5 3Z򋍋½#= |*@/m+$W eol 1?: 7Z+KiyKgx_FM3^ꫭ-5+nI#1uJ3+(w{9>Ѿ#iv:WZ-ݵŝ̖( If#r MNR[[ɵ-ky@\* =+(+i\iaxm.-$3;%=.[v pyΗxA/'.[kyd9倃I s]MopxlD ؇.lNE:ȯ®3'v8k[oѮ!aKT.U'K1lZo]ƴ>~޽8bwUhTQ26B^??x5! {#RXwݓ, )ޠQv+#<1ũy=Ƨq5XR<7WEC (Q@$ԟFY[*?:+.d| Ow@=t:M/PmiݠjҒ;{9Iuv8+?i-" { `zwf'T13خH9yAGh6 |/ռC$^X %(;Ua+M+;]oZnڮj۶EmiWj3Wĺޛx`nڵ*4W~|<}U_Ki"fTG0} 7oBR_u )鏢So='_%GSmqg?j>o='_GWPC)A4^-O66g%FHm̱E:'l Uut?(k=λ7m ,F :pvgjq]/D{O2O?7:n܌2)u{!!FI5.F}FE#'&}1"GxއI@<⿉>0.xnY7VQ3B8?,8Euo>##-ax Es:ζP#Ej[D$Š((((\-qmf$R%ʪI%bC$$nAVSF:]y\| O?|;<1gM*f'$&9'ÿ3^{csfHT`F:5Kayqm$ḵJRpBvːL|>_((1_ B',mm[{o"Cjo2b$I'l;izKyqG5̳`dcVL|>_((1_ v+$s n|.,3~Y.%ewi<}o i #e,-g~uH{]2y;3fPd-1]_)"kE>Ћ[]:Uye֝5Er<2q#c`PB~Q˜}B/QoQ cEH ^\kڼC("AI\0?x΃,Forc#Km5Ŝ3)VĐ:6 $Z.xnl:KRHaԅP_5rxq/ooiwq{l- 6YHڥR#$dy͟ lt71ZA DqBYr (Lr yHhmU*J:Wohik{ Kbogsw^lr:aنӌ` vQwYl`x=i~ PYVxH_$r!2Fx5KrZ"->鯡 [JÆrpIRvm*piY=3ʊae^8">muw@"ǜ Dy; UE;<Cx=WUiיbr"߰NGY$ɧ^ӬOnn$r2e 2<{E/!tK='N[n( 3;~$֕Sp(@QEQEem+ qmg4ђ2*?QTgVw oX|z-e( \|)֓g'/M{ {A |ew-.bO2Ht]:&@U.I$ %O|;{ Ԇ8xv CҲz `u| sj|O{Lf@~ѝvz }6\ڍ:thd{m`Y5؏BФrKSߨj^.eCz bJAϩ4ro\"%{wxWObnKm>So$Nd FŔy$c_3HCחVfҾ nu?B[p$-#|pʜA|Fx;(|Gf x-㾒 ٶESG_Le̓/!Goj~C4j>?|Og5{:u&қP Ai ۿ4,Ѱ3pp>4'}Z _e{8mnn$ugi 31SN9Worrs7te}65@ܬ7'4G7_#+~7x 4MPXoLX.F%mY XQO Cߊ~0ͪjP#q,.d)ʥ< /ppja^^ڭ̋ &yU7E__^Ś[/ W)6#䒫 U#/RxP_ϫ\[W}&ī=˽'մV[N>]Ŭ$oRAVZׅ?_x(OUKIYY V&T ~Q-sĿ!_!t*æ/CZ;5H.?zfdO 01<ծoo9W-ou!QU85P55k7^^bG5&u;V)+k'X'ɉIJW?~&kҮ,ax_hp.u8l03⏎|eh(J׼M{k_.lhP[{yn4ףe_>#_-Rλy$L c&DvVHFH dq] Yi\ExgW5/$s"IBET V\ :oa?gx|ːp+?t+bm~W(4ᶀ`:f82Dma2 #?b;՛;/i字=r,LK ʯGHls/]?|-غ[ߕ -'vK4阣4v)"ʇ  0*jmGlm]>K5Qo;,xda޵غ[ߕ ?t+K{MJQ4 &(a^rw2kI'iυ_O oH Cj崩qnb,lK)~uk_<1Am'z#.-Mđ&s(*J᯾<>w폨%\8PG $u3NSIZZIfI,#"Գno/,KwvSozƒ2tBJ{u65с0+;FKXzW:Nд#Xnv2y.zሮOtOh>!l2B/Yb:Ȭ𥯆gf/e3=F)WTQvR{4𖎶oų[4St+mwUf8t}JmH^ڼ_W[S[N[ "zmՂsn2 4ѽjjZƙ];D[[t:(zȗFf:}ό>5s bUIWKKI`$fkYC{G{%BW>tH0T%I=tZߺ?tZߺ/?\]79~k З>ɢ-RRt]XwcHgï֋ZjR3n+ޕ_ im}V>$WM5Xuuɽ0J}xExQ44. ֖z,VIE杜LI,#^,\W5~9ZO>buiZf<,x_OAo%W*w>Q ]|8 xPx Cmzl#T.׆%$NNr_JoڊG|>McĚneofXHGbnaWv>"Ļ9&_BE垩akY ,И |y~?՝KŚ]uʐ,2V@OrNkh~ x&|@Adꚍ(^XG6#!PX<:Agf[Sk۫'7Q-EhqET!ۆiMZN+Jw}]YG~mB-7PN6wo so0P7`N8#־BnZT[ukVu&]^iEmȿ~>&}2=kŗ5Y"t+?HFѐKnW#|SN5{MzOsԬic#1HFݦE@rCVQE!%ÚKY%Ԕ ּ.>1U(p>ff^ v TM@F v5RO f[6̏ڴPWc{M>#7n5 [7` n-{: $s{NMW^k䳍.IeUr{k(K/3ZX}llmh~Tou|AgjǃiUC֋+ f$zIɯj5Ku٥ vhI[k X"Ry$"~opg^EqmPq%,* CU\\G i捣O+jji60ėON7*2qעQMhe/5 kFƺLZJw{fOxv;Nt -n8Ŝa+mˌqmH uv}tM4h;;cC#*2!Q$p·6X[Emm v(H(SQE0 (2b+7S#bj?%Tt9Z]2cX$Sxnf=tڞa@  mM|sXiDgtHdumc`kw_֡'!i6 R'Ԑ?RvWWiPw2mnN5oڛ~!|I<]eg;g QxNΏp8:]Z}}mM,3iwms%pFvu<zүMtPj:՟Xe'h'B%Hvsx$Yjj>*lR5^{31|UcA!~Mwiy/ws+は:t/|H׵$6^'v] H̎cvʀpFmR~#L',M躖=Eb[Wr{#)8PpKué[{TKO X< kn$:}4W,*mPR_7k_1=3^QO]:]gfЏZү<]Ah~<? OY|/I{,Qw!@ k1PůK!<>˖1rBl{L6׎_隳|1]nRZ:}\l{I 605.ߥ;u 9Ukau?),9J"}zIY(6!d'( *F]} KD| {/Zgo'1"ݜݳ5%E|/ 2I]y?uˋ닻 eiӸey rzr+_G}F&I[E/]y2V۴Mkw+g;^|Kťir]e4.(W{h(CL,- E|Þko,5gc͚ڽcYZhWlc˼E7ivE5.8M뭿ở2AzԕL)|/R=(]$0Y ǵyׇ76C& o?`{E$k0eA;I +ӖitvI}yY^ *-Kk{Xg.+gvS9RFz<M~+^M$kO\ |}ʌd.9\c msA]l5F}OOa{zv}c;y0}=M:f4$ՠJ}>6Ce r8Qy'd<+{'/rf/q$rp8^>i.?cO][&\y/f6|uGvM_/E%Q@Q@Q@5=F< .-wt)ן7Rˤx$^WCpS (˿:^!~ԑ|AXs$=v7?6%H];Ċ A4 :w 7:X+#o ?/&C :߻kh.0 ? fF<$do$pWEQEQE_i,o]j6JmFmbU~yz/uuyO[S|_u ?g<~_{B*wwǿp;7|78UmVI.Ÿ}v+eL=S8*yFK)_ i"w{Z$\ty+\.?-\+wy hzԞ;gs{m}#͡A} H'ܶ ޙ3[ }_%d[6bmx5ܤ,yy!W)?q-M66IRϯεЧSfTa[Ķ\#Hr97)*bL2ʖ\k:#xw/9 CB1jD>m=ɘG7[*PY/wOP9>vM)!׎֏U۲ζe>:d軮\Z s=vG>}zX|}{{Z;վoYneOaVk/2{%4g$mWHI;]^;0xeߙsVwpFz0#%Q@"J?jxo}n?z@1,wk$BѬXQ %y(L{ۡ쇎5r2@<DZfR+1z$5mÙZ1}c4cܤlSs9[+-Nv'qOX~VuTGS6')!.~6n`y)FVfX&6tcC'<r :Nc0f+ ݣ&àr7U\23vYx zܶd؇og$ 8© \u;Ԥ3Y6F zC |;au`=<V[06cG?J4)Z;'ܯYc)4c\ni8g)L_2.qŌ_4k楊 cDȭvDKT]W̘;#/ Ndؠ}hB̥=Ga43Pc:z sBIT|d IDATxkpUu߹^$!!HyHk|!nنE~397hE^(!pPj.Cnhv$pP^W$mk#_J>CjhQA"y"p&I~vf^*NɡRP ߃_˿ ZV>ާZM4 Pu7YVMK+筃rknM G*^Cf(põ v=M &KKmnCIhԥ\ifρB/p-V. Qɡ643 )en=ߥ%F&]ߣ[2="o 2nXmKvnp5YPO6TH[2gzc* m4b0  aҤsGxozGwl #`r4DҶ"f;߆ةK =3vx ht\ׄNj=_PJ/ A1usD5xC˿%R2֛<\K>a~24\7E( nSW-ĄZv(^p2KH͡l!ё) 9%n*Sÿz^k'O>hϱJH@PDw+;CUi-(,j?O e jahf;FPe}O$1{{U5%1-x9,eo $3@JПY6*i8vW,_W~}ϲ*)5Z9 ()6?q>F&3Q>Qlȓ粉$im.̽&x*SRR` ~ NdFY~k P1@0̓FMŎ6bޕ2xLT7ٱDp5hB bxy)5#NUV0g<ǎwaM8o/|hi}][q>`qbwRYީ-U(óS理:I/F^w܀']|7 ΏFvi(1J͔<6RR<q];JM֢fs*dz Li?˗Kg[d<7gb#o4Z+Ih|?xW~bpctnYiC6E]fpIIY/v5

    ^ 5YR F.v ݾbHHLg~ߡ,=MU3 EbH٢f_q5 j4G}8۔V0[B+؂5hhj d*wc~k~W;oίϿ~7ìxqqJ\Ojz*vz^_Kw{Ov߭/\r9ox RVFFu2>)-w$^p^׼wO'̟o5αx 0+]5@ɝA`u)]˝о!8̩=LC\WpƓZA l7QJw{E*5J޲1 A"G6G*Q5Z8bG5s|Oe8 NHm3]ڴg~!!J"NJh:^Y-`iw;Ȯ|AD'ORYFɴ3K.g@'BB^Г,FII ?%Zk U.Y:GH֖Xb _'[?K sLz1_5bLy]%._ġޔs\ x<.7IENDB`fotoxx-18.01.1/images/batch-add-remove-tags.jpg0000644000175000017500000016455613222767271017710 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 528 500 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&KdUU,G$ V2o"t;@fz Ա$9Ҿ2i C#L8~lt=W>O1IJэS-RṔ׿#QKIDq"ۢOf g9#]AD9кH˴3$|Ooz˝jIV^_).-Crŷ1'z^tUTspO/'{~- ̏- ɵ=uKZ(0Ea zpHjNu}egsyd44YHrO;~*=gX)Yêj=0O^r"Xaxc5sͱrʰm% @ $տ9?/XӼid񵾛qX_%-@O /L^n\,lyru!_|i *wQǣp>Roedc+܀q5Hx v:lj4u$纖o)-~R9' vϥ>]mt3:?(B;go.h:cӯ,u?Ogo%!F_/GqOx+GҮln}FpH,dOBx*o;~bJy~*:Ki=j 0 ǥX wwV`I: Dq=ěW 1kj^ң֤.Բ2ok;2ATgRֶ47;Z@Ag-䶉4uxAOߴMgfR)ִ R="FI%[`J NzJ~x߉8De`xifhfٰ7;+l}Y5h$x :_#|8o⯈aq6i&_<Ĝ88H<1izI>~^\ fo}'Wy|Q>,/k}wC>í-Ff ;snuo>j=:uyM72[r18ڷO( hZ>|5GՖ;Oimg2Ip 2YP`]?hOiu84cx-L9'$hfWj0NvFF+^qLO( F׎Issa'N$KJy?,ͧ:ܺLAOƲy~{\6˴`09㓛+_O( ,U&DҜCak2ZYox78z\6|ҭ-Oz at^84b:-yG0X M; RKys򢀹@e+q{}seIb9v>^q,#Ea1"+bZ7Hf8 qoq 𝇂toiI&o,,EC)?Onx|>oխ(.|=48";m33yw6t [ԺxA 4pe5?a1"G#_hQi7)KWR!-)/~Wܿ3q:_ƚo,4$D!XX<xo<go.qisw]JI{> tyL; 62I/.Ko'mPbc#'=mȣ6?Qf<#xЖ?̷:4r$XFȪk>忇n8K.j Ox2q1a1"G#>tyUjRjsܻjZqмaW *s?$եƱfy44A3 1RF:פx|>o2A1 %Nrs^( r0G)? ?|3z4q$Ee`9Es&-EGh\g٭]E*ۅ崉(wk~oh9^xHtZvo&mw}iu>x`q(~]$yu?S=KDmGQM{p @dy^( rλaGmȥΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΌZ>}kG6?Q(aΎ~[[֧]E,Ws:-t|-栊DqGs*.YU=8G}pe4?dOO xn(A$'~cx[״2/-*w6r :tvB3q\ȨNC!=X7j^FJJ*&*+eRN_pxbo4(mM@[aXX$j>.||jm廓>cC$p:*+}yυŜ?mo%GH]G2Hm+KW_<9?YOewEn, Yw*'"/3|4ޮe7ٮ|xɗhmme8"|qmvOjMm&ҼbD.#$lW'NJ#}=Gȱ#d9m ƪTъÂ( :INe$Kh+r:q^ =N';FGpCB2* pqvI\(:uKoR]P_Z޸;~tW^# 4-[ƒsqz1˶+t[a|APHTѾ$k˨^27%vZ}/e_(ݴjP9XHob9ms ++in'b$2I#P2I+xy⛿>Ҽhڷ(]cS ]o/;5>&mgăX# \ $>쵑,[%c+8Ŵ]N}ϳ话?gOj75oRotXub[JwRX]?">E,֟4i$X 1/4a5o4y_-O{O}qi147S$Q3IJD!܎)G|Q|,*xNՠ~ RJľ\,Kr2NSd;xD};ZE$ `mC rF۔)r~me_3뺡kw}OIQ1\۸tp uW$:˝GfkZwxVE@ZUq~u/ Ɵ&w}-e+;mmŕQ.W>5;MLoK[H^ycAfb}i:Z}wvX |/.W}jOKE׺{Y\ h#,n1jсpEW-o?uTQEAAEPEPEPEPEPEPEPEPEPEPEPEPEP^V<]xZ{?Ul}u)uBRIE8c*WQ@Kg#Yi>ZEqƢ>4lCpCO6f#qя5P_ƁEϛl'̷1w2ΒH=,>f.e. = p;_M|=A3#?5 h^Ԣm#d^ Q3BTOK)1[eF%'&@q9;_{LƏM|=A3#{'S{wb1#8YmkvР8a@aY{gG4ki =dG&mQX{gG4ki =dG&mQX{gG4ki =dG&mQX{gG4ki =dG&mQX{gG4ki =dG&mQX{gG4ki =dG&mQX{gG4ki =dZ~eA{6{yV@pH=pAǸ TQE^]jW/_h{-Y$ eOlzhOQ[隭byH z n (oUӵ կX2OjmY@ @ GZ(|jŸYxW,ᱽ-V_1ʴIs*]O|}hVw7)[[848PO>׭~i:>esjd[2AM5?۱Z5D ;JC0#:еI~62[}mN#FFl<1 6K-Ks=I!GH$ӲOoQ]?}%xI^jvzƙp7C{ap WBA exw|a_it=oPڔ7ۀp|F%pAp|5iV_?.o0o~,dR~z~?OsCt _B!m4 Bd6bW/Ҷ?AVwO'|&> ~zω$ޛh'Ӥ..(gEa(1~k{dҭ4۝RonZ;7s\Xl3[wPoy?Ҽ~%xA"k5eJ6:qGO5|ab9m-kxC9$p9[C:Q_cxnы&hÉe!p$HTXVjs3RoKG۟hU}>ΟKƏ@O |1ף`ڎ{quqX6gFI[UeQmTU%I?w;|MLWpLwfI!Y*C @4EK/OI8Ə@O6? ZtP336? G#?l?´ F42h d Ӣ3?c1&+NcGM'QƏ@O:(36? G#?l?´ F42h d Ӣ3?c1&+NcGM'QƏ@O:(36? G#?l?´ F42h d Ӣ3?c1&+NcGM'QƏ@O:(36? G#?l?´ F42h d Ӣ3?c1&+NcGM'WR4M}B"7!n/ ^IETTW6@M2:*Z5)-_ $!1#@s6i\s@kkkIuM:Y ̒4̬xoe5)lh %p0=Yi\zΝ}OHP!̊bra*BXZآ(((?iZfi`uG}s?Y|%pm?oxԱ$.xVrM''6gHI_t¤d@,53G'6gHI_t¤d5 гRuxoNIO)ke|aI$*$s]W*O 6O ''OѨ_x#h,53[<+@?2.Tl?Fayotгn¤d?RxWd] 獿Y?jfOP~ orOj>9VO>l_Gm܎OExVyu4ۭ*IkOQk ΖKs/n(Ws'Bp>lSnJDž?wi׎Ҵ/ Эx]B;эrֶŮ"?h~-Wnq?ߌu?Pľ(lId%GQNJgOސlḳR]IjyClg9 Z(Wuˏh<;xmǃ I :}~ [_z?4FMT9K:6zSZ;/"znktz.kkvvѽ4/Φ4;6~غ_OMN9y^է.S!P6GE_^]FxFY5"[-M1n,HPʑ;Xg?C sc7>ZCf%EpDFxO-n3?>—vn N1jw(s d*2@_uKݤŬOx+T,-%[P&<*\OgoG.I?<=ygXX..]`y#1W|=^~"|KSxsCFX &=[q+X/͖*5~u]NOisZ$MbQUX2^X~n#Qf>r%KrmUus1HJn_Į⯿/DivuؼljlEf#4h&hH1>(P1Ti'PI|#vIR;:@uj7o6o!+1ĶN[o!džf Eֲ)X n*LTS{"X:K˨xK#~m@O <LlG$E%T?,1֝[_XC{ZvmsbF`A~YUŝ3Ozo}i$ cʳ 1o<-#A5iu8d.K`ofms1ŒTs|H_}:/đ&͊ ]Mmf r<oImu__kmwo.ZUđxo[k \xpY[} {%GuD_a D @vloc{wX~~ׄ~IŶˏp}xTqYw֌'o%Ӧ&ҡ[{.$WBv9<U"xs;.ly`'&sm۴05|i!xH|:Z-տ6Pˮ CVr8[k+~{ m3Ǟ/]xĚ]ְ:#Zk[PleTIe]͍< ?sm=?fGZlow|5ѧ 50-a/4&'wiWXHeu!NjmG5 @0iv\f{Yg25WOxج: wI~ <>'i7<57-t}#3)bI$pDOW=O{/VN];ROE4e A#J ;?n?^_?o?yWx{HW׊4jYZ͜/{|n}`0w l_ P ]u4|?oio5B 9MdžGW+H(|c>F&mio3=o۱U.YFI8^ ž Լ-yOh+ژΚQ31S#9yY䁀~ֿu]mmjvGMfox [}+NӨBJf~/Z$D57L4:ƛE&sQ^\۬]ej$)/&ޅ/#=:vȩ˓Y#S{_ I3(QEQEQEQEQEQEQEQEQEQEQEQEQEQEWxc>γ>V||ݤ=&tZUU{}ºX\I,ZYbUwLJ%&Vݺk̷T<2FZ9   xi\OB?,??Tu4Ra?9:exn,lߝSѲ3<;͆r,6!uq^`aV?gO,uן =}hI=ȏwb7gh ]>,|#YFٜ9} E;o+ OQjk^Ut4R׿m?G!A{U@!A{T׿m?] ׿m?G!A{U@!A{T׿m?] ׿m?G!A{U@!A{T׿m?] ׿m?G!A{U@!A{T׿m?] ׿m?G!A{U@!A{T׿m?] ׿m?G!A{U@!A{UCР- gefۘ Ǔ3NI*((|cE¾Ůo7H[[%cq[aR (]~vbj@cυܟu RI<ԶS۲ [ ʢ%[?o>nm|CEnics.u&8EtY`".3U߇⾱uhwHWm#JRE㍠g_Fo>/}f SYѵH/,l5 #kSK6_Zr!>_|#~+CN-zٛ}7R8t7$IbGR_nj> 9G4q,GoެHWں:N|Oasi\Y_]wufE.s`+(w~!dn<=O-/4Ni{u+[y-͞yE6c9$:r&hwŷęϊ[IɨO)`̆a 6_dW%?xU8Ldma4cՌwd}/\gS(%VkShWh#zz]iWv 3 Odk{Ac ƹf>ԯ3WPF-ol- W(Ir״ɌgnȽKUz"x6{oį% l{5ϙI#Ex-_]B| >&7@0CNk_ײ:$,Xeءwew,_iǪ9|o:?<~v:h7.;H]Q5B'tWcP{ZۋH'Dr7m8?u7mg=6#O @NA=I=z-wWp[Iu'M C4'b~f'_3W:~{^#CرUʓ÷c"20I^/~`o(.>2AW|9GpxwXͧ &#PdIvuv?w+ԼFȾ|g =z,mfBcQa0n@5\/L\={ᔒhZJ r)aV#5amYAyes$FC+#>'y$2fcK9I&|J [v!%<2 =t[*;|~|s\5۹tUK='Jl/t+-|h 7P*V>O 5O>xWo Ck#A%ߝI`1lf;H_(sh}[F-{2h76z` jPMGmT0 3[䶷 g?<3"U/0|W+V+.{{*IiCo5OhC1hX#Hۻv2x;oxƐh6Ya\[Ge*o$CrgRXĹCDRO >]o<3"T§D_j➛P ëxKT<]|mm\&t6u >T:IVSЂ8".?SC??"/5G*xgEƫ 8OTQ ?A<3"T§D_j(O ,?SC??"/5G*xgEƫ 8OTQ ?A<3"T§D_j(O ,?SC??"/5G*xgEƫ 8OTQ ?A<3"T§D_j(O ,?SC??"/5G*xgEƫ 8OTQ ?A<3"T§D_j(O ,?SC??"/5G*xgEƫ 8OTW= w-rp$s^^M?iQE W!|!n{Y'TifYI2$NkTi5'BPªj\P.<~c֚:IOa;t?u*r  KKDVSE;Y2KRqj\Qs@{9@ޕW%>K/8#ۺx[t2IB\~ᗅ-F{{K4I{lC$C#cZڗ?j\SV9|&u/ 3L.t{L,eUtX*Dia |ᖽy_]retIJ<*YHs@{9G? ZI_քZt}g]55M l.;̊M߁My/uWXrҸ>00 09⺟Kp?.{}) ٺ;W@mxԤ}O!Wss=jz"ՔkF ܲAoj\Qs@{9M;+!Y^Vw/xV1Oʀkρv>6xR?3>4I+Ox-kԹ=Rr/m6 ;M׷k;Bm r01a?ڣ~xKH4O [i6W6w e]Do(YP63$Was@{9G?aL+uukA6zEĆHd.IN#S;a_33hҥЍZHۙ‰r\*.{}(Թ=;l]9f;Px1zc7V^(-pn$ߍĹ9-`W|>>Þo t N-}>ƥBڗ?j\P ¿w7G.? bj\Qs@{9E_u?n.{}(Թ=m ¿w7Xڗ?j\Qp6qW~]s_u_Kp?.{}(_+?w.t/?ڗ?\ \~\?qW~]sRrKp..? +?w.us@{9G?k?n\~\Թ=Rr ¿w7G.? bj\Qs@{9E_u?n.{}(Թ=m ¿w7Xڗ?j\Qp6qW~]s_u_Kp?.{}(_+?w.t/?ڗ?\ \~\?qW~]sRrKp..? +?w.us@{9G?k?n\~\Թ=Rr ¿w7G.? bj\Qs@{9E_uSwj)\X.u*RrHeXZԹ=oc6ˌDO@(@R7 ~'➝Z⎡ [_飛yRBި czUӵ, i-{m)`s)`W~k>~Z k;:U֩;ăx;Cr4IP#+ˢt%}r_ \^v%@{C m+ )e8,AF֛T]c{;Ͳ\E#3I&vJ˻zh&i_ jv4iW:7ՏŤd@ WUb`oköxEKL+$ѼV9wTP 3SרK|ca6hruO! r,)S;+ʼ=e3#OԐ_q4ev,TWk4xKм@X\[Z_؃ci<ʌ  #cV n"P~0״VIJ.mco |<. 3rܢd9/C?=&-tvV{ĶL1vݑY߆m:I|Ks[O>ЩYLyDnv Y߉o[x|i&+{ ݮ < ! #5O' QqQ \ݿ> %km{}X2#[1&XVVb za{R/9Ҽ'ý#}kkK&MR̹e9PNœW տ<^"O-?wx٣^f[m+S7wVEψR{x}hrd@NX7۩]5)ÚH4]fɮc%d8V7I sZ/]EG' QqR/Ao>}<%[iԚ-R/.\Oo(ɱ_5d_(mշۘV_5Ҽ5m-Qҵ}sLR4A\MpѸ m99O-?O()X -/OǬJƋic[qu,py3o.rB<b KaVNQB)n"U= ٮO((z .?|*]k[O )nXY X:%dYI>St5j_Կ%ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇnyڗ/#vA-K=@ۇq/axB׵"OEi+t' 5/ j_GJnA+/&[-ASl<֦E _Auu?(?υ!NjG Wc$~k [?m_~BT[et%.ZFrX( 9 Gÿ8("e?-f&14MZ*CAW_֋n֯= &|i/_ѮJNDX}A^-g x׉h>h]iiiy{sqɮq%6x(FHvzg sz+zž3ZYO>6*P XkRu_ѵ߇m}Viw˳g`wv0Q͵(V}[2a%F2DS<;%w[&>:kVrGuoa>vgYq0(C@]k_]s^ ŭމq(9ㅔ4YJ@#f7kz3-Q̺iz0ڋexe{UJ!C\[4 tHu?i7Q[B?e2Au <ؼc/ * @o=9/Jns<mjǭAujiir.IqϽGqz> NtbxK4Bu{xfgxvZψ4fҥlnaybd«i\&@/E Pb#M䓅U7o)n{:i՝vךŦV6v$i6b=Kxb 5{R[[5` H_;rb_oGk:̦ O3 &/wc=}-u>wú淧׶^!Q>I5uy-^8Ը 6@ A5ltM RDk\Ci3\]Mhp7i?g .4 4d4VHr5F8*CyS?j <)'#a&lSgkpy~ m<_=j `foSg2g5eꦾhi;A֟\~YxcTÒjsy"DU4kk*KH'o(CM/[Ѽ9&գui^)c%hC\1+k{ܽ/k%6n}K#d{uy'}4  zJV2>>SWGIi5mشm*X,wFӍT/ƻVeˋx+).57,,𳫪 m$6'څn5yf,-H@Y k#k|S{u:6YnlV<9!yK'e߿je@Q&ߍaf ׂ0\| o5.S4KOiܰh)f Is\j;Bc;g^f֢gm>h2܎򠏘5Jf{uhOdmo`2{~ىՁC`韲LWx6j%_Y^Zhbk Vx. y pJ iMm}=.Kꢗ_|J'|}bW[{>n[ThÀIrq]7>9[ljfy.<=/nme ĠrԨ&>&#iz=71nलr'}dagUh6?u?Z-dYil^Xe{M;U;<ă[񾿁~ 5D=lGjŤ$aUxER9WRT0'Ұc-߃t-kU$6S^nn>ꝸ^qMwğukZ]\A41,,yW8)aWgV١A:ϥM|^ڥ>L-" Yc00՛WIwI=ֹ?^6xTDuCba`v8857\ҤQkBdfbw.p'O O1z4ѭva󼥕 3lܻdVsp|kx߁k+ Ykxz jon\Ɩp_=u]roee֗{MϷɌ@F/pW dZO"՞smeCXP8h^" tP<5s;Ɵ5"6k[Xom7R[@W'q.whtŠ((/ ^^^M?iܢ*@Py?^kW獾|<V\KYXqi#h9R쁕^Py?^cm࿄z|Ah.kX۬ "d` V\1k}<'ƾ+o~|X63O_RJr oc-'G;4 >|2Εsײ?&C\[b3S%R}Kz6O=b-udtӤk\EId27×s(|)<cYr 𦐋vV2sbmݹw1wj*+ҕŧ73&#ߊW>$ t}K5׍{LB"H 0|Qxp恦-/)evKƼfLIȯC9|1]Γqx#Gn4xl渷 E1&)G4Gm]/]xDő\%dU󐂲'ğM;OĝyZeY:Ah- =oWi Bo{ 6\`[WCG^?w+Zt"ڟl.,5/仏6-nU;$cjgo~ +|>#+[`pGs5-C^^<]M'SB'vsj&٫Ō#WeDCEQ#U29 K|9fETӮ#He54S2:dI<{|FׇM[kz%F])"țpx⹻ol%xbiQhuub L.r01 7<[?KۅΩk]lu- xђpr.BZ]閍xKfRNB?> xwOkxcEӼ;&.GnvXV~uC|>O Y##J4wV;O H4땿{y"%&@Bʖ +O|?[-ϊ-nP[ B;80C1BѮ倿tX2:&^6ռ1m#4e<_sujH>#kӼ)iӗYoCu$$H莌dLCYfrxj__j~-7ږo!P}gfpgz$Y_^؅`OX6>N0xDž<9xLD[ׄch`#5U!>$ɢ?j/z6H~ Zme^u/BZ 4Q4"քrp$&>^0ԭo. ϫ\^y`(EPU$sRIKߙÏZFwBlĿk9D2vU!Hςk~!k[O &K;?x3Im]2('5;Eo^6!Kb|pyh拡X=6BE_hS[AfP F,Ni-7[o%ݿ(h_VO[xoxGPe.,^87.l(J}l/7<#Lx Km u]W}fTŪ|1e0g#t:g4nķ`@D.?0$䜟S\> !Z߆X/gI]\];Iiܑ'KRKj׻|'-b/ ,tRnu2ז7 myH9;yݼ}}~EHK:Z>%ŴWW-ȵUtTYw ûTHZ[[Z@NBCoz$w3A~ο {:{i2ϧkm(kGC&F *w~'Zp97Gqx<&+|`&)9ܔ޲'gKPmbkE)2 c Tl-N/4Ѵ}|qڤZ'Tm{s rW= 8jI~|=Yi|%Yp^鵧U[6#zy~9TqMJmI4}.׌/"&9<#G#ƪ?>>ψWJ|)xJKKYn"/0Ke,˦U~?Ak^An4"gfc dy𖚶hxz8㌦>PY//ǿ"b/jz6i.2ܬɹ,3Qm#L@88a签xwJf}2FL]%/ ۦ| HBr4υ 4/1z^n gvc~D)QEQEQEjFo Lj5yM&QRs@{?,d}?DCQW03K6!= ^Py?T/ 5oio`X\Vcc8.1?ƒj_ _~t [mI/HWI#vNR+ßFu៉79cω'ҴNtk|S HWȐuVa_O'}Q4$au$"W0FDTw3$Uտ ~zm53ak=\Z.g n<@^9 Vnݿ|*j<XB> *]JA]N3M?eP! )V Y>#xF9 42o,Xqmy!<0m+Roܡ-;eơ F/%Ooʲٗ_*Oo9=K⏈Ҽ\) n,&d1yd ,eX`Ҝ~n߮+w/d^$ 1p*5a lD8wT`g@Ҽcs㾄l<^-OIek6CK8.[*1+ے[?O~q4v6om-"Jf*ڇƗfXSF}J|9Y]L1e]W_oy BR=FP;Y]&E lvt qxGYѯ5K3oi*5a2ucpLjGbH5mpvk_Td\x_.}egÚ}e]ڋB!P>}>~#Ѯ<{a]VT틧>$7)j#j,'zlW6?|%x¶Wךōб>w%YWY/|-u<=ǩK]h[Ko#mHUn7%ىP3O޺KϢ_smDj)efV#̖аg 20 }xCBկ)x:傍[p2N&}I@A7(iQMkg-z4B$r ϸ2.Ԁ %dVݳh|k7yW0$lW? A=kRǤx[Ἰ(>`!˰$) f OQiڝλwowiAw 4#c)hچ|l"IfSL*$ 2A׽^Eu't k{oqޓ<[&IP3g@wWo6~+lu;%uk--ٔjo*(KB~:w<=g=osjZubBKk&ܷ[tYb]7#.CY5kK mfi՚0 C G88-q_گᏌt~ly<@n/k(qO r7y{sJ+[EyJ~ԟ ΋jx{-?L{obHA0Y!Vhu!dPPgB|E}΂!:"U*|҂6bV$2EUWOS |d;_rC56ֶ71me)nzrO5-V^WMn5]HitZz$Ḻ4XlJ-WY@x#)֡}\k:zN}%.rhdʃytz-?j=+N񟅼/+-Zvn߳Ad#T\R_ulɧ|ۨ#unq5o4mce_gQ^kJՇlRpS,/q k*pHJ/ ^^^M?iܢ*@Py?\>j_-~ ZRIљSiLq^? ׼ .Mr_ Wgki涴p.<58*ycM&}>G#>:🃼񞅧xBk:[I]с$l q 砳<[Y.ƚe 'rޘ0F%Dʫ (C;s g_|h>'Ri6v\J֬-;ibu3dxJQ*@]w ]^O7^(լ聆${Xӿ|TqT%{j}ǔ|}>!dLji}Ֆ`"萤<[і\Ijg?h"gi6z.vSI4E3;ʶ##EP_x|nח+'*]͒``Ye T?ioCSx<9S1Eh@'lwnwv(#o?ϑ [Z#_ |U~ [+-vCo'ÝKA/u["yI2ѲȪ謄+W[FO.IO+/4mgP jzQ\CQ炼Att:vrѱ kBg _yaNiTHWOFҲa 7g/,&X]=nؿ [tv݈;Ojewg ,Vwv,2w-$hvc*t.\sR_5qAq?O7>rRghZ̶V0[w vP7Gυ>!__/.>#BxLs--SO.+|>ä_?#f]cTҾYo馁L)$vrJ(/U;߃_xRk .m`r L,?xx5Rg(uO_^dF}'\xvKIѮӾ^rp =ʭ71rOp}r'>?<9bN:j2M%ډQr*r8'$/⟄z#|@ӴX4]RIuYX _:HVL {Ŀ|?xOu8nm;wq`c 뚻>o;JJ+/*zgtM]H[}fh"8w'̤0Uy4qDůyA1 \B@??$O} }tǹKAS0MQe Qߧ˕rng?x;G>:5M4{%D62?+qO_w^+ln~[ }ZK1BI+~N#sxjg|ykzvc944{l28 %[h-t}ǥj>;{fq|5n2FKMl`$J2 'UPz/KO >!O: @k$|ͿK˸n~x↧s❦}]wCS9N?(R fI'ߦQoGn~Ή>^SFkƾ? 8k(Ku!v=qXFs6״WU? z%o HI#e'03ֱ~[0iV>'ZXDKʑq?jO<]4$u+QewS\v"#Vq=Gg^6sj﴿ɛZo/r|$uOiu]Zmh,ܣ g=(~0nMӼ_OllZ/?1E6_&"ı*q/7 ۝I5Kj6Vjᳰ^啔>SWIs6z% :$O?~^0񗊴RILvK=2ľmYUQBq\T[vi{s5<[B6弤wAc-2|`o>%xo~}JMWD5y&ծeE?# 6#gAg^s[,tKgeǰ;y.A8rrء;V_/:_x[.zyJ͌u, c">"YJK6OTd1ǵ]أ19$`irYK+w0]yK#,yأ@)~L[~-W?f/2]k0Z )Rơi7&7=e}0~ǚ+}?i];ឫ_8C&..%[d_(~cյNEίXɪ^ZMq?~EBeetM~LY?3MJHfRƵ{^h}(eF4?h6$ O짫x}>WïikWĩ4[vк;*Ďe$ckwu~x%lm5i2L$Sc—!| 1?<{qj,t+cT&Bk]3;pzVm7{|ٜblDD{'[_h#IJC7R[_4w-#O>ca ԂH`XvȫaNYm-{W}۲Gj+wO Od(-~^ kgM𝼞#-;njb<gṴ㵸UM7os J]$T+2y~z=S}֭e{qᩒZY%@rx~ >9+{}R}R$2 J;+HcUv /QU}[]oX+ɼ1"55jF3r( ]CMFeÿ? mq$ #z*IPy?^cIxB?򵛉ti^-"" MO&E2: gSJpz^sd74 Z-ynZkp#2e3F@cğׂaQk0kWvO$Զ\’<qGϚ 9P|w~M-KS0S2\ k^̩.kѳuđv"V7_-_nk>2O n\A.B)`ZdS:1ۊq압&_3')v+Ӭ'm:N.4fH$ AОr[[OK+SV7- A |0ʲ5? xcOhh+3y+u_o9l;~o| ڽjח$i acWbEZ:ir61Q %dD~UqMy/Djw -3I4&0n-(\  q? ݟa떾.Q_$&ݘDBy_foD_?&<5]+GOw\[O!s$p CBͿ-NMU?м5hIc5K;uE*F;;,?P E~s&Y_÷a|Aq1I$wpPA 1KEwm {/}_C|ӵφCOgx*_CIq-ӤE!WC&$9TzC_ aKjCj"y&129 3sڽR]y?ՉN~G26OY3 ͨ]/PALMtM|5{ i'u([G\uA3"4DmT09w~ c䷏&wobvwy#w'N|eƞ&>Mo]}` #yLj $9oٿv~V:t๱CIe!/ #(`pARig#G@/x]fY$i1wbȮC1 v'Ө_y'ǿs*? [jڦ50J$P]r08ݑ$?|O=FQ6#}(rnX EF;*}KM<޿u_ f_iA4apKjG2 7e?jE'o%k(g~V{q|Y,YՠݎtKs}b_qE4/zJԓPqjhD@!@|W@_m5ZVѵ]9tMCZI* b {*KNwu=./u뙥6ZI&fϑ39m\z=wǚC9x O{+-;Q!{/vW4N@4M Ao"si<'FY@i&<}빾FAkרI-poxMw3,]by-BvPЋ{CmWQ@/>^ vm&[ƺ1[a8\sEr#_:kvZd1^opNQrUH \v(04; @]:G]8mus&έ4C{|׭G}/]?9N-'' ʮ۞>Pe+7-سnn{5;+jH^! ;41|6[9_ٯ:>q]ϧYW^BR#I0I#9RB=:l~xWN'Ccru YM3˨2ȯC.kj3|9z-6pIo=B$P"pKg1^E;oy I.^X!;O+gb%FstQK~aEP^M?iY&|xW4`nQE Q? ׼ ?"s)k? ׼ ?"s)i7 (H8#ǁ:=RTlέj ,>+$'q]y ѡ4 .FghaXB]UY$֥PEPVMXִbJt#i¯-vHcb2բ=~8o$uF舕E5F i\@g3_ |;u]e/.KYc%Fd€R1('m὆iZj_Mwy5ۅP8 ѡ4 .FghaXB]UY$֥QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWxc>=N1km5kI ~st^7c#.>D" &WYxIF6s_ S:m?PG<77N3|gZƓpLHI RG c57}ib?-+;.]im}iLPwO: 2h &hӴ!YĪ*ou9K?Y?NYXj9&{M'H8nbۙ^_ytFi M?QmKDjcRg`ORῲr%/Vh"xi5e6) ygx֝h[\ځ9du1 $@9{ωXizfƇmIo]6T [+y46dgد{NZGwzp!$)6pFvc1Biz=&oo*ʱ,"|nH WqW\5Km:U5}J~)Ԡ(x){)܂.~q>Nn;_z-ZML@ڃݔ0$Ja\cI]]=J; kmPo+#R} v7 >{놊?&&_rxZ\ImiCYu5x£7Dk5p˸E!9pj}J_!jӓFfI.%ƞY #̍YF2F:iZbBR#"č rZo/cawΧjBDauRF*Y @q \kV]BIkˉK 0)4X# gV I5.!mzǯ?0I懲b(^3̒:u@qRv>9#Vҵ]JA wy(W&IG' ǀI~x\q Yɥ*@ q]Krі#ONkwǾ ]SS/G PdY$#G€2wqқJZ}㦝` n*ȦdY4NGq]wxVot |GJͣ,%xn|M{\-\!fW >BF;bx "\t6hΆ((((((((((((((((+&ͪCI[9dTFbFv?c>z' &c|OM+8Ϟ 1=ش<\?c>z' &@?DbϞ wPtofC8N?1=sšqy{o.R8ZΊ\YTgSQ}54es Uđ:|<һ:(0Q?a7?c>z' &c|OM+8Ϟ 1=wš%5uXhi,ȈvO@ :#'F~q$NX<]c|OM(0WEp1=?c>z' &@?Db i7 s>qw9Uj誹IõoQ@Q@fotoxx-18.01.1/images/select-hairy.jpg0000644000175000017500000007520113222767271016227 0ustar micomicoJFIFHHrExifMM*bj(1ri%@HHgnome-screenshot0231zƠ0100Fotoxx:trim_rotate|trim_rotate| Fotoxx:trim_rotate| Fotoxx:trim_rotate|resize|sharpen| Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  1600 2560 0 C     C   /" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?q/4p8b;zhSJC=RN᜞ {O5F@ʖFVdd kxSL>SFtH\@#vo-sbk>_/ }jK@6`SJ1ؚҲx ljZb^\=L1sxWdw\Tvijl~M*YMHOMY2aqo6 ?½W=sBcxl+ \恸fIc*sޯSl2<]y{'W]4Vͬ4 KFjӷ1]Z=ҕ-cz^װ0xm!Kz.?C^]|⮍;Z7?,,c+ 7 ӿZu&e-溳S&27]E4ϴ?z^};zd6{+IV0L,td 1JA·j7Wx[onә=DldJzɿ}J.:ſ|mX-^t Zkp"铵 8ޗ+;OE}#GmC|Q}!+;^}ޏo[}k>lH?Wj>G܍Fo{zXaz?ȾhX6Ϭ?g}ޏ{WFo_3ɱ7+čojF*WQj6-!;n- zeh+=vF"B0j)^/W7Q,o$mѕr)g%pF 4] jJ)5XleEBa]\6Fzgm]x}+Hkۻ 4遉 =o{G#Ad:2}Tʉm-™!!Tm%u'Vf4v}B[kKEQ彿^3R:]5|!HtT CoqW8HQ8ḑe6n=&kXxE-nײ^G,[ΐKg4)@? jy*G:$t'#=:tgŭRO݊&`gYIp?Q+/)x&ܹq|l՛9 +N|iVg.zRRZgHbp935['|97qhkGgZ6A,"~o\;-[6ELjĉ-"j] |>^+xo<.?ɯ,f$ɔFCw=q[VKu}$1D +* <78\ = +NUo}]+fEڅS3ԚwƯ}3G>1چ'ckx.0bw0O?DŪ%tIſx_%%g'n5}_🉵ť=z}ރV0!Y70v `á&u#^4ZɸKk,&$XrWcLVJh] >%QvXz^>*5kmt&u4!AjRvN|k;W9-bdvz2r849[uR[jjzB<-zr:W5a~cFp_@]Ks.[6f?J|p^IC_(0Anހ(rжWq\=cKM A9GLpʃ,%ڹ1ETR9ԫӧJ-:I3uC{K]6QgXo)|\{[I o:)`UVݟMX[?oL=;Q亙B U"D( 5x{f,maX¦5A}Iۂ(C3WѤ2VEGG}kcj0Z@t^@JG_?2_Xqtk-y }xXVGQgn#؁Ȍ²5{FA^sWl9!=t2a. } W}׸#e~ϛńLcK .s1fiAEo=^7I9e⏴Z>}kvQ&8ô7 F|QGO}҃$'zvQ&8Yϊ>}hIPDQ҃$'z0GOi>;J(?PDQf>(I'־iAEo=G;J(,i>}(?MqǨiAEo=E\'֏Z_IN?(?MqǨ }_kô7 IN?asoU^m!߄r*^M 9`T`t֮'w)ukM xo?:Twv*I|me_`PDQ҃$'zʖMŚ,^Yk:;"[HF>b( 5c_5Pk^ީuP5#Wc7*A 2}G҃$'zvQ&8ԬU]麔_G.m"h NV6"6pShʀ3r^+Qf׭hh5E\䓖ǥ}҃$'zvQ&8ĝ>f zGME[vLQѦiK`8s7?l1ԥOM2X "V Fx5;J(?PDRi\l4K[=*(pVfݙqe E:ÿ&tz[4[O`^TpˬΓn>c(I'־iAEo=G;J(,i>}(?MqǨiAEo=E\'֏Z_IN?(?MqǨ }_kô7 IN?asss"HʒȌnR}տ(?MqǨiAEo=N.|gkkk,˲ԬqR}K]z&-An[Fsiu,Lc%IVI~5G;J(?PDQf>4֒{im--K\qOzirw;Tw>kABNObhfbf&t赦ms{:!|+Q]A,6O,8<QnJҁG28 N}+B{e(k;)S1rךϳMEђ2~eJp\r1 NV2'E]tG+A?(<Т(((((('ֿihewk}otP;"1݀ ^_2xsᎫ;\a=wúf["XQǛ,"RU~{_>?>IȲFє>Ե<#%ƛ OwEC)qm刼Q+2PW\e~3KO:o}xcQoc$-+@qB\RϷk 4>xWPӦ`IKD$t+t&ŠJnvyj7~X?w>jNzg b9PF"'k˘cFgtV+ ܠPՕח ݤN WoL v\o4dqe4^3x#z7M]WAnŗy!i?yw+aⰼ#bC>8|qۈM4@vq ,kaKz1ƱF,qUO;]q0F8P쟟7/z4=n} t[Y#eݱ$`B N7.zG_!ŢoF/-UǏ/aפs!x?žZk3M=Ν >}wsZȲg*H}y0oߋGӷ1t^is{n(bYInx]t5^[horK$i3ƮB##0S۝WRt6|No$KTҵD\ΗI$hm\koqga|_Wv3/~]kBG!7BKwHK&#kv^6Sjj5 G܈NzeR'r9 ʊo8ux^__+ ОTx$Ov]'T{%QPXQEQEQEQEQEQEWɿSzuy ׿E]=14Ŀ4qcWe2KC7:PM6tK4́ _|q|-c/G4zFhI!F#YZ6>:ѵ/[bܘ1hYsݱ*߈K'zλZh5~e &@;i$ү<;k6-ME&Sn7C ۳s\yKt-Qd[Im!#FUVR6#|j ~&4!ψ4.l^xR[UODw)V8cWԞ~Zk14h% k ᕁpEy~+K_ιF Ŭݤȓą H=28d/#sm_/ZdIyR[]#IW,BeHlskwy!ihg; >?#lt\ƚiŷoەӂ@\GPyMm;@\?捀!N0XjMv^h 46G"1phxmguq%'UeHՎ>^x޽gt.Mr5o5$u1yW2^*jV@,'˛zbQ9| {wZ\6W epV]QY^r5`*p((;&`X?KEPEPEPEPEPEPEK:< [d2M<u<@$4Esoיg _c?q5M {k1:d{A hE_i+no.~5+3+9(<Š((((((*j6(YKmdqz,מ~+=~|P6u0? ~<>:鈦rb_a))Jf-{jt|ШA֤..Ex3Kh$ЯtXX-f;=JTaEIoqh1iPX&;(Qn ۴F 6+;t^ ռ){h?g2^jdo9}tM3xrN]&+[U5j;š9]/ÚVLXԱ'dq <K xٗ5[hi$($RI Fzо~JlSJl[Xm՝-+','JtKŠ_\jj%̊ ;yI `21iӯo<- eu=B`{#noFטmָ%qj1Cdm46҅E~9: #rYiZsi4XJ7;l{@䧥nxq[-6/E;t!7+"+ &8_^$ {f6 Sb :xyg-|!mi6zV ͨ-M#lʟd%|*e 5ky縊ݼ-?~m `n8#>qźy5Z\$"ᥐOAdwLjntXu]LCgӚm"OW >܏ֶ~+v? m6@K.Ս0w8 Eg(-IcHg+Z <5ENЋsޭ\݈3$G y;-nci0D`Ɲz#V+yteN-&Hl]g{Ÿ!"8 §bsxw1q]ÏǏnb]{GqIJaF9 k}>$tFaR/?v:ץpbq~݆磆_z[ {J6اrwcs k۠T+ .,$1SЏAtlۡ<'U\7-vA%THHQEΓQ|V =vV߅M)P:?}ׁ$46-[$m} |-|0}I|S-'ƺZiͯ, x,26T+q9ӻGfPx6/uO$%/m|7BM{ >YAppslxo?5|SsiϠ~#C[FC̼nO,&Ziڥkq5ͻ+A! ӁvI[]wHcYY2[Y'.kzyhҮ4N]>gĎO+I$b]܅sxY|{#AKt"S5}qCReH2܆#_|s/|/o;\G_WbY^EnVbː:y߂|WKJIQ5 Ų\,lIE ^vk>>$P[w-&U )FoT!X$O_z,D~Ll)$DXܨPl+12+.OB)ft&I-#/:w]xuXvZ۵bIceT`Vz?ޫͥiIo/v T =>}6kMu.f&5* Veq]o|%x'EI,6g3f9gfbY$I$M%dB|F wF<}Z_RnTČ$ٗ~^SRq#=}mEzST]7Hګ%O5?/':# OKơ\@ɿ"~SK%O)8?׿j_N5$uG딿7$U?'?jHG5?/':#Q_j_N5$uGC {j(/MjO5?/':#ש~Ϟ>>'xT?^ݭ-VSĂ'!_Бֺ/SҼmkym>K:LCRXg+y &?Eӵ[oZGyi5xZIiRÖQqT#໛OhgŨ\Okzu<ȥFLxrO=;:WR$7{[^leI򑊨 B㜅99kz5? NYR 6+s ,OIԒr88 Ӽ ?^xM$^[j\'#96q~8Ƌ ~fӆsX+MH y㩥[ oZ׋OxDFԯ4lCjd?xHY09'oOfMv%8&npP@VN8JI|XUxҥ).-ǯ"ÚFjzŗt]l։ϱYŒg,q W>txCQ:ȱ\w l ?5?/acEi#H/T,4Cdۈ# 7 5(XԡZ/?kC񝝦,p̊vÌ8ZE]iVO9ݪ%@Q+ڋ"΃6aoڭL\`Rcp6+I_&cqi_HeN@afG,bhCsqE0&ag[R֢3ir{e]^۬V`Å[txv-}kogj.zoKLҠ4Hp!#3=#Fj%GM{/LW"]ֻokW>2FG{$ɟbEE#~=A)O|<;`A֩ѓx~崻{7_:&y*OQj?j $QH\=Gttڇi\CYmSe[Z3ў%?4|M!|o{4}N_'G^ (# ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( +?*|H߲F)E2CؼnG(Bp2;;4`KtWw.,Q2}3f8g\ńU__+F:P;ƿleoc#@ 75cXOrMmKJ~h¯~@ҩCKdA8>ARR(<9xo J:ÉKx]jFG0RZ?3 M9 LJ|;/ƿuĚv?I}rNY,<-#4#cW<)xt9 uiKL?P(,2ssRGF_u TYQ閣\+p@y8c<59eדgX2t/iM '󍤺3m! 7Öo$s[X%Y )!0RX-/z ItB}_ٶK.Tt F?k[Zx[O"ZZ%/2vA`W:Qb'T}sJI)U>%׺fB8zK~VOTtmvky@ ď0v4ǽXKɤHL߯O-vi$c#}gh}hrv)᭏WceAHK/_O8ʒy{Ax+OjLwk Q}RfkpE`x⮑xSW`1$H,ZEvuM‘$1+>ZV qmzQs.l(r~TrћQ|4uxA6zUy⳵r$/TaTsJ*}W4wSGձD&J}a//;_ 3#.{-MWqy(K|g$W5o0WMF.( 6ps$mpnU ZO#1/ڵ߈detB,7M$G0} I:>{|Wɑ'0} I:`|9uWGJL译?mO$?V>d}wE| {Co?N>$G#+O`|9u6s '>ϟ]_"$ßI?'Q|=+|siXAj!hEyX g iK+={Do#nm}DAǡ5 4JZG6rcq. p0q5wIBmB\˽)E9>ۜS@ԮSKQtTy@z@xNz y|?ʡ6s)ʉ^`|Cwikw<Y:l+?[ƚܚ>}WZY&mo nmT22=k3KL_N}kjmesp#1+8%qU!w :/bu";t{˻H9 ݜN:s xG-|;{Cg"X`1ZG?GO!94-Ln7{8X v8WAۧCCO'B9۩i?IE~HhxHR|nTX/.hdvA᲎Hv,`(` 5薾 rQUn:}wJD%;OHNoaJ'#ֿW~ 8|"L ܋"?s{J̃J?_=Oy+o2QfoVzV~ 蚷ʹM#CMywD͜gzo/7 ?.sjq=~ *$TA$i0A+G޵>15vJ2Oy0)渱m”KF89d|c?e=/|}y/-u|Gj~׼ZrZܬ{qq|i ~&?x3qZqFU>|aƝT<-s=՞Ϥ\=ųE;| œ"8牭:&௉7>.w4[ kVjiV&KW΋9 _ ^m-Ğ /.aqo[WWY[Q+2r0 rW8k5o |4Q=7I]R+ =+W sgVs$x9j_ 5k;&xܐ*6O9zg$W |`<eg$>;-XO43\B ɺg;Wk⯇~j@4Z90QծM&NE2ˇ`6rs6~˥C^K8*m'Cr9*Y[lNfMF1a~l_1WN4 /omPɨЙY]@ zq\8ӄ鯹}.M(-MNOT״Oq>f7wߵNtNXM`GzuVKxK-#Ry, }}+~k}m*M+DX}8uyOb4'9"yn0̓Wʯ*Oi R4Uc.3ӜkZ:YWWU촍;Q[EgmPd2 >x- Aq] 4gxU+7js-%Zg6G]*jݔ-uW\ē*xeXVRrT۬#^ޓ#5j4KOt`+s+m0j+<(((((((((Z26Ү׊Aa:O7ȱx6j`I!HFv<9cF;I1$jQEQEQEQEQEQEQEQE|L85Q;+3SOC+V|I?xin/ M-S͏xcc0_@094|m7ՈF as}Zuus]ϙ/?]"y+4nWZ&a[rF׸?E$gş믩I> ~9uoTѭ΢ |[ 5\h"\}13F|9h9/mPSuiJ v[*G+l RxkǺo4{}g@l*xEBF)zTψZeAgw%rXb:2WW]a._&)$U aMz4hV70fK乒kc6ly0sIƜH)!cpb'B_et~Yо ,׃QmfLsq0Q+'P3=kg[[,=RUUy3 'jM?Lͧ581|S5qy qۆU 3q6ͥڴ(ˌW8-PּgXiM"ZuV8rB nj޿־>ZeԚU% (<W;~&ּ=w?lhd$m1zW\)ƭj]6فMq 휑\W ^w~_pkp.Vw<Rwy[O$t#MR;ܣ8psK¸=o%q2ܷHKn^ǽ{d5Yd;_p̲! W7Z<36kIª+32}NJ>`%޴iv[I>35%vad$1_0;G9>;xwqy4PIp;3y-%c DcW=Ĕ׬SKf?Pw ck +"+YZ)c`è|HŶ+-F!ay؊7\ckݖs fY&*jQׯC_Ajڭ}{0 > KHI$ 5n獘Kק?@EmźhRx;zmŌI 7( ( ( ( ( ( (oo[\j:+ocfy!UFFYr@ W-0noh@n1iȸQ=Vni{e/*AVRVA4ER((((+&wHGNU+ hw*K`p aiYXec,>:뿳x~O/mkmN97IR$8#|^~+jY'R\L6]rO?(d[TW]p[xŶykt f&z ?;)vZ-Ais=8̿Nt>J}2R5WzqU]k'<>}*zr ÁKUC?ɠ,rվx}WAi[j 2x.K)=^7\8^?ËUoo5-5呮 o1doq\j|C/uw>JTӈ\tY/WRԣ_ϥhG n2&쿖Kwk֋ |2ǂOP}IbU`5R/Me-'t/ =h8ԏx/k>~x_[xJKhpMm 46ÐH)< \;yysp-p608lj|C7ZM_/5et$px3/O?CvbB:)1}(zRq8w͘V4&7oyi|Hoatmar\IF#YK~]$|1ۘUR%؈z@*{;hlm % ƻU@8dW] >򓼞^GaGF9)CR}dI$TIa,oF~^~_ZLcTl8 ^sbw.P鎄W.4HJsiןЇFdndfnn:vR)88|gst lU$&b0sҸ1쮏R>5ssZMܜ\_oD<޺ AK.$v X_z)M$Y<-Oh_v^>-xxB寢_N>g"ow Y∨[pw|`jv3¾,~ؾ%!:7| $L+xZMvj)!r~wj?i0["?l-TCO)f/ _Ia?m.һ-[]=jkoh2[sI$<;x^#|=jM?9=Oh-o&/1)#h$Kvkk:ŤkmMOŗZ/<_8<5bUKQ|?746,rdg "qgiV[r#kp'iˍDToU < ^~ x3COB1c. HH %NK oUQ6{?Fx=ǿ-iiׇe,*'!1IT+t^5oWGO;}bXKT|_>skEOɨTγ3̦U؄/*~ϞGSBh๸T[$I怙;@`zU]s'm|_{V5~,YOWZ4u^!w4DqY zGw@6AK]4k’jTo4sգ*8qW57'Z# MG֣~_1O/>JDQ?]z?᩾(:Rx}VKȟ%S|Qu=j?G_ZG)SK%O᩾(:jo?N#Gh딿7$U?'_j?GG57'Z#ִQ_jo?N#Gf|[-{Ğ!ӟIM<Dʬ{H.:W-蚕6Z09B0sȯX?2?+5GmM p8*'`Jޡ8Q:8TO@ɷ9\ ҆,;5_&@CJ֫4@Xɪ9X"wp5wp8mNo'O#䉛ouىkǻSH*l8˧ VA M"k\Q=3^{cktCbgLGC^;P)\^oD3 mLunks,iũn|/ռ:"zWGpu? &dSֽ|>GI& UI_I &T;N儕A!G7?0+0ߩjбESՃo'G߉?<OVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$hOVIğMڰzM~$h)oZ:jv~Ui%̬1ڊH)9<~b+~|>]YU|;߉??`IN~tY~ן-m !#|c[TԴE+k^ HѿX=&4j7b?iOyo5?fب'1/述,u 6a㑂8 ڰzM~$k OH,CU@[Y;_Ov&믜i'kټˬ#0QVo[*>լz)x{}ʣv>*xee^hĩ滑UndTymcϥw 4dM03s'֘<;pEk|; 9˷))5@=+,7 oWiWXN$ђ~,h'WԵ7WFz4"J?YQ]QEQEQEQEQEQEQEQEQEQEQEV6\N(!C$@'k3w_)LjSM&Ќd} ?G?~"F:TZ5`e]Ur,eh_-$k|F<&a} !(pAh]PB]q< k_9-/dy|'G?㿋P[ jτ'xkR\&[Ҥ =Cڀ:Hu+i -VfwvMRiQ^=+P6Vr=pXxu=7/+ .ai xWYMt-T,[j(r@4?. u@}CO.Ck6t[?= =NPk:efEXh>he~e#9#N>$w2YA)b gxPN޼)h]PB]q_ gi)ӗLZDLcmm|S|e^ygku}6XCN0/$p $gbMf[>~ApߠX_IR+3~iMiSTJlcTg5Z}/m)I~u?Aj1!4+8ST_h 4dK&xl C~4Bޔz;ϵ@ UݨPڜc(+S(oA@|4 ţ>9~!?3Os޼|dk/[0O5YǷ{5r?]M +n}E3B( ⠸]@:R\.G#$vw4kp² Wn=wJ~z~׾xnVnx+q\/H9]Q 4"N9ĕ|M6VЎYD}6Oի/OՅQ^Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@sw|J=tˑ 髬{źðe+ր[j{N/2S ;?xF3ƞԣBҚ};DWuY/t8.DoiJ̢6g/$"I"Hv;kLGHt_p|9Sp`s}}8;[M?j]uN$OXTv ]9z PfQۮZ?T^c14.y/ [@RxC_T]GA&FK`322E'|ziokPV5Y/JHH1+GYciiFة|KmmV-fK4u^_-i%\?kG?kT!5{? .>#hZnkK߸/ފ;ֺԴ?_xQЮ${%Hk79`ϴ4 p0 ~O 8?5A]?@w#^8*KO0jק7,z,|JS^$ŽM!}>wo$e^5n>\3;72*t!bs,qjKH/V֮"/mNJ%ԅ%Ojb4kZlĪ\Va_4f*PК>#k1_Iyn2ŷIWk.07|f[?5A?5A~xW~4Gc"M:{fvȅ>Y7i|C3>'I&(&k(|p {W?kG?kI{ zf.:' Ũ[qy}\ڳLpJ9N "|>J5욖XL6O55ĊmF@N3ǿJ3P/%o7V>+3i+44_sw3^A٫N?KEV@f8j%~͚_EvCݧ!J?f8lߥywZnrnErA f-/3_9vBOON \ߥ(]ƒ9ɚ<+hka xٗx ڼB?%ׇc|Ӕ_EV*ũ:;ֿMqNqjE"91OG4Q\TֱP:4da{GO DsϋhP??˽tWύ5$ϥg-+ iQ6pl֊+ORR>Zi߱|U&=m֯KMB.߷0'EG()ױ6􏃃 t]g";`Ѹh!X7d`@2Ey=~?[ ڴQ[œiEYujΫ曻1%w۹It(nQY>(D'G\eI G?Y5&QE[!l~ Mqɔ|?5&QE"g=%xsE~([?~7:7򢊙lӇ⟉70iLH5^⧊>vXE==Lϊ^?,>}ḗt{?} #vnTE\v&[%Mg`!ɒ'qqUi_ /Oq?M/d㴣⟈KY?VcOy,v|R4E0KY,v4v)t K??O;E@/-/wLi?C3Q@ K??O;G-/wLi?P K??O;G-/zi '@E,vZ^"?O;EOZ^"O;EU6? Y?P>"ݦ/Ijگt4. vEM8f8Ŧ?fotoxx-18.01.1/images/export-files.jpg0000644000175000017500000003271113222767271016256 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02300100Fotoxx:voodoo2| 5http://ns.adobe.com/xap/1.0/ 145 360 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((h" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?*;{{uT3EΣclBdx$1ڻJ!#eg,\g sj$.n#mA6#Kxt5\OAFϾ:F}kգӸfhKA%TrI1Eʪ' Jehp jo2'+ i'/`AD,qzS|~͸>fާ;vo۞8Z汲/-NvF gzqm"e8>GjWW][Ak04kV#GD:^m$3KG1Nh '͇|#>*fkCOϣX4x;#sJ.=͇|u?Cnu8--7èF6BIc_Væ}BkOIU'z .=?̇|Ak$W~&yVfEB>,x=ȨC7K[7.O1ǩu?_2֯~(_Gei$M`)F3z!jך_ >cJ_@<c!?j"4VV|ڌi3`-&Id;/+]gDi_Δ$1'(oǷu?!yu?!oK[Zfcs?618s޴\j&-[3ݪȻD~bh4zOS̃|1{6|?dOc֏<6y2GSo>9,lu?K!Ui8|L<~\F5I{uYX_08ܹ8~^:*IysH[F7ݿw_ONJoz_C??^3-M<+,W3 b\ ç5v1CSdQ䝠9c>az5 z/35'_ϖ4k/ ͏~3?_GsEpdg_ɷh O gdMXuIH b2 )61=z?sEsOң)5 9sesð ":vu..^ L;s?#??^#?_^B5[fuQ y 0d$מա;{Zc\y>zvn2?*z?z,s çF7>j3zO?޴oz??j3z_?޴??>j3GZ???ޏ?޴??<!.g}޴??^G~^BlEhIR>׏z( wcԕF|C9E[@ٲyzuM+niYQvni_u&;N [y'E NNZ҅RIrh 翷4GAO+_H-m2ɋNbI;U20NwsJni_- F{YQlcp2ApsqNb54ͰC.pWfs1j5 ĩF?4ZxOŽudG*x_3E^&as K{yIl࿵ é8= f5Əe+b}IǣWEZh*b{c2GQTtWOÝ6FvY6|t>IS-VkfJHw[,22H@mҊjVڤ2hJѶǦxz@ IERYjV׷ְ3lc# PT u]T4A`u9榠u;_q &}O O5v0J((qF)((EbuAK@Q@Q@ۯj_-_5:݈& X» AR9`z֦EG]OXtiwW&hO]0`yu z׊.uHmUmFP[Ecn}ē=G#QU͌0Zo.9~]-Z J]q!E4# Iu,7[<_ D 7rm\mv uP>ezǍH5p<O=q6 jngkX̻ڠH`4.e'{`5;gpG?ֵαnWik=>qR^&ۅO4{g. jZ][,Gi wI2Fyb28:PJ/[Z<^i%FZ<+:\(mn?Ю?|Gxz t@Zv#rܺ z_/?Y"--ou*5$721OR9\ck5L kpmd8<ƶ(7o |IrRƛjdq$ynO<q1^ֵ;KnYx˹7K&v z3U}nMݞ,V`߿Fge*+4jNߘ;JI4v%nv]~eF^E%3 GT]>=GkǴ7ak),HRvkf@х#y%riΤXIZfI =~^N&h, u4fE;4hh٣4f4f4f)-3E4J((Q\Q@6u!.4KPuo DEL\A v>ÚQEp5Bo;NguK,n8|\s6즻Kxȃ94k 74V"Fd@v> YSBMhg)* ?пg~sG4fпg~VBz6O؋H@' 3Y_ BNlc ]O+14|њWBUGR|)i[ Xp{I=)њ̏G:0 cR{ӏ4/ߡNsG4fпg~h_ B4Fk;M6UY4? ǨEc&b.HK* tr9.mi@O(SBM,fпg~Bw1[Ic%Ġq.2UO$ zhf)&?i~ t= j>3Gzzⷺ;2 gӎAo: c{uh JNow1?G=yD:W,?—#Je_`yTI2(ҿc =$OΞ Jc~J{hf3HgZ@i )(boóiu$SH2(by#z EqkDE+B.0nRGgiͭۍT髢mt<2:{q{膑GO珴 fh^WNg`Yvg#ڋ#-fMP֢ҭx{oy'^ڥCEQzȑFZg|p~Q9=hAo F!Xu LLGPӬq@ ~&5Z]_\iw[q dF?̂ cgvM^-AӢy""Bp9%zץEi0*;I7 nBOrghZEk}.)O4vxH}мE'fգI b(PWvNrORxKZÒC.IHmnṛ>lªV'ǦCa}lJ`o$yQٟ Y^Owd%fď&yry3tKjCáMr36--ɉ+_i9 ac!ez(꺕ơ֓[y+@%c'5<0RT2h&(.bÔޤaq過N&[ܬ$fH0 gۊ$pGubjgJ;Cyv">cqUŞ <+oן[M-ժZ4o*nj2H;'6OH-'Wm%:5+M2e kg#:W5=nPN viPoNz(Ueu7w\tbI$`bq |w52H90…?s/Aw5$) "gM=1c9[@D<&KmI8&Y2ѮBnƐl}{Q\>e뛍>9'tdD!Ddo55/GW7+&]ij# 6FHp3IO ߮].ŋ!yyx^ m(F%C}Q z |-kSR0QT'F1Bykki+KZM,g.cxRǾzsW?t oQ%eb߯Xq'kI'S2KS6Leݤwe6== UsY?S%W NԓʺoxNKmlN?*hMVePw'bW> Q͙(/7۟@w߁Hg5CQ'D&Q5±V8x'ЃڹxuO:Oy3o߁lyۘM֍JjqLR O 0nDzcSv_jn,iS&!Ef )O[56.F+ ㄿc;%2 U9OkuM,6!o49/KХ4L9f/kq?ڣGٿZ}OAcSsf/iߦ xttXٿ4ٿ[4ƀ*}?6/VK7i 7@ֿ7R}oV 74oo7@?c߷ ߙom~(XUoYkTo7hǬYXoѽߙ k߷ >=foc{y3F~t_c߶ OYXoѽߙ k߷ Tѽߙ z~(XN]ߙ.o?kYL]ߙo?j }6~fEM O)%m>~f{y3@& nߖ ߙo`&'(o<[U-o{y:]To<7SxaR@=o;74r #v̤:)ĒrI'EXQEQEILRZ( %:qIO44bF(Q@ Qn(;n)1NF)ؠe%8h7;QUt6;(nwwIZ5Pz .B=y3ծqq5[шeanݍ=?TUؚwn?*_*Kmբtկ.& c{Iʂ8[]]izlr&3q.gl]cv 4TUؚwn?*_*Up q%¼'2ARcj5kK[:}̭;2vY)d} w_ؚwn?*_*pIRxr߁k/jwZLr[2Gaq5M}ޠӵ=WWuM@ح0#oZ1l1 />-ABlu8Y$XHw*yFuɤ249.qx柨6cNYOqi_CcCnqKv⫛UMo.lyd$jÕ1'nj QizKtDw_;{nl/NRaݸwIN(Y#ݜ` _7ts%㸕w.GxGZsEUh ": _g,WGcf#1N?~t:;[#1R[b'8QZ +J<=]"6֡Xe<!2p&(B(˱衔O5zxAFbʮX;PeeCwtdrt!*8Ѭv,IqH!-&hMoEU_@ԧe:G`U^l#8N>c_z|9wV2E 330 500 0 C     C   )" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|{ӣ[#2+A՘)wd#[?}¦?‘?_ [?mׄl/bmu䌵{,Li? ? #Zj~,yӄ2G*B<]ݸ0^9JNItobZ4xf *'H@$d#^{Fׄu<ڼ./52+OHaʙBەq 0Fmh:?u;dG6 }j}ɿTC?}'?½OڵZ ܘfӠR:)\+<+x;w+O^}R%̟fKly> /K+17em߶?G+>*Zr)Դ"Cma5 2&G,@WG| 𦳮x.ڭZn F ;}I%&$( 8'qzk%I5s8卹1jSxGPȯU> :}xB^oello _mX 0*K w$]x+Mu(xk[5˧Ծ5 J)^AY^?l>| >}] +Mki[x. l ׬lix+#e Ul kV-].Y/0 :y51~m(]78gG+Cx+|o}hx4Y4f3>Z` HG?xjx^5ĺ0G"#|T~:,:9}&C:`OUWW/mk>BsuVgu'9aOl>|?4V𗋯/o FEs˔/HFysjVj5cc?}'?±GڽF(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?›xAAݽNx{oQGo5! 7&7WtUGԚiþ7IKLNYvnR@'БP˩s I1K}ݍn#1^54=f|QF ,j֟kCNQ #! ]zmVo>f-| Xm*8}cHLp*b٨(Ӄ+)JXюi/oLuni DP FA!"A1msVSYH>ƽ_-Q6vF 4ۦesH~6Io6jqsA1^<r?FwbiRφԭJnVZi?Xq{q,SK o#2ƧҊ&9<_Mv| .N4}O;YvG;YvWfMoZ>}k',_F,_F.~{}_?Og5Og5as'֏Z r|8?.r|8?. i>}O;YvG;YvE\I'ֿBܟ?? j?ܟ?? j,Oi>qA]#QqA]#Qf?=}hIПw'Ïw'Ï0GO~ù>şkù>şkYoZךkL^%V$mue8r|8?.r|8?.It'g>Ҿ%Cj-cQ˧_,OOZO%{KiQ0F$LW9UoMlqnV;W}qA]#QqA]#Sn/mtHY-5y,Jc.@T}؊>69|G<{5A)0|52jg,_F,_FpNMƷ.IKiEqa{i"9PaۓXO՝㯎Q&&&]:X<7%vCApI'#}qA]#QqA]#Qg{auw]i.t;X J%H';g4|y}";ZivGs/*Ckw'Ïw'ÏV>0McTc!~a7K #<&5(UA ^U6_Msfsn=w'Ïw'ÏiOO7xw:OdW\[J(2cqҍw|9[} x>A(X$l [,_F,_F˥ͭψ4ϋ~*Ѣ#bt/Z1RXh!.I$9E,tV]6t̺,܊~`Okw'Ïw'Ïu7W|_[55-R-̡T€+/'ֿBܟ?? j?ܟ?? jJ6/}oZ>}k',_F,_Ff?=}hIПw'Ïw'Ï0GO~ù>şkù>şkYoZ>}k',_F,_F.~{}_?Og5Og5as'֮i !xIp>Br|8?.r|8?. 83jav?cY_˦\E=*şkù>şkYπVӠ0O)A<sK;\'t{5,~жs5ձe(,9pMsrֺdׄMh$~y#FȜv$+vZe#dmJJ"N; ia}\A>+|6Լsh:6NY[SA HGR ?e=WJmRTK$4y9UEvwkh>?EkW# |;٥Rڙב\mVrɴ3'j}i^׵\ZlMm™G7$cfե[}{Nk 1=nSofֲ-"DYVUbO+i283Y|.6xH񕌗K 6H.LeKHO%6Kg-K5j?6xn]PF+h%Eq4fo9~ddgTcz|!qm 4,Zf&Q2Hs;(caUF3^CՍ^QEHŠ((((((<_;C`Y/.dU,V4R̀9':ֽsi+Zo Mb[7 ŽH*pF{Nv\@XCj(sjvZ1+r k/h0ž擤xcz:b>r42yMڷ2x )#,es'ߍ>6]~%Z%v񦙧ol(WrF@Vvlj|Kυ4,W“階_ dUJ%1mC110ޑ k}fsUO.u +ۖɉG̙,Bz.>%k%j66:k5Lq?Dvd192ߍĥ{./>8[֚|A;CO}WLێ[dh?*i %dq\;_x~ܺ><~ xB!c0gjJ߷3YAM;/a/KiS:DV:A㙡213IHEC*'} 'AK[\,pGB0w)BH nF5Mfl[[VK^[d*G2;HeFvi =G_xo:o}BA<Ii b."䉋J.j{_of[RAyȷ#9bbp>o<uou/ FP,uje)8 yKO|κ~6_ƺ_/BVŮex;̶沺Ԡʒd#)uo/KiN`u&2De@U;NL[ΛvtUK6|80^twy$1-(Bzٝ5T}%) 6QQpdccS+5n,y=6-f.φ|;&ӿMRDhlR,[9jyue>s< $-I  V1!##׋[~/*$Sx~C}IGXe>qj~A9$ڴ[-SPI-`0E#;,4ݺ[_~((((((C])u}C9}bMOrxHֵg-mT`oDn[~A]˟[|^OcXv2?Z6譤XB',9XKѪ$bdU*)=ϪtzN蚲jVylnYcUьS(t;HJ3V_c@-zWw?bQ OsWľtSAwӞ."`nZDXL0 sZPLWXVa Љ N6D~TxZa% @˞hN>j(*@KhgNUOk#ksZY3[$*.vKaT ½)tXEIV-׭g#uAsfKeioBw#߭zX.m3Okmy[1+?Oakm/Gn'-lx)Cxk= }G`\|/~69"Vgj0k䯃>6?3m o9 iHF!ԁ:~" ׵W3i~ O:jC #+ rOKQR;V|a/ LW| z/PLTԞP/v*쨻q"2J++3JW/ꚽ<ХɐʥѭĊ,'a$z][i-U3Vgj0hm ƾ8m!qKgWt?0[շm>lwrwD0$0\!l as>z~u5xo&r->(ǒdsjq.ex4ғo\m ƏVgj0k~\⿃|Rӗڣ^ s.fUIu$(( ~`+DSm ƏVgj0k9OVgj0hm ƺ(+m35_K4¶?U]]XS? %?[i_񮮊,) LWa/WEm ƏVgj0k¶?UG+m35_K5E?[i_[G5 -A!ϐʦy?3k+ˬncZQEHYyZ|?}74Fcfk;^]Ŕ/#|pC}? ?~αx?#jfdtm6^U.)yohq6qI}^ ד>i:ֹciWE\`I<eN `B<9w–7N]KYҮx5xR%]| ;k$IWmK\_R(EJ$$I!hf\V0ѼgQ|1Ԧx>(\Kl\4'eʈє*s%;-^?qΥ.K}2IηxsH״MS6?#˪D_(}gE(,Tp?/5EGK>څ$$|14|)o zo߇5CR:}tGyLE$T׵|M{8 kvZޡYxӮp]eaY>}?qzw?QE ((((((((+hoggo<^^CE z~aݤ3ztg?3_ }m׭үff{iPv)ZҞeu RW[JGʐQmŶ!Tl:|5H-ΗҴKIp PA.vdW7<#_hk> v@Od*̬] WEAާqk ΥxIͨnć$ȹ[k_}RQ޾\_-< öO5i]8yYBfd VWoo [^]Z5n'H"F7Ƞk=c0xEu_Ksijpx^%3ji5@rpR^H.ߊj+ľ 5]ׅM/5{+ \vlfϕ+ P+v[j|JR'>*[ʖxRJii+S{_.BnTW5_.9ZW/>|%Z[i=93} HEⶩQEQEQEQEQEQEQEQEQE_7x± zyu5CkIET5~A]1լ>[_i:pi[-A1FHO]>Yy5߈_ $O xzgP𖟠]RK)"34r Ch 6N_oߍkGx58kGH׎&-'ٗlJ##̀Ftz |"SJ2Zkw$fikOktTQEIaEPEPEPEPEPEPEP^C[ [/ˤn++h;Fy^Ht{4 0C5#ޏ^_JIfAKG[CQ4/ڇD7=/^1qJ-'6V$o 1L|n'Gck+a1]G}uEV,TI8|@Ÿ͹0 ̞{l#2ALokJr}|GP].xtBͰ dgti<h7PlJQ[h1|WWþyϊ_m~׼ |`m^MH/s#malkjMa2Ka #C`988^=A%Oś-bG׃f .$`^[6̮1yA>xľԼXZ[Znu+)Ak~b/̠8ZK-gOY7>!5A\^Fr{şee/5[x5HC8, AC~Q޹SPĿxQ|GZ_#)p!g[xŰx*nZ[|ՌbeGqǮ*TS[/k'[ok⇃|iMaZi7ƌ2ʌHS؞ 2⯂s^jp0l1 0'M׀,< ]6^#/ 87RK\!g2%.e~x?^hAipXZ&%ev$Ж_V/?>&;W5MFŚ1MBRH 3"3pU- 2-?X~v˜_=x47SN¾#.xZg,fh%0%T51 2 Ѿ$IJSߖaM=uI_=Ǚ PJm[ =S?ïZk+>Ů\AV+$xsoن֡xO_ d-B+j+$ &ؙ0u98W_sQ7tտGv_y6 x_Su:F~l/[3?4)[CI9M2MJi1n/mpA_'uyw92ֶ7SQEQEQEWY_ 65_7x± &QR_t_^|b{O]xbXŽ壎! RHM' //7{;N|.u^mk wqn)ՠM"{F ioߍu.tZC x<)>k֐j/mOsc\0qvbLnae_W=W>ot .4[[?>S wH+ !w¾/4}b饸uC^YIU̅ȅ76T<)k,fmSndeĶ+/+*Ҥ->:j}?Kv.{Ex7~ v]Ο[Kh\y$MA۲8Y|!t+K]%xz[`a $8yW-+_??h7G"[ ƪіe`k3$8_%Wi75$յJ!-\\:m)L#nGoOroPGZ?6ejZD,NGnҴǘ\eРW+LJ5 tnxcg򡄘 ŵ7#o8}>g>]U]^ӎAi0?Z;.vneO,N` rM{/O;iAa\kYIgt\ 2gnF@ Ծxߍtf-=/ulGuo0ID.p -ȯqƵu[ 3W%]2-!v͕Nc$@K ƥQ@Š((((((hciWwˆ{v~g;wUM냷 =<akm/Gn|0C5#ޏ^_J~7o~I]_O偶$qݑ$(B#*0qH;ku=2Z;ccqg1&dg8oȿk_3|3!VmKM1wq 1/@ nA6WNJqsSG4RGH۹n xZIS+$L:a6%og})|N]~G㿇U |=ZCp\i`B" lP\{e|S5/ּiH#i ɠ5nw$o/)@|Lenެȋ}|%ETQEu?xr@ޕihaB8b jYI((((((((FV?!Q.ƿHmi05(&?:Oӟ9xM5w?šg5->mGBm.mI\o>eEu:KTOxs3iiqu ]9|P.e]>SS Z_s׿l:杦g%جd)!yF!glHh;G<%xOzRXm, Yo޼ȹkLXs)s`C-wVHN[kns{3j-7Ƿ:OQY4 jWpy-Z\ v̋3Quk 5ˮ]Ǿ(N|!⛽5;#kiiϝ AKA_w7ßYs{b&}դWQ_^P]y~ꯦi-Q , Y m'Gg|*Ux~ZC5[XHm#IxD/G)8*U own?+7['5k}7o>eoy}-#wM'c.O?υN'Fiռ&-⇄sHr0|W߈m-DuMfú-As&svl/߳ bMzS=B&F>0Yvg[/Z ]6yaq=]㵃hW2\(%O|KGCwj>,jEqe^G] "X$TQďj/sE񭶛y^\`=J!7AT1(|>yxko [cݷn{a:IFUfO/-a9P:FE|}ޏ @D i9$R7pSG}7HI "gAT@8]jK}EܹETQEQEQEQEQE?iү#R7~݆bn᳜g?3_ }m׭үff{iPv= (((((((((((((.ƿHmkkˬncZL J( μǏ^4h]%Dϩ R!Dv,; ?:O_^y]4_xzAլ-=KLA!=ҩho^p޸!5-9|'oV_rޯDxV=/.ucWيBx!A)&ShŬf-Z\*imw \l2xophf񵶢 r˥HfyVA&sیpkw&`e'J{߿wy5̜vK}?KSc;m'MӴ;R7$g ufrylT,k;O2NK/ip,q70`@0=Ÿ࢚^U.rE%E/&8ûI)-v=¼? @9NGJ_Ogط[ ĵyOxc@MO]1hYfy-bQpCn8o'".xUfq_YBnfYKgKT;aOe ^T?@#ˇL"tf \piV͝ġUd \r2A3|eoymYKKD#:&^M-e;H0SLB+maHf/๏\Ѭ#" Q;]^ӵUׂ(Y܉ t#閖)wmg8Z]o/ KZ/((((((]?VEloax^^C}?V>yg? JfAKG[_$?3_ }m׭ҡ(zQAAEPEPEPEPEPEPEPEPEPEPEPEP^]e#w+רחY_ 6QEHY|{ƾ M'~ ^cK=>ILbWk܅@ pv9"? ꯆ~ ͏Tgm=~!jvoz'_]?5\tjڮBPKaro&9&|PKc\~cY躮6ot e!e}HSןs?lk -{ZCqk Ƒ$O6Vh ^-|W4_7?Ͱi%#dvy2#(vm!JhͲɿCNҶr^|A#>2ixR;K]R(o2BJGph̅.W zjGï LKm[)XkS^4ooȃk 0͐IGo~/_lt^A0,f T_jw9Mޡ;UէO ~OY[<-ۼ9y6ĬdJ(!9RNiGK_Kr}.zeQL((((((((KkEjhm.`0'y6x{x CkKl,/'Lc/ҁ3َT,{MKG[^y]F'OC{iR`B)((((((((((((+ˬncZ/kaX֓R(k//󯚾(XNۣxfKV.ˣB_nSnI8V? eaai2b  x5"F-Yo3'۸|I?קyJSJk].;KjjNͦF{r739EK*{/xBŭf hBȬnyRʒ^E{~xo^"N"9aӵ+)R9HɆDܬblW+/o[jB[kiM+3gp vqU+$noǝxgPb>d۴sg~àjRд4-YaкHcϘR3g*ֿm~~~#}>&>hvu귾*ɭU!`&*rVOU&KUmwW>h.eHyK)-"d_m7aa֭-/Rխ&xD[ 2@)^;WZn.VKMF)"̓ly ۳qoTe*~IieݞC~_O?'^毧3MbqE$o pHi2I>g'| |Z[j֯}5q[BJXHC$- d}J?f&iM.5Kk}^;u8a(7ZxA6HUI]JӜc>xZ5?dtՌO1Wh:PO?!5[wxoox3sj/J8yb 61* $.eKK]7Hgi 6]gE@nttoᯇ|SgM3Ugq-յ;$IQR?.v 9#85{ | TΉXETs`Y@ٍ9#pqN:?{kձQco:`T{=xž{j%A{h-^{dhuVNHr7{];_[,d-42iꮋ>~ 4]W ǥjIhK1 2$Z[??|o^sCM.Ş0Xxi-^G5F[F0 | կu+];3S]^TOUdd ex5<(|R@in3s8k0'^{m?tV4k[9 [`GΏ27:|XZʋ ;h-}~]=gcI m@%%kٓƺo~$Ƚ*onV5M60@UA8+V?K𦚷R]squg>gG79cxOJѬT"8b$VPOȳ>Dh%_R_>Oih9h;ywrrFƯ<~'h_ pkYy|弿,I&x^5 -jwN78U ֱW$x:_o۷~mwuD+W _֖*|UUӮ:CY:MZ$ʳBXi@][(i5O;@֝B-$U$Sy2ye2d?hZ-𥖗m: ,|Ms=ޱ%oeByܪY#?Iv)}I +xPRqZm~V[e\UE+YjךddB,)<$R_ڃgq} C\6s[%v-c1#C8!f;FNI'|?Ùo$φltY/`[kBtk :W_|={|+h6d Fvq@8Z6o.+RS ZTb KG[^{JZ_z?z*Lh*(Š((((((((((((FV?!Q.ƿHmi05(&?:;T6< ,`Q<-qYK10~A]~QеO<ӵϲAuL|7 4@ mv[qw>u>_>/þ!|9/xAnQ`Ic'w6cn20P] ůvd,(| 吪(mכCw}YW#P:m6]rPN MKLt__t ryX2UPϏrk6ou]cSG'Եf%mkhr U%XJ8J[/w>Tž%пcGc ^:w5jA ;1RXzՍs)jjkvTO jh&$#2Yp~=Fcu-{n.8ir#UDUQgį|]%ωU x[qnjڍ @ҕ%xdρ[ EiRz_+ P&G2x='߈_c-RHVvСrJA1ql.Ҽ -}wHk h+ſ5Jhj$ֱͲ(>le-#o1X|e}\Oj)5tK۪:Ɲ DBWxFi?˶_@(QEQEQEQEWP7~$1BJ*߉E/Qy[!X` ׭g;JZ_z?z*3BV?B{iR`B)((((((((((((+ˬncZ/kaX֓R(k//>%@/o]]*+{CrI4hdVFp8v_t_\ht jzƢnҢOo"ci){h$\d.OhZOMlAGFmVz;]“FѱF`NGq^'O¿/Emoy˭?P73Z7({DUJA\a*򷣱2:^+?'ԲBj!qq3OiW̐W l5]oZ_M5|}C7׾t;wC)F9F ׼Y$R-ʍK>H3JO/wo"{O>m)ͧ'//D@s&|NgyίUڎ 7u]{Pm)%4QǵB:>5(*$x⺁'D2{VַOm[|[+>W3d nWzW-..bQOpf8!q0 yL1K7t>H7u؏5RzK~"O?ix?~'<3}2xq̩bG/]|HZx]MVN%;HD .ƌKq)~S)&om~'Ag(~D>;6xsURr%1YK-IRɡ8{m/ok RL2"i4Xd_+HBM'}R8mmscvUWhexCD !ޚ w.Fk 8t?P 7Pn$2pkNj5C# /% iE3sL&7.WEUQI?ЇiB?43}[Jo xGx;XXc×B"-& PŽA]C·*u*K Ld K[uivūhޝsڨk:z;z,1>mjAydnP[ɨTTտ \ONkWeþ,?MAΦ7ͩ>iPHX<Jo;rAZx&Da< io+UTP95'~1h1\)G}.k=fF9 vHn|BqA]}ٙgԣ+Hj'V]~~݌c Y'܇ <+ |-Xh󣰀D@NORI'5V~ /PK6Yp\YΓ*>IRpvz0=ox:v7X/i3cW̾CҨ<gža86 Zxe x~/|p~i =ũsmomw]xeROaeӳoM]mVKxBM}t'ִ101rSVwcjC ( ( ( ( ( Ė}X^N_{ xF2)$:d}LuO+дUg?1^׭ҤESQEQEQEQEQEQEQEQEQEQEQEQEWY_ 65_7x± &QRdkPJЃ BC3pk_-$O"ۿbi3{;oj>մ]FY% cyEk#-2s;˳-/75WXuT6;eHWrUp 3?ğaS}[:Kov׺Rij)ӠxiY͒YGH j=RKh8U5HČdc1_38~?9??|I?VE%QAj}3o-^[Y*_ID&Rs+}֟nKk6DZJ1yv1ߐM| O(q'X~rMiM6R SzQeQ[/!}lGtcğaQ O( SlT9OLc?e:qEƛ˽B+6S&NKڳsᇶ5W3?ğaPgڿƻHmm,H:׽?Tu[$PKk&k]R&v#̶u~$O"gՇ'@]L])m[Xg &b=Ls(u֯-`/")9mdvs_38~?9??|I?VEv}C|]aK6D8`mE W{U,0ҘVUUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iQ@~>6_j?W/ 5ZTPoύ?VeƨUEfc<_|l1iVgftp+a6_j?W/ 5U~ c2Mdt4kQeƪmh\5ջNH*$q@_|l1~>6_j?S/$OgKL@_|l1~>6_j?S/$OgKL@_|l1Mk.skmX\~5uOgKLS7/}wazsn[ѣ,QPQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@s>ڞvχ_طw 3j!۠h)|?M]]$Jd%P=+'ŸMk߁<ωG+!%s%ĪS!O2XdW^"񝇉t^xv]< k(nHuv'Qqwʶ3-li լ5kaƅD.ˏɟoOtk ŢY7XGddZ6m_/'kv$ jv3,wrdBdki Gzܧ8]&wķukdy$[yC܁I'-G&?Ŧُ[.<6f; Ui.92.)]mv{мyggmCC4"wI[69ـMPouZ$>.m,迴=ׇ<*tX/IŤw;BLM|?J0~@=MfPhi:|AX[hdHe CGIm5-4BvP kPjvV\&r*7>虜jNOy{cxYֵ mVSμT)"$KH gz?׵I=`_5N!k訢(((((((((((((((((((((l:=W bZ8V=3mhjg;o1]µ?3[vtP;2-OR*lh@DQs/|%'q}yF L 0u`ps8(jg;o1G+SC>3[O m+8V}oy#K$m=)v|Z((fotoxx-18.01.1/images/paint-image.jpg0000644000175000017500000013655013222767271016036 0ustar micomicoJFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((;." }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?~-|J4Oh T $ ff#=1;W CE褮/(?hxsߨ?hxsߨz"[i۽\jQr}h7777Hd f\8#?/#zyP\ cY19b: ~h ~kJgl<ǰc4H`Axh>aybTqWȄy( oZ//&Z//&iĖRXu=N3c ~h ~k==-noG-no\GE?hxsߨkSj)gdUq {օ6~aDfb0=_|W(Z"?-?77G?VClvJIsw=k,6,~Wm}GdS#O37HjOZ//&olv suF+ ֿCJU Rr{"Ԓwg\@iJ<^FklV!]~ rLGk9? 0QO&QO&a5? iM iM˼GkGk9??Gk9?,y0רŽ%r4Ž%r4X. aQKKhKKh]<^ ;_ ;_`8Syt'0$<m`"8O~9{;$WO2 6s_S](Z_'G(Z_'@ƛ/At1˝5[wKNgEdӴ4a*C⺯QO&QO&_oU[*$0,?a}q[z㺎dmW̎neoȌdbQO&QO&@Omv!bb:^yE@@7tҫ K5q%Wo;۴\Ž%r4Ž%r4%o0\<\/Y~۩A6.l'9׶z/nIYRmIuH/d(/bGk9??Gk9?-}5>in5ٴ)I[ &L&OZůf%֒F1$nz?Gk9??Gk9?,<{yebŤrs9'<~aQKKhKKh\0QO&QO&a5? iM iMWL[E39Vq^ ;_ ;_`8KaL. nYӈXX1hNEzw(Z_'G(Z_'E*vi #+}UHoe^e߼^KKKhKKiJ*J̨MJQjZEi>ۋnzc1ֲ5}H\j?Gk9?lE巗i%vi1hF\סbZ~I%SKS]Ņ p 5A?_KiIkKyyZopHx^؃'Wk}%6 ?IxͿ¾D?ĨL_aϥAjVz͝rCs".%6 ?IxͿ½R \NKd8%qFQ`)/Q Km(ORA0O֜D#:2sR^ ?o?O+1s {PYjVQ:oF*x `A?G)/WбK˺)$eNFiX.| Km’i}ER^ ?o8Nݿ¾)/g?oտR^ ?o(|~ `@77W|#&}IbAR<ܭ]7=Fmj(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQE}R~ʠOT(((sơYG4m%ǵ?z7d ,5MoT>o7I*[1YdbeH-1PQ^7jSK{O1Mpe}(9E Aܗ^RhT 6R\p0sM':6ijzdg;)4}\m]$W˷nD󯚇)NQ?<@o)$ m8W3ijW lv8^A?d6 GjTLAT*z`~/9HX9Æ|er;y4ׄߨ^f~c  Ϥq*K5q4r۝X`ӯ8 x:Lm5g:~-,D"oNrc#̎(sQݚ…J[GD}ME%q3HgK`u`bio=}|}#nF(CvB?Ǐm-$W?{cYz{s/.FHєJ$$;q&*v^ ׅ.4geIef|mvלW2 x_ [||3 x,umVF[4'O^ ^#)Zʃ?l16|yl7n7*`nF26ko_jHm+\v1F?zo4ׄߨ|=.*Ft$~5^ 5k/9G4ׄߨ{_^f~MxGP^ 5k/9G4ׄߨ{_^f~MxGP֮#_:g$L>e9-3q@h`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEcԟ*{U ( (2|AYk h Ko;C">>VRBxw2I&Gb39f-XZ7ٵ/\2y:lMK{=FH-{tMF`LA%x,M0KD\;vA={b>)khGlȅ Hn21 jjz;.kAi9-HIfrx$1ŵgo{= :u;24)8fʀ۴vƩӵ{=o#gzw)+Zoo.5+x]ZN-=,W&Bc&H&izڌkuϗJ p'Vܫ_ס'f${S6$q}kOMmCRy%6|1^_kzQ^6-omn"$)SWI=sMV\񖧥izd7ͷ vU#p>c~|=gYX)"$] !jNH5bEJ;<]:HC`r_b=JՒ8{xb~l c KKt.[+IUv-aE2KXj6sEe"+ù1;s|;y|>Xn"ؑoäS/dR]CIm-Wm<)4Woɏ0);N8mcMLm8=q2x_ƉAycGԮ"X2:Hy N7HK[c$ϓy9Ƹk3yR0I *O~5牴|LT2=Mбn Lg|=}WZ6!8HHՙU+'uZυ5iawx/ 4|eXT ^Z[Yb@,UyzJՍ/u9-M|x>XdS|W4ڝYrǖ|# PzݮmmjQ 1[-mYm/:3.OF=+~ssx/HTm>V&h9ݘYׂ"νm)QHIJ[z䣎vgڊ?˯GusuFYd!oõkZU*ZG/w+qqѴOp9JPWZS0>ԥ 0?JzZ^eD{Xn9R Ие*iT+D(Vweo.4ٹο]ZVKb'xV}gIVu|qW0A}@GJA'ZFg[\BwSx/,,ϟ讗>>δ' q+suqJtdQY(0(_Za+>k Cl?A_((((((((((((((((>?ASԟ)QEQERN,r$#d'9=/~?[HLrX,SJ~!sW/\ߥ߆lNƪ7v Ǒ-&v= j ZBl~U mp Jr?:ٳ\x>go$mYP&"BѲ{-;]a_jOiъhHylKm1 Z,K!Ky"9"8>TQ:VEޅCO-,BR0 qҸWU45&Ԥ^f8 e)PF U_Piͬ2iѵ$θ @jڱiZ+ qG%)7 NAm:UcA5ݩZ~Lo^}^+ͦu[mnrߥ-bXa,4nHBE(5/O׮A2UZ5 ^}*z]wwΑ{QèB7 5"ʟu#aԜ rI/q#_^Mv"`ae5jyww)ލpiQϒ`k^pO0K#шt2YI>|PZ=-J{ W~(]$,ݰ}X(-负 hE]9T1rB)8@O$(?k|~&|I};EM^3s_8SmwW]nT[kmF"-pL@Movzji6).Nڅ ך5vgx~!n.[!kM&G[]:!XާUR5TEUQj1]q˲X5ͼxJٌn_d$J+@3jWZ"יV'd$Sa4jW>gJbԊ*UZ3ZVD$%!*Hr1s+)$m 2#޼? ўzOjztaq07-EG#ȍu*0E6?"֠PX5x*-xB!&2. LNIr:(!_Za+>k Cl?A_((((((((((((((((>?ASԟ)QEQE^[y.Kw!y# b|IpHs h4eYyaӣyQ]]nJ{!_2@]8K1;GPhzM͵;cŬ" RpUVG<%_3U4}>syg5<}7S].^S;BQ'%=sXl5+K>>J+H Ϯ~ hMCewZ}퀷-=Lѝ:)a Șoþm-hmhUyNdzk3>4?P^ A|y$b'F<@rR2ztvl4}:nLVȦ`z֢ѼA`A 6&<5Ipw I<5;aw )'6"V7V:dc9m?7-*xⱻyg[4(8ez {VhzKD6bcHPۦp2Zq>Ѿ}O -ZRc1?oZiZ~A,-.GW8ï#ޟ-A!1:pX:՚ϛDҦ3 yKۡ"} ׆tDe,F1#үQD29Siwϱ4UO5ݴnM?anci" WEeh:ϓ-6ȒɞN'?7ϱ45_%&91IT; 'QEQEQEQE/yҾ?^׼_ }/yҾ?^׼_ 0<+kz$Y?M+"&7O^__ψkJ AYik B"kF#ҽ*T9~8Xy^GT5n:#S,5:Q5x$JEE" h0*U)V&Z2lwRBAQO ^UDv'2( rLltkV)#Z49RtJlV:c JT\+,u5kW!\k/jxtaqsU!p(Mӟ1+Ci+־8xtepؙqkkሦCf|z27N[_Za+>k Cl?Ald_((((((((((((((((>?sZ犴Qod#:Emi$$~uԟ? jODK)nLywnѡ̑x0siuBǚ1A*y<2-ٖYR8ʩ2:ciej`/K9ZBM͵ĂHZ'{.qOeoh ٥ya@Ug\dm;Z~%nZB1raVm 8OB7zfQJk(x"i 6wnqZ~ n]}ZQ o*<r'}kNt˴t)Wvru޴5}]ķ_MEk0gAH~ރhGƺ/,UCڞn6$8Y_K2KyɏO@G3w!䞠Uo k3ϧIIǨvK*c˦7nTzg7o xjGkdU$;,‚O]B}%`)|]yI᪕cZ;UyO'׿}QE8ҥ>'U󵿉/Sۥ +2z:=pA5C6Q%!H yY.PNp jj:U\_$:Νϸ#~ (%z>7ҍ.;MlfyS IvV/ugwn@4_ʺ_nt=bqNdZK>%eoٻ8g@j~+#4MGA嶎/è1**W0uwRPӯ-ඈiPIIGRm*jvY=OOX5Q'!تBmF;0IҶ_C,.HN2],N۶n0Cc||ҽDZu6.dX$i)2#oRQEQEQEQE/yҾ?^׼_ }/yҾ?^׼_ 0k Cl?A}( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (1OT=}I*Q@Q@Q@Vm)A ˸=FF:Eqx WWw:\8QokB G X󞔟"ޥ|븢U%e#7F ݣy/7G+ȿ70()9i"8ϱILd Yn (MEmD(1Fu*~k@bk''J9so?'\ǫJYɑ|dmV5bT~g3%%NZch_8 ?*?)?u iz:(HMfXCjђTrdnv( WNΰe h 8q/so??u izso?tخmBqJ,6,1lOZآ((((ٗ<_ԟV/<_ ~і}ڧYcJhwl۫ K?z "jYw T{OWNH\:Tࢍ(ޭ%fFf7,uFfRUȤxqKJƪf2U^!ޯ/J͘5i9y3Ѕ\j2dU9Zj2)ŞiuҪsEPG54<+=+q=Ы^j(xqA^UTaR q{UڭpHbQSYHk&I+U:EVRbsJYb؋ڞ#f.PGT*[HW0D0#T$Fr??52m^ӮF%}z閇 ~ ІoLE8! ~ / YW>ny$_+r((((((((((((((( {UO}R~ʠ kڭ{++[}:'X<9bp8/Qan4'#\* $Z֥Š?dUI" FTǒ<˓ωt#g}ޡRD`>\U|rn$bύ6B0U Fՠ!gVQ ;O\ǒ\BwxT#e wKpC8ۜG8W6#>-sA80.H0W`ߴI4o쎢E*bC.8gGjRD\ɝ-s$rzI'9;حs13iC`,jTHR"Έwx_[#K;X:FG6sI5a>DW-y`J{NPLT@38^QE!yJ\{K8o*_}`C^\ςW+Ix/"liCݷp݌ⅸ=:ʩåi7s/<[jcV H]AV=Ep?6`ӿ 6vOC2zsF? 7O6_Ö/ ӿ 6v*k=+Ub#IV ǀ[ txcbix3í }J$YƯve;x NT-_k_Y/ Xڬ1wn؞k =wNG8ϡ"bzU0I/Ǐí6U[v3G(ėJ?vznHs4ʶ;]CЌzsUa4EQgm0?tKe>v€+kU>?V/Qe>v€+kU>?V/Qe>v€+kU>?V/Qe>v€+kU>?V/Qe>v€+kU>?V/Qe>v€+kU>?V/Qe>v€+kU>?V/Qe>v€!TIR3\?5PGekac/yҾ5:(WhuTJ:L8 ڄy$iI^h5b6hjt5X25NTdjv6BIV#dz,jKU&XW!KECm/Jֶ8湛izVŤ+DjtpՓY֒txHa\Utk 3ڢX9[EM'|.F(Vzojxubsę{UF;_jmڸxp{T]X=UhrR,UlEN4bD~Ԣ?j PҔ;(YH9xGNԕ~kKq- ~ _۾18UϪ?5Za+z>m~^WxzƛXt$@:C\/]GF=/^,Q QEj9X+z?G/]GAch"5uYƣ} @?gd=/^,Q QEj9X+z?G/]GAch"5uYƣ} @?gd=/^,Q QEj9X+z?G/]GAch"5uYƣ} @?gd=/^,Q QEj9X+z?G/]GAch_F"#W'QxjBЉ !F9S D' v/Z (BQOv?ODJ{Rj=mݰ2˅v+Wj_6ͪ2_RP+[v0 m$GR_i-ūKtY'~BPI' =oP,4 .~e8Ƿ{7/~v)XHpO5VFjz:͵?0iQK8 I̽+bk/麦qi[/YLv7 $e5ҒWBz|@z=\i: <=yԠEi8.Bunvė4V*[B{Y|}5"v- fw73Y̲eh$+. Pku9ncmoS]b^$#BiS`q%wl͵֯ckn JgM'OJקG^K65_[۵=ʉt"g\^yko}k-[JdU ==EQ>!҆s5Kem+,͐Yywqz,J}Hj%a'ٍۿklŽdtH|M+JIz{ҒRo}n>_Ǿ7tۼק#tFTK>u8 Vuk[0{+Bimtu PH'=[z>=I˴<iz/ω.-?ωOGg+ȿ>'N-?ωOG{gUaqJQ_ٟ'sGg E#u=Vz_ٟ'sGg E#u=Vz_ٟ'sGg E#u=Vz_ٟ'sGg E#u=Vz_ٟ'sGg E#u=Vz_ٟ'sGg E#u=Vz_ٟ'sGg E#u=Vz_ٟ'sGg E#u=VzCwp$(QՉ8}MyGg E#֧-]i )|[y4~鯯%6sWɿSAՄW);H5:S^GrXVTu5*ԨjZRmDz5Y8 kwZֲt`nkRVR^9ZVͣZ'4XuVn=+*Vb}7fẒk\Ӂ]ҧnX\WUFrN⬌Q\>*P-!{B=E:YY !Mⱐk4+7@z{)5 ?@BSWq6B??_;|?ѭX{KǯxJFayF%ogg[F{{ogg[F{{_ZiI<؃ ԐsgtFiƧy{r<2M, a gҚ]qi:]:l`~l^ַ5 +>룾\֕z}^Մ}w+ "I4υ111Zo-ֺuiÁTHojܒ; _[15C+J̄}fcQ\b}7Emk2N  csUjj;ڭ؜tRKmZ²|pKqOl}:i[Ez9s$d4k8-^K\9\ H?W~Zo/_7xQON'r p$_Fχ? -7BS9ZtWq_ɝ}I*4}SI-w8 g k_ԟ{^.O bYVcv0  Mv34mR!Yp7>2H!A Ft^ \kkI&7mrUNs46Ѣ\ĭq ]jŢ:! c 8eZx~ OB]yaTc$I$=+>ƚROq"]KIE$66xZvj/o`d]Ai*$݀2{sB;DbEV3A6*J)+БҨiͦ& 2pO\z˴r.̺(m͸$$2ր:#g9NS i9or}Mq+g֘{}&+.lf}@67'$siEH."7Đe*˗Wq'i8=( (D:t-j\{0H扢68d`5@ XT'$-[q;PRQM_EbRu Lgf;IXFGnd_-?|%C*QEr|%C(/4G`sX>O'~ÏI3G$<{4EoyR(Vv iVqﴞ)Ӧ0UѴR=KRf_XT2z,LJg k W>?Ou?Q=cZOgM2[08aGSj_]j_ڶ?@h_ڶ?GE}P*E}Qc?QTvc?QTj_]j_ڶ?@jX- tn9Ơձ?l/)z媈|v *Nnۨ.oB"S(?jg,ɫT/yҾE *u2ԿJKC]O9;Uӳ!je5_'SR)VSVR`ij%3uՈ5UjxRXЀ֕tMh۷"Q97-[mZ7Jnh+4,OJ,Z>1Z`k+8bΖ :մz)wxӭstMNk.=e޼CK̥TjKr4U {wUa%(zS,n'֢IwRQ&Jfi#OL(GCl?AyǭC 543?k4?Wp-wg>0_aZWFܾs=ſGպiq,n$ u_D;IG - ?ƾy|??Ɵb>K|{|_I]a<G uG`/_yg - ?Ə@<[@}' uO60;I+j@r5Ϛoo4#k;5ܶ-M܀x=Zz|AXJ8$v5foo4#ku'*xs#k7)t|{|=0?ƾoٯXmȡc0RQG - ?ƾoi'i:XRm}RQ_y - ?Ə@<[@} Ζ7\g l|{|_G'hoi2R|Zr>G?x7?oo5;HO.?$i~EX2K>ſG{|xO_c׆G&XC4Sg#iLJ?5+WíѮѩ~ 4 69dx - ?Ɛ>Y?/#kOߺS#C~/5XJh?G+?t{tn;ߪPqkG3GiK ;ՕXs91]m}I*3*c6sʡj+bVtKw*FO#T [yjڳ[/d*~V&[%u|U9s)o `Aoo ;ugiQ[6" MTmS'?MDR~]0+x~'*p<  4^:KZY~v?4 V73_ekwޱ. X}/MT2u25(vz2~']Qr-^qf_jx|3*VmǯBWKU`5*,^_ bm{7|GCR5z3zy?84GJ +&{\uaWW?k;=דWIl y$`(#4S\ 4SS/OT=}I*dhٙ RpUO ?hyscHcom,RjWP,FS^w''O{9#~ฑFd+TM:g8!g@t$޼x^WK+=Yۅg `s4xSVYVK>ba䍙v;:w:\:`He=9!YXimZ G;GJ|rҠP_ܡ;ka\j˨,2J- Z2Yģ&6= G!&Xssլw~*QHnfS)kֵJ:5ťɩ_"E;I2€;+C⻉49+UKbwXҍ#p@?{Aj([_O:MKNRţ.Yx.^JoW=w/x! .kmkeM,y%Un {\(2JHV dc* #ުxJʗ Z&bHC;m^XW=Fϥyz,Z}z'g)y^Nö9UxAR}xG/oohww*gG2G !pq(\+|sSL՟HSM$싀O-JTpF8Ix^1K:Zmo-(YF4 z+"G TW`[y%i~ W ʹuNX',sQ;\AMkgr;I vӨ j'Us#H lH#ס/yҾ?^׼_ }/yҾ?^׼_ 005xoth9mQZk𞠶:a}=kQʷQ^_W+8N*P)zL2TKS 攉UD*0vqVg<^ք ҳaDk93NIYq5[4$VqQJeG2=x5z4h$2KY*?p'\di$2Y%JVLHY)JSĞ+$kPhz tb9@~'{ׄ>-쭼9k')\T:>} <TT.oXRs_a+>k Cl?A~F?a4.Pxm%82Hb}S iL]vj̅cpձ=wJy2HvH TW1xvc?K%Ccr52PC"PNH6Dm56v&gbc@pOQS kWFTƊρ?یwn;P-NA38$j $c, @cVx>[3}`@ g+<ҷ<i~X=N]ybx$_JmosnFћWBȻ12Y&nS7Ӻf̶ۤ2;wVlr-Ϙ-hFiBV9R=kGsxNW-ӫB-mCp3=)~ _X4MK֞kȠd(0I[g^[XZIu}qc/,Tt`}4O}sKִ`m´ѿX)m{f柺4> 'EQ ?h?:/*Zhs Fc? տ} /O!CFT}4OV7´ѿX磖?sVU4> 'EQ ?h?:/*Zhs FcZ![W4hG'DiޱG+M~z9h35o_wJDew֏̶2M!y 1}ZMJt ,m#F}kְxZgVY1A-ěϘxe,~J%ȉ, +S,n5Kr8S$N}?wβ}rkn;vPKi w?Əm;7*qMPKhw?44eC=.?kikWhӿ_[N_Mʇz\I/6 dr k>`"03[:w{ '9Ru"G8l9%'{kYG$,(L}~tVT1MkmKK,8g e@zlM$$q%+PAR2kuk_#Z~3 sa*F*0׮El6i3LAi,V o ,ݻN¿Q<0y~|8Dnc >jz>~j\Y?$"d(?j>mFp9$6C:Ƽ͔z1Ea,!; 3t:+>Bg©6=ּ2q׌_Z“}rǰQ^?C#$QC#$U^]މ`w?F/Hw?F/HvqK77W nO#a,@+<4bTa sg9MvE{3_ 3$ cԩ; R29U+cKXG "00`Z̳$&2 gՓy/B>NUӫ+(N<[  + :orſݢӫ+(N<[ + :orſݢӫ+(N<[ + :orſݭm;=%Jѵ…ƶS@/OVDnWYZ' nw_JI >"m>eK_1K9_Ӷk]9'mB;mCn3ЏZM ]`l2vӜc9ދuƲe_[yQ!nB}_4ig XckTD$c`Oc5PIye2's^c}ޛIl?{?BGnݝ Vt.|/ltymn4'&ٔ"Τ;d PlY#ĩ}֦6vyzTzΝesioq)qK:93wi8/aүm3 `_ W1cV]nH4kȍЈ-!LTdr0 T)9C{&zl俴K-2 :=#I3oi)IVErJqzMc{]W-ڒ]Ia] 61`DŽ{_Xk=*m&0 >uY]~>5iVC߽?f໏c*, #5TӶkU6)"PBa{ XmpЊn-C44K֞v$VHr۲0#&5t֞" `'~#4 ;OxE)ĐYJa VzX{l}uL\W:EX繺]u1 p۳+یtcMoöG}6[El JRF;sSKxĩk7K91[2vIba=QZ[jQE!Q@Q@Q@Q@Q@Q@Q@Q@Q@2Ǜ+ړJ{2Ǜ+ړJ{袊ql?8@R+Fʺ{WE}|{dǠ]L[eNJ{EDՈTIbu'WSjfd4ˑf:V5g,qըF!dY11̋hjM XCҼE5:5TFUʮtժEjO ^MhpmZ:̶(erO?V_fAo?'.myEGgv,rI9$eYO+U/E O␆+_Za+>k Cl?AQEYE ayFYId"^~Vrx>s_CB/nlKliHo-Jٸj^`hSöv{]vg8}q+xSï$r>JceK*/f82wU*C)NytOZY)bWF Ƭ#yXW:94=&[Yi|bkt(g;cO5__y-7{O2198\%N/f{*- ?JӛB[ZnpI#$<.6O | z;ɟ4W۫?$qp0:_4ORbZDI 0Yp}ʡ* Z[iR_̐[D2?E?s)AOR%Gvu4W- ŸQ ŸSs#h[?9i?9iϳiSY?gӦvV9mbRNIS{Vg,/ r?G,/ r?Gf=5$0:H&R:}kVc5@W3 ŸQ ŸQهsoX^‘!P)#[8O=1ǹCk Ks4pĽ^F 4;[+n#7 qEӣcԵ[Wp.7D9'`'մ.UQf+o[yOuOhGRSQjXS ۩89x!.~TR Ԣ((((f_o?|RZx_o?|RZx`yQ@Q@^4 &c5Vkɻz6ּeGE2顉-:,++kըp:ëU ek o!ʓF{ 3jQ(ZZқ<"Faf̚-TQV.+TQ^ ^mShSU@GDYk};}TiDYk7'I?nS= gh#9v;{g8 AC+~!OQ-d=Ğ'*Yk}T W\?d%o`MI(Kp\cd8V qWo ~*[u, ǖ%$5EbZk:lfKYG tcWgE\jNERe+g+O sTWgEW'Ug+O sTWgEާ?=?_q´5O]?Zxo vtQ3Ug+O sTWgEާ?=?_q´5O]Fg¤#ڕQGeOQj BW^»Y pOpj}OU2B,=olmo-̣ie"(sdhS 3O"ǒ@ԚdBǴ??=n&O*( {O[ dBǴ??=n&O*(CC(l>k[Na4B>?b=kj>?f֭ya$1$ʺX0϶@ 8ιNB58:}}%?걽.N447Onux R kw 8f2;#IQ<p!ukGga 2 P[;pH#f3׵^>)ϠٛˡA4w;.1In< Ox /5E x@pFMK7,4Lݠ.eC pCO o]3 (kaoK}J-1RQ&F_p</2imk)`a$343:ʫrvlH:FVEhU{S̨_8Q\x?L[]Ir-,7,{P-??o^cӡkynn9Ba$1EeTv ^irXnFqW"Gq>X }l {ռRA+>=ފ;ct+c6D\7 c>_y; wcopcxu*ˑyCA E*)163}h!|.#/VPHEX:o2[& 2opr3ӎƓW'Khm"WbŬDQIyY~B]rv(o5A5ˬo6ܨbH~]s:ifr#n *pu^HO,r.Y˸;@P9k60iP]mxhD10LJ23=W!(uHQӿ [pݘqiZCKU[U-c1;$mĞXjƟ+ e"!#ʞ}Oʼ_? ȝn:[Xqw-M?% Ww\Kx^UΣ,e)t)$C#dmA1|1.uP}>aǙzt8ҍ⩧_Yw^n 8`irOY iO#qԏCI ?(G_t7.d-AC`&sb98'яj%vJqGR\ĿQ((((((((((((((m;=Mki>3 <IRd_ݸ~>?A@n?l¬Q@n?l¬Q@n?l¬Q@n?l¬Q@n?l¬Q@n?l¬Q@n?l¬Q@n?l¬Q@n?l¬Q@n?l¬Q@n?l¬Q@n?RO4B0J*EbK{z>Ͻ=mS'Bf$*}V{xaHuFQʨ,-G~Bw?r\.P/iw|R[i,[ʖVnw?r\.PlEۏIovdʖ[y U2FGqrѾma0}/߇ >n?E˟p>s@9@/߇ >n?E˟p>s@9@/߇ |7 +U3D?2*.y}*{i#v>2Ў76sV%VW:ˈdw=ArMmm[i^IFEx8`0A'{AmJoArd-"1 P.1V`k-KD7_‡ȸR 䎇5ڏ>}?}[X:Zl[勠'1 $ `zV>YWL|@-ȅ<I%h[ #S^أQտ@a{mp-7Og<씈%;j9 rڏ>}?}[:+.+mDK }ζXPV)g\6s9O6tڄ lo"q 6#rqڏ>}?}[:wu}3CbuM2+h%mF3) '2X$N}AEik=Ď~ecTCzϫbGV&/ |Ie $m2HHcYMsg}w]+➧&'ivIQ%KU؇&9@Vʁ :US\̗QFk)cYI".8mnŃ^تZ[GĒDAd`˜(Cg|LEWtaīcL 9D%;y]o5x;Bs C>uJME!5dbŸe`Òx)u8E/ P=Wszţsf/О?:ӼA[_\* HAbw`p@"b{˵X'l)'z+m>$IӠ *8@ 4ù BJ4"G]a?9s/[BRfy߰`r9[e|Bi  r 86`E;8JwŬ x&@zBkG3gkon9dzMvzo{03/ 1x'?MZ?Σ;?4o1P;?4o1P;?4o1P;?4o1P;?4o1P;?4o1P;?4o1P;?4o1P;?4o1)<1F qRĒ݈¤w?v?M?4ݗk~eZߦ?4o1v]ůo*{EI'xdHD*Av $"'q[^h2\A{{%ՑeRѳ[Y_ ÷Cn멽/aٚ%sdd9Ƥ}nE% :ͿHc+r_o?cP2?-GO([g?¤#{?ym= oQ*J(?-GO([g?¤#{?ym= oQ*J(?-GO(C?ZKCRv%f[pF3͋~U <ؿ_^ϦEy-£FdS!EbOqA%йGe `<GtJ]nIAՁ\қE?UW&-QփҲZԧ5Z v*e؃pc=Hր7ؿ_͋~U O3y1I3L!DRq1v/S6]}ٱDYeLr\ h|K I·-yMכUDFH_qr9kiqGR\ĿQ((((((((I2$ly#}bZdlD`̲IaNH9F3eBډ2j7P] V8wt>P=x|S"=u!hZJTr*F\Tҵ[m*;mF;&K{.e0R[8?SEˬ+"?s4}~wiuDXR tRjVwtf@eq Z?SG!hWuaQ]gyXC~C`8?+$?Mdq_e畇?ZzUHn yҾIQ/Cm #?E_o?cVF i]Nq>?SL +Ws4}~TV!h$?MeQZds4Ej~CUH>?S@TV!h$?MeQZds4Ej~CUH>?S@Uwf75^B3\=z?!h$?Mg] **¨?SG!h`VB>@lXO'z>?SSADP Ri9ypsQ?*6AJ`[GhX +{hmEomQ`0hQz , z 6AEAF(4V(==GqB͛2Ҫ;?_{z 6AEmhvŷhm;?_z%ST!st?$jN>Q`9mN_BϚen}?1YAW5Q`8/Gg|[?mk=Gp_AW4;?_{z 6AE4(XIyuy)Cϰ";vN ڌj)@0?fotoxx-18.01.1/f.mashup.cc0000644000175000017500000061042013222767271013715 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit functions - Mashup and Montage functions m_mashup combine images and text into custom layout m_montage combine images into a matrix of images *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /******************************************************************************** MASHUP Define a layout/background image. Add images, text, and lines as overlays on top of layout. Overlays have adjustable position, angle, size, transparency. *********************************************************************************/ namespace mashup { char projectname[100]; // project name char projectfile[200]; // /.../projectname char layoutfile[XFCC]; // background/layout image file char tranfile[220]; // /.../projectname_trandata char warpfile[220]; // /.../projectname_warpdata int Lww, Lhh; // layout image size int flatRGB[3]; // flat layout RGB values (no file) PXB *Lpxb; // layout image PXB int Nimage; // overlay image count int Ntext; // overlay text count int Nline; // overlay line count int updxlo, updxhi, updylo, updyhi; // update region in layout image float PRscale; // project scale for editing int Fupdall; // flag, update entire layout int Fupdatebusy; // window update busy cchar *focus; // "image" "text" "line" "" int focusii; // image[ii] text[ii] line[ii] or -1 int Fnextimage = 0; // flag, image selected with [next] int Mradius = 50; // paint transparency mouse radius int Mpowcen = 10; // paint transparency mouse center power int Mpowedge = 0; // paint transparency mouse edge power int Mgradual = 1; // paint transparency gradual or instant zdialog *zdimage; // image edit dialog active zdialog *zdtext; // text edit dialog active zdialog *zdaddtext; // add text dialog active zdialog *zdline; // line edit dialog active zdialog *zdaddline; // add line dialog active zdialog *zdtransp; // paint transparencies dialog active zdialog *zdwarp; // warp image dialog active struct image_t { // overlay image data char *file; // filespec PXB *pxb1; // image PXB, 1x size PXB *pxb2; // image PXB, scaled size float scale; // pxb2 scale, 0.0 to 1.0 = 1x float theta; // angle, -PI to +PI radians float sinT, cosT; // trig values for theta int ww1, hh1; // image size at 1x scale int ww2, hh2; // image size at curr. scale int px0, py0; // image position in layout image int pxlo, pxhi, pylo, pyhi; // image extent in layout image space int Lmarg, Rmarg, Tmarg, Bmarg; // left/right/top/bottom hard margins int Lblend, Rblend, Tblend, Bblend; // left/right/top/bottom blended margins float Btransp; // base image transparency 0...1 int vtrancc; // var. transparency map cc uint8 *vtranmap; // var. transparency map int Nwarp; // warpmem count float warpmem[200][5]; // warp undo memory, last 200 warps int warpcc; // warpx/warpy data length float *warpx, *warpy; // aggregate pixel warps }; struct text_t { // overlay text data textattr_t attr; // text, font, angle, outline ... int ww, hh; // image dimensions in layout image int px0, py0; // image position in layout image int pxlo, pxhi, pylo, pyhi; // extent of text image in layout image }; struct line_t { // overlay line/arrow data lineattr_t attr; // attributes (length, width, arrowheads) int ww, hh; // image dimensions in layout image int px0, py0; // image position in layout image int pxlo, pxhi, pylo, pyhi; // extent of line image in layout image }; #define maxmash 50 // max. images, text, lines image_t image[maxmash]; text_t text[maxmash]; line_t line[maxmash]; void project(); int project_dialog_event(zdialog *zd, cchar *event); int project_open(); void project_save(); void project_rescale(float scale); void image_edit(); int image_dialog_event(zdialog *zd, cchar *event); void image_rescale(int ii, float scale2); void paintransp_dialog(); int paintransp_dialog_event(zdialog *zd, cchar *event); void paintransp_mousefunc(); void warp_dialog(); int warp_dialog_event(zdialog *zd, cchar *event); void warp_mousefunc(void); void warp_warpfunc(); void * warp_warpfunc_wthread(void *arg); void transparent_margins(); void add_image(); void remove_image(int ii); void flashimage(); void text_edit(); int text_dialog_event(zdialog *zd, cchar *event); void add_text(); void remove_text(int ii); void flashtext(); void line_edit(); int line_dialog_event(zdialog *zd, cchar *event); void add_line(); void remove_line(int ii); void flashline(); void select(cchar *which, int ii); void setlayoutupdatearea(); void mousefunc_layout(); void KBfunc(int key); void Lupdate(); void * Lupdate_thread(void *); void * Lupdate_wthread(void *arg); } /********************************************************************************/ // menu function void m_mashup(GtkWidget *, cchar *) { using namespace mashup; zdialog *zd; cchar *title = ZTX("Mashup layout and background image"); int zstat, nn, err, ww, hh; char color[20], fname[100], *file, *pp; cchar *choice = 0, *ppc; int px, py; uint8 *pix; F1_help_topic = "mashup"; if (checkpend("all")) return; Fblock = 1; *projectname = *layoutfile = *projectfile = *tranfile = *warpfile = 0; // clear project data zdimage = zdtransp = zdwarp = zdtext = zdline = 0; Nimage = Ntext = Nline = 0; for (nn = 0; nn < maxmash; nn++) { memset(&image[nn],0,sizeof(image_t)); memset(&text[nn],0,sizeof(text_t)); memset(&line[nn],0,sizeof(line_t)); } select("",-1); // nothing selected /*** _________________________________________ | Mashup layout and background image | | | | (o) choose an image file | | (o) use current image file | | (o) specify layout size and color | | (o) open a Mashup project file | | | | [Proceed] [Cancel] | |_________________________________________| ***/ zd = zdialog_new("Mashup",Mwin,Bproceed,Bcancel,null); zdialog_add_widget(zd,"label","labtitle","dialog",title,"space=3"); zdialog_add_widget(zd,"hbox","hbopt","dialog",0,"space=3"); zdialog_add_widget(zd,"label","space","hbopt",0,"space=5"); zdialog_add_widget(zd,"vbox","vbopt","hbopt"); zdialog_add_widget(zd,"radio","choosefile","vbopt",ZTX("choose an image file")); zdialog_add_widget(zd,"radio","usecurrent","vbopt",ZTX("use current image file")); zdialog_add_widget(zd,"radio","makelayout","vbopt",ZTX("specify layout size and color")); zdialog_add_widget(zd,"radio","openproject","vbopt",ZTX("open a Mashup project file")); zdialog_stuff(zd,"choosefile",1); zdialog_run(zd,0,"save"); zstat = zdialog_wait(zd); if (zstat != 1) { // canceled zdialog_free(zd); Fblock = 0; return; } zdialog_fetch(zd,"choosefile",nn); if (nn) choice = "choosefile"; zdialog_fetch(zd,"usecurrent",nn); if (nn) choice = "usecurrent"; zdialog_fetch(zd,"makelayout",nn); if (nn) choice = "makelayout"; zdialog_fetch(zd,"openproject",nn); if (nn) choice = "openproject"; zdialog_free(zd); if (strmatch(choice,"choosefile")) // open new curr. file >> layout { file = gallery_select1(curr_file); // 17.08 if (! file) goto try_again; Lpxb = PXB_load(file,1); // layout image if (! Lpxb) goto try_again; Lww = Lpxb->ww; // layout size Lhh = Lpxb->hh; strncpy0(layoutfile,file,XFCC); pp = strrchr(file,'/'); strncpy0(projectname,pp+1,100); // project name from file name pp = strrchr(projectname,'.'); if (strlen(pp) < 6) *pp = 0; // remove .ext project(); // start project return; } if (strmatch(choice,"usecurrent")) { // curr. file >> layout if (! curr_file) { zmessageACK(Mwin,ZTX("no current file")); goto try_again; } file = zstrdup(curr_file); Lpxb = PXB_load(file,1); // layout image if (! Lpxb) goto try_again; Lww = Lpxb->ww; // layout size Lhh = Lpxb->hh; strncpy0(layoutfile,file,XFCC); pp = strrchr(file,'/'); strncpy0(projectname,pp+1,100); // project name from file name pp = strrchr(projectname,'.'); if (strlen(pp) < 6) *pp = 0; // remove .ext project(); // start project return; } if (strmatch(choice,"makelayout")) // create flat layout image { /*** _______________________________________________ | Make Layout Image | | | | project name [________________________] | | width [____] height [____] (pixels) | | color [____] | | [done] [cancel] | |_______________________________________________| ***/ zd = zdialog_new(ZTX("Make Layout Image"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labf","hbf",ZTX("project name"),"space=3"); zdialog_add_widget(zd,"zentry","file","hbf",0,"space=3|expand"); zdialog_add_widget(zd,"hbox","hbz","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labw","hbz",Bwidth,"space=5"); zdialog_add_widget(zd,"zspin","width","hbz","1000|10000|1|2000"); // limit 10K x 6K = 60 megapixel PXB zdialog_add_widget(zd,"label","space","hbz",0,"space=5"); // (x 12 = 720 MB for PXM image) zdialog_add_widget(zd,"label","labh","hbz",Bheight,"space=5"); zdialog_add_widget(zd,"zspin","height","hbz","600|6000|1|1200"); zdialog_add_widget(zd,"label","labp","hbz","(pixels)","space=3"); zdialog_add_widget(zd,"hbox","hbc","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labc","hbc",Bcolor,"space=5"); zdialog_add_widget(zd,"colorbutt","color","hbc","200|200|200"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,null,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) { // cancel zdialog_free(zd); goto try_again; } zdialog_fetch(zd,"file",fname,100); // get new project name strTrim2(fname); if (*fname <= ' ') { zmessageACK(Mwin,ZTX("supply a project name")); zdialog_free(zd); goto try_again; } strncpy0(projectname,fname,100); zdialog_fetch(zd,"width",ww); // get layout dimensions zdialog_fetch(zd,"height",hh); flatRGB[0] = flatRGB[1] = flatRGB[2] = 255; zdialog_fetch(zd,"color",color,19); // get flat layout color ppc = strField(color,"|",1); if (ppc) flatRGB[0] = atoi(ppc); ppc = strField(color,"|",2); if (ppc) flatRGB[1] = atoi(ppc); ppc = strField(color,"|",3); if (ppc) flatRGB[2] = atoi(ppc); zdialog_free(zd); Lpxb = PXB_make(ww,hh,0); // layout image if (! Lpxb) return; Lww = Lpxb->ww; // layout size Lhh = Lpxb->hh; *layoutfile = 0; // no layout file (flat layout) for (py = 0; py < Lhh; py++) // fill layout with flat color for (px = 0; px < Lww; px++) { pix = PXBpix(Lpxb,px,py); pix[0] = flatRGB[0]; pix[1] = flatRGB[1]; pix[2] = flatRGB[2]; } project(); // start project return; } if (strmatch(choice,"openproject")) { // project file >> layout & contents err = project_open(); if (err) goto try_again; project(); // start project return; } try_again: Fblock = 0; m_mashup(0,0); return; } // overall steering dialog - edit dialogs return to this dialog void mashup::project() { using namespace mashup; zdialog *zd; free_resources(); // no E1/E3 etc. Fmashup = 1; // flag for KBpress() func Fpxb = PXB_copy(Lpxb); // initz. window update image m_viewmode(0,"F"); // force file view PRscale = 1.0; select("",-1); // nothing selected Fupdall = 1; Lupdate(); // update layout composite image /*** ____________________________________________ | Mashup | | | | [Edit Images] add or edit images | | [Edit Text] add or edit text | | [Edit Line] add or edit lines/arrows | | [Rescale] change project scale | | [Done] project complete | | [Cancel] cancel project | |____________________________________________| ***/ zd = zdialog_new("Mashup",Mwin,null); zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=3|homog"); zdialog_add_widget(zd,"button","editimage","vb1",ZTX("Edit Images"),"space=3"); zdialog_add_widget(zd,"button","edittext","vb1",ZTX("Edit Text"),"space=3"); zdialog_add_widget(zd,"button","editline","vb1",ZTX("Edit Line"),"space=3"); zdialog_add_widget(zd,"button","rescale","vb1",ZTX("Rescale"),"space=3"); zdialog_add_widget(zd,"button","done","vb1",Bdone,"space=3"); zdialog_add_widget(zd,"button","cancel","vb1",Bcancel,"space=3"); zdialog_add_widget(zd,"hbox","hbedim","vb2"); zdialog_add_widget(zd,"label","labedim","hbedim",ZTX("add or edit images"),"space=3"); zdialog_add_widget(zd,"hbox","hbedtx","vb2"); zdialog_add_widget(zd,"label","labedtx","hbedtx",ZTX("add or edit text"),"space=3"); zdialog_add_widget(zd,"hbox","hbedln","vb2"); zdialog_add_widget(zd,"label","labedln","hbedln",ZTX("add or edit lines/arrows"),"space=3"); zdialog_add_widget(zd,"hbox","hbres","vb2"); zdialog_add_widget(zd,"label","labscale","hbres",ZTX("change project scale"),"space=3"); zdialog_add_widget(zd,"hbox","hbdone","vb2"); zdialog_add_widget(zd,"label","labdone","hbdone",ZTX("project complete"),"space=3"); zdialog_add_widget(zd,"hbox","hbcan","vb2"); zdialog_add_widget(zd,"label","labcancel","hbcan",ZTX("cancel project"),"space=3"); zdialog_run(zd,project_dialog_event,"save"); // start dialog takeMouse(mousefunc_layout,0); // capture mouse click/drag return; } // project dialog event and completion function int mashup::project_dialog_event(zdialog *zd, cchar *event) { using namespace mashup; int ii, cc, yn; GError *gerror = 0; char *file = 0, *file2, *pp; cchar *rescalemess = ZTX("rescale project"); if (zd->zstat) { // [x] don't accept 18.01 zdialog_free(zd); freeMouse(); project(); return 0; } if (strmatch(event,"focus")) { takeMouse(mousefunc_layout,0); return 1; } if (strmatch(event,"editimage")) { zdialog_show(zd,0); image_edit(); // start edit images dialog zdialog_show(zd,1); return 1; } if (strmatch(event,"edittext")) { zdialog_show(zd,0); text_edit(); // start edit text dialog zdialog_show(zd,1); return 1; } if (strmatch(event,"editline")) { zdialog_show(zd,0); line_edit(); // start edit line dialog zdialog_show(zd,1); return 1; } if (strmatch(event,"rescale")) { // rescale project bigger, or reset ii = zdialog_choose(Mwin,rescalemess,"2x","3x","4x",Breset,null); if (ii == 1 && PRscale == 1.0) project_rescale(2.0); // 1x -> 2x if (ii == 2 && PRscale == 1.0) project_rescale(3.0); // 1x -> 3x if (ii == 3 && PRscale == 1.0) project_rescale(4.0); // 1x -> 4x if (ii == 4 && PRscale > 1.0) project_rescale(1.0); // [reset] back to 1x return 1; } if (strmatch(event,"done")) // project done { zdialog_free(zd); // kill dialog freeMouse(); if (curr_dirk) { file2 = zstrdup(curr_dirk,108); cc = strlen(file2); strncatv(file2,cc+108,"/",projectname,".png",null); // curr_dirk/projectname.png } else { file2 = zstrdup(projectname,6); // projectname.png strcat(file2,".png"); } file = zgetfile(ZTX("save Mashup output file"),MWIN,"save",file2); zfree(file2); if (file) { pp = strrchr(file,'.'); // force .png extension if (! pp || strlen(pp) < 4) { pp = zstrdup(file,6); zfree(file); file = pp; pp = strrchr(file,'.'); if (! pp || strlen(pp) > 5) pp = file + strlen(file); } strcpy(pp,".png"); ii = gdk_pixbuf_save(Fpxb->pixbuf,file,"png",&gerror,"compression","1",null); if (! ii) { zmessageACK(Mwin,gerror->message); zfree(file); file = 0; } } yn = zmessageYN(Mwin,ZTX("save Mashup project file?")); // offer to save project file if (yn) project_save(); event = "cleanup"; } if (strmatch(event,"cancel")) // project canceled { zdialog_free(zd); // kill dialog freeMouse(); if (*projectfile) { yn = zmessageYN(Mwin,ZTX("delete Mashup project file?")); // offer to delete project file if (yn) { remove(projectfile); if (*tranfile) remove(tranfile); if (*warpfile) remove(warpfile); } } event = "cleanup"; } if (strmatch(event,"cleanup")) { for (ii = Nimage-1; ii >= 0; ii--) // free memory remove_image(ii); for (ii = Ntext-1; ii >= 0; ii--) remove_text(ii); for (ii = Nline-1; ii >= 0; ii--) remove_line(ii); PXB_free(Lpxb); Lpxb = 0; Fmashup = 0; Fblock = 0; if (file) { // open finished mashup f_open(file,0,0,1); zfree(file); } else free_resources(0); // leave a blank window } return 1; } /********************************************************************************/ // open and restore a saved mashup project // return 0 if success, +N if error int mashup::project_open() { using namespace mashup; PXB *pxbtemp; int ii, nn, err; int ww1, hh1; int px, py, kk; int nfid = -1, cc, wcc, vcc; char *pp, *pp2; char buff[XFCC]; textattr_t *txattr = 0; lineattr_t *lnattr = 0; FILE *fid; STATB sbuff; uint8 *pix; pp = zgetfile(ZTX("Open Project"),MWIN,"file",mashup_dirk); // get project file from user if (! pp) return 1; strncpy0(projectfile,pp,200); // project file strncpy0(tranfile,pp,200); strcat(tranfile,"_trandata"); // corresp. transparencies file strncpy0(warpfile,pp,200); strcat(warpfile,"_warpdata"); // corresp. warp data file zfree(pp); pp = strrchr(projectfile,'/'); // project name from file name strncpy0(projectname,pp+1,100); // read project file and validate all data fid = fopen(projectfile,"r"); // open project file if (! fid) goto failure; pp = fgets_trim(buff,XFCC,fid); // read layout record if (! pp) { printz("no layout \n"); goto badproject; } if (strmatchN(pp,"layout:",7)) // layout: /.../filename.jpg { pp += 7; while (*pp == ' ') pp++; strncpy0(layoutfile,pp,XFCC); err = stat(layoutfile,&sbuff); if (err) { zmessageACK(Mwin,ZTX("layout image file missing: \n %s"),layoutfile); printz("no layout image \n"); goto badproject; } Lpxb = PXB_load(layoutfile,1); // layout image if (! Lpxb) { printz("PXB_load() layout file \n"); goto badproject; } Lww = Lpxb->ww; // layout size Lhh = Lpxb->hh; } else if (strmatchN(pp,"flatlayout:",11)) // flatlayout: ww hh R|G|B { nn = sscanf(buff,"flatlayout: %d %d RGB: %d/%d/%d \n", &ww1,&hh1,&flatRGB[0],&flatRGB[1],&flatRGB[2]); if (nn != 5) { printz("flatlayout rec. \n"); goto badproject; } *layoutfile = 0; // flat layout, no layout file Lpxb = PXB_make(ww1,hh1,0); // layout image if (! Lpxb) { printz("PXB_make() \n"); goto badproject; } Lww = Lpxb->ww; // layout size Lhh = Lpxb->hh; for (py = 0; py < Lhh; py++) // fill layout with flat color for (px = 0; px < Lww; px++) { pix = PXBpix(Lpxb,px,py); pix[0] = flatRGB[0]; pix[1] = flatRGB[1]; pix[2] = flatRGB[2]; } } else { printz("unknown rec. \n"); goto badproject; } // read any number of overlay image records while (true) { pp = fgets_trim(buff,XFCC,fid); if (! pp) break; if (strmatchN(pp,"end",3)) break; if (strmatchN(pp,"image:",6)) // image: /.../filename.jpg { ii = Nimage++; cc = sizeof(image_t); // clear image data memset(&image[ii],0,cc); pp += 6; while (*pp == ' ') pp++; image[ii].file = zstrdup(pp); // overlay image file err = stat(image[ii].file,&sbuff); if (err) { zmessageACK(Mwin,ZTX("overlay image file missing: \n %s"),image[ii].file); goto cleanupErr; } pxbtemp = PXB_load(image[ii].file,1); // make overlay image if (! pxbtemp) goto cleanupErr; PXB_addalpha(pxbtemp); // add alpha channel if missing image[ii].pxb1 = pxbtemp; image[ii].ww1 = pxbtemp->ww; image[ii].hh1 = pxbtemp->hh; continue; } ii = Nimage - 1; // current image if (strmatchN(pp,"position:",9)) { // position: xxx yyy nn = sscanf(pp,"position: %d %d",&image[ii].px0,&image[ii].py0); if (nn != 2) { printz("position rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"scale:",6)) { // scale: n.nnn nn = sscanf(pp,"scale: %f",&image[ii].scale); if (nn != 1) { printz("scale rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"theta:",6)) { // theta: n.nnn nn = sscanf(pp,"theta: %f",&image[ii].theta); if (nn != 1) { printz("theta rec. \n"); goto badproject; } image[ii].sinT = sin(image[ii].theta); image[ii].cosT = cos(image[ii].theta); continue; } if (strmatchN(pp,"Btransp:",8)) { // Btransp: 0.nnn nn = sscanf(pp,"Btransp: %f",&image[ii].Btransp); if (nn != 1) { printz("Btransp rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Lmarg:",6)) { // Lmarg: nnn nn = sscanf(pp,"Lmarg: %d",&image[ii].Lmarg); if (nn != 1) { printz("Lmarg rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Rmarg:",6)) { // Rmarg: nnn nn = sscanf(pp,"Rmarg: %d",&image[ii].Rmarg); if (nn != 1) { printz("Rmarg rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Tmarg:",6)) { // Tmarg: nnn nn = sscanf(pp,"Tmarg: %d",&image[ii].Tmarg); if (nn != 1) { printz("Tmarg rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Bmarg:",6)) { // Bmarg: nnn nn = sscanf(pp,"Bmarg: %d",&image[ii].Bmarg); if (nn != 1) { printz("Bmarg rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Lblend:",7)) { // Lblend: nnn nn = sscanf(pp,"Lblend: %d",&image[ii].Lblend); if (nn != 1) { printz("Lblend rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Rblend:",7)) { // Rblend: nnn nn = sscanf(pp,"Rblend: %d",&image[ii].Rblend); if (nn != 1) { printz("Rblend rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Tblend:",7)) { // Tblend: nnn nn = sscanf(pp,"Tblend: %d",&image[ii].Tblend); if (nn != 1) { printz("Tblend rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Bblend:",7)) { // Bblend: nnn nn = sscanf(pp,"Bblend: %d",&image[ii].Bblend); if (nn != 1) { printz("Bblend rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"Nwarp:",6)) { // Nwarp: nn nn = sscanf(pp,"Nwarp: %d",&image[ii].Nwarp); if (nn != 1) { printz("Nwarp rec. \n"); goto badproject; } if (image[ii].Nwarp > 200) { printz("Nwarp rec. \n"); goto badproject; } for (kk = 0; kk < image[ii].Nwarp; kk++) { pp = fgets_trim(buff,XFCC,fid); // n.nnn n.nnn n.nnn n.nnn n.nnn if (! pp) break; // (loop for Nwarp records) nn = sscanf(pp,"%f %f %f %f %f",&image[ii].warpmem[kk][0], &image[ii].warpmem[kk][1],&image[ii].warpmem[kk][2], &image[ii].warpmem[kk][3],&image[ii].warpmem[kk][4]); if (nn != 5) { printz("warpmem rec. \n"); goto badproject; } } } ww1 = image[ii].ww1; hh1 = image[ii].hh1; if (strmatchN(pp,"vtrancc:",8)) { // vtrancc: nnnn nn = sscanf(pp,"vtrancc: %d",&vcc); if (nn != 1) { printz("vtrancc rec. \n"); goto badproject; } if (vcc && vcc != ww1 * hh1) { printz("vtrancc rec. \n"); goto badproject; } image[ii].vtrancc = vcc; continue; } if (strmatchN(pp,"warpcc:",7)) { // warpcc: nnnn nn = sscanf(pp,"warpcc: %d",&wcc); if (nn != 1) { printz("warpcc rec. \n"); goto badproject; } if (wcc && wcc != (int) (ww1 * hh1 * sizeof(float))) { printz("warpcc rec. \n"); goto badproject; } image[ii].warpcc = wcc; continue; } } // read any number of text records while (true) { pp = fgets_trim(buff,XFCC,fid); if (! pp) break; if (strmatchN(pp,"end",3)) break; if (strmatchN(pp,"text:",5)) // text: this is my text ... { ii = Ntext++; txattr = &text[ii].attr; pp += 5; while (*pp == ' ') pp++; repl_Nstrs(pp,txattr->text,"\\n","\n",null); // replace "\n" with newline continue; } ii = Ntext - 1; // current text if (strmatchN(pp,"position:",9)) { // position: xx yy nn = sscanf(pp,"position: %d %d",&text[ii].px0,&text[ii].py0); if (nn != 2) { printz("position rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"font:",5)) { // font: free sans size: nn pp += 5; while (*pp == ' ') pp++; pp2 = strstr(pp,"size:"); if (! pp2) { printz("font rec. \n"); goto badproject; } *pp2 = 0; pp2 += 5; strncpy0(txattr->font,pp,80); txattr->size = atoi(pp2); if (txattr->size < 8) { printz("font rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"attributes:",11)) { // attributes txattr = &text[ii].attr; nn = sscanf(pp,"attributes: %f %s %s %s %s %d %d %d %d %d %d %d", &txattr->angle, txattr->color[0], txattr->color[1], txattr->color[2], txattr->color[3], &txattr->transp[0], &txattr->transp[1], &txattr->transp[2], &txattr->transp[3], &txattr->towidth, &txattr->shwidth, &txattr->shangle); if (nn != 12) { printz("attributes rec. \n"); goto badproject; } } } // read any number of line/arrow records while (true) { pp = fgets_trim(buff,XFCC,fid); if (! pp) break; if (strmatchN(pp,"end",3)) break; if (strmatchN(pp,"line:",5)) { // line: ii = Nline++; continue; } ii = Nline - 1; // current line if (strmatchN(pp,"position:",9)) { // position: xx yy nn = sscanf(pp,"position: %d %d",&line[ii].px0,&line[ii].py0); if (nn != 2) { printz("position rec. \n"); goto badproject; } continue; } if (strmatchN(pp,"attributes:",11)) { // attributes: lnattr = &line[ii].attr; nn = sscanf(pp,"attributes: %d %d %d %d %f %s %s %s %s %d %d %d %d %d %d %d", &lnattr->length, &lnattr->width, &lnattr->larrow, &lnattr->rarrow, &lnattr->angle, lnattr->color[0], lnattr->color[1], lnattr->color[2], lnattr->color[3], &lnattr->transp[0], &lnattr->transp[1], &lnattr->transp[2], &lnattr->transp[3], &lnattr->towidth, &lnattr->shwidth, &lnattr->shangle); if (nn != 16) { printz("attributes rec. \n"); goto badproject; } } } fclose(fid); fid = 0; // read binary transparency file if any for (vcc = ii = 0; ii < Nimage; ii++) // sum transparency data vcc += image[ii].vtrancc; if (vcc) { nfid = open(tranfile,O_RDONLY); if (nfid < 0) goto failure; for (ii = 0; ii < Nimage; ii++) // read transparency data { vcc = image[ii].vtrancc; if (vcc) { image[ii].vtranmap = (uint8 *) zmalloc(vcc); cc = read(nfid,image[ii].vtranmap,vcc); if (cc != vcc) { printz("vtranmap data \n"); goto badproject; } } else image[ii].vtranmap = 0; } close(nfid); nfid = -1; } // read binary warp data file if any for (wcc = ii = 0; ii < Nimage; ii++) // sum warp data wcc += image[ii].warpcc; if (wcc) { nfid = open(warpfile,O_RDONLY); if (nfid < 0) goto failure; for (ii = 0; ii < Nimage; ii++) // read warp data { wcc = image[ii].warpcc; if (wcc) { image[ii].warpx = (float *) zmalloc(wcc); cc = read(nfid,image[ii].warpx,wcc); if (cc != wcc) { printz("warpx data \n"); goto badproject; } image[ii].warpy = (float *) zmalloc(wcc); cc = read(nfid,image[ii].warpy,wcc); if (cc != wcc) { printz("warpy data \n"); goto badproject; } } else image[ii].warpx = image[ii].warpy = 0; } close(nfid); nfid = -1; } // generate overlay images for (ii = 0; ii < Nimage; ii++) { image_rescale(ii,image[ii].scale); // scaled and warped image select("image",ii); setlayoutupdatearea(); // set layout update area } // generate text images for (ii = 0; ii < Ntext; ii++) { gentext(&text[ii].attr); // make image from text and attributes text[ii].ww = text[ii].attr.pxb_text->ww; // text image size in layout text[ii].hh = text[ii].attr.pxb_text->hh; select("text",ii); setlayoutupdatearea(); // set layout update area } // generate line/arrow images for (ii = 0; ii < Nline; ii++) // load overlay line/arrow images { genline(&line[ii].attr); // make image from line attributes line[ii].ww = line[ii].attr.pxb_line->ww; // line image size in layout line[ii].hh = line[ii].attr.pxb_line->hh; select("line",ii); setlayoutupdatearea(); // set layout update area } m_viewmode(0,"F"); // 17.08 return 0; // success return failure: if (fid) fclose(fid); if (nfid > -1) close(nfid); zmessageACK(Mwin,"%s \n %s \n %s \n %s \n", projectfile,tranfile,warpfile,strerror(errno)); goto cleanupErr; badproject: // project file had a problem if (fid) fclose(fid); if (nfid > -1) close(nfid); zmessageACK(Mwin,ZTX("project file is defective")); cleanupErr: if (Lpxb) PXB_free(Lpxb); Lpxb = 0; for (ii = Nimage-1; ii >= 0; ii--) // free image memory remove_image(ii); for (ii = Ntext-1; ii >= 0; ii--) // free text memory remove_text(ii); for (ii = Nline-1; ii >= 0; ii--) // free line/arrow memory remove_line(ii); return 1; // failure return } // save a mashup project so that it can be edited again later void mashup::project_save() { using namespace mashup; textattr_t txattr; lineattr_t lnattr; char *pp, text2[1200]; FILE *fid; int nfid, ii, kk, cc, vcc, wcc; if (PRscale > 1) project_rescale(1.0); // must save at 1x only snprintf(projectfile,200,"%s/%s",mashup_dirk,projectname); pp = zgetfile(ZTX("Save Project"),MWIN,"save",projectfile); // get project name from user if (! pp) return; strncpy0(projectfile,pp,200); // project file strncpy0(tranfile,pp,200); strcat(tranfile,"_trandata"); // corresp. transparency data file strncpy0(warpfile,pp,200); strcat(warpfile,"_warpdata"); // corresp. warp data file zfree(pp); fid = fopen(projectfile,"w"); if (! fid) goto failure; if (*layoutfile) fprintf(fid,"layout: %s\n",layoutfile); // write layout file else fprintf(fid,"flatlayout: %d %d RGB: %d/%d/%d \n", // write flat layout attributes Lww,Lhh,flatRGB[0],flatRGB[1],flatRGB[2]); for (ii = 0; ii < Nimage; ii++) // write image data { fprintf(fid,"image: %s\n",image[ii].file); fprintf(fid,"position: %d %d \n", image[ii].px0, image[ii].py0); fprintf(fid,"scale: %.4f \n",image[ii].scale); fprintf(fid,"theta: %.4f \n",image[ii].theta); fprintf(fid,"Btransp: %.4f \n",image[ii].Btransp); fprintf(fid,"Lmarg: %d \n",image[ii].Lmarg); fprintf(fid,"Rmarg: %d \n",image[ii].Rmarg); fprintf(fid,"Tmarg: %d \n",image[ii].Tmarg); fprintf(fid,"Bmarg: %d \n",image[ii].Bmarg); fprintf(fid,"Lblend: %d \n",image[ii].Lblend); fprintf(fid,"Rblend: %d \n",image[ii].Rblend); fprintf(fid,"Tblend: %d \n",image[ii].Tblend); fprintf(fid,"Bblend: %d \n",image[ii].Bblend); fprintf(fid,"Nwarp: %d \n",image[ii].Nwarp); for (kk = 0; kk < image[ii].Nwarp; kk++) fprintf(fid,"%f %f %f %f %f \n", image[ii].warpmem[kk][0],image[ii].warpmem[kk][1], image[ii].warpmem[kk][2],image[ii].warpmem[kk][3],image[ii].warpmem[kk][4]); fprintf(fid,"vtrancc: %d \n",image[ii].vtrancc); fprintf(fid,"warpcc: %d \n",image[ii].warpcc); } fprintf(fid,"end\n"); for (ii = 0; ii < Ntext; ii++) // write text data { repl_Nstrs(text[ii].attr.text, text2,"\n","\\n",null); fprintf(fid,"text: %s\n",text2); fprintf(fid,"position: %d %d \n", text[ii].px0, text[ii].py0); txattr = text[ii].attr; fprintf(fid,"font: %s size: %d \n", txattr.font, txattr.size); fprintf(fid,"attributes: %.4f %s %s %s %s %d %d %d %d %d %d %d \n", txattr.angle, txattr.color[0], txattr.color[1], txattr.color[2], txattr.color[3], txattr.transp[0], txattr.transp[1], txattr.transp[2], txattr.transp[3], txattr.towidth, txattr.shwidth, txattr.shangle); } fprintf(fid,"end\n"); for (ii = 0; ii < Nline; ii++) // write line/arrow data { fprintf(fid,"line:\n"); fprintf(fid,"position: %d %d \n", line[ii].px0, line[ii].py0); lnattr = line[ii].attr; fprintf(fid,"attributes: %d %d %d %d %.4f %s %s %s %s %d %d %d %d %d %d %d \n", lnattr.length, lnattr.width, lnattr.larrow, lnattr.rarrow, lnattr.angle, lnattr.color[0], lnattr.color[1], lnattr.color[2], lnattr.color[3], lnattr.transp[0], lnattr.transp[1], lnattr.transp[2], lnattr.transp[3], lnattr.towidth, lnattr.shwidth, lnattr.shangle); } fprintf(fid,"end\n"); fclose(fid); for (vcc = ii = 0; ii < Nimage; ii++) // sum transparency data vcc += image[ii].vtrancc; if (vcc == 0) remove(tranfile); // no transparencies, remove poss. file else { nfid = open(tranfile,O_WRONLY|O_CREAT|O_TRUNC,0640); // write transparencies to binary file if (nfid < 0) goto failure; for (ii = 0; ii < Nimage; ii++) { vcc = image[ii].vtrancc; if (! vcc) continue; cc = write(nfid,image[ii].vtranmap,vcc); if (cc != vcc) goto failure; } close(nfid); } for (wcc = ii = 0; ii < Nimage; ii++) // sum warpcc data wcc += image[ii].warpcc; if (wcc == 0) remove(warpfile); // no warp data, remove poss. file else { nfid = open(warpfile,O_WRONLY|O_CREAT|O_TRUNC,0640); // write warp data to binary file if (nfid < 0) goto failure; for (ii = 0; ii < Nimage; ii++) { wcc = image[ii].warpcc; if (! wcc) continue; cc = write(nfid,image[ii].warpx,wcc); cc = write(nfid,image[ii].warpy,wcc); if (cc != wcc) goto failure; } close(nfid); } return; failure: zmessageACK(Mwin,strerror(errno)); return; } // scale project up by 2x/3x/4x or reset to 1x void mashup::project_rescale(float scale) { using namespace mashup; int ii, nn; int64 size; float scaleR; PXB *pxbtemp; if (scale == 1.0) { if (PRscale == 2.0) scale = 0.5; if (PRscale == 3.0) scale = 0.3334; // int 3/6/9... --> 1/2/3... not 0/1/2... if (PRscale == 4.0) scale = 0.25; } size = Lww * Lhh * scale * scale; if (12 * size > 2000 * MEGA) { // PXM size <2 GB to remain editable zmessageACK(Mwin,ZTX("layout exceeds 2 gigabytes")); return; } if (scale < 1) PRscale = 1.0; // reset to 1x else PRscale = scale; // resize to 2x, 3x or 4x paintlock(1); // block window updates Lww *= scale; // rescale layout image Lhh *= scale; pxbtemp = PXB_rescale(Lpxb,Lww,Lhh); PXB_free(Lpxb); Lpxb = pxbtemp; PXB_free(Fpxb); // rescale window image to match Fpxb = PXB_copy(Lpxb); for (ii = 0; ii < Nimage; ii++) { // rescale overlay images scaleR = scale * image[ii].scale; image_rescale(ii,scaleR); image[ii].px0 *= scale; // new layout position image[ii].py0 *= scale; select("image",ii); setlayoutupdatearea(); } for (ii = 0; ii < Ntext; ii++) { // rescale text images text[ii].px0 *= scale; text[ii].py0 *= scale; text[ii].attr.size *= scale; text[ii].attr.towidth *= scale; text[ii].attr.shwidth *= scale; gentext(&text[ii].attr); text[ii].ww = text[ii].attr.pxb_text->ww; text[ii].hh = text[ii].attr.pxb_text->hh; select("text",ii); setlayoutupdatearea(); } for (ii = 0; ii < Nline; ii++) { // rescale line images nn = line[ii].px0; nn = (nn+10) * scale - 10; // compensate fixed 10 pixel margin line[ii].px0 = nn; nn = line[ii].py0; nn = (nn+10) * scale - 10; line[ii].py0 = nn; line[ii].attr.length *= scale; line[ii].attr.width *= scale; line[ii].attr.towidth *= scale; line[ii].attr.shwidth *= scale; genline(&line[ii].attr); line[ii].ww = line[ii].attr.pxb_line->ww; line[ii].hh = line[ii].attr.pxb_line->hh; select("line",ii); setlayoutupdatearea(); } paintlock(0); // unblock window updates Fupdall = 1; Lupdate(); return; } /********************************************************************************/ // mashup image edit function void mashup::image_edit() { using namespace mashup; cchar *tipmess = ZTX("Click image to select, drag image to move."); cchar *blackmargmess = ZTX("Make black margins transparent"); if (! strmatch(focus,"image")) select("",-1); // nothing selected /*** _______________________________________________ | Edit Images | | | | Click image to select, drag image to move. | | | | Current image: filename.jpg | | Cycle through images: [Prev] [Next] | | | | Scale [0.345 ] [ 1.0 ] | | Angle [12.3 ] | | Stacking Order [raise] [lower] | | Base Transparency [0.5 ] | | Variable Transparency [paint] | | Bend and fine-align [warp] | | [x] Make black margins transparent | | | | Margins hard blend | | Left [ 123 ] [ 0 ] | | Right [ 0 ] [ 123 ] | | Top [ 234 ] [ 0 ] | | Bottom [ 0 ] [ 234 ] | | | | [Add] [Delete] [Done] | |_______________________________________________| ***/ zdimage = zdialog_new("Edit Images",Mwin,Badd,Bdelete,Bdone,null); zdialog *zd = zdimage; zdialog_add_widget(zd,"hbox","hbtip","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labtip","hbtip",tipmess,"space=3"); zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labfile","hbfile",ZTX("Current image:"),"space=3"); zdialog_add_widget(zd,"label","currfile","hbfile",0,"space=3"); zdialog_add_widget(zd,"hbox","hbnext","dialog"); zdialog_add_widget(zd,"label","labnext","hbnext",ZTX("Cycle through images:"),"space=3"); zdialog_add_widget(zd,"button","prev","hbnext",Bprev,"space=8"); zdialog_add_widget(zd,"button","next","hbnext",Bnext); zdialog_add_widget(zd,"vbox","space","dialog",0,"space=5"); zdialog_add_widget(zd,"hbox","hbscale","dialog"); zdialog_add_widget(zd,"label","labscale","hbscale",ZTX("Scale"),"space=3"); zdialog_add_widget(zd,"zspin","scale","hbscale","0.02|2.0|0.001|0.3"); zdialog_add_widget(zd,"button","scale1x","hbscale"," 1.0 ","space=10"); zdialog_add_widget(zd,"hbox","hbangle","dialog"); zdialog_add_widget(zd,"label","labangle","hbangle",Bangle,"space=3"); zdialog_add_widget(zd,"zspin","angle","hbangle","-180|180|0.1|0"); zdialog_add_widget(zd,"hbox","hbstack","dialog"); zdialog_add_widget(zd,"label","labstack","hbstack",ZTX("Stacking Order"),"space=3"); zdialog_add_widget(zd,"button","raise","hbstack",ZTX("Raise"),"space=5"); zdialog_add_widget(zd,"button","lower","hbstack",ZTX("Lower"),"space=5"); zdialog_add_widget(zd,"hbox","hbbtransp","dialog"); zdialog_add_widget(zd,"label","labbtr","hbbtransp",ZTX("Base Transparency"),"space=3"); zdialog_add_widget(zd,"zspin","Btransp","hbbtransp","0|1.0|0.01|0"); zdialog_add_widget(zd,"hbox","hbvtranmap","dialog"); zdialog_add_widget(zd,"label","labvtr","hbvtranmap",ZTX("Var. Transparency"),"space=3"); zdialog_add_widget(zd,"button","vtranmap","hbvtranmap",ZTX("Paint"),"space=5"); zdialog_add_widget(zd,"hbox","hbwarp","dialog"); zdialog_add_widget(zd,"label","labwarp","hbwarp",ZTX("Bend and fine-align"),"space=3"); zdialog_add_widget(zd,"button","warp","hbwarp",ZTX("Warp"),"space=5"); zdialog_add_widget(zd,"hbox","hbmarg","dialog"); zdialog_add_widget(zd,"check","fixmarg","hbmarg",blackmargmess,"space=3"); zdialog_add_widget(zd,"hbox","hbmarg","dialog"); // margins zdialog_add_widget(zd,"vbox","vbmarg1","hbmarg",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vbmarg2","hbmarg",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vbmarg3","hbmarg",0,"space=3|homog"); zdialog_add_widget(zd,"label","labmarg","vbmarg1",ZTX("Margins")); zdialog_add_widget(zd,"label","labhard","vbmarg2",ZTX("Hard")); zdialog_add_widget(zd,"label","labblend","vbmarg3",ZTX("Blend")); zdialog_add_widget(zd,"label","lableft","vbmarg1",Bleft); zdialog_add_widget(zd,"label","labright","vbmarg1",Bright); zdialog_add_widget(zd,"label","labtop","vbmarg1",Btop); zdialog_add_widget(zd,"label","labbott","vbmarg1",Bbottom); zdialog_add_widget(zd,"zspin","Lmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"zspin","Rmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"zspin","Tmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"zspin","Bmarg","vbmarg2","0|999|1|0"); zdialog_add_widget(zd,"zspin","Lblend","vbmarg3","0|999|1|0"); zdialog_add_widget(zd,"zspin","Rblend","vbmarg3","0|999|1|0"); zdialog_add_widget(zd,"zspin","Tblend","vbmarg3","0|999|1|0"); zdialog_add_widget(zd,"zspin","Bblend","vbmarg3","0|999|1|0"); zdialog_add_ttip(zd,Badd,ZTX("add images to layout")); zdialog_run(zd,image_dialog_event,"save"); // run dialog - parallel zdialog_wait(zd); zdialog_free(zd); return; } // image dialog event and completion function int mashup::image_dialog_event(zdialog *zd, cchar *event) { using namespace mashup; int ii; float scale, angle, rad = PI / 180.0; image_t temp; if (strmatch(event,"kill")) // kill dialog { zdialog_free(zd); zdimage = 0; if (zdtransp) { zdialog_free(zdtransp); zdtransp = 0; } if (zdwarp) { zdialog_free(zdwarp); zdwarp = 0; } return 1; } if (zd->zstat) { if (zd->zstat == 1) { // add image zd->zstat = 0; add_image(); return 1; } if (zd->zstat == 2) { // remove image zd->zstat = 0; if (! strmatch(focus,"image")) return 1; ii = focusii; remove_image(ii); return 1; } zdimage = 0; // done or [x] kill if (zdtransp) { zdialog_free(zdtransp); zdtransp = 0; } if (zdwarp) { zdialog_free(zdwarp); zdwarp = 0; } return 1; } if (strmatch(event,"focus")) { // get mouse ownership takeMouse(mousefunc_layout,0); return 1; } if (strmatch(event,"next")) { // select and flash next image if (! Nimage) return 1; if (strmatch(focus,"image")) { if (focusii == Nimage-1) select("image",0); else select("image",focusii+1); } else select("image",0); flashimage(); Fnextimage = 1; // flag for layout mouse func. } if (strmatch(event,"prev")) { // select and flash prev. image if (! Nimage) return 1; if (strmatch(focus,"image")) { if (focusii == 0) select("image",Nimage-1); else select("image",focusii-1); } else select("image",0); flashimage(); Fnextimage = 1; } if (! strmatch(focus,"image")) return 1; // get selected image ii = focusii; if (strmatch(event,"scale")) // resize { zdialog_fetch(zd,"scale",scale); // new image scale image_rescale(ii,scale); select("image",ii); } if (strmatch(event,"scale1x")) // resize to 1x { zdialog_stuff(zd,"scale",1.0); image_rescale(ii,1.0); select("image",ii); } if (strmatch(event,"angle")) // rotate { zdialog_fetch(zd,"angle",angle); angle = angle * rad; image[ii].theta = angle; image[ii].cosT = cos(angle); image[ii].sinT = sin(angle); } if (strmatch(event,"raise")) { // raise image in stacking order if (ii == Nimage - 1) return 1; temp = image[ii+1]; image[ii+1] = image[ii]; image[ii] = temp; ii = focusii = ii + 1; } if (strmatch(event,"lower")) { // lower image in stacking order if (ii == 0) return 1; temp = image[ii-1]; image[ii-1] = image[ii]; image[ii] = temp; ii = focusii = ii - 1; } if (strmatch(event,"Btransp")) // base transparency, 0 to 1.0 = invisible zdialog_fetch(zd,"Btransp",image[ii].Btransp); if (strmatch(event,"vtranmap")) { // paint variable transparency dialog paintransp_dialog(); return 1; } if (strmatch(event,"warp")) { warp_dialog(); return 1; } if (strmatch(event,"fixmarg")) transparent_margins(); // fix black margins if (strmatch(event,"Lmarg")) // left edge margin zdialog_fetch(zd,"Lmarg",image[ii].Lmarg); if (strmatch(event,"Rmarg")) // right edge zdialog_fetch(zd,"Rmarg",image[ii].Rmarg); if (strmatch(event,"Tmarg")) // top edge zdialog_fetch(zd,"Tmarg",image[ii].Tmarg); if (strmatch(event,"Bmarg")) // bottom edge zdialog_fetch(zd,"Bmarg",image[ii].Bmarg); if (strmatch(event,"Lblend")) // left edge blend width zdialog_fetch(zd,"Lblend",image[ii].Lblend); if (strmatch(event,"Rblend")) // right edge zdialog_fetch(zd,"Rblend",image[ii].Rblend); if (strmatch(event,"Tblend")) // top edge zdialog_fetch(zd,"Tblend",image[ii].Tblend); if (strmatch(event,"Bblend")) // bottom edge zdialog_fetch(zd,"Bblend",image[ii].Bblend); zmainloop(); setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return 1; } // rescale an overlay image: pxb1 (1x) --> pxb2 (scale) // if warps are present, rescale from 1x and apply to pxb2 void mashup::image_rescale(int ii, float scale) { int ww1, hh1, ww2, hh2; int px1, py1, px2, py2, kk; float dx1, dy1, dx2, dy2, vstat; uint8 vpix[4], *pix2; PXB *pxb2, *pxb2w; ww1 = image[ii].ww1; // 1x image size hh1 = image[ii].hh1; ww2 = scale * ww1; // image size at scale hh2 = scale * hh1; pxb2 = PXB_rescale(image[ii].pxb1,ww2,hh2); // rescale image -> pxb2 PXB_free(image[ii].pxb2); image[ii].pxb2 = pxb2; image[ii].ww2 = ww2; image[ii].hh2 = hh2; image[ii].scale = scale; if (image[ii].warpcc) { // apply image warps to pxb2 pxb2w = PXB_copy(pxb2); for (py2 = 0; py2 < hh2; py2++) for (px2 = 0; px2 < ww2; px2++) { px1 = px2 / scale; // rescale 1x warps py1 = py2 / scale; kk = py1 * ww1 + px1; dx1 = image[ii].warpx[kk]; dy1 = image[ii].warpy[kk]; dx2 = dx1 * scale; dy2 = dy1 * scale; vstat = vpixel(image[ii].pxb2,px2+dx2,py2+dy2,vpix); pix2 = PXBpix(pxb2w,px2,py2); if (vstat) memcpy(pix2,vpix,3); else pix2[0] = pix2[1] = pix2[2] = pix2[3] = 0; } PXB_free(image[ii].pxb2); image[ii].pxb2 = pxb2w; } return; } // dialog to initiate painting image variable transparency void mashup::paintransp_dialog() { using namespace mashup; cchar *ptitle = ZTX("Paint Image Transparencies"); /*** _________________________________ | Paint Image Transparencies | | | | Radius [123 ] [x] Gradual | | Power Center [100] Edge [ 10] | | | | [done] | |_________________________________| ***/ zdtransp = zdialog_new(ptitle,Mwin,Bdone,null); zdialog_add_widget(zdtransp,"hbox","hbrad","dialog",0,"space=3"); zdialog_add_widget(zdtransp,"label","labrad","hbrad",Bradius,"space=5"); zdialog_add_widget(zdtransp,"zspin","radius","hbrad","5|300|1|100"); zdialog_add_widget(zdtransp,"check","gradual","hbrad",ZTX("Gradual"),"space=10"); zdialog_add_widget(zdtransp,"hbox","hbpow","dialog",0,"space=3"); zdialog_add_widget(zdtransp,"label","labpower","hbpow",ZTX("Power"),"space=5"); zdialog_add_widget(zdtransp,"label","labcen","hbpow",Bcenter,"space=5"); zdialog_add_widget(zdtransp,"zspin","center","hbpow","0|100|1|10"); zdialog_add_widget(zdtransp,"label","space","hbpow",0,"space=6"); zdialog_add_widget(zdtransp,"label","labedge","hbpow",Bedge,"space=5"); zdialog_add_widget(zdtransp,"zspin","edge","hbpow","0|100|1|3"); zdialog_stuff(zdtransp,"radius",Mradius); zdialog_stuff(zdtransp,"radius",Mradius); zdialog_stuff(zdtransp,"gradual",Mgradual); zdialog_stuff(zdtransp,"edge",Mpowedge); zdialog_run(zdtransp,paintransp_dialog_event,"save"); takeMouse(paintransp_mousefunc,0); return; } // zdialog event and completion function int mashup::paintransp_dialog_event(zdialog *zd, cchar *event) { using namespace mashup; if (zd->zstat) { // done or cancel zdialog_free(zd); zdtransp = 0; takeMouse(mousefunc_layout,0); return 1; } if (strmatch(event,"focus")) { // take mouse ownership takeMouse(paintransp_mousefunc,0); return 1; } if (strmatch(event,"radius")) // mouse radius value zdialog_fetch(zd,"radius",Mradius); if (strmatch(event,"gradual")) // gradual/instant paint option zdialog_fetch(zd,"gradual",Mgradual); if (strmatch(event,"center")) // mouse center power zdialog_fetch(zd,"center",Mpowcen); if (strmatch(event,"edge")) // mouse edge power zdialog_fetch(zd,"edge",Mpowedge); return 1; } // mouse function for painting image var. transparency void mashup::paintransp_mousefunc() { using namespace mashup; int ii, Mii, jj, vcc, mx, my; int distx, disty, dist, mindist; int pxlo, pylo, pxhi, pyhi; int cenx, ceny, mrad = Mradius; int ww1, hh1, ww2, hh2, transp, mtransp; int dx, dy, qx, qy, rx, ry; float sinT, cosT, px0, py0, px1, py1; px1 = py1 = -1; // no mouse position in image if (! strmatch(focus,"image")) { // if no image focus, draw_mousecircle(0,0,0,1,0); // no mouse circle gdk_window_set_cursor(gdkwin,0); } mx = Mxposn; // mouse position in layout my = Myposn; if (LMclick) // left click { LMclick = 0; // stop main window click after return mx = Mxclick; // click position my = Myclick; Mii = -1; // no selected image mindist = 9999; for (ii = 0; ii < Nimage; ii++) // find closest image center to mouse { px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout image space sinT = image[ii].sinT; cosT = image[ii].cosT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px1 = (mx-px0) * cosT + (my-py0) * sinT; // image pixel for layout position py1 = (my-py0) * cosT - (mx-px0) * sinT; if (px1 < 0 || px1 > ww2-1) continue; // not within image if (py1 < 0 || py1 > hh2-1) continue; pxlo = image[ii].pxlo; // image extent pxhi = image[ii].pxhi; pylo = image[ii].pylo; pyhi = image[ii].pyhi; cenx = (pxlo + pxhi) / 2; // image center ceny = (pylo + pyhi) / 2; distx = mx - cenx; disty = my - ceny; dist = sqrtf(distx * distx + disty * disty); // mouse/center distance if (dist < mindist) { mindist = dist; Mii = ii; // save closest image center } } if (Mii < 0) { // no image clicked select("",-1); // no selected image draw_mousecircle(0,0,0,1,0); // no mouse circle gdk_window_set_cursor(gdkwin,0); return; } ii = Mii; select("image",ii); // set selected image flashimage(); // flash selected image return; } if (! strmatch(focus,"image")) { // if no current image, draw_mousecircle(0,0,0,1,0); // no mouse circle gdk_window_set_cursor(gdkwin,0); return; } ii = focusii; // current image px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout image space sinT = image[ii].sinT; cosT = image[ii].cosT; ww1 = image[ii].ww1; // image size at 1x hh1 = image[ii].hh1; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px1 = (mx-px0) * cosT + (my-py0) * sinT; // mouse position within image py1 = (my-py0) * cosT - (mx-px0) * sinT; if (px1+mrad < 0 || px1-mrad > ww2 || // entire circle off image py1+mrad < 0 || py1-mrad > hh2) { draw_mousecircle(0,0,0,1,0); // no mouse circle gdk_window_set_cursor(gdkwin,0); return; } draw_mousecircle(mx,my,mrad,0,0); // mouse within image, draw mouse circle gdk_window_set_cursor(gdkwin,dragcursor); zmainloop(); // keep mouse circle visible if (! Mxdrag && ! Mydrag) return; // no drag underway Mxdrag = Mydrag = 0; // stop main window drag after return if (! image[ii].vtranmap) { // make transparency map if not already vcc = ww1 * hh1; // transparency map is 1x scale image[ii].vtranmap = (uint8 *) zmalloc(vcc); // and one uint8 per pixel memset(image[ii].vtranmap,0,vcc); // initial values = 0 transparency image[ii].vtrancc = vcc; } qx = px1 / image[ii].scale; // mouse position in 1x image qy = py1 / image[ii].scale; mrad = mrad / image[ii].scale; // mouse radius at 1x scale for (dy = -mrad; dy <= mrad; dy++) // loop pixels within mouse circle for (dx = -mrad; dx <= mrad; dx++) { dist = sqrtf(dx*dx + dy*dy); // distance from mouse center if (dist >= mrad) continue; // outside mouse circle if (Mgradual) { // gradual paint transparency mtransp = Mpowcen * (mrad - dist) / mrad // mouse power at distance from center + Mpowedge * dist / mrad; // Mpowcen ... Mpowedge scale 0-100 if (drandz() < 0.005) continue; // thin active points } else mtransp = 255; // instant paint transparency rx = qx + dx; // 1x pixel position ry = qy + dy; if (rx < 0 || rx > ww1-1) continue; // outside 1x image if (ry < 0 || ry > hh1-1) continue; jj = ry * ww1 + rx; // get corresp. var. transparency transp = image[ii].vtranmap[jj]; if (Mbutton == 1) { // left mouse button transp += mtransp; // increase image transparency if (transp > 255) transp = 255; // by mouse power } else { // right button transp -= mtransp; // decrease if (transp < 0) transp = 0; } image[ii].vtranmap[jj] = transp; // adjusted image transparency } updxlo = mx - mrad; // update area = mouse circle updxhi = mx + mrad; updylo = my - mrad; updyhi = my + mrad; setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } // dialog to initiate warping an image by dragging the mouse namespace warp { float dragx, dragy, dragw, dragh; float warpD, span; int Fdrag; PXB *pxb2w; int warpii; }; void mashup::warp_dialog() { using namespace mashup; using namespace warp; cchar *warp_message = ZTX("Pull on the image with the mouse."); /*** ___________________________________ | Warp Image | | | | Pull on the image with the mouse. | | | | [undo last] [undo all] | | Warp span [___] | | | | [done] | |___________________________________| ***/ zdwarp = zdialog_new(ZTX("Warp Image"),Mwin,Bdone,null); zdialog_add_widget(zdwarp,"label","lab1","dialog",warp_message,"space=3"); zdialog_add_widget(zdwarp,"hbox","hb1","dialog",0,"space=8"); zdialog_add_widget(zdwarp,"button","undolast","hb1",Bundolast,"space=8"); zdialog_add_widget(zdwarp,"button","undoall","hb1",Bundoall,"space=2"); zdialog_add_widget(zdwarp,"hbox","hb2","dialog",0,"space=4"); zdialog_add_widget(zdwarp,"label","lab2","hb2",ZTX("warp span"),"space=8"); zdialog_add_widget(zdwarp,"zspin","span","hb2","0.00|1.0|0.01|0.2","space=1"); span = 0.2; Fdrag = 0; pxb2w = 0; warpii = -1; zdialog_run(zdwarp,warp_dialog_event,"save"); // run dialog, parallel takeMouse(warp_mousefunc,0); // connect mouse function return; } // dialog event and completion callback function int mashup::warp_dialog_event(zdialog * zd, cchar *event) { using namespace mashup; using namespace warp; int ii, kk; if (zd->zstat) // done or cancel { if (pxb2w) PXB_free(pxb2w); pxb2w = 0; zdialog_free(zd); // kill dialog zdwarp = 0; takeMouse(mousefunc_layout,0); // restore mouse ownership return 1; } if (! strmatch(focus,"image")) return 1; // insure image focus ii = focusii; if (strmatch(event,"focus")) // connect mouse function takeMouse(warp_mousefunc,dragcursor); if (strmatch(event,"undolast")) { if (image[ii].Nwarp == 1) event = "undoall"; else if (image[ii].Nwarp) { kk = image[ii].Nwarp - 1; dragx = image[ii].warpmem[kk][0]; // new warp = negative prior warp dragy = image[ii].warpmem[kk][1]; dragw = -image[ii].warpmem[kk][2]; dragh = -image[ii].warpmem[kk][3]; span = image[ii].warpmem[kk][4]; zdialog_stuff(zd,"span",span); Fdrag = 0; warp_warpfunc(); image[ii].Nwarp -= 1; } } if (strmatch(event,"undoall") && image[ii].warpcc) { // undo all warps image[ii].Nwarp = 0; zfree(image[ii].warpx); zfree(image[ii].warpy); image[ii].warpx = image[ii].warpy = 0; image[ii].warpcc = 0; } if (strstr(event,"undo")) { image_rescale(ii,image[ii].scale); // pxb1 + warps -> pxb2 if (pxb2w) PXB_free(pxb2w); pxb2w = PXB_copy(image[ii].pxb2); // base for next drag/warp setlayoutupdatearea(); Lupdate(); // update layout image } if (strmatch(event,"span")) zdialog_fetch(zd,"span",span); return 1; } // warp mouse function // convert mouse drags to image warps void mashup::warp_mousefunc(void) { using namespace mashup; using namespace warp; int ii, kk, wcc, Mii; int mx, my, dx, dy, dw, dh; int distx, disty, dist, mindist; int pxlo, pylo, pxhi, pyhi; int cenx, ceny, ww1, hh1, ww2, hh2; float sinT, cosT, scale, px0, py0, px1, py1; if (LMclick) // left click { gdk_window_set_cursor(gdkwin,0); // no drag cursor LMclick = 0; // stop main window click after return mx = Mxclick; // click position my = Myclick; Mii = -1; // no selected image mindist = 9999; for (ii = 0; ii < Nimage; ii++) // find closest image center to mouse { px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout image space sinT = image[ii].sinT; cosT = image[ii].cosT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px1 = (mx-px0) * cosT + (my-py0) * sinT; // image pixel for layout position py1 = (my-py0) * cosT - (mx-px0) * sinT; if (px1 < 0 || px1 > ww2-1) continue; // not within image if (py1 < 0 || py1 > hh2-1) continue; pxlo = image[ii].pxlo; // image extent pxhi = image[ii].pxhi; pylo = image[ii].pylo; pyhi = image[ii].pyhi; cenx = (pxlo + pxhi) / 2; // image center ceny = (pylo + pyhi) / 2; distx = mx - cenx; disty = my - ceny; dist = sqrtf(distx * distx + disty * disty); // mouse/center distance if (dist < mindist) { mindist = dist; Mii = ii; // save closest image center } } if (Mii < 0) { // no image clicked select("",-1); // no selected image return; } ii = Mii; select("image",ii); // set selected image flashimage(); // flash selected image return; } if (! strmatch(focus,"image")) { // if no image focus, gdk_window_set_cursor(gdkwin,0); // no drag cursor return; } ii = focusii; // focus image mx = Mxposn; // mouse position in layout space my = Myposn; scale = image[ii].scale; px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout space sinT = image[ii].sinT; cosT = image[ii].cosT; px1 = (mx-px0) * cosT + (my-py0) * sinT; // mouse position within scaled image py1 = (my-py0) * cosT - (mx-px0) * sinT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; if (px1 < 0 || px1 > ww2 || py1 < 0 || py1 > hh2) { // mouse outside image gdk_window_set_cursor(gdkwin,0); // no drag cursor return; } gdk_window_set_cursor(gdkwin,dragcursor); // mouse inside image, drag cursor if (! pxb2w || (ii != warpii)) { // image changed if (pxb2w) PXB_free(pxb2w); pxb2w = PXB_copy(image[ii].pxb2); warpii = ii; } if (Mxdrag || Mydrag) // mouse drag underway { if (! image[ii].warpcc) // first warp for image { ww1 = image[ii].ww1; hh1 = image[ii].hh1; wcc = ww1 * hh1 * sizeof(float); image[ii].warpx = (float *) zmalloc(wcc); // get memory for pixel displacements image[ii].warpy = (float *) zmalloc(wcc); image[ii].warpcc = wcc; memset(image[ii].warpx,0,wcc); memset(image[ii].warpy,0,wcc); image[ii].Nwarp = 0; // no warp memory data } dx = Mxdown; // drag origin, image space dy = Mydown; dw = Mxdrag - Mxdown; // drag increment dh = Mydrag - Mydown; Mxdrag = Mydrag = 0; // reset px0 = image[ii].px0; py0 = image[ii].py0; sinT = image[ii].sinT; cosT = image[ii].cosT; dragx = (dx-px0) * cosT + (dy-py0) * sinT; // drag origin in scaled image dragy = (dy-py0) * cosT - (dx-px0) * sinT; dragw = dw * cosT + dh * sinT; // drag increment in scaled image dragh = dh * cosT - dw * sinT; warpD = ww2 + hh2; // warp span warpD = warpD * span; Fdrag = 1; // drag underway warp_warpfunc(); // drag pxb2 image } else if (Fdrag) // drag complete { dragx = dragx / scale; // conv. drag vector to 1x scale dragy = dragy / scale; dragw = dragw / scale; dragh = dragh / scale; warpD = warpD / scale; Fdrag = 0; warp_warpfunc(); // accumulate pxb1 warp positions image_rescale(ii,image[ii].scale); // pxb1 + warps -> pxb2 PXB_free(pxb2w); // base for next drag/warp pxb2w = PXB_copy(image[ii].pxb2); if (image[ii].Nwarp == 200) // add drag to undo memory { image[ii].Nwarp = 199; // full, remove oldest for (kk = 0; kk < image[ii].Nwarp; kk++) { image[ii].warpmem[kk][0] = image[ii].warpmem[kk+1][0]; image[ii].warpmem[kk][1] = image[ii].warpmem[kk+1][1]; image[ii].warpmem[kk][2] = image[ii].warpmem[kk+1][2]; image[ii].warpmem[kk][3] = image[ii].warpmem[kk+1][3]; image[ii].warpmem[kk][4] = image[ii].warpmem[kk+1][4]; } } kk = image[ii].Nwarp; image[ii].warpmem[kk][0] = dragx; // save drag for undo image[ii].warpmem[kk][1] = dragy; image[ii].warpmem[kk][2] = dragw; image[ii].warpmem[kk][3] = dragh; image[ii].warpmem[kk][4] = span; image[ii].Nwarp++; } setlayoutupdatearea(); // update layout image Lupdate(); return; } // warp image and accumulate warp memory // mouse at (dragx,dragy) is moved (dragw,dragh) pixels void mashup::warp_warpfunc() { using namespace mashup; using namespace warp; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(warp_warpfunc_wthread,&Nval[ii]); wait_wthreads(1); // wait for completion (block) return; } void * mashup::warp_warpfunc_wthread(void *arg) // worker thread { using namespace mashup; using namespace warp; int index = *((int *) arg); int ii, kk, px, py, vstat; int ww1, hh1, ww2, hh2; float d, mag, dx, dy; float FD = 1.0 / (warpD * warpD); uint8 vpix[4], *pix2; ii = focusii; if (Fdrag) // drag underway { ww2 = image[ii].ww2; hh2 = image[ii].hh2; for (py = index; py < hh2; py += NWT) // process all pxb2 pixels for (px = 0; px < ww2; px++) { d = (px-dragx)*(px-dragx) + (py-dragy)*(py-dragy); // compute warp based on nearness mag = 1.0 - d * FD; // to mouse position = drag center if (mag < 0) continue; mag = mag * mag; // faster than pow(mag,4); mag = mag * mag; dx = -dragw * mag; // displacement = drag * mag dy = -dragh * mag; vstat = vpixel(pxb2w,px+dx,py+dy,vpix); // drag/warp pxb2 image pix2 = PXBpix(image[ii].pxb2,px,py); if (vstat) memcpy(pix2,vpix,3); else pix2[0] = pix2[1] = pix2[2] = pix2[3] = 0; } } else // drag complete { ww1 = image[ii].ww1; hh1 = image[ii].hh1; for (py = index; py < hh1; py += NWT) // process all pxb1 pixels for (px = 0; px < ww1; px++) { d = (px-dragx)*(px-dragx) + (py-dragy)*(py-dragy); // compute warp based on nearness mag = 1.0 - d * FD; // to mouse position = drag center if (mag < 0) continue; mag = mag * mag; // faster than pow(mag,4); mag = mag * mag; dx = -dragw * mag; // displacement = drag * mag dy = -dragh * mag; kk = py * ww1 + px; // accumulate drag/warp image[ii].warpx[kk] += dx; // into pxb1 warp memory image[ii].warpy[kk] += dy; } } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } // make black image margins transparent void mashup::transparent_margins() { int ii, ww1, hh1, px, py; PXB *pxb1; uint8 *pixel; for (ii = 0; ii < Nimage; ii++) // loop all images { ww1 = image[ii].ww1; // image 1x size hh1 = image[ii].hh1; pxb1 = image[ii].pxb1; for (py = 0; py < hh1; py++) // loop all rows { for (px = 0; px < ww1/2; px++) // from left edge to middle { pixel = PXBpix(pxb1,px,py); if (pixel[0] > 0 || pixel[1] > 0 || pixel[2] > 1) break; // break out at first non-black pixel pixel[3] = 0; // pixel has full transparency } for (px = ww1-1; px > ww1/2; px--) // from right edge to middle { pixel = PXBpix(pxb1,px,py); if (pixel[0] > 0 || pixel[1] > 0 || pixel[2] > 1) break; pixel[3] = 0; } } for (px = 0; px < ww1; px++) // from top to middle { for (py = 0; py < hh1/2; py++) { pixel = PXBpix(pxb1,px,py); if (pixel[0] > 0 || pixel[1] > 0 || pixel[2] > 1) break; pixel[3] = 0; } for (py = hh1-1; py > hh1/2; py--) // from bottom to middle { pixel = PXBpix(pxb1,px,py); if (pixel[0] > 0 || pixel[1] > 0 || pixel[2] > 1) break; pixel[3] = 0; } } image_rescale(ii,image[ii].scale); // update scaled image pxb2 } Fupdall = 1; Lupdate(); // update layout composite image return; } // select and add one or more new overlay image files void mashup::add_image() { using namespace mashup; int ii, jj, nn, cc, Nfiles = 0; int ww1, hh1, ww2, hh2; float scale; char **selfiles = 0, *file; PXB *pxb1; zdialog_show(zdimage,0); // hide parent dialog gallery_select_clear(); // clear gallery_select() file list gallery_select(); // select image files from gallery m_viewmode(0,"F"); // restore view mode F zdialog_show(zdimage,1); if (! GScount) return; // nothing selected selfiles = (char **) zmalloc(GScount * sizeof(char *)); for (ii = 0; ii < GScount; ii++) selfiles[ii] = GSfiles[ii]; Nfiles = GScount; GScount = 0; for (nn = 0; nn < Nfiles; nn++) { if (Nimage == maxmash) { zmessageACK(Mwin,ZTX("exceeded %d images"),maxmash); break; } file = selfiles[nn]; pxb1 = PXB_load(file,1); // load image, ACK errors if (! pxb1) { zfree(file); continue; } PXB_addalpha(pxb1); // add alpha channel if missing ii = Nimage++; // new image cc = sizeof(image_t); // clear image data memset(&image[ii],0,cc); image[ii].file = file; // add image file to mashup list image[ii].pxb1 = pxb1; ww1 = pxb1->ww; hh1 = pxb1->hh; scale = 1.0; if (scale * ww1 / Lww > 0.3) scale = 0.3 * Lww / ww1; // if image > 30% layout, reduce to 30% if (scale * hh1 / Lhh > 0.3) scale = 0.3 * Lhh / hh1; ww2 = ww1 * scale; hh2 = hh1 * scale; image[ii].scale = scale; image[ii].ww1 = ww1; image[ii].hh1 = hh1; image[ii].ww2 = ww2; image[ii].hh2 = hh2; image[ii].pxb2 = PXB_rescale(image[ii].pxb1,ww2,hh2); // scaled image for display image[ii].theta = 0.0; image[ii].sinT = 0; image[ii].cosT = 1.0; jj = ii; while (jj >= 25) jj -= 25; image[ii].py0 = (jj / 5) * 0.20 * Lhh; // initial position image[ii].px0 = (jj % 5) * 0.20 * Lww; select("image",ii); setlayoutupdatearea(); // set layout update area } while (nn < Nfiles) zfree(selfiles[nn++]); // free unused files zfree(selfiles); select("",-1); // nothing selected Fupdall = 1; Lupdate(); // update layout composite image return; } // remove an overlay image void mashup::remove_image(int ii) { using namespace mashup; select("image",ii); setlayoutupdatearea(); // set layout update area zfree(image[ii].file); // free memory PXB_free(image[ii].pxb1); PXB_free(image[ii].pxb2); if (image[ii].vtranmap) zfree(image[ii].vtranmap); for (int jj = ii+1; jj < Nimage; jj++) // pack-down image list image[ii] = image[jj]; Nimage--; // image count select("",-1); // no selected image Fupdall = 1; Lupdate(); // update layout composite image return; } // flash a selected image as bright as possible for a short moment void mashup::flashimage() { int ii, px, py, ww2, hh2; int xlo, xhi, ylo, yhi; float px0, py0, px1, py1, px2, py2; float sinT, cosT; uint8 *pix2, vpix[4]; PXB *pxb2; if (! strmatch(focus,"image")) return; ii = focusii; xlo = image[ii].pxlo; // overlay image extent in layout image xhi = image[ii].pxhi; ylo = image[ii].pylo; yhi = image[ii].pyhi; for (py = ylo; py < yhi; py++) for (px = xlo; px < xhi; px++) { px1 = px; py1 = py; px0 = image[ii].px0; // overlay image position and rotation py0 = image[ii].py0; // in layout image space pxb2 = image[ii].pxb2; sinT = image[ii].sinT; cosT = image[ii].cosT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px2 = (px1-px0) * cosT + (py1-py0) * sinT; // overlay image pixel for layout position py2 = (py1-py0) * cosT - (px1-px0) * sinT; if (px2 < 0 || px2 > ww2-1) continue; // not within image if (py2 < 0 || py2 > hh2-1) continue; pix2 = PXBpix(Fpxb,px,py); // output image pixel if (! vpixel(pxb2,px2,py2,vpix)) continue; // overlay image virt. pixel pix2[0] = vpix[1]; // rotate the colors pix2[1] = vpix[2]; pix2[2] = vpix[0]; } ww2 = xhi - xlo; hh2 = yhi - ylo; PXB_PXB_update(Fpxb,Mpxb,xlo,ylo,ww2,hh2); // Fpxb > Mpxb, scaled up or down Fpaint4(xlo,ylo,ww2,hh2,0); // update drawing window from Mpxb zmainloop(); zsleep(0.1); // hold for a moment setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } /********************************************************************************/ // add text to image or revise existing text void mashup::text_edit() { using namespace mashup; cchar *intro = ZTX("Enter text, [Add] to layout, edit properties."); /*** __________________________________________________ | Edit Text | | | | Enter text, [Add] to layout, edit properties. | | | | Text [______________________________________] | | | | [font] [FreeSans______________] size [ 44 ] | | | | color transp. width angle | | text [_____] [_____] [_____] | | backing [_____] [_____] | | outline [_____] [_____] [_____] | | shadow [_____] [_____] [_____] [_____] | | | | Text File: [Open] [Save] | | | | [clear] [Add] [Delete] [Done] | |__________________________________________________| ***/ if (! strmatch(focus,"text")) select("",-1); // nothing selected zdtext = zdialog_new(ZTX("Edit Text"),Mwin,Bclear,Badd,Bdelete,Bdone,null); zdialog *zd = zdtext; zdialog_add_widget(zd,"label","intro","dialog",intro,"space=3"); zdialog_add_widget(zd,"hbox","hbtext","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labtext","hbtext",ZTX("Text"),"space=5"); zdialog_add_widget(zd,"frame","frtext","hbtext",0,"expand"); zdialog_add_widget(zd,"edit","text","frtext","","expand"); zdialog_add_widget(zd,"hbox","hbfont","dialog",0,"space=3"); zdialog_add_widget(zd,"button","fontbutt","hbfont",Bfont); zdialog_add_widget(zd,"zentry","fontname","hbfont","FreeSans","space=2|expand"); zdialog_add_widget(zd,"label","labsize","hbfont",Bsize,"space=2"); zdialog_add_widget(zd,"zspin","fontsize","hbfont","8|500|1|40"); zdialog_add_widget(zd,"hbox","hbcol","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vbcol1","hbcol",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbcol2","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol3","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol4","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol5","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"label","space","vbcol1"); zdialog_add_widget(zd,"label","labtext","vbcol1",ZTX("text")); zdialog_add_widget(zd,"label","labback","vbcol1",ZTX("backing")); zdialog_add_widget(zd,"label","laboutln","vbcol1",ZTX("outline")); zdialog_add_widget(zd,"label","labshadow","vbcol1",ZTX("shadow")); zdialog_add_widget(zd,"label","labcol","vbcol2",Bcolor); zdialog_add_widget(zd,"colorbutt","fgcolor","vbcol2","0|0|0"); zdialog_add_widget(zd,"colorbutt","bgcolor","vbcol2","255|255|255"); zdialog_add_widget(zd,"colorbutt","tocolor","vbcol2","255|0|0"); zdialog_add_widget(zd,"colorbutt","shcolor","vbcol2","0|255|0"); zdialog_add_widget(zd,"label","labcol","vbcol3","Transp."); zdialog_add_widget(zd,"zspin","fgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","bgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","totransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","shtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"label","labw","vbcol4",Bwidth); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"zspin","towidth","vbcol4","0|30|1|0"); zdialog_add_widget(zd,"zspin","shwidth","vbcol4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbcol5",Bangle); zdialog_add_widget(zd,"zspin","angle","vbcol5","-180|180|0.1|0"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"zspin","shangle","vbcol5","-180|180|1|0"); zdialog_add_widget(zd,"hbox","hbaf","dialog",0,"space=10"); zdialog_add_widget(zd,"label","labbg","hbaf",ZTX("Text File:"),"space=3"); zdialog_add_widget(zd,"button","loadtext","hbaf",Bopen,"space=5"); zdialog_add_widget(zd,"button","savetext","hbaf",Bsave,"space=5"); zdialog_add_ttip(zd,Badd,ZTX("add entered text to layout")); zdialog_restore_inputs(zd); // restore prior inputs zdialog_run(zd,text_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); zdialog_free(zd); return; } // dialog event and completion callback function int mashup::text_dialog_event(zdialog *zd, cchar *event) { using namespace mashup; GtkWidget *font_dialog; char font[80]; int ii, size; char *pp; if (strmatch(event,"kill")) { // kill dialog zdialog_free(zd); zdtext = 0; return 1; } if (zd->zstat) { if (zd->zstat == 1) { // clear zd->zstat = 0; zdialog_stuff(zd,"text",""); focus = ""; return 1; } if (zd->zstat == 2) { // add zd->zstat = 0; add_text(); // zdialog inputs >> new text image return 1; } if (zd->zstat == 3) { // delete zd->zstat = 0; if (! strmatch(focus,"text")) return 1; ii = focusii; remove_text(ii); // remove selected text image return 1; } zdtext = 0; // done or [x] kill return 1; } if (strmatch(event,"focus")) { // get mouse ownership takeMouse(mousefunc_layout,0); return 1; } if (strmatch(event,"loadtext")) // load text data from file load_text(zdtext); // and stuff zdialog fields if (strmatch(event,"savetext")) { // get all zdialog fields save_text(zdtext); // and save them to a file return 1; } if (strmatch(event,"fontbutt")) // set new font { zdialog_fetch(zd,"fontname",font,80); zdialog_fetch(zd,"fontsize",size); snprintf(font,80,"%s %d",font,size); font_dialog = gtk_font_chooser_dialog_new(ZTX("select font"),MWIN); gtk_font_chooser_set_font(GTK_FONT_CHOOSER(font_dialog),font); gtk_dialog_run(GTK_DIALOG(font_dialog)); pp = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(font_dialog)); gtk_widget_destroy(font_dialog); if (pp) { // should have "fontname nn" strncpy0(font,pp,80); g_free(pp); pp = font + strlen(font); while (*pp != ' ') pp--; if (pp > font) { // set both font name and size size = atoi(pp); if (size < 8) size = 8; zdialog_stuff(zd,"fontsize",size); *pp = 0; zdialog_stuff(zd,"fontname",font); } } } if (! strmatch(focus,"text")) return 1; // selected text image ii = focusii; zdialog_fetch(zd,"text",text[ii].attr.text,1000); // initz. new text and attributes zdialog_fetch(zd,"fontname",text[ii].attr.font,80); zdialog_fetch(zd,"fontsize",text[ii].attr.size); zdialog_fetch(zd,"angle",text[ii].attr.angle); zdialog_fetch(zd,"fgcolor",text[ii].attr.color[0],20); zdialog_fetch(zd,"bgcolor",text[ii].attr.color[1],20); zdialog_fetch(zd,"tocolor",text[ii].attr.color[2],20); zdialog_fetch(zd,"shcolor",text[ii].attr.color[3],20); zdialog_fetch(zd,"fgtransp",text[ii].attr.transp[0]); zdialog_fetch(zd,"bgtransp",text[ii].attr.transp[1]); zdialog_fetch(zd,"totransp",text[ii].attr.transp[2]); zdialog_fetch(zd,"shtransp",text[ii].attr.transp[3]); zdialog_fetch(zd,"towidth",text[ii].attr.towidth); zdialog_fetch(zd,"shwidth",text[ii].attr.shwidth); zdialog_fetch(zd,"shangle",text[ii].attr.shangle); gentext(&text[ii].attr); // generate new text image text[ii].ww = text[ii].attr.pxb_text->ww; text[ii].hh = text[ii].attr.pxb_text->hh; zmainloop(); // update dialog controls setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return 1; } // add new text entry to text[ii] list // get text and attributes from zdialog fields void mashup::add_text() { using namespace mashup; int ii; zdialog *zd = zdtext; cchar *tip = ZTX("click position to add text"); if (! zd) return; if (zdaddtext) return; if (Ntext == maxmash) { zmessageACK(Mwin,ZTX("exceeded %d text entries"),maxmash); return; } Mxclick = Myclick = 0; zdaddtext = zdialog_new(ZTX("Add Text"),Mwin,Bcancel,null); // get mouse click for text position zdialog_set_decorated(zdaddtext,0); zdialog_add_widget(zdaddtext,"label","labtip","dialog",tip,"space=3"); zdialog_run(zdaddtext,0,"mouse"); zdialog_wait(zdaddtext); zdialog_free(zdaddtext); if (! (Mxclick + Myclick)) return; ii = Ntext++; memset(&text[ii],0,sizeof(text_t)); zdialog_fetch(zd,"text",text[ii].attr.text,1000); // initz. new text and attributes zdialog_fetch(zd,"fontname",text[ii].attr.font,80); zdialog_fetch(zd,"fontsize",text[ii].attr.size); zdialog_fetch(zd,"angle",text[ii].attr.angle); zdialog_fetch(zd,"fgcolor",text[ii].attr.color[0],20); zdialog_fetch(zd,"bgcolor",text[ii].attr.color[1],20); zdialog_fetch(zd,"tocolor",text[ii].attr.color[2],20); zdialog_fetch(zd,"shcolor",text[ii].attr.color[3],20); zdialog_fetch(zd,"fgtransp",text[ii].attr.transp[0]); zdialog_fetch(zd,"bgtransp",text[ii].attr.transp[1]); zdialog_fetch(zd,"totransp",text[ii].attr.transp[2]); zdialog_fetch(zd,"shtransp",text[ii].attr.transp[3]); zdialog_fetch(zd,"towidth",text[ii].attr.towidth); zdialog_fetch(zd,"shwidth",text[ii].attr.shwidth); zdialog_fetch(zd,"shangle",text[ii].attr.shangle); gentext(&text[ii].attr); // generate new text image text[ii].ww = text[ii].attr.pxb_text->ww; text[ii].hh = text[ii].attr.pxb_text->hh; text[ii].px0 = Mxclick; // set initial position text[ii].py0 = Myclick; select("text",ii); // set selected text image setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } // remove text entry from text[ii] void mashup::remove_text(int ii) { using namespace mashup; select("text",ii); setlayoutupdatearea(); // set layout update area PXB_free(text[ii].attr.pxb_text); for (int jj = ii+1; jj < Ntext; jj++) // pack down text list text[jj-1] = text[jj]; Ntext--; // text count select("",-1); // nothing selected Fupdall = 1; Lupdate(); // update layout composite image return; } // flash a selected text image as bright as possible for a short moment void mashup::flashtext() { int ii, jj, red, green, blue; char savecolor[4][20], flashcolor[20]; if (! strmatch(focus,"text")) return; ii = focusii; for (jj = 0; jj < 4; jj++) // save text colors strcpy(savecolor[jj],text[ii].attr.color[jj]); for (jj = 0; jj < 4; jj++) { // get complimentary colors red = 255 - atoi(strField(text[ii].attr.color[jj],'|',1)); green = 255 - atoi(strField(text[ii].attr.color[jj],'|',2)); blue = 255 - atoi(strField(text[ii].attr.color[jj],'|',3)); snprintf(flashcolor,20,"%d|%d|%d",red,green,blue); strcpy(text[ii].attr.color[jj],flashcolor); } gentext(&text[ii].attr); // generate complimentary text image zmainloop(); setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image zsleep(0.05); // hold for a moment for (jj = 0; jj < 4; jj++) // restore text colors strcpy(text[ii].attr.color[jj],savecolor[jj]); gentext(&text[ii].attr); // gemerate normal text image zmainloop(); setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } /********************************************************************************/ // add a line/arrow to image or revise existing line/arrow void mashup::line_edit() { using namespace mashup; cchar *intro = ZTX("Set line properties, [Add] to layout, edit."); /*** _______________________________________________ | Edit Line/Arrow | | | | Set line properties, [Add] to layout, edit. | | | | Line length [____] width [____] | | Arrow head [x] left [x] right | | | | color transp. width angle | | line [_____] [_____] [_____] | | backing [_____] [_____] | | outline [_____] [_____] [_____] | | shadow [_____] [_____] [_____] [_____] | | | | [Add] [Delete] [Done] | |_______________________________________________| ***/ if (! strmatch(focus,"line")) select("",-1); // nothing selected zdline = zdialog_new(ZTX("Edit Line/Arrow"),Mwin,Badd,Bdelete,Bdone,null); zdialog *zd = zdline; zdialog_add_widget(zd,"label","intro","dialog",intro,"space=3"); zdialog_add_widget(zd,"hbox","hbline","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lablength","hbline",ZTX("Line length"),"space=5"); zdialog_add_widget(zd,"zspin","length","hbline","2|9999|1|20"); zdialog_add_widget(zd,"label","space","hbline",0,"space=10"); zdialog_add_widget(zd,"label","labwidth","hbline",Bwidth,"space=5"); zdialog_add_widget(zd,"zspin","width","hbline","1|99|1|2"); zdialog_add_widget(zd,"hbox","hbarrow","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labarrow","hbarrow",ZTX("Arrow head"),"space=5"); zdialog_add_widget(zd,"check","larrow","hbarrow",Bleft); zdialog_add_widget(zd,"label","space","hbarrow",0,"space=10"); zdialog_add_widget(zd,"check","rarrow","hbarrow",Bright); zdialog_add_widget(zd,"hbox","hbcol","dialog"); zdialog_add_widget(zd,"vbox","vbcol1","hbcol",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbcol2","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol3","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol4","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"vbox","vbcol5","hbcol",0,"homog|space=2"); zdialog_add_widget(zd,"label","space","vbcol1"); zdialog_add_widget(zd,"label","labline","vbcol1",ZTX("line")); zdialog_add_widget(zd,"label","labback","vbcol1",ZTX("backing")); zdialog_add_widget(zd,"label","laboutln","vbcol1",ZTX("outline")); zdialog_add_widget(zd,"label","labshadow","vbcol1",ZTX("shadow")); zdialog_add_widget(zd,"label","labcol","vbcol2",Bcolor); zdialog_add_widget(zd,"colorbutt","fgcolor","vbcol2","0|0|0"); zdialog_add_widget(zd,"colorbutt","bgcolor","vbcol2","255|255|255"); zdialog_add_widget(zd,"colorbutt","tocolor","vbcol2","255|0|0"); zdialog_add_widget(zd,"colorbutt","shcolor","vbcol2","0|255|0"); zdialog_add_widget(zd,"label","labcol","vbcol3","Transp."); zdialog_add_widget(zd,"zspin","fgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","bgtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","totransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"zspin","shtransp","vbcol3","0|100|1|0"); zdialog_add_widget(zd,"label","labw","vbcol4",Bwidth); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"label","space","vbcol4"); zdialog_add_widget(zd,"zspin","towidth","vbcol4","0|30|1|0"); zdialog_add_widget(zd,"zspin","shwidth","vbcol4","0|50|1|0"); zdialog_add_widget(zd,"label","labw","vbcol5",Bangle); zdialog_add_widget(zd,"zspin","angle","vbcol5","-180|180|0.1|0"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"label","space","vbcol5"); zdialog_add_widget(zd,"zspin","shangle","vbcol5","-180|180|1|0"); zdialog_add_ttip(zd,Badd,ZTX("add line/arrow to layout")); zdialog_restore_inputs(zd); // restore prior inputs zdialog_run(zd,line_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); zdialog_free(zd); return; } // dialog event and completion callback function int mashup::line_dialog_event(zdialog *zd, cchar *event) { using namespace mashup; int ii; if (strmatch(event,"kill")) { // kill dialog zdialog_free(zd); zdline = 0; return 1; } if (zd->zstat) { if (zd->zstat == 1) { // add zd->zstat = 0; add_line(); // zdialog inputs >> new line/arrow image return 1; } if (zd->zstat == 2) { // delete zd->zstat = 0; if (! strmatch(focus,"line")) return 1; ii = focusii; remove_line(ii); // remove selected line/arrow image return 1; } zdline = 0; // done or [x] kill return 1; } if (strmatch(event,"focus")) { // get mouse ownership takeMouse(mousefunc_layout,0); return 1; } ii = focusii; zdialog_fetch(zd,"length",line[ii].attr.length); zdialog_fetch(zd,"width",line[ii].attr.width); zdialog_fetch(zd,"larrow",line[ii].attr.larrow); zdialog_fetch(zd,"rarrow",line[ii].attr.rarrow); zdialog_fetch(zd,"angle",line[ii].attr.angle); zdialog_fetch(zd,"fgcolor",line[ii].attr.color[0],20); zdialog_fetch(zd,"bgcolor",line[ii].attr.color[1],20); zdialog_fetch(zd,"tocolor",line[ii].attr.color[2],20); zdialog_fetch(zd,"shcolor",line[ii].attr.color[3],20); zdialog_fetch(zd,"fgtransp",line[ii].attr.transp[0]); zdialog_fetch(zd,"bgtransp",line[ii].attr.transp[1]); zdialog_fetch(zd,"totransp",line[ii].attr.transp[2]); zdialog_fetch(zd,"shtransp",line[ii].attr.transp[3]); zdialog_fetch(zd,"towidth",line[ii].attr.towidth); zdialog_fetch(zd,"shwidth",line[ii].attr.shwidth); zdialog_fetch(zd,"shangle",line[ii].attr.shangle); genline(&line[ii].attr); // generate new line/arrow image zmainloop(); line[ii].ww = line[ii].attr.pxb_line->ww; line[ii].hh = line[ii].attr.pxb_line->hh; setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return 1; } // add new line/arrow entry to line[ii] list // get line attributes from zdialog fields void mashup::add_line() { using namespace mashup; int ii, zstat; zdialog *zd = zdline; cchar *tip = ZTX("click position to add line"); if (! zd) return; if (zdaddline) return; if (Nline == maxmash) { zmessageACK(Mwin,ZTX("exceeded %d line entries"),maxmash); return; } Mxclick = Myclick = 0; zdaddline = zdialog_new(ZTX("Add Line"),Mwin,Bdone,Bcancel,null); // get mouse click for line position zdialog_set_decorated(zdaddline,0); zdialog_add_widget(zdaddline,"label","labtip","dialog",tip,"space=3"); zdialog_run(zdaddline,0,"mouse"); zstat = zdialog_wait(zdaddline); zdialog_free(zdaddline); if (zstat != 1) return; if (! (Mxclick + Myclick)) return; ii = Nline++; memset(&line[ii],0,sizeof(line_t)); zdialog_fetch(zd,"length",line[ii].attr.length); zdialog_fetch(zd,"width",line[ii].attr.width); zdialog_fetch(zd,"larrow",line[ii].attr.larrow); zdialog_fetch(zd,"rarrow",line[ii].attr.rarrow); zdialog_fetch(zd,"angle",line[ii].attr.angle); zdialog_fetch(zd,"fgcolor",line[ii].attr.color[0],20); zdialog_fetch(zd,"bgcolor",line[ii].attr.color[1],20); zdialog_fetch(zd,"tocolor",line[ii].attr.color[2],20); zdialog_fetch(zd,"shcolor",line[ii].attr.color[3],20); zdialog_fetch(zd,"fgtransp",line[ii].attr.transp[0]); zdialog_fetch(zd,"bgtransp",line[ii].attr.transp[1]); zdialog_fetch(zd,"totransp",line[ii].attr.transp[2]); zdialog_fetch(zd,"shtransp",line[ii].attr.transp[3]); zdialog_fetch(zd,"towidth",line[ii].attr.towidth); zdialog_fetch(zd,"shwidth",line[ii].attr.shwidth); zdialog_fetch(zd,"shangle",line[ii].attr.shangle); genline(&line[ii].attr); // generate new line/arrow image line[ii].ww = line[ii].attr.pxb_line->ww; line[ii].hh = line[ii].attr.pxb_line->hh; line[ii].px0 = Mxclick; // initial position line[ii].py0 = Myclick; select("line",ii); // set selected line/arrow image setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } // remove line/arrow entry from line[ii] void mashup::remove_line(int ii) { using namespace mashup; select("line",ii); setlayoutupdatearea(); // set layout update area PXB_free(line[ii].attr.pxb_line); for (int jj = ii+1; jj < Nline; jj++) // pack down line list line[jj-1] = line[jj]; Nline--; // line/arrow count select("",-1); // nothing selected Fupdall = 1; Lupdate(); // update layout composite image return; } // flash a selected line/arrow image as bright as possible for a short moment void mashup::flashline() { int ii, jj, red, green, blue; char savecolor[4][20], flashcolor[20]; if (! strmatch(focus,"line")) return; ii = focusii; for (jj = 0; jj < 4; jj++) // save line colors strcpy(savecolor[jj],line[ii].attr.color[jj]); for (jj = 0; jj < 4; jj++) { // get complimentary colors red = 255 - atoi(strField(line[ii].attr.color[jj],'|',1)); green = 255 - atoi(strField(line[ii].attr.color[jj],'|',2)); blue = 255 - atoi(strField(line[ii].attr.color[jj],'|',3)); snprintf(flashcolor,20,"%d|%d|%d",red,green,blue); strcpy(line[ii].attr.color[jj],flashcolor); } genline(&line[ii].attr); // generate complimentary image zmainloop(); setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image zsleep(0.05); // hold for a moment for (jj = 0; jj < 4; jj++) // restore line colors strcpy(line[ii].attr.color[jj],savecolor[jj]); genline(&line[ii].attr); // gemerate normal line image zmainloop(); setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } /********************************************************************************/ // select an image, text image, or line/arrow image - update active dialog // which = "image" or "text" or "line" or "" to select nothing void mashup::select(cchar *which, int ii) { using namespace mashup; char *pp; float rad = 180 / PI; zdialog *zd; if (strmatch(which,"image") && ii >= 0) { if (ii >= Nimage) zappcrash("bad image index"); focus = "image"; // for KB key drags focusii = ii; zd = zdimage; if (! zd) return; // no active edit image dialog pp = strrchr(image[ii].file,'/'); // stuff zdialog fields zdialog_stuff(zd,"currfile",pp+1); // from selected image zdialog_stuff(zd,"scale",image[ii].scale); zdialog_stuff(zd,"angle",image[ii].theta * rad); zdialog_stuff(zd,"Btransp",image[ii].Btransp); zdialog_stuff(zd,"Lmarg",image[ii].Lmarg); zdialog_stuff(zd,"Rmarg",image[ii].Rmarg); zdialog_stuff(zd,"Tmarg",image[ii].Tmarg); zdialog_stuff(zd,"Bmarg",image[ii].Bmarg); zdialog_stuff(zd,"Lblend",image[ii].Lblend); zdialog_stuff(zd,"Rblend",image[ii].Rblend); zdialog_stuff(zd,"Tblend",image[ii].Tblend); zdialog_stuff(zd,"Bblend",image[ii].Bblend); return; } if (strmatch(which,"text") && ii >= 0) { if (ii >= Ntext) zappcrash("bad text index"); focus = "text"; // for KB key drags focusii = ii; zd = zdtext; if (! zd) return; // no active edit text dialog zdialog_stuff(zd,"text",text[ii].attr.text); // stuff zdialog fields zdialog_stuff(zd,"fontname",text[ii].attr.font); // from selected text image zdialog_stuff(zd,"fontsize",text[ii].attr.size); zdialog_stuff(zd,"angle",text[ii].attr.angle); zdialog_stuff(zd,"fgcolor",text[ii].attr.color[0]); zdialog_stuff(zd,"bgcolor",text[ii].attr.color[1]); zdialog_stuff(zd,"tocolor",text[ii].attr.color[2]); zdialog_stuff(zd,"shcolor",text[ii].attr.color[3]); zdialog_stuff(zd,"fgtransp",text[ii].attr.transp[0]); zdialog_stuff(zd,"bgtransp",text[ii].attr.transp[1]); zdialog_stuff(zd,"totransp",text[ii].attr.transp[2]); zdialog_stuff(zd,"shtransp",text[ii].attr.transp[3]); zdialog_stuff(zd,"towidth",text[ii].attr.towidth); zdialog_stuff(zd,"shwidth",text[ii].attr.shwidth); zdialog_stuff(zd,"shangle",text[ii].attr.shangle); return; } if (strmatch(which,"line") && ii >= 0) { if (ii >= Nline) zappcrash("bad line index"); focus = "line"; // for KB key drags focusii = ii; zd = zdline; if (! zd) return; // no active edit line/arrow dialog zdialog_stuff(zd,"length",line[ii].attr.length); zdialog_stuff(zd,"width",line[ii].attr.width); zdialog_stuff(zd,"larrow",line[ii].attr.larrow); zdialog_stuff(zd,"rarrow",line[ii].attr.rarrow); zdialog_stuff(zd,"angle",line[ii].attr.angle); zdialog_stuff(zd,"fgcolor",line[ii].attr.color[0]); zdialog_stuff(zd,"bgcolor",line[ii].attr.color[1]); zdialog_stuff(zd,"tocolor",line[ii].attr.color[2]); zdialog_stuff(zd,"shcolor",line[ii].attr.color[3]); zdialog_stuff(zd,"fgtransp",line[ii].attr.transp[0]); zdialog_stuff(zd,"bgtransp",line[ii].attr.transp[1]); zdialog_stuff(zd,"totransp",line[ii].attr.transp[2]); zdialog_stuff(zd,"shtransp",line[ii].attr.transp[3]); zdialog_stuff(zd,"towidth",line[ii].attr.towidth); zdialog_stuff(zd,"shwidth",line[ii].attr.shwidth); zdialog_stuff(zd,"shangle",line[ii].attr.shangle); return; } focus = ""; // no selection focusii = -1; zd = zdimage; if (! zd) return; zdialog_stuff(zd,"currfile","*********"); // mark obvious no selection return; } // Set the update area for the layout based on // the current selected image, text, or line/arrow. void mashup::setlayoutupdatearea() { using namespace mashup; int pxlo, pxhi, pylo, pyhi; int ii, px, py, px0, py0; int ww, hh, ww2, hh2; float cosT, sinT; Fupdall = 0; if (strmatch(focus,"image")) { ii = focusii; px0 = image[ii].px0; py0 = image[ii].py0; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; cosT = image[ii].cosT; sinT = image[ii].sinT; pxlo = pxhi = px0; // NW corner pylo = pyhi = py0; px = px0 + ww2 * cosT; // NE if (px < pxlo) pxlo = px; if (px > pxhi) pxhi = px; py = py0 + ww2 * sinT; if (py < pylo) pylo = py; if (py > pyhi) pyhi = py; px = px0 + ww2 * cosT - hh2 * sinT; // SE if (px < pxlo) pxlo = px; if (px > pxhi) pxhi = px; py = py0 + hh2 * cosT + ww2 * sinT; if (py < pylo) pylo = py; if (py > pyhi) pyhi = py; px = px0 - hh2 * sinT; // SW if (px < pxlo) pxlo = px; if (px > pxhi) pxhi = px; py = py0 + hh2 * cosT; if (py < pylo) pylo = py; if (py > pyhi) pyhi = py; if (pxlo < 0) pxlo = 0; if (pxhi > Lww) pxhi = Lww; if (pylo < 0) pylo = 0; if (pyhi > Lhh) pyhi = Lhh; updxlo = image[ii].pxlo = pxlo; updxhi = image[ii].pxhi = pxhi; updylo = image[ii].pylo = pylo; updyhi = image[ii].pyhi = pyhi; } else if (strmatch(focus,"text")) { ii = focusii; px0 = text[ii].px0; py0 = text[ii].py0; ww = text[ii].ww; hh = text[ii].hh; updxlo = text[ii].pxlo = px0; // set update region updxhi = text[ii].pxhi = px0 + ww; updylo = text[ii].pylo = py0; updyhi = text[ii].pyhi = py0 + hh; } else if (strmatch(focus,"line")) { ii = focusii; px0 = line[ii].px0; py0 = line[ii].py0; ww = line[ii].ww; hh = line[ii].hh; updxlo = line[ii].pxlo = px0; // set update region updxhi = line[ii].pxhi = px0 + ww; updylo = line[ii].pylo = py0; updyhi = line[ii].pyhi = py0 + hh; } return; } // mouse function - called for mouse events inside layout image // move and resize the overlay images, text images, line/arrow images void mashup::mousefunc_layout() { using namespace mashup; static int mdx0 = 0, mdy0 = 0, mdx1, mdy1; static int Mcen, Mcor, Tcen, Tcor, Lcen, Lcor; // mouse at image/text/line center/corner static int iiMcen, iiTcen, iiLcen, iiMcor, iiTcor, iiLcor; // selected image/text/line center/corner static int Tcorx, Tcory; int cenx, ceny; int minMcen, minMcor, minTcen, minTcor, minLcen, minLcor; int ii, Fflash, Fmove, Fnew; int mx, my, mx0, my0; int ww, hh, ww2, hh2; int pxlo, pxhi, pylo, pyhi; int length; float dist, distx, disty, Cdist, Mdist, Mdist0; float expand, contract; float scale, thresh, size; float px0, py0, px1, py1, px2, py2, px3, py3; float tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3; float Vcx, Vcy, Vmx, Vmy; float sinT, cosT; float leng2, amx, amy, d1, d2, angle, rad; if (LMclick) { // left click LMclick = 0; Fnextimage = 0; mx0 = mx = Mxclick; // mouse position my0 = my = Myclick; Fnew = 1; // new image or text selected Fmove = 0; Fflash = 1; select("",-1); // unselect prior } else if (Mxdrag || Mydrag) // drag underway { Fnew = 0; // assume same image/text as before Fmove = 1; // is being moved Fflash = 0; if (Mxdown != mdx0 || Mydown != mdy0) { // new drag was initiated mdx0 = mdx1 = Mxdown; mdy0 = mdy1 = Mydown; Fnew = 1; // new image or text selected } mx0 = mdx1; // drag start my0 = mdy1; mx = Mxdrag; // drag position my = Mydrag; mdx1 = mx; // next drag start mdy1 = my; if (mx == mx0 && my == my0) Fmove = 0; // no mouse movement Mxdrag = Mydrag = 0; // stop main window drag after return } else return; // ignore simple mouse movement if (Fnextimage) { // image set from [next] button Fnextimage = 0; Fnew = 0; Mcen = 1; iiMcen = focusii; } if (zdaddtext && (Mxclick + Myclick)) { // return clicked position zdaddtext->zstat = 1; zdialog_destroy(zdaddtext); return; } if (zdaddline && (Mxclick + Myclick)) { // return clicked position zdaddline->zstat = 1; zdialog_destroy(zdaddline); return; } thresh = 50 / Mscale; // corner match threshhold, pixels if (Fnew) // search all images and text images { minMcen = 9999; // find closest image center to mouse for (ii = 0; ii < Nimage; ii++) { px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout image space sinT = image[ii].sinT; cosT = image[ii].cosT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px2 = (mx-px0) * cosT + (my-py0) * sinT; // image pixel for layout position py2 = (my-py0) * cosT - (mx-px0) * sinT; if (px2 < 0 || px2 > ww2-1) continue; // mouse not within image if (py2 < 0 || py2 > hh2-1) continue; pxlo = image[ii].pxlo; // image extent pxhi = image[ii].pxhi; pylo = image[ii].pylo; pyhi = image[ii].pyhi; cenx = (pxlo + pxhi) / 2; // image center ceny = (pylo + pyhi) / 2; distx = mx - cenx; disty = my - ceny; dist = sqrtf(distx * distx + disty * disty); // mouse/center distance if (dist < minMcen) { minMcen = dist; iiMcen = ii; // save closest image center } } minMcor = 9999; // find closest image corner to mouse for (ii = 0; ii < Nimage; ii++) { px0 = image[ii].px0; // image position and rotation py0 = image[ii].py0; // in layout image space sinT = image[ii].sinT; cosT = image[ii].cosT; ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; px2 = (mx-px0) * cosT + (my-py0) * sinT; // image pixel for layout position py2 = (my-py0) * cosT - (mx-px0) * sinT; if (px2 < 0 || px2 > ww2-1) continue; // mouse not within image if (py2 < 0 || py2 > hh2-1) continue; px0 = image[ii].px0; // NW corner py0 = image[ii].py0; distx = mx - px0; disty = my - py0; dist = sqrtf(distx * distx + disty * disty); // mouse/corner distance if (dist < minMcor && dist < thresh) { minMcor = dist; iiMcor = ii; // save closest image corner } px1 = px0 + ww2 * cosT; // NE py1 = py0 + ww2 * sinT; distx = mx - px1; disty = my - py1; dist = sqrtf(distx * distx + disty * disty); if (dist < minMcor && dist < thresh) { minMcor = dist; iiMcor = ii; } px2 = px0 + ww2 * cosT - hh2 * sinT; // SE py2 = py0 + hh2 * cosT + ww2 * sinT; distx = mx - px2; disty = my - py2; dist = sqrtf(distx * distx + disty * disty); if (dist < minMcor && dist < thresh) { minMcor = dist; iiMcor = ii; } px3 = px0 - hh2 * sinT; // SW py3 = py0 + hh2 * cosT; distx = mx - px3; disty = my - py3; dist = sqrtf(distx * distx + disty * disty); if (dist < minMcor && dist < thresh) { minMcor = dist; iiMcor = ii; } } minTcen = 9999; // find closest text image center to mouse for (ii = 0; ii < Ntext; ii++) { pxlo = text[ii].pxlo; // image extent pxhi = text[ii].pxhi; pylo = text[ii].pylo; pyhi = text[ii].pyhi; if (mx < pxlo || mx > pxhi) continue; // mouse not within text image if (my < pylo || my > pyhi) continue; cenx = (pxlo + pxhi) / 2; // text image center ceny = (pylo + pyhi) / 2; distx = mx - cenx; disty = my - ceny; dist = sqrtf(distx * distx + disty * disty); // mouse/center distance if (dist < minTcen) { minTcen = dist; iiTcen = ii; } } minTcor = 9999; // find closest text image corner to mouse Tcorx = Tcory = 0; for (ii = 0; ii < Ntext; ii++) { pxlo = text[ii].pxlo; // text image extent pxhi = text[ii].pxhi; pylo = text[ii].pylo; pyhi = text[ii].pyhi; if (mx < pxlo || mx > pxhi) continue; // mouse not within text image if (my < pylo || my > pyhi) continue; ww = text[ii].attr.tww; // unrotated text rectangle size hh = text[ii].attr.thh; sinT = text[ii].attr.sinT; // angle of text cosT = text[ii].attr.cosT; px0 = text[ii].px0; // NW corner of enclosing rectangle py0 = text[ii].py0; if (text[ii].attr.angle > 0) { tx0 = px0 + hh * sinT; // NW corner of text rectangle ty0 = py0; tx1 = px0 + ww * cosT + hh * sinT; // NE ty1 = py0 + ww * sinT; tx2 = px0 + ww * cosT; // SE ty2 = py0 + ww * sinT + hh * cosT; tx3 = px0; // SW ty3 = py0 + hh * cosT; } else { sinT = -sinT; tx0 = px0; // NW ty0 = py0 + ww * sinT; tx1 = px0 + ww * cosT; // NE ty1 = py0; tx2 = px0 + ww * cosT + hh * sinT; // SE ty2 = py0 + hh * cosT; tx3 = px0 + hh * sinT; // SW ty3 = py0 + ww * sinT + hh * cosT; } distx = mx - tx0; // NW corner disty = my - ty0; dist = sqrtf(distx * distx + disty * disty); // mouse/corner distance if (dist < minTcor && dist < thresh) { minTcor = dist; iiTcor = ii; Tcorx = tx0; // save closest text image corner Tcory = ty0; } distx = mx - tx1; // NE disty = my - ty1; dist = sqrtf(distx * distx + disty * disty); if (dist < minTcor && dist < thresh) { minTcor = dist; iiTcor = ii; Tcorx = tx1; Tcory = ty1; } distx = mx - tx2; // SE disty = my - ty2; dist = sqrtf(distx * distx + disty * disty); if (dist < minTcor && dist < thresh) { minTcor = dist; iiTcor = ii; Tcorx = tx2; Tcory = ty2; } distx = mx - tx3; // SW disty = my - ty3; dist = sqrtf(distx * distx + disty * disty); if (dist < minTcor && dist < thresh) { minTcor = dist; iiTcor = ii; Tcorx = tx3; Tcory = ty3; } } minLcen = 9999; // find closest line/arrow image // center to mouse position for (ii = 0; ii < Nline; ii++) { pxlo = line[ii].pxlo; // image extent pxhi = line[ii].pxhi; pylo = line[ii].pylo; pyhi = line[ii].pyhi; if (mx < pxlo || mx > pxhi) continue; // mouse not within line image if (my < pylo || my > pyhi) continue; cenx = (pxlo + pxhi) / 2; // line image center ceny = (pylo + pyhi) / 2; distx = mx - cenx; disty = my - ceny; dist = sqrtf(distx * distx + disty * disty); // mouse/center distance if (dist < minLcen) { minLcen = dist; iiLcen = ii; } } minLcor = 9999; // find closest line end point to mouse for (ii = 0; ii < Nline; ii++) { pxlo = line[ii].pxlo; // line image extent pxhi = line[ii].pxhi; pylo = line[ii].pylo; pyhi = line[ii].pyhi; if (mx < pxlo || mx > pxhi) continue; // mouse not within line image if (my < pylo || my > pyhi) continue; angle = line[ii].attr.angle; rad = -angle / 57.296; length = line[ii].attr.length; leng2 = length / 2.0; ww2 = line[ii].attr.pxb_line->ww; // line image hh2 = line[ii].attr.pxb_line->hh; amx = ww2 / 2.0; // line midpoint within line image amy = hh2 / 2.0; px1 = amx - leng2 * cosf(rad) + 0.5; // line end points py1 = amy + leng2 * sinf(rad) + 0.5; px2 = amx + leng2 * cosf(rad) + 0.5; py2 = amy - leng2 * sinf(rad) + 0.5; d1 = (mx-px1-pxlo) * (mx-px1-pxlo) + (my-py1-pylo) * (my-py1-pylo); d2 = (mx-px2-pxlo) * (mx-px2-pxlo) + (my-py2-pylo) * (my-py2-pylo); d1 = sqrtf(d1); // mouse - end point distance d2 = sqrtf(d2); if (d1 < minLcor && d1 < thresh) { minLcor = d1; iiLcor = ii; } if (d2 < minLcor && d2 < thresh) { minLcor = d2; iiLcor = ii; } } Mcen = Mcor = Tcen = Tcor = Lcen = Lcor = 0; // mark where mouse is acting: if (Fmove) // recognize drags from anywhere { if (minMcen < minMcor && minMcen < minTcen && minMcen < minTcor && minMcen < minLcen && minMcen < minLcor) Mcen = 1; if (minMcor < minMcen && minMcor < minTcen && minMcor < minTcor && minMcor < minLcen && minMcor < minLcor) Mcor = 1; if (minTcen < minMcen && minTcen < minMcor && minTcen < minTcor && minTcen < minLcen && minTcen < minLcor) Tcen = 1; if (minTcor < minMcen && minTcor < minMcor && minTcor < minTcen && minTcor < minLcen && minTcor < minLcor) Tcor = 1; if (minLcen < minMcen && minLcen < minMcor && minLcen < minTcen && minLcen < minTcor && minLcen < minLcor) Lcen = 1; if (minLcor < minMcen && minLcor < minMcor && minLcor < minTcen && minLcor < minTcor && minLcor < minLcen) Lcor = 1; } else // recognize clicks near center only { if (minMcen < minTcen && minMcen < minLcen) Mcen = 1; if (minTcen < minMcen && minTcen < minLcen) Tcen = 1; if (minLcen < minMcen && minLcen < minTcen) Lcen = 1; } } if (Mcen) { ii = iiMcen; select("image",ii); } // select the corresp. image, text or line if (Mcor) { ii = iiMcor; select("image",ii); } if (Tcen) { ii = iiTcen; select("text",ii); } if (Tcor) { ii = iiTcor; select("text",ii); } if (Lcen) { ii = iiLcen; select("line",ii); } if (Lcor) { ii = iiLcor; select("line",ii); } if (Fflash) { if (Mcen) flashimage(); // if click on image, flash it if (Tcen) flashtext(); // same for text if (Lcen) flashline(); // same for line return; } if (Fmove) { if (Mcen) // move image { ii = iiMcen; if (mx0 < image[ii].pxlo || mx0 > image[ii].pxhi) return; // mouse outside image bounds if (my0 < image[ii].pylo || my0 > image[ii].pyhi) return; select("image",ii); image[ii].px0 += (mx - mx0); // move selected image image[ii].py0 += (my - my0); // by mouse drag amount } if (Mcor) // resize image by dragging a corner { ii = iiMcor; if (mx0 < image[ii].pxlo || mx0 > image[ii].pxhi) return; // mouse outside image bounds if (my0 < image[ii].pylo || my0 > image[ii].pyhi) return; select("image",ii); pxlo = image[ii].pxlo; pxhi = image[ii].pxhi; pylo = image[ii].pylo; pyhi = image[ii].pyhi; cenx = (pxlo + pxhi) / 2; // image center ceny = (pylo + pyhi) / 2; distx = mx0 - cenx; disty = my0 - ceny; Mdist0 = sqrtf(distx * distx + disty * disty); // center - mouse drag start distx = mx - cenx; disty = my - ceny; Mdist = sqrtf(distx * distx + disty * disty); // center - mouse drag end scale = image[ii].scale * 0.5 * (Mdist + Mdist0) / Mdist0; // image scale change image_rescale(ii,scale); } if (Tcen) // move text image { ii = iiTcen; if (mx0 < text[ii].pxlo || mx0 > text[ii].pxhi) return; // mouse outside text bounds if (my0 < text[ii].pylo || my0 > text[ii].pyhi) return; select("text",ii); text[ii].px0 += (mx - mx0); // move selected text image text[ii].py0 += (my - my0); // by mouse drag amount } if (Tcor) // resize text image { ii = iiTcor; if (mx0 < text[ii].pxlo || mx0 > text[ii].pxhi) return; // mouse outside text bounds if (my0 < text[ii].pylo || my0 > text[ii].pyhi) return; select("text",ii); pxlo = text[ii].pxlo; pxhi = text[ii].pxhi; pylo = text[ii].pylo; pyhi = text[ii].pyhi; cenx = (pxlo + pxhi) / 2; // text image center ceny = (pylo + pyhi) / 2; distx = Tcorx - cenx; disty = Tcory - ceny; Cdist = sqrtf(distx * distx + disty * disty); Vcx = distx / Cdist; // unit vector, text image center to corner Vcy = disty / Cdist; distx = mx - mx0; disty = my - my0; Mdist = sqrtf(distx * distx + disty * disty); Vmx = distx / Mdist; // unit vector, mouse movement Vmy = disty / Mdist; expand = fabsf(Vcx + Vmx) + fabsf(Vcy + Vmy); // mouse pull-out amount contract = fabsf(Vcx - Vmx) + fabsf(Vcy - Vmy); // push-in amount size = expand - contract; // change in text size if (size > 0.5) text[ii].attr.size++; if (size < -0.5) text[ii].attr.size--; gentext(&text[ii].attr); // generate new text image text[ii].ww = text[ii].attr.pxb_text->ww; text[ii].hh = text[ii].attr.pxb_text->hh; } if (Lcen) // move line/arrow image { ii = iiLcen; if (mx0 < line[ii].pxlo || mx0 > line[ii].pxhi) return; // mouse outside line bounds if (my0 < line[ii].pylo || my0 > line[ii].pyhi) return; select("line",ii); line[ii].px0 += (mx - mx0); // move selected line image line[ii].py0 += (my - my0); // by mouse drag amount } if (Lcor) // drag line end point { ii = iiLcor; if (mx0 < line[ii].pxlo || mx0 > line[ii].pxhi) return; // mouse outside line bounds if (my0 < line[ii].pylo || my0 > line[ii].pyhi) return; select("line",ii); angle = line[ii].attr.angle; rad = -angle / 57.296; length = line[ii].attr.length; leng2 = length / 2.0; ww2 = line[ii].attr.pxb_line->ww; // line image hh2 = line[ii].attr.pxb_line->hh; amx = ww2 / 2.0; // line midpoint within line image amy = hh2 / 2.0; px1 = amx - leng2 * cosf(rad) + 0.5; // line end points py1 = amy + leng2 * sinf(rad) + 0.5; px2 = amx + leng2 * cosf(rad) + 0.5; py2 = amy - leng2 * sinf(rad) + 0.5; pxlo = line[ii].pxlo; pylo = line[ii].pylo; d1 = (mx-px1-pxlo) * (mx-px1-pxlo) + (my-py1-pylo) * (my-py1-pylo); d2 = (mx-px2-pxlo) * (mx-px2-pxlo) + (my-py2-pylo) * (my-py2-pylo); d1 = sqrtf(d1); // mouse - end point distance d2 = sqrtf(d2); if (d1 < d2) { // move px1/py1 end to mouse px2 += pxlo; py2 += pylo; px1 = mx; py1 = my; length = d2 + 0.5; rad = asinf((py1-py2) / d2); angle = -57.296 * rad; if (mx > px2) angle = -180 - angle; } if (d2 < d1) { // move px2/py2 end to mouse px1 += pxlo; py1 += pylo; px2 = mx; py2 = my; length = d1 + 0.5; rad = asinf((py1-py2) / d1); angle = -57.296 * rad; if (mx < px1) angle = -180 - angle; } if (angle < -180) angle += 360; if (angle > 180) angle -= 360; line[ii].attr.angle = angle; line[ii].attr.length = length; genline(&line[ii].attr); line[ii].ww = line[ii].attr.pxb_line->ww; line[ii].hh = line[ii].attr.pxb_line->hh; ww2 = line[ii].attr.pxb_line->ww; hh2 = line[ii].attr.pxb_line->hh; amx = (px1 + px2) / 2.0; amy = (py1 + py2) / 2.0; pxlo = amx - ww2 / 2.0; pylo = amy - hh2 / 2.0; line[ii].px0 = pxlo; line[ii].py0 = pylo; } } setlayoutupdatearea(); Lupdate(); // update layout composite image return; } // Keyboard function // KB arrow keys act like mouse drag of 1 pixel for current image void mashup::KBfunc(int key) { using namespace mashup; int ii, xstep, ystep; xstep = ystep = 0; if (key == GDK_KEY_Left) xstep = -1; if (key == GDK_KEY_Right) xstep = +1; if (key == GDK_KEY_Up) ystep = -1; if (key == GDK_KEY_Down) ystep = +1; if (strmatch(focus,"image")) // choose last selected image { ii = focusii; image[ii].px0 += xstep; // move image 1 pixel image[ii].py0 += ystep; } else if (strmatch(focus,"text")) // last selected text image { ii = focusii; text[ii].px0 += xstep; // move selected text image 1 pixel text[ii].py0 += ystep; } else if (strmatch(focus,"line")) // last selected line/arrow image { ii = focusii; line[ii].px0 += xstep; // move selected line image 1 pixel line[ii].py0 += ystep; } else return; setlayoutupdatearea(); // set layout update area Lupdate(); // update layout composite image return; } // update layout image Fpxb from layout + overlay images // paint main window void mashup::Lupdate() { using namespace mashup; int nupdxlo, nupdxhi, nupdylo, nupdyhi; static int pupdxlo, pupdxhi, pupdylo, pupdyhi; // prior image update region static cchar *pfocus = "x"; static int pfocusii = -2; // no prior selected image or text int ww, hh; if (! Lpxb || ! Fpxb) return; if (! strmatch(focus,pfocus) || focusii != pfocusii) // focus image/text/line has changed Fupdall = 1; // update entire layout pfocus = focus; // remember focus image/text/line pfocusii = focusii; if (updxlo < 0) updxlo = 0; // keep within layout image if (updxhi > Lww) updxhi = Lww; if (updylo < 0) updylo = 0; if (updyhi > Lhh) updyhi = Lhh; nupdxlo = updxlo; // save update region nupdxhi = updxhi; nupdylo = updylo; nupdyhi = updyhi; if (! Fupdall) { if (pupdxlo < updxlo) updxlo = pupdxlo; // expand update region to if (pupdxhi > updxhi) updxhi = pupdxhi; // include prior update region if (pupdylo < updylo) updylo = pupdylo; if (pupdyhi > updyhi) updyhi = pupdyhi; } pupdxlo = nupdxlo; // save update region pupdxhi = nupdxhi; pupdylo = nupdylo; pupdyhi = nupdyhi; if (Fupdall) { // update entire layout updxlo = updylo = 0; updxhi = Lww; updyhi = Lhh; } Fupdatebusy = 1; start_detached_thread(Lupdate_thread,0); // trigger image update thread while (Fupdatebusy) zsleep(0.01); if (Fupdall) Fpaintnow(); // paint entire layout else { ww = updxhi-updxlo; hh = updyhi-updylo; PXB_PXB_update(Fpxb,Mpxb,updxlo,updylo,ww,hh); // Fpxb > Mpxb, scaled up or down Fpaint4(updxlo,updylo,ww,hh,0); // update drawing window from Mpxb } Fupdall = 0; return; } void * mashup::Lupdate_thread(void *) // main thread { using namespace mashup; if (! Lpxb || ! Fpxb) { Fupdatebusy = 0; pthread_exit(0); } for (int ii = 0; ii < NWT; ii++) // start worker threads for images start_wthread(Lupdate_wthread,&Nval[ii]); wait_wthreads(1); // wait for completion Fupdatebusy = 0; pthread_exit(0); } void * mashup::Lupdate_wthread(void *arg) // worker thread for images { using namespace mashup; int index = *((int *) (arg)); int ii, jj, px1, py1, ww, hh, qx, qy; uint8 *pix1, *pix2, *pix3, vpix2[4]; float red, green, blue; float Bopac, Iopac, Vtran; float px0, py0, px2, py2; float ww1, hh1, ww2, hh2; float edgedist, margin, blendist; float scale, sinT, cosT; uint8 *vtranmap; PXB *pxb2; float e3part; for (py1 = updylo+index; py1 < updyhi; py1 += NWT) // loop layout image pixels for (px1 = updxlo; px1 < updxhi; px1++) // within update region { pix1 = PXBpix(Lpxb,px1,py1); // start with layout pixel red = pix1[0]; green = pix1[1]; blue = pix1[2]; for (ii = 0; ii < Nimage; ii++) // loop overlay images from bottom to top { if (image[ii].pxhi < updxlo) continue; // no overlap with update region if (image[ii].pxlo > updxhi) continue; if (image[ii].pyhi < updylo) continue; if (image[ii].pylo > updyhi) continue; px0 = image[ii].px0; // overlay image position and rotation py0 = image[ii].py0; // in layout image space ww2 = image[ii].ww2; // image size at scale hh2 = image[ii].hh2; if (image[ii].theta == 0) { px2 = px1 - px0; // overlay image pixel for layout pixel py2 = py1 - py0; } else { sinT = image[ii].sinT; cosT = image[ii].cosT; px2 = (px1-px0) * cosT + (py1-py0) * sinT; // overlay image pixel for layout pixel py2 = (py1-py0) * cosT - (px1-px0) * sinT; } if (px2 < 0 || px2 > ww2-1) continue; // layout pixel not in this image if (py2 < 0 || py2 > hh2-1) continue; pxb2 = image[ii].pxb2; // overlay image virt. pixel if (! vpixel(pxb2,px2,py2,vpix2)) continue; // beyond the edge Bopac = 1.0 - image[ii].Btransp; // image base opacity 0...1 Iopac = vpix2[3] / 255.0; // alpha channel opacity 0...1 Iopac = Iopac * Bopac; // combined vtranmap = image[ii].vtranmap; if (vtranmap) // var. transparency (px2,py2) { ww1 = image[ii].ww1; // image size at 1x scale hh1 = image[ii].hh1; qx = px2 / image[ii].scale; // position in 1x image qy = py2 / image[ii].scale; if (qx < ww1 && qy < hh1) { // position is within image jj = qy * ww1 + qx; Vtran = vtranmap[jj] / 255.0; // var. transparency 0...1 Iopac = Iopac * (1 - Vtran); } } scale = image[ii].scale; if (image[ii].Lmarg || image[ii].Lblend) { // left edge hard or blend margin edgedist = px2 / scale; margin = image[ii].Lmarg; if (edgedist <= margin) Iopac = 0; else { edgedist -= margin; blendist = image[ii].Lblend; if (edgedist < blendist) Iopac = Iopac * edgedist / blendist; } } if (image[ii].Rmarg || image[ii].Rblend) { // right edge edgedist = (ww2 - px2) / scale; margin = image[ii].Rmarg; if (edgedist <= margin) Iopac = 0; else { edgedist -= margin; blendist = image[ii].Rblend; if (edgedist < blendist) Iopac = Iopac * edgedist / blendist; } } if (image[ii].Tmarg || image[ii].Tblend) { // top edge edgedist = py2 / scale; margin = image[ii].Tmarg; if (edgedist <= margin) Iopac = 0; else { edgedist -= margin; blendist = image[ii].Tblend; if (edgedist < blendist) Iopac = Iopac * edgedist / blendist; } } if (image[ii].Bmarg || image[ii].Bblend) { // bottom edge edgedist = (hh2 - py2) / scale; margin = image[ii].Bmarg; if (edgedist <= margin) Iopac = 0; else { edgedist -= margin; blendist = image[ii].Bblend; if (edgedist < blendist) Iopac = Iopac * edgedist / blendist; } } red = Iopac * vpix2[0] + (1 - Iopac) * red; // add image pixel over prior green = Iopac * vpix2[1] + (1 - Iopac) * green; blue = Iopac * vpix2[2] + (1 - Iopac) * blue; } for (ii = 0; ii < Ntext; ii++) // loop overlay text images { if (text[ii].pxhi < updxlo) continue; // no overlap with update region if (text[ii].pxlo > updxhi) continue; if (text[ii].pyhi < updylo) continue; if (text[ii].pylo > updyhi) continue; px0 = text[ii].px0; // text image position in layout space py0 = text[ii].py0; ww = text[ii].ww; // text image size in layout hh = text[ii].hh; qx = px1 - px0; // text image pixel for layout pixel qy = py1 - py0; if (qx < 0 || qx > ww-1) continue; // layout pixel not in this text image if (qy < 0 || qy > hh-1) continue; pxb2 = text[ii].attr.pxb_text; pix2 = PXBpix(pxb2,qx,qy); e3part = pix2[3] / 255.0; red = pix2[0] + e3part * red; green = pix2[1] + e3part * green; blue = pix2[2] + e3part * blue; } for (ii = 0; ii < Nline; ii++) // loop overlay line/arrow images { if (line[ii].pxhi < updxlo) continue; // no overlap with update region if (line[ii].pxlo > updxhi) continue; if (line[ii].pyhi < updylo) continue; if (line[ii].pylo > updyhi) continue; px0 = line[ii].px0; // line image position in layout space py0 = line[ii].py0; ww = line[ii].ww; // line image size in layout hh = line[ii].hh; qx = px1 - px0; // line image pixel for layout pixel qy = py1 - py0; if (qx < 0 || qx > ww-1) continue; // layout pixel not in this line image if (qy < 0 || qy > hh-1) continue; pxb2 = line[ii].attr.pxb_line; pix2 = PXBpix(pxb2,qx,qy); e3part = pix2[3] / 255.0; red = pix2[0] + e3part * red; green = pix2[1] + e3part * green; blue = pix2[2] + e3part * blue; } pix3 = PXBpix(Fpxb,px1,py1); // output image pixel pix3[0] = red; pix3[1] = green; pix3[2] = blue; } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } /******************************************************************************** MONTAGE Make an image of selected images, arranged in a compact matrix format. *********************************************************************************/ namespace montage_names { int Fww; // frame image width (input) int Fhh; // frame image height (calculated) int Fmarg; // frame margin size (input) int Ncols; // image columns in frame (input) int Nrows; // image rows in frame (calculated) int Imarg; // image margin size (input) int Iww; // image width (calculated, fixed) typedef struct { int random; // random number for sorting int Ihh; // image height (calculated, variable) int Ipx, Ipy; // image position in frame image int row, col; // image row and column number char *file; // image filename PIXBUF *pixbuf; // scaled image pixbuf } image_t; #define maxNm 1000 // max. images in montage image_t image[maxNm]; // image data int Nimages; // actual image count int maxNc = 100; // max. columns int maxNr = 100; // max. rows int colH[100]; // column heights char uniquename[100]; // unique montage file name char montagepath[500]; // suggested directory to save file } // menu function void m_montage(GtkWidget *, const char *) // 17.04 { using namespace montage_names; int montage_dialog_event(zdialog* zd, const char *event); int montage_mapimages(); int montage_showframe(); int montage_sort(); int montage_shuffle(); int montage_spread(); int montage_mapfile(); PIXBUF *pixbuf, *pixbuf2; GError *gerror = 0; int ii, nn, err, zstat, ww, hh, yn, makemap; int coldiff, coldiffB, coldiff2; image_t imageB[maxNm]; char text[100], *filename, *pp, *pp1, *pp2; float R; F1_help_topic = "montage"; if (checkpend("all")) return; Fblock = 1; Nimages = 0; /*** ______________________________________ | Image Montage | | | | [Select Files] NN files selected | | Frame Width [___] Margin [___] | | Image Columns [___] Margin [___] | | | | [Proceed] [Cancel] | |______________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Image Montage"),Mwin,Bproceed,Bcancel,null); // [Select Files] NN files selected zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=3"); zdialog_add_widget(zd,"button","files","hbf",Bselectfiles,"space=5"); zdialog_add_widget(zd,"label","labfcount","hbf",Bnofileselected,"space=10"); // Frame Width [___] Margin [___] zdialog_add_widget(zd,"hbox","hbf","dialog"); zdialog_add_widget(zd,"label","labfw","hbf",ZTX("Frame Width"),"space=3"); zdialog_add_widget(zd,"zspin","Fww","hbf","1000|30000|1|2000","space=3"); // 18.01 zdialog_add_widget(zd,"label","space","hbf",0,"space=5"); zdialog_add_widget(zd,"label","labfm","hbf",ZTX("Margin"),"space=3"); zdialog_add_widget(zd,"zspin","Fmarg","hbf","0|100|1|30","space=3"); // Image Columns [___] Margin [___] zdialog_add_widget(zd,"hbox","hbi","dialog"); zdialog_add_widget(zd,"label","labir","hbi",ZTX("Image Columns"),"space=3"); zdialog_add_widget(zd,"zspin","Ncols","hbi","1|100|1|4","space=3"); // max 100 columns zdialog_add_widget(zd,"label","space","hbi",0,"space=5"); zdialog_add_widget(zd,"label","labim","hbi",ZTX("Margin"),"space=3"); zdialog_add_widget(zd,"zspin","Imarg","hbi","0|50|1|20","space=3"); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,montage_dialog_event,"save"); // run dialog - parallel zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) goto cleanup; zdialog_free(zd); zd = 0; Nrows = 1 + (GScount - 1) / Ncols; // required image rows if (Nrows > maxNr) { zmessageACK(Mwin,ZTX("exceed %d rows"),maxNr); goto cleanup; } if (GScount < Ncols) Ncols = GScount; // reduce if fewer images Iww = Fww - 2 * Fmarg - (Ncols - 1) * Imarg; // frame width less margins Iww = Iww / Ncols; // scaled image width, all images zmainloop(); Ffuncbusy = 1; Fbusy_goal = GScount; Fbusy_done = 0; for (nn = ii = 0; ii < GScount; ii++) // loop all images { zmainloop(); image[nn].file = 0; image[nn].Ihh = 0; image[nn].Ipx = 0; image[nn].Ipy = 0; image[nn].row = 0; image[nn].col = 0; image[nn].pixbuf = 0; pixbuf = gdk_pixbuf_new_from_file(GSfiles[ii],&gerror); // load pixbuf image of file if (! pixbuf) { zmessageACK(Mwin,"cannot read: %s",GSfiles[ii]); Fbusy_done++; continue; } if (gdk_pixbuf_get_has_alpha(pixbuf)) { // if alpha channel present, pixbuf2 = gdk_pixbuf_stripalpha(pixbuf); // remove it g_object_unref(pixbuf); pixbuf = pixbuf2; } image[nn].file = zstrdup(GSfiles[ii]); // image file name ww = gdk_pixbuf_get_width(pixbuf); // image dimensions hh = gdk_pixbuf_get_height(pixbuf); R = 1.0 * Iww / ww; // image scale ratio image[nn].Ihh = R * hh; // scaled image height image[nn].row = nn / Ncols; // initial image row image[nn].col = nn - Ncols * image[nn].row; // initial image column pixbuf2 = gdk_pixbuf_scale_simple(pixbuf,Iww,image[nn].Ihh,BILINEAR); image[nn].pixbuf = pixbuf2; g_object_unref(pixbuf); nn++; // accumulate image count Fbusy_done++; } Ffuncbusy = 0; Fbusy_goal = 0; Nimages = nn; // final image count if (! Nimages) { zmessageACK(Mwin,"no images were found"); goto cleanup; } Nrows = 1 + (Nimages - 1) / Ncols; // final row count ii = Nimages - 1; // get last selected image strncpy0(montagepath,image[ii].file,500); pp = strrchr(montagepath,'/'); // use this directory as default if (pp) *pp = 0; // montage file output location coldiff = montage_mapimages(); // map scaled images into frame image err = montage_showframe(); // show completed frame image if (err) goto optdone; for (ii = 0; ii < Nimages; ii++) // best map = initial map imageB[ii] = image[ii]; coldiffB = coldiff; if (coldiff < 3) { zstat = 2; goto optdone; } /*** ___________________________________ | Optimize | | | | column difference: NNN pixels | | | | [Start] [Stop] [Cancel] | |___________________________________| ***/ zd = zdialog_new(ZTX("Optimize"),Mwin,Bstart,Bstop,Bcancel,null); zdialog_add_widget(zd,"label","labdiff","dialog","stuffed"); zdialog_set_modal(zd); // 17.08 zdialog_run(zd,null,"save"); // run dialog zstat = 0; // status = not started while (true) // optimization loop { snprintf(text,100,ZTX("column difference: %d pixels"),coldiffB); // show best column diff. so far zdialog_stuff(zd,"labdiff",text); zmainloop(); if (zd->zstat) { // dialog status change zstat = zd->zstat; // new status zd->zstat = 0; // keep dialog active } if (zstat == 0) { // not started zsleep(0.1); continue; // wait for start } if (zstat < 0 || zstat >= 2) goto optdone; // [x] or [done] or [cancel] Ffuncbusy = 1; // [start] or continue while (true) { coldiff2 = coldiff; // shuffle() until no improvement montage_shuffle(); coldiff = montage_mapimages(); if (coldiff >= coldiff2) break; } if (coldiff < coldiffB) { for (ii = 0; ii < Nimages; ii++) // save new best map imageB[ii] = image[ii]; coldiffB = coldiff; err = montage_showframe(); // show completed frame image if (err) goto optdone; } if (coldiff < 3) { // stop if column diff. is tiny zstat = 2; goto optdone; } montage_sort(); // re-sort all images coldiff = montage_mapimages(); // compute new column difference } optdone: Ffuncbusy = 0; if (zstat != 2) goto cleanup; // quit if [cancel] or [x] zdialog_free(zd); zd = 0; for (ii = 0; ii < Nimages; ii++) // restore best result image[ii] = imageB[ii]; coldiff = montage_mapimages(); if (coldiff > 1) { yn = zmessageYN(Mwin,ZTX("column difference: %d pixels \n" "Make columns even?"),coldiff); if (yn) { montage_mapimages(); montage_spread(); montage_showframe(); } } /*** ___________________________________ | Save with unique montage name | | | | unique name: [__________________] | | | | [x] create image map | | | | [save] [cancel] | |___________________________________| ***/ zd = zdialog_new(ZTX("Save with unique montage name"),Mwin,Bsave,Bcancel,null); zdialog_add_widget(zd,"hbox","hbname","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labname","hbname",ZTX("unique name:"),"space=5"); zdialog_add_widget(zd,"zentry","uniquename","hbname","","size=30"); zdialog_add_widget(zd,"check","createmap","dialog",ZTX("create image map"),"space=10"); zdialog_run(zd,null,"save"); while (true) { zstat = zdialog_wait(zd); if (zstat != 1) break; zdialog_fetch(zd,"uniquename",uniquename,86); // get unique montage file name if (strlen(uniquename) > 3) break; zmessageACK(Mwin,ZTX("supply a reasonable name")); zd->zstat = 0; } if (zstat != 1) goto cleanup; // user canceled strncatv(montagepath,500,"/",uniquename,null); // .../uniquename zdialog_fetch(zd,"createmap",makemap); // create image map file? if (makemap) strncatv(montagepath,500," (fotoxx montage)",null); // .../uniquename (fotoxx montage) strncatv(montagepath,500,".jpg",null); // .../uniquename [...].jpg filename = zgetfile(ZTX("save montage"),MWIN,"save",montagepath,0); // save montage file, user choice if (! filename) goto cleanup; err = f_save(filename,"jpg",8,1); if (err) goto cleanup; zdialog_free(zd); zd = 0; f_open(filename); // make it the current file pp1 = strrchr(filename,'/'); // get actual file name used if (! pp1) goto cleanup; pp2 = strstr(pp1," (fotoxx montage)"); // get uniquename, may have changed if (! pp2) goto cleanup; *pp2 = 0; strncpy0(uniquename,pp1+1,100); zfree(filename); if (makemap) { err = montage_mapfile(); // create corresponding map file if (! err) zmessageACK(Mwin,ZTX("map file saved: %s"),uniquename); } cleanup: if (zd) zdialog_free(zd); // kill dialog for (ii = 0; ii < Nimages; ii++) g_object_unref(image[ii].pixbuf); // free memory Ffuncbusy = Fblock = 0; return; } // montage dialog event and completion function int montage_dialog_event(zdialog *zd, const char *event) { using namespace montage_names; char countmess[80]; if (strmatch(event,"files")) // select images to process { zdialog_show(zd,0); gallery_select(); // get file list snprintf(countmess,80,Bfileselected,GScount); zdialog_stuff(zd,"labfcount",countmess); zdialog_show(zd,1); } if (! zd->zstat) return 1; // wait for completion if (zd->zstat != 1) return 1; // user cancel zd->zstat = 0; // keep dialog active if (GScount == 0) { // no files selected zmessageACK(Mwin,Bnofileselected); return 1; } if (GScount > maxNm) { zmessageACK(Mwin,ZTX("%d max images exceeded"),maxNm); return 1; } zdialog_fetch(zd,"Fww",Fww); // frame dimensions zdialog_fetch(zd,"Fmarg",Fmarg); // frame margin zdialog_fetch(zd,"Ncols",Ncols); // image columns zdialog_fetch(zd,"Imarg",Imarg); // image margin zd->zstat = 1; // dialog finished return 1; } // Map scaled image positions into frame image. // Calculate max. difference in image column height. int montage_mapimages() { using namespace montage_names; int ii, jj; int maxcol, mincol; int row, col, bott; for (ii = 0; ii < Nimages; ii++) // loop all images { row = image[ii].row; // image row col = image[ii].col; // image column jj = ii - Ncols; // prior image in same column image[ii].Ipx = Fmarg + col * (Iww + Imarg); // image x position if (row == 0) image[ii].Ipy = Fmarg; // image y position for row 0 else image[ii].Ipy = image[jj].Ipy + image[jj].Ihh + Imarg; // image y position for row 1+ } maxcol = 0; mincol = 999999; for (ii = Nimages - Ncols; ii < Nimages; ii++) // loop last Ncols images { bott = image[ii].Ipy + image[ii].Ihh; // image y position + height if (bott > maxcol) maxcol = bott; // save max. bottom edge if (bott < mincol) mincol = bott; // save min. bottom edge col = image[ii].col; colH[col] = bott; // save column height } return (maxcol - mincol); // return column height difference } // fill the frame image from scaled images and show the frame image int montage_showframe() { using namespace montage_names; uint8 *Fpixels, *pixel; int ii, px, py, Frs, col, maxcol = 0; int ww, hh, stat, bott, err; char *file; PIXBUF *Fpixbuf, *pixbuf; GError *gerror = 0; for (ii = Nimages - Ncols; ii < Nimages; ii++) // loop last Ncols images { bott = image[ii].Ipy + image[ii].Ihh; // image y position + height if (bott > maxcol) maxcol = bott; // save max. bottom edge col = image[ii].col; colH[col] = bott; // save column height } Fhh = maxcol + Fmarg; // set frame image height if (Fhh > wwhh_limit1) { zmessageACK(Mwin,ZTX("image frame is too large: %d x %d"),Fww,Fhh); return 1; } Fpixbuf = gdk_pixbuf_new(GDKRGB,0,8,Fww,Fhh); // make frame image pixbuf if (! Fpixbuf) { zmessageACK(Mwin,ZTX("image frame is too large: %d x %d"),Fww,Fhh); return 1; // error return } Fpixels = gdk_pixbuf_get_pixels(Fpixbuf); // fill frame image with white Frs = gdk_pixbuf_get_rowstride(Fpixbuf); for (py = 0; py < Fhh; py++) for (px = 0; px < Fww; px++) { pixel = Fpixels + py * Frs + px * 3; pixel[0] = pixel[1] = pixel[2] = 255; } for (ii = 0; ii < Nimages; ii++) // loop all images { pixbuf = image[ii].pixbuf; // copy scaled image into frame image ww = gdk_pixbuf_get_width(pixbuf); hh = gdk_pixbuf_get_height(pixbuf); gdk_pixbuf_copy_area(pixbuf,0,0,ww,hh,Fpixbuf,image[ii].Ipx,image[ii].Ipy); zmainloop(); } file = zstrdup(tempdir,30); // save frame image to temp file strcat(file,"/image (fotoxx montage).jpg"); stat = gdk_pixbuf_save(Fpixbuf,file,"jpeg",&gerror,"quality","90",null); if (! stat) { zmessageACK(Mwin,"GDK pixbuf: cannot save file"); zfree(file); g_object_unref(Fpixbuf); return 2; // error return } m_viewmode(0,"F"); err = f_open(file); // show frame image if (err) return 3; zfree(file); g_object_unref(Fpixbuf); return 0; } // shuffle image column assignments to make column heights even int montage_shuffle() { using namespace montage_names; int ii, jj, col, colii, coljj; int hh1, hh2, diff, maxdiff; int bestii, bestjj; image_t tempimage; for (ii = Nimages - Ncols; ii < Nimages; ii++) // loop last Ncols images { col = image[ii].col; // get column height colH[col] = image[ii].Ipy + image[ii].Ihh; // = y position + image height } for (ii = 0; ii < Nimages; ii++) // loop all images ii { zmainloop(); colii = image[ii].col; maxdiff = bestii = 0; for (jj = 0; jj < Nimages; jj++) // loop all images jj { coljj = image[jj].col; if (colii == coljj ) continue; // both images in same column hh1 = colH[colii] - image[ii].Ihh + image[jj].Ihh; // column heights if images are switched hh2 = colH[coljj] + image[ii].Ihh - image[jj].Ihh; diff = abs(colH[colii] - colH[coljj]) - abs(hh1 - hh2); // column height diff - switched diff if (diff > maxdiff) { maxdiff = diff; // best switch for nearest heights bestii = ii; bestjj = jj; } } if (maxdiff > 0) { ii = bestii; // switch the images jj = bestjj; col = image[ii].col; colH[col] = colH[col] - image[ii].Ihh + image[jj].Ihh; col = image[jj].col; colH[col] = colH[col] + image[ii].Ihh - image[jj].Ihh; tempimage = image[ii]; image[ii] = image[jj]; image[jj] = tempimage; image[jj].row = image[ii].row; image[jj].col = image[ii].col; image[ii].row = tempimage.row; image[ii].col = tempimage.col; } } return 0; } // sort images randomly to get a new starting point for shuffle int montage_sort() { using namespace montage_names; int ii; int keys[1][3] = { { 0, 4, 3 } }; // key position, length, type for (ii = 0; ii < Nimages; ii++) // populate random sort key image[ii].random = 1000000 * drandz(); MemSort((char *) image, sizeof(image_t), Nimages, keys, 1); // sort image[] for (ii = 0; ii < Nimages; ii++) { image[ii].row = ii / Ncols; // new image row, column image[ii].col = ii - Ncols * image[ii].row; } return 1; } // spread images in short columns to make column heights even. int montage_spread() { using namespace montage_names; int col, row, nrows, ii, dy; int maxH = 0, diff, diff2; if (Nrows < 2) return 0; for (ii = Nimages - Ncols; ii < Nimages; ii++) // loop last Ncols images { col = image[ii].col; // get column height colH[col] = image[ii].Ipy + image[ii].Ihh; // = y position + image height } for (col = 0; col < Ncols; col++) // get max. column height if (colH[col] > maxH) maxH = colH[col]; for (col = 0; col < Ncols; col++) // loop all columns { diff = maxH - colH[col]; if (diff < 2) continue; // column height near max, no change nrows = Nrows; // max. images per column ii = (nrows - 1) * Ncols + col; // last image[ii] this column if (ii >= Nimages) nrows -= 1; // this is a short column if (nrows < 2) break; diff2 = diff / (nrows - 1); // increase for Y-posn after row 0 for (dy = row = 0; row < nrows; row++) // loop all rows in column { ii = row * Ncols + col; if (ii == Nimages) break; image[ii].Ipy += dy; dy += diff2; // prior increase + per row increase } } return 0; } // create map file - maps the scaled image areas in the montage file // to the corresponding image files int montage_mapfile() { using namespace montage_names; int ii, lox, hix, loy, hiy; float flox, fhix, floy, fhiy; char montagemapfile[300]; FILE *fid; snprintf(montagemapfile,300,"%s/%s",montage_maps_dirk,uniquename); fid = fopen(montagemapfile,"w"); if (! fid) { zmessageACK(Mwin,strerror(errno)); return 1; } for (ii = 0; ii < Nimages; ii++) { lox = image[ii].Ipx; // scaled image pixel range loy = image[ii].Ipy; hix = lox + Iww; hiy = loy + image[ii].Ihh; flox = 1.0 * lox / Fww; // rescale to 0.0 - 1.0 floy = 1.0 * loy / Fhh; // (immune to image resize) fhix = 1.0 * hix / Fww; fhiy = 1.0 * hiy / Fhh; fprintf(fid,"%.5f %.5f %.5f %.5f file: %s\n", flox,floy,fhix,fhiy,image[ii].file); } fclose(fid); return 0; } // mouse function // show the image file corresponding to clicked position in montage file void montage_Lclick_func(int mousex, int mousey) { using namespace montage_names; typedef struct { char *file; // image filename int lox, loy; // scaled image area in montage image int hix, hiy; } image_t; image_t image[maxNm]; // image data int fww, fhh, Nimages = 0; char buff[500], montagemapfile[300]; char *pp, *pp1, *pp2, *file; int ii, cc, nn, err; float flox, fhix, floy, fhiy; FILE *fid; STATB statb; fww = Fpxb->ww; // montage image dimensions fhh = Fpxb->hh; pp1 = strrchr(curr_file,'/'); // extract montage unique name if (! pp1) goto notfound; pp1++; pp2 = strstr(pp1," (fotoxx montage)"); if (! pp2) goto notfound; cc = pp2 - pp1; if (cc > 100) goto notfound; strncpy0(uniquename,pp1,100); uniquename[cc] = 0; snprintf(montagemapfile,300,"%s/%s",montage_maps_dirk,uniquename); // corresp. montage map file fid = fopen(montagemapfile,"r"); if (! fid) goto notfound; // not found for (ii = 0; ii < maxNm; ii++) // loop map file recs { pp = fgets_trim(buff,500,fid,1); if (! pp) break; nn = sscanf(buff,"%f %f %f %f",&flox,&floy,&fhix,&fhiy); if (nn != 4) goto invalid; pp = strstr(buff,"file: "); if (! pp) goto invalid; image[ii].file = zstrdup(pp + 6); image[ii].lox = flox * fww; image[ii].loy = floy * fhh; image[ii].hix = fhix * fww; image[ii].hiy = fhiy * fhh; } fclose(fid); Nimages = ii; if (Nimages < 2) goto invalid; for (ii = 0; ii < Nimages; ii++) { if (mousex <= image[ii].lox) continue; if (mousex >= image[ii].hix) continue; if (mousey <= image[ii].loy) continue; if (mousey >= image[ii].hiy) continue; break; } if (ii == Nimages) goto freemem; file = image[ii].file; err = stat(file,&statb); if (err) { zmessageACK(Mwin,strerror(errno)); goto freemem; } popup_image(file,MWIN,1,512); goto freemem; notfound: zmessageACK(Mwin,ZTX("montage map file not found: %s"),uniquename); goto freemem; invalid: zmessageACK(Mwin,ZTX("montage map file invalid: %s"),uniquename); goto freemem; freemem: for (ii = 0; ii < Nimages; ii++) if (image[ii].file) zfree(image[ii].file); return; } fotoxx-18.01.1/f.effects.cc0000644000175000017500000067172413222767271014055 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit - Effects (filters) menu functions m_sketch convert photo to simulated sketch m_cartoon convert image into a cartoon drawing m_line_drawing combine image and highlighted edge pixels m_color_drawing convert image into high-contrast color drawing m_grad_blur blur image areas inversely proportional to contrast m_emboss convert image to simulated embossing (3D relief) m_tiles convert image into square tiles with 3D edges m_dots convert image into dot grid (Roy Lichtenstein effect) m_painting convert image into a simulated painting m_vignette highlight selected image region m_texture apply a random texture to an image m_pattern tile an image with a repeating pattern m_mosaic convert image to mosaic of thumbnail images m_anykernel transform an image using any custom kernel m_directed_blur directed 1-dimensional blur using the mouse m_blur_background blur background areas (outside selected areas) m_alien_colors change color hues with an algorithm *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // sketch menu function - convert photo to simulated sketch namespace sketch_names { editfunc EFsketch; // edit function data float Bweight = 0.5; // brightness weight 0-1 int Brthresh = 255; // brightness threshold 0-255 float Cweight = 0.5; // contrast weight 0-1 int cliplev = 255; // clipping level 0-255 int algorithm = 1; // which algorithm to use uint8 fgrgb[3] = { 0, 0, 0 }; // foreground color (black) uint8 bgrgb[3] = { 255, 255, 255 }; // background color (white) int ww, hh, cc; // image dimensions uint8 *britness; // maps pixel brightness 0-255 uint8 *CLcont; // maps pixel color contrast 0-255 uint8 *monopix; // output pixel map 0-255 uint8 *fclip; // output clipping flag 0/1 } void m_sketch(GtkWidget *, const char *menu) { using namespace sketch_names; int sketch_dialog_event(zdialog* zd, const char *event); void * sketch_thread(void *); int ii, px, py; float *pix0, *pix1, *pix2, *pix3, *pix4; float f1, f2; cchar *title = ZTX("Convert to Sketch"); F1_help_topic = "sketch"; EFsketch.menuname = menu; EFsketch.menufunc = m_sketch; EFsketch.funcname = "sketch"; // function name EFsketch.Farea = 2; // select area usable EFsketch.Fscript = 1; // scripting supported 17.04 EFsketch.threadfunc = sketch_thread; // thread function if (! edit_setup(EFsketch)) return; // setup edit ww = E3pxm->ww; hh = E3pxm->hh; cc = ww * hh; britness = (uint8 *) zmalloc(cc); CLcont = (uint8 *) zmalloc(cc); monopix = (uint8 *) zmalloc(cc); fclip = (uint8 *) zmalloc(cc); for (py = 1; py < hh-1; py++) // precalculate pixel brightness for (px = 1; px < ww-1; px++) { pix0 = PXMpix(E1pxm,px,py); ii = py * ww + px; britness[ii] = 0.333 * (pix0[0] + pix0[1] + pix0[2]); // brightness 0-255 (pixbright() nodiff) } for (py = 1; py < hh-1; py++) // precalculate pixel contrast for (px = 1; px < ww-1; px++) { pix0 = PXMpix(E1pxm,px,py); pix1 = PXMpix(E1pxm,px-1,py); // get pixel contrast pix2 = PXMpix(E1pxm,px+1,py); pix3 = PXMpix(E1pxm,px,py-1); pix4 = PXMpix(E1pxm,px,py+1); f1 = PIXMATCH(pix1,pix2); // 0..1 = zero..perfect match f2 = PIXMATCH(pix3,pix4); ii = py * ww + px; CLcont[ii] = 255.0 * (1.0 - f1 * f2); // pixel color contrast 0-255 } /*** _________________________________________ | Convert to Sketch | | | | Brightness ==============[]========== | | Threshold ======[]=================== | | Contrast =========[]================= | | Clip Level ===================[]===== | | Algorithm (o) #1 (o) #2 | | Foreground [#####] Background [#####] | | | | [done] [cancel] | |_________________________________________| ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); // sketch dialog EFsketch.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labbrite","hb1",Bbrightness,"space=5"); zdialog_add_widget(zd,"hscale","Bweight","hb1","0.0|1.0|0.005|0.5","expand|space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=2"); zdialog_add_widget(zd,"label","laBrthresh","hb2",Bthresh,"space=5"); zdialog_add_widget(zd,"hscale","Brthresh","hb2","0|255|1|255","expand|space=3"); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labcon","hb3",Bcontrast,"space=5"); zdialog_add_widget(zd,"hscale","Cweight","hb3","0.0|1.0|0.005|0.5","expand|space=3"); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labclip","hb4",ZTX("Clip Level"),"space=5"); zdialog_add_widget(zd,"hscale","cliplev","hb4","0|255|1|0","expand|space=3"); zdialog_add_widget(zd,"hbox","hb5","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labalg","hb5",ZTX("Algorithm"),"space=5"); zdialog_add_widget(zd,"radio","algorithm1","hb5","#1","space=5"); zdialog_add_widget(zd,"radio","algorithm2","hb5","#2","space=5"); zdialog_add_widget(zd,"hbox","hb6","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labfg","hb6",ZTX("Foreground"),"space=2"); zdialog_add_widget(zd,"colorbutt","fgcolor","hb6","0|0|0","space=2"); zdialog_add_widget(zd,"label","space","hb6",0,"space=8"); zdialog_add_widget(zd,"label","labbg","hb6",ZTX("Background"),"space=2"); zdialog_add_widget(zd,"colorbutt","bgcolor","hb6","255|255|255","space=2"); Bweight = 0.5; // initial values Brthresh = 255; Cweight = 0.5; cliplev = 255; algorithm = 1; zdialog_stuff(zd,"algorithm1",1); zdialog_resize(zd,250,0); zdialog_run(zd,sketch_dialog_event,"save"); // run dialog - parallel signal_thread(); return; } // sketch dialog event and completion function int sketch_dialog_event(zdialog *zd, const char *event) { using namespace sketch_names; int ii; cchar *pp; char color[20]; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit zfree(britness); zfree(CLcont); zfree(monopix); zfree(fclip); return 1; } zdialog_fetch(zd,"Bweight",Bweight); // revised brightness weight zdialog_fetch(zd,"Brthresh",Brthresh); // brightness threshold zdialog_fetch(zd,"Cweight",Cweight); // contrast weight zdialog_fetch(zd,"cliplev",cliplev); cliplev = 255 - cliplev; // scale is reversed zdialog_fetch(zd,"algorithm1",ii); if (ii == 1) algorithm = 1; else algorithm = 2; zdialog_fetch(zd,"fgcolor",color,19); pp = strField(color,"|",1); if (pp) fgrgb[0] = atoi(pp); pp = strField(color,"|",2); if (pp) fgrgb[1] = atoi(pp); pp = strField(color,"|",3); if (pp) fgrgb[2] = atoi(pp); zdialog_fetch(zd,"bgcolor",color,19); pp = strField(color,"|",1); if (pp) bgrgb[0] = atoi(pp); pp = strField(color,"|",2); if (pp) bgrgb[1] = atoi(pp); pp = strField(color,"|",3); if (pp) bgrgb[2] = atoi(pp); signal_thread(); return 1; } // thread function - multiple working threads to update image void * sketch_thread(void *) { using namespace sketch_names; void sketch_algorithm1(); void sketch_algorithm2(); void sketch_finish(); while (true) { thread_idle_loop(); // wait for work or exit request if (algorithm == 1) sketch_algorithm1(); if (algorithm == 2) sketch_algorithm2(); sketch_finish(); CEF->Fmods++; // image modified CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop warning } // algorithm 1 void sketch_algorithm1() { using namespace sketch_names; int px, py, qx, qy; int ii, jj, kk, dist; int br0, br1, br2, br3, move; float bright, cont; for (py = 0; py < hh; py++) // convert to monocolor image for (px = 0; px < ww; px++) // with brightness range 0-255 { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } bright = britness[ii]; if (bright > Brthresh) bright = 255; cont = CLcont[ii]; // contrast 0-255 bright = Bweight * bright - Cweight * cont; if (bright < 0) bright = 0; monopix[ii] = bright; } for (br0 = 5; br0 <= 255; br0 += 10) // brightness threshold, 5 ... 255 { for (py = 1; py < hh-1; py++) // loop all image pixels for (px = 1; px < ww-1; px++) // (interior pixels only) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } br1 = monopix[ii]; // pixel brightness if (br1 == 0) continue; // skip black pixel if (br1 > br0) continue; // skip pixel brighter than br0 for (qy = py-1; qy <= py+1; qy++) // loop pixels within 1 of target pixel for (qx = px-1; qx <= px+1; qx++) // test if target pixel is darkest > 0 { jj = qy * ww + qx; br2 = monopix[jj]; if (br2 == 0) continue; // ignore black pixels if (br2 < br1) goto nextpixel; // target pixel not darkest } br2 = 0; kk = -1; for (qy = py-1; qy <= py+1; qy++) // loop pixels within 1 of target pixel for (qx = px-1; qx <= px+1; qx++) // find brightest pixel < 255 { jj = qy * ww + qx; br3 = monopix[jj]; if (br3 < 255 && br3 > br2) { br2 = br3; kk = jj; } } if (kk < 0) continue; move = br1; // move brightness from target pixel, if (255 - br2 < move) move = 255 - br2; // as much as possible monopix[ii] -= move; monopix[kk] += move; nextpixel: continue; } zmainloop(); } return; // not executed, avoid gcc warning } // algorithm 2 void sketch_algorithm2() { using namespace sketch_names; int px, py, qx, qy; int ii, jj, kk, dist; int br0, br1, br2, br3, move; int bright, cont, brcon; for (py = 0; py < hh; py++) // convert to monocolor image for (px = 0; px < ww; px++) // with brightness range 0-255 { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } bright = Bweight * britness[ii]; // brightness 0-255 if (bright > Brthresh) bright = 255; cont = CLcont[ii]; // contrast 0-255 brcon = (1.0 - Cweight) * bright - Cweight * cont; // 255 = max. brightness + min. contrast if (brcon < 0) brcon = 0; if (brcon > 0) brcon = 255; // pixels are black/white monopix[ii] = brcon; // 0-255 } for (br0 = 5; br0 <= 255; br0 += 50) // loop brightness threshold { for (py = 1; py < hh-1; py++) // loop image interior pixels for (px = 1; px < ww-1; px++) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } br1 = monopix[ii]; if (br1 > br0 || br1 == 0) continue; // skip target pixel > threshold or black for (qy = py-1; qy <= py+1; qy++) // loop pixels within 1 of target pixel for (qx = px-1; qx <= px+1; qx++) { jj = qy * ww + qx; br2 = monopix[jj]; if (br2 == 0) continue; // ignore black pixels if (br2 < br1) goto nextpixel; // target pixel not darkest } br2 = 0; kk = -1; for (qy = py-1; qy <= py+1; qy++) // loop pixels within 1 of target pixel for (qx = px-1; qx <= px+1; qx++) { jj = qy * ww + qx; br3 = monopix[jj]; if (br3 < 255 && br3 > br2) { // find brightest < 255 br2 = br3; kk = jj; } } if (kk < 0) continue; move = br1; // move darkness to target pixel, if (255 - br2 < move) move = 255 - br2; // as much as possible br1 -= move; br2 += move; monopix[ii] = br1; monopix[kk] = br2; nextpixel: ; } } return; // not executed, avoid gcc warning } // convert monopix[*] to final image // black: chosen color // white: white void sketch_finish() { using namespace sketch_names; int px, py, qx, qy; int ii, jj, dist; int br1, brsum; int R, G, B; float *pix1, *pix3, f1, f2; memset(fclip,0,cc); for (py = 1; py < hh-1; py++) // loop all pixels for (px = 1; px < ww-1; px++) // (interior pixels only) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } brsum = 0; for (qy = py-1; qy <= py+1; qy++) // loop pixels within 1 of target pixel for (qx = px-1; qx <= px+1; qx++) { jj = qy * ww + qx; // sum brightness brsum += monopix[jj]; } brsum = brsum / 9; // average brightness if (brsum > cliplev) fclip[ii] = 1; // reduce isolated dark pixels } for (ii = 0; ii < cc; ii++) // clipped pixels >> white if (fclip[ii]) monopix[ii] = 255; for (py = 0; py < hh; py++) // loop all pixels for (px = 0; px < ww; px++) { ii = py * ww + px; br1 = monopix[ii]; // input pixel brightness 0-255 f1 = 0.003906 * br1; // background part, 0 - 1 f2 = 1.0 - f1; // foreground part, 1 - 0 R = f2 * fgrgb[0] + f1 * bgrgb[0]; // foreground + background G = f2 * fgrgb[1] + f1 * bgrgb[1]; B = f2 * fgrgb[2] + f1 * bgrgb[2]; pix3 = PXMpix(E3pxm,px,py); // output pixel if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area if (dist < sa_blend) { pix1 = PXMpix(E1pxm,px,py); // input pixel (blend area) f1 = sa_blendfunc(dist); f2 = 1.0 - f1; pix3[0] = f1 * R + f2 * pix1[0]; // blend monocolor brightness pix3[1] = f1 * G + f2 * pix1[1]; // and original image pix3[2] = f1 * B + f2 * pix1[2]; continue; } } pix3[0] = R; pix3[1] = G; pix3[2] = B; } return; } /********************************************************************************/ // convert an image into a cartoon drawing namespace cartoon { editfunc EFcartoon; int line_threshold = 1000; int line_width = 1; int blur_radius = 1; int kuwahara_depth = 1; int priorKD, priorBR; int ww, hh; float *pixcon; float Fthreshold; float Wrad[21][21]; // radius <= 10 int dialog_event(zdialog* zd, cchar *event); void * thread(void *); void blur(); void * blur_wthread(void *); void kuwahara(); void * kuwahara_wthread(void *); void contrast_map(); void drawlines(); void * drawlines_wthread(void *); } // menu function void m_cartoon(GtkWidget *, cchar *menu) { using namespace cartoon; F1_help_topic = "cartoon"; EFcartoon.menuname = menu; EFcartoon.menufunc = m_cartoon; EFcartoon.funcname = "cartoon"; EFcartoon.Farea = 2; // select area usable EFcartoon.Fscript = 1; // scripting supported 17.04 EFcartoon.threadfunc = thread; // thread function if (! edit_setup(EFcartoon)) return; // setup edit ww = E1pxm->ww; hh = E1pxm->hh; pixcon = (float *) zmalloc(ww * hh * sizeof(float)); E8pxm = PXM_copy(E1pxm); // blurred image E9pxm = PXM_copy(E1pxm); // kuwahara image /*** _____________________________ | Cartoon | | | | Line Threshold [ 20 ] | | Line Width [ 3 ] | | Blur Radius [ 10 ] | | Kuwahara Depth [ 4 ] | | | | [Done] [Cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new("Cartoon",Mwin,Bdone,Bcancel,null); EFcartoon.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog"); zdialog_add_widget(zd,"vbox","space","hb1",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog"); zdialog_add_widget(zd,"vbox","space","hb1",0,"space=3"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog"); zdialog_add_widget(zd,"label","lab1","vb1","Line Threshold"); zdialog_add_widget(zd,"label","lab1","vb1","Line Width"); zdialog_add_widget(zd,"label","lab2","vb1","Blur Radius"); zdialog_add_widget(zd,"label","lab1","vb1","Kuwahara Depth"); zdialog_add_widget(zd,"zspin","line_threshold","vb2","0|1000|1|1000"); zdialog_add_widget(zd,"zspin","line_width","vb2","0|10|1|1"); zdialog_add_widget(zd,"zspin","blur_radius","vb2","0|10|1|1"); zdialog_add_widget(zd,"zspin","kuwahara_depth","vb2","0|10|1|1"); zdialog_resize(zd,200,0); zdialog_restore_inputs(zd); zdialog_run(zd,dialog_event,"save"); // run dialog, parallel zdialog_fetch(zd,"line_threshold",line_threshold); zdialog_fetch(zd,"line_width",line_width); zdialog_fetch(zd,"blur_radius",blur_radius); zdialog_fetch(zd,"kuwahara_depth",kuwahara_depth); priorKD = priorBR = -1; // initial edit using dialog params signal_thread(); return; } // dialog event and completion callback function int cartoon::dialog_event(zdialog *zd, cchar *event) // dialog event function { using namespace cartoon; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit zfree(pixcon); return 1; } zdialog_fetch(zd,"line_threshold",line_threshold); // get outline threshold 0-1000 zdialog_fetch(zd,"line_width",line_width); // get line width 0-10 zdialog_fetch(zd,"blur_radius",blur_radius); // get blur radius 0-10 zdialog_fetch(zd,"kuwahara_depth",kuwahara_depth); // get kuwahara depth 0-10 signal_thread(); return 1; } // thread function to update image void * cartoon::thread(void *) // improved { using namespace cartoon; int Fblur, Fkuwa, Fcon; while (true) { thread_idle_loop(); // wait for work or exit request Fblur = Fkuwa = Fcon = 0; if (blur_radius != priorBR) Fblur = Fkuwa = Fcon = 1; if (kuwahara_depth != priorKD) Fkuwa = Fcon = 1; priorBR = blur_radius; // capture now, can change during process priorKD = kuwahara_depth; if (Fblur) blur(); // E1 + blur >> E8 if (Fkuwa) kuwahara(); // E8 + kuwahara >> E9 if (Fcon) contrast_map(); // compute E9 pixel contrast drawlines(); // draw lines where contrast > threshold CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } // Blur the image using blur_radius. E1 + blur >> E8 void cartoon::blur() { using namespace cartoon; int rad, px, py, qx, qy; float R, W; PXM_free(E8pxm); E8pxm = PXM_copy(E1pxm); if (blur_radius == 0) return; rad = blur_radius; for (qy = -rad; qy <= rad; qy++) // compute blur weights for pixels for (qx = -rad; qx <= rad; qx++) // within rad of target pixel { R = sqrtf(qx * qx + qy * qy); if (R > rad) W = 0.0; else if (R == 0) W = 1.0; else W = 1.0 / R; py = qy + rad; px = qx + rad; Wrad[py][px] = W; } for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(blur_wthread,&Nval[ii]); wait_wthreads(); // wait for completion return; } void * cartoon::blur_wthread(void *arg) // worker thread function { using namespace cartoon; int index = *((int *) arg); int rad = blur_radius; int ii, dist = 0, px, py, qx, qy; float W, Wsum, f1, f2; float R, G, B, *pix1, *pixq, *pix8; for (py = index + rad; py < hh-rad; py += NWT) // loop all image pixels for (px = rad; px < ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } R = G = B = Wsum = 0; for (qy = -rad; qy <= rad; qy++) // compute weighted sum RGB for pixels for (qx = -rad; qx <= rad; qx++) // within rad of target pixel { pix1 = PXMpix(E1pxm,px,py); pixq = PXMpix(E1pxm,px+qx,py+qy); if (PIXMATCH(pix1,pixq) < 0.7) continue; // use only pixels matching target pixel W = Wrad[qy+rad][qx+rad]; R += W * pixq[0]; G += W * pixq[1]; B += W * pixq[2]; Wsum += W; } R = R / Wsum; // divide by sum of weights G = G / Wsum; B = B / Wsum; if (sa_stat == 3 && dist < sa_blend) { // if select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); // input pixel R = f1 * R + f2 * pix1[0]; G = f1 * G + f2 * pix1[1]; B = f1 * B + f2 * pix1[2]; } pix8 = PXMpix(E8pxm,px,py); // output pixel pix8[0] = R; pix8[1] = G; pix8[2] = B; } exit_wthread(); return 0; } // Perform a kuwahara sharpen of the image. E8 + kuwahara >> E9. void cartoon::kuwahara() { using namespace cartoon; PXM_free(E9pxm); E9pxm = PXM_copy(E8pxm); if (kuwahara_depth == 0) return; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(kuwahara_wthread,&Nval[ii]); wait_wthreads(); // wait for completion return; } void * cartoon::kuwahara_wthread(void *arg) // worker thread function { using namespace cartoon; int index = *((int *) arg); int px, py, qx, qy, rx, ry; int ii, rad, N, dist = 0; float *pix1, *pix8, *pix9; float red, green, blue, red2, green2, blue2; float vmin, vall, vred, vgreen, vblue; float red9, green9, blue9; float f1, f2; rad = kuwahara_depth; // user input radius N = (rad + 1) * (rad + 1); for (py = index + rad; py < hh-rad; py += NWT) // loop all image pixels for (px = rad; px < ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } vmin = 99999; red9 = green9 = blue9 = 0; for (qy = py - rad; qy <= py; qy++) // loop all surrounding neighborhoods for (qx = px - rad; qx <= px; qx++) { red = green = blue = 0; red2 = green2 = blue2 = 0; for (ry = qy; ry <= qy + rad; ry++) // loop all pixels in neighborhood for (rx = qx; rx <= qx + rad; rx++) { pix8 = PXMpix(E8pxm,rx,ry); red += pix8[0]; // compute mean RGB and mean RGB**2 red2 += pix8[0] * pix8[0]; green += pix8[1]; green2 += pix8[1] * pix8[1]; blue += pix8[2]; blue2 += pix8[2] * pix8[2]; } red = red / N; // mean RGB of neighborhood green = green / N; blue = blue / N; vred = red2 / N - red * red; // variance RGB vgreen = green2 / N - green * green; vblue = blue2 / N - blue * blue; vall = vred + vgreen + vblue; // save RGB values with least variance if (vall < vmin) { vmin = vall; red9 = red; green9 = green; blue9 = blue; } } if (sa_stat == 3 && dist < sa_blend) { // if select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,px,py); // input pixel red9 = f1 * red9 + f2 * pix1[0]; green9 = f1 * green9 + f2 * pix1[1]; blue9 = f1 * blue9 + f2 * pix1[2]; } pix9 = PXMpix(E9pxm,px,py); // output pixel pix9[0] = red9; pix9[1] = green9; pix9[2] = blue9; } exit_wthread(); return 0; // not executed, avoid gcc warning } // Map contrast for E9 image pixels and set threshold scale. void cartoon::contrast_map() { using namespace cartoon; int ii, px, py, qx, qy; double sumcon, pix1con, maxcon; float *pixx, *pixq; maxcon = 0; for (py = 1; py < hh-1; py++) // make image pixel contrast map for (px = 1; px < ww-1; px++) { sumcon = 0; for (qy = py-1; qy <= py+1; qy++) for (qx = px-1; qx <= px+1; qx++) { pixx = PXMpix(E9pxm,px,py); // pixel contrast is mean contrast pixq = PXMpix(E9pxm,qx,qy); // with 8 neighboring pixels sumcon += fabsf(pixx[0] - pixq[0]); sumcon += fabsf(pixx[1] - pixq[1]); sumcon += fabsf(pixx[2] - pixq[2]); } ii = py * ww + px; pixcon[ii] = pix1con = 0.0417 * sumcon; // / (8 * 3) if (pix1con > maxcon) maxcon = pix1con; } Fthreshold = maxcon; // threshold scale factor return; } // Draw lines on the image where edges are detected. // E9 >> E3, add lines to E3, use E1 for area blend. void cartoon::drawlines() { paintlock(1); PXM_free(E3pxm); E3pxm = PXM_copy(E9pxm); for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(drawlines_wthread,&Nval[ii]); wait_wthreads(); // wait for completion paintlock(0); } void * cartoon::drawlines_wthread(void *arg) // worker thread function { using namespace cartoon; int index = *((int *) arg); int px, py, qx, qy, ii, dist = 0; float Fthresh, paint, f1, f2; float *pix1, *pix3, *pix3q; Fthresh = 0.001 * line_threshold * Fthreshold; // 0 .. 1000 >> 0 .. Fthreshold for (py = index+1; py < hh-1; py += NWT) for (px = 1; px < ww-1; px++) { ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } if (pixcon[ii] < Fthresh) continue; pix1 = PXMpix(E1pxm,px,py); // input image pixel (for area blend) pix3 = PXMpix(E3pxm,px,py); // output image pixel paint = line_width * (pixcon[ii] - Fthresh) / Fthresh; if (paint <= 1) { f1 = paint; f2 = 1.0 - f1; pix3[0] = f2 * pix3[0]; pix3[1] = f2 * pix3[1]; pix3[2] = f2 * pix3[2]; } else { f1 = (paint - 1.0) / 8.0; if (f1 > 1.0) f1 = 1.0; f2 = 1.0 - f1; for (qy = py-1; qy <= py+1; qy++) for (qx = px-1; qx <= px+1; qx++) { if (qx == px && qy == py) pix3[0] = pix3[1] = pix3[2] = 0; else { pix3q = PXMpix(E3pxm,qx,qy); pix3q[0] = f2 * pix3q[0]; pix3q[1] = f2 * pix3q[1]; pix3q[2] = f2 * pix3q[2]; } } } if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; continue; } } exit_wthread(); return 0; // not executed, stop gcc warning } /********************************************************************************/ // make an image outline drawing from edge pixels // superimpose drawing and image and vary brightness of each namespace linedraw_names { editfunc EFlinedraw; float outline_thresh; // contrast threshold to make a line float outline_width; // drawn line width float image_briteness; // background image brightness int blackwhite, negative; // flags } void m_line_drawing(GtkWidget *, cchar *menu) { using namespace linedraw_names; int linedraw_dialog_event(zdialog* zd, cchar *event); void * linedraw_thread(void *); F1_help_topic = "line_drawing"; EFlinedraw.menuname = menu; EFlinedraw.menufunc = m_line_drawing; EFlinedraw.funcname = "linedraw"; EFlinedraw.Farea = 2; // select area usable EFlinedraw.Fscript = 1; // scripting supported 17.04 EFlinedraw.threadfunc = linedraw_thread; // thread function if (! edit_setup(EFlinedraw)) return; // setup edit /*** ___________________________________ | Line Drawing | | | | Threshold =======[]============== | | Width =================[]======== | | Brightness =========[]=========== | | [_] black/white [_] negative | | | | [Done] [Cancel] | |___________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Line Drawing"),Mwin,Bdone,Bcancel,null); EFlinedraw.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0); zdialog_add_widget(zd,"label","lab1","hb1",Bthresh,"space=3"); zdialog_add_widget(zd,"hscale","olth","hb1","0|100|1|90","expand|space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0); zdialog_add_widget(zd,"label","lab2","hb2",Bwidth,"space=5"); zdialog_add_widget(zd,"hscale","olww","hb2","0|100|1|50","expand|space=5"); zdialog_add_widget(zd,"hbox","hb3","dialog",0); zdialog_add_widget(zd,"label","lab3","hb3",Bbrightness,"space=5"); zdialog_add_widget(zd,"hscale","imbr","hb3","0|100|1|10","expand|space=5"); zdialog_add_widget(zd,"hbox","hb4","dialog",0); zdialog_add_widget(zd,"check","blackwhite","hb4",ZTX("black/white"),"space=5"); zdialog_add_widget(zd,"label","space","hb4",0,"space=10"); zdialog_add_widget(zd,"check","negative","hb4",Bnegative); outline_thresh = 90; outline_width = 50; image_briteness = 10; blackwhite = negative = 0; zdialog_stuff(zd,"blackwhite",0); zdialog_stuff(zd,"negative",0); zdialog_resize(zd,300,0); zdialog_run(zd,linedraw_dialog_event,"save"); // run dialog, parallel signal_thread(); return; } // dialog event and completion callback function int linedraw_dialog_event(zdialog *zd, cchar *event) // dialog event function { using namespace linedraw_names; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit PXM_free(E8pxm); PXM_free(E9pxm); return 1; } zdialog_fetch(zd,"olth",outline_thresh); // get outline threshold 0-100 zdialog_fetch(zd,"olww",outline_width); // get outline width 0-100 zdialog_fetch(zd,"imbr",image_briteness); // get image brightness 0-100 zdialog_fetch(zd,"blackwhite",blackwhite); zdialog_fetch(zd,"negative",negative); signal_thread(); return 1; } // thread function to update image void * linedraw_thread(void *) { using namespace linedraw_names; void * linedraw_wthread(void *arg); int px, py, ww, hh; float olww, red3, green3, blue3; float red3h, red3v, green3h, green3v, blue3h, blue3v; float *pix8, *pix9; float *pixa, *pixb, *pixc, *pixd, *pixe, *pixf, *pixg, *pixh; int nc = E1pxm->nc; while (true) { thread_idle_loop(); // wait for work or exit request olww = 0.01 * outline_width; // outline width, 0 - 1.0 olww = 1.0 - olww; // 1.0 - 0 olww = 0.8 * olww + 0.2; // 1.0 - 0.2 ww = E3pxm->ww * olww + 0.5; // create smaller outline image hh = E3pxm->hh * olww + 0.5; if (! E9pxm || ww != E9pxm->ww) // initial or changed outline brightness { PXM_free(E8pxm); PXM_free(E9pxm); E8pxm = PXM_rescale(E1pxm,ww,hh); E9pxm = PXM_copy(E8pxm); for (py = 1; py < hh-1; py++) for (px = 1; px < ww-1; px++) { pix8 = PXMpix(E8pxm,px,py); // input pixel pix9 = PXMpix(E9pxm,px,py); // output pixel pixa = pix8-nc*ww-nc; // 8 neighboring pixels are used pixb = pix8-nc*ww; // to get edge brightness using pixc = pix8-nc*ww+nc; // a Sobel filter pixd = pix8-nc; pixe = pix8+nc; pixf = pix8+nc*ww-nc; pixg = pix8+nc*ww; pixh = pix8+nc*ww+nc; red3h = -pixa[0] - 2 * pixb[0] - pixc[0] + pixf[0] + 2 * pixg[0] + pixh[0]; red3v = -pixa[0] - 2 * pixd[0] - pixf[0] + pixc[0] + 2 * pixe[0] + pixh[0]; green3h = -pixa[1] - 2 * pixb[1] - pixc[1] + pixf[1] + 2 * pixg[1] + pixh[1]; green3v = -pixa[1] - 2 * pixd[1] - pixf[1] + pixc[1] + 2 * pixe[1] + pixh[1]; blue3h = -pixa[2] - 2 * pixb[2] - pixc[2] + pixf[2] + 2 * pixg[2] + pixh[2]; blue3v = -pixa[2] - 2 * pixd[2] - pixf[2] + pixc[2] + 2 * pixe[2] + pixh[2]; red3 = (fabsf(red3h) + fabsf(red3v)) / 2; // average vert. and horz. brightness green3 = (fabsf(green3h) + fabsf(green3v)) / 2; blue3 = (fabsf(blue3h) + fabsf(blue3v)) / 2; if (red3 > 255.9) red3 = 255.9; if (green3 > 255.9) green3 = 255.9; if (blue3 > 255.9) blue3 = 255.9; pix9[0] = red3; pix9[1] = green3; pix9[2] = blue3; } } PXM_free(E8pxm); // scale back to full-size E8pxm = PXM_rescale(E9pxm,E3pxm->ww,E3pxm->hh); for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(linedraw_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * linedraw_wthread(void *arg) // worker thread function { using namespace linedraw_names; int index = *((int *) arg); int px, py, ii, dist = 0; float olth, imbr, br8, dold, dnew; float red1, green1, blue1, red3, green3, blue3; float *pix1, *pix8, *pix3; olth = 0.01 * outline_thresh; // outline threshold, 0 - 1.0 olth = 1.0 - olth; // 1.0 - 0 imbr = 0.01 * image_briteness; // image brightness, 0 - 1.0 for (py = index+1; py < E3pxm->hh-1; py += NWT) for (px = 1; px < E3pxm->ww-1; px++) { if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } pix1 = PXMpix(E1pxm,px,py); // input image pixel pix8 = PXMpix(E8pxm,px,py); // input outline pixel pix3 = PXMpix(E3pxm,px,py); // output image pixel red1 = pix1[0]; // input image pixel green1 = pix1[1]; blue1 = pix1[2]; br8 = pixbright(pix8); // outline brightness br8 = br8 / 256.0; // scale 0 - 1.0 if (br8 > olth) { // > threshold, use outline pixel red3 = pix8[0]; green3 = pix8[1]; blue3 = pix8[2]; } else { // use image pixel dimmed by user control red3 = red1 * imbr; green3 = green1 * imbr; blue3 = blue1 * imbr; } if (sa_stat == 3 && dist < sa_blend) { // select area is active, dnew = sa_blendfunc(dist); // blend changes over sa_blend dold = 1.0 - dnew; red3 = dnew * red3 + dold * red1; green3 = dnew * green3 + dold * green1; blue3 = dnew * blue3 + dold * blue1; } if (blackwhite) red3 = green3 = blue3 = 0.333 * (red3 + green3 + blue3); if (negative) { red3 = 255.9 - red3; green3 = 255.9 - green3; blue3 = 255.9 - blue3; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } exit_wthread(); return 0; // not executed, stop gcc warning } /********************************************************************************/ // convert an image into a high-contrast solid color drawing // somewhat like an illustration or cartoon namespace colordraw_names { editfunc EFcolordraw; int ww, hh; // image dimensions int threshx; // threshold adjustment float darkx, britex; // dark and bright level adjustments } // menu function void m_color_drawing(GtkWidget *, cchar *menu) { using namespace colordraw_names; int colordraw_dialog_event(zdialog *zd, cchar *event); void * colordraw_thread(void *); F1_help_topic = "color_drawing"; EFcolordraw.menuname = menu; EFcolordraw.menufunc = m_color_drawing; EFcolordraw.funcname = "colordraw"; EFcolordraw.Farea = 2; // select area usable EFcolordraw.Frestart = 1; // allow restart EFcolordraw.Fscript = 1; // scripting supported 17.04 EFcolordraw.threadfunc = colordraw_thread; // thread function if (! edit_setup(EFcolordraw)) return; // setup edit ww = E3pxm->ww; // image dimensions hh = E3pxm->hh; /** ____________________________________________ | Color Drawing | | | | Threshold ===============[]=========== | | Dark Areas ==================[]======== | | Bright Areas =========[]================= | | | | [Done] [Cancel] | |____________________________________________| **/ zdialog *zd = zdialog_new(ZTX("Color Drawing"),Mwin,Bdone,Bcancel,null); EFcolordraw.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"space=3|expand|homog"); zdialog_add_widget(zd,"label","labthresh","vb1",Bthresh); zdialog_add_widget(zd,"label","labdark","vb1",ZTX("Dark Areas")); zdialog_add_widget(zd,"label","labbrite","vb1",ZTX("Bright Areas")); zdialog_add_widget(zd,"hscale","threshx","vb2","0|255|1|100"); zdialog_add_widget(zd,"hscale","darkx","vb2","0|1.0|0.002|0.0"); zdialog_add_widget(zd,"hscale","britex","vb2","0|1.0|0.002|1.0"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_fetch(zd,"threshx",threshx); zdialog_fetch(zd,"darkx",darkx); zdialog_fetch(zd,"britex",britex); zdialog_resize(zd,300,0); zdialog_run(zd,colordraw_dialog_event,"save"); // run dialog - parallel signal_thread(); return; } // dialog event and completion callback function int colordraw_dialog_event(zdialog *zd, cchar *event) { using namespace colordraw_names; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } zdialog_fetch(zd,"threshx",threshx); zdialog_fetch(zd,"darkx",darkx); zdialog_fetch(zd,"britex",britex); signal_thread(); return 1; } // image drawing2 thread function void * colordraw_thread(void *) { using namespace colordraw_names; void * colordraw_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(colordraw_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * colordraw_wthread(void *arg) // worker thread function { using namespace colordraw_names; int index = *((int *) arg); int px, py, ii, tt, dist = 0; float red1, green1, blue1; float red3, green3, blue3; float f1, f2, max; float *pix1, *pix3; for (py = index; py < hh; py += NWT) // loop all image pixels for (px = 0; px < ww; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } tt = threshx; // threshold adjustment pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red1 = pix1[0]; green1 = pix1[1]; blue1 = pix1[2]; ii = 0.333 * (red1 + green1 + blue1); // classify pixel as dark or bright if (ii >= tt) { // bright max = red1 + 1; // make brightest - downward adjustment if (green1 > max) max = green1; if (blue1 > max) max = blue1; f1 = 255.0 / max; red3 = red1 * f1; green3 = green1 * f1; blue3 = blue1 * f1; if (britex < 1.0) { f1 = britex; f2 = 1.0 - f1; red3 = f1 * red3 + f2 * red1; green3 = f1 * green3 + f2 * green1; blue3 = f1 * blue3 + f2 * blue1; } } else { // dark red3 = red1 * darkx; // make black + upward adjustment green3 = green1 * darkx; blue3 = blue1 * darkx; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } if (sa_stat == 3 && sa_blend > 0) // select area has edge blend { for (py = index; py < hh-1; py += NWT) // loop all image pixels for (px = 0; px < ww-1; px++) { ii = py * ww + px; dist = sa_pixmap[ii]; if (! dist) continue; // omit pixels outside area if (dist >= sa_blend) continue; // omit if > blendwidth from edge pix1 = PXMpix(E1pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; } } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Graduated blur of image pixels. // Blur radius is zero ... max for pixel contrast max ... zero. namespace gradblur_names { editfunc EFgradblur; int con_limit; float blur_radius; uint8 *pixcon; VOL int gradblur_cancel; // GCC inconsistent int ww, hh, cc; int pixseq_done[122][122]; // up to blur_radius = 60 int pixseq_angle[1000]; int pixseq_dx[13000], pixseq_dy[13000]; int pixseq_rad[13000]; int max1 = 999, max2 = 12999; // for later overflow check } // return a contrast value 0.0 - 1.0 for two given pixels to compare inline float pixcontrast(float *pix1, float *pix2) { float match; match = PIXMATCH(pix1,pix2); // 0..1 = zero..perfect match return 1.0 - match; // 1 = high contrast (black/white) } void m_grad_blur(GtkWidget *, cchar *menu) { using namespace gradblur_names; int gradblur_dialog_event(zdialog *zd, cchar *event); void * gradblur_thread(void *); int ii, px, py, dx, dy; float *pix1, *pix2; float contrast, maxcon; F1_help_topic = "graduated_blur"; EFgradblur.menuname = menu; EFgradblur.menufunc = m_grad_blur; EFgradblur.funcname = "gradblur"; EFgradblur.Farea = 2; // select area usable EFgradblur.Fscript = 1; // scripting supported 17.04 EFgradblur.threadfunc = gradblur_thread; // thread function if (! edit_setup(EFgradblur)) return; // setup edit gradblur_cancel = 0; ww = E3pxm->ww; // pre-calculate pixel contrasts hh = E3pxm->hh; cc = ww * hh; pixcon = (uint8 *) zmalloc(cc); // pixel contrast map for (py = 1; py < hh-1; py++) // loop interior pixels for (px = 1; px < ww-1; px++) { pix1 = PXMpix(E1pxm,px,py); // this pixel in base image E1 contrast = maxcon = 0.0; for (dx = px-1; dx <= px+1; dx++) // loop neighbor pixels for (dy = py-1; dy <= py+1; dy++) { pix2 = PXMpix(E1pxm,dx,dy); contrast = pixcontrast(pix1,pix2); // contrast, 0-1 if (contrast > maxcon) maxcon = contrast; } ii = py * ww + px; // ii maps to (px,py) pixcon[ii] = 255 * maxcon; // pixel contrast, 0 to 255 } /*** _____________________________ | Graduated Blur | | | | Contrast Limit [ 40 +-] | | Blur Radius [ 16 +-] | | | | [apply] [done] [cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(ZTX("Graduated Blur"),Mwin,Bapply,Bdone,Bcancel,null); EFgradblur.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",ZTX("Contrast Limit"),"space=5"); zdialog_add_widget(zd,"zspin","con_limit","hb1","1|255|1|50"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=5"); zdialog_add_widget(zd,"label","lab5","hb2",ZTX("Blur Radius"),"space=5"); zdialog_add_widget(zd,"zspin","blur_radius","hb2","1|50|1|15"); zdialog_restore_inputs(zd); zdialog_run(zd,gradblur_dialog_event,"save"); // run dialog - parallel return; } // dialog event and completion callback function int gradblur_dialog_event(zdialog * zd, cchar *event) { using namespace gradblur_names; if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) // apply { zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"con_limit",con_limit); // get parameters zdialog_fetch(zd,"blur_radius",blur_radius); edit_undo(); // reset image signal_thread(); // trigger working thread } else if (zd->zstat == 2) { // done edit_done(0); // commit edit zfree(pixcon); } else { // cancel gradblur_cancel = 1; edit_cancel(0); // discard edit zfree(pixcon); } } return 1; } // image gradblur thread function void * gradblur_thread(void *) { using namespace gradblur_names; void * gradblur_wthread(void *arg); int dx, dy, adx, ady; int ii, jj; float rad1, rad2, angle, astep; while (true) { thread_idle_loop(); // wait for work or exit request rad1 = blur_radius; for (dy = 0; dy <= 2*rad1; dy++) // no pixels mapped yet for (dx = 0; dx <= 2*rad1; dx++) pixseq_done[dx][dy] = 0; ii = jj = 0; astep = 0.5 / rad1; // 0.5 pixel steps at rad1 from center for (angle = 0; angle < 2*PI; angle += astep) // loop full circle { pixseq_angle[ii] = jj; // start pixel sequence for this angle ii++; for (rad2 = 1; rad2 <= rad1; rad2++) // loop rad2 from center to edge { dx = lround(rad2 * cos(angle)); // pixel at angle and rad2 dy = lround(rad2 * sin(angle)); adx = rad1 + dx; ady = rad1 + dy; if (pixseq_done[adx][ady]) continue; // pixel already mapped pixseq_done[adx][ady] = 1; // map pixel pixseq_dx[jj] = dx; // save pixel sequence for angle pixseq_dy[jj] = dy; pixseq_rad[jj] = rad2; // pixel radius jj++; } pixseq_rad[jj] = 9999; // mark end of pixels for angle jj++; } pixseq_angle[ii] = 9999; // mark end of angle steps if (ii > max1 || jj > max2) // should not happen zappcrash("gradblur array overflow"); if (sa_stat == 3) Fbusy_goal = sa_Npixel; // setup progress tracking else Fbusy_goal = ww * hh; Fbusy_done = 0; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(gradblur_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = 0; CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * gradblur_wthread(void *arg) // worker thread function { using namespace gradblur_names; int index = *((int *) arg); int ii, jj, npix, dist = 0; int px, py, dx, dy; int ww = E3pxm->ww, hh = E3pxm->hh; float red, green, blue, f1, f2; float *pix1, *pix3, *pixN; int rad, blurrad, con; for (py = index+1; py < hh-1; py += NWT) // loop interior pixels for (px = 1; px < ww-1; px++) { if (gradblur_cancel) exit_wthread(); ii = py * ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } if (pixcon[ii] > con_limit) continue; // high contrast pixel pix1 = PXMpix(E1pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel red = pix1[0]; // blur center pixel green = pix1[1]; blue = pix1[2]; npix = 1; blurrad = 1.0 + blur_radius * (con_limit - pixcon[ii]) / con_limit; // blur radius for pixel, 1 - blur_radius for (ii = 0; ii < 2000; ii++) // loop angle around center pixel { jj = pixseq_angle[ii]; // 1st pixel for angle step ii if (jj == 9999) break; // none, end of angle loop while (true) // loop pixels from center to radius { rad = pixseq_rad[jj]; // next pixel step radius if (rad > blurrad) break; // stop here if beyond blur radius dx = pixseq_dx[jj]; // next pixel step px/py dy = pixseq_dy[jj]; if (px+dx < 0 || px+dx > ww-1) break; // stop here if off the edge if (py+dy < 0 || py+dy > hh-1) break; pixN = PXMpix(E1pxm,px+dx,py+dy); con = 255 * pixcontrast(pix1,pixN); if (con > con_limit) break; red += pixN[0]; // add pixel RGB values green += pixN[1]; blue += pixN[2]; npix++; jj++; } } pix3[0] = red / npix; // average pixel values pix3[1] = green / npix; pix3[2] = blue / npix; if (sa_stat == 3 && dist < sa_blend) { f1 = sa_blendfunc(dist); f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; } Fbusy_done++; // track progress } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // convert image to simulate an embossing int emboss_radius, emboss_color; float emboss_depth; float emboss_kernel[20][20]; // support radius <= 9 editfunc EFemboss; // menu function void m_emboss(GtkWidget *, cchar *menu) { int emboss_dialog_event(zdialog* zd, cchar *event); void * emboss_thread(void *); cchar *title = ZTX("Simulate Embossing"); F1_help_topic = "embossing"; EFemboss.menuname = menu; EFemboss.menufunc = m_emboss; EFemboss.funcname = "emboss"; EFemboss.Fscript = 1; // scripting supported 17.04 EFemboss.Farea = 2; // select area usable EFemboss.threadfunc = emboss_thread; // thread function if (! edit_setup(EFemboss)) return; // setup edit // Radius [_____] depth [_____] [_] Color zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); EFemboss.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","lab1","hb1",Bradius,"space=5"); zdialog_add_widget(zd,"zspin","radius","hb1","0|9|1|0"); zdialog_add_widget(zd,"label","lab2","hb1",ZTX("depth"),"space=5"); zdialog_add_widget(zd,"zspin","depth","hb1","0|99|1|0"); zdialog_add_widget(zd,"check","color","hb1",Bcolor,"space=8"); zdialog_run(zd,emboss_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int emboss_dialog_event(zdialog *zd, cchar *event) // emboss dialog event function { if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } zdialog_fetch(zd,"radius",emboss_radius); // get user inputs zdialog_fetch(zd,"depth",emboss_depth); zdialog_fetch(zd,"color",emboss_color); signal_thread(); // trigger update thread return 1; } // thread function - use multiple working threads void * emboss_thread(void *) { void * emboss_wthread(void *arg); int ii, dx, dy, rad; float depth, kern, coeff; while (true) { thread_idle_loop(); // wait for work or exit request rad = emboss_radius; depth = emboss_depth; coeff = 0.1 * depth / (rad * rad + 1); for (dy = -rad; dy <= rad; dy++) // build kernel with radius and depth for (dx = -rad; dx <= rad; dx++) { kern = coeff * (dx + dy); emboss_kernel[dx+rad][dy+rad] = kern; } emboss_kernel[rad][rad] = 1; // kernel center cell = 1 for (ii = 0; ii < NWT; ii++) // start worker threads start_wthread(emboss_wthread,&Nval[ii]); wait_wthreads(); // wait for comletion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * emboss_wthread(void *arg) // worker thread function { void emboss_1pix(int px, int py); int index = *((int *) (arg)); int px, py; for (py = index; py < E3pxm->hh; py += NWT) // process all pixels for (px = 0; px < E3pxm->ww; px++) emboss_1pix(px,py); exit_wthread(); return 0; // not executed, avoid gcc warning } void emboss_1pix(int px, int py) // process one pixel { int ii, dist = 0; float bright1, bright3; int rgb, dx, dy, rad; float *pix1, *pix3, *pixN; float sumpix, kern, dold, dnew; int nc = E1pxm->nc; if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) return; // outside pixel } rad = emboss_radius; if (px < rad || py < rad) return; if (px > E3pxm->ww-rad-1 || py > E3pxm->hh-rad-1) return; pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel if (emboss_color) { for (rgb = 0; rgb < 3; rgb++) { sumpix = 0; for (dy = -rad; dy <= rad; dy++) // loop surrounding block of pixels for (dx = -rad; dx <= rad; dx++) { pixN = pix1 + (dy * E1pxm->ww + dx) * nc; kern = emboss_kernel[dx+rad][dy+rad]; sumpix += kern * pixN[rgb]; bright1 = pix1[rgb]; bright3 = sumpix; if (bright3 < 0) bright3 = 0; if (bright3 > 255) bright3 = 255; if (sa_stat == 3 && dist < sa_blend) { // select area is active, dnew = sa_blendfunc(dist); // blend changes over sa_blend dold = 1.0 - dnew; bright3 = dnew * bright3 + dold * bright1; } pix3[rgb] = bright3; } } } else // use gray scale { sumpix = 0; for (dy = -rad; dy <= rad; dy++) // loop surrounding block of pixels for (dx = -rad; dx <= rad; dx++) { pixN = pix1 + (dy * E1pxm->ww + dx) * nc; kern = emboss_kernel[dx+rad][dy+rad]; sumpix += kern * (pixN[0] + pixN[1] + pixN[2]); } bright1 = 0.3333 * (pix1[0] + pix1[1] + pix1[2]); bright3 = 0.3333 * sumpix; if (bright3 < 0) bright3 = 0; if (bright3 > 255) bright3 = 255; if (sa_stat == 3 && dist < sa_blend) { // select area is active, dnew = sa_blendfunc(dist); // blend changes over sa_blend dold = 1.0 - dnew; bright3 = dnew * bright3 + dold * bright1; } pix3[0] = pix3[1] = pix3[2] = bright3; } return; } /********************************************************************************/ // convert image to simulate square tiles int tile_size, tile_gap, tile_3D; float *tile_pixmap = 0; editfunc EFtiles; void m_tiles(GtkWidget *, cchar *menu) { int tiles_dialog_event(zdialog *zd, cchar *event); void * tiles_thread(void *); F1_help_topic = "tiles"; EFtiles.menuname = menu; EFtiles.menufunc = m_tiles; EFtiles.funcname = "tiles"; EFtiles.Farea = 2; // select area usable EFtiles.Fscript = 1; // scripting supported 17.04 EFtiles.threadfunc = tiles_thread; // thread function if (! edit_setup(EFtiles)) return; // setup edit /*** _____________________________ | Simulate Tiles | | | | tile size [____] | | tile gap [____] | | 3D depth [____] | | | | [Apply] [Done] [Cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(ZTX("Simulate Tiles"),Mwin,Bapply,Bdone,Bcancel,null); EFtiles.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labt","hb1",ZTX("tile size"),"space=5"); zdialog_add_widget(zd,"zspin","size","hb1","1|99|1|10","space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labg","hb2",ZTX("tile gap"),"space=5"); zdialog_add_widget(zd,"zspin","gap","hb2","0|9|1|1","space=5"); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=2"); zdialog_add_widget(zd,"label","labd","hb3",ZTX("3D depth"),"space=5"); zdialog_add_widget(zd,"zspin","3D","hb3","0|9|1|1","space=5"); tile_size = 10; // defaults tile_gap = 1; tile_3D = 0; int cc = E1pxm->ww * E1pxm->hh * 3 * sizeof(float); tile_pixmap = (float *) zmalloc(cc); // set up pixel color map memset(tile_pixmap,0,cc); zdialog_run(zd,tiles_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int tiles_dialog_event(zdialog * zd, cchar *event) { if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) // dialog complete { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"size",tile_size); // get tile size zdialog_fetch(zd,"gap",tile_gap); // get tile gap zdialog_fetch(zd,"3D",tile_3D); // get 3D yes/no if (tile_size < 2) edit_reset(); // restore original image else signal_thread(); // trigger working thread return 1; } else if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit zfree(tile_pixmap); } return 1; } // image tiles thread function void * tiles_thread(void *) { int sg, gg, eg, sumpix; int ii, jj, px, py, qx, qy, dist; int td, bd, ld, rd; float red = 0, green = 0, blue = 0; float hired = 0, higreen = 0, hiblue = 0; float lored = 0, logreen = 0, loblue = 0; float dnew, dold; float *pix1, *pix3; while (true) { thread_idle_loop(); // wait for work or exit request sg = tile_size + tile_gap; gg = tile_gap; for (py = 0; py < E1pxm->hh; py += sg) // initz. pixel color map for for (px = 0; px < E1pxm->ww; px += sg) // given pixel size { sumpix = red = green = blue = 0; for (qy = py + gg; qy < py + sg; qy++) // get mean color for pixel block for (qx = px + gg; qx < px + sg; qx++) { if (qy > E1pxm->hh-1 || qx > E1pxm->ww-1) continue; pix1 = PXMpix(E1pxm,qx,qy); red += pix1[0]; green += pix1[1]; blue += pix1[2]; sumpix++; } if (! sumpix) continue; red = (red / sumpix); // tile color green = (green / sumpix); blue = (blue / sumpix); if (tile_3D) { hired = 0.5 * (red + 255.0); // 3D highlight and shadow colors higreen = 0.5 * (green + 255.0); hiblue = 0.5 * (blue + 255.0); lored = 0.5 * red; logreen = 0.5 * green; loblue = 0.5 * blue; } for (qy = py; qy < py + sg; qy++) // set color for pixels in block for (qx = px; qx < px + sg; qx++) { if (qy > E1pxm->hh-1 || qx > E1pxm->ww-1) continue; jj = (qy * E1pxm->ww + qx) * 3; // pixel index td = qx - px; // distance from top edge of tile bd = px + sg - qx; // distance from bottom edge ld = qy - py; // distance from left edge rd = py + sg - qy; // distance from right edge if (td < gg || ld < gg) { // gap pixels are black tile_pixmap[jj] = tile_pixmap[jj+1] = tile_pixmap[jj+2] = 0; continue; } if (tile_3D) { eg = tile_3D; // tile "depth" if (td < gg+eg && td < rd) { // top edge: highlight tile_pixmap[jj] = hired; // bevel at right end tile_pixmap[jj+1] = higreen; tile_pixmap[jj+2] = hiblue; continue; } if (ld < gg+eg && ld < bd) { // left edge: highlight tile_pixmap[jj] = hired; // bevel at bottom end tile_pixmap[jj+1] = higreen; tile_pixmap[jj+2] = hiblue; continue; } if (bd <= eg || rd <= eg) { // bottom or right edge: shadow tile_pixmap[jj] = lored; tile_pixmap[jj+1] = logreen; tile_pixmap[jj+2] = loblue; continue; } } tile_pixmap[jj] = red; tile_pixmap[jj+1] = green; tile_pixmap[jj+2] = blue; } } if (sa_stat == 3) // process selected area { for (ii = 0; ii < E3pxm->ww * E3pxm->hh; ii++) // find pixels in select area { if (! sa_pixmap[ii]) continue; py = ii / E3pxm->ww; px = ii - py * E3pxm->ww; dist = sa_pixmap[ii]; // distance from edge pix3 = PXMpix(E3pxm,px,py); jj = (py * E3pxm->ww + px) * 3; if (dist >= sa_blend) { // blend changes over sa_blend pix3[0] = tile_pixmap[jj]; pix3[1] = tile_pixmap[jj+1]; // apply block color to member pixels pix3[2] = tile_pixmap[jj+2]; } else { dnew = sa_blendfunc(dist); dold = 1.0 - dnew; pix1 = PXMpix(E1pxm,px,py); pix3[0] = dnew * tile_pixmap[jj] + dold * pix1[0]; pix3[1] = dnew * tile_pixmap[jj+1] + dold * pix1[1]; pix3[2] = dnew * tile_pixmap[jj+2] + dold * pix1[2]; } } } else // process entire image { for (py = 0; py < E3pxm->hh-1; py++) // loop all image pixels for (px = 0; px < E3pxm->ww-1; px++) { pix3 = PXMpix(E3pxm,px,py); // target pixel jj = (py * E3pxm->ww + px) * 3; // color map for (px,py) pix3[0] = tile_pixmap[jj]; pix3[1] = tile_pixmap[jj+1]; pix3[2] = tile_pixmap[jj+2]; } } CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } /********************************************************************************/ // convert image to dot grid (like Roy Lichtenstein) int dot_size; // tile size enclosing dots editfunc EFdots; void m_dots(GtkWidget *, cchar *menu) { int dots_dialog_event(zdialog *zd, cchar *event); void * dots_thread(void *); F1_help_topic = "dot_matrix"; EFdots.menuname = menu; EFdots.menufunc = m_dots; EFdots.funcname = "dots"; EFdots.Farea = 2; // select area usable EFdots.Fscript = 1; // scripting supported 17.04 EFdots.threadfunc = dots_thread; // thread function if (! edit_setup(EFdots)) return; // setup edit zdialog *zd = zdialog_new(ZTX("Convert Image to Dots"),Mwin,Bapply,Bdone,Bcancel,null); EFdots.zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labt","hb1",ZTX("dot size"),"space=5"); zdialog_add_widget(zd,"zspin","size","hb1","4|99|1|9","space=5"); dot_size = 9; zdialog_run(zd,dots_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int dots_dialog_event(zdialog * zd, cchar *event) { if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"size",dot_size); // get dot size signal_thread(); // trigger working thread } else if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit } return 1; } // image dots thread function void * dots_thread(void *) { int ds, ii, sumpix; int dist, shift1, shift2; int px, py, px1, px2, py1, py2; int qx, qy, qx1, qx2, qy1, qy2; float *pix1, *pix3; float red, green, blue, maxrgb; float relmax, radius, radius2a, radius2b, f1, f2; float fpi = 1.0 / 3.14159; float dcx, dcy, qcx, qcy, qdist2; int nc = E3pxm->nc; while (true) { thread_idle_loop(); // wait for work or exit request paintlock(1); // block window updates ds = dot_size; // dot size and tile size for (py = 0; py < E3pxm->hh; py++) // set image to black for (px = 0; px < E3pxm->ww; px++) { // not alpha channel pix3 = PXMpix(E3pxm,px,py); pix3[0] = pix3[1] = pix3[2] = 0; } px1 = 0; // limits for tiles px2 = E3pxm->ww - ds; py1 = 0; py2 = E3pxm->hh - ds; if (sa_stat == 3) { // reduce for active area px1 = sa_minx; px2 = sa_maxx; py1 = sa_miny; py2 = sa_maxy; if (px2 > E3pxm->ww - ds) px2 = E3pxm->ww - ds; if (py2 > E3pxm->hh - ds) py2 = E3pxm->hh - ds; } shift1 = 0; for (py = py1; py < py2; py += ds) // loop all tiles in input image { shift1 = 1 - shift1; // offset alternate rows shift2 = 0.5 * shift1 * ds; for (px = px1 + shift2; px < px2; px += ds) { sumpix = red = green = blue = 0; for (qy = py; qy < py + ds; qy++) // loop all pixels in tile for (qx = px; qx < px + ds; qx++) // get mean RGB levels for tile { pix1 = PXMpix(E1pxm,qx,qy); red += pix1[0]; green += pix1[1]; blue += pix1[2]; sumpix++; } red = red / sumpix; // mean RGB levels, 0 to 255.9 green = green / sumpix; blue = blue / sumpix; maxrgb = red; // max. mean RGB level, 0 to 255.9 if (green > maxrgb) maxrgb = green; if (blue > maxrgb) maxrgb = blue; relmax = maxrgb / 256.0; // max. RGB as 0 to 0.999 radius = ds * sqrt(fpi * relmax); // radius of dot with maximized color radius = radius * 0.875; // deliberate reduction if (radius < 0.5) continue; red = 255.9 * red / maxrgb; // dot color, maximized green = 255.9 * green / maxrgb; blue = 255.9 * blue / maxrgb; dcx = px + 0.5 * ds; // center of dot / tile dcy = py + 0.5 * ds; qx1 = dcx - radius; // pixels within dot radius of center qx2 = dcx + radius; qy1 = dcy - radius; qy2 = dcy + radius; radius2a = (radius + 0.5) * (radius + 0.5); radius2b = (radius - 0.5) * (radius - 0.5); for (qy = qy1; qy <= qy2; qy++) // loop all pixels within dot radius for (qx = qx1; qx <= qx2; qx++) { qcx = qx + 0.5; // center of pixel qcy = qy + 0.5; qdist2 = (qcx-dcx)*(qcx-dcx) + (qcy-dcy)*(qcy-dcy); // pixel distance**2 from center of dot if (qdist2 > radius2a) f1 = 0.0; // pixel outside dot, no color else if (qdist2 < radius2b) f1 = 1.0; // pixel inside dot, full color else f1 = 1.0 - sqrt(qdist2) + radius - 0.5; // pixel straddles edge, some color pix3 = PXMpix(E3pxm,qx,qy); pix3[0] = f1 * red; pix3[1] = f1 * green; pix3[2] = f1 * blue; } } } if (sa_stat == 3) // select area active { pix1 = E1pxm->pixels; pix3 = E3pxm->pixels; for (ii = 0; ii < E3pxm->ww * E3pxm->hh; ii++) // loop all pixels { dist = sa_pixmap[ii]; if (dist == 0) // outside area memmove(pix3,pix1,3*sizeof(float)); else if (dist < sa_blend) { f1 = sa_blendfunc(dist); // pixel in blend region f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix1[0]; // output pixel is mix of input and output pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; } pix1 += nc; pix3 += nc; } } paintlock(0); // unblock window updates CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } /********************************************************************************/ // convert image to simulate a painting namespace painting_names { editfunc EFpainting; int color_depth; int group_area; float color_match; int borders; typedef struct { int16 px, py; char direc; } spixstack; int Nstack; spixstack *pixstack; // pixel group search memory int *pixgroup; // maps (px,py) to pixel group no. int *groupcount; // count of pixels in each group int group; char direc; float gcolor[3]; } void m_painting(GtkWidget *, cchar *menu) { using namespace painting_names; int painting_dialog_event(zdialog *zd, cchar *event); void * painting_thread(void *); F1_help_topic = "painting"; EFpainting.menuname = menu; EFpainting.menufunc = m_painting; EFpainting.funcname = "painting"; EFpainting.Farea = 2; // select area usable EFpainting.Fscript = 1; // scripting supported 17.04 EFpainting.threadfunc = painting_thread; // thread function if (! edit_setup(EFpainting)) return; // setup edit zdialog *zd = zdialog_new(ZTX("Simulate Painting"),Mwin,Bapply,Bdone,Bcancel,null); EFpainting.zd = zd; /*** _____________________________ | Simulate Painting | | | | color depth [____] | | patch area goal [____] | | req. color match [____] | | borders [_] | | | | [Apply] [Done] [Cancel] | |_____________________________| ***/ zdialog_add_widget(zd,"hbox","hbcd","dialog",0,"space=1"); zdialog_add_widget(zd,"label","lab1","hbcd",ZTX("color depth"),"space=5"); zdialog_add_widget(zd,"zspin","colordepth","hbcd","1|5|1|3","space=5"); zdialog_add_widget(zd,"hbox","hbts","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labts","hbts",ZTX("patch area goal"),"space=5"); zdialog_add_widget(zd,"zspin","grouparea","hbts","0|9999|10|1000","space=5"); zdialog_add_widget(zd,"hbox","hbcm","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labcm","hbcm",ZTX("req. color match"),"space=5"); zdialog_add_widget(zd,"zspin","colormatch","hbcm","0|99|1|50","space=5"); zdialog_add_widget(zd,"hbox","hbbd","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labbd","hbbd",ZTX("borders"),"space=5"); zdialog_add_widget(zd,"check","borders","hbbd",0,"space=2"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_run(zd,painting_dialog_event,"save"); // run dialog, parallel return; } // dialog event and completion callback function int painting_dialog_event(zdialog *zd, cchar *event) { using namespace painting_names; if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active zdialog_fetch(zd,"colordepth",color_depth); // color depth zdialog_fetch(zd,"grouparea",group_area); // target group area (pixels) zdialog_fetch(zd,"colormatch",color_match); // req. color match to combine groups zdialog_fetch(zd,"borders",borders); // borders wanted color_match = 0.01 * color_match; // scale 0 to 1 signal_thread(); // do the work } else if (zd->zstat == 2) edit_done(0); // done, commit edit else edit_cancel(0); // cancel, discard edit return 1; } return 0; } // painting thread function void * painting_thread(void *) { void painting_colordepth(); void painting_pixgroups(); void painting_mergegroups(); void painting_paintborders(); void painting_blend(); while (true) { thread_idle_loop(); // wait for work or exit request painting_colordepth(); // set new color depth painting_pixgroups(); // group pixel patches of a color painting_mergegroups(); // merge smaller into larger groups painting_paintborders(); // add borders around groups painting_blend(); // blend edges of selected area CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } // set the specified color depth, 1-5 bits/color void painting_colordepth() { using namespace painting_names; int ii, px, py, rgb; uint16 m1, m2, val1, val3; float fmag, *pix1, *pix3; m1 = 0xFFFF << (16 - color_depth); // 5 > 1111100000000000 m2 = 0x8000 >> color_depth; // 5 > 0000010000000000 fmag = 65535.0 / m1; // full brightness range for (py = 0; py < E3pxm->hh; py++) // loop all pixels for (px = 0; px < E3pxm->ww; px++) { if (sa_stat == 3) { // select area active ii = py * E3pxm->ww + px; if (! sa_pixmap[ii]) continue; // outside pixel } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel for (rgb = 0; rgb < 3; rgb++) { val1 = 256 * pix1[rgb]; if (val1 < m1) val3 = (val1 + m2) & m1; else val3 = m1; val3 = val3 * fmag; pix3[rgb] = val3 / 256; } } return; } // find all groups of contiguous pixels with the same color void painting_pixgroups() { using namespace painting_names; void painting_pushpix(int px, int py); int cc1, cc2; int ii, kk, px, py; int ppx, ppy, npx, npy; float *pix3; cc1 = E3pxm->ww * E3pxm->hh; cc2 = cc1 * sizeof(int); pixgroup = (int *) zmalloc(cc2); // maps pixel to assigned group memset(pixgroup,0,cc2); if (sa_stat == 3) cc1 = sa_Npixel; cc2 = cc1 * sizeof(spixstack); pixstack = (spixstack *) zmalloc(cc2); // memory stack for pixel search memset(pixstack,0,cc2); cc2 = cc1 * sizeof(int); groupcount = (int *) zmalloc(cc2); // counts pixels per group memset(groupcount,0,cc2); group = 0; for (py = 0; py < E3pxm->hh; py++) // loop all pixels for (px = 0; px < E3pxm->ww; px++) { kk = py * E3pxm->ww + px; if (sa_stat == 3 && ! sa_pixmap[kk]) continue; if (pixgroup[kk]) continue; // already assigned to group pixgroup[kk] = ++group; // assign next group ++groupcount[group]; pix3 = PXMpix(E3pxm,px,py); gcolor[0] = pix3[0]; gcolor[1] = pix3[1]; gcolor[2] = pix3[2]; pixstack[0].px = px; // put pixel into stack with pixstack[0].py = py; // direction = ahead pixstack[0].direc = 'a'; Nstack = 1; while (Nstack) // overhauled { kk = Nstack - 1; // get last pixel in stack px = pixstack[kk].px; py = pixstack[kk].py; direc = pixstack[kk].direc; if (direc == 'x') { Nstack--; continue; } if (Nstack > 1) { ii = Nstack - 2; // get prior pixel in stack ppx = pixstack[ii].px; ppy = pixstack[ii].py; } else { ppx = px - 1; // if only one, assume prior = left ppy = py; } if (direc == 'a') { // next ahead pixel npx = px + px - ppx; npy = py + py - ppy; pixstack[kk].direc = 'r'; // next search direction } else if (direc == 'r') { // next right pixel npx = px + py - ppy; npy = py + px - ppx; pixstack[kk].direc = 'l'; } else { /* direc = 'l' */ // next left pixel npx = px + ppy - py; npy = py + ppx - px; pixstack[kk].direc = 'x'; } if (npx < 0 || npx > E3pxm->ww-1) continue; // pixel off the edge if (npy < 0 || npy > E3pxm->hh-1) continue; kk = npy * E3pxm->ww + npx; if (sa_stat == 3 && ! sa_pixmap[kk]) continue; // pixel outside area if (pixgroup[kk]) continue; // pixel already assigned pix3 = PXMpix(E3pxm,npx,npy); if (pix3[0] != gcolor[0] || pix3[1] != gcolor[1] // not same color as group || pix3[2] != gcolor[2]) continue; pixgroup[kk] = group; // assign pixel to group ++groupcount[group]; kk = Nstack++; // put pixel into stack pixstack[kk].px = npx; pixstack[kk].py = npy; pixstack[kk].direc = 'a'; // direction = ahead } } return; } // merge small pixel groups into adjacent larger groups with best color match void painting_mergegroups() { using namespace painting_names; int ii, jj, kk, px, py, npx, npy; int nccc, mcount, group2; int nnpx[4] = { 0, -1, +1, 0 }; int nnpy[4] = { -1, 0, 0, +1 }; float match; float *pix3, *pixN; typedef struct { int group; double match; float pixM[3]; } snewgroup; snewgroup *newgroup; nccc = (group + 1) * sizeof(snewgroup); newgroup = (snewgroup *) zmalloc(nccc); if (sa_stat == 3) // process select area { while (true) { memset(newgroup,0,nccc); for (ii = 0; ii < E3pxm->ww * E3pxm->hh; ii++) // find pixels in select area { if (! sa_pixmap[ii]) continue; py = ii / E3pxm->ww; px = ii - py * E3pxm->ww; kk = E3pxm->ww * py + px; // get assigned group group = pixgroup[kk]; if (groupcount[group] >= group_area) continue; // group count large enough pix3 = PXMpix(E3pxm,px,py); for (jj = 0; jj < 4; jj++) // get 4 neighbor pixels { npx = px + nnpx[jj]; npy = py + nnpy[jj]; if (npx < 0 || npx >= E3pxm->ww) continue; // off the edge if (npy < 0 || npy >= E3pxm->hh) continue; kk = E3pxm->ww * npy + npx; if (! sa_pixmap[kk]) continue; // pixel outside area if (pixgroup[kk] == group) continue; // already in same group pixN = PXMpix(E3pxm,npx,npy); // match group neighbor color to my color match = PIXMATCH(pix3,pixN); // 0..1 = zero..perfect match if (match < color_match) continue; if (match > newgroup[group].match) { newgroup[group].match = match; // remember best match newgroup[group].group = pixgroup[kk]; // and corresp. group no. newgroup[group].pixM[0] = pixN[0]; // and corresp. new color newgroup[group].pixM[1] = pixN[1]; newgroup[group].pixM[2] = pixN[2]; } } } mcount = 0; for (ii = 0; ii < E3pxm->ww * E3pxm->hh; ii++) // find pixels in select area { if (! sa_pixmap[ii]) continue; py = ii / E3pxm->ww; px = ii - py * E3pxm->ww; kk = E3pxm->ww * py + px; group = pixgroup[kk]; // test for new group assignment group2 = newgroup[group].group; if (! group2) continue; if (groupcount[group] > groupcount[group2]) continue; // accept only bigger new group pixgroup[kk] = group2; // make new group assignment --groupcount[group]; ++groupcount[group2]; pix3 = PXMpix(E3pxm,px,py); // make new color assignment pix3[0] = newgroup[group].pixM[0]; pix3[1] = newgroup[group].pixM[1]; pix3[2] = newgroup[group].pixM[2]; mcount++; } if (mcount == 0) break; } } else // process entire image { while (true) { memset(newgroup,0,nccc); for (py = 0; py < E3pxm->hh; py++) // loop all pixels for (px = 0; px < E3pxm->ww; px++) { kk = E3pxm->ww * py + px; // get assigned group group = pixgroup[kk]; if (groupcount[group] >= group_area) continue; // group count large enough pix3 = PXMpix(E3pxm,px,py); for (jj = 0; jj < 4; jj++) // get 4 neighbor pixels { npx = px + nnpx[jj]; npy = py + nnpy[jj]; if (npx < 0 || npx >= E3pxm->ww) continue; // off the edge if (npy < 0 || npy >= E3pxm->hh) continue; kk = E3pxm->ww * npy + npx; if (pixgroup[kk] == group) continue; // in same group pixN = PXMpix(E3pxm,npx,npy); // match color of group neighbor match = PIXMATCH(pix3,pixN); // 0..1 = zero..perfect match if (match < color_match) continue; if (match > newgroup[group].match) { newgroup[group].match = match; // remember best match newgroup[group].group = pixgroup[kk]; // and corresp. group no. newgroup[group].pixM[0] = pixN[0]; // and corresp. new color newgroup[group].pixM[1] = pixN[1]; newgroup[group].pixM[2] = pixN[2]; } } } mcount = 0; for (py = 0; py < E3pxm->hh; py++) // loop all pixels for (px = 0; px < E3pxm->ww; px++) { kk = E3pxm->ww * py + px; group = pixgroup[kk]; // test for new group assignment group2 = newgroup[group].group; if (! group2) continue; if (groupcount[group] > groupcount[group2]) continue; // accept only bigger new group pixgroup[kk] = group2; // make new group assignment --groupcount[group]; ++groupcount[group2]; pix3 = PXMpix(E3pxm,px,py); // make new color assignment pix3[0] = newgroup[group].pixM[0]; pix3[1] = newgroup[group].pixM[1]; pix3[2] = newgroup[group].pixM[2]; mcount++; } if (mcount == 0) break; } } zfree(pixgroup); zfree(pixstack); zfree(groupcount); zfree(newgroup); return; } // paint borders between the groups of contiguous pixels void painting_paintborders() { using namespace painting_names; int ii, kk, px, py, cc; float *pix3, *pixL, *pixA; if (! borders) return; cc = E3pxm->ww * E3pxm->hh; char * pixblack = (char *) zmalloc(cc); memset(pixblack,0,cc); if (sa_stat == 3) { for (ii = 0; ii < E3pxm->ww * E3pxm->hh; ii++) // find pixels in select area { if (! sa_pixmap[ii]) continue; py = ii / E3pxm->ww; px = ii - py * E3pxm->ww; if (px < 1 || py < 1) continue; pix3 = PXMpix(E3pxm,px,py); pixL = PXMpix(E3pxm,px-1,py); pixA = PXMpix(E3pxm,px,py-1); if (pix3[0] != pixL[0] || pix3[1] != pixL[1] || pix3[2] != pixL[2]) { kk = ii - 1; if (pixblack[kk]) continue; kk += 1; pixblack[kk] = 1; continue; } if (pix3[0] != pixA[0] || pix3[1] != pixA[1] || pix3[2] != pixA[2]) { kk = ii - E3pxm->ww; if (pixblack[kk]) continue; kk += E3pxm->ww; pixblack[kk] = 1; } } for (ii = 0; ii < E3pxm->ww * E3pxm->hh; ii++) // find pixels in select area { if (! sa_pixmap[ii]) continue; py = ii / E3pxm->ww; px = ii - py * E3pxm->ww; if (px < 1 || py < 1) continue; if (! pixblack[ii]) continue; pix3 = PXMpix(E3pxm,px,py); pix3[0] = pix3[1] = pix3[2] = 0; } } else { for (py = 1; py < E3pxm->hh; py++) // loop all pixels for (px = 1; px < E3pxm->ww; px++) // omit top and left { pix3 = PXMpix(E3pxm,px,py); // output pixel pixL = PXMpix(E3pxm,px-1,py); // pixel to left pixA = PXMpix(E3pxm,px,py-1); // pixel above if (pix3[0] != pixL[0] || pix3[1] != pixL[1] || pix3[2] != pixL[2]) { kk = E3pxm->ww * py + px-1; // have horiz. transition if (pixblack[kk]) continue; kk += 1; pixblack[kk] = 1; continue; } if (pix3[0] != pixA[0] || pix3[1] != pixA[1] || pix3[2] != pixA[2]) { kk = E3pxm->ww * (py-1) + px; // have vertical transition if (pixblack[kk]) continue; kk += E3pxm->ww; pixblack[kk] = 1; } } for (py = 1; py < E3pxm->hh; py++) for (px = 1; px < E3pxm->ww; px++) { kk = E3pxm->ww * py + px; if (! pixblack[kk]) continue; pix3 = PXMpix(E3pxm,px,py); pix3[0] = pix3[1] = pix3[2] = 0; } } zfree(pixblack); return; } // blend edges of selected area void painting_blend() { int ii, px, py, dist; float *pix1, *pix3; double f1, f2; if (sa_stat == 3 && sa_blend > 0) { for (ii = 0; ii < E3pxm->ww * E3pxm->hh; ii++) // find pixels in select area { dist = sa_pixmap[ii]; if (! dist || dist >= sa_blend) continue; py = ii / E3pxm->ww; px = ii - py * E3pxm->ww; pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel f2 = sa_blendfunc(dist); // changes over distance sa_blend f1 = 1.0 - f2; pix3[0] = f1 * pix1[0] + f2 * pix3[0]; pix3[1] = f1 * pix1[1] + f2 * pix3[1]; pix3[2] = f1 * pix1[2] + f2 * pix3[2]; } } return; } /******************************************************************************** Vignette function 1. Change the brightness from center to edge using a curve. 2. Change the color from center to edge using a color and a curve. (the pixel varies between original RGB and selected color) 3. Mouse click or drag on image sets a new vignette center. *********************************************************************************/ void vign_mousefunc(); editfunc EFvignette; uint8 vignette_RGB[3] = { 0, 0, 255 }; int vignette_spc; float vign_cx, vign_cy; float vign_rad; void m_vignette(GtkWidget *, cchar *) { int Vign_dialog_event(zdialog *zd, cchar *event); void Vign_curvedit(int); void * Vign_thread(void *); F1_help_topic = "vignette"; cchar *title = ZTX("Vignette"); EFvignette.menufunc = m_vignette; EFvignette.funcname = "vignette"; EFvignette.Farea = 2; // select area usable EFvignette.FprevReq = 1; // use preview image EFvignette.threadfunc = Vign_thread; // thread function EFvignette.mousefunc = vign_mousefunc; // mouse function if (! edit_setup(EFvignette)) return; // setup edit /*** _____________________________ | | | | | curve drawing area | | | |_____________________________| center edge (o) Brightness (o) Color [___] Curve File: [ Open ] [ Save ] [Done] [Cancel] ***/ zdialog *zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); EFvignette.zd = zd; zdialog_add_widget(zd,"frame","frame","dialog",0,"expand"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labcenter","hb1",Bcenter,"space=4"); zdialog_add_widget(zd,"label","space","hb1",0,"expand"); zdialog_add_widget(zd,"label","labedge","hb1",Bedge,"space=5"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","RBbrite","hb2",Bbrightness,"space=5"); zdialog_add_widget(zd,"radio","RBcolor","hb2",Bcolor,"space=5"); zdialog_add_widget(zd,"colorbutt","color","hb2","0|0|255"); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labcurve","hb3",Bcurvefile,"space=5"); zdialog_add_widget(zd,"button","load","hb3",Bopen,"space=5"); zdialog_add_widget(zd,"button","savecurve","hb3",Bsave,"space=5"); vignette_RGB[0] = vignette_RGB[1] = 0; // initial color = blue vignette_RGB[2] = 255; vign_cx = E3pxm->ww / 2; // initial vignette center vign_cy = E3pxm->hh / 2; vign_rad = vign_cx * vign_cx + vign_cy * vign_cy; // radius = distance to corners vign_rad = sqrtf(vign_rad); zdialog_stuff(zd,"RBbrite",1); // default curve = brightness GtkWidget *frame = zdialog_widget(zd,"frame"); // set up curve edit spldat *sd = splcurve_init(frame,Vign_curvedit); EFvignette.curves = sd; sd->Nspc = 2; // 2 curves sd->vert[0] = 0; // curve 0 = brightness curve sd->nap[0] = 2; sd->apx[0][0] = 0.01; sd->apy[0][0] = 0.5; sd->apx[0][1] = 0.99; sd->apy[0][1] = 0.5; splcurve_generate(sd,0); sd->vert[1] = 0; // curve 1 = color curve sd->nap[1] = 2; sd->apx[1][0] = 0.01; sd->apy[1][0] = 0.01; sd->apx[1][1] = 0.99; sd->apy[1][1] = 0.01; splcurve_generate(sd,1); vignette_spc = 0; // initial curve = brightness sd->fact[0] = 1; sd->fact[1] = 0; zdialog_resize(zd,300,300); zdialog_run(zd,Vign_dialog_event,"save"); // run dialog - parallel takeMouse(vign_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int Vign_dialog_event(zdialog *zd, cchar *event) { void Vign_curvedit(int); spldat *sd = EFvignette.curves; int ii; char color[20]; cchar *pp; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strmatch(event,"fullsize")) { // from select area edit_fullsize(); signal_thread(); return 1; } if (zd->zstat) { if (zd->zstat == 1) { // done wait_thread_idle(); // insure thread done float R = 1.0 * E0pxm->ww / E3pxm->ww; vign_cx = R * vign_cx; // scale geometries to full size vign_cy = R * vign_cy; vign_rad = R * vign_rad; edit_fullsize(); // get full size image signal_thread(); edit_done(0); // commit edit } else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(vign_mousefunc,dragcursor); // connect mouse function if (strmatchN(event,"RB",2)) { // new choice of curve sd->fact[0] = sd->fact[1] = 0; ii = strmatchV(event,"RBbrite","RBcolor",null); vignette_spc = ii = ii - 1; sd->fact[ii] = 1; // active curve splcurve_generate(sd,ii); // regenerate curve gtk_widget_queue_draw(sd->drawarea); // draw curve signal_thread(); } if (strmatch(event,"blendwidth")) signal_thread(); if (strmatch(event,"color")) { // change color zdialog_fetch(zd,"color",color,19); // get color from color wheel pp = strField(color,"|",1); if (pp) vignette_RGB[0] = atoi(pp); pp = strField(color,"|",2); if (pp) vignette_RGB[1] = atoi(pp); pp = strField(color,"|",3); if (pp) vignette_RGB[2] = atoi(pp); signal_thread(); // trigger update thread } if (strmatch(event,"load")) { // load saved curve splcurve_load(sd); Vign_curvedit(0); signal_thread(); return 0; } if (strmatch(event,"savecurve")) { // save curve to file splcurve_save(sd); return 0; } return 0; } // get mouse position and set new center for vignette void vign_mousefunc() // mouse function { if (! LMclick && ! Mdrag) return; LMclick = 0; vign_cx = Mxposn; // new vignette center = mouse position vign_cy = Myposn; Mxdrag = Mydrag = 0; signal_thread(); // trigger image update return; } // this function is called when the curve is edited void Vign_curvedit(int) { signal_thread(); // update image return; } // thread function void * Vign_thread(void *) { void * Vign_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start working threads start_wthread(Vign_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); } return 0; // not executed, stop g++ warning } // working thread void * Vign_wthread(void *arg) { float *pix1, *pix3; int index, ii, kk, px, py, dist = 0; float cx, cy, rad, radx, rady, f1, f2, xval, yval; float red1, green1, blue1, red3, green3, blue3, cmax; spldat *sd = EFvignette.curves; cx = vign_cx; // vignette center (mouse) cy = vign_cy; index = *((int *) arg); for (py = index; py < E3pxm->hh; py += NWT) // loop all image pixels for (px = 0; px < E3pxm->ww; px++) { ii = py * E3pxm->ww + px; if (sa_stat == 3) { // select area active dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel is outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel red1 = pix1[0]; // input RGB green1 = pix1[1]; blue1 = pix1[2]; radx = px - cx; // distance from vignette center rady = py - cy; rad = sqrtf(radx*radx + rady*rady); // (px,py) distance from center xval = rad / vign_rad; // scale 0 to 1.0 kk = 999.0 * xval; // scale 0 to 999 if (kk > 999) kk = 999; // beyond radius yval = sd->yval[0][kk]; // brightness curve y-value 0 to 1.0 if (yval > 1.0) yval = 1.0; yval = 2.0 * yval; // 0 to 2.0 red3 = yval * red1; // adjust brightness green3 = yval * green1; blue3 = yval * blue1; yval = sd->yval[1][kk]; // color curve y-value 0 to 1.0 if (yval > 1.0) yval = 1.0; f1 = yval; // 0 to 1.0 new color f2 = 1.0 - f1; // 1.0 to 0 old color red3 = f1 * vignette_RGB[0] + f2 * red3; // mix input and vignette color green3 = f1 * vignette_RGB[1] + f2 * green3; blue3 = f1 * vignette_RGB[2] + f2 * blue3; if (sa_stat == 3 && dist < sa_blend) { // select area is active, f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; red3 = f1 * red3 + f2 * red1; green3 = f1 * green3 + f2 * green1; blue3 = f1 * blue3 + f2 * blue1; } cmax = red3; // detect overflow if (green3 > cmax) cmax = green3; if (blue3 > cmax) cmax = blue3; if (cmax > 255.9) { // stop overflow red3 = red3 * 255.9 / cmax; green3 = green3 * 255.9 / cmax; blue3 = blue3 * 255.9 / cmax; } pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; } exit_wthread(); return 0; // not executed, stop g++ warning } /********************************************************************************/ // texture menu function // apply a random texture to the image namespace texturenames { editfunc EFtexture; // edit function data int radius = 4; int power = 40; int e3ww, e3hh; } void m_texture(GtkWidget *, cchar *menu) { using namespace texturenames; int texture_dialog_event(zdialog* zd, const char *event); void * texture_thread(void *); zdialog *zd; F1_help_topic = "texture"; EFtexture.menuname = menu; EFtexture.menufunc = m_texture; EFtexture.funcname = "texture"; // function name EFtexture.Farea = 2; // select area OK EFtexture.Fscript = 1; // scripting supported 17.04 EFtexture.threadfunc = texture_thread; // thread function if (! edit_setup(EFtexture)) return; // setup edit e3ww = E3pxm->ww; // image dimensions e3hh = E3pxm->hh; /*** ___________________________________ | Add Texture | | | | Radius [___] Power [___] | | | | [apply] [done] [cancel] | |___________________________________| ***/ zd = zdialog_new(ZTX("Add Texture"),Mwin,Bapply,Bdone,Bcancel,null); // texture dialog CEF->zd = zd; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=10"); zdialog_add_widget(zd,"label","labrad","hb1",Bradius,"space=3"); zdialog_add_widget(zd,"zspin","radius","hb1","1|40|1|4"); zdialog_add_widget(zd,"label","space","hb1",0,"space=1"); zdialog_add_widget(zd,"label","labpow","hb1",Bpower); zdialog_add_widget(zd,"zspin","power","hb1","1|100|1|40","space=3"); zdialog_stuff(zd,"radius",radius); zdialog_stuff(zd,"power",power); zdialog_run(zd,texture_dialog_event,"save"); // run dialog - parallel return; } // texture dialog event and completion function int texture_dialog_event(zdialog *zd, const char *event) { using namespace texturenames; if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 1; // from script if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { // apply zd->zstat = 0; // keep dialog active edit_reset(); zdialog_fetch(zd,"radius",radius); zdialog_fetch(zd,"power",power); signal_thread(); // process return 1; } if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } return 1; } // thread function - update image void * texture_thread(void *) { using namespace texturenames; void * texture_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request if (sa_stat == 3) Fbusy_goal = sa_Npixel; // set up progress monitor else Fbusy_goal = e3ww * e3hh; Fbusy_done = 0; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(texture_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = Fbusy_done = 0; CEF->Fmods = 1; // image modified CEF->Fsaved = 0; // not saved Fpaint2(); // update window } return 0; // not executed, stop warning } void * texture_wthread(void *arg) // worker thread function { using namespace texturenames; int index = *((int *) (arg)); int px, py, qx, qy, npix, dist = 0; int ii, rank1, rank2; float fred, fgreen, fblue; float *pix1, *pix3, *qpix; float pow, ff, f1, f3; float pbright, qbright; pow = 0.001 * powf(power,1.5); // 0.001 ... 1.0 for (py = index+radius; py < e3hh-radius; py += NWT) // loop all image pixels for (px = radius; px < e3ww-radius; px++) { if (sa_stat == 3) { // select area active ii = py * e3ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // pixel outside area } pix1 = PXMpix(E1pxm,px,py); // input pixel pbright = pixbright(pix1); // input pixel brightness npix = 0; rank1 = rank2 = 0; for (qy = py-radius; qy <= py+radius; qy++) // loop pixels in neighborhood for (qx = px-radius; qx <= px+radius; qx++) { qpix = PXMpix(E1pxm,qx,qy); // neighbor pixel qbright = pixbright(qpix); // compare neighbor brightness if (qbright < pbright) rank1++; // count darker neighbors if (qbright == pbright) rank2++; // count equal neighbors npix++; } if (! pbright) ff = 1; else { qbright = 255.9 * (rank1 + 0.5 * rank2) / npix; // assigned brightness = 256*rank/npix ff = qbright / pbright; // new/old brightness ratio } ff = 1.0 - pow * (1.0 - ff); // 1 ... ff for pow = 0 ... 1 fred = ff * pix1[0]; // RGB for new brightness level fgreen = ff * pix1[1]; fblue = ff * pix1[2]; if (fred > 255.9) fred = 255.9; // stop overflow if (fgreen > 255.9) fgreen = 255.9; if (fblue > 255.9) fblue = 255.9; if (sa_stat == 3 && dist < sa_blend) { // select area is active, f3 = sa_blendfunc(dist); // blend changes over sa_blend f1 = 1.0 - f3; fred = f3 * fred + f1 * pix1[0]; fgreen = f3 * fgreen + f1 * pix1[1]; fblue = f3 * fblue + f1 * pix1[2]; } pix3 = PXMpix(E3pxm,px,py); // output pixel pix3[0] = fred; pix3[1] = fgreen; pix3[2] = fblue; Fbusy_done++; } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } /********************************************************************************/ // pattern menu function // tile the image with a repeating pattern namespace patternnames { editfunc EFpattern; // edit function data int e3ww, e3hh; char *pattfile = 0; // pattern image file PIXBUF *pixbuf = 0; // pattern pixbuf int pattww, patthh, pattrs; // pixbuf length, width, row stride uint8 *pixels; // pixbuf pixels int olapww = 0, olaphh = 0; // pattern overlap int opacity = 0; // pattern opacity 0-100 int contrast = 100; // pattern contrast 0-200 float zoom = 1.0; // pattern magnification int update_thread = 0; } // menu function void m_pattern(GtkWidget *, cchar *menu) { using namespace patternnames; int pattern_dialog_event(zdialog* zd, const char *event); void * pattern_thread(void *); zdialog *zd; F1_help_topic = "pattern"; EFpattern.menuname = menu; EFpattern.menufunc = m_pattern; EFpattern.funcname = "pattern"; // function name EFpattern.Farea = 2; // select area OK EFpattern.Fscript = 1; // scripting supported 17.04 EFpattern.threadfunc = pattern_thread; // thread function if (! edit_setup(EFpattern)) return; // setup edit e3ww = E3pxm->ww; // image dimensions e3hh = E3pxm->hh; /*** _______________________________________________ | [x][-][_] Background Pattern | | | | Pattern File [____________________] [Browse] | | Geometry [Calculate] Zoom [___] | | Pattern Width NNN Height NNN | // labels not spin buttons 18.01 | Overlap Width [___] Height [___] | | Opacity [___] Contrast [___] | | | | [done] [cancel] | |_______________________________________________| ***/ zd = zdialog_new(ZTX("Background Pattern"),Mwin,Bdone,Bcancel,null); CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labfile","hbfile",ZTX("Pattern File:"),"space=5"); zdialog_add_widget(zd,"zentry","pattfile","hbfile",0,"expand"); zdialog_add_widget(zd,"button","browse","hbfile",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hbcalc","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labzoom","hbcalc",ZTX("Zoom"),"space=5"); zdialog_add_widget(zd,"zspin","zoom","hbcalc","1.0|5.0|0.01|1.0"); zdialog_add_widget(zd,"label","space","hbcalc",0,"space=10"); zdialog_add_widget(zd,"label","labcalc","hbcalc",ZTX("Geometry"),"space=5"); zdialog_add_widget(zd,"button","calc","hbcalc",ZTX("Calculate")); zdialog_add_widget(zd,"hbox","hbs11","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labpatt","hbs11",ZTX("Pattern"),"space=3"); zdialog_add_widget(zd,"label","labwidth","hbs11",Bwidth,"space=3"); zdialog_add_widget(zd,"label","pattww","hbs11","tbd","space=5"); zdialog_add_widget(zd,"label","space","hbs11",0,"space=10"); zdialog_add_widget(zd,"label","labheight","hbs11",Bheight,"space=3"); zdialog_add_widget(zd,"label","patthh","hbs11","tbd","space=5"); zdialog_add_widget(zd,"hbox","hbs12","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labover","hbs12",ZTX("Overlap"),"space=3"); zdialog_add_widget(zd,"label","labwidth","hbs12",Bwidth,"space=3"); zdialog_add_widget(zd,"zspin","olapww","hbs12","0|1000|1|0"); zdialog_add_widget(zd,"label","space","hbs12",0,"space=10"); zdialog_add_widget(zd,"label","labheight","hbs12",Bheight,"space=3"); zdialog_add_widget(zd,"zspin","olaphh","hbs12","0|1000|1|0"); zdialog_add_widget(zd,"hbox","hbopac","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labopac","hbopac",ZTX("Opacity"),"space=5"); zdialog_add_widget(zd,"zspin","opacity","hbopac","0|100|1|0"); zdialog_add_widget(zd,"label","space","hbopac",0,"space=10"); zdialog_add_widget(zd,"label","labcont","hbopac",ZTX("Contrast"),"space=5"); zdialog_add_widget(zd,"zspin","contrast","hbopac","0|200|1|100"); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,pattern_dialog_event,"save"); // run dialog - parallel zdialog_send_event(zd,"init"); // initialize return; } // pattern dialog event and completion function int pattern_dialog_event(zdialog *zd, const char *event) // overhauled to retain prior data { using namespace patternnames; void pattern_match(); char temp1[150], temp2[200]; char *file, *pp; PIXBUF *pixbuf2; GError *gerror = 0; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit if (pixbuf) g_object_unref(pixbuf); // free memory pixbuf = 0; if (pattfile) zfree(pattfile); pattfile = 0; return 1; } zdialog_fetch(zd,"olapww",olapww); zdialog_fetch(zd,"olaphh",olaphh); zdialog_fetch(zd,"zoom",zoom); zdialog_fetch(zd,"opacity",opacity); zdialog_fetch(zd,"contrast",contrast); if (strmatch(event,"init")) { if (pattfile) zfree(pattfile); pattfile = 0; if (pixbuf) g_object_unref(pixbuf); pixbuf = 0; zdialog_fetch(zd,"pattfile",temp1,150); // get prior pattern file if (*temp1 > ' ') { snprintf(temp2,200,"%s/%s",pattern_dirk,temp1); pattfile = zstrdup(temp2); } } if (strmatch(event,"browse")) // choose new pattern file { if (pattfile) pp = pattfile; else pp = pattern_dirk; file = gallery_select1(pp); // use thumbnail selection 17.08 if (! file) return 1; if (pattfile) zfree(pattfile); pattfile = file; pp = strrchr(pattfile,'/'); // update dialog if (pp) zdialog_stuff(zd,"pattfile",pp+1); olapww = olaphh = 0; // reset zoom, overlaps zoom = 1; } if (strstr("init browse",event)) // open pattern file { if (! pattfile) return 1; if (pixbuf) g_object_unref(pixbuf); pixbuf = gdk_pixbuf_new_from_file(pattfile,&gerror); // create pixbuf image if (! pixbuf) { zmessageACK(Mwin,"bad pattern file: %s",pattfile); // not an image file zfree(pattfile); pattfile = 0; return 1; } if (! strstr(pattfile,pattern_dirk)) // add pattern to directory if needed copyFile(pattfile,pattern_dirk); // 17.08 pixbuf2 = gdk_pixbuf_stripalpha(pixbuf); // strip alpha channel if present if (pixbuf2) { g_object_unref(pixbuf); pixbuf = pixbuf2; } pattrs = gdk_pixbuf_get_rowstride(pixbuf); // row stride pixels = gdk_pixbuf_get_pixels(pixbuf); // image data (pixels) } if (! pixbuf) return 1; // continuation pointless pattww = gdk_pixbuf_get_width(pixbuf); // pixbuf dimensions patthh = gdk_pixbuf_get_height(pixbuf); if (strmatch(event,"calc")) { // find pattern dimensions automatically pattern_match(); olapww = olaphh = 0; // reset overlaps } if (strstr("zoom init",event)) // set pattern zoom level { g_object_unref(pixbuf); pixbuf = gdk_pixbuf_new_from_file(pattfile,&gerror); // refresh pixbuf at scale 1x if (! pixbuf) { zmessageACK(Mwin,"%s \n %s",pattfile,gerror->message); return 1; } pixbuf2 = gdk_pixbuf_stripalpha(pixbuf); // strip alpha channel if present if (pixbuf2) { g_object_unref(pixbuf); pixbuf = pixbuf2; } pattww = gdk_pixbuf_get_width(pixbuf); // pixbuf dimensions patthh = gdk_pixbuf_get_height(pixbuf); pattww = pattww * zoom; // new dimensions patthh = patthh * zoom; pixbuf2 = gdk_pixbuf_scale_simple(pixbuf,pattww,patthh,BILINEAR); if (! pixbuf2) return 1; g_object_unref(pixbuf); // replace original pixbuf pixbuf = pixbuf2; pattww = gdk_pixbuf_get_width(pixbuf); // new pixbuf dimensions patthh = gdk_pixbuf_get_height(pixbuf); pattrs = gdk_pixbuf_get_rowstride(pixbuf); // row stride pixels = gdk_pixbuf_get_pixels(pixbuf); // image data (pixels) } if (olapww > pattww/3) olapww = pattww/3; // prevent nonsense if (olaphh > patthh/3) olaphh = patthh/3; zdialog_stuff(zd,"pattww",pattww); // stuff all dialog parameters zdialog_stuff(zd,"patthh",patthh); zdialog_stuff(zd,"olapww",olapww); zdialog_stuff(zd,"olaphh",olaphh); zdialog_stuff(zd,"zoom",zoom); zdialog_stuff(zd,"opacity",opacity); zdialog_stuff(zd,"contrast",contrast); if (strstr("init browse calc zoom olapww olaphh opacity contrast",event)) { update_thread++; signal_thread(); // update image zmainloop(); zsleep(0.05); // slow down spin rate 18.01 } return 1; } // set tile dimensions to match the pitch of a repeating pattern void pattern_match() { using namespace patternnames; int limx, limy; int px, py, qx, qy, sx, sy; int diff, mindiff; uint8 *pix1, *pix2; limx = pattww / 2; limy = patthh / 2; if (limx < 21 || limy < 21) return; sx = sy = 1; // best origin found mindiff = 999999999; // best pixel difference found for (px = 20; px < limx; px++) // loop all possible pattern origins for (py = 20; py < limy; py++) // (pattern must be > 20 x 20) { diff = 0; for (qy = py; qy < py + limy; qy++) // match vert. lines from (1,1) and (px,py) { pix1 = pixels + (1+qy-py) * pattrs + 1 * 3; // line from (1,1) pix2 = pixels + qy * pattrs + px * 3; // line from (qx,qy) diff += abs(pix1[0] - pix2[0]) + abs(pix1[1] - pix2[1]) + abs(pix1[2] - pix2[2]); } for (qx = px; qx < px + limx; qx++) // match horz. lines from (1,1) and (px,py) { pix1 = pixels + 1 * pattrs + (1+qx-px) * 3; // line from (1,1) pix2 = pixels + py * pattrs + qx * 3; // line from (qx,qy) diff += abs(pix1[0] - pix2[0]) + abs(pix1[1] - pix2[1]) + abs(pix1[2] - pix2[2]); } if (diff < mindiff) { // save best match found mindiff = diff; sx = px; // pattern origin sy = py; } } pattww = 2 * (sx - 1); // set width, height to match patthh = 2 * (sy - 1); // pattern size return; } // thread function - update image void * pattern_thread(void *) { using namespace patternnames; void * pattern_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request if (! pixbuf) continue; while (update_thread) { paintlock(1); // 18.01 update_thread = 0; start_wthread(pattern_wthread,&Nval[0]); // start worker thread (1) wait_wthreads(); // wait for completion paintlock(0); } CEF->Fmods = 1; // image modified CEF->Fsaved = 0; // not saved Fpaint2(); // update window } return 0; // not executed, stop warning } void * pattern_wthread(void *arg) // worker thread function { using namespace patternnames; char *tmap; // maps tiles overlapping image pixel int ii, cc, dist; int tcol, trow, Ntcols, Ntrows; // tile columns and rows int pww, phh, prs; // pattern/tile dimensions int mpx, mpy, tpx, tpy; uint8 *pixt; float *pix1, *pix3; float pbrite, f1, f2; double mbrite = 0; float *tbmap; pww = pattww; // capture and freeze volatile params phh = patthh; prs = pattrs; cc = e3ww * e3hh; // tile pixels mapping to image pixel tmap = (char *) zmalloc(cc); memset(tmap,0,cc); cc = pww * phh * sizeof(float); // tile brightness map tbmap = (float *) zmalloc(cc); for (mpy = 0; mpy < e3hh; mpy++) // clear output image to black for (mpx = 0; mpx < e3ww; mpx++) { pix3 = PXMpix(E3pxm,mpx,mpy); pix3[0] = pix3[1] = pix3[2] = 0; } Ntrows = e3hh / (phh-2*olaphh) + 1; // tile rows and columns including Ntcols = e3ww / (pww-2*olapww) + 1; // top/bottom and left/right overlaps for (trow = 0; trow < Ntrows; trow++) // loop tile rows, columns for (tcol = 0; tcol < Ntcols; tcol++) { for (tpy = 0; tpy < phh; tpy++) // loop tile pixels for (tpx = 0; tpx < pww; tpx++) { mpy = trow * (phh-2*olaphh) + tpy; // corresponding image pixel mpx = tcol * (pww-2*olapww) + tpx; if (mpy >= e3hh || mpx >= e3ww) continue; ii = mpy * e3ww + mpx; // count tile pixels overlapping ++tmap[ii]; // this image pixel } } for (trow = 0; trow < Ntrows; trow++) // loop tile rows, columns for (tcol = 0; tcol < Ntcols; tcol++) { for (tpy = 0; tpy < phh; tpy++) // loop tile pixels for (tpx = 0; tpx < pww; tpx++) { mpy = trow * (phh-2*olaphh) + tpy; // corresponding image pixel mpx = tcol * (pww-2*olapww) + tpx; if (mpy >= e3hh || mpx >= e3ww) continue; ii = mpy * e3ww + mpx; pixt = pixels + tpy * prs + tpx * 3; // input tile pixel pix3 = PXMpix(E3pxm,mpx,mpy); // output image pixel pix3[0] += (0.01 * opacity / tmap[ii]) * pixt[0]; // image = tile * opacity 0-100% pix3[1] += (0.01 * opacity / tmap[ii]) * pixt[1]; // reduce for overlapping tiles pix3[2] += (0.01 * opacity / tmap[ii]) * pixt[2]; } } for (mpy = 0; mpy < e3hh; mpy++) // loop image pixels for (mpx = 0; mpx < e3ww; mpx++) { pix1 = PXMpix(E1pxm,mpx,mpy); // input image pixel pix3 = PXMpix(E3pxm,mpx,mpy); // output image pixel pix3[0] += (1.0 - 0.01 * opacity) * pix1[0]; // add input pixel to output, pix3[1] += (1.0 - 0.01 * opacity) * pix1[1]; // part not taken by tiles pix3[2] += (1.0 - 0.01 * opacity) * pix1[2]; } for (tpy = 0; tpy < phh; tpy++) // loop tile pixels for (tpx = 0; tpx < pww; tpx++) { pixt = pixels + tpy * prs + tpx * 3; mbrite += pixt[0] + pixt[1] + pixt[2]; // sum of all pixel RGB values } mbrite = mbrite / (phh * pww); // mean RGB sum for all tile pixels for (tpy = 0; tpy < phh; tpy++) // loop tile pixels for (tpx = 0; tpx < pww; tpx++) { pixt = pixels + tpy * prs + tpx * 3; pbrite = pixt[0] + pixt[1] + pixt[2]; pbrite = pbrite / mbrite; // pixel relative brightness 0...2 ii = tpy * pww + tpx; tbmap[ii] = pbrite; } for (trow = 0; trow < Ntrows; trow++) // loop tile rows, columns for (tcol = 0; tcol < Ntcols; tcol++) { for (tpy = 0; tpy < phh; tpy++) // loop tile pixels for (tpx = 0; tpx < pww; tpx++) { mpy = trow * (phh-2*olaphh) + tpy; // corresponding image pixel mpx = tcol * (pww-2*olapww) + tpx; if (mpy >= e3hh || mpx >= e3ww) continue; pix3 = PXMpix(E3pxm,mpx,mpy); ii = tpy * pww + tpx; pbrite = tbmap[ii]; // tile pixel rel. brightness 0...2 ii = mpy * e3ww + mpx; if (tmap[ii] > 1) // if 2+ tile pixels map to image pixel pbrite = (pbrite - 1.0) / tmap[ii] + 1.0; // reduce impact of each pbrite = 0.01 * contrast * (pbrite - 1.0) + 1.0; // apply contrast factor 0-100% pix3[0] *= pbrite; pix3[1] *= pbrite; pix3[2] *= pbrite; } } for (mpy = 0; mpy < e3hh; mpy++) // stop RGB overflows for (mpx = 0; mpx < e3ww; mpx++) { pix3 = PXMpix(E3pxm,mpx,mpy); if (pix3[0] > 255) pix3[0] = 255; if (pix3[1] > 255) pix3[1] = 255; if (pix3[2] > 255) pix3[2] = 255; } if (sa_stat == 3) // select area active { for (mpy = 0; mpy < e3hh; mpy++) // loop image pixels for (mpx = 0; mpx < e3ww; mpx++) { ii = mpy * e3ww + mpx; dist = sa_pixmap[ii]; // distance from edge if (dist) { // pixel inside area if (dist < sa_blend) f1 = sa_blendfunc(dist); // blend edges else f1 = 1.0; } else f1 = 0.0; // pixel outside area f2 = 1.0 - f1; pix1 = PXMpix(E1pxm,mpx,mpy); // input image pixel pix3 = PXMpix(E3pxm,mpx,mpy); // output image pixel pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; } } zfree(tmap); zfree(tbmap); exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Create a mosaic image using tiny thumbnails as tiles. namespace mosaic_names { editfunc EFmosaic; // edit function data #define maxtiles maximages int iww, ihh; // base image dimensions int tww, thh; // tile size (default 32x24) int mww, mhh; // image size char *tilefile[maxtiles]; // tile files list (100K x 100 --> 10M) uint8 *tileimage[maxtiles]; // tile images (tww x thh x 3) (100K x 32 x 24 x 3 --> 230M) int Ntiles, Ndone; // tile image count float tileRGB1[maxtiles][3]; // tile mean RGB values for each tile quadrant float tileRGB2[maxtiles][3]; // (100K x 3 x 4B --> 1.2M x 4Q --> 4.8M) float tileRGB3[maxtiles][3]; float tileRGB4[maxtiles][3]; int *tilemap; // maps image [px,py] to tile used at that position int tbusy[max_threads]; } // menu function void m_mosaic(GtkWidget *, const char *) { using namespace mosaic_names; int mosaic_dialog_event(zdialog*, cchar *); void mosaic_mousefunc(); zdialog *zd; cchar *title = ZTX("Create Mosaic"); char label[12]; int cc; F1_help_topic = "mosaic"; EFmosaic.menufunc = m_mosaic; EFmosaic.funcname = "mosaic"; // function name EFmosaic.mousefunc = mosaic_mousefunc; // mouse function if (! edit_setup(EFmosaic)) return; // setup edit tww = 32; // default tile size thh = 24; Ntiles = 0; // tile file and image counts iww = E3pxm->ww; // base image dimensions ihh = E3pxm->hh; cc = maxtiles * sizeof(char *); // clear tile file list memset(tilefile,0,cc); cc = maxtiles * sizeof(uint8 *); // clear tile image list memset(tileimage,0,cc); cc = iww * ihh * sizeof(int); // allocate image/tile map tilemap = (int *) zmalloc(cc); memset(tilemap,-1,cc); /*** ____________________________________________ | Create Mosaic | | | | Tile Width [___] Height [___] | | [Tiles] nnnnn [Image] | | Tile blending =========[]============= | | | | [done] [cancel] | |____________________________________________| ***/ zd = zdialog_new(title,Mwin,Bdone,Bcancel,null); // mosaic dialog CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbsize","dialog"); zdialog_add_widget(zd,"label","labsize","hbsize",ZTX("Tile"),"space=3"); zdialog_add_widget(zd,"label","labwidth","hbsize",Bwidth,"space=3"); zdialog_add_widget(zd,"zspin","width","hbsize","16|48|2|32"); zdialog_add_widget(zd,"label","space","hbsize",0,"space=5"); zdialog_add_widget(zd,"label","labheight","hbsize",Bheight,"space=3"); zdialog_add_widget(zd,"zspin","height","hbsize","16|48|2|24"); zdialog_add_widget(zd,"hbox","hbcreate","dialog"); zdialog_add_widget(zd,"button","tiles","hbcreate",ZTX("Tiles"),"space=3"); zdialog_add_widget(zd,"label","labNtiles","hbcreate","0","space=5"); zdialog_add_widget(zd,"button","image","hbcreate",Bimage,"space=10"); zdialog_add_widget(zd,"vbox","space","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbblend","dialog"); zdialog_add_widget(zd,"label","labblend","hbblend",ZTX("Tile blending"),"space=3"); zdialog_add_widget(zd,"hscale","blend","hbblend","0|100|1|0","space=5|expand"); zdialog_stuff(zd,"width",tww); zdialog_stuff(zd,"height",thh); snprintf(label,12,"%d",Ntiles); zdialog_stuff(zd,"labNtiles",label); zdialog_run(zd,mosaic_dialog_event,"save"); // run dialog - parallel return; } // mosaic dialog event and completion function int mosaic_dialog_event(zdialog *zd, const char *event) { using namespace mosaic_names; void * mosaic_thread1(void *arg); void * mosaic_thread2(void *arg); void mosaic_mousefunc(); static int block = 0; char **flist = 0; char label[12]; int ii, err, NF; int trow, tcol, tpx, tpy, ipx, ipy; uint8 *timage, *tpix; float blend, f1, f3; float *pix1, *pix3; if (block) return 1; block = 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (zd->zstat) { if (zd->zstat == 1) edit_done(0); // commit edit else edit_cancel(0); // discard edit for (ii = 0; ii < Ntiles; ii++) // free memory zfree(tilefile[ii]); for (ii = 0; ii < Ntiles; ii++) zfree(tileimage[ii]); if (tilemap) zfree(tilemap); block = 0; return 1; } if (strmatch(event,"focus")) { takeMouse(mosaic_mousefunc,dragcursor); // connect mouse function block = 0; return 1; } if (strmatch(event,"tiles")) // read thumbnails, create tile images { for (ii = 0; ii < Ntiles; ii++) { // free prior memory zfree(tilefile[ii]); zfree(tileimage[ii]); } Ntiles = 0; Ndone = 0; // progress counter zdialog_stuff(zd,"labNtiles","0"); zdialog_fetch(zd,"width",tww); // get tile size from dialog zdialog_fetch(zd,"height",thh); err = find_imagefiles(thumbdirk,2+16,flist,NF); // all thumbnails, all levels 18.01 if (err) { zmessageACK(Mwin,strerror(errno)); block = 0; return 1; } if (NF > maxtiles) { // too many zmessageACK(Mwin,ZTX("exceeded max. tiles: %d"),maxtiles); zfree(flist); block = 0; return 1; } if (NF < 100) { // too few zmessageACK(Mwin,ZTX("only %d tile images found"),NF); zfree(flist); block = 0; return 1; } for (ii = 0; ii < NF; ii++) { // save thumbnail filespecs tilefile[ii] = flist[ii]; tileimage[ii] = 0; } zfree(flist); Ntiles = NF; for (ii = 0; ii < NWT; ii++) { // create tile images and tbusy[ii] = 1; // RGB color data per tile start_detached_thread(mosaic_thread1,&Nval[ii]); } for (ii = 0; ii < NWT; ii++) { // wait for completion while(tbusy[ii]) { zsleep(0.01); snprintf(label,12,"%d",Ndone); // show count in dialog zdialog_stuff(zd,"labNtiles",label); zmainloop(); } } block = 0; return 1; } if (strmatch(event,"image")) // apply tiles to mosaic image { if (! Ntiles) { block = 0; return 1; } edit_undo(); // reset to original image for (ii = 0; ii < NWT; ii++) { // apply tiles to image tbusy[ii] = 1; start_detached_thread(mosaic_thread2,&Nval[ii]); } for (int ii = 0; ii < NWT; ii++) { // wait for completion while(tbusy[ii]) { zsleep(0.01); Fpaint2(); // update image continuously zmainloop(); } } zdialog_stuff(zd,"blend",0); // reset blend control CEF->Fmods++; // image is modified CEF->Fsaved = 0; block = 0; return 1; } if (strmatch(event,"blend")) { if (! Ntiles) { block = 0; return 1; } zdialog_fetch(zd,"blend",blend); // get blend value 0-100 f1 = blend / 100.0; // base image part, 0.0 --> 1.0 f3 = 1.0 - f1; // tile image part, 1.0 --> 0.0 for (trow = 0; trow < ihh/thh; trow++) // loop tile positions in base image for (tcol = 0; tcol < iww/tww; tcol++) { for (tpy = 0; tpy < thh; tpy++) // loop tile pixels for (tpx = 0; tpx < tww; tpx++) { ipy = trow * thh + tpy; // corresponding image pixels ipx = tcol * tww + tpx; ii = iww * ipy + ipx; // get tile used at image pixel ii = tilemap[ii]; timage = tileimage[ii]; tpix = timage + tpy * tww * 3 + tpx * 3; // input tile pixel pix1 = PXMpix(E1pxm,ipx,ipy); // input E1 image pixel pix3 = PXMpix(E3pxm,ipx,ipy); // output E3 image pixel pix3[0] = f1 * pix1[0] + f3 * tpix[0]; // replace image pixels with tile pixels pix3[1] = f1 * pix1[1] + f3 * tpix[1]; pix3[2] = f1 * pix1[2] + f3 * tpix[2]; } } Fpaint2(); CEF->Fmods++; // image is modified CEF->Fsaved = 0; block = 0; return 1; } block = 0; return 1; } // thread to make tile images from thumbnails and tile RGB color data void * mosaic_thread1(void *arg) // 18.01 { using namespace mosaic_names; int index = *((int *) arg); PIXBUF *pxb1, *pxb2; GError *gerror = 0; int ii, jj; int tpx, tpy; int tww1, thh1, tww2, thh2; int rs; uint8 *tpixels, *tpix, *timage; uint8 *row1, *row2; float fred1, fgreen1, fblue1; float fred2, fgreen2, fblue2; float fred3, fgreen3, fblue3; float fred4, fgreen4, fblue4; float coeff; for (ii = index; ii < Ntiles; ii += NWT) // loop tile image files { if (tileimage[ii]) zfree(tileimage[ii]); tileimage[ii] = 0; pxb1 = gdk_pixbuf_new_from_file_at_size(tilefile[ii],64,64,&gerror); // create pixbuf within 64x64 if (! pxb1) { printz("file: %s \n %s",tilefile[ii],gerror->message); gerror = 0; continue; } tww1 = gdk_pixbuf_get_width(pxb1); // actual width, height (max. 64) thh1 = gdk_pixbuf_get_height(pxb1); g_object_unref(pxb1); if (tww1 * thh < tww * thh1) { // tww1/thh1 < tww/thh tww2 = tww; // (too high for tww x thh) thh2 = tww * thh1 / tww1; } else { // tww1/thh1 > tww/thh thh2 = thh; // (too wide for tww x thh) tww2 = thh * tww1 / thh1; } pxb2 = gdk_pixbuf_new_from_file_at_size(tilefile[ii],tww2,thh2,&gerror); // rescale for max. tww x thh section if (! pxb2) { printz("file: %s \n %s",tilefile[ii],gerror->message); gerror = 0; continue; } tpx = (tww2 - tww) / 2; // tww2 or thh2 offset for pixels tpy = (thh2 - thh) / 2; // copied to tww x thh tile image rs = gdk_pixbuf_get_rowstride(pxb2); tpixels = gdk_pixbuf_get_pixels(pxb2); timage = (uint8 *) zmalloc(tww * thh * 3); // allocate memory for tww x thh pixels for (jj = 0; jj < thh; jj++) { // copy pixbuf tww x thh section row1 = tpixels + rs * (tpy + jj) + tpx * 3; row2 = timage + tww * 3 * jj; memcpy(row2,row1,tww * 3); // memmove_avx_unaligned_erms crash } // (one in a million) FIXME g_object_unref(pxb2); tileimage[ii] = timage; // save final tile image fred1 = fgreen1 = fblue1 = 0; fred2 = fgreen2 = fblue2 = 0; fred3 = fgreen3 = fblue3 = 0; fred4 = fgreen4 = fblue4 = 0; for (tpy = 0; tpy < thh/2; tpy++) // loop pixels in top left tile quadrant for (tpx = 0; tpx < tww/2; tpx++) { tpix = timage + tpy * tww * 3 + tpx * 3; fred1 += tpix[0]; // sum RGB values fgreen1 += tpix[1]; fblue1 += tpix[2]; } for (tpy = 0; tpy < thh/2; tpy++) // top right for (tpx = tww/2; tpx < tww; tpx++) { tpix = timage + tpy * tww * 3 + tpx * 3; fred2 += tpix[0]; fgreen2 += tpix[1]; fblue2 += tpix[2]; } for (tpy = thh/2; tpy < thh; tpy++) // bottom left for (tpx = 0; tpx < tww/2; tpx++) { tpix = timage + tpy * tww * 3 + tpx * 3; fred3 += tpix[0]; fgreen3 += tpix[1]; fblue3 += tpix[2]; } for (tpy = thh/2; tpy < thh; tpy++) // bottom right for (tpx = tww/2; tpx < tww; tpx++) { tpix = timage + tpy * tww * 3 + tpx * 3; fred4 += tpix[0]; fgreen4 += tpix[1]; fblue4 += tpix[2]; } coeff = 0.25 / (tww * thh); tileRGB1[ii][0] = coeff * fred1; // save tile mean RGB values tileRGB1[ii][1] = coeff * fgreen1; // for each quadrant tileRGB1[ii][2] = coeff * fblue1; tileRGB2[ii][0] = coeff * fred2; tileRGB2[ii][1] = coeff * fgreen2; tileRGB2[ii][2] = coeff * fblue2; tileRGB3[ii][0] = coeff * fred3; tileRGB3[ii][1] = coeff * fgreen3; tileRGB3[ii][2] = coeff * fblue3; tileRGB4[ii][0] = coeff * fred4; tileRGB4[ii][1] = coeff * fgreen4; tileRGB4[ii][2] = coeff * fblue4; Ndone++; // progress counter } tbusy[index] = 0; pthread_exit(0); return 0; } // thread to apply best color-matched tile to each image position void * mosaic_thread2(void *arg) // 18.01 { using namespace mosaic_names; int index = *((int *) arg); int trow, tcol, tpx, tpy, ipx, ipy; int ii, bestii; uint8 *tpix, *timage; float *pix1, *pix3; float fred1, fgreen1, fblue1; float fred2, fgreen2, fblue2; float fred3, fgreen3, fblue3; float fred4, fgreen4, fblue4; float match, bestmatch; float coeff; for (trow = index; trow < ihh/thh; trow += NWT) // loop tile positions in base image for (tcol = 0; tcol < iww/tww; tcol++) { fred1 = fgreen1 = fblue1 = 0; fred2 = fgreen2 = fblue2 = 0; fred3 = fgreen3 = fblue3 = 0; fred4 = fgreen4 = fblue4 = 0; for (tpy = 0; tpy < thh/2; tpy++) // loop pixels in top left tile quadrant for (tpx = 0; tpx < tww/2; tpx++) { ipy = trow * thh + tpy; // corresponding image pixels ipx = tcol * tww + tpx; pix1 = PXMpix(E1pxm,ipx,ipy); fred1 += pix1[0]; // sum image RGB values fgreen1 += pix1[1]; fblue1 += pix1[2]; } for (tpy = 0; tpy < thh/2; tpy++) // top right quadrant for (tpx = tww/2; tpx < tww; tpx++) { ipy = trow * thh + tpy; ipx = tcol * tww + tpx; pix1 = PXMpix(E1pxm,ipx,ipy); fred2 += pix1[0]; fgreen2 += pix1[1]; fblue2 += pix1[2]; } for (tpy = thh/2; tpy < thh; tpy++) // lower left quadrant for (tpx = 0; tpx < tww/2; tpx++) { ipy = trow * thh + tpy; ipx = tcol * tww + tpx; pix1 = PXMpix(E1pxm,ipx,ipy); fred3 += pix1[0]; fgreen3 += pix1[1]; fblue3 += pix1[2]; } for (tpy = thh/2; tpy < thh; tpy++) // lower right quadrant for (tpx = tww/2; tpx < tww; tpx++) { ipy = trow * thh + tpy; ipx = tcol * tww + tpx; pix1 = PXMpix(E1pxm,ipx,ipy); fred4 += pix1[0]; fgreen4 += pix1[1]; fblue4 += pix1[2]; } coeff = 0.25 / (tww * thh); fred1 = coeff * fred1; // mean image RGB values for fgreen1 = coeff * fgreen1; // each quadrant fblue1 = coeff * fblue1; fred2 = coeff * fred2; fgreen2 = coeff * fgreen2; fblue2 = coeff * fblue2; fred3 = coeff * fred3; fgreen3 = coeff * fgreen3; fblue3 = coeff * fblue3; fred4 = coeff * fred4; fgreen4 = coeff * fgreen4; fblue4 = coeff * fblue4; bestmatch = 0; bestii = 0; for (ii = 0; ii < Ntiles; ii++) // loop tile RGB values { if (! tileimage[ii]) continue; match = RGBMATCH(fred1,fgreen1,fblue1, // 0..1 = zero..perfect match tileRGB1[ii][0],tileRGB1[ii][1],tileRGB1[ii][2]); match += RGBMATCH(fred2,fgreen2,fblue2, tileRGB2[ii][0],tileRGB2[ii][1],tileRGB2[ii][2]); match += RGBMATCH(fred3,fgreen3,fblue3, tileRGB3[ii][0],tileRGB3[ii][1],tileRGB3[ii][2]); match += RGBMATCH(fred4,fgreen4,fblue4, tileRGB4[ii][0],tileRGB4[ii][1],tileRGB4[ii][2]); if (match > bestmatch) { bestmatch = match; // save best matching tile bestii = ii; } } ii = bestii; // best matching tile timage = tileimage[ii]; for (tpy = 0; tpy < thh; tpy++) // loop tile pixels for (tpx = 0; tpx < tww; tpx++) { ipy = trow * thh + tpy; // corresponding image pixels ipx = tcol * tww + tpx; tpix = timage + tpy * tww * 3 + tpx * 3; pix1 = PXMpix(E1pxm,ipx,ipy); pix3 = PXMpix(E3pxm,ipx,ipy); pix3[0] = tpix[0]; // replace image pixels with tile pixels pix3[1] = tpix[1]; pix3[2] = tpix[2]; ii = iww * ipy + ipx; // save map of tile used tilemap[ii] = bestii; // at each image pixel } } tbusy[index] = 0; pthread_exit(0); return 0; } // mouse function - called for mouse events inside image void mosaic_mousefunc() { using namespace mosaic_names; int ii; char *tfile, *mfile; if (! LMclick) return; LMclick = 0; ii = Myclick * iww + Mxclick; ii = tilemap[ii]; if (ii < 0) return; tfile = tilefile[ii]; mfile = thumb2imagefile(tfile); if (mfile) popup_image(mfile,MWIN,1,256); else popup_image(tfile,MWIN,1,256); return; } /********************************************************************************/ // process image using a custom kernel namespace anykernel_names { int ww, hh; // image dimensions int kernsize = 5; // kernel size, N x N float fmul = 1.0; // multiplier int fadd = 0; // adder int kernel[15][15]; // up to 15 x 15 editfunc EFanykernel; } void m_anykernel(GtkWidget *, cchar *menu) // menu function { using namespace anykernel_names; int anykernel_make_dialog(); void * anykernel_thread(void *); F1_help_topic = "custom_kernel"; EFanykernel.menuname = menu; EFanykernel.menufunc = m_anykernel; EFanykernel.funcname = "custom_kernel"; EFanykernel.Farea = 2; // select area usable EFanykernel.Fscript = 1; // scripting supported 17.04 EFanykernel.threadfunc = anykernel_thread; // thread function EFanykernel.Frestart = 1; // allow restart if (! edit_setup(EFanykernel)) return; // setup edit ww = E3pxm->ww; // image dimensions hh = E3pxm->hh; anykernel_make_dialog(); return; } // build the input dialog with the requested array size int anykernel_make_dialog() { using namespace anykernel_names; int anykernel_dialog_event(zdialog *zd, cchar *event); zdialog *zd; int row, col; char rowname[16], cellname[30]; /*** __________________________________________ | Custom Kernel | | | | Kernel size [___] | | | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | [____] [____] [____] [____] [____] ... | | ... | | multiply [____] add [____] | 17.08 | | | Data file [Load] [Save] | | | | [Reset] [Apply] [Done] [Cancel] | |__________________________________________| ***/ zd = zdialog_new(ZTX("Custom Kernel"),Mwin,Breset,Bapply,Bdone,Bcancel,null); EFanykernel.zd = zd; zdialog_add_widget(zd,"hbox","hbkern","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labkern","hbkern",ZTX("Kernel size"),"space=3"); zdialog_add_widget(zd,"zspin","kernsize","hbkern","3|15|2|5"); zdialog_stuff(zd,"kernsize",kernsize); for (row = 1; row <= kernsize; row++) // make entry cell matrix { snprintf(rowname,16,"row%02d",row); zdialog_add_widget(zd,"hbox",rowname,"dialog","space=5"); for (col = 1; col <= kernsize; col++) { snprintf(cellname,30,"cell%02d%02d",col,row); zdialog_add_widget(zd,"zspin",cellname,rowname,"-99|+99|1|0","size=5|space=5"); } } zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=5"); // multiplier and adder 17.08 zdialog_add_widget(zd,"label","labmul","hbf",ZTX("multiply"),"space=3"); zdialog_add_widget(zd,"zspin","fmul","hbf","0|99|0.01|1","size=7"); zdialog_add_widget(zd,"label","space","hbf",0,"space=5"); zdialog_add_widget(zd,"label","labadd","hbf",ZTX("add"),"space=3"); zdialog_add_widget(zd,"zspin","fadd","hbf","-999|+999|1|0","size=7"); zdialog_add_widget(zd,"hbox","hbfile","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labfile","hbfile",ZTX("Data file"),"space=3"); zdialog_add_widget(zd,"button","load","hbfile",Bload,"space=3"); zdialog_add_widget(zd,"button","save","hbfile",Bsave,"space=3"); zdialog_run(zd,anykernel_dialog_event,"save"); // run dialog - parallel return 1; } // dialog event and completion callback function int anykernel_dialog_event(zdialog *zd, cchar *event) { using namespace anykernel_names; int row, col, err, nn, kernsize2; float value; char cellname[20]; char *filename, dirname[200]; FILE *fid; cchar *mess = ZTX("Load settings from file"); if (strmatch(event,"focus")) return 1; if (strmatch(event,"apply")) zd->zstat = 2; // from script if (strmatch(event,"done")) zd->zstat = 3; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 4; // from f_open() if (zd->zstat) { if (zd->zstat == 1) // reset { zd->zstat = 0; // keep dialog active edit_reset(); return 1; } if (zd->zstat == 2) // apply { zd->zstat = 0; // keep dialog active for (row = 1; row <= kernsize; row++) // get kernel values from dialog for (col = 1; col <= kernsize; col++) { snprintf(cellname,20,"cell%02d%02d",col,row); zdialog_fetch(zd,cellname,value); kernel[row-1][col-1] = value; } zdialog_fetch(zd,"fmul",fmul); // 17.08 zdialog_fetch(zd,"fadd",fadd); signal_thread(); // start thread return 1; } if (zd->zstat == 3) // done - commit edit { edit_save_last_widgets(&EFanykernel); edit_done(0); } else edit_cancel(0); // cancel - discard edit return 1; } if (strmatch(event,"kernsize")) // change kernel size { zdialog_fetch(zd,"kernsize",kernsize2); if (kernsize2 == kernsize) return 1; kernsize = kernsize2; zdialog_destroy(zd); anykernel_make_dialog(); return 1; } if (strmatch(event,"save")) // save kernel data to a file edit_save_widgets(&EFanykernel); if (strmatch(event,"load")) // load kernel data from a file { snprintf(dirname,200,"%s/%s",get_zhomedir(),CEF->funcname); // directory for data files filename = zgetfile(mess,MWIN,"file",dirname,0); // open data file if (! filename) return 1; // user cancel fid = fopen(filename,"r"); // open file if (! fid) { zmessageACK(Mwin,"%s \n %s",filename,strerror(errno)); zfree(filename); return 1; } nn = fscanf(fid,"kernsize == %d",&kernsize2); // read 1st record, "kernsize == N" fclose(fid); if (nn != 1) { zmessageACK(Mwin,"file format error: \n %s",filename); zfree(filename); return 1; } if (kernsize2 != kernsize) { // change kernel size to match file zdialog_destroy(zd); kernsize = kernsize2; anykernel_make_dialog(); } fid = fopen(filename,"r"); // re-open file and load kernel data err = edit_load_widgets(&EFanykernel,fid); if (err) zmessageACK(Mwin,"file format error: \n %s",filename); fclose(fid); // bugfix 17.04.2 zfree(filename); } return 1; } // image anykernel thread function void * anykernel_thread(void *) { using namespace anykernel_names; void * anykernel_wthread(void *arg); while (true) { thread_idle_loop(); // wait for work or exit request E9pxm = PXM_copy(E3pxm); // [apply] accumulates for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(anykernel_wthread,&Nval[ii]); wait_wthreads(); // wait for completion PXM_free(E9pxm); CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window } return 0; // not executed, stop g++ warning } void * anykernel_wthread(void *arg) // worker thread function { using namespace anykernel_names; int index = *((int *) arg); int px, py, qx, qy; int ii, rad, dist = 0; float kval, red, green, blue; float max, f1, f2; float *pix1, *pix3, *pixN; rad = kernsize / 2; // image margins, not processed for (py = index+rad; py < hh-rad; py += NWT) // loop all image pixels for (px = rad; px < ww-rad; px++) { if (sa_stat == 3) { // select area active ii = py * ww + px; dist = sa_pixmap[ii]; // distance from edge if (! dist) continue; // outside pixel } red = green = blue = 0; for (qy = -rad; qy <= +rad; qy++) // loop pixels around (px,py) for (qx = -rad; qx <= +rad; qx++) // mapped to kernel { pixN = PXMpix(E9pxm,px+qx,py+qy); // pixel kval = kernel[qy+rad][qx+rad]; // kernel value for pixel red += kval * pixN[0]; // sum (kernel * pixel value) per RGB green += kval * pixN[1]; blue += kval * pixN[2]; } if (fmul != 1.0) { // apply multiplier 17.08 red = fmul * red; green = fmul * green; blue = fmul * blue; } if (fadd != 0) { // apply adder 17.08 red = red + fadd; green = green + fadd; blue = blue + fadd; } if (red < 0) red = 0; // stop underflow if (green < 0) green = 0; if (blue < 0) blue = 0; max = red; // stop overflow if (green > max) max = green; if (blue > max) max = blue; if (max > 255.9) { max = 255.9 / max; red = red * max; green = green * max; blue = blue * max; } pix3 = PXMpix(E3pxm,px,py); // output pixel pix3[0] = red; pix3[1] = green; pix3[2] = blue; } if (sa_stat == 3 && sa_blend > 0) // select area has edge blend { for (py = index; py < hh-1; py += NWT) // loop all image pixels for (px = 0; px < ww-1; px++) { ii = py * ww + px; dist = sa_pixmap[ii]; if (! dist) continue; // omit pixels outside area if (dist >= sa_blend) continue; // omit if > blendwidth from edge pix1 = PXMpix(E1pxm,px,py); // source pixel pix3 = PXMpix(E3pxm,px,py); // target pixel f1 = sa_blendfunc(dist); // blend changes over sa_blend f2 = 1.0 - f1; pix3[0] = f1 * pix3[0] + f2 * pix1[0]; pix3[1] = f1 * pix3[1] + f2 * pix1[1]; pix3[2] = f1 * pix3[2] + f2 * pix1[2]; } } exit_wthread(); return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Blur the image in one direction determined by dragging the mouse. // The effect diminishes to zero at the specified distance from the mouse. namespace dirblur_names { int E3ww, E3hh; float $mdx, $mdy, $mdw, $mdh; // blurfunc() and wthread() float $D, $span, $intens; // use these $ args editfunc EFdirblur; int dirblur_dialog_event(zdialog *zd, cchar *event); void dirblur_blurfunc(); void dirblur_mousefunc(void); void * dirblur_wthread(void *arg); } void m_directed_blur(GtkWidget *, cchar *) { using namespace dirblur_names; cchar *dirblur_tip = ZTX("Pull image using the mouse."); F1_help_topic = "directed_blur"; EFdirblur.menufunc = m_directed_blur; EFdirblur.funcname = "dirblur"; EFdirblur.mousefunc = dirblur_mousefunc; // mouse function if (! edit_setup(EFdirblur)) return; // setup edit /*** ____________________________________ | Pull image using the mouse. | | | | blur span [ 0.23 ] | | intensity [ 0.976 ] | | | | [Reset] [Done] [Cancel] | |____________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Directed Blur"),Mwin,Breset,Bdone,Bcancel,null); EFdirblur.zd = zd; zdialog_add_widget(zd,"label","labm","dialog",dirblur_tip,"space=3"); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=5"); zdialog_add_widget(zd,"label","lab1","hb1",ZTX("blur span"),"space=8"); zdialog_add_widget(zd,"zspin","span","hb1","0.00|1.0|0.01|0.1","space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"label","lab2","hb2",ZTX("intensity"),"space=8"); zdialog_add_widget(zd,"zspin","intens","hb2","0.00|1.0|0.01|0.1","space=3"); E3ww = E3pxm->ww; // preview dimensions E3hh = E3pxm->hh; zdialog_restore_inputs(zd); // restore previous inputs zdialog_fetch(zd,"span",$span); zdialog_fetch(zd,"intens",$intens); zdialog_run(zd,dirblur_dialog_event,"save"); // run dialog, parallel takeMouse(dirblur_mousefunc,dragcursor); // connect mouse function return; } // dialog event and completion callback function int dirblur_names::dirblur_dialog_event(zdialog * zd, cchar *event) { using namespace dirblur_names; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) { edit_reset(); // reset all changes zd->zstat = 0; } else if (zd->zstat == 2) edit_done(0); // commit edit else edit_cancel(0); // discard edit return 1; } if (strmatch(event,"span")) zdialog_fetch(zd,"span",$span); if (strmatch(event,"intens")) zdialog_fetch(zd,"intens",$intens); return 1; } // dirblur mouse function void dirblur_names::dirblur_mousefunc(void) { using namespace dirblur_names; if (Mxdrag || Mydrag) // mouse drag underway { $mdx = Mxdown; // drag origin $mdy = Mydown; $mdw = Mxdrag - Mxdown; // drag increment $mdh = Mydrag - Mydown; dirblur_blurfunc(); // drag image Mxdrag = Mydrag = 0; return; } return; } // blur image and accumulate blur memory // mouse at (mx,my) is moved (mw,mh) pixels void dirblur_names::dirblur_blurfunc() { using namespace dirblur_names; float D, d1, d2, d3, d4; d1 = ($mdx-0) * ($mdx-0) + ($mdy-0) * ($mdy-0); // distance, mouse to 4 corners d2 = (E3pxm->ww-$mdx) * (E3pxm->ww-$mdx) + ($mdy-0) * ($mdy-0); d3 = (E3pxm->ww-$mdx) * (E3pxm->ww-$mdx) + (E3pxm->hh-$mdy) * (E3pxm->hh-$mdy); d4 = ($mdx-0) * ($mdx-0) + (E3pxm->hh-$mdy) * (E3pxm->hh-$mdy); D = d1; if (d2 > D) D = d2; // find greatest corner distance if (d3 > D) D = d3; if (d4 > D) D = d4; $D = D * $span; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(dirblur_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; CEF->Fsaved = 0; Fpaint2(); // update window return; } // working thread to process the pixels void * dirblur_names::dirblur_wthread(void *arg) { using namespace dirblur_names; int index = *((int *) arg); int px, py, vstat; float d, mag, dispx, dispy; float F1, F2; float vpix[4], *pix3; F1 = $intens * $intens; F2 = 1.0 - F1; for (py = index; py < E3pxm->hh; py += NWT) // process all pixels for (px = 0; px < E3pxm->ww; px++) { d = (px-$mdx)*(px-$mdx) + (py-$mdy)*(py-$mdy); mag = (1.0 - d / $D); if (mag < 0) continue; mag = mag * mag; // faster than pow(mag,4); mag = mag * mag; dispx = -$mdw * mag; // displacement = drag * mag dispy = -$mdh * mag; vstat = vpixel(E3pxm,px+dispx,py+dispy,vpix); // input virtual pixel pix3 = PXMpix(E3pxm,px,py); // output pixel if (vstat) { pix3[0] = F2 * pix3[0] + F1 * vpix[0]; // output = input pixel blend pix3[1] = F2 * pix3[1] + F1 * vpix[1]; pix3[2] = F2 * pix3[2] + F1 * vpix[2]; } } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } /********************************************************************************/ // Blur Background // Blur the image outside of a selected area or areas. // Blur increases with distance from selected area edges. namespace blur_BG_names { int conrad, incrad; // constant or increasing blur int conbrad; // constant blur radius int minbrad; // min. blur radius int maxbrad; // max. blur radius VOL int cancel; // GCC inconsistent int ww, hh; // image dimensions int maxdist; // max. area edge distance editfunc EFblurBG; } // menu function void m_blur_background(GtkWidget *, const char *) { using namespace blur_BG_names; int blur_BG_dialog_event(zdialog* zd, const char *event); void * blur_BG_thread(void *); F1_help_topic = "blur_background"; EFblurBG.menufunc = m_blur_background; EFblurBG.funcname = "blur_background"; // function name EFblurBG.Farea = 2; // select area usable (required) EFblurBG.threadfunc = blur_BG_thread; // thread function if (! edit_setup(EFblurBG)) return; // setup edit minbrad = 10; // defaults maxbrad = 20; conbrad = 10; conrad = 1; incrad = 0; cancel = 0; ww = E3pxm->ww; hh = E3pxm->hh; /*** ____________________________________ | Blur Background | | | | [x] constant blur [ 20 ] | | | | [x] increase blur with distance | | min. blur radius [ 20 ] | | max. blur radius [ 90 ] | | | | [Apply] [Done] [Cancel] | |____________________________________| ***/ zdialog *zd = zdialog_new(ZTX("Blur Background"),Mwin,Bapply,Bdone,Bcancel,null); CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbcon","dialog",0,"space=5"); zdialog_add_widget(zd,"check","conrad","hbcon",ZTX("constant blur"),"space=3"); zdialog_add_widget(zd,"zspin","conbrad","hbcon","1|100|1|10","space=8"); zdialog_add_widget(zd,"hbox","hbinc","dialog"); zdialog_add_widget(zd,"check","incrad","hbinc",ZTX("increase blur with distance"),"space=3"); zdialog_add_widget(zd,"hbox","hbmin","dialog"); zdialog_add_widget(zd,"label","labmin","hbmin",ZTX("min. blur radius"),"space=8"); zdialog_add_widget(zd,"zspin","minbrad","hbmin","0|100|1|10","space=3"); zdialog_add_widget(zd,"hbox","hbmax","dialog"); zdialog_add_widget(zd,"label","labmax","hbmax",ZTX("max. blur radius"),"space=8"); zdialog_add_widget(zd,"zspin","maxbrad","hbmax","1|100|1|20","space=3"); zdialog_stuff(zd,"conrad",conrad); zdialog_stuff(zd,"incrad",incrad); zdialog_resize(zd,300,0); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,blur_BG_dialog_event,"save"); // run dialog - parallel if (sa_stat != 3) m_select(0,0); // start select area dialog F1_help_topic = "blur_background"; // don't lose the help topic return; } // blur_BG dialog event and completion function int blur_BG_dialog_event(zdialog *zd, const char *event) // blur_BG dialog event function { using namespace blur_BG_names; if (strmatch(event,"done")) zd->zstat = 2; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 3; // from f_open() if (zd->zstat) { if (zd->zstat == 1) // [apply] { zd->zstat = 0; // keep dialog active if (sa_stat != 3) { zmessageACK(Mwin,ZTX("no active Select Area")); m_select(0,0); return 1; } if (incrad && ! sa_calced) // if increading blur radius, sa_edgecalc(); // calc. area edge distances sa_show(0,0); edit_reset(); signal_thread(); return 1; } else if (zd->zstat == 2) { edit_done(0); // [done] if (zdsela) zdialog_send_event(zdsela,"done"); // kill select area dialog } else { cancel = 1; // kill threads edit_cancel(0); // [cancel] or [x], discard edit if (zdsela) zdialog_send_event(zdsela,"done"); } return 1; } if (strstr("conrad incrad",event)) { zdialog_stuff(zd,"conrad",0); zdialog_stuff(zd,"incrad",0); zdialog_stuff(zd,event,1); } zdialog_fetch(zd,"conrad",conrad); zdialog_fetch(zd,"incrad",incrad); zdialog_fetch(zd,"conbrad",conbrad); zdialog_fetch(zd,"minbrad",minbrad); zdialog_fetch(zd,"maxbrad",maxbrad); return 1; } // thread function - multiple working threads to update image void * blur_BG_thread(void *) { using namespace blur_BG_names; void * blur_BG_wthread(void *arg); // worker thread int ii, dist; while (true) { thread_idle_loop(); // wait for work or exit request if (incrad && sa_calced) { // if increasing blur radius, maxdist = 0; // get max. area edge distance for (ii = 0; ii < ww * hh; ii++) { dist = sa_pixmap[ii]; if (dist > maxdist) maxdist = dist; } } Fbusy_goal = sa_Npixel; Fbusy_done = 0; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(blur_BG_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fbusy_goal = Fbusy_done = 0; CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved Fpaint2(); // update window } return 0; // not executed, stop warning } void * blur_BG_wthread(void *arg) // worker thread function { using namespace blur_BG_names; int index = *((int *) (arg)); int ii, rad = 0, dist, npix; int px, py, qx, qy; float *pix1, *pix3; float red, green, blue, F; for (py = index; py < hh; py += NWT) // loop all image pixels { if (cancel) break; // cancel edit for (px = 0; px < ww; px++) { ii = py * ww + px; dist = sa_pixmap[ii]; // area edge distance if (! dist) continue; // pixel outside the area if (conrad) rad = conbrad; // use constant blur radius if (incrad) { // use increasing blur radius if (! sa_calced) exit_wthread(); // depending on edge distance rad = minbrad + (maxbrad - minbrad) * dist / maxdist; } npix = 0; red = green = blue = 0; for (qy = py-rad; qy <= py+rad; qy++) // average surrounding pixels for (qx = px-rad; qx <= px+rad; qx++) { if (qy < 0 || qy > hh-1) continue; if (qx < 0 || qx > ww-1) continue; ii = qy * ww + qx; if (! sa_pixmap[ii]) continue; pix1 = PXMpix(E1pxm,qx,qy); red += pix1[0]; green += pix1[1]; blue += pix1[2]; npix++; } F = 0.999 / npix; red = F * red; // blurred pixel RGB green = F * green; blue = F * blue; pix3 = PXMpix(E3pxm,px,py); pix3[0] = red; pix3[1] = green; pix3[2] = blue; Fbusy_done++; // count pixels done } } exit_wthread(); // exit thread return 0; // not executed, avoid gcc warning } /********************************************************************************/ // alien colors - displace hue by an angle that varies over the image namespace alien_colors_names { editfunc EFaliencolors; float BLsz, Ampl; // block size, amplitude float Frand[200][200]; // random numbers #define maxblocks 200 } // menu function void m_alien_colors(GtkWidget *, cchar *menu) // 17.04 { using namespace alien_colors_names; int alien_colors_dialog_event(zdialog* zd, const char *event); void * alien_colors_thread(void *); F1_help_topic = "alien_colors"; EFaliencolors.menuname = menu; EFaliencolors.menufunc = m_alien_colors; EFaliencolors.funcname = "alien_colors"; EFaliencolors.Farea = 2; // select area usable EFaliencolors.FprevReq = 1; // use preview mode EFaliencolors.Fscript = 1; // scripting supported 17.04 EFaliencolors.threadfunc = alien_colors_thread; if (! edit_setup(EFaliencolors)) return; /*** _____________________________ | Alien Colors | | | | blocksize [___] | | amplitude [___] | | | | [done] [cancel] | |_____________________________| ***/ zdialog *zd = zdialog_new(ZTX("Alien Colors"),Mwin,Bdone,Bcancel,null); CEF->zd = zd; zdialog_add_widget(zd,"hbox","hbbsz","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labbsz","hbbsz",ZTX("blocksize"),"space=5"); zdialog_add_widget(zd,"zspin","BLsz","hbbsz","10|1000|1|100"); zdialog_add_widget(zd,"hbox","hbamp","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labamp","hbamp",ZTX("amplitude"),"space=5"); zdialog_add_widget(zd,"zspin","Ampl","hbamp","0.0|1.0|0.01|1.0"); zdialog_restore_inputs(zd); // restore previous inputs zdialog_run(zd,alien_colors_dialog_event,"save"); // run dialog - parallel zdialog_send_event(zd,"BLsz"); // initial image return; } // alien dialog event and completion function int alien_colors_dialog_event(zdialog *zd, const char *event) // alien dialog event function { using namespace alien_colors_names; if (strmatch(event,"focus")) return 1; if (strmatch(event,"done")) zd->zstat = 1; // from edit_setup() or f_save() if (strmatch(event,"cancel")) zd->zstat = 2; // from f_open() if (strstr("BLsz Ampl Apply",event)) { zdialog_fetch(zd,"BLsz",BLsz); // get user inputs zdialog_fetch(zd,"Ampl",Ampl); signal_thread(); // calculate } if (zd->zstat) { if (zd->zstat == 1) // [done] { int ww = E3pxm->ww; // preview size edit_fullsize(); if (E3pxm->ww > ww) { BLsz *= 1.0 * E3pxm->ww / ww; // scale up for full size signal_thread(); } edit_done(0); } else edit_cancel(0); // discard edit } return 1; } // thread function - multiple working threads to update image void * alien_colors_thread(void *) { using namespace alien_colors_names; void * alien_colors_wthread(void *); for (int ii = 0; ii < maxblocks; ii++) // populate random values for (int jj = 0; jj < maxblocks; jj++) Frand[ii][jj] = drandz(); while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(alien_colors_wthread,&Nval[ii]); wait_wthreads(); // wait for completion CEF->Fmods++; // image modified CEF->Fsaved = 0; // not saved Fpaint2(); // update window } return 0; // not executed, stop warning } void * alien_colors_wthread(void *arg) // working threads { using namespace alien_colors_names; int index = *((int *) (arg)); int px, py, ww, hh; int row, col, row1, col1, row2, col2; int maxrow, maxcol, bsize, edist = 0; float d1, d7, dx, dy; float W0, W1, W2, W3, W4, W5, W6, W7, W8, Wsum; float R1, G1, B1, R3, G3, B3, H, S, L; float theta, f1, f2; float *pix1, *pix3; ww = E3pxm->ww; hh = E3pxm->hh; bsize = BLsz; // block size if (ww / bsize >= maxblocks) bsize = ww / (maxblocks - 1); // stop > maxblocks rows or cols if (hh / bsize >= maxblocks) bsize = hh / (maxblocks - 1); maxrow = 1 + hh / bsize; maxcol = 1 + ww / bsize; for (py = index; py < hh; py += NWT) for (px = 0; px < ww; px++) { if (sa_stat == 3) { // select area active int ii = py * ww + px; edist = sa_pixmap[ii]; // distance from edge if (! edist) continue; // pixel outside area } d1 = py - py / bsize * bsize; // y coordinate within block d7 = px - px / bsize * bsize; // x coordinate d1 = d1 / bsize; // rescale, 0 .. 1 d7 = d7 / bsize; dx = d7 - 0.5; // distance from pixel to center dy = d1 - 0.5; // of 9 block group W0 = sqrtf(dx*dx + dy*dy); dx = d7 - 0.5; dy = d1 + 0.5; W1 = sqrtf(dx*dx + dy*dy); dx = 1.5 - d7; dy = d1 + 0.5; W2 = sqrtf(dx*dx + dy*dy); dx = 1.5 - d7; dy = 0.5 - d1; W3 = sqrtf(dx*dx + dy*dy); dx = 1.5 - d7; dy = 1.5 - d1; W4 = sqrtf(dx*dx + dy*dy); dx = 0.5 - d7; dy = 1.5 - d1; W5 = sqrtf(dx*dx + dy*dy); dx = d7 + 0.5; dy = 1.5 - d1; W6 = sqrtf(dx*dx + dy*dy); dx = d7 + 0.5; dy = 0.5 - d1; W7 = sqrtf(dx*dx + dy*dy); dx = d7 + 0.5; dy = d1 + 0.5; W8 = sqrtf(dx*dx + dy*dy); W0 = 1.5 - W0; // block weight: W1 = 1.5 - W1; // max. distance - actual W2 = 1.5 - W2; // closer blocks have more weight W3 = 1.5 - W3; W4 = 1.5 - W4; W5 = 1.5 - W5; W6 = 1.5 - W6; W7 = 1.5 - W7; W8 = 1.5 - W8; if (W2 < 0) W2 = 0; // corners may go negative if (W4 < 0) W4 = 0; if (W6 < 0) W6 = 0; if (W8 < 0) W8 = 0; Wsum = W0 + W1 + W2 + W3 + W4 + W5 + W6 + W7 + W8; // sum of weights W0 = W0 / Wsum; // normalize to make sum = 1 W1 = W1 / Wsum; W2 = W2 / Wsum; W3 = W3 / Wsum; W4 = W4 / Wsum; W5 = W5 / Wsum; W6 = W6 / Wsum; W7 = W7 / Wsum; W8 = W8 / Wsum; row = py / bsize; // block[row][col] for this px/py col = px / bsize; row1 = row2 = row; col1 = col2 = col; if (row > 0) row1 = row - 1; if (row < maxrow) row2 = row + 1; if (col > 0) col1 = col - 1; if (col < maxcol) col2 = col + 1; W0 = W0 * Frand[col][row]; // compute random for pixel from W1 = W1 * Frand[col][row1]; // block randoms * weights W2 = W2 * Frand[col2][row1]; W3 = W3 * Frand[col2][row]; W4 = W4 * Frand[col2][row2]; W5 = W5 * Frand[col][row2]; W6 = W6 * Frand[col1][row2]; W7 = W7 * Frand[col1][row]; W8 = W8 * Frand[col1][row1]; theta = W0 + W1 + W2 + W3 + W4 + W5 + W6 + W7 + W8; // hue angle change theta = theta * 2.0 * Ampl - Ampl; // scale -Ampl ... +Ampl pix1 = PXMpix(E1pxm,px,py); // input pixel pix3 = PXMpix(E3pxm,px,py); // output pixel R1 = pix1[0]; G1 = pix1[1]; B1 = pix1[2]; RGBtoHSL(R1,G1,B1,H,S,L); H = H + 180 * theta; // displace H by -90 ... +90 if (H < 0) H += 360.0; else if (H >= 360.0) H -= 360.0; HSLtoRGB(H,S,L,R3,G3,B3); if (sa_stat == 3 && edist < sa_blend) { // blend edges f1 = sa_blendfunc(edist); f2 = 1.0 - f1; R3 = f1 * R3 + f2 * R1; G3 = f1 * G3 + f2 * G1; B3 = f1 * B3 + f2 * B1; } pix3[0] = R3; pix3[1] = G3; pix3[2] = B3; } exit_wthread(); // exit thread return 0; // not executed, stop gcc warning } fotoxx-18.01.1/fotoxx.desktop0000644000175000017500000000160413222767271014605 0ustar micomico[Desktop Entry] Name=fotoxx GenericName=Photo Editor Comment=Edit photos and manage collection Categories=Graphics;Photography; Keywords=photo;image;raw;edit;metadata;album; Type=Application Terminal=false MimeType=image/bmp;image/gif;image/tiff;image/jpeg;image/png; Exec=fotoxx %f Icon=/usr/share/fotoxx/icons/fotoxx.png GenericName[de]=Foto Bearbeitung GenericName[fr]=Gestionnaire de photos GenericName[es]=Editor fotográfico GenericName[gl]=Editor fotográfico Comment[de]=Fotos bearbeiten und Sammlung verwalten Comment[fr]=Organisez et éditez vos photos Comment[es]=Editar fotografías y administrar colecciones Comment[gl]=Editar fotografías e administrar coleccións Actions=Last;Recent;Blank; Name[en_US]=fotoxx [Desktop Action Last] Name=last image Exec=fotoxx -p [Desktop Action Recent] Name=recent images Exec=fotoxx -r [Desktop Action Blank] Name=blank window Exec=fotoxx -b fotoxx-18.01.1/fotoxx.png0000644000175000017500000002332713222767271013726 0ustar micomicoPNG  IHDR@@iqAzTXtRaw profile type exifxڥm$ D*H5]CRRg,bOUokσ>~%'I{SQa﯍}}PE֛͢<64jrfraKoxypw[_߾,uro;/߹?K3ۃ{wT6.0ַ@ߍ;'9PG5fFLQ^ _\.iɵ}&G 9f¬wv~s^uZz4\Т0ylqrSɢ?__g97;_\0~9_A7?_>OV07i~*T/ʋma!x|ߍ*g+Ɛ&UIQ1!g! KD5FZMI=ָs8Zdl7V?^ZkӮÊܮ /^]G+6mAks/hv[} <<ˬSMm9Yu嫭F(QC£EeC]>۶rcO;W /P/ԝ?Qc|a* /fI"w1K=1R/6!i%둟 *rυD ߠ`OޘB|:[e.5-nk]ud=5dejH ŢX~ٻ=#i&ڛl˒ f4B6܋P-IA9yT5X!sK3!щ

    Ys53oo۱KH.N~ZwoJE Cb{8;lَ}mנBI0AVA]- NԘlu#M/'T-vZeK;}BIpqLx|3bAU==o%u9&/Ep;5ߓ< TZWy<-i=FS'@ܥdv6eہ9#W7X?l;3I9JB9lC`} qluC(25-S $:ϡ־uⰱL)y:P iv Fm#Fz6dvbxb=p5!C)GlZuԯ:FdhF&-!^U! + o#uOMj.MQW* KQ><W{3ӠFDW5M~ O4Wn(R:}Y{xIE?2y+d Pv&DJ#[nd'JP}ggm è'JG;fGG}}HD[O.:]͎,NѫУcA 0#B=6+ux 5T*6XFL@6xm7bQRTmYC"Fav^ Άpwґl,t4)BmGPp: O:%H)+(uuAcǁ_t.Ѽ_^vn}nNZd)W'\Wi`#{'}!TJ|߿p-ko}ߩϟ LbQiꃃ{{{z)Sԅ~nR,-e:z /wu>QM)Wh`xd[֎Dggf^exxd2k+rogfzL&2TM}뺁fBMJH)q)TgeXlyrrgyoJ%gŕ+SbӦ?O?GlcipI* j|>OzaQ/BHOttZFP ϲˋT*b(#M٫9r!y.&0Mp8D<%HLuٙ3E3I4r,GDcф^Poܰʝ۶?=y-^L?W\N&QogIR(zrl.Kf92|-[u|/  4!^opE2 aP.$@|-!x@k!uh4p(D6cr 0W.Oi=z,ۼu˲>^Vo^t>|x-lxH\6 KKr98X58(z݃!%R\7[)%B 4M'}Z>?Xfp8eed2* RFQc`hs \˄–w𾇿O:|:Rjݺ[Pf^EHA\ctt8}4O>$Hb!"w}RH)!_Kt]#N/!@Y) 4M`< ]07RM7K&8Cb-td?G>WJ^†b4)R)S,O~7ٺu+>?γ~p8L6AJIZ!JaYJT<(cccBJrY&&.Y"`iiJB>cM%\ץZQ*ƚ5c+\bMtwuqYb(kh/{~OBMRJ:\Qy{ 122뺜9sFr4 .hMh4_W;;(]]]o}l6c7Y\\` zg(|_@|_!$Hi&nݺʊ :ܵӧN395úu <|_""4 ];NT$R(m^'m W:yT {! >K! $0x |\ElOqIa׮]<={!4}y~MƄ!т<g=9qE6oބFA*bfi4$ .\=%SըT>}1B ܹbO͛yKLNNreQ(q0tww34<(XEX`U(C!x+/322JgAJɽ{׮6lP]ĈBobph;wzWy7r 'e%Ν?C#],H$oNZE)Ji.>͆mLMMQ*'\p^xp,F) &ab&eBb1پ};izzz0!(@ IZ'#<Iu8qb~% PôHъ)Fc_+C|011A4~CP?W_.\8Ilwri c\x?0??iu&O`MmS,T̰n:xLD, )+沘RRjĢ1Z|҄G"1))P-N_cjj )%h~_`F˿R^P)%CCD"QT}=:w_q\zcppD"A8^@)8V,//S(i6:}l6Ýَa+{xSרkw4Mb_M7<뺮Yե['8\qlסX,<ɓ'(+n߾\ n߇kੂ|Ӝ>})zz맿p8i躁ah2) Di7es377p~W? Bpa޽W, 4#ݟs: ]MZE<4M&q]JL:$IX޸a}+H[o+Wҏ^bdxdFA,#O`YDT*E<88zjJTRR1tH$L6ennz/)Jkʇ>b2n.4MAӅBub]] 088fET%%s1vۊ٫Rgnn1VdY2 BL6"33 4Mӟҥ 6l؀i޵;9z-~rjf]}ſ:Ѫe9mJ Jb+.b:5ݻÇ r׾UΝ;F|%iɭgyrY|=J%ZZK/'N|={BGr{T&X~=LMONWBJՄ@ɖ@>o61-|0J8CVcqi\x Mذa#l'Kf^E)H$t: Ɔ(wxp-}&}QAg"JWWX^^駟ĉ2>~+TGi&l͛F"1 %K 2Xyt5q\\[H&,..122JR _t$ B0QCy8m :CRo2o<¶N2Bu4K:8KRoYr{}li&TA Y ρW=aIQ({xT^˗'PJ1ɚ)Hc]p+'|v 8BfRTˌ GD"LOM#Q]oT@R7;ƀ |Vh == 9{u]  2u )}5a ‰F@3S7`wa2M\si"I|ߣ\E3uVVdzJV)Q~T BH֭P( $H~|tpE fR OPk:t'L¡JP.yWH$jnn~ls@VKRʗ-o+n[>_zu8zh&J)d2B!,-&&[&p$xp]dzQr0٦ATm}(&4 u9H) yx{^p}?l6}Kfn+BFFb7Tkx~@}'LrN p9V:Rl:yB,pe[~##Hg]蚎fI&! RRfddRhLOOٳHTG[v"D?]]]4m mg:L\s*KoLO7^|>_:u]3B"бh4J:l9V شqTK~6W_ [9W[]g?xǧ>i^x;wz@;[mQRJ"[la]XIwwz۷置ًs~ؑS|!u|w5M[|)Μ90qT^k׿pp(|i7/,-\~ t]~ip`hTvt$dlWLy.Z:JTQoѱ^/,-- Fܸq۷o˗BFAR!NcF~gRC2R`&CCCxGGG-[+WO?sq `7iCDcW<} ~z2 Z]7_x'&&cA1Gu4'Y^2sjA,h!::R*uC{{W_vXRDϵ6]Px`pp:007 S4r>tʕ>|Q_$((w| :P([v|}ݷo˖-;LJH$I!B)DZ\.NMM&&&߿gϼd\70 H$q2cJjPJH7~f[u-39Bk. rTT뵩t:]t]w۟5jqiOО";o=z.6Sky۱zwǻio/hUIENDB`fotoxx-18.01.1/f.combine.cc0000644000175000017500000105171713222767271014045 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit functions - Combine menu functions m_HDR combine and adjust images with different exposures m_HDF combine and adjust images with different focus depth m_stack_paint combine multiple photos to eliminate transient objects m_stack_noise combine multiple photos to reduce noise m_pano_horz combine overlapped images horizontally m_pano_vert combine overlapped images vertically m_pano_PT combine overlapped images using Panorama Tools (Hugin) *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // File scope variables and functions for composite images // used by HDR, HDF, STP, STN, Panorama. int cimNF; // image count, <= 10 int cimLF; // index of alphabetically last input file 17.04 char *cimFile[10]; // input image files PXM *cimPXMf[10]; // original images PXM *cimPXMs[10]; // alignment images, scaled and curved (pano) PXM *cimPXMw[10]; // alignment images, warped struct cimoffs { // image alignment offsets float xf, yf, tf; // x, y, theta offsets float wx[4], wy[4]; // x/y corner warps, 0=NW, 1=NE, 2=SE, 3=SW }; cimoffs cimOffs[10]; // image alignment data in E3 output image int pixcc = 4 * sizeof(float); // RGB pixel cc with alpha channel float cimScale; // alignment image size relative to full image float cimBlend; // image blend width at overlap, pixels (pano) float cimSearchRange; // alignment search range, pixels float cimSearchStep; // alignment search step, vpixels int cim_manualwarp; // alignmant manual warping instead of auto int cim_manualalign; // alignment is manual instead of auto float cimWarpRange; // alignment corner warp range, pixels float cimWarpStep; // alignment corner warp step, vpixels float cimSampSize; // pixel sample size int cimOv1xlo, cimOv1xhi, cimOv1ylo, cimOv1yhi; // rectangle enclosing overlap area, int cimOv2xlo, cimOv2xhi, cimOv2ylo, cimOv2yhi; // image 1 and image2 coordinates float cimRGBmf1[3][256]; // RGB matching factors for image 1/2 compare: float cimRGBmf2[3][256]; // cimRGBmf1[*][pix1[*]] == cimRGBmf2[*][pix2[*]] char *cimRedpix = 0; // maps high-contrast pixels for alignment int cimRedImage; // which image has red pixels int cimNsearch; // alignment search counter int cimShowIm1, cimShowIm2; // two images for cim_show_images() int cimShowAll; // if > 0, show all images int cimPano; // pano mode flag for cim_align_image() int cimPanoV; // vertical pano flag int cimPanoNC; // pano no-curve flag (scanned image) float cimPanoFL; // pano lens focal length at image scale int cim_load_files(); // load and check selected files void cim_scale_image(int im, PXM **); // scale image, 1.0 to cimScale (normally < 1) float cim_get_overlap(int im1, int im2, PXM **); // get overlap area for images (horiz or vert) void cim_match_colors(int im1, int im2, PXM **); // match image RGB levels >> match data void cim_adjust_colors(PXM *pxm, int fwhich); // adjust RGB levels from match data void cim_adjust_colors_pano(PXM *pxm, int fwhich); // adjust RGB levels from match data void cim_adjust_colors_vpano(PXM *pxm, int fwhich); // adjust RGB levels from match data void cim_get_redpix(int im1); // find high-contrast pixels in overlap area void cim_curve_image(int im); // curve cimPXMs[im] using lens parameters void cim_curve_Vimage(int im); // vertical pano version void cim_warp_image(int im); // warp image corners: cimPXMs[im] >> cimPXMw[im] void cim_warp_image_pano(int im, int fblend); // pano version, all / left side / blend stripe void cim_warp_image_Vpano(int im, int fblend); // vertical pans version: bottom side corners void cim_align_image(int im1, int im2); // align image im2 to im1, modify im2 offsets float cim_match_images(int im1, int im2); // compute match for overlapped images void cim_show_images(int fnew, int fblend); // combine images >> E3pxm >> main window void cim_show_Vimages(int fnew, int fblend); // vertical pano version void cim_trim(); // cut-off edges where all images do not overlap void cim_trim_margins(); // cut-off excess margins (panorama) void cim_dump_offsets(cchar *label); // diagnostic tool void cim_flatten_image(float F); // flatten panorama image void cim_flatten_Vimage(float F); // flatten vert. panorama image /******************************************************************************** Make an HDR (high dynamic range) image from several images of the same subject with different exposure levels. The composite image has better visibility of detail in both the brightest and darkest areas. *********************************************************************************/ int HDR_stat; // 1 = OK, 0 = failed or canceled float HDR_initAlignSize = 160; // initial align image size float HDR_imageIncrease = 1.6; // image size increase per align cycle float HDR_sampSize = 10000; // pixel sample size float HDR_initSearchRange = 8.0; // initial search range, +/- pixels float HDR_initSearchStep = 1.0; // initial search step, pixels float HDR_searchRange = 3.0; // normal search range, +/- pixels float HDR_searchStep = 1.0; // normal search step, pixels float HDR_initWarpRange = 5.0; // initial corner warp range, +/- pixels float HDR_initWarpStep = 1.0; // initial corner warp step, pixels float HDR_warpRange = 3.0; // normal corner warp range, +/- pixels float HDR_warpStep = 1.0; // normal corner warp step, pixels float *HDR_bright = 0; // maps brightness per pixel zdialog *HDR_adjustzd = 0; // adjust dialog float HDR_respfac[10][1000]; // contribution / image / pixel brightness void * HDR_align_thread(void *); // align 2 images void HDR_brightness(); // compute pixel brightness levels void HDR_adjust(); // adjust image contribution curves void * HDR_adjust_thread(void *); // combine images per contribution curves editfunc EFhdr; // edit function data // menu function void m_HDR(GtkWidget *, cchar *) { char *ftemp; int imx, jj, err, px, py, ww, hh; float diffw, diffh; float fbright[10], btemp; float pixsum, fnorm = 1.0 / 256.0; float *pixel; PXM *pxmtemp; F1_help_topic = "HDR"; // help topic if (checkpend("all")) return; HDR_bright = 0; cim_manualwarp = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 9) { zmessageACK(Mwin,ZTX("Select 2 to 9 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files ww = cimPXMf[0]->ww; hh = cimPXMf[0]->hh; for (imx = 1; imx < cimNF; imx++) // check image compatibility { diffw = abs(ww - cimPXMf[imx]->ww); diffw = diffw / ww; diffh = abs(hh - cimPXMf[imx]->hh); diffh = diffh / hh; if (diffw > 0.02 || diffh > 0.02) { zmessageACK(Mwin,ZTX("Images are not all the same size")); goto cleanup; } } free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last 17.04 if (err) goto cleanup; EFhdr.menufunc = m_HDR; EFhdr.funcname = "HDR"; if (! edit_setup(EFhdr)) goto cleanup; // setup edit (will lock) for (imx = 0; imx < cimNF; imx++) // compute image brightness levels { ww = cimPXMf[imx]->ww; hh = cimPXMf[imx]->hh; // image sizes can vary a little pixsum = 0; for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { pixel = PXMpix(cimPXMf[imx],px,py); pixsum += fnorm * (pixel[0] + pixel[1] + pixel[2]); } fbright[imx] = pixsum / (ww * hh); } for (imx = 0; imx < cimNF; imx++) // sort file and pixmap lists for (jj = imx+1; jj < cimNF; jj++) // by decreasing brightness { if (fbright[jj] > fbright[imx]) { // bubble sort btemp = fbright[jj]; fbright[jj] = fbright[imx]; fbright[imx] = btemp; ftemp = cimFile[jj]; cimFile[jj] = cimFile[imx]; cimFile[imx] = ftemp; pxmtemp = cimPXMf[jj]; cimPXMf[jj] = cimPXMf[imx]; cimPXMf[imx] = pxmtemp; } } start_thread(HDR_align_thread,0); // align each pair of images wrapup_thread(0); // wait for completion if (HDR_stat != 1) goto cancel; HDR_brightness(); // compute pixel brightness levels if (HDR_stat != 1) goto cancel; HDR_adjust(); // combine images based on user inputs if (HDR_stat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } if (HDR_bright) zfree(HDR_bright); *paneltext = 0; return; } // HDR align each pair of input images, output combined image to E3pxm // cimPXMf[*] original image // cimPXMs[*] scaled and color adjusted for pixel comparisons // cimPXMw[*] warped for display void * HDR_align_thread(void *) { int imx, im1, im2, ww, hh, ii, nn; float R, maxtf, mintf, midtf; float xoff, yoff, toff, dxoff, dyoff; cimoffs offsets[10]; // x/y/t offsets after alignment Fzoom = 0; // fit to window if big Fblowup = 1; // scale up to window if small Ffuncbusy = 1; cimPano = cimPanoV = 0; // no pano mode for (imx = 0; imx < cimNF; imx++) memset(&offsets[imx],0,sizeof(cimoffs)); for (im1 = 0; im1 < cimNF-1; im1++) // loop each pair of images { im2 = im1 + 1; memset(&cimOffs[im1],0,sizeof(cimoffs)); // initial image offsets = 0 memset(&cimOffs[im2],0,sizeof(cimoffs)); ww = cimPXMf[im1]->ww; // image dimensions hh = cimPXMf[im1]->hh; nn = ww; // use larger of ww, hh if (hh > ww) nn = hh; cimScale = HDR_initAlignSize / nn; // initial align image size if (cimScale > 1.0) cimScale = 1.0; cimBlend = 0; // no blend width (use all) cim_get_overlap(im1,im2,cimPXMf); // get overlap area cim_match_colors(im1,im2,cimPXMf); // get color matching factors cimSearchRange = HDR_initSearchRange; // initial align search range cimSearchStep = HDR_initSearchStep; // initial align search step cimWarpRange = HDR_initWarpRange; // initial align corner warp range cimWarpStep = HDR_initWarpStep; // initial align corner warp step cimSampSize = HDR_sampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { cim_scale_image(im1,cimPXMs); // scale images to cimScale cim_scale_image(im2,cimPXMs); cim_adjust_colors(cimPXMs[im1],1); // apply color adjustments cim_adjust_colors(cimPXMs[im2],2); cim_warp_image(im1); // make warped images to show cim_warp_image(im2); cim_get_overlap(im1,im2,cimPXMs); // get overlap area cim_get_redpix(im1); // get high-contrast pixels cimShowIm1 = im1; // show two images with 50/50 blend cimShowIm2 = im2; cimShowAll = 0; cim_show_images(1,0); // (x/y offsets can change) cim_align_image(im1,im2); // align im2 to im1 zfree(cimRedpix); // clear red pixels cimRedpix = 0; if (cimScale == 1.0) break; // done R = HDR_imageIncrease; // next larger image size cimScale = cimScale * R; if (cimScale > 0.85) { // if close to end, jump to end R = R / cimScale; cimScale = 1.0; } cimOffs[im1].xf *= R; // scale offsets for larger image cimOffs[im1].yf *= R; cimOffs[im2].xf *= R; cimOffs[im2].yf *= R; for (ii = 0; ii < 4; ii++) { cimOffs[im1].wx[ii] *= R; cimOffs[im1].wy[ii] *= R; cimOffs[im2].wx[ii] *= R; cimOffs[im2].wy[ii] *= R; } cimSearchRange = HDR_searchRange; // align search range cimSearchStep = HDR_searchStep; // align search step size cimWarpRange = HDR_warpRange; // align corner warp range cimWarpStep = HDR_warpStep; // align corner warp step size } offsets[im2].xf = cimOffs[im2].xf - cimOffs[im1].xf; // save im2 offsets from im1 offsets[im2].yf = cimOffs[im2].yf - cimOffs[im1].yf; offsets[im2].tf = cimOffs[im2].tf - cimOffs[im1].tf; for (ii = 0; ii < 4; ii++) { offsets[im2].wx[ii] = cimOffs[im2].wx[ii] - cimOffs[im1].wx[ii]; offsets[im2].wy[ii] = cimOffs[im2].wy[ii] - cimOffs[im1].wy[ii]; } } for (imx = 0; imx < cimNF; imx++) // offsets[*] >> cimOffs[*] cimOffs[imx] = offsets[imx]; cimOffs[0].xf = cimOffs[0].yf = cimOffs[0].tf = 0; // image 0 at (0,0,0) for (im1 = 0; im1 < cimNF-1; im1++) // absolute offsets for image 1 to last { im2 = im1 + 1; cimOffs[im2].xf += cimOffs[im1].xf; // x/y/t offsets are additive cimOffs[im2].yf += cimOffs[im1].yf; cimOffs[im2].tf += cimOffs[im1].tf; for (ii = 0; ii < 4; ii++) { // corner warps are additive cimOffs[im2].wx[ii] += cimOffs[im1].wx[ii]; cimOffs[im2].wy[ii] += cimOffs[im1].wy[ii]; } } for (imx = 1; imx < cimNF; imx++) // re-warp to absolute cim_warp_image(imx); toff = cimOffs[0].tf; // balance +/- thetas maxtf = mintf = toff; for (imx = 1; imx < cimNF; imx++) { toff = cimOffs[imx].tf; if (toff > maxtf) maxtf = toff; if (toff < mintf) mintf = toff; } midtf = 0.5 * (maxtf + mintf); for (imx = 0; imx < cimNF; imx++) cimOffs[imx].tf -= midtf; for (im1 = 0; im1 < cimNF-1; im1++) // adjust x/y offsets for images after im1 for (im2 = im1+1; im2 < cimNF; im2++) // due to im1 theta offset { toff = cimOffs[im1].tf; xoff = cimOffs[im2].xf - cimOffs[im1].xf; yoff = cimOffs[im2].yf - cimOffs[im1].yf; dxoff = yoff * sinf(toff); dyoff = xoff * sinf(toff); cimOffs[im2].xf -= dxoff; cimOffs[im2].yf += dyoff; } Fzoom = Fblowup = 0; Ffuncbusy = 0; HDR_stat = 1; thread_exit(); return 0; } // Compute mean image pixel brightness levels. // (basis for setting image contributions per brightness level) void HDR_brightness() { int px3, py3, ww, hh, imx, kk, vstat; float px, py, red, green, blue; float bright, maxbright, minbright; float xoff, yoff, sintf[10], costf[10]; float norm, fnorm = 1.0 / 256.0; float vpix[4], *pix3; Ffuncbusy = 1; cimScale = 1.0; for (imx = 0; imx < cimNF; imx++) // replace alignment images { // (color adjusted for pixel matching) PXM_free(cimPXMs[imx]); // with the original images cimPXMs[imx] = PXM_copy(cimPXMf[imx]); cim_warp_image(imx); // re-apply warps } for (imx = 0; imx < cimNF; imx++) // pre-calculate trig functions { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } ww = E3pxm->ww; hh = E3pxm->hh; HDR_bright = (float *) zmalloc(ww*hh*sizeof(int)); // get memory for brightness array minbright = 1.0; maxbright = 0.0; for (py3 = 0; py3 < hh; py3++) // step through all output pixels for (px3 = 0; px3 < ww; px3++) { red = green = blue = 0; vstat = 0; for (imx = 0; imx < cimNF; imx++) // step through all input images { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); // image N pixel, after offsets py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (! vstat) break; red += fnorm * vpix[0]; // sum input pixels green += fnorm * vpix[1]; blue += fnorm * vpix[2]; } if (! vstat) { // pixel outside some image pix3 = PXMpix(E3pxm,px3,py3); // output pixel = black pix3[0] = pix3[1] = pix3[2] = pix3[3] = 0; // alpha = 0 kk = py3 * ww + px3; HDR_bright[kk] = 0; continue; } bright = (red + green + blue) / (3 * cimNF); // mean pixel brightness, 0.0 to 1.0 kk = py3 * ww + px3; HDR_bright[kk] = bright; if (bright > maxbright) maxbright = bright; if (bright < minbright) minbright = bright; pix3 = PXMpix(E3pxm,px3,py3); // output pixel pix3[0] = red * 255.9 / cimNF; pix3[1] = green * 255.9 / cimNF; pix3[2] = blue * 255.9 / cimNF; } norm = 0.999 / (maxbright - minbright); // normalize to range 0.0 to 0.999 for (int ii = 0; ii < ww * hh; ii++) HDR_bright[ii] = (HDR_bright[ii] - minbright) * norm; Ffuncbusy = 0; Fpaint2(); // update window return; } // Dialog for user to control the contributions of each input image // while watching the output image which is updated in real time. void HDR_adjust() { int HDR_adjust_dialog_event(zdialog *zd, cchar *event); void HDR_curvedit(int); int imx; float cww = 1.0 / (cimNF-1); /*** _____________________________________________________ | Adjust Image Contributions | | _________________________________________________ | | | | | | | | | | | | | | | curve drawing area | | | | | | | | | | | | | | | |_________________________________________________| | | dark pixels light pixels | | file: xxxxxx.jpg | | | | Curve File: [Open] [Save] | | [Done] [Cancel] | |_____________________________________________________| ***/ HDR_adjustzd = zdialog_new(ZTX("Adjust Image Contributions"),Mwin,Bdone,Bcancel,null); zdialog *zd = HDR_adjustzd; zdialog_add_widget(zd,"frame","brframe","dialog",0,"expand|space=2"); zdialog_add_widget(zd,"hbox","hb1","dialog",0); zdialog_add_widget(zd,"label","lab11","hb1",ZTX("dark pixels"),"space=3"); zdialog_add_widget(zd,"label","lab12","hb1",0,"expand"); zdialog_add_widget(zd,"label","lab13","hb1",ZTX("light pixels"),"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labf1","hb2",ZTX("file:"),"space=3"); zdialog_add_widget(zd,"label","labf2","hb2","*"); zdialog_add_widget(zd,"hbox","hbcf","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labcf","hbcf",Bcurvefile,"space=5"); zdialog_add_widget(zd,"button","load","hbcf",Bopen,"space=5"); zdialog_add_widget(zd,"button","savecurve","hbcf",Bsave,"space=5"); GtkWidget *brframe = zdialog_widget(zd,"brframe"); // set up curve edit spldat *sd = splcurve_init(brframe,HDR_curvedit); EFhdr.curves = sd; sd->Nspc = cimNF; // no. curves = no. files for (imx = 0; imx < cimNF; imx++) // set up initial response curve { sd->fact[imx] = 1; sd->vert[imx] = 0; sd->nap[imx] = 2; sd->apx[imx][0] = 0.01; sd->apx[imx][1] = 0.99; sd->apy[imx][0] = 0.9 - imx * 0.8 * cww; sd->apy[imx][1] = 0.1 + imx * 0.8 * cww; splcurve_generate(sd,imx); } start_thread(HDR_adjust_thread,0); // start working thread signal_thread(); zdialog_resize(zd,400,360); zdialog_run(zd,HDR_adjust_dialog_event,"save"); // run dialog zdialog_wait(zd); // wait for completion zdialog_free(zd); return; } // dialog event and completion callback function int HDR_adjust_dialog_event(zdialog *zd, cchar *event) { spldat *sd = EFhdr.curves; if (strmatch(event,"load")) { // load saved curve splcurve_load(sd); zdialog_stuff(zd,"labf2","*"); signal_thread(); return 0; } if (strmatch(event,"savecurve")) { // save curve to file splcurve_save(sd); return 0; } if (zd->zstat) // dialog complete { zdialog_destroy(zd); // prevent double [cancel] wrapup_thread(8); if (zd->zstat == 1) HDR_stat = 1; else HDR_stat = 0; if (HDR_stat == 1) cim_trim(); // cut-off edges } return 1; } // this function is called when a curve is edited void HDR_curvedit(int spc) { cchar *pp; pp = strrchr(cimFile[spc],'/'); zdialog_stuff(HDR_adjustzd,"labf2",pp+1); signal_thread(); return; } // Combine all input images >> E3pxm based on image response curves. void * HDR_adjust_thread(void *) { void * HDR_adjust_wthread(void *arg); int imx, ii, kk; float xlo, xhi, xval, yval, sumrf; spldat *sd = EFhdr.curves; while (true) { thread_idle_loop(); // wait for work or exit request for (imx = 0; imx < cimNF; imx++) // loop input images { ii = sd->nap[imx]; // get low and high anchor points xlo = sd->apx[imx][0]; // for image response curve xhi = sd->apx[imx][ii-1]; if (xlo < 0.02) xlo = 0; // snap-to scale end points if (xhi > 0.98) xhi = 1; for (ii = 0; ii < 1000; ii++) // loop all brightness levels { HDR_respfac[imx][ii] = 0; xval = 0.001 * ii; if (xval < xlo || xval > xhi) continue; // no influence for brightness level kk = 1000 * xval; yval = sd->yval[imx][kk]; HDR_respfac[imx][ii] = yval; // contribution of this input image } } for (ii = 0; ii < 1000; ii++) // normalize the factors so that { // they sum to 1.0 sumrf = 0; for (imx = 0; imx < cimNF; imx++) sumrf += HDR_respfac[imx][ii]; if (! sumrf) continue; for (imx = 0; imx < cimNF; imx++) HDR_respfac[imx][ii] = HDR_respfac[imx][ii] / sumrf; } for (ii = 0; ii < NWT; ii++) // start worker threads start_wthread(HDR_adjust_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fpaint2(); // update window } return 0; // not executed } void * HDR_adjust_wthread(void *arg) // working thread { int index = *((int *) (arg)); int imx, ww, hh, ii, px3, py3, vstat; float sintf[10], costf[10], xoff, yoff; float px, py, red, green, blue, bright, factor; float vpix[4], *pix3; for (imx = 0; imx < cimNF; imx++) // pre-calculate trig functions { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } ww = E3pxm->ww; hh = E3pxm->hh; for (py3 = index; py3 < hh; py3 += NWT) // step through all output pixels for (px3 = 0; px3 < ww; px3++) { ii = py3 * ww + px3; bright = HDR_bright[ii]; // mean brightness, 0.0 to 1.0 ii = 1000 * bright; red = green = blue = 0; for (imx = 0; imx < cimNF; imx++) // loop input images { factor = HDR_respfac[imx][ii]; // image contribution to this pixel if (! factor) continue; // none xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); // input virtual pixel mapping to py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); // this output pixel vstat = vpixel(cimPXMw[imx],px,py,vpix); // get input pixel if (! vstat) continue; red += factor * vpix[0]; // accumulate brightness contribution green += factor * vpix[1]; blue += factor * vpix[2]; } pix3 = PXMpix(E3pxm,px3,py3); // output pixel pix3[0] = red; // = sum of input pixel contributions pix3[1] = green; pix3[2] = blue; } exit_wthread(); return 0; // not executed } /******************************************************************************** Make an HDF (high depth of field) image from several images of the same subject with different focus settings. Combine the images and allow the user to "paint" the output composite image using the mouse and choosing the sharpest input image for each area of the output image. The result is an image with a depth of field that exceeds the camera capability. The images are aligned at the center, but small differences in camera position (hand-held photos) will cause parallax errors that prevent perfect alignment of the images. Also, the images with nearer focus will be slightly larger than those with farther focus. These problems can be compensated by dragging and warping the images using the mouse. ********************************************************************************/ int HDF_stat; // 1 = OK, 0 = failed or canceled float HDF_initAlignSize = 160; // initial align image size float HDF_imageIncrease = 1.6; // image size increase per align cycle float HDF_sampSize = 10000; // pixel sample size float HDF_initSearchRange = 8.0; // initial search range, +/- pixels float HDF_initSearchStep = 2.0; // initial search step, pixels float HDF_searchRange = 4.0; // normal search range float HDF_searchStep = 1.0; // normal search step float HDF_initWarpRange = 6.0; // initial corner warp range float HDF_initWarpStep = 2.0; // initial corner warp step float HDF_warpRange = 3.0; // normal corner warp range float HDF_warpStep = 1.0; // normal corner warp step void * HDF_align_thread(void *); void HDF_adjust(); void HDF_mousefunc(); void * HDF_adjust_thread(void *); editfunc EFhdf; // edit function data // menu function void m_HDF(GtkWidget *, cchar *) { int imx, err, ww, hh; float diffw, diffh; F1_help_topic = "HDF"; // help topic if (checkpend("all")) return; cim_manualwarp = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 9) { zmessageACK(Mwin,ZTX("Select 2 to 9 files")); goto cleanup; } cimNF = GScount; for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files ww = cimPXMf[0]->ww; hh = cimPXMf[0]->hh; for (imx = 1; imx < cimNF; imx++) // check image compatibility { diffw = abs(ww - cimPXMf[imx]->ww); diffw = diffw / ww; diffh = abs(hh - cimPXMf[imx]->hh); diffh = diffh / hh; if (diffw > 0.02 || diffh > 0.02) { zmessageACK(Mwin,ZTX("Images are not all the same size")); goto cleanup; } } free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last 17.04 if (err) goto cleanup; EFhdf.menufunc = m_HDF; EFhdf.funcname = "HDF"; EFhdf.mousefunc = HDF_mousefunc; // 18.01 if (! edit_setup(EFhdf)) goto cleanup; // setup edit (will lock) start_thread(HDF_align_thread,0); // align each pair of images wrapup_thread(0); // wait for completion if (HDF_stat != 1) goto cancel; HDF_adjust(); // combine images based on user inputs if (HDF_stat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } *paneltext = 0; return; } // align each image 2nd-last to 1st image // cimPXMf[*] original image // cimPXMs[*] scaled and color adjusted for pixel comparisons // cimPXMw[*] warped for display void * HDF_align_thread(void *) { int imx, im1, im2, ww, hh, ii, nn; float R; Fzoom = 0; // fit to window if big Fblowup = 1; // scale up to window if small Ffuncbusy = 1; cimPano = cimPanoV = 0; // no pano mode for (imx = 1; imx < cimNF; imx++) // loop 2nd to last image { im1 = 0; // images to align im2 = imx; memset(&cimOffs[im1],0,sizeof(cimoffs)); // initial image offsets = 0 memset(&cimOffs[im2],0,sizeof(cimoffs)); ww = cimPXMf[im1]->ww; // image dimensions hh = cimPXMf[im1]->hh; nn = ww; // use larger of ww, hh if (hh > ww) nn = hh; cimScale = HDF_initAlignSize / nn; // initial align image size if (cimScale > 1.0) cimScale = 1.0; cimBlend = 0; // no blend width (use all) cim_get_overlap(im1,im2,cimPXMf); // get overlap area cim_match_colors(im1,im2,cimPXMf); // get color matching factors cimSearchRange = HDF_initSearchRange; // initial align search range cimSearchStep = HDF_initSearchStep; // initial align search step cimWarpRange = HDF_initWarpRange; // initial align corner warp range cimWarpStep = HDF_initWarpStep; // initial align corner warp step cimSampSize = HDF_sampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { cim_scale_image(im1,cimPXMs); // scale images to cimScale cim_scale_image(im2,cimPXMs); cim_adjust_colors(cimPXMs[im1],1); // apply color adjustments cim_adjust_colors(cimPXMs[im2],2); cim_warp_image(im1); // warp images for show cim_warp_image(im2); cim_get_overlap(im1,im2,cimPXMs); // get overlap area cim_get_redpix(im1); // get high-contrast pixels cimShowIm1 = im1; // show these two images cimShowIm2 = im2; // with 50/50 blend cimShowAll = 0; cim_show_images(1,0); // (x/y offsets can change) cim_align_image(im1,im2); // align im2 to im1 zfree(cimRedpix); // clear red pixels cimRedpix = 0; if (cimScale == 1.0) break; // done R = HDF_imageIncrease; // next larger image size cimScale = cimScale * R; if (cimScale > 0.85) { // if close to end, jump to end R = R / cimScale; cimScale = 1.0; } cimOffs[im1].xf *= R; // scale offsets for larger image cimOffs[im1].yf *= R; cimOffs[im2].xf *= R; cimOffs[im2].yf *= R; for (ii = 0; ii < 4; ii++) { cimOffs[im1].wx[ii] *= R; cimOffs[im1].wy[ii] *= R; cimOffs[im2].wx[ii] *= R; cimOffs[im2].wy[ii] *= R; } cimSearchRange = HDF_searchRange; // align search range cimSearchStep = HDF_searchStep; // align search step size cimWarpRange = HDF_warpRange; // align corner warp range cimWarpStep = HDF_warpStep; // align corner warp step size } cimOffs[im2].xf = cimOffs[im2].xf - cimOffs[im1].xf; // save im2 offsets from im1 cimOffs[im2].yf = cimOffs[im2].yf - cimOffs[im1].yf; cimOffs[im2].tf = cimOffs[im2].tf - cimOffs[im1].tf; } memset(&cimOffs[0],0,sizeof(cimoffs)); Fzoom = Fblowup = 0; Ffuncbusy = 0; HDF_stat = 1; thread_exit(); return 0; // not executed } // paint and warp output image zdialog *HDF_adjustzd = 0; // paint/warp dialog int HDF_mode; // mode: paint or warp int HDF_image; // current image (0 based) int HDF_radius; // paint mode radius char *HDF_pixmap = 0; // map input image per output pixel float *HDF_warpx[10], *HDF_warpy[10]; // warp memory, pixel displacements void HDF_adjust() { char imageN[8] = "imageN", labN[4] = "0"; int cc, imx, ww, hh; int HDF_adjust_dialog_event(zdialog *zd, cchar *event); /*** ______________________________________ | Paint and Warp Image | | | | Image (o) 1 (o) 2 (o) 3 ... | | (o) paint Radius [____] | | (o) warp | | [Done] [Cancel] | |______________________________________| ***/ HDF_adjustzd = zdialog_new(ZTX("Paint and Warp Image"),Mwin,Bdone,Bcancel,null); zdialog *zd = HDF_adjustzd; zdialog_add_widget(zd,"hbox","hbim","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labim","hbim",Bimage,"space=5"); zdialog_add_widget(zd,"hbox","hbpw","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vbpw1","hbpw",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vbpw2","hbpw",0,"homog|space=5"); zdialog_add_widget(zd,"radio","paint","vbpw1",ZTX("paint")); zdialog_add_widget(zd,"radio","warp","vbpw1",ZTX("warp")); zdialog_add_widget(zd,"hbox","hbp","vbpw2"); zdialog_add_widget(zd,"label","labpr","hbp",Bradius,"space=5"); zdialog_add_widget(zd,"zspin","radius","hbp","1|400|1|100"); zdialog_add_widget(zd,"label","space","vbpw2"); for (imx = 0; imx < cimNF; imx++) { // add radio button for each image imageN[5] = '1' + imx; labN[0] = '1' + imx; zdialog_add_widget(zd,"radio",imageN,"hbim",labN); } zdialog_stuff(zd,"paint",1); // paint button on zdialog_stuff(zd,"warp",0); // warp button off zdialog_stuff(zd,"image1",1); // initial image = 1st HDF_mode = 0; // start in paint mode HDF_image = 0; // initial image HDF_radius = 100; // paint radius takeMouse(HDF_mousefunc,0); // connect mouse function cc = E3pxm->ww * E3pxm->hh; // allocate pixel map HDF_pixmap = (char *) zmalloc(cc); memset(HDF_pixmap,cimNF,cc); // initial state, blend all images for (imx = 0; imx < cimNF; imx++) { // allocate warp memory ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; HDF_warpx[imx] = (float *) zmalloc(ww * hh * sizeof(float)); HDF_warpy[imx] = (float *) zmalloc(ww * hh * sizeof(float)); } start_thread(HDF_adjust_thread,0); // start working thread signal_thread(); zdialog_resize(zd,250,0); // stretch a bit zdialog_run(zd,HDF_adjust_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); // wait for completion zdialog_free(zd); return; } // dialog event and completion callback function int HDF_adjust_dialog_event(zdialog *zd, cchar *event) { int imx, nn; if (zd->zstat) // dialog finish { zdialog_destroy(zd); // prevent double [cancel] freeMouse(); // disconnect mouse function signal_thread(); wrapup_thread(8); if (zd->zstat == 1) HDF_stat = 1; else HDF_stat = 0; if (HDF_stat == 1) cim_trim(); // cut-off edges HDF_mode = 0; zfree(HDF_pixmap); // free pixel map for (imx = 0; imx < cimNF; imx++) { zfree(HDF_warpx[imx]); // free warp memory zfree(HDF_warpy[imx]); } } if (strmatch(event,"paint")) { // set paint mode zdialog_fetch(zd,"paint",nn); if (! nn) return 1; HDF_mode = 0; gdk_window_set_cursor(gdkwin,0); // no drag cursor } if (strmatch(event,"warp")) { // set warp mode zdialog_fetch(zd,"warp",nn); if (! nn) return 1; HDF_mode = 1; draw_mousecircle(0,0,0,1,0); // stop mouse circle gdk_window_set_cursor(gdkwin,dragcursor); // set drag cursor } if (strmatchN(event,"image",5)) { // image radio button nn = event[5] - '0'; // 1 to cimNF if (nn > 0 && nn <= cimNF) HDF_image = nn - 1; // 0 to cimNF-1 signal_thread(); } if (strmatch(event,"radius")) // change paint radius zdialog_fetch(zd,"radius",HDF_radius); if (strmatch(event,"focus")) { // toggle mouse capture takeMouse(HDF_mousefunc,0); // connect mouse function if (HDF_mode == 1) gdk_window_set_cursor(gdkwin,dragcursor); // warp mode, drag cursor signal_thread(); } return 1; } // HDF dialog mouse function // paint: during drag, selected image >> HDF_pixmap (within paint radius) >> E3 // warp: for selected image, cimPXMs >> warp >> cimPXMw >> E3 void HDF_mousefunc() { float vpix1[4], *pix2, *pix3; int imx, radius, rect, radius2, vstat1; int mx, my, dx, dy, px3, py3; char imageN[8] = "imageN"; float px1, py1; float xoff, yoff, sintf[10], costf[10]; int ii, px, py, ww, hh; float mag, dispx, dispy, d1, d2; PXM *pxm1, *pxm2; if (LMclick || RMclick) // mouse click return; // process zooms normally if (HDF_mode == 0) goto paint; else if (HDF_mode == 1) goto warp; else return; paint: radius = HDF_radius; // paintbrush radius radius2 = radius * radius; draw_mousecircle(Mxposn,Myposn,radius,0,0); // draw mouse selection circle if (! Mxdrag && ! Mydrag) return; mx = Mxdrag; // mouse drag my = Mydrag; Mxdrag = Mydrag = 0; if (mx < 0 || mx > E3pxm->ww-1 || my < 0 || my > E3pxm->hh-1) // mouse outside image area return; for (imx = 0; imx < cimNF; imx++) // pre-calculate trig funcs { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } for (dy = -radius; dy <= radius; dy++) // loop pixels around mouse for (dx = -radius; dx <= radius; dx++) { if (dx*dx + dy*dy > radius2) continue; // outside radius px3 = mx + dx; // output pixel py3 = my + dy; if (px3 < 0 || px3 > E3pxm->ww-1) continue; // outside image if (py3 < 0 || py3 > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,px3,py3); // output pixel imx = py3 * E3pxm->ww + px3; // update pixmap to selected image HDF_pixmap[imx] = HDF_image; imx = HDF_image; xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px1 = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); // input virtual pixel py1 = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat1 = vpixel(cimPXMw[imx],px1,py1,vpix1); if (vstat1) memcpy(pix3,vpix1,pixcc); else memset(pix3,0,pixcc); // voided pixel } rect = 2 * radius; Fpaint3(mx-radius,my-radius,rect,rect,0); // update window draw_mousecircle(mx,my,radius,0,0); // draw mouse selection circle return; warp: if (! Mxdrag && ! Mydrag) return; mx = Mxdrag; // mouse drag my = Mydrag; if (mx < 0 || mx > E3pxm->ww-1 || my < 0 || my > E3pxm->hh-1) // mouse outside image area return; imx = my * E3pxm->ww + mx; // if pixel has been painted, imx = HDF_pixmap[imx]; // select corresp. image to warp if (imx == cimNF) return; // else no action if (imx != HDF_image) { HDF_image = imx; // update selected image and imageN[5] = '1' + imx; // dialog radio button zdialog_stuff(HDF_adjustzd,imageN,1); } pxm1 = cimPXMs[imx]; // input image pxm2 = cimPXMw[imx]; // output image ww = pxm2->ww; hh = pxm2->hh; mx = Mxdown; // drag origin, image coordinates my = Mydown; dx = Mxdrag - Mxdown; // drag increment dy = Mydrag - Mydown; Mxdown = Mxdrag; // next drag origin Mydown = Mydrag; Mxdrag = Mydrag = 0; d1 = ww * ww + hh * hh; for (py = 0; py < hh; py++) // process all output pixels for (px = 0; px < ww; px++) { d2 = (px-mx)*(px-mx) + (py-my)*(py-my); mag = (1.0 - d2 / d1); mag = mag * mag * mag * mag; mag = mag * mag * mag * mag; mag = mag * mag * mag * mag; dispx = -dx * mag; // displacement = drag * mag dispy = -dy * mag; ii = py * ww + px; HDF_warpx[imx][ii] += dispx; // add this drag to prior sum HDF_warpy[imx][ii] += dispy; dispx = HDF_warpx[imx][ii]; dispy = HDF_warpy[imx][ii]; vstat1 = vpixel(pxm1,px+dispx,py+dispy,vpix1); // input virtual pixel pix2 = PXMpix(pxm2,px,py); // output pixel if (vstat1) memcpy(pix2,vpix1,pixcc); else memset(pix2,0,pixcc); // voided pixel } signal_thread(); // combine images >> E3 >> main window return; } // Combine images in E3pxm (not reallocated). Update main window. void * HDF_adjust_thread(void *) { void * HDF_combine_wthread(void *); while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(HDF_combine_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fpaint2(); } return 0; // not executed } void * HDF_combine_wthread(void *arg) // worker thread { int index = *((int *) (arg)); // no more paint and warp modes int px3, py3, vstat1, imx; float red, green, blue; // float float px, py; float xoff, yoff, sintf[10], costf[10]; float vpix1[4], *pix3; for (imx = 0; imx < cimNF; imx++) // pre-calculate trig funcs { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } for (py3 = index+1; py3 < E3pxm->hh-1; py3 += NWT) // step through output pixels for (px3 = 1; px3 < E3pxm->ww-1; px3++) { pix3 = PXMpix(E3pxm,px3,py3); imx = py3 * E3pxm->ww + px3; imx = HDF_pixmap[imx]; if (imx < cimNF) // specific image maps to pixel { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat1 = vpixel(cimPXMw[imx],px,py,vpix1); // corresp. input vpixel if (vstat1) memcpy(pix3,vpix1,pixcc); else memset(pix3,0,pixcc); // voided pixel } else // use blend of all images { red = green = blue = 0; for (imx = 0; imx < cimNF; imx++) { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat1 = vpixel(cimPXMw[imx],px,py,vpix1); if (vstat1) { red += vpix1[0]; green += vpix1[1]; blue += vpix1[2]; } } pix3[0] = red / cimNF; pix3[1] = green / cimNF; pix3[2] = blue / cimNF; } } exit_wthread(); return 0; // not executed, avoid gcc warning } /******************************************************************************** Stack/Paint function Combine multiple images of one subject taken at different times from (almost) the same camera position. Align the images and allow the user to choose which input image to use for each area of the output image, by "painting" with the mouse. Use this to remove tourists and cars that move in and out of a scene being photographed. ********************************************************************************/ int STP_stat; // 1 = OK, 0 = failed or canceled float STP_initAlignSize = 160; // initial align image size float STP_imageIncrease = 1.6; // image size increase per align cycle float STP_sampSize = 10000; // pixel sample size float STP_initSearchRange = 8.0; // initial search range, +/- pixels float STP_initSearchStep = 2.0; // initial search step, pixels float STP_searchRange = 4.0; // normal search range float STP_searchStep = 1.0; // normal search step float STP_initWarpRange = 6.0; // initial corner warp range float STP_initWarpStep = 2.0; // initial corner warp step float STP_warpRange = 3.0; // normal corner warp range float STP_warpStep = 0.5; // normal corner warp step void * STP_align_thread(void *); void STP_adjust(); void STP_mousefunc(); void * STP_adjust_thread(void *); void STP_setpixel(int px, int py, int source); editfunc EFstp; // edit function data // menu function void m_stack_paint(GtkWidget *, cchar *) { int imx, err, ww, hh; float diffw, diffh; F1_help_topic = "stack_paint"; // help topic if (checkpend("all")) return; cim_manualwarp = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 9) { zmessageACK(Mwin,ZTX("Select 2 to 9 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files ww = cimPXMf[0]->ww; hh = cimPXMf[0]->hh; for (imx = 1; imx < cimNF; imx++) // check image compatibility { diffw = abs(ww - cimPXMf[imx]->ww); diffw = diffw / ww; diffh = abs(hh - cimPXMf[imx]->hh); diffh = diffh / hh; if (diffw > 0.02 || diffh > 0.02) { zmessageACK(Mwin,ZTX("Images are not all the same size")); goto cleanup; } } free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last 17.04 if (err) goto cleanup; EFstp.menufunc = m_stack_paint; EFstp.funcname = "stack_paint"; EFstp.mousefunc = STP_mousefunc; // 18.01 if (! edit_setup(EFstp)) goto cleanup; // setup edit (will lock) start_thread(STP_align_thread,0); // align each pair of images wrapup_thread(0); // wait for completion if (STP_stat != 1) goto cancel; STP_adjust(); // combine images based on user inputs if (STP_stat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } *paneltext = 0; return; } // align each image 2nd-last to 1st image // cimPXMf[*] original image // cimPXMs[*] scaled and color adjusted for pixel comparisons // cimPXMw[*] warped for display void * STP_align_thread(void *) { int imx, im1, im2, ww, hh, ii, nn; float R; Fzoom = 0; // fit to window if big Fblowup = 1; // scale up to window if small Ffuncbusy = 1; cimPano = cimPanoV = 0; // no pano mode for (imx = 1; imx < cimNF; imx++) // loop 2nd to last image { im1 = 0; // images to align im2 = imx; memset(&cimOffs[im1],0,sizeof(cimoffs)); // initial image offsets = 0 memset(&cimOffs[im2],0,sizeof(cimoffs)); ww = cimPXMf[im1]->ww; // image dimensions hh = cimPXMf[im1]->hh; nn = ww; // use larger of ww, hh if (hh > ww) nn = hh; cimScale = STP_initAlignSize / nn; // initial align image size if (cimScale > 1.0) cimScale = 1.0; cimBlend = 0; // no blend width (use all) cim_get_overlap(im1,im2,cimPXMf); // get overlap area cim_match_colors(im1,im2,cimPXMf); // get color matching factors cimSearchRange = STP_initSearchRange; // initial align search range cimSearchStep = STP_initSearchStep; // initial align search step cimWarpRange = STP_initWarpRange; // initial align corner warp range cimWarpStep = STP_initWarpStep; // initial align corner warp step cimSampSize = STP_sampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { cim_scale_image(im1,cimPXMs); // scale images to cimScale cim_scale_image(im2,cimPXMs); cim_adjust_colors(cimPXMs[im1],1); // apply color adjustments cim_adjust_colors(cimPXMs[im2],2); cim_warp_image(im1); // warp images for show cim_warp_image(im2); cim_get_overlap(im1,im2,cimPXMs); // get overlap area cim_get_redpix(im1); // get high-contrast pixels cimShowIm1 = im1; // show these two images cimShowIm2 = im2; // with 50/50 blend cimShowAll = 0; cim_show_images(1,0); // (x/y offsets can change) cim_align_image(im1,im2); // align im2 to im1 zfree(cimRedpix); // clear red pixels cimRedpix = 0; if (cimScale == 1.0) break; // done R = STP_imageIncrease; // next larger image size cimScale = cimScale * R; if (cimScale > 0.85) { // if close to end, jump to end R = R / cimScale; cimScale = 1.0; } cimOffs[im1].xf *= R; // scale offsets for larger image cimOffs[im1].yf *= R; cimOffs[im2].xf *= R; cimOffs[im2].yf *= R; for (ii = 0; ii < 4; ii++) { cimOffs[im1].wx[ii] *= R; cimOffs[im1].wy[ii] *= R; cimOffs[im2].wx[ii] *= R; cimOffs[im2].wy[ii] *= R; } cimSearchRange = STP_searchRange; // align search range cimSearchStep = STP_searchStep; // align search step size cimWarpRange = STP_warpRange; // align corner warp range cimWarpStep = STP_warpStep; // align corner warp step size } cimOffs[im2].xf = cimOffs[im2].xf - cimOffs[im1].xf; // save im2 offsets from im1 cimOffs[im2].yf = cimOffs[im2].yf - cimOffs[im1].yf; cimOffs[im2].tf = cimOffs[im2].tf - cimOffs[im1].tf; } memset(&cimOffs[0],0,sizeof(cimoffs)); // image 0, no offsets Fzoom = Fblowup = 0; Ffuncbusy = 0; STP_stat = 1; thread_exit(); return 0; // not executed } // dialog to selectively paint output image int STP_image; // current image (0 based) int STP_radius; // paint mode radius int STP_mode; // 1/2 = show/hide float STP_show_adjust; // contrast adjustment float STP_hide_adjust; // contrast adjustment void STP_adjust() { zdialog *zd; char imageN[8] = "imageN", labN[4] = "0"; int imx; int STP_adjust_dialog_event(zdialog *zd, cchar *event); /*** _________________________________________ | Select and Paint Image | | | | image (o) 1 (o) 2 (o) 3 ... | | paint radius [___] | | | | transient objects | | show (o) =========[]================= | | hide (o) ==============[]============ | | | | [Done] [Cancel] | |_________________________________________| ***/ zd = zdialog_new(ZTX("Select and Paint Image"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbim","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labim","hbim",Bimage,"space=5"); zdialog_add_widget(zd,"hbox","hbmr","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labr","hbmr",Bpaintradius,"space=5"); zdialog_add_widget(zd,"zspin","radius","hbmr","1|400|1|100"); zdialog_add_widget(zd,"hbox","hbtrob","dialog",0,"space=5"); zdialog_add_widget(zd,"label","labtob","hbtrob",ZTX("Transient Objects"),"space=5"); zdialog_add_widget(zd,"hbox","hbshow","dialog"); zdialog_add_widget(zd,"vbox","vbshow1","hbshow",0,"space=5"); zdialog_add_widget(zd,"vbox","vbshow2","hbshow",0,"expand"); zdialog_add_widget(zd,"radio","show","vbshow1",Bshow); zdialog_add_widget(zd,"radio","hide","vbshow1",Bhide); zdialog_add_widget(zd,"hscale","show-adjust","vbshow2","0.0|1.0|0.001|0.9","expand"); zdialog_add_widget(zd,"hscale","hide-adjust","vbshow2","0.0|1.0|0.001|0.9","expand"); for (imx = 0; imx < cimNF; imx++) { // add radio button for each image imageN[5] = '1' + imx; labN[0] = '1' + imx; zdialog_add_widget(zd,"radio",imageN,"hbim",labN); } zdialog_stuff(zd,"image1",1); // initial image = 1st STP_image = 0; // initial image STP_radius = 100; // paint radius zdialog_stuff(zd,"show",1); // initial mode, show zdialog_stuff(zd,"hide",0); STP_mode = 1; STP_show_adjust = 0.9; // contrast adjustments STP_hide_adjust = 0.9; takeMouse(STP_mousefunc,0); // connect mouse function start_thread(STP_adjust_thread,0); // start working thread signal_thread(); zdialog_resize(zd,250,0); zdialog_run(zd,STP_adjust_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); // wait for completion zdialog_free(zd); return; } // dialog event and completion callback function int STP_adjust_dialog_event(zdialog *zd, cchar *event) { int nn; if (zd->zstat) // dialog finish { freeMouse(); zdialog_destroy(zd); // prevent double [cancel] wrapup_thread(8); if (zd->zstat == 1) STP_stat = 1; else STP_stat = 0; if (STP_stat == 1) cim_trim(); // cut-off edges } if (strmatchN(event,"image",5)) { // image radio button nn = event[5] - '0'; // 1 to cimNF if (nn > 0 && nn <= cimNF) STP_image = nn - 1; // 0 to cimNF-1 } if (strmatch(event,"focus")) // toggle mouse capture takeMouse(STP_mousefunc,0); // connect mouse function if (strmatch(event,"radius")) // change paint radius zdialog_fetch(zd,"radius",STP_radius); if (strmatch(event,"show-adjust")) { // contrast adjustment zdialog_fetch(zd,"show-adjust",STP_show_adjust); signal_thread(); } if (strmatch(event,"hide-adjust")) { // contrast adjustment zdialog_fetch(zd,"hide-adjust",STP_hide_adjust); signal_thread(); } if (strstr("show hide",event)) { // set show/hide mode zdialog_fetch(zd,"show",nn); if (nn) STP_mode = 1; zdialog_fetch(zd,"hide",nn); if (nn) STP_mode = 2; signal_thread(); } return 1; } // STP dialog mouse function void STP_mousefunc() { int radius, rect, radius2; int mx, my, dx, dy, px3, py3; radius = STP_radius; // paintbrush radius radius2 = radius * radius; rect = 2 * radius; // draw mouse selection circle draw_mousecircle(Mxposn,Myposn,radius,0,0); if (LMclick || RMclick) // mouse click return; // handle zoom normally if (! Mxdrag && ! Mydrag) return; mx = Mxdrag; // mouse drag my = Mydrag; Mxdrag = Mydrag = 0; if (mx < 0 || mx > E3pxm->ww-1 || my < 0 || my > E3pxm->hh-1) // mouse outside image area return; for (dy = -radius; dy <= radius; dy++) // loop pixels around mouse for (dx = -radius; dx <= radius; dx++) { if (dx*dx + dy*dy > radius2) continue; // outside radius px3 = mx + dx; // output pixel py3 = my + dy; if (px3 < 0 || px3 > E3pxm->ww-1) continue; // outside image if (py3 < 0 || py3 > E3pxm->hh-1) continue; if (Mbutton == 1) // left button STP_setpixel(px3,py3,STP_image); // paint chosen input image if (Mbutton == 3) // right button, set background 17.04 STP_setpixel(px3,py3,-3); } Fpaint3(mx-radius,my-radius,rect,rect,0); // update window draw_mousecircle(mx,my,radius,0,0); // draw mouse selection circle return; } // show all transient objects on top of background void * STP_adjust_thread(void *) // 17.04 { void * STP_adjust_wthread(void *); STP_setpixel(0,0,-1); // initialization while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(STP_adjust_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fpaint2(); } return 0; } void * STP_adjust_wthread(void *arg) // worker thread { int index = *((int *) arg); int px3, py3; for (py3 = index+1; py3 < E3pxm->hh-1; py3 += NWT) // step through output pixels for (px3 = 1; px3 < E3pxm->ww-1; px3++) { if (STP_mode == 0) STP_setpixel(px3,py3,-2); // blend, all images equal if (STP_mode == 1) STP_setpixel(px3,py3,-4); // show, foreground + background if (STP_mode == 2) STP_setpixel(px3,py3,-3); // hide, background only } exit_wthread(); return 0; // not executed } // Set E3 image pixel for given pixel location and input source // source: -1 initialize: pre-calculate trig functions // -2 mix of all input images (blend) // -3 background only (median pixels) // -4 forground (outlier pixels) + background // 0 - cimNF-1 specific input image (paint) void STP_setpixel(int px3, int py3, int source) // 17.04 { int imx, vstat; int ii, ns, ns1, ns2; int Rlist[10], Glist[10], Blist[10]; float px, py, xoff, yoff; float *pix3, pix1[4], vpix[4]; float R, G, B, R2, G2, B2; float match, worstmatch; // sorted colors [N] --> low-hi range for central group // for 9 colors [xxxxxxxxx], the central group is ---xxx--- or range 3-5 from 0-8 // 0 1 2 3 4 5 6 7 8 9 10 int nsx[11][2] = { {0,0}, {0,0}, {0,0}, {1,1}, {1,2}, {2,2}, {2,3}, {3,3}, {3,4}, {4,4}, {4,5} }; static float sintf[10], costf[10]; pix3 = PXMpix(E3pxm,px3,py3); // output pixel if (source >= 0 && source < cimNF) // specific input image { imx = source; xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); // input virtual pixel py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (vstat) memcpy(pix3,vpix,pixcc); return; } if (source == -1) { // pre-calculate trig funcs for (imx = 0; imx < cimNF; imx++) { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } return; } if (source == -2) // blend all input images { R = G = B = 0; ns = 0; for (imx = 0; imx < cimNF; imx++) { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (vstat) { R += vpix[0]; G += vpix[1]; B += vpix[2]; ns++; } } pix3[0] = R / ns; pix3[1] = G / ns; pix3[2] = B / ns; return; } if (source == -3) // background only { if (cimNF < 3) return; for (imx = ns = 0; imx < cimNF; imx++) // get aligned input pixels { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (vstat) { Rlist[ns] = vpix[0]; // add pixel RGB values to list Glist[ns] = vpix[1]; Blist[ns] = vpix[2]; ns++; } } if (ns < 3) return; HeapSort(Rlist,ns); // sort RGB values HeapSort(Glist,ns); HeapSort(Blist,ns); R = G = B = 0; ns1 = nsx[ns][0]; // middle group of pixels ns2 = nsx[ns][1]; for (ii = ns1; ii <= ns2; ii++) { R += Rlist[ii]; G += Glist[ii]; B += Blist[ii]; } ns = ns2 - ns1 + 1; // sample count R = R / ns; // middle group average G = G / ns; B = B / ns; R2 = G2 = B2 = 0; ns2 = 0; for (ii = 0; ii < ns; ii++) // get pixels matching middle group { match = RGBMATCH(R,G,B,Rlist[ii],Glist[ii],Blist[ii]); if (match > STP_hide_adjust) { // match threshold R2 += Rlist[ii]; G2 += Glist[ii]; B2 += Blist[ii]; ns2++; } } pix3[0] = (R * ns + R2) / (ns + ns2); // mean background pixel pix3[1] = (G * ns + G2) / (ns + ns2); pix3[2] = (B * ns + B2) / (ns + ns2); return; } if (source == -4) // show foreground pixel { STP_setpixel(px3,py3,-3); // set pix3 to background worstmatch = 1.0; for (imx = 0; imx < cimNF; imx++) // loop aligned input pixels { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (! vstat) continue; match = PIXMATCH(vpix,pix3); // find worst match to background if (match < worstmatch) { worstmatch = match; memcpy(pix1,vpix,pixcc); } } if (worstmatch < STP_show_adjust) memcpy(pix3,pix1,pixcc); return; } } /******************************************************************************** Stack/Noise function Combine multiple photos of the same subject and average the pixels for noise reduction. ********************************************************************************/ float STN_initAlignSize = 160; // initial align image size float STN_imageIncrease = 1.6; // image size increase per align cycle float STN_sampSize = 10000; // pixel sample size float STN_initSearchRange = 5.0; // initial search range, +/- pixels float STN_initSearchStep = 1.0; // initial search step, pixels float STN_searchRange = 2.0; // normal search range float STN_searchStep = 1.0; // normal search step float STN_initWarpRange = 2.0; // initial corner warp range float STN_initWarpStep = 1.0; // initial corner warp step float STN_warpRange = 1.0; // normal corner warp range float STN_warpStep = 0.67; // normal corner warp step int STN_stat; // 1 = OK, 0 = failed or canceled int STN_average = 1, STN_median = 0; // use average/median of input pixels int STN_exlow = 0, STN_exhigh = 0; // exclude low/high pixel void * STN_align_thread(void *); void STN_adjust(); void * STN_adjust_thread(void *); editfunc EFstn; // edit function data // menu function void m_stack_noise(GtkWidget *, cchar *) { int imx, err, ww, hh; float diffw, diffh; F1_help_topic = "stack_noise"; // help topic if (checkpend("all")) return; cim_manualwarp = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 9) { zmessageACK(Mwin,ZTX("Select 2 to 9 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files ww = cimPXMf[0]->ww; hh = cimPXMf[0]->hh; for (imx = 1; imx < cimNF; imx++) // check image compatibility { diffw = abs(ww - cimPXMf[imx]->ww); diffw = diffw / ww; diffh = abs(hh - cimPXMf[imx]->hh); diffh = diffh / hh; if (diffw > 0.02 || diffh > 0.02) { zmessageACK(Mwin,ZTX("Images are not all the same size")); goto cleanup; } } free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last 17.04 if (err) goto cleanup; EFstn.menufunc = m_stack_noise; EFstn.funcname = "stack_noise"; if (! edit_setup(EFstn)) goto cleanup; // setup edit (will lock) start_thread(STN_align_thread,0); // align each pair of images wrapup_thread(0); // wait for completion if (STN_stat != 1) goto cancel; STN_adjust(); // combine images based on user inputs if (STN_stat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } *paneltext = 0; return; } // align each image 2nd-last to 1st image // cimPXMf[*] original image // cimPXMs[*] scaled and color adjusted for pixel comparisons // cimPXMw[*] warped for display void * STN_align_thread(void *) { int imx, im1, im2, ww, hh, ii, nn; float R; Fzoom = 0; // fit to window if big Fblowup = 1; // scale up to window if small Ffuncbusy = 1; cimPano = cimPanoV = 0; // no pano mode for (imx = 1; imx < cimNF; imx++) // loop 2nd to last image { im1 = 0; // images to align im2 = imx; memset(&cimOffs[im1],0,sizeof(cimoffs)); // initial image offsets = 0 memset(&cimOffs[im2],0,sizeof(cimoffs)); ww = cimPXMf[im1]->ww; // image dimensions hh = cimPXMf[im1]->hh; nn = ww; // use larger of ww, hh if (hh > ww) nn = hh; cimScale = STN_initAlignSize / nn; // initial align image size if (cimScale > 1.0) cimScale = 1.0; cimBlend = 0; // no blend width (use all) cim_get_overlap(im1,im2,cimPXMf); // get overlap area cim_match_colors(im1,im2,cimPXMf); // get color matching factors cimSearchRange = STN_initSearchRange; // initial align search range cimSearchStep = STN_initSearchStep; // initial align search step cimWarpRange = STN_initWarpRange; // initial align corner warp range cimWarpStep = STN_initWarpStep; // initial align corner warp step cimSampSize = STN_sampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { cim_scale_image(im1,cimPXMs); // scale images to cimScale cim_scale_image(im2,cimPXMs); cim_adjust_colors(cimPXMs[im1],1); // apply color adjustments cim_adjust_colors(cimPXMs[im2],2); cim_warp_image(im1); // warp images for show cim_warp_image(im2); cim_get_overlap(im1,im2,cimPXMs); // get overlap area cim_get_redpix(im1); // get high-contrast pixels cimShowIm1 = im1; // show these two images cimShowIm2 = im2; // with 50/50 blend cimShowAll = 0; cim_show_images(1,0); // (x/y offsets can change) cim_align_image(im1,im2); // align im2 to im1 zfree(cimRedpix); // clear red pixels cimRedpix = 0; if (cimScale == 1.0) break; // done R = STN_imageIncrease; // next larger image size cimScale = cimScale * R; if (cimScale > 0.85) { // if close to end, jump to end R = R / cimScale; cimScale = 1.0; } cimOffs[im1].xf *= R; // scale offsets for larger image cimOffs[im1].yf *= R; cimOffs[im2].xf *= R; cimOffs[im2].yf *= R; for (ii = 0; ii < 4; ii++) { cimOffs[im1].wx[ii] *= R; cimOffs[im1].wy[ii] *= R; cimOffs[im2].wx[ii] *= R; cimOffs[im2].wy[ii] *= R; } cimSearchRange = STN_searchRange; // align search range cimSearchStep = STN_searchStep; // align search step size cimWarpRange = STN_warpRange; // align corner warp range cimWarpStep = STN_warpStep; // align corner warp step size } cimOffs[im2].xf = cimOffs[im2].xf - cimOffs[im1].xf; // save im2 offsets from im1 cimOffs[im2].yf = cimOffs[im2].yf - cimOffs[im1].yf; cimOffs[im2].tf = cimOffs[im2].tf - cimOffs[im1].tf; } memset(&cimOffs[0],0,sizeof(cimoffs)); Fzoom = Fblowup = 0; Ffuncbusy = 0; STN_stat = 1; thread_exit(); return 0; // not executed } // change pixel combination according to user input void STN_adjust() { zdialog *zd; int STN_adjust_dialog_event(zdialog *zd, cchar *event); /*** _________________________________________ | Adjust Pixel Composition | | | | (o) use average (o) use median | | [x] omit low pixel [x] omit high pixel | | | | [Done] [Cancel] | |_________________________________________| ***/ zd = zdialog_new(ZTX("Adjust Pixel Composition"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","average","hb1",ZTX("use average"),"space=3"); zdialog_add_widget(zd,"radio","median","hb1",ZTX("use median"),"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"check","exlow","hb2",ZTX("omit low pixel"),"space=3"); zdialog_add_widget(zd,"check","exhigh","hb2",ZTX("omit high pixel"),"space=3"); zdialog_stuff(zd,"average",1); // default = average zdialog_stuff(zd,"median",0); zdialog_stuff(zd,"exlow",0); zdialog_stuff(zd,"exhigh",0); STN_average = 1; STN_median = 0; STN_exlow = 0; STN_exhigh = 0; start_thread(STN_adjust_thread,0); // start working thread signal_thread(); zdialog_resize(zd,250,0); zdialog_run(zd,STN_adjust_dialog_event,"save"); // run dialog, parallel zdialog_wait(zd); // wait for completion zdialog_free(zd); return; } // dialog event and completion callback function int STN_adjust_dialog_event(zdialog *zd, cchar *event) { if (zd->zstat) { // dialog finish zdialog_destroy(zd); // prevent double [cancel] if (zd->zstat == 1) STN_stat = 1; else STN_stat = 0; wrapup_thread(8); if (STN_stat == 1) cim_trim(); // trim edges } if (strmatch(event,"average")) { zdialog_fetch(zd,"average",STN_average); signal_thread(); } if (strmatch(event,"median")) { zdialog_fetch(zd,"median",STN_median); signal_thread(); } if (strmatch(event,"exlow")) { zdialog_fetch(zd,"exlow",STN_exlow); signal_thread(); } if (strmatch(event,"exhigh")) { zdialog_fetch(zd,"exhigh",STN_exhigh); signal_thread(); } return 1; } // compute mean/median mix for each output pixel and update E3 image void * STN_adjust_thread(void *) { void * STN_adjust_wthread(void *arg); // worker thread while (true) { thread_idle_loop(); // wait for work or exit request for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(STN_adjust_wthread,&Nval[ii]); wait_wthreads(); // wait for completion Fpaint2(); // update window } return 0; // not executed } // worker thread void * STN_adjust_wthread(void *arg) { int index = *((int *) arg); int imx, vstat, px3, py3; float red, green, blue; // float int ii, ns, ns1, ns2; int Rlist[10], Glist[10], Blist[10]; float px, py; float xoff, yoff, sintf[10], costf[10]; float *pix3, vpix[4]; // sorted colors [N] --> low-hi range for median group // 0 1 2 3 4 5 6 7 8 9 10 int nsx[11][2] = { {0,0}, {0,0}, {0,1}, {1,1}, {1,2}, {2,2}, {2,3}, {3,3}, {3,4}, {4,4}, {4,5} }; for (imx = 0; imx < cimNF; imx++) // pre-calculate trig funcs { sintf[imx] = sinf(cimOffs[imx].tf); costf[imx] = cosf(cimOffs[imx].tf); } for (py3 = index+1; py3 < E3pxm->hh-1; py3 += NWT) // step through output pixels for (px3 = 1; px3 < E3pxm->ww-1; px3++) { for (imx = ns = 0; imx < cimNF; imx++) // get aligned input pixels { xoff = cimOffs[imx].xf; yoff = cimOffs[imx].yf; px = costf[imx] * (px3 - xoff) + sintf[imx] * (py3 - yoff); py = costf[imx] * (py3 - yoff) - sintf[imx] * (px3 - xoff); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (vstat) { Rlist[ns] = vpix[0]; // add pixel RGB values to list Glist[ns] = vpix[1]; Blist[ns] = vpix[2]; ns++; } } if (! ns) continue; if (STN_exlow || STN_exhigh || STN_median) { // RGB values must be sorted HeapSort(Rlist,ns); HeapSort(Glist,ns); HeapSort(Blist,ns); } red = green = blue = 0; if (STN_average) // average the input pixels { ns1 = 0; // low and high RGB values ns2 = ns - 1; if (STN_exlow) { // exclude low ns1++; if (ns1 > ns2) ns1--; } if (STN_exhigh) { // exclude high ns2--; if (ns1 > ns2) ns2++; } for (ii = ns1; ii <= ns2; ii++) // sum remaining RGB levels { red += Rlist[ii]; green += Glist[ii]; blue += Blist[ii]; } ns = ns2 - ns1 + 1; // sample count red = red / ns; // output RGB = average green = green / ns; blue = blue / ns; } if (STN_median) // use median input pixels { ns1 = nsx[ns][0]; // middle group of pixels ns2 = nsx[ns][1]; for (ii = ns1; ii <= ns2; ii++) { red += Rlist[ii]; green += Glist[ii]; blue += Blist[ii]; } ns = ns2 - ns1 + 1; // sample count red = red / ns; // output RGB = average green = green / ns; blue = blue / ns; } pix3 = PXMpix(E3pxm,px3,py3); // output pixel pix3[0] = red; pix3[1] = green; pix3[2] = blue; } exit_wthread(); return 0; // not executed } /******************************************************************************** Panorama function: join 2, 3, or 4 images horizontally *********************************************************************************/ int panStat; // 1 = OK zdialog *panozd = 0; // pre-align dialog float panPreAlignSize = 1000; // pre-align image size (ww) float panInitAlignSize = 200; // initial align image size float panImageIncrease = 1.6; // image size increase per align cycle float panSampSize = 50000; // pixel sample size float panPreAlignBlend = 0.20; // pre-align blend width * ww float panInitBlend = 0.20; // initial auto-align blend width float panFinalBlend = 0.10; // final blend width * ww 18.01 float panBlendDecrease = 0.7; // BW reduction per align cycle float panInitSearchRange = 15.0; // initial search range, +/- pixels 18.01 float panInitSearchStep = 1.0; // initial search step, pixels float panSearchRange = 4.0; // normal search range, +/- pixels float panSearchStep = 1.0; // normal search step, pixels float panInitWarpRange = 15.0; // initial corner warp range, +/- pixels float panInitWarpStep = 1.0; // initial corner warp step, pixels float panWarpRange = 8.0; // normal corner warp range, +/- pixels float panWarpStep = 1.0; // normal corner warp step, pixels float pano_adjust_RGB[4][3]; // user color adjustments per image int pano_mousewarp = 0; // use mouse to warp/align images int pano_currimage = 0; // curr. image for color/warp adjustments float *panowarpx[4], *panowarpy[4]; // warp memory, pixel displacements PXM *cimPXMwdup[4]; // copies of images before mouse warps PXM *preflatPXM; // copy of E3pxm before flatten void pano_prealign(); // manual pre-align void pano_align(); // auto fine-align void pano_adjust(); // user color adjust void panowarp_mousefunc(); // image warp mouse function void panowarp_dowarps(int imx); editfunc EFpano; // edit function data // menu function void m_pano_horz(GtkWidget *, cchar *) { int imx, err; F1_help_topic = "panorama"; // help topic if (checkpend("all")) return; cim_manualwarp = 0; pano_mousewarp = 0; cim_manualalign = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 4) { zmessageACK(Mwin,ZTX("Select 2 to 4 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last 17.04 if (err) goto cleanup; EFpano.menufunc = m_pano_horz; EFpano.funcname = "pano"; EFpano.mousefunc = panowarp_mousefunc; // 18.01 if (! edit_setup(EFpano)) goto cleanup; // setup edit (will lock) cimShowAll = 1; // for cim_show_images(), show all cimPano = 1; // horizontal pano mode cimPanoV = 0; pano_prealign(); // manual pre-alignment if (panStat != 1) goto cancel; pano_align(); // auto full alignment if (panStat != 1) goto cancel; pano_adjust(); // manual color adjustment if (panStat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: // failed or canceled edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } *paneltext = 0; return; } // perform manual pre-align of all images // returns alignment data in cimOffs[*] // lens_mm may also be altered void pano_prealign() { int pano_prealign_event(zdialog *zd, cchar *event); // dialog event function void * pano_prealign_thread(void *); // working thread int imx, ww, err = 0; cchar *exifkey[1] = { exif_focal_length_key }; cchar *lens_source; char *pp[1] = { 0 }; float temp; cchar *align_mess = ZTX("Drag images into rough alignment.\n" "To rotate, drag from lower edge."); cchar *scan_mess = ZTX("no curve (scanned image)"); cchar *search_mess = ZTX("Search for lens mm"); cchar *save_mess = ZTX("Save lens mm → image EXIF"); err = 1; lens_source = "NO EXIF"; exif_get(curr_file,exifkey,pp,1); // get lens mm from EXIF if available if (pp[0]) err = convSF(pp[0], temp, 20, 1000); if (! err) { lens_mm = temp; lens_source = "(EXIF)"; } for (imx = 0; imx < 10; imx++) // set all alignment offsets = 0 memset(&cimOffs[imx],0,sizeof(cimoffs)); for (imx = ww = 0; imx < cimNF; imx++) // sum image widths ww += cimPXMf[imx]->ww; cimScale = 1.4 * panPreAlignSize / ww; // set alignment image scale if (cimScale > 1.0) cimScale = 1.0; // (* 0.7 after overlaps) for (imx = 0; imx < cimNF; imx++) // scale images > cimPXMs[*] cim_scale_image(imx,cimPXMs); for (imx = 0; imx < cimNF; imx++) { // curve images, cimPXMs[*] replaced cim_curve_image(imx); cimPXMw[imx] = PXM_copy(cimPXMs[imx]); // copy to cimPXMw[*] for display } cimOffs[0].xf = cimOffs[0].yf = 0; // first image at (0,0) for (imx = 1; imx < cimNF; imx++) // position images with 30% overlap { // in horizontal row cimOffs[imx].xf = cimOffs[imx-1].xf + 0.7 * cimPXMw[imx-1]->ww; cimOffs[imx].yf = cimOffs[imx-1].yf; } Fzoom = 0; // scale image to fit window Fblowup = 1; // magnify small image to window size cimBlend = panPreAlignBlend * cimPXMw[1]->ww; // overlap in align window cim_show_images(1,0); // combine and show images in main window /* _________________________________________ | Pre-align Images | | | | Drag images into rough alignment. | | To rotate, drag from lower edge. | | | | [ 35.5 ] lens mm (EXIF) | | [x] no curve (scanned image) | | [x] no auto warp | | [x] manual align | | [Resize] resize window | | [Search] Search for lens mm | | [Save] Save lens mm -> image EXIF | | | | [Proceed] [Cancel] | |_________________________________________| */ panozd = zdialog_new(ZTX("Pre-align Images"),Mwin,Bproceed,Bcancel,null); // start pre-align dialog zdialog_add_widget(panozd,"label","lab1","dialog",align_mess,"space=3"); zdialog_add_widget(panozd,"hbox","hb1","dialog",0); zdialog_add_widget(panozd,"zspin","spmm","hb1","20|999|0.1|35","space=5"); zdialog_add_widget(panozd,"label","labmm","hb1",ZTX("lens mm")); zdialog_add_widget(panozd,"label","labsorc","hb1","","space=5"); zdialog_add_widget(panozd,"hbox","hbnc","dialog"); zdialog_add_widget(panozd,"check","nocurve","hbnc",scan_mess,"space=5"); zdialog_add_widget(panozd,"hbox","hbmw","dialog"); zdialog_add_widget(panozd,"check","manwarp","hbmw",ZTX("no auto warp"),"space=5"); zdialog_add_widget(panozd,"hbox","hbma","dialog"); zdialog_add_widget(panozd,"check","manalign","hbma",ZTX("manual align"),"space=5"); zdialog_add_widget(panozd,"hbox","hb5","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","resize","hb5",ZTX("Resize"),"space=5"); zdialog_add_widget(panozd,"label","labsiz","hb5",ZTX("resize window"),"space=5"); zdialog_add_widget(panozd,"hbox","hb6","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","search","hb6",Bsearch,"space=5"); zdialog_add_widget(panozd,"label","labsearch","hb6",search_mess,"space=5"); zdialog_add_widget(panozd,"hbox","hb7","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","save","hb7",Bsave,"space=5"); zdialog_add_widget(panozd,"label","labsave","hb7",save_mess,"space=5"); zdialog_add_ttip(panozd,"manwarp",ZTX("do not warp images during auto-alignment")); zdialog_stuff(panozd,"spmm",lens_mm); // stuff lens data zdialog_stuff(panozd,"labsorc",lens_source); // show source of lens data zdialog_stuff(panozd,"nocurve",cimPanoNC); zdialog_stuff(panozd,"manwarp",0); panStat = -1; // busy status gdk_window_set_cursor(gdkwin,dragcursor); // set drag cursor zdialog_run(panozd,pano_prealign_event,"save"); // start dialog start_thread(pano_prealign_thread,0); // start working thread zdialog_wait(panozd); // wait for dialog completion zdialog_free(panozd); // free dialog gdk_window_set_cursor(gdkwin,0); // restore normal cursor Fzoom = Fblowup = 0; return; } // pre-align dialog event function int pano_prealign_event(zdialog *zd, cchar *event) { int imx; float overlap; cchar *exifkey[1] = { exif_focal_length_key }; cchar *exifdata[1]; char lensdata[8]; if (strmatch(event,"spmm")) zdialog_fetch(zd,"spmm",lens_mm); // get revised lens data if (strmatch(event,"manwarp")) // get auto/manual warp option zdialog_fetch(zd,"manwarp",cim_manualwarp); if (strmatch(event,"manalign")) // get auto/manual align option zdialog_fetch(zd,"manalign",cim_manualalign); if (strmatch(event,"nocurve")) zdialog_fetch(zd,"nocurve",cimPanoNC); // get "no-curve" option if (strmatch(event,"resize")) // allocate new E3 image cim_show_images(1,0); if (strmatch(event,"search")) { // search for optimal lens parms if (cimNF != 2) zmessageACK(Mwin,ZTX("use two images only")); else panStat = 2; // tell thread to search return 0; } if (strmatch(event,"save")) { // put lens data into dialog zdialog_stuff(zd,"spmm",lens_mm); snprintf(lensdata,8,"%d",int(lens_mm)); exifdata[0] = lensdata; for (imx = 0; imx < cimNF; imx++) // save lens mm in EXIF data exif_put(cimFile[imx],exifkey,exifdata,1); } if (zd->zstat) // dialog complete { if (zd->zstat == 1) // proceed panStat = 1; else // cancel or other panStat = 0; wrapup_thread(0); // wait for thread if (! panStat) return 0; // canceled for (imx = 0; imx < cimNF-1; imx++) // check for enough overlap { overlap = cim_get_overlap(imx,imx+1,cimPXMs); if (overlap < panFinalBlend) { zmessageACK(Mwin,ZTX("Too little overlap, cannot align")); panStat = 0; return 0; } } } return 0; } // pre-align working thread // convert mouse and KB events into image movements void * pano_prealign_thread(void *) { void pano_autolens(); cimoffs offstemp; PXM *pxmtemp; char *ftemp; int im1, im2, imm, imx; int mx0, my0, mx, my; // mouse drag origin, position int xoff, yoff, lox, hix; int sepx, minsep; int ww, hh, rotate, midx; int fchange, nc0; float lens_mm0; float dx, dy, t1, t2, dt; imm = ww = hh = rotate = xoff = yoff = 0; // stop compiler warnings lens_mm0 = lens_mm; // to detect changes nc0 = cimPanoNC; mx0 = my0 = 0; // no drag in progress Mcapture = KBcapture = 1; // capture mouse drag and KB keys cimBlend = 0; // full blend during pre-align while (true) // loop and align until done { zsleep(0.02); // logic simplified if (panStat == 2) { // dialog search button panStat = -1; // back to busy status pano_autolens(); } if (panStat != -1) break; // quit signal from dialog fchange = 0; if (lens_mm != lens_mm0) // change in lens parameter fchange = 1; if (cimPanoNC != nc0) fchange = 1; // change in "no-curve" option if (fchange) { lens_mm0 = lens_mm; nc0 = cimPanoNC; for (imx = 0; imx < cimNF; imx++) { // re-curve images cim_scale_image(imx,cimPXMs); cim_curve_image(imx); PXM_free(cimPXMw[imx]); cimPXMw[imx] = PXM_copy(cimPXMs[imx]); } cim_show_images(1,0); // combine and show images continue; } if (KBkey) { // KB input if (KBkey == GDK_KEY_Left) cimOffs[imm].xf -= 0.5; // adjust alignment offsets if (KBkey == GDK_KEY_Right) cimOffs[imm].xf += 0.5; if (KBkey == GDK_KEY_Up) cimOffs[imm].yf -= 0.5; if (KBkey == GDK_KEY_Down) cimOffs[imm].yf += 0.5; if (KBkey == GDK_KEY_r) cimOffs[imm].tf += 0.0005; if (KBkey == GDK_KEY_l) cimOffs[imm].tf -= 0.0005; KBkey = 0; cim_show_images(0,0); // combine and show images continue; } if (! Mxdrag && ! Mydrag) // no drag underway mx0 = my0 = 0; // reset drag origin if ((Mxdrag || Mydrag) && ! Fmousemain) // mouse drag underway { mx = Mxdrag; // mouse position in image my = Mydrag; if (! mx0 && ! my0) // new drag { mx0 = mx; // set drag origin my0 = my; minsep = 9999; for (imx = 0; imx < cimNF; imx++) // find image with midpoint { // closest to mouse x lox = cimOffs[imx].xf; hix = lox + cimPXMw[imx]->ww; midx = (lox + hix) / 2; sepx = abs(midx - mx0); if (sepx < minsep) { minsep = sepx; imm = imx; // image to drag or rotate } } xoff = cimOffs[imm].xf; yoff = cimOffs[imm].yf; ww = cimPXMw[imm]->ww; hh = cimPXMw[imm]->hh; rotate = 0; // if drag at bottom edge, if (my0 > yoff + 0.85 * hh) rotate = 1; // set rotate flag } if (mx != mx0 || my != my0) // drag is progressing { dx = mx - mx0; // mouse movement dy = my - my0; if (rotate && my0 > yoff && my > yoff) // rotation { if (imm > 0) { lox = cimOffs[imm].xf; // if there is an image to the left, hix = cimOffs[imm-1].xf + cimPXMw[imm-1]->ww; // midx = midpoint of overlap midx = (lox + hix) / 2; } else midx = 0; // this is the leftmost image t1 = atan(1.0 * (mx0-xoff) / (my0-yoff)); t2 = atan(1.0 * (mx-xoff) / (my-yoff)); dt = t1 - t2; // angle change dx = dt * (hh/2 + yoff); // pivot = middle of overlap on left dy = -dt * (midx-xoff); } else dt = 0; // x/y drag cimOffs[imm].xf += dx; // update image cimOffs[imm].yf += dy; cimOffs[imm].tf += dt; xoff = cimOffs[imm].xf; yoff = cimOffs[imm].yf; mx0 = mx; // next drag origin = current mouse my0 = my; cim_show_images(0,0); // show combined images } } for (im1 = 0; im1 < cimNF-1; im1++) // track image order changes { im2 = im1 + 1; if (cimOffs[im2].xf < cimOffs[im1].xf) { ftemp = cimFile[im2]; // switch filespecs cimFile[im2] = cimFile[im1]; cimFile[im1] = ftemp; pxmtemp = cimPXMf[im2]; // switch images cimPXMf[im2] = cimPXMf[im1]; cimPXMf[im1] = pxmtemp; pxmtemp = cimPXMs[im2]; // scaled images cimPXMs[im2] = cimPXMs[im1]; cimPXMs[im1] = pxmtemp; pxmtemp = cimPXMw[im2]; // warped images cimPXMw[im2] = cimPXMw[im1]; cimPXMw[im1] = pxmtemp; offstemp = cimOffs[im2]; // offsets cimOffs[im2] = cimOffs[im1]; cimOffs[im1] = offstemp; if (imm == im1) imm = im2; // current drag image else if (imm == im2) imm = im1; break; } } } KBcapture = Mcapture = 0; thread_exit(); return 0; // not executed, stop g++ warning } // optimize lens parameters // inputs and outputs: // pre-aligned images cimPXMw[0] and [1] // offsets in cimOffs[0] and [1] // lens_mm void pano_autolens() { float mm_range, xf_range, yf_range, tf_range; float squeeze, xf_rfinal, rnum, matchB, matchlev; float overlap, lens_mmB; int imx, randcount = 0; cimoffs offsetsB; overlap = cim_get_overlap(0,1,cimPXMs); if (overlap < 0.1) { zmessageACK(Mwin,ZTX("Too little overlap, cannot align")); return; } Ffuncbusy = 1; cimSampSize = 5000; cimNsearch = 0; mm_range = 0.1 * lens_mm; // set initial search ranges xf_range = 7; yf_range = 7; tf_range = 0.01; xf_rfinal = 0.3; // final xf range - when to quit cim_match_colors(0,1,cimPXMw); // adjust colors for image matching cim_adjust_colors(cimPXMs[0],1); cim_adjust_colors(cimPXMw[0],1); cim_adjust_colors(cimPXMs[1],2); cim_adjust_colors(cimPXMw[1],2); lens_mmB = lens_mm; // starting point offsetsB = cimOffs[1]; cimSearchRange = 7; matchB = 0; while (true) { srand48(time(0) + randcount++); lens_mm = lens_mmB + mm_range * (drand48() - 0.5); // new random lens factor for (imx = 0; imx <= 1; imx++) { // re-curve images cim_scale_image(imx,cimPXMs); cim_curve_image(imx); PXM_free(cimPXMw[imx]); cimPXMw[imx] = PXM_copy(cimPXMs[imx]); } cim_get_redpix(0); // get high-contrast pixels cim_show_images(0,0); // combine and show images squeeze = 0.97; // search range reduction for (int ii = 0; ii < 1000; ii++) // loop random x/y/t alignments { rnum = drand48(); if (rnum < 0.33) // random change some alignment offset cimOffs[1].xf = offsetsB.xf + xf_range * (drand48() - 0.5); else if (rnum < 0.67) cimOffs[1].yf = offsetsB.yf + yf_range * (drand48() - 0.5); else cimOffs[1].tf = offsetsB.tf + tf_range * (drand48() - 0.5); matchlev = cim_match_images(0,1); // test quality of image alignment snprintf(paneltext,200,"align: %d match: %.5f lens: %.1f", ++cimNsearch, matchB, lens_mmB); if (sigdiff(matchlev,matchB,0.00001) > 0) { matchB = matchlev; // save new best fit lens_mmB = lens_mm; // alignment is better offsetsB = cimOffs[1]; cim_show_images(0,0); squeeze = 1; // keep same search range as long break; // as improvements are found } if (panStat != -1) goto done; // user kill zmainloop(); } if (xf_range < xf_rfinal) goto done; // finished snprintf(paneltext,200,"align: %d match: %.5f lens: %.1f", cimNsearch, matchB, lens_mmB); mm_range = squeeze * mm_range; // reduce search range if no if (mm_range < 0.02 * lens_mmB) mm_range = 0.02 * lens_mmB; // improvements were found xf_range = squeeze * xf_range; yf_range = squeeze * yf_range; tf_range = squeeze * tf_range; zmainloop(); } done: zfree(cimRedpix); cimRedpix = 0; lens_mm = lens_mmB; // save best lens param found cimSampSize = panSampSize; // restore Ffuncbusy = 0; cim_show_images(1,0); // images are left color-matched return; } // fine-alignment // start with very small image size // search around offset values for best match // increase image size and loop until full-size void pano_align() { int imx, im1, im2, ww; float R, dx, dy, dt; float overlap; cimoffs offsets0; Fzoom = 0; // scale E3 to fit window Fblowup = 1; // magnify small image to window size Ffuncbusy = 1; for (imx = 0; imx < cimNF; imx++) { cimOffs[imx].xf = cimOffs[imx].xf / cimScale; // scale x/y offsets for full-size images cimOffs[imx].yf = cimOffs[imx].yf / cimScale; } cimScale = 1.0; // full-size for (imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMs[imx]); cimPXMs[imx] = PXM_copy(cimPXMf[imx]); // copy full-size images cim_curve_image(imx); // curve them } cimBlend = 0.3 * cimPXMs[0]->ww; cim_get_overlap(0,1,cimPXMs); // match images 0 & 1 in overlap area cim_match_colors(0,1,cimPXMs); cim_adjust_colors(cimPXMf[0],1); // image 0 << profile 1 cim_adjust_colors(cimPXMf[1],2); // image 1 << profile 2 if (cimNF > 2) { cimBlend = 0.3 * cimPXMs[1]->ww; cim_get_overlap(1,2,cimPXMs); cim_match_colors(1,2,cimPXMs); cim_adjust_colors(cimPXMf[0],1); cim_adjust_colors(cimPXMf[1],1); cim_adjust_colors(cimPXMf[2],2); } if (cimNF > 3) { cimBlend = 0.3 * cimPXMs[2]->ww; cim_get_overlap(2,3,cimPXMs); cim_match_colors(2,3,cimPXMs); cim_adjust_colors(cimPXMf[0],1); cim_adjust_colors(cimPXMf[1],1); cim_adjust_colors(cimPXMf[2],1); cim_adjust_colors(cimPXMf[3],2); } cimScale = panInitAlignSize / cimPXMf[1]->hh; // initial align image scale if (cimScale > 1.0) cimScale = 1.0; if (cim_manualalign) cimScale = 1.0; // manual alignment, full size for (imx = 0; imx < cimNF; imx++) { // scale offsets for image scale cimOffs[imx].xf = cimOffs[imx].xf * cimScale; cimOffs[imx].yf = cimOffs[imx].yf * cimScale; } cimSearchRange = panInitSearchRange; // initial align search range cimSearchStep = panInitSearchStep; // initial align search step cimWarpRange = panInitWarpRange; // initial align corner warp range cimWarpStep = panInitWarpStep; // initial align corner warp step ww = cimPXMf[0]->ww * cimScale; // initial align image width cimBlend = ww * panInitBlend; // initial align blend width cimSampSize = panSampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { for (imx = 0; imx < cimNF; imx++) { // prepare images cim_scale_image(imx,cimPXMs); // scale to new size cim_curve_image(imx); // curve based on lens params cim_warp_image_pano(imx,1); // apply corner warps } cim_show_images(1,0); // show with 50/50 blend in overlaps for (im1 = 0; im1 < cimNF-1; im1++) // fine-align each image with left neighbor { im2 = im1 + 1; offsets0 = cimOffs[im2]; // save initial alignment offsets overlap = cim_get_overlap(im1,im2,cimPXMs); // get overlap area if (overlap < panFinalBlend-2) { zmessageACK(Mwin,ZTX("Too little overlap, cannot align")); goto fail; } if (cim_manualalign) cim_show_images(0,0); else { cim_get_redpix(im1); // get high-contrast pixels cim_show_images(0,0); // show with 50/50 blend in overlaps cim_align_image(im1,im2); // search for best offsets and warps zfree(cimRedpix); // clear red pixels cimRedpix = 0; } dx = cimOffs[im2].xf - offsets0.xf; // changes from initial offsets dy = cimOffs[im2].yf - offsets0.yf; dt = cimOffs[im2].tf - offsets0.tf; for (imx = im2+1; imx < cimNF; imx++) // propagate to following images { cimOffs[imx].xf += dx; cimOffs[imx].yf += dy; cimOffs[imx].tf += dt; ww = cimOffs[imx].xf - cimOffs[im2].xf; cimOffs[imx].yf += ww * dt; } } if (cimScale == 1.0) goto success; // done R = panImageIncrease; // next larger image size cimScale = cimScale * R; if (cimScale > 0.85) { // if close to end, jump to end R = R / cimScale; cimScale = 1.0; } for (imx = 0; imx < cimNF; imx++) // scale offsets for new size { cimOffs[imx].xf *= R; cimOffs[imx].yf *= R; for (int ii = 0; ii < 4; ii++) { cimOffs[imx].wx[ii] *= R; cimOffs[imx].wy[ii] *= R; } } cimSearchRange = panSearchRange; // align search range cimSearchStep = panSearchStep; // align search step size cimWarpRange = panWarpRange; // align corner warp range cimWarpStep = panWarpStep; // align corner warp step size cimBlend = cimBlend * panBlendDecrease * R; // blend width, reduced ww = cimPXMf[0]->ww * cimScale; if (cimBlend < panFinalBlend * ww) cimBlend = panFinalBlend * ww; // stay above minimum } success: panStat = 1; goto align_done; fail: panStat = 0; align_done: cimBlend = 1; // tiny blend (increase in adjust) Fzoom = Fblowup = 0; Ffuncbusy = 0; cim_show_images(0,0); return; } // get user inputs for RGB changes and blend width, update cimPXMw[*] void pano_adjust() { int pano_adjust_event(zdialog *zd, cchar *event); // dialog event function cchar *adjusttitle = ZTX("Match Brightness and Color"); char imageN[8] = "imageN"; int ww, hh, cc, imx; for (imx = 0; imx < cimNF; imx++) { // neutral color adjustments pano_adjust_RGB[imx][0] = 100; pano_adjust_RGB[imx][1] = 100; pano_adjust_RGB[imx][2] = 100; } for (imx = 0; imx < cimNF; imx++) { // allocate warp memory ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; cc = ww * hh * sizeof(float); panowarpx[imx] = (float *) zmalloc(cc); panowarpy[imx] = (float *) zmalloc(cc); memset(panowarpx[imx],0,cc); memset(panowarpy[imx],0,cc); cimPXMwdup[imx] = PXM_copy(cimPXMw[imx]); } cimBlend = 1; // init. blend width /*** ___________________________________ | Match Brightness and Color | | | | Select image (o) (o) (o) (o) | | | | red green blue | | [_____] [_____] [_____] | | brightness [____] [apply] | | [auto color] [file color] | | blend width [____] [apply] | | [x] mouse warp | | [flatten] curved image | | flatten image [___] [apply] | | | | [done] [cancel] | |___________________________________| ***/ panozd = zdialog_new(adjusttitle,Mwin,Bdone,Bcancel,null); zdialog_add_widget(panozd,"hbox","hbim","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labim","hbim",ZTX("Select image"),"space=5"); zdialog_add_widget(panozd,"hbox","hbc1","dialog",0,"homog"); zdialog_add_widget(panozd,"label","labred","hbc1",Bred); zdialog_add_widget(panozd,"label","labgreen","hbc1",Bgreen); zdialog_add_widget(panozd,"label","labblue","hbc1",Bblue); zdialog_add_widget(panozd,"hbox","hbc2","dialog",0,"homog"); zdialog_add_widget(panozd,"zspin","red","hbc2","50|200|0.1|100"); zdialog_add_widget(panozd,"zspin","green","hbc2","50|200|0.1|100"); zdialog_add_widget(panozd,"zspin","blue","hbc2","50|200|0.1|100"); zdialog_add_widget(panozd,"hbox","hbbri","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labbr","hbbri",Bbrightness,"space=5"); zdialog_add_widget(panozd,"zspin","bright","hbbri","50|200|0.1|100"); zdialog_add_widget(panozd,"button","RGBapply","hbbri",Bapply,"space=10"); zdialog_add_widget(panozd,"hsep","hsep","dialog",0,"space=3"); zdialog_add_widget(panozd,"hbox","hbc3","dialog",0,"space=3"); zdialog_add_widget(panozd,"button","auto","hbc3",ZTX("auto color"),"space=5"); zdialog_add_widget(panozd,"button","file","hbc3",ZTX("file color"),"space=5"); zdialog_add_widget(panozd,"hbox","hbblen","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labbl","hbblen",Bblendwidth,"space=5"); zdialog_add_widget(panozd,"zspin","blend","hbblen","1|999|1|1"); zdialog_add_widget(panozd,"button","blendapply","hbblen",Bapply,"space=15"); zdialog_add_widget(panozd,"hbox","hbwarp","dialog",0,"space=3"); zdialog_add_widget(panozd,"check","mousewarp","hbwarp",ZTX("mouse warp"),"space=3"); zdialog_add_widget(panozd,"hbox","hbflat","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labflat","hbflat",ZTX("flatten image"),"space=3"); zdialog_add_widget(panozd,"zspin","flatten","hbflat","0|1|0.01|0","space=3"); zdialog_add_widget(panozd,"button","flatapply","hbflat",Bapply,"space=15"); for (imx = 0; imx < cimNF; imx++) { // add radio button per image imageN[5] = '0' + imx; zdialog_add_widget(panozd,"radio",imageN,"hbim",0,"space=3"); } zdialog_stuff(panozd,"image0",1); // pre-select 1st image pano_currimage = 0; zdialog_stuff(panozd,"mousewarp",0); // default mouse warp off panStat = -1; // busy status zdialog_run(panozd,pano_adjust_event,"save"); // run dialog, parallel zdialog_wait(panozd); // wait for dialog completion zdialog_free(panozd); // free dialog return; } // dialog event function int pano_adjust_event(zdialog *zd, cchar *event) { char imageN[8] = "imageN"; float R, G, B, R1, G1, B1, F; int imS, imx, im1, im2; int ww, hh, px, py, nn; float bright, bright2, *pixel; if (zd->zstat) // dialog complete { if (zd->zstat == 1) panStat = 1; // done else panStat = 0; // cancel freeMouse(); for (imx = 0; imx < cimNF; imx++) // free memory PXM_free(cimPXMwdup[imx]); if (preflatPXM) PXM_free(preflatPXM); preflatPXM = 0; return 0; } imS = pano_currimage; // last image selected zdialog_fetch(zd,"red",R); // get color settings zdialog_fetch(zd,"green",G); zdialog_fetch(zd,"blue",B); zdialog_fetch(zd,"bright",bright); for (imx = 0; imx < cimNF; imx++) { // get which image is selected imageN[5] = '0' + imx; // by the radio buttons >> imS zdialog_fetch(zd,imageN,nn); if (nn) break; } if (imx == cimNF) return 1; // none selected if (imx != imS) { // new image selected pano_adjust_RGB[imS][0] = R; // save color settings for old image pano_adjust_RGB[imS][1] = G; pano_adjust_RGB[imS][2] = B; imS = pano_currimage = imx; R = pano_adjust_RGB[imS][0]; // get settings for new image G = pano_adjust_RGB[imS][1]; B = pano_adjust_RGB[imS][2]; zdialog_stuff(zd,"red",R); zdialog_stuff(zd,"green",G); zdialog_stuff(zd,"blue",B); bright = (R + G + B) / 3; zdialog_stuff(zd,"bright",bright); } if (strstr("red green blue",event)) { // new RGB value bright = (R + G + B) / 3; zdialog_stuff(zd,"bright",bright); // matching brightness } if (strmatch(event,"bright")) { // brightness change bright2 = (R + G + B) / 3; bright2 = bright / bright2; R = R * bright2; // matching RGB G = G * bright2; B = B * bright2; zdialog_stuff(zd,"red",R); zdialog_stuff(zd,"green",G); zdialog_stuff(zd,"blue",B); } if (strmatch(event,"RGBapply")) // apply color & brightness changes { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } R = R / 100; // normalize 0.5 ... 2.0 G = G / 100; B = B / 100; cim_warp_image_pano(imS,0); // refresh cimPXMw from cimPXMs ww = cimPXMw[imS]->ww; hh = cimPXMw[imS]->hh; for (py = 0; py < hh; py++) // loop all image pixels for (px = 0; px < ww; px++) { pixel = PXMpix(cimPXMw[imS],px,py); R1 = R * pixel[0]; // apply color factors G1 = G * pixel[1]; B1 = B * pixel[2]; if (R1 > 255.9 || G1 > 255.9 || B1 > 255.9) { bright = R1; // avoid overflow if (G1 > bright) bright = G1; if (B1 > bright) bright = B1; bright = 255.9 / bright; R1 = R1 * bright; G1 = G1 * bright; B1 = B1 * bright; } pixel[0] = R1; pixel[1] = G1; pixel[2] = B1; } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_images(0,0); // combine and show with 50/50 blend } if (strmatch(event,"auto")) // auto match color of selected image { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } for (im1 = imS; im1 < cimNF-1; im1++) // from selected image to last image { im2 = im1 + 1; cimBlend = 0.3 * cimPXMw[im2]->ww; cim_get_overlap(im1,im2,cimPXMw); // match images in overlap area cim_match_colors(im1,im2,cimPXMw); cim_adjust_colors_pano(cimPXMw[im1],1); // image im1 << profile 1 cim_adjust_colors_pano(cimPXMw[im2],2); // image im2 << profile 2 for (imx = im1-1; imx >= imS; imx--) cim_adjust_colors_pano(cimPXMw[imx],1); } for (im1 = imS-1; im1 >= 0; im1--) // from selected image to 1st image { im2 = im1 + 1; cimBlend = 0.3 * cimPXMw[im2]->ww; cim_get_overlap(im1,im2,cimPXMw); // match images in overlap area cim_match_colors(im1,im2,cimPXMw); cim_adjust_colors_pano(cimPXMw[im1],1); // image im1 << profile 1 cim_adjust_colors_pano(cimPXMw[im2],2); // image im2 << profile 2 for (imx = im2+1; imx < cimNF; imx++) cim_adjust_colors_pano(cimPXMw[imx],2); } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_images(0,0); } if (strmatch(event,"file")) // use original file colors { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } if (! cim_load_files()) return 1; for (imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMs[imx]); cimPXMs[imx] = PXM_copy(cimPXMf[imx]); cim_curve_image(imx); // curve and warp cim_warp_image_pano(imx,0); PXM_free(cimPXMwdup[imx]); cimPXMwdup[imx] = PXM_copy(cimPXMw[imx]); panowarp_dowarps(imx); // re-apply mouse warps } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_images(0,0); } if (strmatch(event,"blendapply")) // apply new blend width { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } zdialog_fetch(zd,"blend",cimBlend); // can be zero cim_show_images(0,1); // show with gradual blend } if (strmatch(event,"mousewarp")) // engage mouse warp/align { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_images(0,0); zdialog_fetch(zd,"mousewarp",pano_mousewarp); if (pano_mousewarp) takeMouse(panowarp_mousefunc,dragcursor); // connect mouse function else freeMouse(); } if (strmatch(event,"flatapply")) // 17.08 { if (preflatPXM) { PXM_free(E3pxm); E3pxm = PXM_copy(preflatPXM); } else preflatPXM = PXM_copy(E3pxm); zdialog_fetch(zd,"flatten",F); cim_flatten_image(F); Fpaint2(); } return 1; } // panorama dialog mouse warp function // for selected image, cimPXMs >> warp >> cimPXMw >> E3 void panowarp_mousefunc() { int imx; int mx, my, dx, dy; int ii, px, py, ww, hh; float mag, dispx, dispy, d1, d2; PXM *pxm; if (LMclick || RMclick) return; // mouse click if (! Mxdrag && ! Mydrag) return; if (! pano_mousewarp) return; mx = Mxdrag; // mouse drag my = Mydrag; if (mx < 0 || mx > E3pxm->ww-1 || my < 0 || my > E3pxm->hh-1) // mouse outside image area return; imx = pano_currimage; pxm = cimPXMw[imx]; // output image ww = pxm->ww; hh = pxm->hh; mx = Mxdown; // drag origin, image coordinates my = Mydown; dx = Mxdrag - Mxdown; // drag increment dy = Mydrag - Mydown; Mxdown = Mxdrag; // next drag origin Mydown = Mydrag; Mxdrag = Mydrag = 0; mx = mx - cimOffs[imx].xf; // mouse position relative my = my - cimOffs[imx].yf; // to image origin d1 = ww * ww + hh * hh; for (py = 0; py < hh; py++) // process all output pixels for (px = 0; px < ww; px++) { d2 = (px-mx)*(px-mx) + (py-my)*(py-my); mag = (1.0 - d2 / d1); mag = pow(mag,50); dispx = -dx * mag; // displacement = drag * mag dispy = -dy * mag; ii = py * ww + px; panowarpx[imx][ii] += dispx; // add this drag to prior sum panowarpy[imx][ii] += dispy; } panowarp_dowarps(imx); // do accumulated warps if (cimPanoV) cim_show_Vimages(0,0); // show images else cim_show_images(0,0); return; } // warp the image using the accumulated mouse drags void panowarp_dowarps(int imx) { float vpix1[4], *pix2; int ii, px, py, ww, hh, vstat1; float dispx, dispy; PXM *pxm1, *pxm2; pxm1 = cimPXMwdup[imx]; // input image pxm2 = cimPXMw[imx]; // output image ww = pxm2->ww; hh = pxm2->hh; for (py = 0; py < hh; py++) // process all output pixels for (px = 0; px < ww; px++) { ii = py * ww + px; dispx = panowarpx[imx][ii]; dispy = panowarpy[imx][ii]; vstat1 = vpixel(pxm1,px+dispx,py+dispy,vpix1); // input virtual pixel pix2 = PXMpix(pxm2,px,py); // output pixel if (vstat1) memcpy(pix2,vpix1,pixcc); else memset(pix2,0,pixcc); // voided pixel } return; } /******************************************************************************** Vertical Panorama function: join 2, 3, or 4 images. *********************************************************************************/ void vpano_prealign(); // manual pre-align void vpano_align(); // auto fine-align void vpano_adjust(); // user color adjust editfunc EFvpano; // edit function data // menu function void m_pano_vert(GtkWidget *, cchar *) { int imx, err; F1_help_topic = "vertical_panorama"; // help topic if (checkpend("all")) return; cim_manualwarp = 0; pano_mousewarp = 0; cim_manualalign = 0; cimNF = 0; for (imx = 0; imx < 10; imx++) { // clear all file and PXM data cimFile[imx] = 0; cimPXMf[imx] = cimPXMs[imx] = cimPXMw[imx] = 0; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2 || GScount > 4) { zmessageACK(Mwin,ZTX("Select 2 to 4 files")); goto cleanup; } cimNF = GScount; // file count for (imx = 0; imx < cimNF; imx++) cimFile[imx] = zstrdup(GSfiles[imx]); // set up file list if (! cim_load_files()) goto cleanup; // load and check all files free_resources(); // ready to commit err = f_open(cimFile[cimLF]); // curr_file = alphabetically last 17.04 if (err) goto cleanup; EFvpano.menufunc = m_pano_vert; EFvpano.funcname = "vpano"; EFvpano.mousefunc = panowarp_mousefunc; // 18.01 if (! edit_setup(EFvpano)) goto cleanup; // setup edit (will lock) cimShowAll = 1; // for cim_show_images(), show all cimPano = 0; // vertical pano mode cimPanoV = 1; vpano_prealign(); // manual pre-alignment if (panStat != 1) goto cancel; vpano_align(); // auto full alignment if (panStat != 1) goto cancel; vpano_adjust(); // manual color adjustment if (panStat != 1) goto cancel; CEF->Fmods++; // done CEF->Fsaved = 0; edit_done(0); goto cleanup; cancel: // failed or canceled edit_cancel(0); cleanup: for (imx = 0; imx < cimNF; imx++) { // free cim file and PXM data if (cimFile[imx]) zfree(cimFile[imx]); if (cimPXMf[imx]) PXM_free(cimPXMf[imx]); if (cimPXMs[imx]) PXM_free(cimPXMs[imx]); if (cimPXMw[imx]) PXM_free(cimPXMw[imx]); } *paneltext = 0; return; } // perform manual pre-align of all images // returns alignment data in cimOffs[*] // lens_mm may also be altered void vpano_prealign() { int vpano_prealign_event(zdialog *zd, cchar *event); // dialog event function void * vpano_prealign_thread(void *); // working thread int imx, hh, err = 0; cchar *exifkey[1] = { exif_focal_length_key }; cchar *lens_source; char *pp[1] = { 0 }; float temp; cchar *align_mess = ZTX("Drag images into rough alignment.\n" "To rotate, drag from right edge."); cchar *scan_mess = ZTX("no curve (scanned image)"); cchar *search_mess = ZTX("Search for lens mm"); cchar *save_mess = ZTX("Save lens mm → image EXIF"); err = 1; lens_source = "NO EXIF"; exif_get(curr_file,exifkey,pp,1); // get lens mm from EXIF if available if (pp[0]) err = convSF(pp[0], temp, 20, 1000); if (! err) { lens_mm = temp; lens_source = "(EXIF)"; } for (imx = 0; imx < 10; imx++) // set all alignment offsets = 0 memset(&cimOffs[imx],0,sizeof(cimoffs)); for (imx = hh = 0; imx < cimNF; imx++) // sum image heights hh += cimPXMf[imx]->hh; cimScale = 1.4 * panPreAlignSize / hh; // set alignment image scale if (cimScale > 1.0) cimScale = 1.0; // (* 0.7 after overlaps) for (imx = 0; imx < cimNF; imx++) // scale images > cimPXMs[*] cim_scale_image(imx,cimPXMs); for (imx = 0; imx < cimNF; imx++) { // curve images, cimPXMs[*] replaced cim_curve_Vimage(imx); cimPXMw[imx] = PXM_copy(cimPXMs[imx]); // copy to cimPXMw[*] for display } cimOffs[0].xf = cimOffs[0].yf = 0; // first image at (0,0) for (imx = 1; imx < cimNF; imx++) // position images with 30% overlap { // in vertical row cimOffs[imx].yf = cimOffs[imx-1].yf + 0.7 * cimPXMw[imx-1]->hh; cimOffs[imx].xf = cimOffs[imx-1].xf; } Fzoom = 0; // scale image to fit window Fblowup = 1; // magnify small image to window size cimBlend = panPreAlignBlend * cimPXMw[1]->hh; // overlap in align window cim_show_Vimages(1,0); // combine and show images in main window /* _________________________________________ | Pre-align Images | | | | Drag images into rough alignment. | | To rotate, drag from right edge. | | | | [ 35.5 ] lens mm (EXIF) | | [x] no curve (scanned image) | | [x] no auto warp | | [x] manual align | | [Resize] resize window | | [Search] Search for lens mm | | [Save] Save lens mm -> image EXIF | | | | [Proceed] [Cancel] | |_________________________________________| */ panozd = zdialog_new(ZTX("Pre-align Images"),Mwin,Bproceed,Bcancel,null); // start pre-align dialog zdialog_add_widget(panozd,"label","lab1","dialog",align_mess,"space=3"); zdialog_add_widget(panozd,"hbox","hb1","dialog",0); zdialog_add_widget(panozd,"zspin","spmm","hb1","20|999|0.1|35","space=5"); zdialog_add_widget(panozd,"label","labmm","hb1",ZTX("lens mm")); zdialog_add_widget(panozd,"label","labsorc","hb1","","space=5"); zdialog_add_widget(panozd,"hbox","hbnc","dialog"); zdialog_add_widget(panozd,"check","nocurve","hbnc",scan_mess,"space=5"); zdialog_add_widget(panozd,"hbox","hbmw","dialog"); zdialog_add_widget(panozd,"check","manwarp","hbmw",ZTX("no auto warp"),"space=5"); zdialog_add_widget(panozd,"hbox","hbma","dialog"); zdialog_add_widget(panozd,"check","manalign","hbma",ZTX("manual align"),"space=5"); zdialog_add_widget(panozd,"hbox","hb5","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","resize","hb5",ZTX("Resize"),"space=5"); zdialog_add_widget(panozd,"label","labsiz","hb5",ZTX("resize window"),"space=5"); zdialog_add_widget(panozd,"hbox","hb6","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","search","hb6",Bsearch,"space=5"); zdialog_add_widget(panozd,"label","labsearch","hb6",search_mess,"space=5"); zdialog_add_widget(panozd,"hbox","hb7","dialog",0,"space=2"); zdialog_add_widget(panozd,"button","save","hb7",Bsave,"space=5"); zdialog_add_widget(panozd,"label","labsave","hb7",save_mess,"space=5"); zdialog_add_ttip(panozd,"manwarp",ZTX("do not warp images during auto-alignment")); zdialog_stuff(panozd,"spmm",lens_mm); // stuff lens data zdialog_stuff(panozd,"labsorc",lens_source); // show source of lens data zdialog_stuff(panozd,"nocurve",cimPanoNC); zdialog_stuff(panozd,"manwarp",0); panStat = -1; // busy status gdk_window_set_cursor(gdkwin,dragcursor); // set drag cursor zdialog_run(panozd,vpano_prealign_event,"save"); // start dialog start_thread(vpano_prealign_thread,0); // start working thread zdialog_wait(panozd); // wait for dialog completion zdialog_free(panozd); // free dialog gdk_window_set_cursor(gdkwin,0); // restore normal cursor Fzoom = Fblowup = 0; return; } // pre-align dialog event function int vpano_prealign_event(zdialog *zd, cchar *event) { int imx; float overlap; cchar *exifkey[1] = { exif_focal_length_key }; cchar *exifdata[1]; char lensdata[8]; if (strmatch(event,"spmm")) zdialog_fetch(zd,"spmm",lens_mm); // get revised lens data if (strmatch(event,"manwarp")) // get auto/manual warp option zdialog_fetch(zd,"manwarp",cim_manualwarp); if (strmatch(event,"manalign")) // get auto/manual align option zdialog_fetch(zd,"manalign",cim_manualalign); if (strmatch(event,"nocurve")) zdialog_fetch(zd,"nocurve",cimPanoNC); // get "no-curve" option if (strmatch(event,"resize")) // allocate new E3 image cim_show_Vimages(1,0); if (strmatch(event,"search")) { // search for optimal lens parms if (cimNF != 2) zmessageACK(Mwin,ZTX("use two images only")); else panStat = 2; // tell thread to search return 0; } if (strmatch(event,"save")) { // put lens data into dialog zdialog_stuff(zd,"spmm",lens_mm); snprintf(lensdata,8,"%d",int(lens_mm)); exifdata[0] = lensdata; for (imx = 0; imx < cimNF; imx++) // save lens mm in EXIF data exif_put(cimFile[imx],exifkey,exifdata,1); } if (zd->zstat) // dialog complete { if (zd->zstat == 1) panStat = 1; // proceed else panStat = 0; // cancel or other wrapup_thread(0); // wait for thread if (! panStat) return 0; // canceled for (imx = 0; imx < cimNF-1; imx++) // check for enough overlap { overlap = cim_get_overlap(imx,imx+1,cimPXMs); if (overlap < panFinalBlend) { zmessageACK(Mwin,ZTX("Too little overlap, cannot align")); panStat = 0; return 0; } } } return 0; } // pre-align working thread // convert mouse and KB events into image movements // overhauled void * vpano_prealign_thread(void *) { void vpano_autolens(); cimoffs offstemp; PXM *pxmtemp; char *ftemp; int im1, im2, imm, imx; int mx0, my0, mx, my; // mouse drag origin, position int xoff, yoff, loy, hiy; int sepy, minsep; int ww, hh, rotate, midy; int fchange, nc0; float lens_mm0; float dx, dy, t1, t2, dt; imm = ww = hh = rotate = xoff = yoff = 0; // stop compiler warnings lens_mm0 = lens_mm; // to detect changes nc0 = cimPanoNC; mx0 = my0 = 0; // no drag in progress Mcapture = KBcapture = 1; // capture mouse drag and KB keys cimBlend = 0; // full blend during pre-align while (true) // loop and align until done { zsleep(0.02); // logic simplified if (panStat == 2) { // dialog search button panStat = -1; // back to busy status vpano_autolens(); } if (panStat != -1) break; // quit signal from dialog fchange = 0; if (lens_mm != lens_mm0) // change in lens parameter fchange = 1; if (cimPanoNC != nc0) fchange = 1; // change in "no-curve" option if (fchange) { lens_mm0 = lens_mm; nc0 = cimPanoNC; for (imx = 0; imx < cimNF; imx++) { // re-curve images cim_scale_image(imx,cimPXMs); cim_curve_Vimage(imx); PXM_free(cimPXMw[imx]); cimPXMw[imx] = PXM_copy(cimPXMs[imx]); } cim_show_Vimages(1,0); // combine and show images continue; } if (KBkey) { // KB input if (KBkey == GDK_KEY_Left) cimOffs[imm].xf -= 0.5; // adjust alignment offsets if (KBkey == GDK_KEY_Right) cimOffs[imm].xf += 0.5; if (KBkey == GDK_KEY_Up) cimOffs[imm].yf -= 0.5; if (KBkey == GDK_KEY_Down) cimOffs[imm].yf += 0.5; if (KBkey == GDK_KEY_r) cimOffs[imm].tf += 0.0005; if (KBkey == GDK_KEY_l) cimOffs[imm].tf -= 0.0005; KBkey = 0; cim_show_Vimages(0,0); // combine and show images continue; } if (! Mxdrag && ! Mydrag) // no drag underway mx0 = my0 = 0; // reset drag origin if ((Mxdrag || Mydrag) && ! Fmousemain) // mouse drag underway { mx = Mxdrag; // mouse position in image my = Mydrag; if (! mx0 && ! my0) // new drag { mx0 = mx; // set drag origin my0 = my; minsep = 9999; for (imx = 0; imx < cimNF; imx++) // find image with midpoint { // closest to mouse y loy = cimOffs[imx].yf; hiy = loy + cimPXMw[imx]->hh; midy = (loy + hiy) / 2; sepy = abs(midy - my0); if (sepy < minsep) { minsep = sepy; imm = imx; // image to drag or rotate } } xoff = cimOffs[imm].xf; yoff = cimOffs[imm].yf; ww = cimPXMw[imm]->ww; hh = cimPXMw[imm]->hh; rotate = 0; // if drag at right edge, if (mx0 > xoff + 0.85 * ww) rotate = 1; // set rotate flag } if (mx != mx0 || my != my0) // drag is progressing { dx = mx - mx0; // mouse movement dy = my - my0; if (rotate && my0 > yoff && my > yoff) // rotation { if (imm > 0) { loy = cimOffs[imm].yf; // if there is an image above, hiy = cimOffs[imm-1].yf + cimPXMw[imm-1]->hh; // midy = midpoint of overlap midy = (loy + hiy) / 2; } else midy = 0; // this is the topmist image t1 = atan(1.0 * (my0-yoff) / (mx0-xoff)); t2 = atan(1.0 * (my-yoff) / (mx-xoff)); dt = t2 - t1; // angle change dy = - dt * ww / 2; // pivot = middle of overlap above dx = dt * (midy-yoff); } else dt = 0; // x/y drag cimOffs[imm].xf += dx; // update image cimOffs[imm].yf += dy; cimOffs[imm].tf += dt; xoff = cimOffs[imm].xf; yoff = cimOffs[imm].yf; cim_show_Vimages(0,0); // show combined images mx0 = mx; // next drag origin = current mouse my0 = my; } } for (im1 = 0; im1 < cimNF-1; im1++) // track image order changes { im2 = im1 + 1; if (cimOffs[im2].yf < cimOffs[im1].yf) { ftemp = cimFile[im2]; // switch filespecs cimFile[im2] = cimFile[im1]; cimFile[im1] = ftemp; pxmtemp = cimPXMf[im2]; // switch images cimPXMf[im2] = cimPXMf[im1]; cimPXMf[im1] = pxmtemp; pxmtemp = cimPXMs[im2]; // scaled images cimPXMs[im2] = cimPXMs[im1]; cimPXMs[im1] = pxmtemp; pxmtemp = cimPXMw[im2]; // warped images cimPXMw[im2] = cimPXMw[im1]; cimPXMw[im1] = pxmtemp; offstemp = cimOffs[im2]; // offsets cimOffs[im2] = cimOffs[im1]; cimOffs[im1] = offstemp; if (imm == im1) imm = im2; // current drag image else if (imm == im2) imm = im1; break; } } } KBcapture = Mcapture = 0; thread_exit(); return 0; // not executed, stop g++ warning } // optimize lens parameters // inputs and outputs: // pre-aligned images cimPXMw[0] and [1] // offsets in cimOffs[0] and [1] // lens_mm void vpano_autolens() { float mm_range, xf_range, yf_range, tf_range; float squeeze, xf_rfinal, rnum, matchB, matchlev; float overlap, lens_mmB; int imx, randcount = 0; cimoffs offsetsB; overlap = cim_get_overlap(0,1,cimPXMs); if (overlap < 0.1) { zmessageACK(Mwin,ZTX("Too little overlap, cannot align")); return; } Ffuncbusy = 1; cimSampSize = 5000; cimNsearch = 0; mm_range = 0.1 * lens_mm; // set initial search ranges xf_range = 7; yf_range = 7; tf_range = 0.01; xf_rfinal = 0.3; // final xf range - when to quit cim_match_colors(0,1,cimPXMw); // adjust colors for image matching cim_adjust_colors(cimPXMs[0],1); cim_adjust_colors(cimPXMw[0],1); cim_adjust_colors(cimPXMs[1],2); cim_adjust_colors(cimPXMw[1],2); lens_mmB = lens_mm; // starting point offsetsB = cimOffs[1]; cimSearchRange = 7; matchB = 0; while (true) { srand48(time(0) + randcount++); lens_mm = lens_mmB + mm_range * (drand48() - 0.5); // new random lens factor for (imx = 0; imx <= 1; imx++) { // re-curve images cim_scale_image(imx,cimPXMs); cim_curve_Vimage(imx); PXM_free(cimPXMw[imx]); cimPXMw[imx] = PXM_copy(cimPXMs[imx]); } cim_get_redpix(0); // get high-contrast pixels cim_show_Vimages(0,0); // combine and show images squeeze = 0.97; // search range reduction for (int ii = 0; ii < 1000; ii++) // loop random x/y/t alignments { rnum = drand48(); if (rnum < 0.33) // random change some alignment offset cimOffs[1].xf = offsetsB.xf + xf_range * (drand48() - 0.5); else if (rnum < 0.67) cimOffs[1].yf = offsetsB.yf + yf_range * (drand48() - 0.5); else cimOffs[1].tf = offsetsB.tf + tf_range * (drand48() - 0.5); matchlev = cim_match_images(0,1); // test quality of image alignment snprintf(paneltext,200,"align: %d match: %.5f lens: %.1f", ++cimNsearch, matchB, lens_mmB); if (sigdiff(matchlev,matchB,0.00001) > 0) { matchB = matchlev; // save new best fit lens_mmB = lens_mm; // alignment is better offsetsB = cimOffs[1]; cim_show_Vimages(0,0); squeeze = 1; // keep same search range as long break; // as improvements are found } if (panStat != -1) goto done; // user kill zmainloop(); } if (xf_range < xf_rfinal) goto done; // finished snprintf(paneltext,200,"align: %d match: %.5f lens: %.1f", cimNsearch, matchB, lens_mmB); mm_range = squeeze * mm_range; // reduce search range if no if (mm_range < 0.02 * lens_mmB) mm_range = 0.02 * lens_mmB; // improvements were found xf_range = squeeze * xf_range; yf_range = squeeze * yf_range; tf_range = squeeze * tf_range; zmainloop(); } done: zfree(cimRedpix); cimRedpix = 0; lens_mm = lens_mmB; // save best lens params found cimSampSize = panSampSize; // restore Ffuncbusy = 0; cim_show_Vimages(1,0); // images are left color-matched return; } // fine-alignment // start with very small image size // search around offset values for best match // increase image size and loop until full-size void vpano_align() { int imx, im1, im2, ww, hh; float R, dx, dy, dt; float overlap; cimoffs offsets0; Fzoom = 0; // scale E3 to fit window Fblowup = 1; // magnify small image to window size Ffuncbusy = 1; for (imx = 0; imx < cimNF; imx++) { cimOffs[imx].xf = cimOffs[imx].xf / cimScale; // scale x/y offsets for full-size images cimOffs[imx].yf = cimOffs[imx].yf / cimScale; } cimScale = 1.0; // full-size for (imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMs[imx]); cimPXMs[imx] = PXM_copy(cimPXMf[imx]); // copy full-size images cim_curve_Vimage(imx); // curve them } cimBlend = 0.3 * cimPXMs[0]->hh; cim_get_overlap(0,1,cimPXMs); // match images 0 & 1 in overlap area cim_match_colors(0,1,cimPXMs); cim_adjust_colors(cimPXMf[0],1); // image 0 << profile 1 cim_adjust_colors(cimPXMf[1],2); // image 1 << profile 2 if (cimNF > 2) { cimBlend = 0.3 * cimPXMs[1]->hh; cim_get_overlap(1,2,cimPXMs); cim_match_colors(1,2,cimPXMs); cim_adjust_colors(cimPXMf[0],1); cim_adjust_colors(cimPXMf[1],1); cim_adjust_colors(cimPXMf[2],2); } if (cimNF > 3) { cimBlend = 0.3 * cimPXMs[2]->hh; cim_get_overlap(2,3,cimPXMs); cim_match_colors(2,3,cimPXMs); cim_adjust_colors(cimPXMf[0],1); cim_adjust_colors(cimPXMf[1],1); cim_adjust_colors(cimPXMf[2],1); cim_adjust_colors(cimPXMf[3],2); } cimScale = panInitAlignSize / cimPXMf[1]->hh; // initial align image scale if (cimScale > 1.0) cimScale = 1.0; if (cim_manualalign) cimScale = 1.0; // manual alignment, full size for (imx = 0; imx < cimNF; imx++) { // scale offsets for image scale cimOffs[imx].xf = cimOffs[imx].xf * cimScale; cimOffs[imx].yf = cimOffs[imx].yf * cimScale; } cimSearchRange = panInitSearchRange; // initial align search range cimSearchStep = panInitSearchStep; // initial align search step cimWarpRange = panInitWarpRange; // initial align corner warp range cimWarpStep = panInitWarpStep; // initial align corner warp step hh = cimPXMf[0]->hh * cimScale; // initial align image width cimBlend = hh * panInitBlend; // initial align blend width cimSampSize = panSampSize; // pixel sample size for align/compare cimNsearch = 0; // reset align search counter while (true) // loop, increasing image size { for (imx = 0; imx < cimNF; imx++) { // prepare images cim_scale_image(imx,cimPXMs); // scale to new size cim_curve_Vimage(imx); // curve based on lens params cim_warp_image_Vpano(imx,1); // apply corner warps } cim_show_Vimages(1,0); // show with 50/50 blend in overlaps for (im1 = 0; im1 < cimNF-1; im1++) // fine-align each image with top neighbor { im2 = im1 + 1; offsets0 = cimOffs[im2]; // save initial alignment offsets overlap = cim_get_overlap(im1,im2,cimPXMs); // get overlap area if (overlap < panFinalBlend-2) { zmessageACK(Mwin,ZTX("Too little overlap, cannot align")); goto fail; } if (cim_manualalign) cim_show_Vimages(0,0); else { cim_get_redpix(im1); // get high-contrast pixels cim_show_Vimages(0,0); // show with 50/50 blend in overlaps cim_align_image(im1,im2); // search for best offsets and warps zfree(cimRedpix); // clear red pixels cimRedpix = 0; } dx = cimOffs[im2].xf - offsets0.xf; // changes from initial offsets dy = cimOffs[im2].yf - offsets0.yf; dt = cimOffs[im2].tf - offsets0.tf; for (imx = im2+1; imx < cimNF; imx++) // propagate to following images { cimOffs[imx].xf += dx; cimOffs[imx].yf += dy; cimOffs[imx].tf += dt; ww = cimOffs[imx].xf - cimOffs[im2].xf; cimOffs[imx].yf += ww * dt; } } if (cimScale == 1.0) goto success; // done R = panImageIncrease; // next larger image size cimScale = cimScale * R; if (cimScale > 0.85) { // if close to end, jump to end R = R / cimScale; cimScale = 1.0; } for (imx = 0; imx < cimNF; imx++) // scale offsets for new size { cimOffs[imx].xf *= R; cimOffs[imx].yf *= R; for (int ii = 0; ii < 4; ii++) { cimOffs[imx].wx[ii] *= R; cimOffs[imx].wy[ii] *= R; } } cimSearchRange = panSearchRange; // align search range cimSearchStep = panSearchStep; // align search step size cimWarpRange = panWarpRange; // align corner warp range cimWarpStep = panWarpStep; // align corner warp step size cimBlend = cimBlend * panBlendDecrease * R; // blend width, reduced hh = cimPXMf[0]->hh * cimScale; if (cimBlend < panFinalBlend * hh) cimBlend = panFinalBlend * hh; // stay above minimum } success: panStat = 1; goto align_done; fail: panStat = 0; align_done: Fzoom = Fblowup = 0; Ffuncbusy = 0; cimBlend = 1; // tiny blend (increase in adjust if wanted) cim_show_Vimages(0,0); return; } // get user inputs for RGB changes and blend width, update cimPXMw[*] void vpano_adjust() { int vpano_adjust_event(zdialog *zd, cchar *event); // dialog event function cchar *adjusttitle = ZTX("Match Brightness and Color"); char imageN[8] = "imageN"; int ww, hh, cc, imx; for (imx = 0; imx < cimNF; imx++) { // neutral color adjustments pano_adjust_RGB[imx][0] = 100; pano_adjust_RGB[imx][1] = 100; pano_adjust_RGB[imx][2] = 100; } for (imx = 0; imx < cimNF; imx++) { // allocate warp memory ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; cc = ww * hh * sizeof(float); panowarpx[imx] = (float *) zmalloc(cc); panowarpy[imx] = (float *) zmalloc(cc); memset(panowarpx[imx],0,cc); memset(panowarpy[imx],0,cc); cimPXMwdup[imx] = PXM_copy(cimPXMw[imx]); } cimBlend = 1; // init. blend width /*** ___________________________________ | Match Brightness and Color | | | | Image (o) (o) (o) (o) | | | | red green blue | | [_____] [_____] [_____] | | brightness [____] [apply] | | [auto color] [file color] | | blend width [____] [apply] | | [x] mouse warp | | [flatten] curved image | | flatten image [___] [apply] | | | | [done] [cancel] | |___________________________________| ***/ panozd = zdialog_new(adjusttitle,Mwin,Bdone,Bcancel,null); zdialog_add_widget(panozd,"hbox","hbim","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labim","hbim",Bimage,"space=5"); zdialog_add_widget(panozd,"hbox","hbc1","dialog",0,"homog"); zdialog_add_widget(panozd,"label","labred","hbc1",Bred); zdialog_add_widget(panozd,"label","labgreen","hbc1",Bgreen); zdialog_add_widget(panozd,"label","labblue","hbc1",Bblue); zdialog_add_widget(panozd,"hbox","hbc2","dialog",0,"homog"); zdialog_add_widget(panozd,"zspin","red","hbc2","50|200|0.1|100","space=5"); zdialog_add_widget(panozd,"zspin","green","hbc2","50|200|0.1|100","space=5"); zdialog_add_widget(panozd,"zspin","blue","hbc2","50|200|0.1|100","space=5"); zdialog_add_widget(panozd,"hbox","hbbri","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labbr","hbbri",Bbrightness,"space=5"); zdialog_add_widget(panozd,"zspin","bright","hbbri","50|200|0.1|100"); zdialog_add_widget(panozd,"button","RGBapply","hbbri",Bapply,"space=10"); zdialog_add_widget(panozd,"hsep","hsep","dialog",0,"space=3"); zdialog_add_widget(panozd,"hbox","hbc3","dialog",0,"space=3"); zdialog_add_widget(panozd,"button","auto","hbc3",ZTX("auto color"),"space=5"); zdialog_add_widget(panozd,"button","file","hbc3",ZTX("file color"),"space=5"); zdialog_add_widget(panozd,"hbox","hbblen","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labbl","hbblen",Bblendwidth,"space=5"); zdialog_add_widget(panozd,"zspin","blend","hbblen","1|999|1|1"); zdialog_add_widget(panozd,"button","blendapply","hbblen",Bapply,"space=15"); zdialog_add_widget(panozd,"hbox","hbwarp","dialog",0,"space=3"); zdialog_add_widget(panozd,"check","mousewarp","hbwarp",ZTX("mouse warp"),"space=3"); zdialog_add_widget(panozd,"hbox","hbflat","dialog",0,"space=3"); zdialog_add_widget(panozd,"label","labflat","hbflat",ZTX("flatten image"),"space=3"); zdialog_add_widget(panozd,"zspin","flatten","hbflat","0|1|0.01|0","space=3"); zdialog_add_widget(panozd,"button","flatapply","hbflat",Bapply,"space=15"); for (imx = 0; imx < cimNF; imx++) { // add radio button per image imageN[5] = '0' + imx; zdialog_add_widget(panozd,"radio",imageN,"hbim",0,"space=3"); } zdialog_stuff(panozd,"image0",1); // pre-select 1st image pano_currimage = 0; zdialog_stuff(panozd,"mousewarp",0); // default mouse warp off panStat = -1; // busy status zdialog_run(panozd,vpano_adjust_event,"save"); // run dialog, parallel zdialog_wait(panozd); // wait for dialog completion zdialog_free(panozd); // free dialog return; } // dialog event function int vpano_adjust_event(zdialog *zd, cchar *event) { char imageN[8] = "imageN"; float R, G, B, R1, G1, B1, F; int imS, imx, im1, im2; int ww, hh, px, py, nn; float bright, bright2, *pixel; if (zd->zstat) // dialog complete { if (zd->zstat == 1) panStat = 1; // done else panStat = 0; // cancel freeMouse(); for (imx = 0; imx < cimNF; imx++) // free memory PXM_free(cimPXMwdup[imx]); if (preflatPXM) PXM_free(preflatPXM); preflatPXM = 0; return 0; } imS = pano_currimage; // last image selected zdialog_fetch(zd,"red",R); // get color settings zdialog_fetch(zd,"green",G); zdialog_fetch(zd,"blue",B); zdialog_fetch(zd,"bright",bright); for (imx = 0; imx < cimNF; imx++) { // get which image is selected imageN[5] = '0' + imx; // by the radio buttons >> imS zdialog_fetch(zd,imageN,nn); if (nn) break; } if (imx == cimNF) return 1; // none selected if (imx != imS) { // new image selected pano_adjust_RGB[imS][0] = R; // save color settings for old image pano_adjust_RGB[imS][1] = G; pano_adjust_RGB[imS][2] = B; imS = pano_currimage = imx; R = pano_adjust_RGB[imS][0]; // get settings for new image G = pano_adjust_RGB[imS][1]; B = pano_adjust_RGB[imS][2]; zdialog_stuff(zd,"red",R); zdialog_stuff(zd,"green",G); zdialog_stuff(zd,"blue",B); bright = (R + G + B) / 3; zdialog_stuff(zd,"bright",bright); } if (strstr("red green blue",event)) { // new RGB value bright = (R + G + B) / 3; zdialog_stuff(zd,"bright",bright); // matching brightness } if (strmatch(event,"bright")) { // brightness change bright2 = (R + G + B) / 3; bright2 = bright / bright2; R = R * bright2; // matching RGB G = G * bright2; B = B * bright2; zdialog_stuff(zd,"red",R); zdialog_stuff(zd,"green",G); zdialog_stuff(zd,"blue",B); } if (strmatch(event,"RGBapply")) // apply color & brightness changes { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } R = R / 100; // normalize 0.5 ... 2.0 G = G / 100; B = B / 100; cim_warp_image_Vpano(imS,0); // refresh cimPXMw from cimPXMs ww = cimPXMw[imS]->ww; hh = cimPXMw[imS]->hh; for (py = 0; py < hh; py++) // loop all image pixels for (px = 0; px < ww; px++) { pixel = PXMpix(cimPXMw[imS],px,py); R1 = R * pixel[0]; // apply color factors G1 = G * pixel[1]; B1 = B * pixel[2]; if (R1 > 255.9 || G1 > 255.9 || B1 > 255.9) { bright = R1; // avoid overflow if (G1 > bright) bright = G1; if (B1 > bright) bright = B1; bright = 255.9 / bright; R1 = R1 * bright; G1 = G1 * bright; B1 = B1 * bright; } pixel[0] = R1; pixel[1] = G1; pixel[2] = B1; } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_Vimages(0,0); // combine and show with 50/50 blend } if (strmatch(event,"auto")) // auto match color of selected image { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } for (im1 = imS; im1 < cimNF-1; im1++) // from selected image to last image { im2 = im1 + 1; cimBlend = 0.3 * cimPXMw[im2]->ww; cim_get_overlap(im1,im2,cimPXMw); // match images in overlap area cim_match_colors(im1,im2,cimPXMw); cim_adjust_colors_vpano(cimPXMw[im1],1); // image im1 << profile 1 cim_adjust_colors_vpano(cimPXMw[im2],2); // image im2 << profile 2 for (imx = im1-1; imx >= imS; imx--) cim_adjust_colors_vpano(cimPXMw[imx],1); } for (im1 = imS-1; im1 >= 0; im1--) // from selected image to 1st image { im2 = im1 + 1; cimBlend = 0.3 * cimPXMw[im2]->ww; cim_get_overlap(im1,im2,cimPXMw); // match images in overlap area cim_match_colors(im1,im2,cimPXMw); cim_adjust_colors_vpano(cimPXMw[im1],1); // image im1 << profile 1 cim_adjust_colors_vpano(cimPXMw[im2],2); // image im2 << profile 2 for (imx = im2+1; imx < cimNF; imx++) cim_adjust_colors_vpano(cimPXMw[imx],2); } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_Vimages(0,0); } if (strmatch(event,"file")) // use original file colors { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } if (! cim_load_files()) return 1; for (imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMs[imx]); cimPXMs[imx] = PXM_copy(cimPXMf[imx]); cim_curve_Vimage(imx); // curve and warp cim_warp_image_Vpano(imx,0); PXM_free(cimPXMwdup[imx]); cimPXMwdup[imx] = PXM_copy(cimPXMw[imx]); panowarp_dowarps(imx); // re-apply mouse warps } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_Vimages(0,0); } if (strmatch(event,"blendapply")) // apply new blend width { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } zdialog_fetch(zd,"blend",cimBlend); // can be zero cim_show_Vimages(0,1); // show with gradual blend } if (strmatch(event,"mousewarp")) // engage mouse warp/align { if (preflatPXM) { PXM_free(E3pxm); E3pxm = preflatPXM; preflatPXM = 0; } cimBlend = 1; zdialog_stuff(zd,"blend",cimBlend); cim_show_Vimages(0,0); // 18.01 zdialog_fetch(zd,"mousewarp",pano_mousewarp); if (pano_mousewarp) takeMouse(panowarp_mousefunc,dragcursor); // connect mouse function else freeMouse(); } if (strmatch(event,"flatapply")) // 17.08 { if (preflatPXM) { PXM_free(E3pxm); E3pxm = PXM_copy(preflatPXM); } else preflatPXM = PXM_copy(E3pxm); zdialog_fetch(zd,"flatten",F); cim_flatten_Vimage(F); Fpaint2(); } return 1; } /********************************************************************************/ // make a multi-row panorama via Panorama Tools package (Hugin version) void m_pano_PT(GtkWidget *, cchar *) { int im, ii, err; char file1[100], *file2, *pp; char cdirk[XFCC]; // 17.08 FILE *fid; struct stat statb; F1_help_topic = "PT_panorama"; // help topic cimNF = 0; if (! PTtools) { zmessageACK(Mwin,ZTX("pano tools (hugin) not installed")); return; } gallery_select_clear(); // clear selected file list gallery_select(); // get new list if (GScount == 0) goto cleanup; if (GScount < 2) { zmessageACK(Mwin,ZTX("Select at least 2 files")); goto cleanup; } cimNF = GScount; if (checkpend("all")) return; Fblock = 1; pp = getcwd(cdirk,XFCC); // save curr. directory 17.08 err = chdir(tempdir); // use /tmp/fotoxx-xxxxx if (err) { zmessageACK(Mwin,strerror(errno)); goto cleanup; } fid = fopen("PTscript","w"); // build pano tools script if (! fid) { zmessageACK(Mwin,strerror(errno)); goto cleanup; } fprintf(fid,"%s","pto_gen -o PTpano.pto "); // create a project file for (im = 0; im < cimNF; im++) // for member image files fprintf(fid," \"%s\" ",GSfiles[im]); fprintf(fid,"\n"); fprintf(fid,"cpfind --celeste --multirow -o PTpano.pto PTpano.pto \n"); // find control points fprintf(fid,"cpclean -o PTpano.pto PTpano.pto \n"); // remove flakey ones fprintf(fid,"autooptimiser -a -m -l -s -o PTpano.pto PTpano.pto \n"); // optimize colors etc. fprintf(fid,"pano_modify --crop=AUTO -o PTpano.pto PTpano.pto \n"); // cut off black margins fprintf(fid,"hugin_executor --prefix=PTpano --stitching PTpano.pto \n"); // execute script err = fclose(fid); if (err) { zmessageACK(Mwin,strerror(errno)); goto cleanup; } chmod("PTscript",0760); // run the script popup_command("./PTscript",600,400,Mwin,0); snprintf(file1,100,"%s/PTpano.tif",tempdir); // look for output file err = stat(file1,&statb); if (err) goto cleanup; im = 0; for (ii = 1; ii < cimNF; ii++) // get alphabetically last input file if (strcmp(GSfiles[ii],GSfiles[im]) > 0) im = ii; file2 = zstrdup(GSfiles[im],12); // make fotoxx output file 17.04 pp = strrchr(file2,'.'); // = < last input file > -PT.tif if (! pp) pp = file2; strcpy(pp,"-PT.tif"); err = copyFile(file1,file2); // copy /tmp/.../PTpano.tif 17.08 if (err) goto cleanup; // to fotoxx output file f_open(file2,0,0,1,0); // and open it as curr. file m_viewmode(0,"F"); popup_report_close(3); // close script window in 3 secs. cleanup: err = chdir(cdirk); // restore curr. dirk 17.08 Fblock = 0; return; } /********************************************************************************/ // load image files into pixmaps cimPXMf[*] and check for errors // returns 0 if error int cim_load_files() { PXM *pxm; int imx; for (imx = 0; imx < cimNF; imx++) { PXM_free(cimPXMf[imx]); pxm = PXM_load(cimFile[imx],1); // ACK errors if (! pxm) return 0; PXM_addalpha(pxm); cimPXMf[imx] = pxm; } cimLF = 0; // find alphabetically last file 17.04 for (imx = 1; imx < cimNF; imx++) if (strcmp(cimFile[imx],cimFile[cimLF]) > 0) cimLF = imx; return 1; } // scale image from full size to cimScale (normally < 1.0) void cim_scale_image(int im, PXM** pxmout) { int ww, hh; ww = cimScale * cimPXMf[im]->ww; hh = cimScale * cimPXMf[im]->hh; PXM_free(pxmout[im]); pxmout[im] = PXM_rescale(cimPXMf[im],ww,hh); return; } // get overlap area for a pair of images im1 and im2 // outputs are coordinates of overlap area in im1 and in im2 // returns overlap width as fraction of image width <= 1.0 float cim_get_overlap(int im1, int im2, PXM **pxmx) { float x1, y1, t1, x2, y2, t2; float xoff, yoff, toff, costf, sintf; int ww1, ww2, hh1, hh2, pxM; PXM *pxm1, *pxm2; x1 = cimOffs[im1].xf; // im1, im2 absolute offsets y1 = cimOffs[im1].yf; t1 = cimOffs[im1].tf; x2 = cimOffs[im2].xf; y2 = cimOffs[im2].yf; t2 = cimOffs[im2].tf; xoff = (x2 - x1) * cosf(t1) + (y2 - y1) * sinf(t1); // offset of im2 relative to im1 yoff = (y2 - y1) * cosf(t1) - (x2 - x1) * sinf(t1); toff = t2 - t1; costf = cosf(toff); sintf = sinf(toff); pxm1 = pxmx[im1]; pxm2 = pxmx[im2]; ww1 = pxm1->ww; hh1 = pxm1->hh; ww2 = pxm2->ww; hh2 = pxm2->hh; cimOv1xlo = 0; // lowest x overlap if (xoff > 0) cimOv1xlo = xoff; cimOv1xhi = ww1-1; // highest x overlap if (cimOv1xhi > xoff + ww2-1) cimOv1xhi = xoff + ww2-1; cimOv1ylo = 0; // lowest y overlap if (yoff > 0) cimOv1ylo = yoff; cimOv1yhi = hh1-1; // highest y overlap if (cimOv1yhi > yoff + hh2-1) cimOv1yhi = yoff + hh2-1; if (toff < 0) cimOv1xlo -= toff * (cimOv1yhi - cimOv1ylo); // reduce for theta offset if (toff < 0) cimOv1yhi += toff * (cimOv1xhi - cimOv1xlo); if (toff > 0) cimOv1xhi -= toff * (cimOv1yhi - cimOv1ylo); if (toff > 0) cimOv1ylo += toff * (cimOv1xhi - cimOv1xlo); if (cimPanoV) { if (cimBlend && cimBlend < (cimOv1yhi - cimOv1ylo)) { // reduce y range to cimBlend pxM = (cimOv1yhi + cimOv1ylo) / 2; cimOv1ylo = pxM - cimBlend / 2; cimOv1yhi = pxM + cimBlend / 2; } } else { if (cimBlend && cimBlend < (cimOv1xhi - cimOv1xlo)) { // reduce x range to cimBlend pxM = (cimOv1xhi + cimOv1xlo) / 2; cimOv1xlo = pxM - cimBlend / 2; cimOv1xhi = pxM + cimBlend / 2; } } cimOv2xlo = costf * (cimOv1xlo - xoff) + sintf * (cimOv1ylo - yoff); // overlap area in im2 coordinates cimOv2xhi = costf * (cimOv1xhi - xoff) + sintf * (cimOv1yhi - yoff); cimOv2ylo = costf * (cimOv1ylo - yoff) + sintf * (cimOv1xlo - xoff); cimOv2yhi = costf * (cimOv1yhi - yoff) + sintf * (cimOv1xhi - xoff); if (cimOv1xlo < 0) cimOv1xlo = 0; // take care of limits if (cimOv1ylo < 0) cimOv1ylo = 0; if (cimOv2xlo < 0) cimOv2xlo = 0; if (cimOv2ylo < 0) cimOv2ylo = 0; if (cimOv1xhi > ww1-1) cimOv1xhi = ww1-1; if (cimOv1yhi > hh1-1) cimOv1yhi = hh1-1; if (cimOv2xhi > ww2-1) cimOv2xhi = ww2-1; if (cimOv2yhi > hh2-1) cimOv2yhi = hh2-1; if (cimPanoV) return 1.0 * (cimOv1yhi - cimOv1ylo) / hh1; // return overlap height <= 1.0 else return 1.0 * (cimOv1xhi - cimOv1xlo) / ww1; // return overlap width <= 1.0 } // Get the RGB brightness distribution in the overlap area for each image. // Compute matching factors to compare pixels within the overlap area. // compare cimRGBmf1[rgb][pix1[rgb]] to cimRGBmf2[rgb][pix2[rgb]]. void cim_match_colors(int im1, int im2, PXM **pxmx) { float Bratios1[3][256]; // image2/image1 brightness ratio per color per level float Bratios2[3][256]; // image1/image2 brightness ratio per color per level float *pix1, vpix2[4]; int vstat2, px1, py1; int ii, jj, rgb; int npix, npix1, npix2, npix3; int brdist1[3][256], brdist2[3][256]; float x1, y1, t1, x2, y2, t2; float xoff, yoff, toff, costf, sintf; float px2, py2; float brlev1[3][256], brlev2[3][256]; float a1, a2, b1, b2, bratio = 1; PXM *pxm1, *pxm2; pxm1 = pxmx[im1]; pxm2 = pxmx[im2]; x1 = cimOffs[im1].xf; // im1, im2 absolute offsets y1 = cimOffs[im1].yf; t1 = cimOffs[im1].tf; x2 = cimOffs[im2].xf; y2 = cimOffs[im2].yf; t2 = cimOffs[im2].tf; xoff = (x2 - x1) * cosf(t1) + (y2 - y1) * sinf(t1); // offset of im2 relative to im1 yoff = (y2 - y1) * cosf(t1) - (x2 - x1) * sinf(t1); toff = t2 - t1; costf = cosf(toff); sintf = sinf(toff); for (rgb = 0; rgb < 3; rgb++) // clear distributions for (ii = 0; ii < 256; ii++) brdist1[rgb][ii] = brdist2[rgb][ii] = 0; npix = 0; for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++) // loop overlapped rows for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++) // loop overlapped columns { pix1 = PXMpix(pxm1,px1,py1); // image1 pixel if (pix1[3] < 254) continue; // ignore void pixels px2 = costf * (px1 - xoff) + sintf * (py1 - yoff); // corresponding image2 pixel py2 = costf * (py1 - yoff) - sintf * (px1 - xoff); vstat2 = vpixel(pxm2,px2,py2,vpix2); if (! vstat2) continue; // does not exist if (vpix2[3] < 254) continue; // ignore void pixels ++npix; // count overlapping pixels for (rgb = 0; rgb < 3; rgb++) // accumulate distributions { // by color in 256 bins ++brdist1[rgb][int(pix1[rgb])]; ++brdist2[rgb][int(vpix2[rgb])]; } } npix1 = npix / 256; // 1/256th of total pixels for (rgb = 0; rgb < 3; rgb++) // get brlev1[rgb][N] = mean bright for (ii = jj = 0; jj < 256; jj++) // for Nth group of image1 pixels { // for color rgb brlev1[rgb][jj] = 0; npix2 = npix1; // 1/256th of total pixels while (npix2 > 0 && ii < 256) // next 1/256th group from distr. { npix3 = brdist1[rgb][ii]; if (npix3 == 0) { ++ii; continue; } if (npix3 > npix2) npix3 = npix2; brlev1[rgb][jj] += ii * npix3; // brightness * (pixels with) brdist1[rgb][ii] -= npix3; npix2 -= npix3; } brlev1[rgb][jj] = brlev1[rgb][jj] / npix1; // mean brightness for group, 0-255 } for (rgb = 0; rgb < 3; rgb++) // do same for image2 for (ii = jj = 0; jj < 256; jj++) { brlev2[rgb][jj] = 0; npix2 = npix1; while (npix2 > 0 && ii < 256) { npix3 = brdist2[rgb][ii]; if (npix3 == 0) { ++ii; continue; } if (npix3 > npix2) npix3 = npix2; brlev2[rgb][jj] += ii * npix3; brdist2[rgb][ii] -= npix3; npix2 -= npix3; } brlev2[rgb][jj] = brlev2[rgb][jj] / npix1; } for (rgb = 0; rgb < 3; rgb++) // color for (ii = jj = 0; ii < 256; ii++) // brlev1 brightness, 0 to 255 { if (ii == 0) bratio = 1; while (ii > brlev2[rgb][jj] && jj < 256) ++jj; // find matching brlev2 brightness a2 = brlev2[rgb][jj]; // next higher value b2 = brlev1[rgb][jj]; if (a2 > 0 && b2 > 0) { if (jj > 0) { a1 = brlev2[rgb][jj-1]; // next lower value b1 = brlev1[rgb][jj-1]; } else a1 = b1 = 0; if (ii == 0) bratio = b2 / a2; else bratio = (b1 + (ii-a1)/(a2-a1) * (b2-b1)) / ii; // interpolate } if (bratio < 0.2) bratio = 0.2; // contain outliers if (bratio > 5) bratio = 5; Bratios2[rgb][ii] = bratio; } for (rgb = 0; rgb < 3; rgb++) // color for (ii = jj = 0; ii < 256; ii++) // brlev2 brightness, 0 to 255 { if (ii == 0) bratio = 1; while (ii > brlev1[rgb][jj] && jj < 256) ++jj; // find matching brlev1 brightness a2 = brlev1[rgb][jj]; // next higher value b2 = brlev2[rgb][jj]; if (a2 > 0 && b2 > 0) { if (jj > 0) { a1 = brlev1[rgb][jj-1]; // next lower value b1 = brlev2[rgb][jj-1]; } else a1 = b1 = 0; if (ii == 0) bratio = b2 / a2; else bratio = (b1 + (ii-a1)/(a2-a1) * (b2-b1)) / ii; // interpolate } if (bratio < 0.2) bratio = 0.2; // contain outliers if (bratio > 5) bratio = 5; Bratios1[rgb][ii] = bratio; } for (ii = 0; ii < 256; ii++) // convert brightness ratios for (rgb = 0; rgb < 3; rgb++) // to conversion factors { cimRGBmf1[rgb][ii] = sqrt(Bratios1[rgb][ii]) * ii; // use sqrt(ratio) so that adjustment cimRGBmf2[rgb][ii] = sqrt(Bratios2[rgb][ii]) * ii; // can be applied to both images } return; } // Use color match data from cim_match_colors() to // modify images so the colors match. void cim_adjust_colors(PXM *pxm, int fwhich) { int ww, hh, px, py; float red, green, blue, max; float *pix; float ff; ww = pxm->ww; hh = pxm->hh; for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { pix = PXMpix(pxm,px,py); red = pix[0]; green = pix[1]; blue = pix[2]; if (! blue) continue; if (fwhich == 1) { red = cimRGBmf1[0][int(red)]; green = cimRGBmf1[1][int(green)]; blue = cimRGBmf1[2][int(blue)]; } else { red = cimRGBmf2[0][int(red)]; green = cimRGBmf2[1][int(green)]; blue = cimRGBmf2[2][int(blue)]; } if (red > 255.9 || green > 255.9 || blue > 255.9) { max = red; if (green > max) max = green; if (blue > max) max = blue; ff = 255.9 / max; red = red * ff; green = green * ff; blue = blue * ff; } pix[0] = red; pix[1] = green; pix[2] = blue; } return; } // Use color match data from cim_match_colors() to // modify images so the colors match. // fwhich = 1/2 = left/right image void cim_adjust_colors_pano(PXM *pxm, int fwhich) { int ww, hh, ww2, px, py; int pxlo, pxhi; float red1, green1, blue1, max; float red2, green2, blue2; float *pix; float f1, f2; ww = pxm->ww; hh = pxm->hh; ww2 = ww / 2; if (fwhich == 1) { // scan half the image pxlo = ww2; pxhi = ww; } else { pxlo = 0; pxhi = ww2; } for (py = 0; py < hh; py++) for (px = pxlo; px < pxhi; px++) { pix = PXMpix(pxm,px,py); red1 = pix[0]; green1 = pix[1]; blue1 = pix[2]; if (! blue1) continue; if (fwhich == 1) { red2 = cimRGBmf1[0][int(red1)]; green2 = cimRGBmf1[1][int(green1)]; blue2 = cimRGBmf1[2][int(blue1)]; } else { red2 = cimRGBmf2[0][int(red1)]; green2 = cimRGBmf2[1][int(green1)]; blue2 = cimRGBmf2[2][int(blue1)]; } if (fwhich == 1) { // taper color adjustment over distance f2 = 1.0 * (px - pxlo) / ww2; f1 = 1.0 - f2; } else { f1 = 1.0 * px / ww2; f2 = 1.0 - f1; } red2 = f2 * red2 + f1 * red1; green2 = f2 * green2 + f1 * green1; blue2 = f2 * blue2 + f1 * blue1; if (red2 > 255.9 || green2 > 255.9 || blue2 > 255.9) { max = red2; if (green2 > max) max = green2; if (blue2 > max) max = blue2; f1 = 255.9 / max; red2 = red2 * f1; green2 = green2 * f1; blue2 = blue2 * f1; } pix[0] = red2; pix[1] = green2; pix[2] = blue2; } return; } // version for vertical panoramas // fwhich = 1/2 = upper/lower image void cim_adjust_colors_vpano(PXM *pxm, int fwhich) { int ww, hh, hh2, px, py; int pylo, pyhi; float red1, green1, blue1, max; float red2, green2, blue2; float *pix; float f1, f2; ww = pxm->ww; hh = pxm->hh; hh2 = hh / 2; if (fwhich == 1) { // scan half the image pylo = hh2; pyhi = hh; } else { pylo = 0; pyhi = hh2; } for (py = pylo; py < pyhi; py++) for (px = 0; px < ww; px++) { pix = PXMpix(pxm,px,py); red1 = pix[0]; green1 = pix[1]; blue1 = pix[2]; if (! blue1) continue; if (fwhich == 1) { red2 = cimRGBmf1[0][int(red1)]; green2 = cimRGBmf1[1][int(green1)]; blue2 = cimRGBmf1[2][int(blue1)]; } else { red2 = cimRGBmf2[0][int(red1)]; green2 = cimRGBmf2[1][int(green1)]; blue2 = cimRGBmf2[2][int(blue1)]; } if (fwhich == 1) { // taper color adjustment over distance f2 = 1.0 * (py - pylo) / hh2; f1 = 1.0 - f2; } else { f1 = 1.0 * py / hh2; f2 = 1.0 - f1; } red2 = f2 * red2 + f1 * red1; green2 = f2 * green2 + f1 * green1; blue2 = f2 * blue2 + f1 * blue1; if (red2 > 255.9 || green2 > 255.9 || blue2 > 255.9) { max = red2; if (green2 > max) max = green2; if (blue2 > max) max = blue2; f1 = 255.9 / max; red2 = red2 * f1; green2 = green2 * f1; blue2 = blue2 * f1; } pix[0] = red2; pix[1] = green2; pix[2] = blue2; } return; } // find pixels of greatest contrast within overlap area // flag high-contrast pixels to use in each image compare region void cim_get_redpix(int im1) { int ww, hh, samp, xzone, yzone; int pxL, pxH, pyL, pyH; int px, py, ii, jj, npix; int ov1xlo, ov1xhi, ov1ylo, ov1yhi; int Hdist[256], Vdist[256], Hmin, Vmin; float zsamp[16] = { 5,5,5,5, 7,8,8,7, 7,8,8,7, 5,5,5,5 }; // % sample per zone, sum = 100 uchar *Hcon, *Vcon; float *pix1, *pix2; PXM *pxm; pxm = cimPXMs[im1]; ww = pxm->ww; hh = pxm->hh; if (cimRedpix) zfree(cimRedpix); // clear prior cimRedpix = (char *) zmalloc(ww*hh); memset(cimRedpix,0,ww*hh); cimRedImage = im1; // image with red pixels ov1xlo = cimOv1xlo + cimSearchRange; // stay within x/y search range ov1xhi = cimOv1xhi - cimSearchRange; // so that red pixels persist ov1ylo = cimOv1ylo + cimSearchRange; // over offset changes ov1yhi = cimOv1yhi - cimSearchRange; for (yzone = 0; yzone < 4; yzone++) // loop 16 zones for (xzone = 0; xzone < 4; xzone++) { pxL = ov1xlo + 0.25 * xzone * (ov1xhi - ov1xlo); // px and py zone limits pxH = ov1xlo + 0.25 * (xzone+1) * (ov1xhi - ov1xlo); pyL = ov1ylo + 0.25 * yzone * (ov1yhi - ov1ylo); pyH = ov1ylo + 0.25 * (yzone+1) * (ov1yhi - ov1ylo); npix = (pxH - pxL) * (pyH - pyL); // zone pixels if (npix < 1) continue; // bug fix 17.01.2 Hcon = (uchar *) zmalloc(npix); // horizontal pixel contrast 0-255 Vcon = (uchar *) zmalloc(npix); // vertical pixel contrast 0-255 ii = 4 * yzone + xzone; samp = cimSampSize * 0.01 * zsamp[ii]; // sample size for zone if (samp > 0.1 * npix) samp = 0.1 * npix; // limit to 10% of zone pixels for (py = pyL; py < pyH; py++) // scan image pixels in zone for (px = pxL; px < pxH; px++) { ii = (py-pyL) * (pxH-pxL) + (px-pxL); Hcon[ii] = Vcon[ii] = 0; // horiz. = vert. contrast = 0 if (py < 8 || py > hh-9) continue; // keep away from image edges if (px < 8 || px > ww-9) continue; pix1 = PXMpix(pxm,px,py-6); // verify not near void areas if (pix1[3] < 254) continue; // avoid void pixels pix1 = PXMpix(pxm,px+6,py); if (pix1[3] < 254) continue; pix1 = PXMpix(pxm,px,py+6); if (pix1[3] < 254) continue; pix1 = PXMpix(pxm,px-6,py); if (pix1[3] < 254) continue; pix1 = PXMpix(pxm,px,py); // candidate red pixel pix2 = PXMpix(pxm,px+2,py); // 2 pixels to right Hcon[ii] = 255 * (1.0 - PIXMATCH(pix1,pix2)); // horz. contrast 0-255 pix2 = PXMpix(pxm,px,py+2); // 2 pixels below Vcon[ii] = 255 * (1.0 - PIXMATCH(pix1,pix2)); // vert. contrast } for (ii = 0; ii < 256; ii++) // clear contrast distributions Hdist[ii] = Vdist[ii] = 0; for (py = pyL; py < pyH; py++) // scan image pixels for (px = pxL; px < pxH; px++) { // build contrast distributions ii = (py-pyL) * (pxH-pxL) + (px-pxL); ++Hdist[Hcon[ii]]; ++Vdist[Vcon[ii]]; } for (npix = 0, ii = 255; ii > 0; ii--) // find minimum contrast needed to get { // enough pixels for sample size npix += Hdist[ii]; // (horizontal contrast pixels) if (npix > samp) break; } Hmin = ii; for (npix = 0, ii = 255; ii > 0; ii--) // (vertical contrast pixels) { npix += Vdist[ii]; if (npix > samp) break; } Vmin = ii; for (py = pyL; py < pyH; py++) // scan zone pixels for (px = pxL; px < pxH; px++) { ii = (py-pyL) * (pxH-pxL) + (px-pxL); jj = py * ww + px; if (Hcon[ii] + Vcon[ii] < 20) continue; // ignore low contrast pixels if (Hcon[ii] > Hmin) cimRedpix[jj] = 1; // flag pixels above min. contrast if (Vcon[ii] > Vmin) cimRedpix[jj] = 1; } zfree(Hcon); zfree(Vcon); for (py = pyL; py < pyH; py++) // scan zone pixels for (px = pxL; px < pxH; px++) { ii = (py-pyL) * (pxH-pxL) + (px-pxL); jj = py * ww + px; if (! cimRedpix[jj]) continue; npix = cimRedpix[jj-1] + cimRedpix[jj+1]; // eliminate flagged pixels with no npix += cimRedpix[jj-ww] + cimRedpix[jj+ww]; // neighboring flagged pixels npix += cimRedpix[jj-ww-1] + cimRedpix[jj+ww-1]; npix += cimRedpix[jj-ww+1] + cimRedpix[jj+ww+1]; if (npix < 2) cimRedpix[jj] = 0; } for (py = pyL; py < pyH; py++) // scan zone pixels for (px = pxL; px < pxH; px++) { ii = (py-pyL) * (pxH-pxL) + (px-pxL); jj = py * ww + px; if (cimRedpix[jj] == 1) { // flag horizontal group of 3 cimRedpix[jj+1] = 2; cimRedpix[jj+2] = 2; cimRedpix[jj+ww] = 2; // and vertical group of 3 cimRedpix[jj+2*ww] = 2; } } } return; } // curve image based on lens mm (pano) // replaces cimPXMs[im] with curved version void cim_curve_image(int im) // overhauled { int px, py, ww, hh, vstat; float ww2, hh2; float dx, dy; float sx, sy; float tx, ty; float F = lens_mm; // lens focal length, 35mm equivalent float S = 35.0; // corresponding image width PXM *pxmin, *pxmout; float vpix[4], *pix; if (cimPanoNC) return; // no-curve flag pxmin = cimPXMs[im]; // input and output image ww = pxmin->ww; hh = pxmin->hh; ww2 = ww / 2; hh2 = hh / 2; if (ww > hh) F = F / S * ww; else F = F / S * hh; pxmout = PXM_make(ww,hh,4); // temp. output PXM for (py = 0; py < hh; py++) // cylindrical projection for (px = 0; px < ww; px++) { sx = px - ww2; sy = py - hh2; tx = sx / F; ty = sy / F; dx = F * tanf(tx); dy = F * tanf(ty) / cosf(tx); dx += ww2; dy += hh2; vstat = vpixel(pxmin,dx,dy,vpix); // input virtual pixel pix = PXMpix(pxmout,px,py); // output real pixel if (vstat) memcpy(pix,vpix,pixcc); else memset(pix,0,pixcc); // voided pixels are (0,0,0,0) } PXM_free(pxmin); // replace input with output PXM cimPXMs[im] = pxmout; cimPanoFL = F; // save focal length globally return; } // version for vertical panorama void cim_curve_Vimage(int im) { int px, py, ww, hh, vstat; float ww2, hh2; float dx, dy; float sx, sy; float tx, ty; float F = lens_mm; float S = 35.0; PXM *pxmin, *pxmout; float vpix[4], *pix; if (cimPanoNC) return; pxmin = cimPXMs[im]; ww = pxmin->ww; hh = pxmin->hh; ww2 = ww / 2; hh2 = hh / 2; if (ww > hh) F = F / S * ww; else F = F / S * hh; pxmout = PXM_make(ww,hh,4); for (py = 0; py < hh; py++) for (px = 0; px < ww; px++) { sx = px - ww2; sy = py - hh2; tx = sx / F; ty = sy / F; dy = F * tanf(ty); // these two lines dx = F * tanf(tx) / cosf(ty); // are different dx += ww2; dy += hh2; vstat = vpixel(pxmin,dx,dy,vpix); pix = PXMpix(pxmout,px,py); if (vstat) memcpy(pix,vpix,pixcc); else memset(pix,0,pixcc); // voided pixel } PXM_free(pxmin); cimPXMs[im] = pxmout; cimPanoFL = F; // save focal length globally return; } // Warp 4 image corners according to cimOffs[im].wx[ii] and .wy[ii] // corner = 0 = NW, 1 = NE, 2 = SE, 3 = SW // 4 corners move by these pixel amounts and center does not move. // input: cimPXMs[im] (flat or curved) output: cimPXMw[im] namespace cim_warp_image_names { PXM *pxmin, *pxmout; float ww, hh, wwi, hhi; float wx0, wy0, wx1, wy1, wx2, wy2, wx3, wy3; } void cim_warp_image(int im) // caller function { using namespace cim_warp_image_names; void * cim_warp_image_wthread(void *arg); pxmin = cimPXMs[im]; // input and output pixmaps pxmout = cimPXMw[im]; PXM_free(pxmout); pxmout = PXM_copy(pxmin); cimPXMw[im] = pxmout; ww = pxmin->ww; hh = pxmin->hh; wwi = 1.0 / ww; hhi = 1.0 / hh; wx0 = cimOffs[im].wx[0]; // corner warps wy0 = cimOffs[im].wy[0]; wx1 = cimOffs[im].wx[1]; wy1 = cimOffs[im].wy[1]; wx2 = cimOffs[im].wx[2]; wy2 = cimOffs[im].wy[2]; wx3 = cimOffs[im].wx[3]; wy3 = cimOffs[im].wy[3]; for (int ii = 0; ii < NWT; ii++) // start worker threads start_wthread(cim_warp_image_wthread,&Nval[ii]); wait_wthreads(); // wait for completion return; } void * cim_warp_image_wthread(void *arg) // worker thread function { using namespace cim_warp_image_names; int index = *((int *) arg); int pxm, pym, vstat; float vpix[4], *pixm; float px, py, dx, dy, coeff; for (pym = index; pym < hh; pym += NWT) // loop all pixels for this thread for (pxm = 0; pxm < ww; pxm++) { dx = dy = 0.0; coeff = (1.0 - pym * hhi - pxm * wwi); // corner 0 NW if (coeff > 0) { dx += coeff * wx0; dy += coeff * wy0; } coeff = (1.0 - pym * hhi - (ww - pxm) * wwi); // corner 1 NE if (coeff > 0) { dx += coeff * wx1; dy += coeff * wy1; } coeff = (1.0 - (hh - pym) * hhi - (ww - pxm) * wwi); // corner 2 SE if (coeff > 0) { dx += coeff * wx2; dy += coeff * wy2; } coeff = (1.0 - (hh - pym) * hhi - pxm * wwi); // corner 3 SW if (coeff > 0) { dx += coeff * wx3; dy += coeff * wy3; } px = pxm + dx; // source pixel location py = pym + dy; vstat = vpixel(pxmin,px,py,vpix); // input virtual pixel pixm = PXMpix(pxmout,pxm,pym); // output real pixel if (vstat) memcpy(pixm,vpix,pixcc); else memset(pixm,0,pixcc); // voided pixel } exit_wthread(); return 0; // not executed, avoid gcc warning } // warp image for pano, left side corners only, reduced warp range // input: cimPXMs[im] (curved) // output: cimPXMw[im] (warped) // fblend: 0 = process entire image // 1 = process left half only // 2 = process blend stripe only void cim_warp_image_pano(int im, int fblend) { int ww, hh, ww2, hh2, pxL, pxH; int pxm, pym, vstat; float vpix[4], *pixm; float ww2i, hh2i, pxs, pys, xdisp, ydisp; float wx0, wy0, wx3, wy3; PXM *pxmin, *pxmout; pxmin = cimPXMs[im]; // input and output pixmaps pxmout = cimPXMw[im]; PXM_free(pxmout); pxmout = PXM_copy(pxmin); cimPXMw[im] = pxmout; ww = pxmin->ww; hh = pxmin->hh; ww2 = ww / 2; hh2 = hh / 2; ww2i = 1.0 / ww2; hh2i = 1.0 / hh2; wx0 = cimOffs[im].wx[0]; // NW corner warp wy0 = cimOffs[im].wy[0]; wx3 = cimOffs[im].wx[3]; // SW corner warp wy3 = cimOffs[im].wy[3]; pxL = 0; // entire image pxH = ww; if (fblend == 1) // left half pxH = ww2; if (fblend == 2) { pxL = cimOv2xlo; // limit to overlap/blend width pxH = cimOv2xhi; } for (pym = 0; pym < hh; pym++) // loop all output pixels for (pxm = pxL; pxm < pxH; pxm++) { pixm = PXMpix(pxmout,pxm,pym); // output pixel xdisp = (pxm - ww2) * ww2i; // -1 ... 0 ... +1 ydisp = (pym - hh2) * hh2i; if (xdisp > 0) { // right half, no warp pxs = pxm; pys = pym; } else if (ydisp < 0) { // use NW corner warp pxs = pxm + wx0 * xdisp * ydisp; pys = pym + wy0 * xdisp * ydisp; } else { // use SW corner warp pxs = pxm + wx3 * xdisp * ydisp; pys = pym + wy3 * xdisp * ydisp; } vstat = vpixel(pxmin,pxs,pys,vpix); // input virtual pixel if (vstat) memcpy(pixm,vpix,pixcc); else memset(pixm,0,pixcc); // voided pixel } return; } // vertical pano version - warp top side corners (NW, NE) void cim_warp_image_Vpano(int im, int fblend) { int ww, hh, ww2, hh2, pyL, pyH; int pxm, pym, vstat; float vpix[4], *pixm; float ww2i, hh2i, pxs, pys, xdisp, ydisp; float wx0, wy0, wx1, wy1; PXM *pxmin, *pxmout; pxmin = cimPXMs[im]; // input and output pixmaps pxmout = cimPXMw[im]; PXM_free(pxmout); pxmout = PXM_copy(pxmin); cimPXMw[im] = pxmout; ww = pxmin->ww; hh = pxmin->hh; ww2 = ww / 2; hh2 = hh / 2; ww2i = 1.0 / ww2; hh2i = 1.0 / hh2; wx0 = cimOffs[im].wx[0]; // NW corner warp wy0 = cimOffs[im].wy[0]; wx1 = cimOffs[im].wx[1]; // NE corner warp wy1 = cimOffs[im].wy[1]; pyL = 0; // entire image pyH = hh; if (fblend == 1) // top half pyH = hh2; if (fblend == 2) { pyL = cimOv2ylo; // limit to overlap/blend width pyH = cimOv2yhi; } for (pym = pyL; pym < pyH; pym++) // loop all output pixels for (pxm = 0; pxm < ww; pxm++) { pixm = PXMpix(pxmout,pxm,pym); // output pixel xdisp = (pxm - ww2) * ww2i; // -1 ... 0 ... +1 ydisp = (pym - hh2) * hh2i; if (ydisp > 0) { // bottom half, no warp pxs = pxm; pys = pym; } else if (xdisp < 0) { // use NW corner warp pxs = pxm + wx0 * xdisp * ydisp; pys = pym + wy0 * xdisp * ydisp; } else { // use NE corner warp pxs = pxm + wx1 * xdisp * ydisp; pys = pym + wy1 * xdisp * ydisp; } vstat = vpixel(pxmin,pxs,pys,vpix); // input virtual pixel if (vstat) memcpy(pixm,vpix,pixcc); else memset(pixm,0,pixcc); // voided pixel } return; } // fine-align a pair of images im1 and im2 // cimPXMs[im2] is aligned with cimPXMs[im1] // inputs are cimOffs[im1] and cimOffs[im2] (x/y/t and corner offsets) // output is adjusted offsets and corner warp values for im2 only // (im1 is used as-is without corner warps) void cim_align_image(int im1, int im2) { int ii, corner1, cornerstep, cornerN, pass; float xyrange, xystep, trange, tstep, wrange, wstep; float xfL, xfH, yfL, yfH, tfL, tfH; float wxL, wxH, wyL, wyH; float match, matchB; cimoffs offsets0, offsetsB; Ffuncbusy = 1; offsets0 = cimOffs[im2]; // initial offsets offsetsB = offsets0; // = best offsets so far matchB = cim_match_images(im1,im2); // = best image match level for (pass = 1; pass <=2; pass++) // main pass and 2nd pass { xyrange = cimSearchRange; // x/y search range and step xystep = cimSearchStep; trange = xyrange / (cimOv1yhi - cimOv1ylo); // angle range, radians tstep = trange * xystep / xyrange; if (pass == 2) { xyrange = 0.5 * xyrange; // 2nd pass, reduce range and step xystep = 0.5 * xystep; trange = 0.5 * trange; tstep = 0.5 * tstep; } // search x/y/t range for best match xfL = cimOffs[im2].xf - xyrange; xfH = cimOffs[im2].xf + xyrange + 0.5 * xystep; yfL = cimOffs[im2].yf - xyrange; yfH = cimOffs[im2].yf + xyrange + 0.5 * xystep; tfL = cimOffs[im2].tf - trange; tfH = cimOffs[im2].tf + trange + 0.5 * tstep; for (cimOffs[im2].xf = xfL; cimOffs[im2].xf < xfH; cimOffs[im2].xf += xystep) for (cimOffs[im2].yf = yfL; cimOffs[im2].yf < yfH; cimOffs[im2].yf += xystep) for (cimOffs[im2].tf = tfL; cimOffs[im2].tf < tfH; cimOffs[im2].tf += tstep) { match = cim_match_images(im1,im2); // get match level if (sigdiff(match,matchB,0.00001) > 0) { matchB = match; // save best match offsetsB = cimOffs[im2]; } snprintf(paneltext,200,"align: %d match: %.5f",cimNsearch++,matchB); zmainloop(); } cimOffs[im2] = offsetsB; // restore best match if (cim_manualwarp) continue; // skip auto warp if (cimPano) cim_warp_image_pano(im2,1); // apply prior corner warps else if (cimPanoV) cim_warp_image_Vpano(im2,1); else cim_warp_image(im2); // warp corners and search for best match wrange = cimWarpRange; // corner warp range and step wstep = cimWarpStep; if (! wrange) continue; if (pass == 2) { // 2nd pass, 1/4 range and 1/2 step wrange = wrange / 4; wstep = wstep / 2; } corner1 = 0; // process all 4 corners cornerN = 3; cornerstep = 1; if (cimPano) { corner1 = 0; // left side corners 0, 3 cornerN = 3; cornerstep = 3; } if (cimPanoV) { corner1 = 0; // top side corners 0, 1 cornerN = 1; cornerstep = 1; } matchB = cim_match_images(im1,im2); // initial image match level for (ii = corner1; ii <= cornerN; ii += cornerstep) // modify one corner at a time { wxL = cimOffs[im2].wx[ii] - wrange; wxH = cimOffs[im2].wx[ii] + wrange + 0.5 * wstep; wyL = cimOffs[im2].wy[ii] - wrange; wyH = cimOffs[im2].wy[ii] + wrange + 0.5 * wstep; for (cimOffs[im2].wy[ii] = wyL; cimOffs[im2].wy[ii] < wyH; cimOffs[im2].wy[ii] += wstep) for (cimOffs[im2].wx[ii] = wxL; cimOffs[im2].wx[ii] < wxH; cimOffs[im2].wx[ii] += wstep) { match = cim_match_images(im1,im2); // get match level if (sigdiff(match,matchB,0.00001) > 0) { matchB = match; // save best match offsetsB = cimOffs[im2]; } snprintf(paneltext,200,"warp: %d match: %.5f",cimNsearch++,matchB); zmainloop(); } cimOffs[im2] = offsetsB; // restore best match if (cimPano) cim_warp_image_pano(im2,1); // apply corner warps else if (cimPanoV) cim_warp_image_Vpano(im2,1); else cim_warp_image(im2); } } Ffuncbusy = 0; return; } // Compare two images in overlapping areas. // Use the high-contrast pixels from cim_get_redpix() // return: 1 = perfect match, 0 = total mismatch (black/white) // cimPXMs[im1] is matched to cimPXMs[im2] + virtual corner warps float cim_match_images(int im1, int im2) { float *pix1, vpix2[4]; int ww, hh, ww2, hh2; int px1, py1, ii, vstat; float wwi, hhi, ww2i, hh2i, xdisp, ydisp; float wx0, wy0, wx1, wy1, wx2, wy2, wx3, wy3; float dx, dy, px2, py2; float x1, y1, t1, x2, y2, t2; float xoff, yoff, toff, costf, sintf, coeff; float match, cmatch, maxcmatch; PXM *pxm1, *pxm2; x1 = cimOffs[im1].xf; // im1, im2 absolute offsets y1 = cimOffs[im1].yf; t1 = cimOffs[im1].tf; x2 = cimOffs[im2].xf; y2 = cimOffs[im2].yf; t2 = cimOffs[im2].tf; xoff = (x2 - x1) * cosf(t1) + (y2 - y1) * sinf(t1); // offset of im2 relative to im1 yoff = (y2 - y1) * cosf(t1) - (x2 - x1) * sinf(t1); toff = t2 - t1; costf = cosf(toff); sintf = sinf(toff); wx0 = cimOffs[im2].wx[0]; // im2 corner warps wy0 = cimOffs[im2].wy[0]; wx1 = cimOffs[im2].wx[1]; wy1 = cimOffs[im2].wy[1]; wx2 = cimOffs[im2].wx[2]; wy2 = cimOffs[im2].wy[2]; wx3 = cimOffs[im2].wx[3]; wy3 = cimOffs[im2].wy[3]; pxm1 = cimPXMs[im1]; // base image pxm2 = cimPXMs[im2]; // comparison image (virtual warps) ww = pxm1->ww; hh = pxm1->hh; ww2 = ww / 2; hh2 = hh / 2; wwi = 1.0 / ww; hhi = 1.0 / hh; ww2i = 1.0 / ww2; hh2i = 1.0 / hh2; cmatch = 0; maxcmatch = 1; if (cimPano) { for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++) // loop overlapping pixels for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++) { ii = py1 * ww + px1; // skip low-contrast pixels if (! cimRedpix[ii]) continue; pix1 = PXMpix(pxm1,px1,py1); // image1 pixel if (pix1[3] < 254) continue; // ignore void pixels px2 = costf * (px1 - xoff) + sintf * (py1 - yoff); // corresponding image2 pixel py2 = costf * (py1 - yoff) - sintf * (px1 - xoff); dx = dy = 0.0; // corner warp xdisp = (px2 - ww2) * ww2i; // -1 ... 0 ... +1 ydisp = (py2 - hh2) * hh2i; if (xdisp > 0) // right half, no warp dx = dy = 0; else if (ydisp < 0) { // use NW corner warp dx = wx0 * xdisp * ydisp; dy = wy0 * xdisp * ydisp; } else { // use SW corner warp dx = wx3 * xdisp * ydisp; dy = wy3 * xdisp * ydisp; } px2 += dx; // source pixel location py2 += dy; // after corner warps vstat = vpixel(pxm2,px2,py2,vpix2); // get virtual pixel if (! vstat) continue; match = PIXMATCH(pix1,vpix2); // compare, brightness adjusted cmatch += match * match; // accumulate total match maxcmatch += 1.0; } } else if (cimPanoV) { for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++) // loop overlapping pixels for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++) { ii = py1 * ww + px1; // skip low-contrast pixels if (! cimRedpix[ii]) continue; pix1 = PXMpix(pxm1,px1,py1); // image1 pixel if (pix1[3] < 254) continue; // ignore void pixels px2 = costf * (px1 - xoff) + sintf * (py1 - yoff); // corresponding image2 pixel py2 = costf * (py1 - yoff) - sintf * (px1 - xoff); dx = dy = 0.0; // corner warp xdisp = (px2 - ww2) * ww2i; // -1 ... 0 ... +1 ydisp = (py2 - hh2) * hh2i; if (ydisp > 0) // bottom half, no warp dx = dy = 0; else if (xdisp < 0) { // use NW corner warp dx = wx0 * xdisp * ydisp; dy = wy0 * xdisp * ydisp; } else { // use NE corner warp dx = wx1 * xdisp * ydisp; dy = wy1 * xdisp * ydisp; } px2 += dx; // source pixel location py2 += dy; // after corner warps vstat = vpixel(pxm2,px2,py2,vpix2); if (! vstat) continue; match = PIXMATCH(pix1,vpix2); // compare brightness adjusted cmatch += match * match; // accumulate total match maxcmatch += 1.0; } } else { for (py1 = cimOv1ylo; py1 < cimOv1yhi; py1++) // loop overlapping pixels for (px1 = cimOv1xlo; px1 < cimOv1xhi; px1++) { ii = py1 * ww + px1; // skip low-contrast pixels if (! cimRedpix[ii]) continue; pix1 = PXMpix(pxm1,px1,py1); // image1 pixel if (pix1[3] < 254) continue; // ignore void pixels px2 = costf * (px1 - xoff) + sintf * (py1 - yoff); // corresponding image2 pixel py2 = costf * (py1 - yoff) - sintf * (px1 - xoff); dx = dy = 0.0; // corner warp coeff = (1.0 - py2 * hhi - px2 * wwi); // corner 0 NW if (coeff > 0) { dx += coeff * wx0; dy += coeff * wy0; } coeff = (1.0 - py2 * hhi - (ww - px2) * wwi); // corner 1 NE if (coeff > 0) { dx += coeff * wx1; dy += coeff * wy1; } coeff = (1.0 - (hh - py2) * hhi - (ww - px2) * wwi); // corner 2 SE if (coeff > 0) { dx += coeff * wx2; dy += coeff * wy2; } coeff = (1.0 - (hh - py2) * hhi - px2 * wwi); // corner 3 SW if (coeff > 0) { dx += coeff * wx3; dy += coeff * wy3; } px2 += dx; // source pixel location py2 += dy; // after corner warps vstat = vpixel(pxm2,px2,py2,vpix2); if (! vstat) continue; match = PIXMATCH(pix1,vpix2); // compare brightness adjusted cmatch += match * match; // accumulate total match maxcmatch += 1.0; } } return cmatch / maxcmatch; } // combine and show all images // used for all composite image functions except Vpano. // fnew >> make new E3 output image and adjust x and y offsets // cimPXMw[*] >> E3pxm >> main window // fblend: 0 > 50/50 blend, 1 > gradual blend // CALLED FROM THREADS as well as from main() namespace cim_show_images_names { int im1, im2, iminc, fblendd; int wwlo[10], wwhi[10]; int hhlo[10], hhhi[10]; float costf[10], sintf[10]; } void cim_show_images(int fnew, int fblend) { using namespace cim_show_images_names; void * cim_show_images_wthread(void *arg); int imx, pxr, pyr, ii, px3, py3; int ww, hh, wwmin, wwmax, hhmin, hhmax, bmid; float xf, yf, tf; float *pix3; paintlock(1); // block window updates fblendd = fblend; // blend 50/50 or gradual ramp im1 = cimShowIm1; // two images to show im2 = cimShowIm2; iminc = im2 - im1; if (cimShowAll) { // show all images im1 = 0; im2 = cimNF-1; iminc = 1; } for (imx = 0; imx < cimNF; imx++) { // pre-calculate costf[imx] = cosf(cimOffs[imx].tf); sintf[imx] = sinf(cimOffs[imx].tf); } if (fnew) PXM_free(E3pxm); // force new output pixmap if (! E3pxm) // allocate output pixmap { wwmin = hhmin = 9999; // initial values wwmax = cimPXMw[im2]->ww; hhmax = cimPXMw[im2]->hh; for (imx = im1; imx <= im2; imx += iminc) // find min and max ww and hh extents { xf = cimOffs[imx].xf; yf = cimOffs[imx].yf; tf = cimOffs[imx].tf; ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; if (xf < wwmin) wwmin = xf; if (xf - tf * hh < wwmin) wwmin = xf + tf * hh; if (xf + ww > wwmax) wwmax = xf + ww; if (xf + ww - tf * hh > wwmax) wwmax = xf + ww - tf * hh; if (yf < hhmin) hhmin = yf; if (yf + tf * ww < hhmin) hhmin = yf + tf * ww; if (yf + hh > hhmax) hhmax = yf + hh; if (yf + hh + tf * ww > hhmax) hhmax = yf + hh + tf * ww; } for (imx = im1; imx <= im2; imx += iminc) { // align to top and left edges cimOffs[imx].xf -= wwmin; cimOffs[imx].yf -= hhmin; } wwmax = wwmax - wwmin; hhmax = hhmax - hhmin; wwmin = hhmin = 0; if (cimPano) { for (imx = im1; imx <= im2; imx += iminc) // deliberate margins cimOffs[imx].yf += 10; hhmax += 20; } E3pxm = PXM_make(wwmax,hhmax,4); // allocate output image E3pxm->ww = wwmax; E3pxm->hh = hhmax; } for (imx = im1; imx <= im2; imx += iminc) // get ww range of each image { ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; tf = cimOffs[imx].tf; wwlo[imx] = cimOffs[imx].xf; wwhi[imx] = wwlo[imx] + ww; wwlo[imx] -= 0.5 * tf * hh; // use midpoint of sloping edges wwhi[imx] -= 0.5 * tf * hh; } if (cimBlend) { // blend width active for (imx = im1; imx <= im2-1; imx += iminc) // reduce for blend width { if (wwhi[imx] - wwlo[imx+1] > cimBlend) { bmid = (wwhi[imx] + wwlo[imx+1]) / 2; wwlo[imx+1] = bmid - cimBlend / 2; wwhi[imx] = bmid + cimBlend / 2; } } } for (ii = 0; ii < NWT; ii++) // start worker threads start_wthread(cim_show_images_wthread,&Nval[ii]); wait_wthreads(); // wait for completion if (cimRedpix) { imx = cimRedImage; // paint red pixels for current image ww = cimPXMw[imx]->ww; // being aligned hh = cimPXMw[imx]->hh; for (ii = 0; ii < ww * hh; ii++) { if (cimRedpix[ii]) { pyr = ii / ww; // red pixel pxr = ii - pyr * ww; px3 = cimOffs[imx].xf + pxr * costf[imx] - pyr * sintf[imx] + 0.5; py3 = cimOffs[imx].yf + pyr * costf[imx] + pxr * sintf[imx] + 0.5; if (px3 < 0 || px3 > E3pxm->ww-1) continue; // off top/bottom edge if (py3 < 0 || py3 > E3pxm->hh-1) continue; pix3 = PXMpix(E3pxm,px3,py3); pix3[0] = 255; pix3[1] = pix3[2] = 0; } } } paintlock(0); // unblock window updates Fpaint2(); // update window return; } void * cim_show_images_wthread(void *arg) // working thread { using namespace cim_show_images_names; int index = *((int *) (arg)); int imx, imy; int px3, py3; int vstat, vstat1, vstat2; float red1, green1, blue1; float red2, green2, blue2; float red3, green3, blue3, alpha3; float f1, f2, px, py; float vpix[4], *pix3; red1 = green1 = blue1 = 0; f1 = f2 = 0.5; // to use if no fblend flag for (py3 = index; py3 < E3pxm->hh; py3 += NWT) // loop E3 rows for (px3 = 0; px3 < E3pxm->ww; px3++) // loop E3 columns { vstat1 = vstat2 = 0; for (imx = imy = im1; imx <= im2; imx += iminc) // find which images overlap this pixel { if (px3 < wwlo[imx] || px3 > wwhi[imx]) continue; px = costf[imx] * (px3 - cimOffs[imx].xf) + sintf[imx] * (py3 - cimOffs[imx].yf); py = costf[imx] * (py3 - cimOffs[imx].yf) - sintf[imx] * (px3 - cimOffs[imx].xf); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (! vstat) continue; if (vpix[3] < 250) continue; // voided pixel 18.01 if (! vstat1) { // first overlapping image vstat1 = 1; imy = imx; red1 = vpix[0]; green1 = vpix[1]; blue1 = vpix[2]; } else { // second image vstat2 = 1; red2 = vpix[0]; green2 = vpix[1]; blue2 = vpix[2]; break; } } imx = imy; // first of 1 or 2 overlapping images if (vstat1) { if (! vstat2) { red3 = red1; // use image1 pixel green3 = green1; blue3 = blue1; } else { // use blended image1 + image2 pixels if (fblendd) { f1 = wwhi[imx] - px3; // gradual blend f2 = px3 - wwlo[imx+1]; f1 = f1 / (f1 + f2); f2 = 1.0 - f1; } red3 = f1 * red1 + f2 * red2; green3 = f1 * green1 + f2 * green2; blue3 = f1 * blue1 + f2 * blue2; } alpha3 = 255; } else red3 = green3 = blue3 = alpha3 = 0; // no overlapping image, pixel void pix3 = PXMpix(E3pxm,px3,py3); // output pixel pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; pix3[3] = alpha3; } exit_wthread(); return 0; // not executed, avoid gcc warning } // version for vertical panorama void cim_show_Vimages(int fnew, int fblend) { using namespace cim_show_images_names; void * cim_show_Vimages_wthread(void *arg); int imx, pxr, pyr, ii, px3, py3; int ww, hh, wwmin, wwmax, hhmin, hhmax, bmid; float xf, yf, tf; float *pix3; paintlock(1); // block window updates fblendd = fblend; // blend 50/50 or gradual ramp im1 = 0; // show all images (pano) im2 = cimNF-1; for (imx = 0; imx < cimNF; imx++) { // pre-calculate costf[imx] = cosf(cimOffs[imx].tf); sintf[imx] = sinf(cimOffs[imx].tf); } if (fnew) PXM_free(E3pxm); // force new output pixmap if (! E3pxm) // allocate output pixmap { wwmin = hhmin = 9999; wwmax = hhmax = 0; for (imx = im1; imx <= im2; imx++) // find min and max ww and hh extents { xf = cimOffs[imx].xf; yf = cimOffs[imx].yf; tf = cimOffs[imx].tf; ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; if (xf < wwmin) wwmin = xf; if (xf - tf * hh < wwmin) wwmin = xf + tf * hh; if (xf + ww > wwmax) wwmax = xf + ww; if (xf + ww - tf * hh > wwmax) wwmax = xf + ww - tf * hh; if (yf < hhmin) hhmin = yf; if (yf + tf * ww < hhmin) hhmin = yf + tf * ww; if (yf + hh > hhmax) hhmax = yf + hh; if (yf + hh + tf * ww > hhmax) hhmax = yf + hh + tf * ww; } for (imx = im1; imx <= im2; imx++) { // align to top and left edges cimOffs[imx].xf -= wwmin; cimOffs[imx].yf -= hhmin; } wwmax = wwmax - wwmin; hhmax = hhmax - hhmin; wwmin = hhmin = 0; for (imx = im1; imx <= im2; imx++) // deliberate margins cimOffs[imx].xf += 10; wwmax += 20; E3pxm = PXM_make(wwmax,hhmax,4); // allocate output image E3pxm->ww = wwmax; E3pxm->hh = hhmax; } for (imx = im1; imx <= im2; imx++) // get hh range of each image { ww = cimPXMw[imx]->ww; hh = cimPXMw[imx]->hh; tf = cimOffs[imx].tf; hhlo[imx] = cimOffs[imx].yf; hhhi[imx] = hhlo[imx] + hh; hhlo[imx] += 0.5 * tf * ww; // use midpoint of sloping edges hhhi[imx] += 0.5 * tf * ww; } if (cimBlend) { // blend width active for (imx = im1; imx <= im2-1; imx++) // reduce for blend width { if (hhhi[imx] - hhlo[imx+1] > cimBlend) { bmid = (hhhi[imx] + hhlo[imx+1]) / 2; hhlo[imx+1] = bmid - cimBlend / 2; hhhi[imx] = bmid + cimBlend / 2; } } } for (ii = 0; ii < NWT; ii++) // start worker threads start_wthread(cim_show_Vimages_wthread,&Nval[ii]); wait_wthreads(); // wait for completion if (cimRedpix) { imx = cimRedImage; // paint red pixels for current image ww = cimPXMw[imx]->ww; // being aligned hh = cimPXMw[imx]->hh; for (ii = 0; ii < ww * hh; ii++) { if (cimRedpix[ii]) { pyr = ii / ww; // red pixel pxr = ii - pyr * ww; px3 = cimOffs[imx].xf + pxr * costf[imx] - pyr * sintf[imx] + 0.5; py3 = cimOffs[imx].yf + pyr * costf[imx] + pxr * sintf[imx] + 0.5; if (px3 < 0 || px3 > E3pxm->ww-1) continue; // off left/right edge pix3 = PXMpix(E3pxm,px3,py3); pix3[0] = 255; pix3[1] = pix3[2] = 0; } } } paintlock(0); // unblock window updates Fpaint2(); // update window return; } void * cim_show_Vimages_wthread(void *arg) // working thread { using namespace cim_show_images_names; int index = *((int *) (arg)); int imx, imy; int px3, py3; int vstat, vstat1, vstat2; float red1, green1, blue1, alpha1; float red2, green2, blue2, alpha2; float red3, green3, blue3, alpha3; float f1, f2, px, py; float vpix[4], *pix3; red1 = green1 = blue1 = 0; alpha1 = 255; // alpha f1 = f2 = 0.5; // to use if no fblend flag for (py3 = index; py3 < E3pxm->hh; py3 += NWT) // loop E3 rows for (px3 = 0; px3 < E3pxm->ww; px3++) // loop E3 columns { vstat1 = vstat2 = 0; for (imx = imy = im1; imx <= im2; imx++) // find which images overlap this pixel { if (py3 < hhlo[imx] || py3 > hhhi[imx]) continue; px = costf[imx] * (px3 - cimOffs[imx].xf) + sintf[imx] * (py3 - cimOffs[imx].yf); py = costf[imx] * (py3 - cimOffs[imx].yf) - sintf[imx] * (px3 - cimOffs[imx].xf); vstat = vpixel(cimPXMw[imx],px,py,vpix); if (! vstat) continue; if (vpix[3] < 250) continue; // voided pixel 18.01 if (! vstat1) { // first overlapping image vstat1 = 1; imy = imx; red1 = vpix[0]; green1 = vpix[1]; blue1 = vpix[2]; alpha1 = vpix[3]; } else { // second image vstat2 = 1; red2 = vpix[0]; green2 = vpix[1]; blue2 = vpix[2]; alpha2 = vpix[3]; break; } } imx = imy; // first of 1 or 2 overlapping images if (vstat1) { if (! vstat2) { red3 = red1; // use image1 pixel green3 = green1; blue3 = blue1; alpha3 = alpha1; } else { // use blended image1 + image2 pixels if (fblendd) { f1 = hhhi[imx] - py3; // gradual blend f2 = py3 - hhlo[imx+1]; f1 = f1 / (f1 + f2); f2 = 1.0 - f1; } red3 = f1 * red1 + f2 * red2; green3 = f1 * green1 + f2 * green2; blue3 = f1 * blue1 + f2 * blue2; alpha3 = f1 * alpha1 + f2 * alpha2; } } else red3 = green3 = blue3 = alpha3 = 0; // no overlapping image, voided pixel pix3 = PXMpix(E3pxm,px3,py3); // output pixel pix3[0] = red3; pix3[1] = green3; pix3[2] = blue3; pix3[3] = alpha3; } exit_wthread(); return 0; // not executed, avoid gcc warning } // cut-off edges of output image where all input images do not overlap // (HDR HDF Stack) void cim_trim() { int edgex[8] = { 0, 1, 2, 2, 2, 1, 0, 0 }; // 4 corners and 4 midpoints of rectangle int edgey[8] = { 0, 0, 0, 1, 2, 2, 2, 1 }; // 0 and 2 mark corners, 1 marks midpoints int edgewx[4] = { +1, -1, -1, +1 }; int edgewy[4] = { +1, +1, -1, -1 }; int imx, ii, jj, ww, hh, px3, py3, px9, py9; int wwmin, wwmax, hhmin, hhmax; float xf, yf, tf, sintf, costf, px, py, wx, wy; float *pix3, *pix9; wwmin = hhmin = 0; wwmax = E3pxm->ww; hhmax = E3pxm->hh; for (imx = 0; imx < cimNF; imx++) // loop all images { ww = cimPXMw[imx]->ww; // image size hh = cimPXMw[imx]->hh; xf = cimOffs[imx].xf; // alignment offsets yf = cimOffs[imx].yf; tf = cimOffs[imx].tf; sintf = sinf(tf); costf = cosf(tf); for (ii = 0; ii < 8; ii++) // 8 points around image rectangle { px = ww * edgex[ii] / 2; // coordinates before warping py = hh * edgey[ii] / 2; if (edgex[ii] != 1 && edgey[ii] != 1) { // if a corner jj = ii / 2; wx = cimOffs[imx].wx[jj]; // corner warp wy = cimOffs[imx].wy[jj]; if (edgewx[jj] > 0 && wx < 0) px -= wx; // if warp direction inwards, if (edgewx[jj] < 0 && wx > 0) px -= wx; // reduce px/py by warp if (edgewy[jj] > 0 && wy < 0) py -= wy; if (edgewy[jj] < 0 && wy > 0) py -= wy; } px3 = xf + px * costf - py * sintf; // map px/py to output image px3/py3 py3 = yf + py * costf + px * sintf; if (edgex[ii] != 1) { if (px3 < ww/2 && px3 > wwmin) wwmin = px3; // remember px3/py3 extremes if (px3 > ww/2 && px3 < wwmax) wwmax = px3; } if (edgey[ii] != 1) { if (py3 < hh/2 && py3 > hhmin) hhmin = py3; if (py3 > hh/2 && py3 < hhmax) hhmax = py3; } } } wwmin += 2; // compensate rounding wwmax -= 2; hhmin += 2; hhmax -= 2; ww = wwmax - wwmin; // new image size hh = hhmax - hhmin; if (ww < 0.7 * E3pxm->ww) return; // sanity check if (hh < 0.7 * E3pxm->hh) return; E9pxm = PXM_make(ww,hh,4); for (py3 = hhmin; py3 < hhmax; py3++) // E9 = trimmed E3 for (px3 = wwmin; px3 < wwmax; px3++) { px9 = px3 - wwmin; py9 = py3 - hhmin; pix3 = PXMpix(E3pxm,px3,py3); pix9 = PXMpix(E9pxm,px9,py9); memcpy(pix9,pix3,pixcc); } PXM_free(E3pxm); // E3 = E9 E3pxm = E9pxm; E9pxm = 0; E3pxm->ww = ww; E3pxm->hh = hh; return; } // cut off excess margins (panorama) void cim_trim_margins() // 17.08 { int ww, hh; int xlo, xhi, ylo, yhi; int px, py; float *pix, *pix1, *pix2; PXM *pxmout; ww = E3pxm->ww; hh = E3pxm->hh; xlo = ww; xhi = 0; ylo = hh; yhi = 0; for (py = 0; py < hh; py += 2) // find extent of empty margins for (px = 0; px < ww; px += 2) { pix = PXMpix(E3pxm,px,py); if (pix[3] == 0) continue; if (px < xlo) xlo = px; if (px > xhi) xhi = px; if (py < ylo) ylo = py; if (py > yhi) yhi = py; } ww = xhi - xlo; // make new image just large enough hh = yhi - ylo; pxmout = PXM_make(ww,hh,4); if (! pxmout) return; for (py = 0; py < hh; py++) // copy pixels into new image for (px = 0; px < ww; px++) { pix1 = PXMpix(E3pxm,px+xlo,py+ylo); pix2 = PXMpix(pxmout,px,py); memcpy(pix2,pix1,pixcc); } PXM_free(E3pxm); // replace input with output PXM E3pxm = pxmout; return; } // dump offsets to stdout - diagnostic tool void cim_dump_offsets(cchar *label) { printz("\n offsets: %s \n",label); for (int imx = 0; imx < cimNF; imx++) { printz(" imx %d x/y/t: %.1f %.1f %.4f w0: %.1f %.1f w1: %.1f %.1f w2: %.1f %.1f w3: %.1f %.1f \n", imx, cimOffs[imx].xf, cimOffs[imx].yf, cimOffs[imx].tf, cimOffs[imx].wx[0], cimOffs[imx].wy[0], cimOffs[imx].wx[1], cimOffs[imx].wy[1], cimOffs[imx].wx[2], cimOffs[imx].wy[2], cimOffs[imx].wx[3], cimOffs[imx].wy[3]); } return; } // uncurve the combined panorama image // input F = 0.0 - 1.0 = no unbend - full unbend void cim_flatten_image(float F) // scaled range 17.08 { PXM *pxmout; int ww, hh, vstat; float ww2, hh2; int px, py, rx, ry; float dx, dy; float sx, sy; float tx, ty; float *pix, vpix[4]; float *pix1, *pix2; // F = 0.0 0.2 0.4 0.6 0.8 1.0 F = 1.0 - F; // 1.0 0.8 0.6 0.4 0.2 0.0 F = F * 3.0; // 3.0 2.4 1.8 1.2 0.6 0.0 F = pow(2,F); // 8.0 5.3 3.5 1.7 1.5 1.0 F = F * cimPanoFL; // 8*FL (min flat) to 1*FL (max flat) ww = E3pxm->ww; hh = E3pxm->hh; pxmout = PXM_make(1.4*ww,1.2*hh,4); // larger image space if (! pxmout) return; // too big, do nothing for (ry = 0; ry < hh; ry++) // copy image to middle for (rx = 0; rx < ww; rx++) { px = rx + 0.2 * ww; py = ry + 0.1 * hh; pix1 = PXMpix(E3pxm,rx,ry); pix2 = PXMpix(pxmout,px,py); memcpy(pix2,pix1,pixcc); } PXM_free(E3pxm); E3pxm = pxmout; // new image with bigger margins ww = E3pxm->ww; hh = E3pxm->hh; pxmout = PXM_make(ww,hh,4); // flattened image space if (! pxmout) return; ww2 = ww/2; hh2 = hh/2; for (py = 0; py < hh; py++) // copy and flatten image for (px = 0; px < ww; px++) { dx = px - ww2; dy = py - hh2; tx = atanf(dx / F); ty = atanf(dy / F * cosf(tx)); sx = F * tx; sy = F * ty; sy += hh2; sx += ww2; vstat = vpixel(E3pxm,sx,sy,vpix); // input virtual pixel pix = PXMpix(pxmout,px,py); // output real pixel if (vstat) memcpy(pix,vpix,pixcc); else memset(pix,0,pixcc); // voided pixel } PXM_free(E3pxm); // replace input with output PXM E3pxm = pxmout; cim_trim_margins(); // trim excess margins return; } // version for vertical panorama void cim_flatten_Vimage(float F) // 17.08 { PXM *pxmout; int ww, hh, vstat; float ww2, hh2; int px, py, rx, ry; float dx, dy; float sx, sy; float tx, ty; float *pix, vpix[4]; float *pix1, *pix2; // F = 0.0 0.2 0.4 0.6 0.8 1.0 F = 1.0 - F; // 1.0 0.8 0.6 0.4 0.2 0.0 F = F * 3.0; // 3.0 2.4 1.8 1.2 0.6 0.0 F = pow(2,F); // 8.0 5.3 3.5 1.7 1.5 1.0 F = F * cimPanoFL; // 8*FL (min flat) to 1*FL (max flat) ww = E3pxm->ww; hh = E3pxm->hh; pxmout = PXM_make(1.2*ww,1.4*hh,4); // larger image space if (! pxmout) return; // too big, do nothing for (ry = 0; ry < hh; ry++) // copy image to middle for (rx = 0; rx < ww; rx++) { px = rx + 0.1 * ww; py = ry + 0.2 * hh; pix1 = PXMpix(E3pxm,rx,ry); pix2 = PXMpix(pxmout,px,py); memcpy(pix2,pix1,pixcc); } PXM_free(E3pxm); E3pxm = pxmout; // new image with bigger margins ww = E3pxm->ww; hh = E3pxm->hh; pxmout = PXM_make(ww,hh,4); // flattened image space if (! pxmout) return; ww2 = ww/2; hh2 = hh/2; for (py = 0; py < hh; py++) // copy and flatten image for (px = 0; px < ww; px++) { dy = py - hh2; dx = px - ww2; ty = atanf(dy / F); tx = atanf(dx / F * cosf(ty)); sy = F * ty; sx = F * tx; sy += hh2; sx += ww2; vstat = vpixel(E3pxm,sx,sy,vpix); // input virtual pixel pix = PXMpix(pxmout,px,py); // output real pixel if (vstat) memcpy(pix,vpix,pixcc); else memset(pix,0,pixcc); // voided pixel } PXM_free(E3pxm); // replace input with output PXM E3pxm = pxmout; cim_trim_margins(); // trim excess margins return; } fotoxx-18.01.1/fotoxx.h0000644000175000017500000032655313222767271013400 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. *********************************************************************************/ #include #include #include #include #include "libraw/libraw.h" #include #include #include #include "zfuncs.h" // Fotoxx definitions #define Frelease "fotoxx-18.01.1" // Fotoxx release version #define Flicense "Free software - GNU General Public License v.3" #define Fhomepage "https://kornelix.net" #define Fcontact "kornelix@posteo.de" #define Ftranslators "Translators: \n" \ " Doriano Blengino, André Campos Rodovalho, \n" \ " J. A. Miralles Puignau, Xavier Ribes " #define MEGA (1024 * 1024) // 1 million as 2**20 #define PI 3.141592654 #define RAD (180.0/PI) #define PXMpix(PXM,px,py) (PXM->pixels+((py)*(PXM->ww)+(px))*(PXM->nc)) // PXM and PXB pixel (RGB/RGBA nc = 3/4) #define PXBpix(PXB,px,py) (PXB->pixels+((py)*(PXB->rs)+(px)*(PXB->nc))) // e.g. red = PXMpix(...)[0] #define pixbright(pix) (0.25*(pix)[0]+0.65*(pix)[1]+0.10*(pix)[2]) // overall brightness 0-255 // color match function for integer/float RGB colors 0-255 // returns 0.0 to 1.0 = perfect match #define RGBMATCH(r1,g1,b1,r2,g2,b2) \ 0.0000000596 * (256.0 - fabsf(r1-r2)) * (256.0 - fabsf(g1-g2)) * (256.0 - fabsf(b1-b2)) #define PIXMATCH(pix1,pix2) \ (RGBMATCH(pix1[0],pix1[1],pix1[2],pix2[0],pix2[1],pix2[2])) // compile time limits that could be increased #define thumbfilesize 512 // thumbnail file pixel size 17.08 #define wwhh_limit1 30000 // max. image width or height 18.01 #define wwhh_limit2 (256*MEGA) // max. image width x height (4 GB) 18.01 #define max_threads 8 // max. threads (hyperthreads useless) 18.01 #define maxtopdirks 200 // max. no. of top image directories #define indexrecl 2000 // max. image index rec. (>XFCC >tagFcc) #define maximages 1000000 // max. image files supported #define maxtagcats 200 // max tag categories #define tagcc 50 // max cc for one tag or category ID #define tagFcc 1000 // max tag cc for one image file #define maxtags 100000 // max tags and tags/category 18.01 #define tagGcc 200000 // max tag cc for one category #define tagMcc 1000 // max tag cc for batch add tags #define tagScc 500 // max tag cc for search tags #define tagRcc 300 // max tag cc for recent tags #define GSmax 100000 // max files for gallery_select() #define Mxmeta 20 // max indexed metadata keys 18.01 // EXIF/IPTC keys for embedded image metadata #define iptc_keywords_key "Keywords" // keywords (tags) #define iptc_rating_key "Rating" // star rating #define exif_size_key "ImageSize" // image size, NNNNxNNNN #define exif_date_key "DateTimeOriginal" // date/time #define exif_width_key "ImageWidth" // width, pixels #define exif_height_key "ImageHeight" // height, pixels #define exif_orientation_key "Orientation" // orientation #define exif_rollangle_key "RollAngle" // roll angle - minor level error #define exif_editlog_key "ImageHistory" // edit history log #define exif_comment_key "Comment" // image comment #define exif_usercomment_key "UserComment" // image comment #define iptc_caption_key "Caption-Abstract" // image caption #define exif_copyright_key "Copyright" // image copyright #define exif_focal_length_key "FocalLengthIn35mmFormat" // focal length #define exif_city_key "City" // city/location name (geotags) #define exif_country_key "Country" // country name #define exif_lati_key "GPSLatitude" // latitude in degrees (-180 to +180) #define exif_longi_key "GPSLongitude" // longitude in degrees (-180 to +180) #define exif_colorprof1_key "ICCProfileName" // ICC color profile name, e.g. "sRGB" #define exif_colorprof2_key "ICC_Profile" // embedded color profile data #define exif_maxcc 2000 // max. cc for exif/iptc text // GTK/GDK shortcuts #define TEXTWIN GTK_TEXT_WINDOW_TEXT #define NODITHER GDK_RGB_DITHER_NONE #define ALWAYS GTK_POLICY_ALWAYS #define NEVER GTK_POLICY_NEVER #define GDKRGB GDK_COLORSPACE_RGB #define LINEATTRIBUTES GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER #define BILINEAR GDK_INTERP_BILINEAR #define NEAREST GDK_INTERP_NEAREST #define SCROLLWIN GTK_SCROLLED_WINDOW #define VERTICAL GTK_ORIENTATION_VERTICAL #define HORIZONTAL GTK_ORIENTATION_HORIZONTAL #define MWIN GTK_WINDOW(Mwin) // externals from zfuncs module namespace zfuncs { extern GdkDisplay *display; // X11 workstation (KB, mouse, screen) extern GdkDeviceManager *manager; // knows screen / mouse associations extern GdkScreen *screen; // monitor (screen) extern GdkDevice *mouse; // pointer device extern GtkSettings *settings; // screen settings extern timeb startime; // fotoxx startup time 17.08 extern int monitor_ww, monitor_hh; // monitor pixel dimensions extern int appfontsize; // app font size extern char zappname[20]; // app name/version extern char zlang[8]; // current language lc_RC extern char zicondir[200]; // where application icons live extern char zimagedir[200]; // where application image files live 18.01 extern int Nmalloc, Nstrdup, Nfree; // malloc() strdup() free() counters extern int vmenuclickposn; // vert. menu icon click position, 0-100 extern int vmenuclickbutton; // "" button: 1/3 = left/right mouse extern int zdialog_count; // zdialog count (new - free) extern int zdialog_busy; // open zdialogs (run - destroy) extern zdialog *zdialog_list[zdialog_max]; // active zdialog list 18.01 extern int open_popup_windows; // open popup window count extern pthread_t tid_main; // main() thread ID } enum FTYPE { FNF, FDIR, IMAGE, RAW, VIDEO, THUMB, OTHER }; // file types, FNF = file not found enum GTYPE { TNONE, GDIR, SEARCH, META, RECENT, NEWEST, ALBUM }; // gallery types enum GSORT { SNONE, FNAME, FDATE, PDATE }; // gallery sort data enum GSEQ { QNONE, ASCEND, DESCEND }; // gallery sort sequence typedef struct { // current gallery file list in memory char *file; // /directory.../filename char fdate[16]; // file date: yyyymmddhhmmss char pdate[16]; // photo date: yyyymmddhhmmss int mdindex; // index to metadata list Gmdlist[*] } GFlist_t; // externals from f.gallery namespace navi { // gallery() data extern GFlist_t *GFlist; // gallery file data table extern char **Gmdlist; // corresp. metadata list extern int Gmdrows; // text rows in metadata list extern int Nfiles; // gallery file count (images + subdirs) extern int Nsubdirs; // gallery subdirectory count extern int Nimages; // gallery image file count extern char *galleryname; // directory path or gallery type name extern GTYPE gallerytype; // gallery type: directory, recent, etc. extern GSORT gallerysort; // filename/file-date/photo-date extern GSEQ galleryseq; // ascending/descending extern int gallerypainted; // gallery paint is complete extern int galleryposn; // gallery target scroll position extern int scrollposn; // current scroll position extern int xwinW, xwinH; // image gallery window size extern int thumbsize; // curr. thumbnail display size extern int genthumbs; // counts thumbnails generated extern double thumbcache_MB; // thumbnail cache size, MB extern int Fpreload_thumbs_block; // block thumbnail preload thread int gallery_paint(GtkWidget *, cairo_t *); // gallery window paint function void menufuncx(GtkWidget *, cchar *menu); // gallery menu buttons function void changedirk(GtkWidget *widget, GdkEventButton *event); // gallery directory change function void newtop(GtkWidget *widget, GdkEventButton *event); // gallery change top directory function void newalbum(GtkWidget *widget, GdkEventButton *event); // gallery change album function void mouse_event(GtkWidget *, GdkEvent *, void *); // gallery window mouse event function void gallery_sort(); // gallery choose sort order and sort char * gallery_dragfile(); // gallery window file drag function void gallery_dropfile(int mx, int my, char *file); // gallery window file drop function int KBaction(cchar *action); // gallery window KB key function } EX int GScount; // gallery_select(), file count EX char *GSfiles[GSmax]; // gallery_select(), selected files // GTK etc. parameters EX GtkWidget *Mwin, *MWvbox; // main window and vbox EX GtkWidget *Fhbox, *Fmenu, *Fvbox, *Fpanel, *Fpanlab, *Fdrawin; // F window widgets, file view EX GtkWidget *Ghbox, *Gmenu, *Gvbox, *Gsep, *Gpanel, *Gtop, *Galbum; // G window widgets, gallery view EX GtkWidget *Gscroll, *Gdrawin; EX GtkAdjustment *Gadjust; EX GtkWidget *Whbox, *Wmenu, *Wvbox, *Wdrawin; // W window widgets, world map view EX GtkWidget *Mhbox, *Mmenu, *Mvbox; // M window widgets, net map view EX GtkWidget *Cdrawin; // curr. drawing window, Fdrawin/Wdrawin EX GdkWindow *gdkwin; // corresp. GDK window EX PIXBUF *BGpixbuf; // window background pixbuf EX GdkCursor *arrowcursor; // main window cursors EX GdkCursor *dragcursor; EX GdkCursor *drawcursor; EX GdkCursor *blankcursor; EX uint8 BLACK[3], WHITE[3], RED[3], GREEN[3], BLUE[3]; // RGB values 0-255 EX uint8 *LINE_COLOR; // line drawing color, one of the above EX draw_context_t draw_context; // GDK window drawing context 17.04 // user settings EX char *menu_style; // menu style: icons/text/both EX int FBrgb[3]; // F view background color, RGB 0-255 EX int GBrgb[3]; // G view background color, RGB 0-255 EX int MFrgb[3]; // menu font color, RGB 0-255 18.01 EX int MBrgb[3]; // menu background color, RGB 0-255 18.01 EX int iconsize; // menu icon size EX char *dialog_font; // dialog font e.g. "sans 10" EX char *startalbum; // start album: file name 17.04 EX char *startdisplay; // start display: recent/prev/blank/file/dirk EX char *startdirk; // start directory (startdisplay=dirk) EX char *startfile; // start image file (startdisplay=file) EX int Fdragopt; // 1/2 = 1x drag / magnified scroll EX int Fshowhidden; // show hidden directories in gallery view EX int Flastversion; // prev/next button gets last versions only EX int Fshiftright; // shift image to right margin EX int zoomcount; // zoom count to reach 2x (1-8) EX float zoomratio; // corresp. zoom ratio: 2**(1/zoomcount) EX int map_dotsize; // map dot size / mouse capture size EX char *RAWfiletypes; // recognized RAW files: .raw .rw2 ... EX char *myRAWtypes; // RAW types encountered EX char *VIDEOfiletypes; // recognized video files: .mov .mp4 ... EX char *myVIDEOtypes; // VIDEO types encountered #define CCC (XFCC*2+200) // max. command line size EX char command[CCC]; // (command, parameters, 2 filespecs) typedef void menufunc_t(GtkWidget *,cchar *); // table of menu names and functions typedef struct { GtkWidget *topmenu; // parent menu cchar *menu; // menu text cchar *mtran; // translation cchar *icon; // menu icon cchar *desc; // menu short description (tooltip) cchar *dtran; // translation menufunc_t *func; // menu function (GtkWidget *, cchar *) cchar *arg; // argument } menutab_t; #define maxmenus 250 EX menutab_t menutab[maxmenus]; EX int Nmenus; #define maxshortcuts 100 EX char *shortcutkey[50]; // KB shortcut keys (or combinations) EX char *shortcutmenu[50]; // corresponding menu names EX int Nshortcuts; // count of entries // general parameters EX char *Prelease; // prior fotoxx version EX cchar *Arelease; // "fotoxx-NN.N" or "*-appimage" 17.04 EX char *appimage; // appimage executable location or null 17.04 EX int Ffirsttime; // first time startup EX int sessioncount; // session counter 18.01 EX int mwgeom[4]; // main window position and size EX char PIDstring[8]; // process PID as string EX int NWT; // working threads to use EX int wthreads_busy; // working threads running EX int Nval[100]; // static integer values 0-99 EX char FGWM; // curr. view mode: 'F' 'G' 'W' 'M' EX char PFGWM; // prior view mode 17.08 EX int Frawtherapee; // flag, Raw Therapee available EX int Fgrowisofs; // flag, growisofs available EX int PTtools; // flag, pano tools available EX int Fvideo; // flag, ffmpeg available 17.08 EX int Fshutdown; // app shutdown underway EX int Fdebug; // debug flag EX int Findexlev; // 0/1/2 = none/old/old+new image files EX int FMindexlev; // Findexlev if start via file manager EX int Pindexlev; // Findexlev if command parameter EX int Fkillfunc; // flag, running function should quit EX int Fpaintlock; // gtimefunc() request from thread: EX int Fpaintunlock; // freeze or thaw image window EX int Fmetamod; // image metadata unsaved changes EX int Fblock; // edit functions mutex flag EX int Ffuncbusy; // function is busy/working EX int Fthreadbusy; // thread is busy/working EX int Ffullscreen; // flag, window is fullscreen EX int Fpanelshow; // show or hide F view top panel EX int Fpaintrequest; // window paint request pending EX int Fimagerefresh; // window image refresh needed EX int Fblowup; // zoom small images to window size EX int Fmashup; // flag, mashup function is active EX int Fslideshow; // flag, slide show is active EX int Fview360; // flag, view360 function is active 18.01 EX int ss_escape; // KB escape >> slide show process EX char *ss_KBkeys; // slide show KB keys packed 18.01 EX int Frecent; // start with recent files gallery EX int Fnew; // start with newly added files gallery EX int Fprev; // start with previous file EX int Fblank; // start with blank window EX char *topmenu; // latest top-menu selection EX char *commandmenu; // command line, startup menu function EX char *commandalbum; // command line, startup album gallery EX char file_errmess[1000]; // error message from low-level file funcs EX cchar *F1_help_topic; // current function help topic EX int Fcaptions; // show image captions/comments EX int Fscriptbuild; // edit script build is in-progress EX char *netmap_source; // net map source EX char *mapbox_access_key; // mapbox map source access key EX char tempdir[100]; // directory for temp files /tmp/fotoxx... EX char *topdirks[maxtopdirks]; // user top-level image directories EX int Ntopdirks; // topdirk[] count EX char *thumbdirk; // thumbnails directory EX char *xmeta_keys[Mxmeta]; // indexed metadata names (compressed) 18.01 EX int xmeta_changed; // indexed metadata names changed 18.01 EX char *initial_file; // initial file on command line 17.08 EX char *curr_file, *curr_dirk; // current image file and directory EX char curr_file_type[8]; // jpg / tif / png / other EX int curr_file_bpc; // image file bits/color, 8/16 EX int curr_file_size; // image file size on disk EX int curr_file_posn; // position in current gallery, 0-last EX int last_file_posn; // remember when curr. file deleted EX int curr_file_count; // count of images in current set EX char *clicked_file; // image file / thumbnail clicked EX int clicked_posn; // clicked gallery position (Nth) EX int clicked_width; // clicked thumbnail position EX int clicked_height; // (normalized 0-100) EX char *rawfile; // RAW file passed to raw edit program EX char *imagefiletypes; // supported image file types, .jpg .png etc. EX char *last_curr_file; // curr_file in last session EX char *last_galleryname; // galleryname in last session EX GTYPE last_gallerytype; // gallerytype in last session EX char *colormapfile; // curr. printer color map file EX char *copymove_loc; // copy/move directory last used EX char *color_palette_file; // user's color palette image file 17.04 EX char f_load_type[8]; // data set by f_open() EX int f_load_bpc, f_load_size; EX char *f_save_file; // data set by f_save() EX char f_save_type[8]; EX int f_save_bpc; EX uint f_save_size; EX VOL double Fbusy_goal, Fbusy_done; // BUSY message progress tracking EX char paneltext[200]; // top panel application text // files and directories in /home//.fotoxx/ EX char index_dirk[200]; // image index directory EX char tags_defined_file[200]; // tags defined file EX char recentfiles_file[200]; // file of recent image files EX char albums_dirk[200]; // directory for saved image albums EX char gallerymem_file[200]; // file for recent galleries memory 17.08 EX char saved_areas_dirk[200]; // directory for saved select area files EX char saved_curves_dirk[200]; // directory for saved curve data EX char retouch_combo_dirk[200]; // directory for saved retouch combo settings EX char custom_kernel_dirk[200]; // directory for saved custom kernel data EX char addtext_dirk[200]; // directory for m_add_text files EX char addline_dirk[200]; // directory for m_add_lines files EX char favorites_dirk[200]; // directory for favorites menu EX char mashup_dirk[200]; // directory for mashup projects EX char KBshortcuts[200]; // keyboard shortcuts file EX char quickstart_file[200]; // quick start image file EX char slideshow_dirk[200]; // directory for slide show files EX char slideshow_trans_dirk[200]; // directory for slide show transition files EX char pattern_dirk[200]; // directory for pattern files EX char printer_color_dirk[200]; // directory for printer calibration EX char edit_scripts_dirk[200]; // directory for edit script files EX char searchresults_file[200]; // file for search results EX char maps_dirk[200]; // directory for fotoxx-maps files EX char user_maps_dirk[200]; // directory for user-added map files EX char montage_maps_dirk[200]; // directory for montage map files 17.04 // fotoxx PXM and PXB pixmaps typedef struct { // PXM pixmap, 3 x float per pixel char wmi[8]; // self-identifier int ww, hh; // width, height int nc; // channels (3 or more) float *pixels; // ww * hh * nc * sizeof(float) } PXM; typedef struct { // PXB pixmap, 3 x uint8/pixel char wmi[8]; // self-identifier PIXBUF *pixbuf; // underlying pixbuf image int ww, hh; // width, height, rowstride, channels int rs, nc; // rowstride, channels (3/4) uint8 *pixels; // hh * rs bytes (in pixbuf) } PXB; // parameters for F/W image window painting (functions Fpaint() etc.) typedef struct { PXB *fpxb; // image PXB, size = 1x PXB *mpxb; // image PXB, size = Mscale int morgx, morgy; // Dpxb origin in Mpxb image int dorgx, dorgy; // Dpxb origin in drawing window float fzoom; // image zoom scale (0 = fit window) float mscale; // scale factor, 1x image to window image float pscale; // prior scale factor } FWstate; EX FWstate Fstate, Wstate; // parameters for F and W windows EX FWstate *Cstate; // current parameters, F or W #define Fpxb Fstate.fpxb // F window parameter equivalents #define Mpxb Fstate.mpxb #define Morgx Fstate.morgx #define Morgy Fstate.morgy #define Dorgx Fstate.dorgx #define Dorgy Fstate.dorgy #define Fzoom Fstate.fzoom #define Mscale Fstate.mscale #define Pscale Fstate.pscale EX PXM *E0pxm; // edit pixmap, original image EX PXM *E1pxm; // edit pixmap, base image for editing EX PXM *E3pxm; // edit pixmap, edited image EX PXM *ERpxm; // edit pixmap, undo/redo image EX PXM *E8pxm; // scratch image for some functions EX PXM *E9pxm; // scratch image for some functions EX int Dww, Dhh; // main/drawing window size EX int dww, dhh; // Dpxb size in drawing window, <= Dww, Dhh EX int zoomx, zoomy; // req. zoom center of window EX int Mbutton; // mouse button, 1/3 = left/right EX int Mwxposn, Mwyposn; // mouse position, window space EX int Mxposn, Myposn; // mouse position, image space EX int LMclick, RMclick; // mouse left, right click EX int Mxclick, Myclick; // mouse click position, image space EX int Mdrag; // mouse drag underway EX int mouse_dragtime; // current drag duration, milliseconds EX double wacom_pressure; // Wacom tablet, stylus pressure 0.0 to 1.0 EX int Mwdragx, Mwdragy; // drag increment, window space EX int Mxdown, Mydown, Mxdrag, Mydrag; // mouse drag vector, image space EX int Fmousemain; // mouse acts on main window not dialog EX int Mcapture; // mouse captured by edit function EX int KBcapture; // KB key captured by edit function EX int KBkey; // active keyboard key EX int KBcontrolkey; // keyboard key states, 1 = down EX int KBshiftkey; EX int KBaltkey; // lines, text and circles drawn over window image whenever repainted struct topline_t { int x1, y1, x2, y2; // endpoint coordinates in image space int type; // 1/2 = solid/dotted }; #define maxtoplines 8 // max. top lines EX topline_t toplines[8], ptoplines[8]; // top lines, prior top lines EX int Ntoplines, Nptoplines; // current counts struct toptext_t { int ID, px, py; cchar *text; cchar *font; }; #define maxtoptext 100 // max. text strings EX toptext_t toptext[100]; // text strings EX int Ntoptext; // current count struct topcircle_t { int px, py; int radius; }; #define maxtopcircles 100 // max. circles EX topcircle_t topcircles[100]; // circles EX int Ntopcircles; // current count // spline curve data typedef void spcfunc_t(int spc); // callback function, spline curve edit EX float splcurve_minx; // min. anchor point dist, % scale typedef struct { // spline curve data GtkWidget *drawarea; // drawing area for spline curves spcfunc_t *spcfunc; // callback function when curve changed int Nspc; // number of curves, 1-10 int fact[10]; // curve is active int vert[10]; // curve is vert. (1) or horz. (0) int nap[10]; // anchor points per curve float apx[10][50], apy[10][50]; // up to 50 anchor points per curve float yval[10][1000]; // y-values for x = 0 to 1 by 0.001 int Nscale; // no. of fixed scale lines, 0-10 float xscale[2][10]; // 2 x-values for end points float yscale[2][10]; // 2 y-values for end points } spldat; // select area data #define sa_initseq 10 // initial sequence number (0/1/2 reserved) #define sa_maxseq 9999 // ultimate limit 64K #define mode_rect 1 // select rectangle by drag/click #define mode_ellipse 2 // select ellipse by drag #define mode_draw 3 // freehand draw by drag/click #define mode_follow 4 // follow edge indicated by clicks #define mode_replace 5 // adjust edge by dragging mouse #define mode_mouse 6 // select area within mouse (radius) #define mode_onecolor 7 // select one matching color within mouse #define mode_allcolors 8 // select all matching colors within mouse #define mode_image 9 // select whole image EX VOL int sa_stat; // 0/1/2/3/4 = none/edit/xx/fini/disab EX int sa_mode; // 1-7 = curr. select area edit method EX uint16 sa_endpx[10000], sa_endpy[10000]; // last pixel drawn per seqence no. EX int sa_thresh; // mouse pixel distance threshold EX int sa_mouseradius; // mouse selection radius EX int sa_searchrange; // search range (* mouse radius) EX int sa_mousex, sa_mousey; // mouse position in image EX float sa_colormatch; // color range to match (0.001 to 1.0) EX int sa_calced; // edge calculation done EX int sa_blend; // edge blend width EX int sa_minx, sa_maxx; // enclosing rectangle for area EX int sa_miny, sa_maxy; EX char *sa_stackdirec; // pixel search stack EX int *sa_stackii; EX int sa_maxstack; EX int sa_Nstack; EX int sa_currseq; // current select sequence no. EX int sa_Ncurrseq; // current sequence pixel count EX char *sa_pixselc; // maps pixels selected in current cycle EX uint16 *sa_pixmap; // 0/1/2+ = outside/edge/inside edge dist EX uint sa_Npixel; // total select area pixel count EX uint8 *sa_hairy_alpha; // m_select_hairy() transparency poop EX int sa_fww, sa_fhh; // valid image dimensions for select area EX int Fshowarea; // show select area outline EX int areanumber; // increasing sequential number // grid lines parameters, for each grid lines set [G] #define GON 0 // gridsettings[G][0] grid lines on/off #define GX 1 // [G][1] x-lines on/off #define GY 2 // [G][2] y-lines on/off #define GXS 3 // [G][3] x-lines spacing #define GYS 4 // [G][4] y-lines spacing #define GXC 5 // [G][5] x-lines count #define GYC 6 // [G][6] y-lines count #define GXF 7 // [G][7] x-lines offset #define GYF 8 // [G][8] y-lines offset #define G9 9 // [G][9] unused EX int currgrid; // current grid params set, 0-5 EX int gridsettings[6][10]; // settings for 6 sets of grid lines // Image index record. // pdate, size, tags, capt, comms, gtags may have "null" (char) as a missing value. struct xxrec_t { char *file; // image filespec char fdate[16]; // file date, yyyymmddhhmmss char pdate[16]; // image (photo) date, yyyymmddhhmmss char size[16]; // image size, "12345x9876" char rating[8]; // IPTC rating, "0" to "5" stars char *tags; // IPTC tags char *capt; // IPTC caption char *comms; // EXIF comments char *location; // city, park, monument ... char *country; // country float flati, flongi; // earth coordinates char *xmeta; // indexed metadata 18.01 }; EX xxrec_t **xxrec_tab; // image index table, file sequence EX int Nxxrec; // count, < maximages EX int Findexvalid; // 0/1/2 = no / yes:old / yes:old+new // image edit function data typedef struct { // edit function data cchar *menuname; // menu name in menutab[*] cchar *funcname; // function name, e.g. flatten (< 32 chars) int FprevReq; // request to use smaller image if poss. int Farea; // area: 0/1/2 = delete/ignore/useable int Fmods; // flag, image modifications by this func int Fpreview; // flag, using smaller image for edits int Frestart; // flag, OK to restart with new file int FusePL; // flag, OK to use with paint/lever edits int Fscript; // flag, function can be scripted int Fsaved; // current mods are saved to disk zdialog *zd; // edit dialog spldat *curves; // edit curve data void (*menufunc)(GtkWidget *, cchar *); // edit menu function in menutab[*] void * (*threadfunc)(void *); // edit thread function void (*mousefunc)(); // edit mouse function int thread_command, thread_status; int thread_pend, thread_done, thread_hiwater; } editfunc; EX editfunc *CEF; // current active edit function // undo/redo stack data #define maxedits 100 // undo/redo stack size EX char URS_filename[100]; // stack image filename template EX int URS_pos; // stack position, 0-99 EX int URS_max; // stack max. position, 0-99 EX char URS_funcs[100][32]; // corresp. edit func. names EX int URS_saved[100]; // corresp. edit saved to disk or not EX int URS_reopen_pos; // reopen last saved file, stack position struct textattr_t { // attributes for gentext() function char text[1000]; // text to generate image from char font[80]; // font name int size; // font size int tww, thh; // generated image size, unrotated float angle; // text angle, degrees float sinT, cosT; // trig funcs for text angle char color[4][20]; // text, backing, outline, shadow "R|G|B" int transp[4]; // corresponding transparencies 0-255 int towidth; // outline width, pixels int shwidth; // shadow width int shangle; // shadow angle -180...+180 PXB *pxb_text; // image with text/outline/shadow }; struct lineattr_t { // attributes for genline() function int length, width; // line length and width, pixels int larrow, rarrow; // left/right arrow head size (0 = no arrow) int lww, lhh; // generated image size, unrotated float angle; // line angle, degrees float sinT, cosT; // trig funcs for line angle char color[4][20]; // line, background, outline, shadow "R|G|B" int transp[4]; // corresponding transparencies 0-255 int towidth; // outline width, pixels int shwidth; // shadow width int shangle; // shadow angle -180...+180 PXB *pxb_line; // image with line/outline/shadow }; // dialogs with global visibility EX zdialog *zdbrdist; // brightness distribution zdialog EX zdialog *zddarkbrite; // highlight dark/bright pixels zdialog EX zdialog *zdeditmeta; // edit metadata zdialog EX zdialog *zdeditanymeta; // edit any metadata zdialog EX zdialog *zddeletemeta; // delete metadata zdialog EX zdialog *zdbatchtags; // batch tags zdialog EX zdialog *zdexifview; // view metadata zdialog EX zdialog *zdfilesave; // file save zdialog EX zdialog *zdrename; // rename file zdialog EX zdialog *zdcopymove; // copy/move file zdialog EX zdialog *zddeltrash; // delete/trash zdialog EX zdialog *zdupright; // upright file zdialog EX zdialog *zdsela; // select area zdialog EX zdialog *zdmagnify; // magnify image zdialog EX zdialog *zd_gallery_select1; // gallery_select1 zdialog 17.08 EX zdialog *zd_gallery_select; // gallery_select zdialog EX zdialog *zd_edit_bookmarks; // bookmarks edit zdialog EX zdialog *zd_ss_imageprefs; // slide show image prefs zdialog // method for running thread to send an event to an active zdialog EX zdialog *zd_thread; // active zdialog EX cchar *zd_thread_event; // event to send // edit function parameters EX int trimx1, trimy1, trimx2, trimy2; // trim rectangle NW and SE corners 17.04 EX int trimww, trimhh; // trim rectangle width and height EX char *trimbuttons[6]; // trim dialog button labels EX char *trimratios[6]; // corresponding aspect ratios EX int editresize[2]; // edit resize width, height EX int jpeg_def_quality; // default jpeg save quality (user setting) EX int jpeg_1x_quality; // file save 1-time quality setting EX float lens_mm; // pano lens mm setting // GTK functions (fotoxx main) int main(int argc, char * argv[]); // main program int initzfunc(void *); // initializations int delete_event(); // window delete event function int destroy_event(); // window destroy event function int state_event(GtkWidget *, GdkEvent *event); // window state event function void drop_event(int mousex, int mousey, char *file); // file drag-drop event function int gtimefunc(void *arg); // periodic function void update_Fpanel(); // update F window information panel void paintlock(int lock); // block or unblock window updates int Fpaint(GtkWidget *, cairo_t *); // F and W drawing area paint function void Fpaintnow(); // window repaint synchronously void Fpaint2(); // window repaint (thread callable) void Fpaint3(int px, int py, int ww, int hh, cairo_t *cr); // update Mpxb area from updated E3 area void Fpaint0(int px, int py, int ww, int hh, cairo_t *cr); // update Mpxb area from updated E0 area void Fpaint4(int px, int py, int ww, int hh, cairo_t *cr); // update Dpxb area from updated Mpxb void Fpaint3_thread(int px, int py, int ww, int hh); // Fpaint3 callable from threads 17.01 void mouse_event(GtkWidget *, GdkEventButton *, void *); // mouse event function void m_zoom(GtkWidget *, cchar *); // zoom image +/- void KBevent(GdkEventKey *event); // pass dialog KB events to main app int KBpress(GtkWidget *, GdkEventKey *, void *); // KB key press event function int KBrelease(GtkWidget *, GdkEventKey *, void *); // KB key release event function void win_fullscreen(int hidemenu); // full screen without menu/panel void win_unfullscreen(); // restore to prior size with menu etc. void set_mwin_title(); // main window title = curr_file path // cairo drawing functions (fotoxx main) void draw_pixel(int px, int py, cairo_t *cr, int fat = 0); // draw pixel 17.08 void erase_pixel(int px, int py, cairo_t *cr); // erase pixel void draw_line(int x1, int y1, int x2, int y2, int type, cairo_t *cr); // draw line or dotted line (type 1/2) void erase_line(int x1, int y1, int x2, int y2, cairo_t *cr); // erase line void draw_toplines(int arg, cairo_t *cr); // draw all pre-set overlay lines void draw_gridlines(cairo_t *cr); // draw grid lines on image void add_toptext(int ID, int px, int py, cchar *text, cchar *font); // add text string with ID on window void draw_toptext(cairo_t *cr); // draw text strings when window repainted void erase_toptext(int ID); // remove all text strings with ID void draw_text(int px, int py, cchar *text, cchar *font, cairo_t *cr); // draw text on window void add_topcircle(int px, int py, int radius); // draw circle on window void draw_topcircles(cairo_t *cr); // draw circles when window repainted void erase_topcircles(); // remove all circles void draw_mousecircle(int cx, int cy, int rad, int Ferase, cairo_t *cr); // draw circle around mouse pointer void draw_mousecircle2(int cx, int cy, int rad, int Ferase, cairo_t *cr); // 2nd circle for paint/clone void draw_mousearc(int cx, int cy, int ww, int hh, int Ferase, cairo_t *cr); // draw ellipse around pointer // spline curve edit functions (fotoxx main) spldat * splcurve_init(GtkWidget *frame, void func(int spc)); // initialize spline curves int splcurve_adjust(void *, GdkEventButton *event, spldat *); // curve editing function int splcurve_addnode(spldat *, int spc, float px, float py); // add a new node to a curve int splcurve_resize(GtkWidget *); // adjust drawing area height int splcurve_draw(GtkWidget *, cairo_t *, spldat *); // spline curve draw signal function int splcurve_generate(spldat *, int spc); // generate data from anchor points float splcurve_yval(spldat *, int spc, float xval); // get curve y-value int splcurve_load(spldat *sd, FILE *fid = 0); // load curve from a file int splcurve_save(spldat *sd, FILE *fid = 0); // save curve to a file // edit support functions (fotoxx main) int edit_setup(editfunc &EF); // start new edit transaction void edit_cancel(int keep); // cancel edit (keep zdialog etc.) void edit_done(int keep); // commit edit, add undo stack void edit_undo(); // undo edit, back to org. image void edit_redo(); // redo the edit after undo void edit_reset(); // reset to initial status void edit_fullsize(); // convert to full-size pixmaps int edit_load_widgets(editfunc *EF, FILE *fid = 0); // load zdialog widgets from a file int edit_save_widgets(editfunc *EF, FILE *fid = 0); // save zdialog widgets to a file int edit_save_last_widgets(editfunc *EF); // save last-used zdialog widgets int edit_load_prev_widgets(editfunc *EF); // load last-used zdialog widgets void m_undo_redo(GtkWidget *, cchar *); // undo / redo if left / right mouse click void m_undo(GtkWidget *, cchar *); // undo one edit void m_redo(GtkWidget *, cchar *); // redo one edit void undo_all(); // undo all edits of current image void redo_all(); // redo all edits of current image void save_undo(); // undo/redo save function void load_undo(); // undo/redo load function int checkpend(cchar *list); // check for lock, busy, pending changes typedef void mcbFunc(); // callback function type EX mcbFunc *mouseCBfunc; // current edit mouse function void takeMouse(mcbFunc func, GdkCursor *); // capture mouse for edit dialog void freeMouse(); // free mouse for main window // thread support functions (fotoxx main) typedef void * threadfunc(void *); // edit thread function void start_thread(threadfunc func, void *arg); // start edit thread void signal_thread(); // signal thread, work is pending void wait_thread_idle(); // wait for work complete void wrapup_thread(int command); // wait for exit or command exit void thread_idle_loop(); // thread: wait for work or exit command void thread_exit(); // thread: exit unconditionally void start_wthread(threadfunc func, void *arg); // start working thread (per proc. core) void exit_wthread(); // exit working thread void wait_wthreads(int block = 1); // wait for working threads // other support functions (fotoxx main) void save_params(); // save parameters for next session void load_params(); // load parameters from prior session void free_resources(int fkeepundo = 0); // free all allocated resources int sigdiff(float d1, float d2, float signf); // test for significant difference // window and menu builder (f.widgets.cc) void build_widgets(); // build widgets for F/G/W/M view modes void popup_menufunc(GtkWidget *, cchar *menu); // image/thumb right-click menu func void image_Rclick_popup(); // popup menu for image right-click void gallery_Lclick_func(int Nth); // function for thumbnail left-click void gallery_Rclick_popup(int Nth); // popup menu for thumbnail right-click void m_viewmode(GtkWidget *, cchar *fgw); // set current F/G/W/M view mode void m_favorites(GtkWidget *, cchar *); // graphic popup menu, user favorites // file menu functions (f.file.cc) void m_clone(GtkWidget *, cchar *); // start parallel fotoxx instance void m_recentfiles(GtkWidget *, cchar *); // open recently accessed file void add_recent_file(cchar *file); // add file to recent file list void m_newfiles(GtkWidget *, cchar *); // open newly added file void m_open(GtkWidget *, cchar *); // open image file in same window void m_open_drag(int x, int y, char *file); // open drag-drop file void m_cycle2files(GtkWidget *, cchar *); // open/cycle 2 previous files void m_cycle3files(GtkWidget *, cchar *); // open/cycle 3 previous files void m_rawtherapee(GtkWidget *, cchar *); // open RAW file with Raw Therapee void m_view360(GtkWidget *, cchar *); // view a 360 degree panorama 18.01 int f_open(cchar *file, int n = 0, int kp = 0, int ak = 1, int z = 0); // open new current image file int f_open_saved(); // open saved file, retain undo/redo stack void f_preload(int next); // start preload of next file void m_prev(GtkWidget *, cchar *); // open previous file in gallery void m_next(GtkWidget *, cchar *); // open next file in gallery void m_prev_next(GtkWidget *, cchar *); // open prev/next if left/right icon click void m_blank_image(GtkWidget *, cchar *); // create new blank image file int create_blank_file(cchar *file, int ww, int hh, int RGB[3]); // create new blank image file, callable void m_blank_window(GtkWidget *, cchar *); // blank / unblank window 18.01 void m_rename(GtkWidget *, cchar *); // rename an image file (same location) void m_copy_move(GtkWidget *, cchar *); // copy or move image file to new location void m_copyto_desktop(GtkWidget *, cchar *); // copy image file to desktop void m_copyto_clip(GtkWidget *, cchar *file); // copy an image file to the clipboard void m_delete_trash(GtkWidget *, cchar *); // delete or trash an image file void m_print(GtkWidget *, cchar *); // print an image file void m_print_calibrated(GtkWidget *, cchar *); // print an image file with adjusted colors void m_quit(GtkWidget *, cchar *); // exit application with checks void quitxx(); // exit unconditionally void m_file_save(GtkWidget *, cchar *); // save modified image file to disk void m_file_save_replace(GtkWidget *, cchar *menu); // for KB shortcut Save File (replace) void m_file_save_version(GtkWidget *, cchar *menu); // for KB shortcut Save File Version char * file_new_version(char *file); // get next avail. file version name char * file_last_version(char *file); // get last existing file version char * file_prior_version(char *file); // get prior file version 18.01 int f_save(char *outfile, cchar *type, int bpc, int Fack = 1); // save current image file to disk int f_save_as(); // save_as dialog (choose file and bpc) int linedit_open(cchar *filespec); // open text file for line editing char * linedit_get(); // read next existing record int linedit_put(cchar *record); // put next output record int linedit_close(); // finish, output file replaces input void m_help(GtkWidget *, cchar *menu); // various help menu functions int find_imagefiles(cchar *dr, int fgs, char **&fils, int &nf, int zf = 1); // find all image files within a directory // image file load and save functions (f.file.cc) char * raw_to_tiff(cchar *rawfile); // RAW filespec >> corresp. tiff filespec PXB * PXB_load(cchar *filespec, int fack = 0); // load file to PXB pixmap, 8 bpc PXM * PXM_load(cchar *filespec, int fack = 0); // load file to PXM pixmap, 8/16 bpc PXB * TIFF_PXB_load(cchar *filespec); // TIFF file to PXB, 8 bpc PXM * TIFF_PXM_load(cchar *filespec); // TIFF file to PXM, 8/16 bpc int PXM_TIFF_save(PXM *pxm, cchar *filespec, int bpc); // PXM to TIFF file, 8/16 bpc PXB * PNG_PXB_load(cchar *filespec); // PNG file to PXB, 8 bpc, keep alpha PXM * PNG_PXM_load(cchar *filespec); // PNG file to PXM, 8/16 bpc int PXM_PNG_save(PXM *pxm, cchar *filespec, int bpc); // PXM to PNG file, 8/16 bpc PXB * ANY_PXB_load(cchar *filespec); // ANY file to PXB, 8 bpc PXM * ANY_PXM_load(cchar *filespec); // ANY file to PXM, 8 bpc int PXM_ANY_save(PXM *pxm, cchar *filespec); // PXM to ANY file, 8 bpc PXB * RAW_PXB_load(cchar *filespec); // RAW file to PXB, 8 bpc PXM * RAW_PXM_load(cchar *filespec); // RAW file to PXM, 16 bpc // PXM and PXB pixmap image functions (f.file.cc) int vpixel(PXB *pxb, float px, float py, uint8 *vpix); // get PXB virtual pixel at (px,py) int vpixel(PXM *pxm, float px, float py, float *vpix); // get PXM virtual pixel at (px,py) void PXM_audit(PXM *pxm); // audit contents of a PXM pixmap PXM * PXM_make(int ww, int hh, int nc); // create a PXM pixmap void PXM_free(PXM *&pxm); // free PXM pixmap void PXM_addalpha(PXM *pxm); // add alpha channel to PXM pixmap PXM * PXM_copy(PXM *pxm); // copy PXM pixmap PXM * PXM_copy_area(PXM *pxm, int orgx, int orgy, int ww, int hh); // copy section of PXM pixmap void PXM_fixblue(PXM *pxm); // set blue = 0 pixels to blue = 2 PXM * PXM_rescale(PXM *pxm, int ww, int hh); // rescale PXM pixmap (ww/hh) PXM * PXM_rotate(PXM *pxm, float angle, int fast = 0); // rotate PXM pixmap PXB * PXB_make(int ww, int hh, int ac); // create a PXB pixmap PXB * PXB_make(PIXBUF *pixbuf); // create a PXB pixmap from a pixbuf void PXB_free(PXB *&pxb); // free PXB pixmap void PXB_addalpha(PXB *pxb); // add alpha channel to PXB pixmap PXB * PXB_copy(PXB *pxb); // copy PXB PXB * PXB_rescale(PXB *pxb, int ww, int hh); // rescale PXB pixmap (ww/hh) PXB * PXB_rotate(PXB *pxb, float angle); // rotate PXB pixmap PXB * PXM_PXB_copy(PXM *pxm); // PXM to pixbuf, same scale void PXM_PXB_update(PXM *, PXB *, int px3, int py3, int ww3, int hh3); // update PXB area from PXM, same scale void PXB_PXB_update(PXB *, PXB *, int px3, int py3, int ww3, int hh3); // update PXB area from PXB, any scale PIXBUF * pixbuf_rescale_fast(PIXBUF *pixbuf1, int ww, int hh); // faster gdk_pixbuf_scale_simple() // metadata menu functions (f.meta.cc) int select_meta_keys(char *itemlist[], int Fexclude); // dialog - select metadata items 18.01 void meta_view(int type); // popup metadata report void m_meta_view_short(GtkWidget *, cchar *); // view selected EXIF/IPTC data void m_meta_view_long(GtkWidget *, cchar *); // view all EXIF/IPTC data void m_meta_edit(GtkWidget *, cchar *); // edit date/rating/tags dialog void m_meta_edit_any(GtkWidget *, cchar *); // add or change any EXIF/IPTC data void m_meta_delete(GtkWidget *, cchar *); // delete EXIF/IPTC data void m_captions(GtkWidget *, cchar *); // show caption/comments at the top void m_batch_tags(GtkWidget *, cchar *); // add and remove tags void m_batch_rename_tags(GtkWidget *, cchar *); // rename tags for all image files void m_batch_photo_date_time(GtkWidget *, cchar *menu); // change or shift photo dates/times 18.01 void m_batch_change_metadata(GtkWidget *, cchar *); // add/change/delete metadata void m_batch_report_metadata(GtkWidget *, cchar *); // metadata report void m_batch_geotags(GtkWidget *, cchar *menu); // add or change geotags void m_locations(GtkWidget *, cchar *); // report images by location [date-span] void m_timeline(GtkWidget *, cchar *); // report images by month void m_search_images(GtkWidget *, cchar *); // search image metadata void metadate_pdate(cchar *metadate, char *pdate, char *ptime); // "yyyymmddhhmm" > "yyyy-mm-dd" + "hh:mm:ss" void load_filemeta(cchar *file); // load metadata for image file void save_filemeta(cchar *file); // save metadata in EXIF and image index int add_tag_fotoxx(cchar *file); // add "fotoxx" tag to an edited file 17.04 void set_meta_size(int ww, int hh); // set size outside m_meta_edit() 17.04 void update_image_index(cchar *file); // update image index for image file void delete_image_index(cchar *file); // delete image index for image file void m_set_map_markers(GtkWidget *, cchar *); // set markers for all images or gallery only void m_load_filemap(GtkWidget *, cchar *); // load a file map chosen by user (W view) void filemap_mousefunc(); // mouse function for file map view mode void filemap_paint_dots(); // paint red dots on map image locations void free_filemap(); // free memory used by file map image void m_netmap_source(GtkWidget *, cchar *); // choose net map source (M view) void m_load_netmap(GtkWidget *, cchar *); // load the initial net map void netmap_paint_dots(); // paint red dots on map image locations void m_netmap_zoomin(GtkWidget *, cchar *); // zoom net map to given location void m_netmap_locs(GtkWidget *, cchar *); // save amd recall net map locations 17.08 int exif_get(cchar *file, cchar **keys, char **kdat, int nk); // get EXIF/IPTC data for given keys int exif_put(cchar *file, cchar **keys, cchar **kdat, int nk); // put EXIF/IPTC data for given keys int exif_copy(cchar *f1, cchar *f2, cchar **keys, cchar **kdat, int nk); // copy EXIF/IPTC data from file to file int exif_server(int Nrecs, char **inputs, char **outputs); // run exiftool as a server process void exif_tagdate(cchar *exifdate, char *tagdate); // yyyy:mm:dd:hh:mm:ss to yyyymmddhhmmss void tag_exifdate(cchar *tagdate, char *exifdate); // yyyymmddhhmmss to yyyy:mm:dd:hh:mm:ss xxrec_t * get_xxrec(cchar *file); // get image index record from memory table int get_xxrec_min(cchar *file, char *fdate, char *pdate, char *size); // get image data for gallery display int put_xxrec(xxrec_t *xxrec, cchar *file); // add/update image index table and file xxrec_t * read_xxrec_seq(int &ftf); // read image index records 1-last int write_xxrec_seq(xxrec_t *xxrec, int &ftf); // write image index records 1-last // select area menu functions (f.area.cc) void m_select(GtkWidget *, cchar *); // select area within image void m_select_find_gap(GtkWidget *, cchar *); // find gap in area outline void m_select_hairy(GtkWidget *, cchar *); // select hairy or irregular edge void m_select_show(GtkWidget *, cchar *); // enable area for subsequent edits void m_select_hide(GtkWidget *, cchar *); // show area outline void m_select_enable(GtkWidget *, cchar *); // hide area outline void m_select_disable(GtkWidget *, cchar *); // disable area void m_select_invert(GtkWidget *, cchar *); // invert area void m_select_unselect(GtkWidget *, cchar *); // unselect area void m_select_save(GtkWidget *, cchar *); // copy area or save area to a file void m_select_paste(GtkWidget *, cchar *); // paste last copied area into image void m_select_open(GtkWidget *, cchar *); // open file and paste as area in image void sa_geom_mousefunc(); // select rectangle or ellipse void sa_draw_mousefunc(); // edge line drawing function int sa_nearpix(int mx, int my, int rad, int &px, int &py, int fx); // find nearest pixel void sa_draw_line(int px1, int py1, int px2, int py2, cairo_t *cr); // draw a connected line void sa_draw1pix(int px, int py, cairo_t *cr); // add one pixel to select area void sa_mouse_select(); // select by mouse (opt. color match) void sa_nextseq(); // start next sequence number void sa_unselect_pixels(cairo_t *cr); // remove current selection void sa_finish(); // finish - map interior pixels void sa_finish_auto(); // finish - interior pixels already known void sa_unfinish(); // set finished area back to edit mode void sa_map_pixels(); // map edge and interior pixels void sa_show(int flag, cairo_t *cr); // show or hide area outline void sa_show_rect(int px1, int py1, int ww, int hh, cairo_t *cr); // show area outline inside a rectangle int sa_validate(); // validate area for curr. image void sa_enable(); // enable area void sa_disable(); // disable area void sa_invert(); // invert area void sa_unselect(); // unselect area void sa_edgecalc(); // calculate edge distances void sa_edgecreep(int); // adjust area edge +/- float sa_blendfunc(int edgedist); // calculate edge blend factor // image edit functions - Edit menu (f.edit.cc) void m_trim_rotate(GtkWidget *, cchar *); // combined trim/rotate function void m_upright(GtkWidget *, cchar *); // upright rotated image void m_resize(GtkWidget *, cchar *); // resize image void m_voodoo1(GtkWidget *, cchar *); // automatic image retouch void m_voodoo2(GtkWidget *, cchar *); // automatic image retouch void m_retouch_combo(GtkWidget *, cchar *); // combo function, brightness/contrast/color void m_edit_britedist(GtkWidget *, cchar *); // edit brightness distribution void m_gradients(GtkWidget *, cchar *); // magnify brightness gradients void m_flatten(GtkWidget *, cchar *); // flatten brightness distribution void m_retinex(GtkWidget *, cchar *); // rescale RGB brightness range 18.01 void m_mirror(GtkWidget *, cchar *); // mirror image horz. or vert. void m_paint_image(GtkWidget *, cchar *); // paint pixels with the mouse 17.04 int color_palette(uint8 RGB[3]); // dialog, get color from color file 17.04 int HSL_chooser(uint8 RGB[3]); // dialog, get color from HSL dialog 17.08 void m_clone_image(GtkWidget *, cchar *); // clone pixels with the mouse 17.04 void m_blend_image(GtkWidget *, cchar *); // blend image pixels with the mouse 17.01 void m_add_text(GtkWidget *, cchar *); // add text to an image void load_text(zdialog *zd); // load text and attributes from a file void save_text(zdialog *zd); // save text and attributes to a file int gentext(textattr_t *attr); // generate text image from attributes void m_add_lines(GtkWidget *, cchar *); // add line/arrow to an image void load_line(zdialog *zd); // load line attributes from a file void save_line(zdialog *zd); // save line attributes to a file int genline(lineattr_t *attr); // generate line/arrow image from attributes void m_paint_edits(GtkWidget *, cchar *); // select and edit in parallel void m_lever_edits(GtkWidget *, cchar *); // leverage edits by pixel bright/color void m_plugins(GtkWidget *, cchar *); // edit plugins menu or run a plugin function // image edit functions - Repair menu (f.repair.cc) void m_sharpen(GtkWidget *, cchar *); // sharpen image void m_blur(GtkWidget *, cchar *); // blur image void m_denoise(GtkWidget *, cchar *); // image noise reduction void m_redeye(GtkWidget *, cchar *); // red-eye removal void m_color_mode(GtkWidget *, cchar *); // B+W/color, negative/positive, sepia void m_colordep(GtkWidget *, cchar *); // set color depth 1-16 bits/color void m_shift_colors(GtkWidget *, cchar *); // shift one color into another color void m_colorsat(GtkWidget *, cchar *); // adjust color saturation void m_adjust_RGB(GtkWidget *, cchar *); // color adjust using RGB or CMY colors void m_adjust_HSL(GtkWidget *, cchar *); // color adjust with HSL void HSLtoRGB(float H, float S, float L, float &R, float &G, float &B); // convert HSL color space to RGB void RGBtoHSL(float R, float G, float B, float &H, float &S, float &L); // convert RGB color space to HSL void m_zonal_colors(GtkWidget *, cchar *); // revise brightness/color in local areas void m_match_color(GtkWidget *, cchar *); // set image2 colors to match image1 void m_color_fringes(GtkWidget *, const char *); // reduce chromatic abberation void m_smart_erase(GtkWidget *, const char *); // smart erase object void m_bright_ramp(GtkWidget *, cchar *); // add brightness/color ramp across image void m_remove_dust(GtkWidget *, const char *); // remove dust void m_anti_alias(GtkWidget *, const char *); // anti-alias void m_stuck_pixels(GtkWidget *, const char *); // fix stuck pixels void m_paint_transp(GtkWidget *, cchar *); // paint transparnecy with the mouse void m_add_transp(GtkWidget *, cchar *); // add transparency based on image attributes // image edit functions - Warp menu (f.warp.cc) void m_unbend(GtkWidget *, cchar *); // unbend panoramas void m_perspective(GtkWidget *, cchar *); // warp tetragon into rectangle void m_warp_area(GtkWidget *, cchar *); // warp image within an area void m_unwarp_closeup(GtkWidget *, cchar *); // warp image within closeup face area void m_warp_curved(GtkWidget *, cchar *); // warp image, curved transform void m_warp_linear(GtkWidget *, cchar *); // warp image, linear transform void m_warp_affine(GtkWidget *, cchar *); // warp image, affine transform void m_flatbook(GtkWidget *, cchar *); // flatten a photographed book page void m_sphere(GtkWidget *, cchar *); // image spherical projection void m_selective_rescale(GtkWidget *, cchar *); // rescale image, selected areas unchanged void m_waves(GtkWidget *, cchar *); // warp image using a wave pattern void m_twist(GtkWidget *, cchar *); // twist image centered at mouse position // image edit functions - Effects menu (f.effects.cc) void m_sketch(GtkWidget *, cchar *); // convert image to simulated sketch void m_cartoon(GtkWidget *, cchar *); // convert image into a cartoon drawing void m_line_drawing(GtkWidget *, cchar *); // comvert image to outline drawing void m_color_drawing(GtkWidget *, cchar *); // convert image to solid color drawing void m_grad_blur(GtkWidget *, cchar *); // graduated blur tool void m_emboss(GtkWidget *, cchar *); // convert image to simulated embossing void m_tiles(GtkWidget *, cchar *); // convert image to tile array (pixelate) void m_dots(GtkWidget *, cchar *); // convert image to dots (Roy Lichtenstein) void m_painting(GtkWidget *, cchar *); // convert image to simulated painting void m_vignette(GtkWidget *, cchar *); // vignette tool void m_texture(GtkWidget *, cchar *); // add texture to image void m_pattern(GtkWidget *, cchar *); // tile image with a pattern void m_mosaic(GtkWidget *, cchar *); // create mosaic using tiles made from images void m_anykernel(GtkWidget *, cchar *); // operate on image using a custom kernel void m_directed_blur(GtkWidget *, cchar *); // blur image in the direction of mouse drags void m_blur_background(GtkWidget *, cchar *); // blur image outside of selected areas void m_alien_colors(GtkWidget *, cchar *); // revise color hues using an algorithm // image edit functions - Combine menu (f.combine.cc) void m_HDR(GtkWidget *, cchar *); // make HDR composite image void m_HDF(GtkWidget *, cchar *); // make HDF composite image void m_stack_paint(GtkWidget *, cchar *); // stack / paint image void m_stack_noise(GtkWidget *, cchar *); // stack / noise reduction void m_pano_horz(GtkWidget *, cchar *); // make panorama composite image void m_pano_vert(GtkWidget *, cchar *); // make vertical panorama void m_pano_PT(GtkWidget *, cchar *); // make panorama via pano tools // image edit functions - mashup and montage (f.mashup.cc) void m_mashup(GtkWidget *, cchar *); // arrange images and text in custom layout void m_montage(GtkWidget *, cchar *); // combine images into an image montage void montage_Lclick_func(int mx, int my); // montage image click function // process menu functions (f.process.cc) void m_batch_convert(GtkWidget *, cchar *); // rename/convert/resize/export image files int batch_sharp_func(PXM *pxm, int amount, int thresh); // callable sharpen func used by batch funcs void m_batch_upright(GtkWidget *, cchar *); // upright rotated image files void m_batch_deltrash(GtkWidget *, cchar *); // delete or trash selected files void m_batch_raw(GtkWidget *, cchar *); // convert RAW files, libraw or Raw Therapee void m_scriptfiles(GtkWidget *, cchar *); // build and run edit script files int addscript(editfunc *); // add edit function to script void m_burn_DVD(GtkWidget *, cchar *); // burn selected images to CD/DVD void m_duplicates(GtkWidget *, cchar *); // find duplicate image files void m_export_filelist(GtkWidget *, cchar *); // create file of selected image files void m_export_images(GtkWidget *, cchar *); // export image files to a directory // tools menu functions (f.tools.cc) void m_index(GtkWidget *, cchar *); // rebuild image index and thumbnails void index_rebuild(int level, int menu); // index rebuild function void m_settings(GtkWidget *, cchar *); // user settings void m_KBshortcuts(GtkWidget *, cchar *); // edit KB shortcuts, update file int KBshortcuts_load(); // load KB shortcuts at startup time void m_show_brdist(GtkWidget *, cchar *); // show brightness distribution graph void brdist_drawgraph(GtkWidget *drawin, cairo_t *cr, int *); // draw brightness distribution graph void brdist_drawscale(GtkWidget *drawarea, cairo_t *cr, int *); // draw brightness scale, black to white band void m_gridlines(GtkWidget *, cchar *); // grid lines setup dialog void toggle_grid(int action); // set grid off/on or toggle (0/1/2) void m_line_color(GtkWidget *, cchar *); // foreground line color (area/mouse/trim...) void m_show_RGB(GtkWidget *, cchar *); // show RGB values at mouse click void m_magnify(GtkWidget *, cchar *); // magnify image within a radius of the mouse void m_extreme_pixels(GtkWidget *, cchar *); // highlight the darkest and brightest pixels void darkbrite_paint(); // paint function called from Fpaint() void m_monitor_color(GtkWidget *, cchar *); // check monitor brightness and color void m_monitor_gamma(GtkWidget *, cchar *); // adjust monitor gamma void m_change_lang(GtkWidget *, cchar *); // change language void m_untranslated(GtkWidget *, cchar *); // report missing translations void m_color_profile(GtkWidget *, cchar *); // convert to another color profile void m_calibrate_printer(GtkWidget *, cchar *); // calibrate printer colors void print_calibrated(); // print image with adjusted colors int make_appimage_desktop(); // install desktop menu for appimage package void m_uninstall_appimage(GtkWidget *, cchar *); // completely uninstall appimage package void m_resources(GtkWidget *, cchar *); // report CPU and memory usage void m_zappcrash(GtkWidget *, cchar *); // deliberate zappcrash (test function) // thumbnail gallery and navigation functions (f.gallery.cc) char * gallery(cchar *filez, cchar *action, int Nth); // display image gallery window, navigate void gallery_memory(cchar *action); // save/recall gallery sort and posn. 18.01 void set_gwin_title(); // main window title = gallery name char * gallery_getnext(int index, int lastver); // get prev/next file with last version option char * prev_next_gallery(int index); // get prev/next gallery (physical directory) int file_position(cchar *file, int Nth); // get rel. position of file in gallery FTYPE image_file_type(cchar *file); // file type: FNF DIRK IMAGE RAW THUMB OTHER char * thumb2imagefile(cchar *thumbfile); // get image file for thumbnail file char * image2thumbfile(cchar *imagefile); // get thumbnail file for image file int thumbnail_OK(cchar *imagefile); // check thumbnail exists and not stale 18.01 int update_thumbnail_file(cchar *imagefile); // create or refresh thumbnail file PIXBUF * get_cache_thumbnail(cchar *imagefile, int size); // get thumbnail from cache, add if needed PIXBUF * make_thumbnail_pixbuf(cchar *filename, int size); // make thumbnail pixbuf from image file PIXBUF * make_thumbnail_pixbuf_raw(cchar *rawfile, int size); // make thumbnail pixbuf from RAW image file void delete_thumbnail(cchar *imagefile); // delete thumbnail for file void preload_thumbs(int targetfile); // preload thumbnails into cache 17.04 void gallery_popimage(); // popup big image of clicked thumbnail char * gallery_select1(cchar *gdirk); // select one file from gallery window 17.08 void gallery_select1_Lclick_func(int Nth); // gallery_select1, thumbnail left-click void gallery_select_clear(); // clear gallery_select() file list int gallery_select(); // select files from gallery window void gallery_select_Lclick_func(int Nth); // gallery_select, thumbnail left-click void gallery_select_Rclick_func(int Nth); // gallery_select, thumbnail right-click void m_edit_bookmarks(GtkWidget *, cchar *); // edit bookmarks (gallery/file position) void m_goto_bookmark(GtkWidget *, cchar *); // select bookmarked image, goto gallery posn void bookmarks_Lclick_func(int Nth); // thumbnail click response function void m_alldirs(GtkWidget *, cchar *); // list directories, click for gallery 17.01 void m_sync_gallery(GtkWidget *, cchar *); // set gallery from current image file void m_show_hidden(GtkWidget *, cchar *); // for KB shortcut "show hidden files" 18.01 // albums and slide show menu (f.albums.cc) void m_manage_albums(GtkWidget *, cchar *); // create and edit image albums int album_from_gallery(cchar *albumname); // create album from current gallery 17.01 void album_pastefile(char *file, int posn); // insert file at position void album_movefile(int pos1, int pos2); // move file position in album void album_show(char *file = 0); // show current album in gallery void conv_albums(char **, char **, int); // convert after files renamed/moved void m_copyto_cache(GtkWidget *, cchar *); // copy an image file to the file cache void m_album_removefile(GtkWidget *, cchar *); // remove image from album void m_album_cutfile(GtkWidget *, cchar *); // remove image from album, add to cache void m_album_pastecache(GtkWidget *, cchar *); // paste image cache to album position void m_update_albums(GtkWidget *, cchar *); // update album files to last version void m_replace_album_file(GtkWidget *, cchar *); // replace album file with another file void m_slideshow(GtkWidget *, cchar *); // enter or leave slideshow mode void ss_imageprefs_Lclick_func(int Nth); // slideshow image prefs thumbnail click func void ss_KBfunc(int KBkey); // slideshow keyboard input 18.01 // translatable strings used in multiple dialogs #define Badd ZTX("Add") #define Baddall ZTX("Add All") #define Ball ZTX("All") #define Bamount ZTX("Amount") #define Bangle ZTX("Angle") #define Bapply ZTX("Apply") #define Bauto ZTX("Auto") #define Bblack ZTX("Black") #define Bblendwidth ZTX("Blend Width") #define Bblue ZTX("Blue") #define Bbottom ZTX("Bottom") #define Bbrightness ZTX("Brightness") #define Bbrowse ZTX("Browse") #define Bcancel ZTX("Cancel") #define Bcenter ZTX("Center") #define Bchoose ZTX("Choose") #define Bclear ZTX("Clear") #define Bclose ZTX("Close") #define Bcolor ZTX("Color") #define Bcompleted ZTX("COMPLETED") #define Bcontinue ZTX("continue") #define Bcontrast ZTX("Contrast") #define Bcopy ZTX("Copy") #define Bcreate ZTX("Create") #define Bcurvefile ZTX("Curve File:") #define Bcut ZTX("Cut") #define Bdeband ZTX("Deband") #define Bdelete ZTX("Delete") #define Bdisable ZTX("Disable") #define Bdone ZTX("Done") #define Bedge ZTX("edge") #define Bedit ZTX("Edit") #define Benable ZTX("Enable") #define Berase ZTX("Erase") #define Bfetch ZTX("Fetch") #define Bfileexists ZTX("output file already exists") #define Bfilenotfound ZTX("file not found") #define Bfilenotfound2 ZTX("file not found: %s") #define Bfileselected ZTX("%d image files selected") #define Bfind ZTX("Find") #define Bfinish ZTX("Finish") #define Bflatten ZTX("Flatten") #define Bfont ZTX("Font") #define Bgreen ZTX("Green") #define Bgrid ZTX("Grid") #define Bheight ZTX("Height") #define Bhelp ZTX("Help") #define Bhide ZTX("Hide") #define Bimage ZTX("Image") #define Bimages ZTX("Images") #define Binsert ZTX("Insert") #define Binvert ZTX("Invert") #define Bkeep ZTX("Keep") #define Bleft ZTX("Left") #define Blimit ZTX("limit") #define Bload ZTX("Load") #define Bmake ZTX("Make") #define Bmanagetags ZTX("Manage Tags") #define Bmap ZTX("Map") #define Bmatchlevel ZTX("Match Level:") #define Bmax ZTX("Max") #define Bmouseradius ZTX("mouse radius") #define Bnegative ZTX("Negative") #define Bnew ZTX("New") #define Bnext ZTX("Next") #define Bno ZTX("No") #define Bnoimages ZTX("no images") #define Bnoindex ZTX("image index disabled \n Enable?") #define Bnofileselected ZTX("no image files selected") #define Bnone ZTX("None") #define Bnoselection ZTX("no selection") #define BOK ZTX("OK") #define Boldindex ZTX("image index not updated") #define Bopen ZTX("Open") #define Bpaintradius ZTX("Paint Radius") #define Bpaste ZTX("Paste") #define Bpause ZTX("Pause") #define Bpercent ZTX("Percent") #define Bpower ZTX("Power") #define Bpresets ZTX("Presets") #define Bprev ZTX("Prev") #define Bproceed ZTX("Proceed") #define Bradius ZTX("Radius") #define Brange ZTX("range") #define Bred ZTX("Red") #define Bredo ZTX("Redo") #define Breduce ZTX("Reduce") #define Bremove ZTX("Remove") #define Brename ZTX("Rename") #define Breplace ZTX("Replace") #define Breserved ZTX("Reserved") #define Breset ZTX("Reset") #define Bright ZTX("Right") #define Brotate ZTX("Rotate") #define Brun ZTX("Run") #define Bsave ZTX("Save") #define Bsearch ZTX("Search") #define Bseconds ZTX("Seconds") #define Bselect ZTX("Select") #define Bselectfiles ZTX("Select Files") #define Bshow ZTX("Show") #define Bsize ZTX("Size") #define Bstart ZTX("Start") #define Bstop ZTX("Stop") #define Bstrength ZTX("Strength") #define Bthresh ZTX("Threshold") #define Btoomanyfiles ZTX("exceed %d files") #define Btop ZTX("Top") #define Btransparency ZTX("Transparency") #define Btrash ZTX("Trash") #define Btrim ZTX("Trim") #define Bundoall ZTX("Undo All") #define Bundolast ZTX("Undo Last") #define Bundo ZTX("Undo") #define Bunfinish ZTX("Unfinish") #define Bunselect ZTX("Unselect") #define Bview ZTX("View") #define Bweb ZTX("Web") #define Bwhite ZTX("White") #define Bwidth ZTX("Width") #define Bxoffset ZTX("x-offset") #define Byoffset ZTX("y-offset") #define Byes ZTX("Yes") fotoxx-18.01.1/f.widgets.cc0000644000175000017500000020460113222767271014066 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx window and menu build functions build_widgets build widgets and menus for F/G/W/M view modes m_viewmode set current F/G/W/M view mode popup_menufunc image/thumb right-click menu func image_Rclick_popup popup menu for image right-click gallery_Lclick_func thumbnail left-click function gallery_Rclick_popup popup menu for thumbnail right-click m_favorites function to generate favorites menu favorites_callback response function for clicked menu *********************************************************************************/ #define EX extern // enable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are defined) /********************************************************************************/ GtkWidget *mFile, *mTools, *mProc, *mGmenu, *mMeta, *mArea; GtkWidget *mEdit, *mRep, *mWarp, *mEff, *mComb, *mHelp; GtkWidget *popmenu_image, *popmenu_raw, *popmenu_video; GtkWidget *popmenu_thumb, *popmenu_album; int NFmenu, NGmenu, NWmenu, NMmenu; // initialize widgets and menus for F/G/W/M view modes // called from main() before gtk_main() loop is entered void build_widgets() { Mwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); // create main window gtk_window_set_title(MWIN,Arelease); MWvbox = gtk_box_new(VERTICAL,0); // top container gtk_container_add(GTK_CONTAINER(Mwin),MWvbox); G_SIGNAL(Mwin,"delete-event",delete_event,0); // connect signals to main window G_SIGNAL(Mwin,"destroy",destroy_event,0); G_SIGNAL(Mwin,"window-state-event",state_event,0); G_SIGNAL(Mwin,"key-press-event",KBpress,0); // connect KB events to main window G_SIGNAL(Mwin,"key-release-event",KBrelease,0); // F view widgets - image file Fhbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(MWvbox),Fhbox,1,1,0); Fmenu = gtk_box_new(VERTICAL,0); // left vbox for vert. menu gtk_box_pack_start(GTK_BOX(Fhbox),Fmenu,0,0,0); Fvbox = gtk_box_new(VERTICAL,0); // right vbox for image gtk_box_pack_start(GTK_BOX(Fhbox),Fvbox,1,1,0); Fpanel = gtk_box_new(HORIZONTAL,0); // panel over image gtk_box_pack_start(GTK_BOX(Fvbox),Fpanel,0,0,0); gtk_widget_set_size_request(Fpanel,0,20); Fpanlab = gtk_label_new("panel"); gtk_box_pack_start(GTK_BOX(Fpanel),Fpanlab,0,0,0); Fpanelshow = 1; // panel normally shows Fdrawin = gtk_drawing_area_new(); // image drawing area gtk_box_pack_start(GTK_BOX(Fvbox),Fdrawin,1,1,0); gtk_widget_hide(Fhbox); gtk_widget_add_events(Fdrawin,GDK_BUTTON_PRESS_MASK); // connect mouse events to image window gtk_widget_add_events(Fdrawin,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(Fdrawin,GDK_BUTTON_MOTION_MASK); gtk_widget_add_events(Fdrawin,GDK_POINTER_MOTION_MASK); gtk_widget_add_events(Fdrawin,GDK_SCROLL_MASK); G_SIGNAL(Fdrawin,"button-press-event",mouse_event,0); // connect signals G_SIGNAL(Fdrawin,"button-release-event",mouse_event,0); G_SIGNAL(Fdrawin,"motion-notify-event",mouse_event,0); G_SIGNAL(Fdrawin,"scroll-event",mouse_event,0); G_SIGNAL(Fdrawin,"draw",Fpaint,0); drag_drop_dest(Fdrawin,drop_event); // accept drag-drop file // G view widgets - thumbnail gallery Ghbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(MWvbox),Ghbox,1,1,0); Gmenu = gtk_box_new(VERTICAL,0); // left vbox for vert. menu gtk_box_pack_start(GTK_BOX(Ghbox),Gmenu,0,0,0); Gvbox = gtk_box_new(VERTICAL,0); // right vbox for gallery gtk_box_pack_start(GTK_BOX(Ghbox),Gvbox,1,1,0); Gpanel = gtk_box_new(HORIZONTAL,0); // top panel for [TOP] and navi buttons gtk_box_pack_start(GTK_BOX(Gvbox),Gpanel,0,0,2); Galbum = gtk_button_new_with_label(ZTX("Album")); // [Album] button in panel gtk_box_pack_start(GTK_BOX(Gpanel),Galbum,0,0,3); Gtop = gtk_button_new_with_label(ZTX("TOP")); // [TOP] button in panel gtk_box_pack_start(GTK_BOX(Gpanel),Gtop,0,0,3); Gsep = gtk_label_new(0); gtk_label_set_markup(GTK_LABEL(Gsep),"@"); gtk_box_pack_start(GTK_BOX(Gpanel),Gsep,0,0,10); Gsep = gtk_separator_new(HORIZONTAL); // separator line gtk_box_pack_start(GTK_BOX(Gvbox),Gsep,0,0,3); Gscroll = gtk_scrolled_window_new(0,0); // scrolled window for gallery gtk_scrolled_window_set_policy(SCROLLWIN(Gscroll),NEVER,ALWAYS); Gadjust = gtk_scrolled_window_get_vadjustment(SCROLLWIN(Gscroll)); gtk_box_pack_start(GTK_BOX(Gvbox),Gscroll,1,1,0); Gdrawin = gtk_drawing_area_new(); // gallery drawing area gtk_container_add(GTK_CONTAINER(Gscroll),Gdrawin); gtk_widget_hide(Ghbox); gtk_widget_add_events(Gdrawin,GDK_BUTTON_PRESS_MASK); // connect mouse events to gallery window gtk_widget_add_events(Gdrawin,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(Gdrawin,GDK_POINTER_MOTION_MASK); G_SIGNAL(Gtop,"clicked",navi::newtop,0); G_SIGNAL(Galbum,"clicked",navi::newalbum,0); G_SIGNAL(Gdrawin,"button-press-event",navi::mouse_event,0); G_SIGNAL(Gdrawin,"button-release-event",navi::mouse_event,0); G_SIGNAL(Gdrawin,"motion-notify-event",navi::mouse_event,0); G_SIGNAL(Gdrawin,"draw",navi::gallery_paint,null); drag_drop_source(Gdrawin,navi::gallery_dragfile); // start file drag-drop drag_drop_dest(Gdrawin,navi::gallery_dropfile); // accept drag-drop file // W view widgets - local map files Whbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(MWvbox),Whbox,1,1,0); Wmenu = gtk_box_new(VERTICAL,0); // left vbox for vert. menu gtk_box_pack_start(GTK_BOX(Whbox),Wmenu,0,0,0); Wvbox = gtk_box_new(VERTICAL,0); // right vbox for filemap gtk_box_pack_start(GTK_BOX(Whbox),Wvbox,1,1,0); Wdrawin = gtk_drawing_area_new(); // filemap drawing area gtk_box_pack_start(GTK_BOX(Wvbox),Wdrawin,1,1,0); gtk_widget_hide(Whbox); gtk_widget_add_events(Wdrawin,GDK_BUTTON_PRESS_MASK); // connect mouse events to filemap window gtk_widget_add_events(Wdrawin,GDK_BUTTON_RELEASE_MASK); gtk_widget_add_events(Wdrawin,GDK_BUTTON_MOTION_MASK); gtk_widget_add_events(Wdrawin,GDK_POINTER_MOTION_MASK); gtk_widget_add_events(Wdrawin,GDK_SCROLL_MASK); G_SIGNAL(Wdrawin,"button-press-event",mouse_event,0); // connect signals G_SIGNAL(Wdrawin,"button-release-event",mouse_event,0); G_SIGNAL(Wdrawin,"motion-notify-event",mouse_event,0); G_SIGNAL(Wdrawin,"scroll-event",mouse_event,0); G_SIGNAL(Wdrawin,"draw",Fpaint,0); // M view widgets - internet maps Mhbox = gtk_box_new(HORIZONTAL,0); // top container gtk_box_pack_start(GTK_BOX(MWvbox),Mhbox,1,1,0); Mmenu = gtk_box_new(VERTICAL,0); // left vbox for vert. menu gtk_box_pack_start(GTK_BOX(Mhbox),Mmenu,0,0,0); Mvbox = gtk_box_new(VERTICAL,0); // right vbox for net map gtk_box_pack_start(GTK_BOX(Mhbox),Mvbox,1,1,0); gtk_widget_hide(Mhbox); // menu popup text (tool tips) ---------------------------------------- // F/G/W/M view menu buttons cchar * imagefile_tip = ZTX("Current Image File (key F)"); cchar * gallery_tip = ZTX("Thumbnail Gallery (key G)"); cchar * worldmaps_tip = ZTX("World Maps (key W)"); cchar * netmaps_tip = ZTX("Net Maps (key M)"); // F view menu buttons cchar * favorites_tip = ZTX("Favorite Functions"); cchar * file_tip = ZTX("File: Open, RAW, Rename, Delete, Print"); cchar * save_tip = ZTX("Save modified image file to disk"); cchar * prev_next_tip = ZTX("Open previous or next file (left/right mouse click)"); cchar * metadata_tip = ZTX("Metadata: Captions, Tags, Ratings, Geotags, Search ... "); cchar * areas_tip = ZTX("Areas: Select areas to edit, copy and paste"); cchar * edit_tip = ZTX("Edit: Trim, Rotate, Resize, Brightness, Contrast, Text ..."); cchar * repair_tip = ZTX("Repair: Sharpen, Noise, Red-eyes, Color, Paint, Clone ..."); cchar * warp_tip = ZTX("Warp: Fix Perspective, Warp or unwarp image ..."); cchar * effects_tip = ZTX("Effects: Special Effects, Arty Transforms"); cchar * undo_redo_tip = ZTX("left/right click: undo/redo 1 edit \n" " with key A: undo/redo all edits \n" " middle click: go to any prior edit"); cchar * tools_tip = ZTX("Tools: Index, Settings, Shortcuts, Magnify ..."); cchar * help_tip = ZTX("Help: Quick Start, User Guide, Recent Changes ..."); // G view menu buttons cchar * gmenu_tip = ZTX("Sync Gallery, Albums, Slide Show"); cchar * bookmarks_tip = ZTX("set and recall bookmarked image locations"); cchar * thumb_increase_tip = ZTX("increase thumbnail size"); cchar * thumb_reduce_tip = ZTX("reduce thumbnail size"); cchar * sort_order_tip = ZTX("change sort order"); cchar * jump_begin_tip = ZTX("jump to beginning (top)"); cchar * jump_end_tip = ZTX("jump to end (bottom)"); cchar * previous_page_tip = ZTX("previous page"); cchar * next_page_tip = ZTX("next page"); cchar * combine_tip = ZTX("Combine: HDR, HDF, Panorama, Stack, Mashup"); cchar * process_tip = ZTX("Process: convert, export, metadata, search ..."); // W view menu buttons cchar * load_filemap_tip = ZTX("Choose a map file"); cchar * mark_filemap_tip = ZTX("Mark all images or current gallery"); // M view menu buttons cchar * netmap_source_tip = ZTX("Choose Internet map source"); cchar * mark_netmap_tip = ZTX("Mark all images or current gallery"); cchar * netmap_locs_tip = ZTX("Save and recall named map locations"); // file menu cchar * clone_tip = ZTX("Open another window"); cchar * open_tip = ZTX("Open a new image file"); cchar * cycle2files_tip = ZTX("Cycle 2 Previous Files"); cchar * cycle3files_tip = ZTX("Cycle 3 Previous Files"); cchar * recentfiles_tip = ZTX("Open a recently seen file"); cchar * newfiles_tip = ZTX("Open a newly added file"); cchar * rawtherapee_tip = ZTX("Open and edit a camera RAW file"); cchar * view360_tip = ZTX("View a 360 degree panorama image file"); cchar * rename_tip = ZTX("Change the image file name"); cchar * copy_move_tip = ZTX("Copy or Move an image file to a new location"); cchar * copyto_desktop_tip = ZTX("Copy an image file to the desktop"); cchar * copyto_clipboard_tip = ZTX("Copy image file to the clipboard"); cchar * copyto_image_cache_tip = ZTX("Copy image file to the image cache"); cchar * show_on_net_map_tip = ZTX("Show location on Interet map"); cchar * blank_image_tip = ZTX("Create a blank image"); cchar * blank_window_tip = ZTX("toggle - blank or restore window"); cchar * deltrash_tip = ZTX("Delete or trash image file"); cchar * print_tip = ZTX("Print the current image"); cchar * print_calibrated_tip = ZTX("Print current image with adjusted colors"); cchar * quit_tip = ZTX("Quit Fotoxx"); // metadata menu cchar * meta_view_short_tip = ZTX("List a few key metadata items"); cchar * meta_view_long_tip = ZTX("List all metadata items"); cchar * edit_metadata_tip = ZTX("Edit image tags/geotags/caption/rating ..."); cchar * meta_edit_any_tip = ZTX("Edit any image metadata"); cchar * meta_delete_tip = ZTX("Remove selected image metadata"); cchar * captions_tip = ZTX("(Toggle) show captions and comments"); // select area menu cchar * select_tip = ZTX("Select object or area for editing"); cchar * select_find_gap_tip = ZTX("Find a gap in an area outline"); cchar * select_hairy_tip = ZTX("Select hairy or irregular edge"); cchar * select_show_tip = ZTX("Show (outline) existing area"); cchar * select_hide_tip = ZTX("Hide existing area"); cchar * select_enable_tip = ZTX("Enable area for editing"); cchar * select_disable_tip = ZTX("Disable area for editing"); cchar * select_invert_tip = ZTX("Reverse existing area"); cchar * select_unselect_tip = ZTX("Erase existing area"); cchar * select_copy_tip = ZTX("Copy area for later pasting into image"); cchar * select_save_tip = ZTX("Save area to a file with transparency"); cchar * select_open_tip = ZTX("Open a file and paste as area into image"); cchar * select_paste_tip = ZTX("Paste previously copied area into image"); // edit menu cchar * trim_rotate_tip = ZTX("Trim/Crop margins and/or Rotate"); cchar * upright_tip = ZTX("Upright a rotated image"); cchar * resize_tip = ZTX("Change pixel dimensions"); cchar * voodoo1_tip = ZTX("Fast auto enhance that may work OK"); cchar * voodoo2_tip = ZTX("Fast auto enhance that may work OK"); cchar * retouch_combo_tip = ZTX("Adjust brightness, contrast, color"); cchar * edit_britedist_tip = ZTX("Edit brightness distribution"); cchar * gradients_tip = ZTX("Magnify brightness gradients to enhance details"); cchar * flatten_tip = ZTX("Flatten brightness distribution"); cchar * retinex_tip = ZTX("Rescale brightness - reduce color caste and fog/haze"); cchar * mirror_tip = ZTX("Mirror image horizontally or vertically"); cchar * paint_image_tip = ZTX("Paint image pixels using the mouse"); cchar * clone_image_tip = ZTX("Clone image pixels using the mouse"); cchar * blend_image_tip = ZTX("Blend image pixels using the mouse"); cchar * add_text_tip = ZTX("Add Text to Image"); cchar * add_lines_tip = ZTX("Add lines or arrows to an image"); cchar * paint_edits_tip = ZTX("Paint edit function gradually with mouse"); cchar * lever_edits_tip = ZTX("Leverage edits by brightness or color"); cchar * plugins_tip = ZTX("Edit plugins menu or run a plugin function"); // repair menu cchar * sharpen_tip = ZTX("Make the image look sharper"); cchar * blur_tip = ZTX("Make the image look fuzzy"); cchar * denoise_tip = ZTX("Filter noise from low-light photos"); cchar * redeye_tip = ZTX("Fix red-eyes from electronic flash"); cchar * color_mode_tip = ZTX("Make BW/color, negative/positive, sepia"); cchar * colordep_tip = ZTX("Reduce color depth (posterize)"); cchar * shift_colors_tip = ZTX("Shift/convert colors into other colors"); cchar * color_sat_tip = ZTX("Adjust color intensity (saturation)"); cchar * adjust_RGB_tip = ZTX("Adjust color using RGB or CMY colors"); cchar * adjust_HSL_tip = ZTX("Adjust color using HSL colors"); cchar * zonal_colors_tip = ZTX("Adjust color in selected image areas"); cchar * match_color_tip = ZTX("Match colors on one image with another"); cchar * color_fringes_tip = ZTX("Reduce Chromatic Abberation"); cchar * smart_erase_tip = ZTX("Remove unwanted objects"); cchar * bright_ramp_tip = ZTX("Add a brightness/color ramp across the image"); cchar * remove_dust_tip = ZTX("Remove dust spots from scanned slides"); cchar * anti_alias_tip = ZTX("Smoothen edges with jaggies"); cchar * stuck_pixels_tip = ZTX("Erase known hot and dark pixels"); cchar * paint_transp_tip = ZTX("Paint image transparency using the mouse"); cchar * add_transp_tip = ZTX("Add image transparency based on image attributes"); // warp menu cchar * unbend_tip = ZTX("Remove curvature, esp. panoramas"); cchar * perspective_tip = ZTX("Straighten objects seen from an angle"); cchar * warp_area_tip = ZTX("Distort image areas using the mouse"); cchar * unwarp_closeup_tip = ZTX("Unwarp closeup face photo to remove distortion"); cchar * warp_curved_tip = ZTX("Distort the whole image using the mouse"); cchar * warp_linear_tip = ZTX("Distort the whole image using the mouse"); cchar * warp_affine_tip = ZTX("Distort the whole image using the mouse"); cchar * flatbook_tip = ZTX("Flatten a photographed book page"); cchar * sphere_tip = ZTX("Make a spherical projection of an image"); cchar * selective_rescale_tip = ZTX("Rescale image outside selected areas"); cchar * waves_tip = ZTX("Warp an image with a wave pattern"); cchar * twist_tip = ZTX("Twist image centered at mouse position"); // effects menu cchar * sketch_tip = ZTX("Convert to simulated sketch"); cchar * cartoon_tip = ZTX("Convert image into a cartoon drawing"); cchar * line_drawing_tip = ZTX("Convert to line drawing (edge detection)"); cchar * color_drawing_tip = ZTX("Convert to solid color drawing"); cchar * grad_blur_tip = ZTX("Graduated Blur depending on contrast"); cchar * emboss_tip = ZTX("Create an embossed or 3D appearance"); cchar * tiles_tip = ZTX("Convert to square tiles"); cchar * dots_tip = ZTX("Convert to dots (Roy Lichtenstein effect)"); cchar * painting_tip = ZTX("Convert into a simulated painting"); cchar * vignette_tip = ZTX("Change brightness or color radially"); cchar * texture_tip = ZTX("Add texture to an image"); cchar * pattern_tip = ZTX("Tile image with a repeating pattern"); cchar * mosaic_tip = ZTX("Create a mosaic with tiles made from all images"); cchar * anykernel_tip = ZTX("Process an image using a custom kernel"); cchar * directed_blur_tip = ZTX("Blur an image in the direction of mouse drags"); cchar * blur_background_tip = ZTX("Increasing blur with distance from selected areas"); cchar * alien_colors_tip = ZTX("Change color hue using an algorithm"); // combine menu cchar * HDR_tip = ZTX("Combine bright/dark images for better detail"); cchar * HDF_tip = ZTX("Combine near/far focus images for deeper focus"); cchar * stack_paint_tip = ZTX("Combine images to erase passing people, etc."); cchar * stack_noise_tip = ZTX("Combine noisy images into a low-noise image"); cchar * pano_horz_tip = ZTX("Combine images into a panorama"); cchar * pano_vert_tip = ZTX("Combine images into a vertical panorama"); cchar * pano_PT_tip = ZTX("Combine images into a panorama (panorama tools)"); cchar * mashup_tip = ZTX("Arrange images and text in a layout (montage)"); cchar * montage_tip = ZTX("Combine images into a montage of images"); // process menu cchar * batch_convert_tip = ZTX("Rename/convert/resize/move multiple files"); cchar * batch_upright_tip = ZTX("Upright multiple rotated image files"); cchar * batch_deltrash_tip = ZTX("Delete or Trash multiple files"); cchar * batch_raw_tip = ZTX("Convert camera RAW files using libraw or Raw Therapee"); cchar * scriptfiles_tip = ZTX("Build and run edit script files"); cchar * burn_DVD_tip = ZTX("Burn selected image files to DVD/BlueRay disc"); cchar * duplicates_tip = ZTX("Search all image files and report duplicates"); cchar * export_filelist_tip = ZTX("Create a file of selected image files"); cchar * export_images_tip = ZTX("Export selected image files to a directory"); cchar * batch_tags_tip = ZTX("Add/remove tags for multiple images"); cchar * batch_rename_tags_tip = ZTX("Convert tag names for all images"); cchar * batch_photo_date_time_tip = ZTX("change or shift photo dates/times"); cchar * batch_change_metadata_tip = ZTX("Add/change/delete metadata for multiple images"); cchar * batch_report_metadata_tip = ZTX("Report metadata for multiple images"); cchar * batch_geotags_tip = ZTX("Add/revise geotags for multiple images"); cchar * locations_tip = ZTX("Find all images for a location [date]"); cchar * timeline_tip = ZTX("Show image counts by month, select and report"); cchar * search_images_tip = ZTX("Find images meeting select criteria"); // tools menu cchar * index_tip = ZTX("Index new files and make thumbnails"); cchar * settings_tip = ZTX("Change user preferences"); cchar * KBshortcuts_tip = ZTX("Change Keyboard Shortcut Keys"); cchar * show_brdist_tip = ZTX("Show a brightness distribution graph"); cchar * gridlines_tip = ZTX("Show or revise grid lines"); cchar * line_color_tip = ZTX("Change color of foreground lines"); cchar * show_RGB_tip = ZTX("Show RGB colors at mouse click"); cchar * magnify_tip = ZTX("Magnify image around the mouse position"); cchar * extreme_pixels_tip = ZTX("Highlight darkest and brightest pixels"); cchar * monitor_color_tip = ZTX("Chart to adjust monitor color"); cchar * monitor_gamma_tip = ZTX("Chart to adjust monitor gamma"); cchar * change_lang_tip = ZTX("Change the GUI language"); cchar * untranslated_tip = ZTX("Report missing translations"); cchar * color_profile_tip = ZTX("Convert to another color profile"); cchar * calibrate_printer_tip = ZTX("Calibrate printer colors"); cchar * uninstall_appimage_tip = ZTX("Completely uninstall AppImage package"); cchar * resources_tip = ZTX("Memory and CPU (to terminal/logfile)"); // help menu cchar * quick_start_tip = ZTX("Quick Start mini-guide"); cchar * user_guide_tip = ZTX("Read the user guide"); cchar * user_guide_changes_tip = ZTX("Recent user guide changes"); cchar * readme_tip = ZTX("Technical installation notes"); cchar * changelog_tip = ZTX("List updates by Fotoxx version"); cchar * logfile_tip = ZTX("View the log file and error messages"); cchar * translations_tip = ZTX("How to do Fotoxx translations"); cchar * homepage_tip = ZTX("Show the Fotoxx web page"); cchar * about_tip = ZTX("Version, license, contact, credits"); // gallery menu cchar * alldirs_tip = ZTX("list all directories, click any for gallery view"); cchar * sync_gallery_tip = ZTX("Set gallery from current image file"); cchar * manage_albums_tip = ZTX("Organize images into albums"); cchar * update_albums_tip = ZTX("Update album files to latest version"); cchar * replace_album_file_tip = ZTX("Replace album file with another file"); cchar * slideshow_tip = ZTX("Start a slide show"); // build menu entries ------------------------------------------------- #define MENUENT(_topmenu, _text, _icon, _desc, _func, _arg) \ me = Nmenus++; \ if (me >= maxmenus) zappcrash("maxmenus exceeded"); \ menutab[me].topmenu = _topmenu; \ menutab[me].menu = _text; \ menutab[me].icon = _icon; \ menutab[me].desc = _desc; \ menutab[me].func = _func; \ if (_arg) menutab[me].arg = _arg; \ else menutab[me].arg = _text; \ int me; Nmenus = 0; // build menus first mFile = create_popmenu(); MENUENT(mFile, ZTX("New Window"), 0, clone_tip, m_clone, 0 ); MENUENT(mFile, ZTX("Sync Gallery"), 0, sync_gallery_tip, m_sync_gallery, 0 ); MENUENT(mFile, ZTX("Recently Seen Images"), 0, recentfiles_tip, m_recentfiles, 0 ); MENUENT(mFile, ZTX("Newest Images"), 0, newfiles_tip, m_newfiles, 0 ); MENUENT(mFile, ZTX("Open Image File"), 0, open_tip, m_open, 0 ); MENUENT(mFile, ZTX("Cycle 2 Previous Files"), 0, cycle2files_tip, m_cycle2files, 0 ); MENUENT(mFile, ZTX("Cycle 3 Previous Files"), 0, cycle3files_tip, m_cycle3files, 0 ); MENUENT(mFile, ZTX("Open RAW (Raw Therapee)"), 0, rawtherapee_tip, m_rawtherapee, 0 ); MENUENT(mFile, ZTX("View 360° Panorama"), 0, view360_tip, m_view360, 0); MENUENT(mFile, ZTX("New Blank Image"), 0, blank_image_tip, m_blank_image, 0 ); MENUENT(mFile, ZTX("Blank Window"), 0, blank_window_tip, m_blank_window, 0 ); MENUENT(mFile, ZTX("Rename Image File"), 0, rename_tip, m_rename, 0 ); MENUENT(mFile, ZTX("Copy/Move to Location"), 0, copy_move_tip, m_copy_move, 0 ); MENUENT(mFile, ZTX("Copy to Desktop"), 0, copyto_desktop_tip, m_copyto_desktop, 0 ); MENUENT(mFile, ZTX("Copy to Clipboard"), 0, copyto_clipboard_tip, m_copyto_clip, 0 ); MENUENT(mFile, ZTX("Copy to Image Cache"), 0, copyto_image_cache_tip, m_copyto_cache, 0 ); MENUENT(mFile, ZTX("Show on Net Map"), 0, show_on_net_map_tip, m_netmap_zoomin, 0 ); MENUENT(mFile, ZTX("Delete/Trash Image File"), 0, deltrash_tip, m_delete_trash, 0 ); MENUENT(mFile, ZTX("Print Image"), 0, print_tip, m_print, 0 ); MENUENT(mFile, ZTX("Print Calibrated Image"), 0, print_calibrated_tip, m_print_calibrated, 0 ); MENUENT(mFile, ZTX("Quit Fotoxx"), 0, quit_tip, m_quit, 0 ); mMeta = create_popmenu(); MENUENT(mMeta, ZTX("View Metadata (short)"), 0, meta_view_short_tip, m_meta_view_short, 0 ); MENUENT(mMeta, ZTX("View Metadata (long)"), 0, meta_view_long_tip, m_meta_view_long, 0 ); MENUENT(mMeta, ZTX("Edit Metadata"), 0, edit_metadata_tip, m_meta_edit, 0 ); MENUENT(mMeta, ZTX("Edit Any Metadata"), 0, meta_edit_any_tip, m_meta_edit_any, 0 ); MENUENT(mMeta, ZTX("Delete Metadata"), 0, meta_delete_tip, m_meta_delete, 0 ); MENUENT(mMeta, ZTX("Show Captions on Image"), 0, captions_tip, m_captions, 0 ); mArea = create_popmenu(); MENUENT(mArea, ZTX("Select Area"), 0, select_tip, m_select, 0 ); MENUENT(mArea, ZTX("Find Area Gap"), 0, select_find_gap_tip, m_select_find_gap, 0 ); MENUENT(mArea, ZTX("Select Hairy"), 0, select_hairy_tip, m_select_hairy, 0); MENUENT(mArea, ZTX("Show Area"), 0, select_show_tip, m_select_show, 0 ); MENUENT(mArea, ZTX("Hide Area"), 0, select_hide_tip, m_select_hide, 0 ); MENUENT(mArea, ZTX("Enable Area"), 0, select_enable_tip, m_select_enable, 0 ); MENUENT(mArea, ZTX("Disable Area"), 0, select_disable_tip, m_select_disable, 0 ); MENUENT(mArea, ZTX("Invert Area"), 0, select_invert_tip, m_select_invert, 0 ); MENUENT(mArea, ZTX("Unselect Area"), 0, select_unselect_tip, m_select_unselect, 0 ); MENUENT(mArea, ZTX("Copy Area"), 0, select_copy_tip, m_select_save, 0 ); MENUENT(mArea, ZTX("Paste Area"), 0, select_paste_tip, m_select_paste, 0 ); MENUENT(mArea, ZTX("Open Area File"), 0, select_open_tip, m_select_open, 0 ); MENUENT(mArea, ZTX("Save Area File"), 0, select_save_tip, m_select_save, 0 ); mEdit = create_popmenu(); MENUENT(mEdit, ZTX("Trim/Rotate"), 0, trim_rotate_tip, m_trim_rotate, 0 ); MENUENT(mEdit, ZTX("Upright"), 0, upright_tip, m_upright, 0 ); MENUENT(mEdit, ZTX("Resize"), 0, resize_tip, m_resize, 0 ); MENUENT(mEdit, ZTX("Voodoo 1"), 0, voodoo1_tip, m_voodoo1, 0 ); MENUENT(mEdit, ZTX("Voodoo 2"), 0, voodoo2_tip, m_voodoo2, 0); MENUENT(mEdit, ZTX("Retouch Combo"), 0, retouch_combo_tip, m_retouch_combo, 0 ); MENUENT(mEdit, ZTX("Edit Brightness"), 0, edit_britedist_tip, m_edit_britedist, 0 ); MENUENT(mEdit, ZTX("Gradients"), 0, gradients_tip, m_gradients, 0 ); MENUENT(mEdit, ZTX("Flatten"), 0, flatten_tip, m_flatten, 0 ); MENUENT(mEdit, "Retinex", 0, retinex_tip, m_retinex, 0 ); MENUENT(mEdit, ZTX("Mirror Image"), 0, mirror_tip, m_mirror, 0 ); MENUENT(mEdit, ZTX("Paint Image"), 0, paint_image_tip, m_paint_image, 0 ); MENUENT(mEdit, ZTX("Clone Image"), 0, clone_image_tip, m_clone_image, 0 ); MENUENT(mEdit, ZTX("Blend Image"), 0, blend_image_tip, m_blend_image, 0 ); MENUENT(mEdit, ZTX("Add Text"), 0, add_text_tip, m_add_text, 0 ); MENUENT(mEdit, ZTX("Add Line"), 0, add_lines_tip, m_add_lines, 0 ); MENUENT(mEdit, ZTX("Paint Edits"), 0, paint_edits_tip, m_paint_edits, 0 ); MENUENT(mEdit, ZTX("Leverage Edits"), 0, lever_edits_tip, m_lever_edits, 0 ); MENUENT(mEdit, ZTX("Plugins"), 0, plugins_tip, m_plugins, 0); mRep = create_popmenu(); MENUENT(mRep, ZTX("Sharpen"), 0, sharpen_tip, m_sharpen, 0 ); MENUENT(mRep, ZTX("Blur"), 0, blur_tip, m_blur, 0 ); MENUENT(mRep, ZTX("Denoise"), 0, denoise_tip, m_denoise, 0 ); MENUENT(mRep, ZTX("Red Eyes"), 0, redeye_tip, m_redeye, 0 ); MENUENT(mRep, ZTX("Color Mode"), 0, color_mode_tip, m_color_mode, 0 ); MENUENT(mRep, ZTX("Color Depth"), 0, colordep_tip, m_colordep, 0 ); MENUENT(mRep, ZTX("Shift Colors"), 0, shift_colors_tip, m_shift_colors, 0 ); MENUENT(mRep, ZTX("Color Saturation"), 0, color_sat_tip, m_colorsat, 0 ); MENUENT(mRep, ZTX("Adjust RGB/CMY"), 0, adjust_RGB_tip, m_adjust_RGB, 0 ); MENUENT(mRep, ZTX("Adjust HSL"), 0, adjust_HSL_tip, m_adjust_HSL, 0 ); MENUENT(mRep, ZTX("Zonal Colors"), 0, zonal_colors_tip, m_zonal_colors, 0 ); MENUENT(mRep, ZTX("Match Colors"), 0, match_color_tip, m_match_color, 0 ); MENUENT(mRep, ZTX("Color Fringes"), 0, color_fringes_tip, m_color_fringes, 0 ); MENUENT(mRep, ZTX("Smart Erase"), 0, smart_erase_tip, m_smart_erase, 0 ); MENUENT(mRep, ZTX("Brightness Ramp"), 0, bright_ramp_tip, m_bright_ramp, 0 ); MENUENT(mRep, ZTX("Remove Dust"), 0, remove_dust_tip, m_remove_dust, 0 ); MENUENT(mRep, ZTX("Anti-Alias"), 0, anti_alias_tip, m_anti_alias, 0 ); MENUENT(mRep, ZTX("Stuck Pixels"), 0, stuck_pixels_tip, m_stuck_pixels, 0 ); MENUENT(mRep, ZTX("Paint Transparency"), 0, paint_transp_tip, m_paint_transp, 0 ); MENUENT(mRep, ZTX("Add Transparency"), 0, add_transp_tip, m_add_transp, 0 ); mWarp = create_popmenu(); MENUENT(mWarp, ZTX("Unbend"), 0, unbend_tip, m_unbend, 0 ); MENUENT(mWarp, ZTX("Fix Perspective"), 0, perspective_tip, m_perspective, 0 ); MENUENT(mWarp, ZTX("Warp area"), 0, warp_area_tip, m_warp_area, 0 ); MENUENT(mWarp, ZTX("Unwarp Closeup"), 0, unwarp_closeup_tip, m_unwarp_closeup, 0 ); MENUENT(mWarp, ZTX("Warp curved"), 0, warp_curved_tip, m_warp_curved, 0 ); MENUENT(mWarp, ZTX("Warp linear"), 0, warp_linear_tip, m_warp_linear, 0 ); MENUENT(mWarp, ZTX("Warp affine"), 0, warp_affine_tip, m_warp_affine, 0 ); MENUENT(mWarp, ZTX("Flatten Book Page"), 0, flatbook_tip, m_flatbook, 0 ); MENUENT(mWarp, ZTX("Spherical Projection"), 0, sphere_tip, m_sphere, 0); MENUENT(mWarp, ZTX("Selective Rescale"), 0, selective_rescale_tip, m_selective_rescale, 0); MENUENT(mWarp, ZTX("Make Waves"), 0, waves_tip, m_waves, 0); MENUENT(mWarp, ZTX("Twist Image"), 0, twist_tip, m_twist, 0); mEff = create_popmenu(); MENUENT(mEff, ZTX("Sketch"), 0, sketch_tip, m_sketch, 0 ); MENUENT(mEff, ZTX("Cartoon"), 0, cartoon_tip, m_cartoon, 0 ); MENUENT(mEff, ZTX("Line Drawing"), 0, line_drawing_tip, m_line_drawing, 0 ); MENUENT(mEff, ZTX("Color Drawing"), 0, color_drawing_tip, m_color_drawing, 0 ); MENUENT(mEff, ZTX("Graduated Blur"), 0, grad_blur_tip, m_grad_blur, 0 ); MENUENT(mEff, ZTX("Embossing"), 0, emboss_tip, m_emboss, 0 ); MENUENT(mEff, ZTX("Tiles"), 0, tiles_tip, m_tiles, 0 ); MENUENT(mEff, ZTX("Dots"), 0, dots_tip, m_dots, 0 ); MENUENT(mEff, ZTX("Painting"), 0, painting_tip, m_painting, 0 ); MENUENT(mEff, ZTX("Vignette"), 0, vignette_tip, m_vignette, 0 ); MENUENT(mEff, ZTX("Texture"), 0, texture_tip, m_texture, 0 ); MENUENT(mEff, ZTX("Pattern"), 0, pattern_tip, m_pattern, 0 ); MENUENT(mEff, ZTX("Mosaic"), 0, mosaic_tip, m_mosaic, 0); MENUENT(mEff, ZTX("Custom Kernel"), 0, anykernel_tip, m_anykernel, 0); MENUENT(mEff, ZTX("Directed Blur"), 0, directed_blur_tip, m_directed_blur, 0); MENUENT(mEff, ZTX("Blur Background"), 0, blur_background_tip, m_blur_background, 0); MENUENT(mEff, ZTX("Alien Colors"), 0, alien_colors_tip, m_alien_colors, 0); mComb = create_popmenu(); MENUENT(mComb, ZTX("High Dynamic Range"), 0, HDR_tip, m_HDR, 0 ); MENUENT(mComb, ZTX("High Depth of Field"), 0, HDF_tip, m_HDF, 0 ); MENUENT(mComb, ZTX("Stack/Paint"), 0, stack_paint_tip, m_stack_paint, 0 ); MENUENT(mComb, ZTX("Stack/Noise"), 0, stack_noise_tip, m_stack_noise, 0 ); MENUENT(mComb, ZTX("Panorama"), 0, pano_horz_tip, m_pano_horz, 0 ); MENUENT(mComb, ZTX("Vertical Panorama"), 0, pano_vert_tip, m_pano_vert, 0 ); MENUENT(mComb, ZTX("PT Panorama"), 0, pano_PT_tip, m_pano_PT, 0 ); MENUENT(mComb, ZTX("Montage"), 0, montage_tip, m_montage, 0 ); MENUENT(mComb, ZTX("Mashup"), 0, mashup_tip, m_mashup, 0 ); mProc = create_popmenu(); MENUENT(mProc, ZTX("Batch Convert"), 0, batch_convert_tip, m_batch_convert, 0 ); MENUENT(mProc, ZTX("Batch Upright"), 0, batch_upright_tip, m_batch_upright, 0 ); MENUENT(mProc, ZTX("Batch Delete/Trash"), 0, batch_deltrash_tip, m_batch_deltrash, 0 ); MENUENT(mProc, ZTX("Batch RAW"), 0, batch_raw_tip, m_batch_raw, 0 ); MENUENT(mProc, ZTX("Script Files"), 0, scriptfiles_tip, m_scriptfiles, 0 ); MENUENT(mProc, ZTX("Burn Images to DVD/BlueRay"), 0, burn_DVD_tip, m_burn_DVD, 0 ); MENUENT(mProc, ZTX("Find Duplicate Images"), 0, duplicates_tip, m_duplicates, 0 ); MENUENT(mProc, ZTX("Export File List"), 0, export_filelist_tip, m_export_filelist, 0 ); MENUENT(mProc, ZTX("Export Image Files"), 0, export_images_tip, m_export_images, 0 ); MENUENT(mProc, ZTX("Batch Add/Remove Tags"), 0, batch_tags_tip, m_batch_tags, 0 ); MENUENT(mProc, ZTX("Batch Rename Tags"), 0, batch_rename_tags_tip, m_batch_rename_tags, 0 ); MENUENT(mProc, ZTX("Batch Photo Date/Time"), 0, batch_photo_date_time_tip, m_batch_photo_date_time, 0 ); MENUENT(mProc, ZTX("Batch Add/Change Metadata"), 0, batch_change_metadata_tip, m_batch_change_metadata, 0 ); MENUENT(mProc, ZTX("Batch Report Metadata"), 0, batch_report_metadata_tip, m_batch_report_metadata, 0 ); MENUENT(mProc, ZTX("Batch Geotags"), 0, batch_geotags_tip, m_batch_geotags, 0 ); MENUENT(mProc, ZTX("Image Locations/Dates"), 0, locations_tip, m_locations, 0 ); MENUENT(mProc, ZTX("Image Timeline"), 0, timeline_tip, m_timeline, 0 ); MENUENT(mProc, ZTX("Search Images"), 0, search_images_tip, m_search_images, 0 ); mTools = create_popmenu(); MENUENT(mTools, ZTX("Index Image Files"), 0, index_tip, m_index, 0 ); MENUENT(mTools, ZTX("User Settings"), 0, settings_tip, m_settings, 0 ); MENUENT(mTools, ZTX("Keyboard Shortcuts"), 0, KBshortcuts_tip, m_KBshortcuts, 0 ); MENUENT(mTools, ZTX("Brightness Graph"), 0, show_brdist_tip, m_show_brdist, 0 ); MENUENT(mTools, ZTX("Grid Lines"), 0, gridlines_tip, m_gridlines, 0 ); MENUENT(mTools, ZTX("Line Color"), 0, line_color_tip, m_line_color, 0 ); MENUENT(mTools, ZTX("Show RGB"), 0, show_RGB_tip, m_show_RGB, 0 ); MENUENT(mTools, ZTX("Magnify Image"), 0, magnify_tip, m_magnify, 0 ); MENUENT(mTools, ZTX("Dark/Bright Pixels"), 0, extreme_pixels_tip, m_extreme_pixels, 0 ); MENUENT(mTools, ZTX("Monitor Color"), 0, monitor_color_tip, m_monitor_color, 0 ); MENUENT(mTools, ZTX("Monitor Gamma"), 0, monitor_gamma_tip, m_monitor_gamma, 0 ); MENUENT(mTools, ZTX("Change Language"), 0, change_lang_tip, m_change_lang, 0 ); MENUENT(mTools, ZTX("Missing Translations"), 0, untranslated_tip, m_untranslated, 0 ); MENUENT(mTools, ZTX("Color Profile"), 0, color_profile_tip, m_color_profile, 0 ); MENUENT(mTools, ZTX("Calibrate Printer"), 0, calibrate_printer_tip, m_calibrate_printer, 0 ); MENUENT(mTools, ZTX("Uninstall AppImage"), 0, uninstall_appimage_tip, m_uninstall_appimage, 0 ); MENUENT(mTools, "Show Resources", 0, resources_tip, m_resources, 0 ); // MENUENT(mTools, "zappcrash test", 0, "zappcrash test", m_zappcrash, 0 ); mHelp = create_popmenu(); MENUENT(mHelp, ZTX("Quick Start"), 0, quick_start_tip, m_help, 0 ); MENUENT(mHelp, ZTX("User Guide"), 0, user_guide_tip, m_help, 0 ); MENUENT(mHelp, ZTX("User Guide Changes"), 0, user_guide_changes_tip, m_help, 0 ); MENUENT(mHelp, ZTX("README"), 0, readme_tip, m_help, 0 ); MENUENT(mHelp, ZTX("Change Log"), 0, changelog_tip, m_help, 0 ); MENUENT(mHelp, ZTX("Log File"), 0, logfile_tip, m_help, 0 ); MENUENT(mHelp, ZTX("Translations"), 0, translations_tip, m_help, 0 ); MENUENT(mHelp, ZTX("Home Page"), 0, homepage_tip, m_help, 0 ); MENUENT(mHelp, ZTX("About"), 0, about_tip, m_help, 0 ); mGmenu = create_popmenu(); MENUENT(mGmenu, ZTX("Sync Gallery"), 0, sync_gallery_tip, m_sync_gallery, 0 ); MENUENT(mGmenu, ZTX("All Directories"), 0, alldirs_tip, m_alldirs, 0 ); MENUENT(mGmenu, ZTX("Bookmarks"), 0, bookmarks_tip, m_goto_bookmark, 0 ); MENUENT(mGmenu, ZTX("Manage Albums"), 0, manage_albums_tip, m_manage_albums, 0 ); MENUENT(mGmenu, ZTX("Update Album Files"), 0, update_albums_tip, m_update_albums, 0 ); MENUENT(mGmenu, ZTX("Replace Album File"), 0, replace_album_file_tip, m_replace_album_file, 0 ); MENUENT(mGmenu, ZTX("Slide Show"), 0, slideshow_tip, m_slideshow, 0 ); // F view menu buttons MENUENT(0, Bimage, "viewF-check.png", imagefile_tip, m_viewmode, "F" ); MENUENT(0, ZTX("Gallery"), "viewG.png", gallery_tip, m_viewmode, "G" ); MENUENT(0, ZTX("World Maps"), "viewW.png", worldmaps_tip, m_viewmode, "W" ); MENUENT(0, ZTX("Net Maps"), "viewM.png", netmaps_tip, m_viewmode, "M" ); MENUENT(0, 0, "separator.png", 0, 0, 0 ); MENUENT(0, ZTX("Favorites"), "favorites.png", favorites_tip, m_favorites, 0 ); MENUENT(0, ZTX("File"), "file.png", file_tip, (cbFunc *) popup_menu, (cchar *) mFile); MENUENT(0, ZTX("Save File"), "save.png", save_tip, m_file_save, 0 ); MENUENT(0, ZTX("Prev/Next"), "prev_next.png", prev_next_tip, m_prev_next, 0 ); MENUENT(0, "Metadata", "metadata.png", metadata_tip, (cbFunc *) popup_menu, (cchar *) mMeta); MENUENT(0, ZTX("Areas"), "areas.png", areas_tip, (cbFunc *) popup_menu, (cchar *) mArea); MENUENT(0, ZTX("Undo/Redo"), "undo_redo.png", undo_redo_tip, m_undo_redo, 0 ); MENUENT(0, ZTX("Edit"), "edit.png", edit_tip, (cbFunc *) popup_menu, (cchar *) mEdit); MENUENT(0, ZTX("Repair"), "repair.png", repair_tip, (cbFunc *) popup_menu, (cchar *) mRep); MENUENT(0, ZTX("Warp"), "warp.png", warp_tip, (cbFunc *) popup_menu, (cchar *) mWarp); MENUENT(0, ZTX("Effects"), "effects.png", effects_tip, (cbFunc *) popup_menu, (cchar *) mEff); MENUENT(0, ZTX("Combine"), "combine.png", combine_tip, (cbFunc *) popup_menu, (cchar *) mComb); MENUENT(0, ZTX("Process"), "process.png", process_tip, (cbFunc *) popup_menu, (cchar *) mProc); MENUENT(0, ZTX("Tools"), "tools.png", tools_tip, (cbFunc *) popup_menu, (cchar *) mTools); MENUENT(0, ZTX("Help"), "help.png", help_tip, (cbFunc *) popup_menu, (cchar *) mHelp); NFmenu = Nmenus; // end of F view menus // G view menu buttons MENUENT(0, Bimage, "viewF.png", imagefile_tip, m_viewmode, "F" ); MENUENT(0, ZTX("Gallery"), "viewG-check.png", gallery_tip, m_viewmode, "G" ); MENUENT(0, ZTX("World Maps"), "viewW.png", worldmaps_tip, m_viewmode, "W" ); MENUENT(0, ZTX("Net Maps"), "viewM.png", netmaps_tip, m_viewmode, "M" ); MENUENT(0, 0, "separator.png", 0, 0, 0 ); MENUENT(0, ZTX("Favorites"), "favorites.png", favorites_tip, m_favorites, 0 ); MENUENT(0, "Gallery Menu", "gmenu.png", gmenu_tip, (cbFunc *) popup_menu, (cchar *) mGmenu); MENUENT(0, "Metadata", "metadata.png", metadata_tip, (cbFunc *) popup_menu, (cchar *) mMeta); MENUENT(0, ZTX("Zoom+"), "zoom+.png", thumb_increase_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("Zoom-"), "zoom-.png", thumb_reduce_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("Sort"), "sort.png", sort_order_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("First"), "top.png", jump_begin_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("Last"), "bottom.png", jump_end_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("Page Up"), "up.png", previous_page_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("Page Down"), "down.png", next_page_tip, navi::menufuncx, 0 ); MENUENT(0, ZTX("Combine"), "combine.png", combine_tip, (cbFunc *) popup_menu, (cchar *) mComb); MENUENT(0, ZTX("Process"), "process.png", process_tip, (cbFunc *) popup_menu, (cchar *) mProc); MENUENT(0, ZTX("Tools"), "tools.png", tools_tip, (cbFunc *) popup_menu, (cchar *) mTools); MENUENT(0, ZTX("Help"), "help.png", help_tip, (cbFunc *) popup_menu, (cchar *) mHelp); NGmenu = Nmenus; // end of G view menus // W view menu buttons MENUENT(0, Bimage, "viewF.png", imagefile_tip, m_viewmode, "F" ); MENUENT(0, ZTX("Gallery"), "viewG.png", gallery_tip, m_viewmode, "G" ); MENUENT(0, ZTX("World Maps"), "viewW-check.png", worldmaps_tip, m_viewmode, "W" ); MENUENT(0, ZTX("Net Maps"), "viewM.png", netmaps_tip, m_viewmode, "M" ); MENUENT(0, 0, "separator.png", 0, 0, 0 ); MENUENT(0, ZTX("Choose Map"), "choosemap.png", load_filemap_tip, m_load_filemap, 0 ); MENUENT(0, ZTX("Map Markers"), "viewW-dots.png", mark_filemap_tip, m_set_map_markers, 0 ); MENUENT(0, ZTX("Help"), "help.png", help_tip, (cbFunc *) popup_menu, (cchar *) mHelp); NWmenu = Nmenus; // end of W view menus // M view menu buttons MENUENT(0, Bimage, "viewF.png", imagefile_tip, m_viewmode, "F" ); MENUENT(0, ZTX("Gallery"), "viewG.png", gallery_tip, m_viewmode, "G" ); MENUENT(0, ZTX("World Maps"), "viewW.png", worldmaps_tip, m_viewmode, "W" ); MENUENT(0, ZTX("Net Maps"), "viewM-check.png", netmaps_tip, m_viewmode, "M" ); MENUENT(0, 0, "separator.png", 0, 0, 0 ); MENUENT(0, ZTX("Map Source"), "choosemap.png", netmap_source_tip, m_netmap_source, 0 ); MENUENT(0, ZTX("Map Markers"), "viewM-dots.png", mark_netmap_tip, m_set_map_markers, 0 ); MENUENT(0, ZTX("Map Locations"), "netmap-locs.png", netmap_locs_tip, m_netmap_locs, 0 ); MENUENT(0, ZTX("Help"), "help.png", help_tip, (cbFunc *) popup_menu, (cchar *) mHelp); NMmenu = Nmenus; // end of M view menus // dummy menus only for KB shortcuts MENUENT(0, "Undo", "KB shortcut", 0, m_undo, 0 ); MENUENT(0, "Redo", "KB shortcut", 0, m_redo, 0 ); MENUENT(0, "Show Hidden Files", "KB shortcut", 0, m_show_hidden, 0 ); // 18.01 // build the menu buttons for the F/G/W/M windows float frgb[3], brgb[3]; frgb[0] = MFrgb[0] / 256.0; // menu font color 18.01 frgb[1] = MFrgb[1] / 256.0; // convert range to 0-1 frgb[2] = MFrgb[2] / 256.0; brgb[0] = MBrgb[0] / 256.0; // menu background color 18.01 brgb[1] = MBrgb[1] / 256.0; // convert range to 0-1 brgb[2] = MBrgb[2] / 256.0; Vmenu *Fvm = Vmenu_new(Fmenu,frgb,brgb); Vmenu *Gvm = Vmenu_new(Gmenu,frgb,brgb); Vmenu *Wvm = Vmenu_new(Wmenu,frgb,brgb); Vmenu *Mvm = Vmenu_new(Mmenu,frgb,brgb); Vmenu *Xvm; int siz = iconsize; // user settings parameter for (me = 0; me < Nmenus; me++) { cchar *pp = menutab[me].icon; // skip entries for KB shortcuts if (pp && strmatch(pp,"KB shortcut")) continue; if (me < NFmenu) Xvm = Fvm; // F-view menu else if (me < NGmenu) Xvm = Gvm; // G-view else if (me < NWmenu) Xvm = Wvm; // W-view else Xvm = Mvm; // M-view if (menutab[me].topmenu) // submenu within top menu add_popmenu_item(menutab[me].topmenu, menutab[me].menu, menutab[me].func, menutab[me].arg, menutab[me].desc); else // top menu (button) { if (strmatch(menutab[me].icon,"separator.png")) // separator bar Vmenu_add(Xvm, 0, menutab[me].icon, siz, siz/3, 0, 0, 0); else if (strmatch(menu_style,"icons")) { // icons only if (menutab[me].icon) Vmenu_add(Xvm, 0, menutab[me].icon,siz,siz,menutab[me].desc, menutab[me].func, menutab[me].arg); else // no icon, use menu text Vmenu_add(Xvm, menutab[me].menu, 0, 0, 0, menutab[me].desc, menutab[me].func, menutab[me].arg); } else // icons + menu text Vmenu_add(Xvm, menutab[me].menu, menutab[me].icon, siz, siz, menutab[me].desc, menutab[me].func, menutab[me].arg); } } // right-click popup menus -------------------------------------------------- cchar *menupopimage = ZTX("Popup Image"); cchar *menumeta1 = ZTX("View Metadata"); cchar *menumeta2 = ZTX("Edit Metadata"); cchar *menumeta3 = ZTX("Edit Any Metadata"); cchar *menurename = ZTX("Rename"); cchar *menucopymove = ZTX("Copy/Move to Location"); cchar *menucopytodesktop = ZTX("Copy to Desktop"); cchar *menucopytoclip = ZTX("Copy to Clipboard"); cchar *menuremovefromalbum = ZTX("Remove from Album"); cchar *menureplacealbumfile = ZTX("Replace Album File"); // 18.01 cchar *menucuttocache = ZTX("Cut to Image Cache"); cchar *menucopytocache = ZTX("Copy to Image Cache"); cchar *menupastecachehere = ZTX("Paste Image Cache Here (clear)"); cchar *menupastecachekeep = ZTX("Paste Image Cache Here (keep)"); cchar *menutrimrotate = ZTX("Trim/Rotate"); cchar *menuresize = ZTX("Resize"); cchar *menuupright = ZTX("Upright"); cchar *menuvoodoo1 = "Voodoo 1"; cchar *menuvoodoo2 = "Voodoo 2"; cchar *menucombo = ZTX("Retouch Combo"); cchar *menubrightdist = ZTX("Edit Brightness"); cchar *menuflatten = ZTX("Flatten"); cchar *menugradients = ZTX("Gradients"); cchar *menuselect = ZTX("Select Area"); cchar *menuopenraw = ZTX("Open RAW (Raw Therapee)"); cchar *menunetzoom = ZTX("Show on Net Map"); cchar *menudeltrash = ZTX("Delete/Trash ..."); popmenu_image = create_popmenu(); // popup menu for image files add_popmenu_item(popmenu_image,menumeta1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_image,menumeta2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_image,menumeta3,popup_menufunc,"edit any metadata"); add_popmenu_item(popmenu_image,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_image,menucopymove,popup_menufunc,"copymove"); add_popmenu_item(popmenu_image,menucopytodesktop,popup_menufunc,"copytodesktop"); add_popmenu_item(popmenu_image,menucopytoclip,popup_menufunc,"copytoclip"); add_popmenu_item(popmenu_image,menucopytocache,popup_menufunc,"copytocache"); add_popmenu_item(popmenu_image,menureplacealbumfile,popup_menufunc,"replacealbumfile"); // 18.01 add_popmenu_item(popmenu_image,menuupright,popup_menufunc,"upright"); add_popmenu_item(popmenu_image,menutrimrotate,popup_menufunc,"trim/rotate"); add_popmenu_item(popmenu_image,menuresize,popup_menufunc,"resize"); add_popmenu_item(popmenu_image,menuvoodoo1,popup_menufunc,"voodoo1"); add_popmenu_item(popmenu_image,menuvoodoo2,popup_menufunc,"voodoo2"); add_popmenu_item(popmenu_image,menucombo,popup_menufunc,"combo"); add_popmenu_item(popmenu_image,menubrightdist,popup_menufunc,"edit brightness"); add_popmenu_item(popmenu_image,menuflatten,popup_menufunc,"flatten"); add_popmenu_item(popmenu_image,menugradients,popup_menufunc,"gradients"); add_popmenu_item(popmenu_image,menuselect,popup_menufunc,"select"); add_popmenu_item(popmenu_image,menunetzoom,popup_menufunc,"netmap_zoomin"); add_popmenu_item(popmenu_image,menudeltrash,popup_menufunc,"delete/trash"); popmenu_raw = create_popmenu(); // popup menu for RAW files add_popmenu_item(popmenu_raw,menuopenraw,m_rawtherapee,0); add_popmenu_item(popmenu_raw,menumeta1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_raw,menumeta2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_raw,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_raw,menucopymove,popup_menufunc,"copymove"); add_popmenu_item(popmenu_raw,menucopytodesktop,popup_menufunc,"copytodesktop"); add_popmenu_item(popmenu_raw,menunetzoom,popup_menufunc,"netmap_zoomin"); add_popmenu_item(popmenu_raw,menudeltrash,popup_menufunc,"delete/trash"); popmenu_video = create_popmenu(); // popup menu for VIDEO files 17.08 add_popmenu_item(popmenu_video,menumeta1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_video,menumeta2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_video,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_video,menucopymove,popup_menufunc,"copymove"); add_popmenu_item(popmenu_video,menucopytodesktop,popup_menufunc,"copytodesktop"); add_popmenu_item(popmenu_video,menucopytocache,popup_menufunc,"copytocache"); add_popmenu_item(popmenu_video,menunetzoom,popup_menufunc,"netmap_zoomin"); add_popmenu_item(popmenu_video,menudeltrash,popup_menufunc,"delete/trash"); popmenu_thumb = create_popmenu(); // gallery thumbnail popup menu add_popmenu_item(popmenu_thumb,menupopimage,popup_menufunc,"popimage"); add_popmenu_item(popmenu_thumb,menumeta1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_thumb,menumeta2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_thumb,menumeta3,popup_menufunc,"edit any metadata"); add_popmenu_item(popmenu_thumb,menurename,popup_menufunc,"rename"); add_popmenu_item(popmenu_thumb,menucopymove,popup_menufunc,"copymove"); add_popmenu_item(popmenu_thumb,menucopytodesktop,popup_menufunc,"copytodesktop"); add_popmenu_item(popmenu_thumb,menucopytoclip,popup_menufunc,"copytoclip"); add_popmenu_item(popmenu_thumb,menucopytocache,popup_menufunc,"copytocache"); add_popmenu_item(popmenu_thumb,menureplacealbumfile,popup_menufunc,"replacealbumfile"); // 18.01 add_popmenu_item(popmenu_thumb,menuupright,popup_menufunc,"upright"); add_popmenu_item(popmenu_thumb,menunetzoom,popup_menufunc,"netmap_zoomin"); add_popmenu_item(popmenu_thumb,menudeltrash,popup_menufunc,"delete/trash"); popmenu_album = create_popmenu(); // album gallery thumbnail popup menu add_popmenu_item(popmenu_album,menupopimage,popup_menufunc,"popimage"); add_popmenu_item(popmenu_album,menumeta1,popup_menufunc,"view metadata"); add_popmenu_item(popmenu_album,menumeta2,popup_menufunc,"edit metadata"); add_popmenu_item(popmenu_album,menumeta3,popup_menufunc,"edit any metadata"); add_popmenu_item(popmenu_album,menucopytodesktop,popup_menufunc,"copytodesktop"); add_popmenu_item(popmenu_album,menucopytoclip,popup_menufunc,"copytoclip"); add_popmenu_item(popmenu_album,menucopytocache,popup_menufunc,"copytocache"); add_popmenu_item(popmenu_album,menucuttocache,popup_menufunc,"cuttocache"); add_popmenu_item(popmenu_album,menupastecachekeep,popup_menufunc,"pastecachekeep"); add_popmenu_item(popmenu_album,menupastecachehere,popup_menufunc,"pastecachehere"); add_popmenu_item(popmenu_album,menuremovefromalbum,popup_menufunc,"removefromalbum"); return; } // right-click popup menu response function void popup_menufunc(GtkWidget *, cchar *menu) { if (strmatch(menu,"popimage")) gallery_popimage(); // funcs for main and gallery windows if (strmatch(menu,"view metadata")) meta_view(1); if (strmatch(menu,"edit metadata")) m_meta_edit(0,0); if (strmatch(menu,"edit any metadata")) m_meta_edit_any(0,0); if (strmatch(menu,"rename")) m_rename(0,0); // these use clicked_file if defined, if (strmatch(menu,"upright")) m_upright(0,0); // else they use curr_file. if (strmatch(menu,"copymove")) m_copy_move(0,0); if (strmatch(menu,"copytodesktop")) m_copyto_desktop(0,0); if (strmatch(menu,"delete/trash")) m_delete_trash(0,0); if (strmatch(menu,"netmap_zoomin")) m_netmap_zoomin(0,0); if (strmatch(menu,"copytoclip")) m_copyto_clip(0,0); if (strmatch(menu,"copytocache")) m_copyto_cache(0,0); if (strmatch(menu,"removefromalbum")) m_album_removefile(0,0); // funcs for album gallery if (strmatch(menu,"cuttocache")) m_album_cutfile(0,0); // depend on clicked_file being defined if (strmatch(menu,"pastecachehere")) m_album_pastecache(0,"clear"); if (strmatch(menu,"pastecachekeep")) m_album_pastecache(0,"keep"); if (strmatch(menu,"replacealbumfile")) m_replace_album_file(0,0); // 18.01 if (strmatch(menu,"trim/rotate")) m_trim_rotate(0,0); // functions using curr_file only if (strmatch(menu,"resize")) m_resize(0,0); // (not used for gallery/thumbnail click) if (strmatch(menu,"voodoo1")) m_voodoo1(0,0); if (strmatch(menu,"voodoo2")) m_voodoo2(0,0); if (strmatch(menu,"combo")) m_retouch_combo(0,0); if (strmatch(menu,"edit brightness")) m_edit_britedist(0,0); if (strmatch(menu,"flatten")) m_flatten(0,0); if (strmatch(menu,"gradients")) m_gradients(0,0); if (strmatch(menu,"select")) m_select(0,0); return; } // main window mouse right-click popup menu void image_Rclick_popup() { int ftype; if (! curr_file) return; ftype = image_file_type(curr_file); if (ftype == IMAGE) popup_menu(Mwin,popmenu_image); if (ftype == RAW) popup_menu(Mwin,popmenu_raw); if (ftype == VIDEO) popup_menu(Mwin,popmenu_video); // 17.08 return; } // gallery thumbnail mouse left-click function // open the clicked file in view mode F void gallery_Lclick_func(int Nth) { char *file; int err; if (clicked_file) { // lose memory of clicked thumbnail zfree(clicked_file); clicked_file = 0; } if (checkpend("busy block mods")) return; file = gallery(0,"get",Nth); if (! file) return; err = f_open(file,Nth,0,1); // clicked file >> current file zfree(file); if (! err) m_viewmode(0,"F"); // 18.01 return; } // gallery thumbnail mouse right-click popup menu void gallery_Rclick_popup(int Nth) { FTYPE ftype; clicked_posn = Nth; // clicked gallery position (0 base) clicked_file = gallery(0,"get",Nth); // clicked_file is subject for zfree() if (! clicked_file) return; ftype = image_file_type(clicked_file); if (navi::gallerytype == ALBUM) popup_menu(Mwin,popmenu_album); else if (ftype == IMAGE) popup_menu(Mwin,popmenu_thumb); else if (ftype == RAW) popup_menu(Mwin,popmenu_raw); else if (ftype == VIDEO) // 17.08 popup_menu(Mwin,popmenu_video); return; } /********************************************************************************/ // set window view mode, F/G/W/M void m_viewmode(GtkWidget *, cchar *fgwm) { if (FGWM == *fgwm) return; // no change if (*fgwm == 'F') // set F view mode for image file { F1_help_topic = "file_view"; gtk_widget_hide(Ghbox); gtk_widget_hide(Whbox); gtk_widget_hide(Mhbox); gtk_widget_show_all(Fhbox); FGWM = 'F'; PFGWM = 'F'; // remember last F/G view 17.08 set_mwin_title(); Cstate = &Fstate; // set drawing area Cdrawin = Fdrawin; gdkwin = gtk_widget_get_window(Fdrawin); // GDK window if (zddeltrash) m_delete_trash(0,0); // reset target file for funcs in if (zdcopymove) m_copy_move(0,0); // gallery thumbnail popup menus if (zdexifview) meta_view(0); if (zdrename) m_rename(0,0); } if (*fgwm == 'G') // set G view mode for thumbnail gallery { if (Fslideshow) return; // no crash slide show F1_help_topic = "gallery_view"; // 17.04 gtk_widget_hide(Fhbox); gtk_widget_hide(Whbox); gtk_widget_hide(Mhbox); gtk_widget_show_all(Ghbox); FGWM = 'G'; PFGWM = 'G'; // remember last F/G view 17.08 Cstate = 0; // no F/W image drawing area Cdrawin = 0; gdkwin = 0; if (curr_file) gallery(curr_file,"paint",0); // set gallery posn. at curr. file else gallery(0,"paint",-1); // else leave unchanged } if (*fgwm == 'W') // set W view mode for file maps { if (CEF) return; // don't interrupt edit func. if (Fslideshow) return; // no crash slide show F1_help_topic = "worldmap_view"; gtk_widget_hide(Fhbox); gtk_widget_hide(Ghbox); gtk_widget_hide(Mhbox); gtk_widget_show_all(Whbox); FGWM = 'W'; Cstate = &Wstate; // set drawing area Cdrawin = Wdrawin; gdkwin = gtk_widget_get_window(Wdrawin); // GDK window if (! Wstate.fpxb) m_load_filemap(0,"default"); // no map loaded, load default map gtk_window_set_title(MWIN,ZTX("Image Locations")); // window title Fpaintnow(); } if (*fgwm == 'M') { if (CEF) return; // don't interrupt edit func. if (Fslideshow) return; // no crash slide show F1_help_topic = "netmap_view"; gtk_widget_hide(Fhbox); gtk_widget_hide(Ghbox); gtk_widget_hide(Whbox); gtk_widget_show_all(Mhbox); FGWM = 'M'; Cstate = 0; // no F/W image drawing area Cdrawin = 0; gdkwin = 0; m_load_netmap(0,"init"); // load net initial map gtk_window_set_title(MWIN,ZTX("Image Locations")); // window title } return; } /********************************************************************************/ // favorites menu - popup graphic menu with user's favorites void m_favorites(GtkWidget *, cchar *) { void favorites_callback(cchar *menu); char menuconfigfile[200]; F1_help_topic = "favorites_menu"; snprintf(menuconfigfile,200,"%s/menu-config",favorites_dirk); Gmenuz(Mwin,ZTX("Favorites"),menuconfigfile,favorites_callback); return; } // response function for clicked menu // a menu function is called as from the text menus void favorites_callback(cchar *menu) { int ii; if (! menu) return; if (strmatchcase(menu,"quit")) return; if (strmatchcase(menu,"help")) { showz_userguide("favorites_menu"); return; } for (ii = 0; ii < Nmenus; ii++) { if (! menutab[ii].menu) continue; if (strmatchcase(ZTX(menu),ZTX(menutab[ii].menu))) break; } if (ii == Nmenus || ! menutab[ii].topmenu) { // bugfix, disallow top menus 18.01 zmessageACK(Mwin,ZTX("menu name is invalid")); return; } menutab[ii].func(0,menu); return; } fotoxx-18.01.1/f.file.cc0000644000175000017500000066527713222767271013363 0ustar micomico/******************************************************************************** Fotoxx edit photos and manage collections Copyright 2007-2018 Michael Cornelison source code URL: https://kornelix.net contact: kornelix@posteo.de 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 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses 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. ********************************************************************************* Fotoxx image edit - file menu m_clone start a new parallel instance of Fotoxx m_recentfiles show gallery of recently viewed files add_recent_file add an image file to the list of recent files m_newfiles show gallery of newest image files m_open open image file menu function m_cycle2files cycle 2 previous image files m_cycle3files cycle 3 previous image files m_rawtherapee open a camera RAW file using the RawTherapee program m_view360 view a 360 degree panorama image f_open open and display an image file f_open_saved open the last image file saved f_preload preload image files ahead of need x_prev_next open prev/next file in current or prev/next gallery m_prev menu function - open previous m_next menu function - open next m_prev_next menu button function - open previous or next m_blank_image create a new monocolor image create_blank_file callable function to create a new monocolor image m_blank_window blank/restore window m_rename rename current image file or clicked thumbnail m_copy_move copy or move an image file to a new location m_copyto_desktop copy an image file to the desktop m_copyto_clip copy clicked file or current file to the clipboard m_delete_trash delete or trash an image file m_print print an image file m_print_calibrated print an image file with calibrated colors m_quit menu quit quitxx callable quit m_file_save save a (modified) image file to disk file_new_version get next avail. file version name file_last_version get last existing file version name f_save save an image file to disk (replace, new version, new file) f_save_as dialog to save an image file with a designated file name linedit text file line edit functions m_help help menu find_imagefiles find all image files under a given directory path raw_to_tiff convert a RAW file name to equivalent .tif name PXB_load load an image file into a PXB pixmap structure (8-bit RGB) PXM_load load an image file into a PXM pixmap structure (float RGB) TIFF_PXB_load load a .tif file into a PXB pixmap structure (8-bit RGB) TIFF_PXM_load load a .tif file into a PXM pixmap structure (float RGB) PXM_TIFF_save save a PXM pixmap to a .tif file (8/16-bit RGB) PNG_PXB_load load a .png file into a PXB pixmap structure (8-bit RGB) PNG_PXM_load load a .png file into a PXM pixmap structure (float RGB) PXM_PNG_save save a PXM pixmap to a .png file (8/16-bit RGB) ANY_PXB_load load other image file into a PXB pixmap structure (8-bit RGB) ANY_PXM_load load other image file into a PXM pixmap structure (float RGB) PXM_ANY_save save a PXM pixmap to other file type (8-bit RGB) RAW_PXB_load load a RAW file into a PXB pixmap structure (8-bit RGB) RAW_PXM_load load a RAW file into a PXM pixmap structure (float RGB) vpixel get virtual pixel at any PXM or PXB image location (float) PXM_audit check PXM image for pixel values > 255.99... PXM_make create new PXM pixmap image PXM_free destroy PXM and free memory PXM_addalpha add an alpha channel to a PXM pixmap PXM_copy create a copy of a PXM image PXM_copy_area create a copy of a rectangular area within a PXM PXM_rescale create a rescaled copy of a PXM image PXM_rotate create a rotated copy of a PXM image PXB_make create a new PXB pixmap image, empty or from PIXBUF PXB_free destroy PXB and free memory PXB_addalpha add an alpha channel to a PXB pixmap PXB_copy create a copy of a PXB image PXB_rescale create a rescaled copy of a PXB image PXB_rotate create a rotated copy of a PXB image PXM_PXB_copy create a PXB copy of a PXM image PXM_PXB_update update a PXB area fom an updated PXM area PXB_PXB_update update a PXB area from another PXB area pixbuf_rescale_fast fast pixbuf rescale using threads *********************************************************************************/ #define EX extern // disable extern declarations #include "fotoxx.h" // (variables in fotoxx.h are refs) /********************************************************************************/ // start a new parallel instance of fotoxx // new window is slightly down and right from old window void m_clone(GtkWidget *, cchar *) { int cc, xx, yy, ww, hh; char progexe[300]; F1_help_topic = "new_window"; gtk_window_get_position(MWIN,&xx,&yy); // get window position and size gtk_window_get_size(MWIN,&ww,&hh); cc = readlink("/proc/self/exe",progexe,300); // get own program path if (cc <= 0) { zmessageACK(Mwin,"cannot get /proc/self/exe"); return; } progexe[cc] = 0; snprintf(command,CCC,"%s -c %d %d %d %d -lang %s",progexe,xx,yy,ww,hh,zfuncs::zlang); if (curr_file) strncatv(command,CCC," \"",curr_file,"\"",null); strcat(command," &"); // start new instance and pass shell_ack(command); // my window posn and size return; } /********************************************************************************/ // show recently seen image files (edited or only viewed) // optionally update gallery list only but no change to G view void m_recentfiles(GtkWidget *, cchar *menu) { F1_help_topic = "recent_images"; navi::gallerytype = RECENT; // gallery type = recent files gallery(recentfiles_file,"initF",0); // generate gallery of recent files gallery(0,"paint",0); m_viewmode(0,"G"); return; } // add a new file to the list of recent files, first position void add_recent_file(cchar *newfile) { int ii; char *pp; linedit_open(recentfiles_file); // 17.08 linedit_put(newfile); // add new file as first in list for (ii = 0; ii < 100; ii++) { // read list of recent files <= 100 pp = linedit_get(); if (! pp) break; if (strmatch(pp,newfile)) continue; // skip new file if present linedit_put(pp); } linedit_close(); return; } /********************************************************************************/ // report the newest or most recently modified image files // based on EXIF photo date if available, else file mod date is used struct NFxrec_t { // index record char date[16]; // sort date, yyyymmddhhmmss char *file; // image file }; // menu function void m_newfiles(GtkWidget *, cchar *menu) { int NFxrec_comp(cchar *rec1, cchar *rec2); cchar *mess = ZTX("Use EXIF photo date or \n file modification date?"); F1_help_topic = "newest_images"; int ii, jj, cc, sort, Nxrec; xxrec_t *xxrec; FILE *fid; NFxrec_t *NFxrec = 0; cc = Nxxrec * sizeof(NFxrec_t); // allocate memory NFxrec = (NFxrec_t *) zmalloc(cc); if (menu && strmatch(menu,"EXIF")) sort = 1; else if (menu && strmatch(menu,"file")) sort = 2; else sort = zdialog_choose(Mwin,mess,"EXIF",ZTX("File"),null); for (Nxrec = ii = 0; ii < Nxxrec; ii++) // loop image index table { xxrec = xxrec_tab[ii]; jj = Nxrec++; NFxrec[jj].file = xxrec->file; // image file if (sort == 1) { if (strmatch(xxrec->pdate,"null")) continue; // use EXIF photo date strncpy0(NFxrec[jj].date,xxrec->pdate,15); // ignore images without photo date } else strncpy0(NFxrec[jj].date,xxrec->fdate,15); // else use file mod date } if (! Nxrec) { zmessageACK(Mwin,"no data found"); return; } if (Nxrec > 1) // sort index recs. by file date HeapSort((char *) NFxrec, sizeof(NFxrec_t), Nxrec, NFxrec_comp); fid = fopen(searchresults_file,"w"); // open output file if (! fid) { zmessageACK(Mwin,"file error: %s",strerror(errno)); goto cleanup; } for (ii = 0; ii < 1000 && ii < Nxrec; ii++) // output newest 1000 image files fprintf(fid,"%s\n",NFxrec[ii].file); fclose(fid); cleanup: zfree(NFxrec); navi::gallerytype = NEWEST; // newest files gallery(searchresults_file,"initF",0); // generate gallery of files gallery(0,"paint",0); m_viewmode(0,"G"); return; } // Compare 2 NFxrec records by file date // return <0 =0 >0 for rec2 < = > rec1. int NFxrec_comp(cchar *rec1, cchar *rec2) { char *date1 = ((NFxrec_t *) rec1)->date; char *date2 = ((NFxrec_t *) rec2)->date; return strcmp(date2,date1); } /********************************************************************************/ // open file menu function void m_open(GtkWidget *, cchar *) { char *file = 0; F1_help_topic = "open_image_file"; if (checkpend("busy block mods")) return; file = gallery_select1(null); // gallery thumbnail selection 17.08 if (! file) return; f_open(file); m_viewmode(0,"F"); zfree(file); return; } /********************************************************************************/ // open the previous file opened (not the same as toolbar [prev] button) // repeated use will alternate the two most recent files void m_cycle2files(GtkWidget *, cchar *menu) { FILE *fid; char *file, buff[XFCC]; int Nth = 0, err; float gzoom; F1_help_topic = "cycle_prev_file"; if (checkpend("busy block mods")) return; m_viewmode(0,"F"); if (! Cstate) return; gzoom = Cstate->fzoom; fid = fopen(recentfiles_file,"r"); if (! fid) return; file = fgets_trim(buff,XFCC,fid,1); // skip over first most recent file while (true) { file = fgets_trim(buff,XFCC,fid,1); // find next most recent file if (! file) break; err = f_open(file,Nth,0,0,0); if (! err) break; } fclose(fid); Cstate->fzoom = gzoom; Fpaint2(); return; } /********************************************************************************/ // open the 2nd previous file opened // repeated use will alternate the 3 most recent files void m_cycle3files(GtkWidget *, cchar *menu) { FILE *fid; char *file, buff[XFCC]; int Nth = 0, err; float gzoom; F1_help_topic = "cycle_prev_file"; if (checkpend("busy block mods")) return; m_viewmode(0,"F"); if (! Cstate) return; gzoom = Cstate->fzoom; fid = fopen(recentfiles_file,"r"); if (! fid) return; file = fgets_trim(buff,XFCC,fid,1); // skip over 2 most recent files file = fgets_trim(buff,XFCC,fid,1); while (true) { file = fgets_trim(buff,XFCC,fid,1); // find next most recent file if (! file) break; err = f_open(file,Nth,0,0,0); if (! err) break; } fclose(fid); Cstate->fzoom = gzoom; Fpaint2(); return; } /********************************************************************************/ // menu function: open a camera RAW file and edit with the Raw Therapee GUI // opens 'clicked_file' if present or 'rawfile' if not void m_rawtherapee(GtkWidget *, cchar *menu) { char *pp; char *tiffile; int err; STATB statb; F1_help_topic = "open_raw_file"; if (! Frawtherapee) { zmessageACK(Mwin,ZTX("Raw Therapee not installed")); return; } if (checkpend("busy block mods")) return; if (rawfile) zfree(rawfile); rawfile = 0; if (clicked_file) { rawfile = clicked_file; clicked_file = 0; } else { pp = 0; if (curr_file) pp = curr_file; else if (curr_dirk) pp = curr_dirk; rawfile = zgetfile(ZTX("Open RAW (Raw Therapee)"),MWIN,"file",pp); if (! rawfile) return; } if (image_file_type(rawfile) != RAW) { zmessageACK(Mwin,ZTX("RAW type not registered in User Settings")); zfree(rawfile); rawfile = 0; return; } tiffile = raw_to_tiff(rawfile); shell_ack("rawtherapee \"%s\"",rawfile); // 18.01 zfree(rawfile); rawfile = 0; err = stat(tiffile,&statb); if (err) { zfree(tiffile); zmessage_post(Mwin,3,ZTX("Raw Therapee produced no tif file")); return; } err = f_open(tiffile,0,0,1); // open tiff file if (err) { zfree(tiffile); return; } update_image_index(tiffile); // update index rec. zfree(tiffile); m_viewmode(0,"F"); return; } /********************************************************************************/ // view a 360 degree panorama image // center of view direction can be any angle 0-360 degrees // image width is assumed to correspond to a 360 degree view namespace view360 // 18.01 { void show(); void * show_thread(void *arg); void mouse(); void KB_func(int key); void quit(); PXB *filepxb, *viewpxb; PIXBUF *filepixbuf, *viewpixbuf; int tbusy[max_threads]; char *filename = 0; int projection = 1; int Frecalc; int fww, fhh; // file dimensions int dww, dhh; // window dimentions int pdww, pdhh; int cx, cy; float *Fx, *Fy, zoom; zdialog *zd; } // menu function void m_view360(GtkWidget *, const char *) // 18.01 { using namespace view360; GError *gerror = 0; F1_help_topic = "view-360"; if (checkpend("busy block mods")) return; if (clicked_file) { // use clicked file if present filename = clicked_file; clicked_file = 0; } else if (curr_file) // else use current file filename = zstrdup(curr_file); else return; filepixbuf = gdk_pixbuf_new_from_file(filename,&gerror); zfree(filename); if (! filepixbuf) { zmessageACK(Mwin,"%s",gerror->message); return; } zd = zmessage_post_bold(Mwin,8, "Mouse drag: pan image 360° \n" "L/R arrow keys: pan image 360° \n" "L/R mouse click: zoom in/out \n" "Key 1 or 2: change projection \n" "Key Q: quit panorama view"); Vmenu_block(1); // block menus Fview360 = 1; // flag for main window KBpress() fww = gdk_pixbuf_get_width(filepixbuf); // image dimensions fhh = gdk_pixbuf_get_height(filepixbuf); filepxb = PXB_make(filepixbuf); cx = fww/2; // initial view center = middle cy = fhh/2; zoom = 0.5; // initial zoom Frecalc = 1; // initial calculate Fx = Fy = 0; // no allocated constants pdww = pdhh = 0; m_viewmode(0,"F"); gdk_window_freeze_updates(gdkwin); takeMouse(mouse,0); show(); return; } // show current view region on window void view360::show() { using namespace view360; int ii, px, py, dx, dy; float sx, sy, R, C, Tx, Ty; if (! Fview360) return; dww = gdk_window_get_width(gdkwin); // drawing window size dhh = gdk_window_get_height(gdkwin); if (dww < 20 || dhh < 20) return; // too small if (dww != pdww || dhh != pdhh) { pdww = dww; pdhh = dhh; if (Fx) zfree(Fx); if (Fy) zfree(Fy); Fx = (float *) zmalloc(dww * dhh * sizeof(float)); Fy = (float *) zmalloc(dww * dhh * sizeof(float)); Frecalc = 1; } viewpxb = PXB_make(dww,dhh,0); viewpixbuf = viewpxb->pixbuf; gdk_pixbuf_fill(viewpixbuf,0); if (Frecalc && projection == 1) { for (py = 0; py < dhh; py++) // loop window pixels for (px = 0; px < dww; px++) { dx = px - dww/2; // distance from center dy = py - dhh/2; dx = dx / zoom; // scale for zoom dy = dy / zoom; Tx = PI * dx / fww; // x view angle Ty = PI * dy / fww; // y view angle if (Tx > PI/3 || Tx < -PI/3 || Ty > PI/4 || Ty < -PI/4) { // limit to +-60 and +-45 deg. view ii = py * dww + px; Fx[ii] = Fy[ii] = 0; continue; } Tx = 0.4 * Tx; sx = dx * cosf(Tx); sy = dy * cosf(Ty) * cosf(Tx); ii = py * dww + px; Fx[ii] = sx; Fy[ii] = sy; } } if (Frecalc && projection == 2) { for (py = 0; py < dhh; py++) // loop window pixels for (px = 0; px < dww; px++) { dx = px - dww/2; // distance from center of window dy = py - dhh/2; dx = dx / zoom; // scale for zoom dy = dy / zoom; sx = 6.28 * dx / fww; // scale (dx,dy) so that fww/4 sy = 6.28 * dy / fww; // (90 deg.) is PI/2 if (sx > 1.0 || sx < -1.0 || sy > 0.7 || sy < -0.7) { // limit +-90 and +-63 deg. view ii = py * dww + px; Fx[ii] = Fy[ii] = 0; continue; } R = sqrtf(sx*sx + sy*sy); // conv. (dx,dy) in rectilinear proj. C = atanf(R); // to spherical coordinates Tx = atanf(sx); Ty = asinf(sy/R * sinf(C)); sx = 0.25 * fww * Tx; // conv. spherical coordinate (Ty,Tx) sy = 0.25 * fww * Ty * cosf(Tx); // to flat coordinates (sx,sy) ii = py * dww + px; Fx[ii] = sx; Fy[ii] = sy; } } Frecalc = 0; for (ii = 0; ii < NWT; ii++) { // start worker threads tbusy[ii] = 1; start_detached_thread(show_thread,&Nval[ii]); } for (ii = 0; ii < NWT; ii++) // wait for completion while(tbusy[ii]) zsleep(0.01); cairo_t *cr = draw_context_create(gdkwin,draw_context); gdk_cairo_set_source_pixbuf(cr,viewpxb->pixbuf,0,0); cairo_paint(cr); draw_context_destroy(draw_context); gdk_flush(); PXB_free(viewpxb); return; } // thread function to generate the image void * view360::show_thread(void *arg) { using namespace view360; int index = *((int *) (arg)); int ii, stat, px, py; float sx, sy; uint8 pixs[4], *pixss, *pixp; for (py = index; py < dhh; py += NWT) // loop window pixels for (px = 0; px < dww; px++) { ii = py * dww + px; sx = Fx[ii]; sy = Fy[ii]; if (sx == 0 && sy == 0) continue; sx += cx; // add back center sy += cy; if (sy < 0) continue; // off the image if (sy >= fhh) continue; if (sx < 0) sx = fww + sx; // handle x wrap-around if (sx >= fww) sx = sx - fww; pixp = PXBpix(viewpxb,px,py); // dest. pixel stat = vpixel(filepxb,sx,sy,pixs); // source pixel interpolated if (stat) memcpy(pixp,pixs,3); // fails at image left/right edge else { pixss = PXBpix(filepxb,int(sx),int(sy)); // use no interpolation memcpy(pixp,pixss,3); } } tbusy[index] = 0; pthread_exit(0); return 0; } // mouse function - move center of view with mouse drag void view360::mouse() { using namespace view360; int Fshow = 0; if (Mxdrag || Mydrag) { // change center of view cx += 2 * Mwdragx; if (cx < 0) cx = fww + cx; if (cx >= fww) cx = cx - fww; Mxdrag = Mydrag = 0; Fshow = 1; } if (LMclick) { // zoom-in LMclick = 0; zoom = zoom * 1.4142; if (zoom > 1.99) zoom = 2; Fshow = 1; Frecalc = 1; } if (RMclick) { // zoom-out RMclick = 0; zoom = zoom / 1.4142; if (zoom < 0.36) zoom = 0.354; Frecalc = 1; Fshow = 1; } if (Fshow) { if (zd) zdialog_free(zd); zd = 0; show(); } return; } // keyboard function void view360::KB_func(int key) { using namespace view360; static int busy = 0; while (busy) zmainloop(); busy++; if (zd) zdialog_free(zd); zd = 0; if (key == GDK_KEY_Escape || key == GDK_KEY_q) quit(); if (key == GDK_KEY_1) { projection = 1; Frecalc = 1; } if (key == GDK_KEY_2) { projection = 2; Frecalc = 1; } if (key == GDK_KEY_Left) { cx -= 20; if (cx < 0) cx = fww + cx; } if (key == GDK_KEY_Right) { cx += 20; if (cx >= fww) cx = cx - fww; } show(); busy = 0; return; } // quit and free resources void view360::quit() { if (! Fview360) return; freeMouse(); PXB_free(filepxb); PXB_free(viewpxb); if (Fx) zfree(Fx); if (Fy) zfree(Fy); Fview360 = 0; Vmenu_block(0); gdk_window_thaw_updates(gdkwin); Fpaint2(); } /********************************************************************************/ // Open a file and initialize Fpxb pixbuf. // // Nth: if Nth matches file position in current gallery, curr_file_posn // is set to Nth, otherwise it is searched and set correctly. // (a file can be present multiple times in an album gallery). // Fkeep: edit undo/redo stack is not purged, and current edits are kept // after opening the new file (used by file_save()). // Fack: failure will cause a popup ACK dialog. // zoom: keep current zoom level and position, otherwise fit window. // // Following are set: curr_file_type, curr_file_bpc, curr_file_size. // Returns: 0 = OK, +N = error. // errors: 1 reentry (bug) // 2 curr. edit function cannot be restarted or canceled // 3 file not found or user cancel // 4 unsupported file type or PXB_load() failure int f_open(cchar *filespec, int Nth, int Fkeep, int Fack, int zoom) { PXB *tempxb = 0; int err, fposn, retcode = 0; FTYPE ftype; static int Freent = 0; char *pp, *file; void (*menufunc)(GtkWidget *, cchar *); static zdialog *zdvideo = 0; STATB statb; if (Freent++) { // stop re-entry printz("f_open() re-entry \n"); goto ret1; } if (CEF && ! CEF->Frestart) goto ret2; // edit function not restartable if (CEF) menufunc = CEF->menufunc; // active edit, save menu function else menufunc = 0; // for possible edit restart if (CEF && CEF->zd) zdialog_send_event(CEF->zd,"cancel"); // cancel if possible if (CEF) goto ret2; // cannot sa_unselect(); // unselect area if any file = 0; if (filespec) file = zstrdup(filespec); // use passed filespec else { pp = curr_file; // use file open dialog if (! pp) pp = curr_dirk; file = zgetfile(ZTX("Open Image File"),MWIN,"file",pp); } if (! file) goto ret3; err = stat(file,&statb); // check file exists if (err) { if (Fack) zmessage_post(Mwin,4,"%s \n %s",file,strerror(errno)); zfree(file); goto ret3; } ftype = image_file_type(file); // must be image or RAW file type if (ftype == THUMB) { if (Fack) zmessageACK(Mwin,"thumbnail file"); goto ret4; } if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { // must be supported image file type 17.08 if (Fack) zmessageACK(Mwin,ZTX("unknown file type")); // or RAW file or VIDEO file zfree(file); goto ret4; } Ffuncbusy = 1; // may be large or RAW file, slow CPU tempxb = PXB_load(file,1); // load image as PXB pixbuf Ffuncbusy = 0; if (! tempxb) { // PXB_load() messages user zfree(file); goto ret4; } free_resources(Fkeep); // free resources for old image file curr_file = file; // new current file if (curr_dirk) zfree(curr_dirk); // set current directory curr_dirk = zstrdup(curr_file); // for new current file pp = strrchr(curr_dirk,'/'); *pp = 0; Fpxb = tempxb; // pixmap for current image strcpy(curr_file_type,f_load_type); // set curr_file_xxx from f_load_xxx curr_file_bpc = f_load_bpc; curr_file_size = f_load_size; load_filemeta(curr_file); // load metadata used by fotoxx fposn = file_position(file,Nth); // file position in gallery list if (fposn < 0) { // not there gallery(curr_file,"init",0); // generate new gallery list gallery(0,"sort",-2); // recall sort and position 18.01 gallery(curr_file,"paint",0); // position at curr. file fposn = file_position(curr_file,0); // position and count in gallery list } curr_file_posn = fposn; // keep track of file position URS_reopen_pos = 0; // not f_open_saved() if (! zoom) { // discard zoom state Fzoom = 0; // zoom level = fit window zoomx = zoomy = 0; // no zoom center } set_mwin_title(); // set win title from curr_file info add_recent_file(curr_file); // most recent file opened if (zdrename) m_rename(0,0); // update active rename dialog if (zdcopymove) m_copy_move(0,0); // " copy/move dialog if (zddeltrash) m_delete_trash(0,0); // " delete/trash dialog if (zdexifview) meta_view(0); // " EXIF/IPTC data view window if (zdeditmeta) m_meta_edit(0,0); // " edit metadata dialog if (zdeditanymeta) m_meta_edit_any(0,0); // " edit any metadata dialog if (zddeletemeta) m_meta_delete(0,0); // " delete metadata dialog if (zdupright) m_upright(0,0); // " upright dialog if (Fcaptions) m_captions(0,0); // show caption/comments at top if (menufunc) menufunc(0,0); // restart edit function if (zdvideo) zdialog_free(zdvideo); // popup video message 17.08 zdvideo = 0; if (ftype == VIDEO) zdvideo = zmessage_post_bold(Mwin,2,"VIDEO P-play Q-quit"); gallery(0,"paint",-1); // 18.01 goto ret0; ret4: retcode++; ret3: retcode++; ret2: retcode++; ret1: retcode++; ret0: Freent = 0; return retcode; } /********************************************************************************/ // Open a file that was just saved. Used by file_save(). // The edit undo/redo stack is not purged and current edits are kept. // Following are set: // curr_file *_dirk *_file_posn *_file_type *_file_bpc *_file_size // Returns: 0 = OK, +N = error. int f_open_saved() { int Nth = -1, fposn; if (clicked_file) zfree(clicked_file); clicked_file = 0; if (! f_save_file) return 1; if (E0pxm) { // edits were made PXB_free(Fpxb); // new window image Fpxb = PXM_PXB_copy(E0pxm); } if (curr_file) zfree(curr_file); curr_file = zstrdup(f_save_file); // new current file fposn = file_position(curr_file,Nth); // file position in gallery list if (fposn < 0) { // not there gallery(curr_file,"init",0); // generate new gallery list gallery(0,"sort",-2); // recall sort and position 18.01 gallery(curr_file,"paint",-1); // position at current file fposn = file_position(curr_file,0); // position and count in gallery list } curr_file_posn = fposn; // keep track of file position strcpy(curr_file_type,f_save_type); // set curr_file_xxx from f_save_xxx curr_file_bpc = f_save_bpc; curr_file_size = f_save_size; URS_reopen_pos = URS_pos; // track undo/redo stack position zoomx = zoomy = 0; // no zoom center set_mwin_title(); // set win title from curr_file info if (zdexifview) meta_view(0); // update EXIF/IPTC view window return 0; } /********************************************************************************/ // Function to preload image files hopefully ahead of need. // Usage: f_preload(next) // next = -1 / +1 to read previous / next image file in curr. gallery // preload_file will be non-zero if and when preload_pxb is available. void f_preload(int next) { int fd; char *file; if (! curr_file) return; file = gallery_getnext(next,Flastversion); if (! file) return; if (strmatch(file,curr_file)) { zfree(file); return; } fd = open(file,O_RDONLY); if (fd >= 0) { posix_fadvise(fd,0,0,POSIX_FADV_WILLNEED); // preload file in kernel cache close(fd); } zfree(file); return; } /********************************************************************************/ // Open previous or next file in current gallery list, // or jump to previous or next gallery when at the limits. // index is -1 or +1 void x_prev_next(int index) { using namespace navi; char *pp, *newfile = 0, *newgallery = 0; int err, Nth = 0; static int xbusy = 0; static int jumpgallery = 0; static zdialog *zd = 0; cchar *endmess1 = ZTX("Start of gallery, preceding gallery: %s"); cchar *endmess2 = ZTX("End of gallery, following gallery: %s"); F1_help_topic = "prev_next"; if (zd && zdialog_valid(zd)) { if (strmatch(zd->widget[0].data,"post")) { zdialog_free(zd); // clear prior popup message zd = 0; } } if (checkpend("busy block mods")) return; if (xbusy) return; // avoid "function busy" xbusy = 1; newfile = gallery_getnext(index,Flastversion); // get prev/next file (last version) if (newfile) { Nth = curr_file_posn + index; // albums can have repeat files err = f_open(newfile,Nth,0,1,0); // open image or RAW file if (! err) f_preload(index); // preload next image goto returnx; } if (jumpgallery) { // jump to prev/next gallery jumpgallery = 0; newgallery = prev_next_gallery(index); if (! newgallery) goto returnx; gallery(newgallery,"init",0); // load gallery gallery(0,"sort",-2); // preserve sort 18.01 if (Nfiles - Nsubdirs > 0) { // at least one image file present 17.01 if (index == +1) Nth = Nsubdirs; // get first or last image file if (index == -1) Nth = Nfiles - 1; newfile = gallery(0,"get",Nth); err = f_open(newfile,Nth,0,1,0); // open image or RAW file if (! err) gallery(newfile,"paint",Nth); // 18.01 if (! err) f_preload(index); // preload next image goto returnx; } } newgallery = prev_next_gallery(index); // check for prev/next gallery if (newgallery) { pp = strrchr(newgallery,'/'); if (pp) pp++; if (index == -1) zd = zmessage_post_bold(Mwin,3,endmess1,pp,0); // prepare jump to prev/next gallery if (index == +1) zd = zmessage_post_bold(Mwin,3,endmess2,pp,0); // if user tries prev/next again jumpgallery = 1; } else { if (index == -1) zd = zmessage_post_bold(Mwin,3,endmess1,"(null)",0); // no prev/next gallery if (index == +1) zd = zmessage_post_bold(Mwin,3,endmess2,"(null)",0); } returnx: if (newfile) zfree(newfile); if (newgallery) zfree(newgallery); Fpaint2(); zmainloop(); // refresh window xbusy = 0; return; } void m_prev(GtkWidget *, cchar *menu) { F1_help_topic = "open_image_file"; x_prev_next(-1); // search from curr_file -1 to first file return; } void m_next(GtkWidget *, cchar *menu) { F1_help_topic = "open_image_file"; x_prev_next(+1); // search from curr_file +1 to last file return; } void m_prev_next(GtkWidget *, cchar *menu) // prev/next if left/right mouse click on icon { int button = zfuncs::vmenuclickbutton; if (button == 1) m_prev(0,0); else m_next(0,0); return; } /********************************************************************************/ // create a new blank image file with desired background color void m_blank_image(GtkWidget *, cchar *pname) { char color[20], fname[100], fext[8], *filespec; int zstat, err, cc, ww, hh, RGB[3]; zdialog *zd; cchar *pp; F1_help_topic = "new_blank_image"; if (checkpend("all")) return; Fblock = 1; // file name [___________________________] [.jpg|v] // width [____] height [____] (pixels) // color [____] zd = zdialog_new(ZTX("Create Blank Image"),Mwin,Bdone,Bcancel,null); zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labf","hbf",ZTX("file name"),"space=3"); zdialog_add_widget(zd,"zentry","file","hbf",0,"space=3|expand"); zdialog_add_widget(zd,"combo","ext","hbf",".jpg"); zdialog_add_widget(zd,"hbox","hbz","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labw","hbz",Bwidth,"space=5"); zdialog_add_widget(zd,"zspin","width","hbz","100|30000|1|1600"); // fotoxx.h 18.01 zdialog_add_widget(zd,"label","space","hbz",0,"space=5"); zdialog_add_widget(zd,"label","labh","hbz",Bheight,"space=5"); zdialog_add_widget(zd,"zspin","height","hbz","100|16000|1|1000"); zdialog_add_widget(zd,"label","labp","hbz","(pixels)","space=3"); zdialog_add_widget(zd,"hbox","hbc","dialog",0,"space=1"); zdialog_add_widget(zd,"label","labc","hbc",Bcolor,"space=5"); zdialog_add_widget(zd,"colorbutt","color","hbc","200|200|200"); zdialog_cb_app(zd,"ext",".jpg"); zdialog_cb_app(zd,"ext",".png"); zdialog_cb_app(zd,"ext",".tif"); zdialog_restore_inputs(zd); // preload prior user inputs zdialog_stuff(zd,"file",""); // force input of new name zdialog_set_modal(zd); // 17.08 zdialog_run(zd,0,"parent"); // run dialog zstat = zdialog_wait(zd); // wait for completion if (zstat != 1) { // cancel zdialog_free(zd); Fblock = 0; return; } zdialog_fetch(zd,"file",fname,92); // get new file name strTrim2(fname); if (*fname <= ' ') { zmessageACK(Mwin,ZTX("supply a file name")); zdialog_free(zd); Fblock = 0; m_blank_image(0,0); // retry return; } zdialog_fetch(zd,"ext",fext,8); // add extension strcat(fname,fext); cc = strlen(fname); filespec = zstrdup(curr_dirk,cc+4); // make full filespec strcat(filespec,"/"); strcat(filespec,fname); zdialog_fetch(zd,"width",ww); // get image dimensions zdialog_fetch(zd,"height",hh); RGB[0] = RGB[1] = RGB[2] = 255; zdialog_fetch(zd,"color",color,19); // get image color pp = strField(color,"|",1); if (pp) RGB[0] = atoi(pp); pp = strField(color,"|",2); if (pp) RGB[1] = atoi(pp); pp = strField(color,"|",3); if (pp) RGB[2] = atoi(pp); zdialog_free(zd); err = create_blank_file(filespec,ww,hh,RGB); if (! err) f_open(filespec); // make it the current file Fblock = 0; return; } // function to create a new blank image file // file extension must be one of: .jpg .tif .png // RGB args are in the range 0 - 255 // if file exists it is overwritten // returns 0 if OK, 1 if file exists, +N if error int create_blank_file(cchar *file, int ww, int hh, int RGB[3]) { cchar *pp; cchar *fext; int err, cstat; PXB *tempxb; GError *gerror = 0; uint8 *pixel; STATB statb; err = stat(file,&statb); if (! err) { // file already exists zmessageACK(Mwin,Bfileexists); return 1; } pp = strrchr(file,'.'); // get file .ext if (! pp || strlen(pp) > 4) return 1; if (strmatch(pp,".jpg")) fext = "jpeg"; // validate and set pixbuf arg. else if (strmatch(pp,".png")) fext = "png"; else if (strmatch(pp,".tif")) fext = "tif"; else return 2; tempxb = PXB_make(ww,hh,0); // create pixbuf image for (int py = 0; py < hh; py++) // fill with color for (int px = 0; px < ww; px++) { pixel = PXBpix(tempxb,px,py); pixel[0] = RGB[0]; pixel[1] = RGB[1]; pixel[2] = RGB[2]; } cstat = gdk_pixbuf_save(tempxb->pixbuf,file,fext,&gerror,null); if (! cstat) { zmessageACK(Mwin,"error: %s",gerror->message); PXB_free(tempxb); return 3; } PXB_free(tempxb); return 0; } /********************************************************************************/ // blank or restore window void m_blank_window(GtkWidget *, cchar *menu) { static int Fblank = 0; static char *savefile = 0; F1_help_topic = "blank_window"; if (checkpend("all")) return; if (FGWM != 'F') m_viewmode(0,"F"); // insure file view mode 18.01 if (! Fblank) { if (curr_file) { if (savefile) zfree(savefile); savefile = zstrdup(curr_file); } free_resources(); } else if (savefile) { f_open(savefile); zfree(savefile); savefile = 0; } Fblank = 1 - Fblank; return; } /********************************************************************************/ // rename menu function // activate rename dialog, stuff data from current or clicked file // dialog remains active when new file is opened char rename_old[200] = ""; char rename_new[200] = ""; char rename_prev[200] = ""; char *rename_file = 0; void m_rename(GtkWidget *, cchar *menu) { int rename_dialog_event(zdialog *zd, cchar *event); char *pdir, *pfile, *pext; F1_help_topic = "rename_file"; if (rename_file) zfree(rename_file); rename_file = 0; if (clicked_file) { // use clicked file if present rename_file = clicked_file; clicked_file = 0; } else if (curr_file) // else current file rename_file = zstrdup(curr_file); else return; if (checkpend("all")) return; /*** ______________________________________ | Rename Image File | | | | Old Name [_______________________] | | New Name [_______________________] | | [previous name] [Add 1] | | | | [x] keep this dialog open | // 17.04 | | | [apply] [cancel] | |______________________________________| ***/ if (! zdrename) // restart dialog { zdrename = zdialog_new(ZTX("Rename Image File"),Mwin,Bapply,Bcancel,null); zdialog *zd = zdrename; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"vbox","vb1","hb1",0,"homog|space=5"); zdialog_add_widget(zd,"vbox","vb2","hb1",0,"homog|expand"); zdialog_add_widget(zd,"label","Lold","vb1",ZTX("Old Name")); zdialog_add_widget(zd,"label","Lnew","vb1",ZTX("New Name")); zdialog_add_widget(zd,"label","space","vb1"); zdialog_add_widget(zd,"hbox","hb2","vb2"); zdialog_add_widget(zd,"label","oldname","hb2"); zdialog_add_widget(zd,"label","space","hb2",0,"expand"); zdialog_add_widget(zd,"zentry","newname","vb2",0,"size=30"); zdialog_add_widget(zd,"hbox","hb3","vb2",0,"space=3"); zdialog_add_widget(zd,"button","Bprev","hb3",ZTX("previous name")); zdialog_add_widget(zd,"button","Badd1","hb3",ZTX("Add 1"),"space=8"); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=3"); zdialog_add_widget(zd,"check","keepopen","hb4",ZTX("keep this dialog open"),"space=3"); zdialog_restore_inputs(zd); zdialog_run(zd,rename_dialog_event,"parent"); // run dialog } zdialog *zd = zdrename; parsefile(rename_file,&pdir,&pfile,&pext); strncpy0(rename_old,pfile,199); strncpy0(rename_new,pfile,199); zdialog_stuff(zd,"oldname",rename_old); // current file name zdialog_stuff(zd,"newname",rename_new); // entered file name (same) return; } // dialog event and completion callback function int rename_dialog_event(zdialog *zd, cchar *event) { char *pp, *pdir, *pfile, *pext, suffix[16]; char *newfile = 0, *nextfile = 0, namever[200]; int nseq, digits, ccp, ccn, ccx, err, Fkeep; STATB statb; if (strmatch(event,"Bprev")) // previous name >> new name { if (! *rename_prev) return 1; parsefile(rename_prev,&pdir,&pfile,&pext); // get previous rename name pp = strrchr(rename_prev,'.'); if (pp && strlen(pp) == 4 && pp[1] == 'v' // look for file version .vNN && pp[2] >= '0' && pp[2] <= '9' // and remove if found && pp[3] >= '0' && pp[3] <= '9') *pp = 0; zdialog_stuff(zd,"newname",rename_prev); // stuff prev rename name into dialog if (! rename_file) return 1; // 17.01 parsefile(rename_file,&pdir,&pfile,&pext); // curr. file to be renamed pp = strrchr(pfile,'.'); // look for file version .vNN if (pp && strlen(pp) == 4 && pp[1] == 'v' && pp[2] >= '0' && pp[2] <= '9' && pp[3] >= '0' && pp[3] <= '9') { *namever = 0; // prev rename name + curr file version strncatv(namever,200,rename_prev,pp,null); zdialog_stuff(zd,"newname",namever); // stuff into dialog } } if (strmatch(event,"Badd1")) // increment sequence number { zdialog_fetch(zd,"newname",rename_new,188); // get entered filename pp = strrchr(rename_new,'.'); // find .ext if (pp && strlen(pp) < 8) { if (strmatchN(pp-4,".v",2) && // find .vNN.ext 18.01 (pp[2] >= '0' && pp[2] <= '9') && (pp[3] >= '0' && pp[3] <= '9')) pp -= 4; strncpy0(suffix,pp,16); // save .ext or .vNN.ext } else { pp = rename_new + strlen(rename_new); *suffix = 0; } digits = 0; while (pp[-1] >= '0' && pp[-1] <= '9') { pp--; // look for NNN in filenameNNN digits++; } nseq = 1; if (digits) nseq += atoi(pp); // NNN + 1 if (nseq > 9999) nseq = 1; if (digits < 1) digits = 1; if (nseq > 9 && digits < 2) digits = 2; if (nseq > 99 && digits < 3) digits = 3; if (nseq > 999 && digits < 4) digits = 4; sprintf(pp,"%0*d",digits,nseq); // replace NNN with NNN + 1 strncat(rename_new,suffix,199); // append .ext or .vNN.ext 18.01 zdialog_stuff(zd,"newname",rename_new); } if (zd->zstat == 0) return 1; // not finished if (zd->zstat != 1) goto KILL; // canceled zd->zstat = 0; // apply - keep dialog active if (! rename_file) return 1; if (checkpend("all")) return 1; parsefile(rename_file,&pdir,&pfile,&pext); // existing /directories/file.ext zdialog_fetch(zd,"newname",rename_new,194); // new file name from user ccp = strlen(pdir); // length of /directories/ ccn = strlen(rename_new); // length of file if (pext) ccx = strlen(pext); // length of .ext else ccx = 0; newfile = (char *) zmalloc(ccp + ccn + ccx + 1); // put it all together strncpy(newfile,rename_file,ccp); // /directories.../newfilename.ext strcpy(newfile+ccp,rename_new); if (ccx) strcpy(newfile+ccp+ccn,pext); err = stat(newfile,&statb); // check if new name exists if (! err) { zmessageACK(Mwin,Bfileexists); zfree(newfile); newfile = 0; return 1; } if (FGWM == 'F') nextfile = gallery(0,"get",curr_file_posn+1); // save next file, before rename else nextfile = 0; err = rename(rename_file,newfile); // rename the file if (err) { zmessageACK(Mwin,"file error: %s",strerror(errno)); goto KILL; } load_filemeta(newfile); // add new file to image index update_image_index(newfile); delete_image_index(rename_file); // delete old file in image index delete_thumbnail(rename_file); // remove thumbnails, disk and cache delete_thumbnail(newfile); // (will auto add when referenced) add_recent_file(newfile); // first in recent files list strncpy0(rename_prev,rename_new,199); // save new name to previous name if (curr_file && strmatch(rename_file,curr_file)) // current file exists no more free_resources(); if (navi::gallerytype == GDIR) { // refresh gallery list gallery(0,"init",0); gallery(0,"sort",-2); // recall sort and position 18.01 gallery(0,"paint",-1); } zdialog_fetch(zd,"keepopen",Fkeep); // 17.04 if (Fkeep) { // keep dialog open if (FGWM == 'F' && nextfile) { f_open(nextfile); // will call m_rename() gtk_window_present(MWIN); // keep focus on main window } if (nextfile) zfree(nextfile); nextfile = 0; if (newfile) zfree(newfile); newfile = 0; return 1; } else { // close dialog 17.04 f_open(newfile); // open renamed file goto KILL; } KILL: if (nextfile) zfree(nextfile); nextfile = 0; if (newfile) zfree(newfile); newfile = 0; zdialog_free(zd); // kill dialog zdrename = 0; return 1; } /******************************************************************************** right-click popup menu functions *********************************************************************************/ // copy or move an image file to a new location char *copymove_file = 0; void m_copy_move(GtkWidget *, cchar *) // combine copy and move { int copymove_dialog_event(zdialog *zd, cchar *event); cchar *title = ZTX("Copy or Move Image File"); F1_help_topic = "copy_move"; if (copymove_file) zfree(copymove_file); copymove_file = 0; if (clicked_file) { // use clicked file if present copymove_file = clicked_file; clicked_file = 0; } else if (curr_file) // else use current file copymove_file = zstrdup(curr_file); else return; if (checkpend("all")) return; /*** ________________________________________________________ | Copy or Move Image File | | | | Image File: [______________________________] | | New Location: [____________________________] [browse] | | | | (o) copy (duplicate file) (o) move (remove original) | | [x] keep this dialog open | // 17.04 | | | [apply] [cancel] | |________________________________________________________| ***/ if (! zdcopymove) { zdcopymove = zdialog_new(title,Mwin,Bapply,Bcancel,null); zdialog *zd = zdcopymove; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labf","hb1",ZTX("Image File:"),"space=5"); zdialog_add_widget(zd,"label","file","hb1"); zdialog_add_widget(zd,"label","space","hb1",0,"expand"); zdialog_add_widget(zd,"hbox","hb2","dialog"); zdialog_add_widget(zd,"label","labl","hb2",ZTX("New Location:"),"space=5"); zdialog_add_widget(zd,"zentry","newloc","hb2",0,"expand"); zdialog_add_widget(zd,"button","browse","hb2",Bbrowse,"space=5"); zdialog_add_widget(zd,"hbox","hb3","dialog",0,"space=3"); zdialog_add_widget(zd,"radio","copy","hb3",ZTX("copy (duplicate file)"),"space=5"); zdialog_add_widget(zd,"radio","move","hb3",ZTX("move (remove original)"),"space=5"); zdialog_add_widget(zd,"hbox","hb4","dialog",0,"space=3"); zdialog_add_widget(zd,"check","keepopen","hb4",ZTX("keep this dialog open"),"space=3"); zdialog_stuff(zd,"copy",1); zdialog_stuff(zd,"move",0); zdialog_stuff(zd,"keepopen",0); zdialog_restore_inputs(zd); zdialog_resize(zd,400,0); zdialog_run(zd,copymove_dialog_event,"parent"); // run dialog } zdialog *zd = zdcopymove; char *pp = strrchr(copymove_file,'/'); if (pp) pp++; else pp = copymove_file; zdialog_stuff(zd,"file",pp); if (copymove_loc) zdialog_stuff(zd,"newloc",copymove_loc); return; } // dialog event and completion callback function int copymove_dialog_event(zdialog *zd, cchar *event) { int Fcopy, Fmove; char *newloc, *pfile, *newfile = 0, *nextfile = 0; int cc, err, Nth, Fkeep; STATB statb; if (strmatch(event,"browse")) { // browse for new location if (! copymove_loc) { copymove_loc = (char *) zmalloc(XFCC); zdialog_fetch(zd,"newloc",copymove_loc,XFCC); } newloc = zgetfile(ZTX("Select directory"),MWIN,"folder",copymove_loc); if (! newloc) return 1; zdialog_stuff(zd,"newloc",newloc); if (copymove_loc) zfree(copymove_loc); copymove_loc = newloc; return 1; } if (strmatch(event,"copy")) { // get copy or move option zdialog_fetch(zd,"copy",Fcopy); Fmove = 1 - Fcopy; } if (strmatch(event,"move")) { zdialog_fetch(zd,"move",Fmove); Fcopy = 1 - Fmove; } if (zd->zstat == 0) return 1; // wait for dialog finished if (zd->zstat != 1) { // cancel or [x] zdialog_free(zd); // kill dialog zdcopymove = 0; return 1; } zd->zstat = 0; // apply - keep dialog active if (! copymove_file) return 1; if (checkpend("all")) return 1; if (copymove_loc) zfree(copymove_loc); // get new location from dialog copymove_loc = (char *) zmalloc(XFCC); zdialog_fetch(zd,"newloc",copymove_loc,XFCC); stat(copymove_loc,&statb); // check for valid directory if (! S_ISDIR(statb.st_mode)) { zmessageACK(Mwin,ZTX("new location is not a directory")); return 1; } pfile = strrchr(copymove_file,'/'); // isolate source file filename.ext if (pfile) pfile++; else pfile = copymove_file; cc = strlen(copymove_loc) + strlen(pfile) + 2; // new file = /new/location/filename.ext newfile = (char *) zmalloc(cc); strcpy(newfile,copymove_loc); strcat(newfile,"/"); strcat(newfile,pfile); err = stat(newfile,&statb); // check if new file exists if (! err) { zmessageACK(Mwin,Bfileexists); zfree(newfile); return 1; } err = copyFile(copymove_file,newfile); // copy source file to new file 17.08 if (err) { zmessageACK(Mwin,strerror(err)); zfree(newfile); zdialog_free(zd); zdcopymove = 0; return 1; } load_filemeta(newfile); // update image index for new file update_image_index(newfile); // (memory metadata now invalid) if (FGWM == 'F') { // if F-view, get next file Nth = curr_file_posn + 1; nextfile = gallery(0,"get",Nth); } zdialog_fetch(zd,"move",Fmove); if (Fmove) { // move - delete source file err = remove(copymove_file); if (err) { zmessageACK(Mwin,ZTX("delete failed: \n %s"),strerror(errno)); zfree(newfile); zdialog_free(zd); zdcopymove = 0; return 1; } delete_image_index(copymove_file); // delete in image index delete_thumbnail(copymove_file); // delete thumbnail file and cache if (curr_file && strmatch(curr_file,copymove_file)) // current file gone free_resources(); } if (FGWM == 'F') { // F-view if (nextfile) f_open(nextfile); // open next file else { // end of gallery if (Fmove) free_resources(); // no curr. file zdialog_free(zd); // kill dialog zdcopymove = 0; } gtk_window_present(MWIN); // keep focus on main window } if (FGWM == 'G' && Fmove) // G-view and file is gone zdialog_stuff(zd,"file",""); if (navi::gallerytype == GDIR) { // refresh gallery gallery(0,"init",0); gallery(0,"sort",-2); // recall sort and position 18.01 gallery(0,"paint",-1); } if (nextfile) zfree(nextfile); // free memory if (newfile) zfree(newfile); zdialog_fetch(zd,"keepopen",Fkeep); // 17.04 if (Fkeep) return 1; // keep dialog open zdialog_free(zd); // kill dialog zdcopymove = 0; return 1; } /********************************************************************************/ // copy selected image file to the desktop void m_copyto_desktop(GtkWidget *, cchar *) { char *pp, desktop[100]; int err; F1_help_topic = "copyto_desktop"; snprintf(desktop,100,"%s/Desktop",getenv("HOME")); pp = clicked_file; if (! pp) pp = curr_file; if (! pp) return; err = copyFile(pp,desktop); if (err) zmessageACK(Mwin,strerror(err)); if (clicked_file) zfree(clicked_file); clicked_file = 0; return; } /********************************************************************************/ // function to copy an image file to the clipboard // used in file view and thumbnail right-click popup menu // uses and frees clicked_file if present, else uses current file void m_copyto_clip(GtkWidget *, cchar *) { int file_copytoclipboard(char *file); char tempfile[200]; int err; if (clicked_file) { file_copytoclipboard(clicked_file); // get file behind thumbnail zfree(clicked_file); clicked_file = 0; return; } if (! curr_file) return; // no current file snprintf(tempfile,200,"%s/clipboard.jpg",tempdir); // may be edited and unsaved 17.04 err = f_save(tempfile,"jpg",8,1); if (err) return; file_copytoclipboard(tempfile); remove(tempfile); return; } // copy an image file to the clipboard (as pixbuf) // any prior clipboard image is replaced // supports copy/paste to other apps (not used in fotoxx) // returns 1 if OK, else 0 int file_copytoclipboard(char *file) { GtkClipboard *clipboard; PIXBUF *pixbuf; GError *gerror = 0; clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); if (! clipboard) return 0; gtk_clipboard_clear(clipboard); pixbuf = gdk_pixbuf_new_from_file(file,&gerror); if (! pixbuf) return 0; gtk_clipboard_set_image(clipboard,pixbuf); g_object_unref(pixbuf); return 1; } /********************************************************************************/ // Delete or Trash an image file. // Use the Linux standard trash function. // If not available, revert to Desktop folder. char *delete_trash_file = 0; void m_delete_trash(GtkWidget *, cchar *) // combined delete/trash function { int delete_trash_dialog_event(zdialog *zd, cchar *event); cchar *title = ZTX("Delete/Trash Image File"); F1_help_topic = "delete_trash"; if (delete_trash_file) zfree(delete_trash_file); delete_trash_file = 0; if (clicked_file) { // use clicked file if present 18.01 delete_trash_file = clicked_file; clicked_file = 0; } else if (curr_file) // else current file delete_trash_file = zstrdup(curr_file); if (checkpend("all")) return; /*** ______________________________________ | Delete/Trash Image File | | | | Image File: [______________________] | | | | [x] keep this dialog open | // 17.04 | | | [delete] [trash] [cancel] | |______________________________________| ***/ if (! zddeltrash) // start dialog if not already { zddeltrash = zdialog_new(title,Mwin,Bdelete,Btrash,Bcancel,null); zdialog *zd = zddeltrash; zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); zdialog_add_widget(zd,"label","labf","hb1",ZTX("Image File:"),"space=3"); zdialog_add_widget(zd,"label","file","hb1",0,"space=3"); zdialog_add_widget(zd,"hbox","hb2","dialog",0,"space=3"); zdialog_add_widget(zd,"check","keepopen","hb2",ZTX("keep this dialog open"),"space=3"); zdialog_resize(zd,300,0); zdialog_restore_inputs(zd); zdialog_run(zd,delete_trash_dialog_event,"parent"); // run dialog } if (delete_trash_file) { char *pp = strrchr(delete_trash_file,'/'); if (pp) pp++; else pp = delete_trash_file; zdialog_stuff(zddeltrash,"file",pp); } else zdialog_stuff(zddeltrash,"file",""); return; } // dialog event and completion callback function int delete_trash_dialog_event(zdialog *zd, cchar *event) { int err = 0, yn = 0, Nth, gstat, ftype, Fkeep; char *file; GError *gerror = 0; GFile *gfile = 0; STATB statb; cchar *trashmess = ZTX("GTK g_file_trash() function failed"); if (zd->zstat == 0) return 1; // wait for dialog finished if (zd->zstat == 1) goto PREP; // [delete] button if (zd->zstat == 2) goto PREP; // [trash] button goto KILL; // [cancel] or [x] PREP: if (! delete_trash_file) goto KILL; // no file to delete or trash err = stat(delete_trash_file,&statb); // check file exists if (err) { zmessageACK(Mwin,strerror(errno)); goto KILL; } ftype = image_file_type(delete_trash_file); if (ftype != IMAGE && ftype != RAW && ftype != VIDEO) { // must be image or RAW or VIDEO 17.08 zmessageACK(Mwin,ZTX("not a known image file")); goto KILL; } if (! (statb.st_mode & S_IWUSR)) { // check permission if (zd->zstat == 1) yn = zmessageYN(Mwin,ZTX("Delete read-only file?")); if (zd->zstat == 2) yn = zmessageYN(Mwin,ZTX("Trash read-only file?")); if (! yn) goto NEXT; // keep file statb.st_mode |= S_IWUSR; // make writable if needed err = chmod(delete_trash_file,statb.st_mode); if (err) { zmessageACK(Mwin,strerror(errno)); goto KILL; } } if (checkpend("all")) return 1; // check for conflicts if (zd->zstat == 1) goto DELETE; // [delete] button if (zd->zstat == 2) goto TRASH; // [trash] button DELETE: err = remove(delete_trash_file); // delete file if (! err) goto BOTH; zmessageACK(Mwin,ZTX("delete failed: \n %s"),strerror(errno)); goto KILL; TRASH: gfile = g_file_new_for_path(delete_trash_file); gstat = g_file_trash(gfile,0,&gerror); // move file to trash g_object_unref(gfile); if (gstat) goto BOTH; zmessageACK(Mwin,trashmess); if (gerror) printz("%s\n",gerror->message); goto KILL; BOTH: delete_image_index(delete_trash_file); // delete file in image index delete_thumbnail(delete_trash_file); // delete thumbnail file and cache Nth = file_position(delete_trash_file,0); // find in gallery list if (Nth >= 0) gallery(0,"delete",Nth); // delete from gallery list if (curr_file && strmatch(delete_trash_file,curr_file)) { // current file was removed last_file_posn = curr_file_posn; // remember for prev/next use free_resources(); } if (FGWM == 'F') { // F window file = gallery(0,"get",curr_file_posn); // new current file if (! file) poptext_window(ZTX("no more images"),MWIN,200,200,0,3); else { f_open(file); zfree(file); } zdialog_fetch(zd,"keepopen",Fkeep); // 17.04 if (! Fkeep) goto KILL; zd->zstat = 0; // keep dialog active gtk_window_present(MWIN); // keep focus on main window return 1; } NEXT: if (FGWM == 'G') { // gallery view gallery(0,"init",0); // refresh for removed file gallery(0,"sort",-2); // recall sort and position 18.01 gallery(0,"paint",-1); // paint zdialog_stuff(zd,"file",""); // erase file in dialog } if (FGWM == 'F') m_next(0,0); // file view mode, open next image zdialog_fetch(zd,"keepopen",Fkeep); // 17.04 if (Fkeep) { zd->zstat = 0; // keep dialog active return 1; } KILL: if (delete_trash_file) zfree(delete_trash_file); // free memory delete_trash_file = 0; zdialog_free(zd); // kill dialog zddeltrash = 0; return 1; } /********************************************************************************/ // print image file void m_print(GtkWidget *, cchar *) // use GTK print { int print_addgrid(PXB *Ppxb); int pstat; char *printfile = 0; PXB *Ppxb = 0; GError *gerror = 0; F1_help_topic = "print_image"; if (clicked_file) Ppxb = PXB_load(clicked_file); // clicked thumbnail else if (E0pxm) Ppxb = PXM_PXB_copy(E0pxm); // current edited file else if (curr_file) Ppxb = PXB_load(curr_file); // current file clicked_file = 0; if (! Ppxb) return; print_addgrid(Ppxb); // add grid lines if wanted printfile = zstrdup(tempdir,20); // make temp print file: strcat(printfile,"/printfile.bmp"); // /.../fotoxx-nnnnnn/printfile.bmp pstat = gdk_pixbuf_save(Ppxb->pixbuf,printfile,"bmp",&gerror,null); // bmp much faster than png if (! pstat) { zmessageACK(Mwin,"error: %s",gerror->message); PXB_free(Ppxb); zfree(printfile); return; } print_image_file(Mwin,printfile); PXB_free(Ppxb); zfree(printfile); return; } // add grid lines to print image if wanted int print_addgrid(PXB *Ppxb) { uint8 *pix; int px, py, ww, hh; int startx, starty, stepx, stepy; int G = currgrid; if (! gridsettings[G][GON]) return 0; // grid lines off ww = Ppxb->ww; hh = Ppxb->hh; stepx = gridsettings[G][GXS]; // space between grid lines stepy = gridsettings[G][GYS]; stepx = stepx / Mscale; // window scale to image scale stepy = stepy / Mscale; if (gridsettings[G][GXC]) // if line counts specified, stepx = ww / (1 + gridsettings[G][GXC]); // set spacing accordingly if (gridsettings[G][GYC]) stepy = hh / ( 1 + gridsettings[G][GYC]); startx = stepx * gridsettings[G][GXF] / 100; // variable offsets if (startx < 0) startx += stepx; starty = stepy * gridsettings[G][GYF] / 100; if (starty < 0) starty += stepy; if (gridsettings[G][GX]) { // x-grid enabled for (px = startx; px < ww-1; px += stepx) for (py = 0; py < hh; py++) { pix = PXBpix(Ppxb,px,py); pix[0] = pix[1] = pix[2] = 255; pix[3] = pix[4] = pix[5] = 0; } } if (gridsettings[G][GY]) { // y-grid enabled for (py = starty; py < hh-1; py += stepy) for (px = 0; px < ww; px++) { pix = PXBpix(Ppxb,px,py); pix[0] = pix[1] = pix[2] = 255; pix = PXBpix(Ppxb,px,py+1); pix[0] = pix[1] = pix[2] = 0; } } return 1; } // print calibrated image // menu function calling print_calibrated() in f.tools.cc void m_print_calibrated(GtkWidget *, cchar *) { print_calibrated(); return; } /********************************************************************************/ // normal quit menu function void m_quit(GtkWidget *, cchar *) { int busy; printz("quit \n"); Fshutdown++; for (int ii = 0; ii < 20; ii++) { // wait up to 2 secs. if something running busy = checkpend("all quiet"); if (! busy) break; zmainloop(); zsleep(0.1); } if (busy) printz("busy function killed \n"); quitxx(); // does not return return; } // used also for main window delete and destroy events // does not return void quitxx() { Fshutdown++; gtk_window_get_position(MWIN,&mwgeom[0],&mwgeom[1]); // get last window position gtk_window_get_size(MWIN,&mwgeom[2],&mwgeom[3]); // and size for next session zdialog_inputs("save"); // save dialog inputs zdialog_positions("save"); // save dialog positions gallery_memory("save"); // save recent gallery positions 17.08 free_resources(); // delete temp files fflush(null); // flush stdout, stderr exif_server(0,0,0); // kill exif_server processes shell_quiet("rm -R -f %s",tempdir); // delete temp files cchar *startup_counter = "https://kornelix.net/fotoxx/fotoxx-startups"; // kornelix.net startup counter 18.01 if (++sessioncount % 100 == 0) // (1% of fotoxx sessions) shell_quiet("wget -b -q -O /dev/null %s 1>/dev/null",startup_counter); // reference counter, no data xfer save_params(); // save state for next session gtk_main_quit(); // gone forever exit(1); // just in case } /********************************************************************************/ // save (modified) image file to disk void m_file_save(GtkWidget *, cchar *menu) { int file_save_dialog_event(zdialog *zd, cchar *event); cchar *pp; zdialog *zd; F1_help_topic = "file_save"; if (! curr_file) { if (zdfilesave) zdialog_destroy(zdfilesave); zdfilesave = 0; return; } if (strmatch(curr_file_type,"other")) // if unsupported type, use jpg strcpy(curr_file_type,"jpg"); /*** _______________________________________________ | | | Save Image File | | | | filename.jpg | | | | [new version] save as new file version | | [new file ...] save as new file name or type | | [replace file] replace file (OVERWRITE) | | | | [cancel] | |_______________________________________________| ***/ if (! zdfilesave) { zd = zdialog_new(ZTX("Save Image File"),Mwin,Bcancel,null); zdfilesave = zd; zdialog_add_widget(zd,"hbox","hbf","dialog",0,"space=3"); zdialog_add_widget(zd,"label","filename","hbf",0,"space=10"); zdialog_add_widget(zd,"hbox","hb0","dialog"); zdialog_add_widget(zd,"vbox","vb1","hb0",0,"space=3|homog"); zdialog_add_widget(zd,"vbox","vb2","hb0",0,"space=3|homog"); zdialog_add_widget(zd,"button","newvers","vb1",ZTX("new version")); zdialog_add_widget(zd,"button","newfile","vb1",ZTX("new file ...")); zdialog_add_widget(zd,"button","replace","vb1",ZTX("replace file")); zdialog_add_widget(zd,"hbox","hb1","vb2"); zdialog_add_widget(zd,"hbox","hb2","vb2"); zdialog_add_widget(zd,"hbox","hb3","vb2"); zdialog_add_widget(zd,"label","labvers","hb1",ZTX("save as new file version")); zdialog_add_widget(zd,"label","labfile","hb2",ZTX("save as new file name or type")); zdialog_add_widget(zd,"icon","warning","hb3","warning.png","size=30"); zdialog_add_widget(zd,"label","labrepl","hb3",ZTX("replace old file (OVERWRITE)"),"space=3"); } zd = zdfilesave; pp = strrchr(curr_file,'/'); if (pp) zdialog_stuff(zd,"filename",pp+1); zdialog_run(zd,file_save_dialog_event,"mouse"); return; } // dialog event and completion function int file_save_dialog_event(zdialog *zd, cchar *event) { char *newfilename; char newfiletype[8]; int err; char event2[12]; if (zd->zstat) { // cancel zdialog_free(zd); // kill dialog zdfilesave = 0; return 1; } if (! strstr("newvers newfile replace",event)) return 1; // ignore other events strncpy0(event2,event,12); // preserve event zdialog_free(zd); // kill dialog zdfilesave = 0; if (strstr("newvers replace",event2)) { if (strmatch(curr_file_type,"RAW")) { zmessageACK(Mwin,ZTX("cannot save as RAW type")); return 1; } } if (checkpend("busy block")) return 1; if (strmatch(event2,"newvers")) { strncpy0(newfiletype,curr_file_type,6); newfilename = file_new_version(curr_file); // get next avail. file version name if (! newfilename) return 1; err = f_save(newfilename,newfiletype,curr_file_bpc); // save file if (! err) f_open_saved(); // open saved file with edit hist zfree(newfilename); } if (strmatch(event2,"replace")) { err = f_save(curr_file,curr_file_type,curr_file_bpc); // save file with curr. edits applied if (err) return 1; curr_file_size = f_save_size; } if (strmatch(event2,"newfile")) err = f_save_as(); // save-as file chooser dialog return 1; } // menu entry for KB shortcut Save File (replace) // 17.04 void m_file_save_replace(GtkWidget *, cchar *menu) { int err; err = f_save(curr_file,curr_file_type,curr_file_bpc); // save file if (! err) f_open_saved(); // open saved file with edit hist return; } // menu entry for KB shortcut Save File Version void m_file_save_version(GtkWidget *, cchar *menu) { char *newfilename; char newfiletype[8]; int err; strncpy0(newfiletype,curr_file_type,6); newfilename = file_new_version(curr_file); // get next avail. file version name if (! newfilename) return; err = f_save(newfilename,newfiletype,curr_file_bpc); // save file if (! err) f_open_saved(); // open saved file with edit hist zfree(newfilename); return; } /********************************************************************************/ // Get the next available version name "filename.vNN" for given file. // Returns null if bad file name or 99 versions already exist. // Returned file name is subject for zfree(). char * file_new_version(char *file) { char *findcom = 0, *findbuff = 0; char *pp1, *pp2, pext[8]; int vers, hivers, cc, nn; FILE *fid; pp1 = strrchr(file,'/'); // find file .ext if (! pp1) return 0; pp1 = strrchr(pp1,'.'); if (! pp1) return 0; strncpy0(pext,pp1,6); // save file .ext if (pp1[-4] == '.' && pp1[-3] == 'v' && // look for .vNN.ext pp1[-2] >= '0' && pp1[-2] <= '9' && pp1[-1] >= '0' && pp1[-1] <= '9') pp1 -= 4; // pp1 --> .ext or .vNN.ext cc = strlen(file); findcom = (char *) zmalloc(cc+20); // construct: find "/.../filename".* strcpy(findcom,"find \""); strcpy(findcom+6,file); nn = 6 + (pp1 - file); strcpy(findcom + nn,"\".*"); fid = popen(findcom,"r"); // find matching files if (! fid) goto ret0; findbuff = (char *) zmalloc(XFCC); pp2 = findbuff + (pp1 - file); // pp2 --> .* buffer position hivers = 0; while ((fgets(findbuff,XFCC,fid))) { // find all matching files if (pp2[0] != '.' || pp2[1] != 'v' || // ignore if not ".vNN.ext" pp2[2] < '0' || pp2[2] > '9' || pp2[3] < '0' || pp2[3] > '9' || pp2[4] != '.') continue; vers = 10 * (pp2[2] - '0') + (pp2[3] - '0'); // convert ".vNN" to integer NN if (vers > hivers) hivers = vers; } pclose(fid); if (hivers == 99) { zmessageACK(Mwin,ZTX("too many file versions: 99")); goto ret0; } vers = hivers + 1; strcpy(findcom,file); // construct: /.../filename.vNN.ext pp2 = findcom + (pp1 - file); strcpy(pp2,".v"); pp2[2] = vers/10 + '0'; pp2[3] = vers - 10 * (vers/10) + '0'; strcpy(pp2+4,pext); zfree(findbuff); return findcom; ret0: if (findcom) zfree(findcom); if (findbuff) zfree(findbuff); return 0; } /********************************************************************************/ // Get the last version name "filename.vNN" for the given file. // Returns null if bad file name or 99 versions already exist. // Returned file name is subject for zfree(). char * file_last_version(char *file) { char *findcom = 0, *findbuff = 0; char *pp1, *pp2, pext[8]; int vers, hivers, cc, nn; FILE *fid; pp1 = strrchr(file,'/'); // find file .ext if (! pp1) return 0; pp1 = strrchr(pp1,'.'); if (! pp1) return 0; strncpy0(pext,pp1,6); // save file .ext if (pp1[-4] == '.' && pp1[-3] == 'v' && // look for .vNN.ext pp1[-2] >= '0' && pp1[-2] <= '9' && pp1[-1] >= '0' && pp1[-1] <= '9') pp1 -= 4; // pp1 --> .ext or .vNN.ext cc = strlen(file); findcom = (char *) zmalloc(cc+20); // construct: find "/.../filename".* strcpy(findcom,"find \""); strcpy(findcom+6,file); nn = 6 + (pp1 - file); strcpy(findcom + nn,"\".*"); fid = popen(findcom,"r"); // find matching files if (! fid) goto ret0; findbuff = (char *) zmalloc(XFCC); pp2 = findbuff + (pp1 - file); // pp2 --> .* buffer position hivers = 0; while ((fgets(findbuff,XFCC,fid))) { // find all matching files if (pp2[0] != '.' || pp2[1] != 'v' || // ignore if not ".vNN.ext" pp2[2] < '0' || pp2[2] > '9' || pp2[3] < '0' || pp2[3] > '9' || pp2[4] != '.') continue; vers = 10 * (pp2[2] - '0') + (pp2[3] - '0'); // convert ".vNN" to integer NN if (vers > hivers) hivers = vers; } pclose(fid); vers = hivers; strcpy(findcom,file); // construct: /.../filename.vNN.ext if (vers == 0) return findcom; pp2 = findcom + (pp1 - file); strcpy(pp2,".v"); pp2[2] = vers/10 + '0'; pp2[3] = vers - 10 * (vers/10) + '0'; strcpy(pp2+4,pext); zfree(findbuff); return findcom; ret0: if (findcom) zfree(findcom); if (findbuff) zfree(findbuff); return 0; } /********************************************************************************/ // Get the previous version name "filename.vNN" for the given file. // Returns null if bad file name or no prior version exists. // Returned file name is subject for zfree(). char * file_prior_version(char *file) // 18.01 { char *findcom = 0, *findbuff = 0; char *pp1, *pp2, pext[8]; int vers, pvers, cc, nn; FILE *fid; pp1 = strrchr(file,'/'); // find file .ext if (! pp1) return 0; pp1 = strrchr(pp1,'.'); if (! pp1) return 0; strncpy0(pext,pp1,6); // save file .ext if (pp1[-4] == '.' && pp1[-3] == 'v' && // look for .vNN.ext pp1[-2] >= '0' && pp1[-2] <= '9' && pp1[-1] >= '0' && pp1[-1] <= '9') pp1 -= 4; // pp1 --> .vNN.ext else goto ret0; // if no .vNN, no prior version vers = 10 * (pp1[2] - '0') + (pp1[3] - '0'); // input file version cc = strlen(file) + 20; findcom = (char *) zmalloc(cc); // construct: find "/.../filename".* strcpy(findcom,"find \""); strcpy(findcom+6,file); nn = 6 + (pp1 - file); strcpy(findcom + nn,"\".*"); fid = popen(findcom,"r"); // find matching files if (! fid) goto ret0; findbuff = (char *) zmalloc(cc); pp2 = findbuff + (pp1 - file); // pp2 --> .* buffer position pvers = 0; while ((fgets(findbuff,cc,fid))) { // find all matching files if (pp2[0] != '.' || pp2[1] != 'v' || // ignore if not ".vNN.ext" pp2[2] < '0' || pp2[2] > '9' || pp2[3] < '0' || pp2[3] > '9' || pp2[4] != '.') continue; nn = 10 * (pp2[2] - '0') + (pp2[3] - '0'); // convert ".vNN" to integer NN if (nn < vers && nn > pvers) pvers = nn; // keep highest version < input version } pclose(fid); zfree(findbuff); findbuff = 0; strcpy(findcom,file); pp2 = findcom + (pp1 - file); // pp2 --> .vNN.ext if (pvers == 0) { // prior version is filename.ext strcpy(pp2,pext); // (may or may not exist) return findcom; } strcpy(pp2,".v"); // construct: /.../filename.vNN.ext pp2[2] = pvers/10 + '0'; pp2[3] = pvers - 10 * (pvers/10) + '0'; strcpy(pp2+4,pext); return findcom; ret0: if (findcom) zfree(findcom); if (findbuff) zfree(findbuff); return 0; } /********************************************************************************/ // save current image to specified disk file (same or new). // set f_save_file, f_save_type, f_save_bpc, f_save_size. // update image index file. // returns 0 if OK, else +N. // If Fack is true, failure will cause a popup ACK dialog. int f_save(char *outfile, cchar *outype, int outbpc, int Fack) // use tiff/png/pixbuf libraries { cchar *exifkey[5], *exifdata[5]; char *ppv[1], *tempfile, *pext, *outfile2; char edithist[exif_maxcc]; // edit history trail int nkeys, err, cc1, cc2, ii, ii0; int Fmod, Fcopy, Ftransp, Fnewfile; int px, py; void (*menufunc)(GtkWidget *, cchar *); STATB statbuf; printf("f_save() %s %s %d \n",outfile,outype,outbpc); //////////// cchar *warnalpha = ZTX("Transparency map will be lost.\n" "save to PNG file to retain."); if (! curr_file) return 1; if (strmatch(outype,"RAW")) { // disallow saving as RAW type zmessageACK(Mwin,ZTX("cannot save as RAW type")); return 1; } Fmod = 0; menufunc = 0; if (CEF) { // edit function active if (CEF->Fmods) Fmod = 1; // active edits pending menufunc = CEF->menufunc; // save menu function for restart if (CEF->zd) zdialog_send_event(CEF->zd,"done"); // tell it to finish if (CEF) return 1; // failed (HDR etc.) } if (URS_pos > 0 && URS_saved[URS_pos] == 0) Fmod = 1; // completed edits not saved if (strmatch(outfile,curr_file)) Fnewfile = 0; // replace current file else Fnewfile = 1; // new file (name/.ext/location) outfile2 = zstrdup(outfile,6); // file to be output if (Fnewfile) { // if new file, force .ext pext = strrchr(outfile2,'/'); // to one of: .jpg .png .tif if (pext) pext = strrchr(pext,'.'); if (! pext) pext = outfile2 + strlen(outfile2); pext[0] = '.'; strcpy(pext+1,outype); } if (! Fnewfile && ! Fmod) { // no edit changes to file 18.01 Fcopy = 1; // direct copy to output file? if (E0pxm) Fcopy = 0; // no, non-edit change (upright) if (curr_file_bpc != outbpc) Fcopy = 0; // no, BPC change if (jpeg_1x_quality < jpeg_def_quality) Fcopy = 0; // no, higher compression wanted if (Fcopy) { err = copyFile(curr_file,outfile2); // copy unchanged file to output 17.08 if (! err) goto updateindex; zmessageACK(Mwin,strerror(err)); return 1; } } Ffuncbusy = 1; // may be large file, slow CPU if (! E0pxm) { paintlock(1); // block window updates E0pxm = PXM_load(curr_file,1); // no prior edits, load image file paintlock(0); // unblock window updates if (! E0pxm) { zfree(outfile2); Ffuncbusy = 0; // PXM_load() diagnoses error return 1; } } tempfile = zstrdup(outfile2,20); // temp file in same directory strcat(tempfile,"-temp."); strcat(tempfile,outype); if (strmatch(outype,"tif")) // save as tif file (bpc = 8 or 16) err = PXM_TIFF_save(E0pxm,tempfile,outbpc); else if (strmatch(outype,"png")) // save as png file (bpc = 8 or 16) err = PXM_PNG_save(E0pxm,tempfile,outbpc); else // save as .jpg { Ftransp = 0; if (E0pxm->nc > 3) { // check for transparency for (py = 2; py < E0pxm->hh-2; py += 2) // ignore extreme edges for (px = 2; px < E0pxm->ww-2; px += 2) if (PXMpix(E0pxm,px,py)[3] < 254) { Ftransp = 1; goto breakout; } } breakout: if (Ftransp) { ii = zdialog_choose(Mwin,warnalpha,ZTX("save anyway"),Bcancel,null); if (ii != 1) { remove(tempfile); zfree(tempfile); zfree(outfile2); Ffuncbusy = 0; return 0; } } err = PXM_ANY_save(E0pxm,tempfile); // (bpc = 8 always) outbpc = 8; } if (err) { if (*file_errmess && Fack) // pass error to user zmessageACK(Mwin,file_errmess); remove(tempfile); // failure, clean up zfree(tempfile); zfree(outfile2); Ffuncbusy = 0; return 1; } exifkey[0] = exif_editlog_key; exif_get(curr_file,&exifkey[0],ppv,1); // get prior edit history in EXIF if (ppv[0]) { strncpy0(edithist,ppv[0],exif_maxcc-2); zfree(ppv[0]); cc1 = strlen(edithist); // edits made before this file was opened } else cc1 = 0; // none if ((CEF && CEF->Fmods) || URS_pos > 0) // active or completed unsaved edits { strcpy(edithist+cc1," "); // update edit history strcpy(edithist+cc1+1,"Fotoxx:"); // append " Fotoxx:" cc1 += 8; if (URS_reopen_pos > 0) ii0 = URS_reopen_pos + 1; // if last file saved was kept open, else ii0 = 1; // these edits are already in history for (ii = ii0; ii <= URS_pos; ii++) // append list of edits from undo/redo stack { // (omit index 0 = initial image) cc2 = strlen(URS_funcs[ii]); strcpy(edithist+cc1,URS_funcs[ii]); strcpy(edithist+cc1+cc2,"|"); cc1 += cc2 + 1; } if (CEF && CEF->Fmods) { // save during active edit function cc2 = strlen(CEF->funcname); // add curr. edit to history list strcpy(edithist+cc1,CEF->funcname); strcpy(edithist+cc1+cc2,"|"); cc1 += cc2 + 1; } } exifkey[0] = exif_orientation_key; // remove EXIF orientation exifdata[0] = ""; // (assume saved file is fixed) nkeys = 1; if (cc1) { // prior and/or curr. edit history exifkey[1] = exif_editlog_key; // will be added to EXIF exifdata[1] = edithist; nkeys = 2; } err = exif_copy(curr_file,tempfile,exifkey,exifdata,nkeys); // copy all EXIF/IPTC data to if (err && Fack) // temp file with above revisions zmessageACK(Mwin,ZTX("Unable to copy EXIF/IPTC data")); err = rename(tempfile,outfile2); // rename temp file to output file if (err) { if (Fack) zmessageACK(Mwin,strerror(err)); remove(tempfile); // delete temp file zfree(tempfile); zfree(outfile2); Ffuncbusy = 0; return 2; // could not save } zfree(tempfile); // free memory for (ii = 0; ii <= URS_pos; ii++) // mark all prior edits as saved URS_saved[ii] = 1; updateindex: update_thumbnail_file(outfile2); // refresh thumbnail file load_filemeta(outfile2); // load output file metadata if (Fmod) { set_meta_size(E0pxm->ww,E0pxm->hh); // set size in metadata 17.04 add_tag_fotoxx(outfile2); // edited file has "fotoxx" tag 17.04 save_filemeta(outfile2); // includes update_image_index() } else update_image_index(outfile2); // add output file to image index // if (Fnewfile) load_filemeta(curr_file); // removed 18.01 if (samedirk(outfile2,navi::galleryname)) { // if saving into current gallery gallery(curr_file,"init",0); // update curr. gallery list gallery(0,"sort",-2); // recall sort and position 18.01 set_mwin_title(); // update posn, count in title } add_recent_file(outfile2); // first in recent files list if (f_save_file) zfree(f_save_file); // actual file saved f_save_file = outfile2; strcpy(f_save_type,outype); // update f_save_xxx data f_save_bpc = outbpc; err = stat(outfile2,&statbuf); f_save_size = statbuf.st_size; Ffuncbusy = 0; if (menufunc) menufunc(0,0); // restart edit function return 0; } // save (modified) image to new file name or type // confirm if overwrite of existing file // returns 0 if OK, 1 if cancel or error GtkWidget *saveas_fchooser; int f_save_as() { int f_save_as_dialog_event(zdialog *zd, cchar *event); zdialog *zd; static char *save_dirk = 0; cchar *type; char *newfile, *fname; char *outfile = 0, *outfile2 = 0, *pp, *pext; int ii, zstat, err, yn; int bpc, mkcurr = 0; STATB statbuf; /*** _____________________________________________________________________________ | Save as New File Name or Type | | ________________________________________________________________________ | | | | | | | file chooser dialog | | | | | | | | | | | | | | | | | | | | | | | |________________________________________________________________________| | | | | (o) tif-8 (o) tif-16 (o) png-8 (o) png-16 (o) jpg jpg quality [90] | | [x] make current (new file becomes current file) | | | | [save] [cancel] | |_____________________________________________________________________________| ***/ zd = zdialog_new(ZTX("save as new file name or type"),Mwin,Bsave,Bcancel,null); zdialog_add_widget(zd,"hbox","hbfc","dialog",0,"expand"); saveas_fchooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_SAVE); gtk_container_add(GTK_CONTAINER(zdialog_widget(zd,"hbfc")),saveas_fchooser); zdialog_add_widget(zd,"vbox","space","dialog",0,"space=3"); zdialog_add_widget(zd,"hbox","hbft","dialog"); zdialog_add_widget(zd,"radio","tif-8","hbft","tif-8","space=4"); zdialog_add_widget(zd,"radio","tif-16","hbft","tif-16","space=4"); zdialog_add_widget(zd,"radio","png-8","hbft","png-8","space=4"); zdialog_add_widget(zd,"radio","png-16","hbft","png-16","space=4"); zdialog_add_widget(zd,"radio","jpg","hbft","jpg","space=4"); zdialog_add_widget(zd,"label","space","hbft",0,"space=4"); zdialog_add_widget(zd,"label","labqual","hbft","jpg quality"); zdialog_add_widget(zd,"zspin","jpgqual","hbft","10|100|1|90","space=3"); zdialog_add_widget(zd,"hbox","hbmc","dialog"); zdialog_add_widget(zd,"check","mkcurr","hbmc",0,"space=8"); zdialog_add_widget(zd,"label","labmc","hbmc",ZTX("make current")); zdialog_add_widget(zd,"label","labmc2","hbmc",ZTX("(new file becomes current file)"),"space=5"); zdialog_labelfont(zd,"labmc","sans bold",ZTX("make current")); if (! save_dirk) { // default from curr. file save_dirk = zstrdup(curr_file); pp = strrchr(save_dirk,'/'); if (! pp) zappcrash("curr_file missing /"); *pp = 0; } gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(saveas_fchooser),save_dirk); newfile = file_new_version(curr_file); // suggest next version if (! newfile) newfile = zstrdup(curr_file); fname = strrchr(newfile,'/') + 1; gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(saveas_fchooser),fname); if (newfile) zfree(newfile); zdialog_stuff(zd,"tif-8",0); // turn off all radio buttons zdialog_stuff(zd,"tif-16",0); // GTK bug: programatic selection of one zdialog_stuff(zd,"png-8",0); // does not deselect the alternatives zdialog_stuff(zd,"png-16",0); // (this works for GUI selection only) zdialog_stuff(zd,"jpg",0); zdialog_stuff(zd,"jpgqual",jpeg_def_quality); // default jpeg quality, user setting if (strmatch(curr_file_type,"tif")) { // if curr. file type is tif, if (curr_file_bpc == 16) // select tif-8 or tif-16 button zdialog_stuff(zd,"tif-16",1); else zdialog_stuff(zd,"tif-8",1); } else if (strmatch(curr_file_type,"png")) { // same for png if (curr_file_bpc == 16) zdialog_stuff(zd,"png-16",1); else zdialog_stuff(zd,"png-8",1); } else zdialog_stuff(zd,"jpg",1); // else default jpg zdialog_stuff(zd,"mkcurr",0); // deselect "make current" zdialog_resize(zd,600,500); zdialog_run(zd,f_save_as_dialog_event,"parent"); zdialog_wait: zstat = zdialog_wait(zd); if (zstat != 1) { // user cancel zdialog_free(zd); return 1; } outfile2 = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(saveas_fchooser)); if (! outfile2) { zd->zstat = 0; goto zdialog_wait; } if (outfile) zfree(outfile); outfile = zstrdup(outfile2,12); // add space for possible .vNN and .ext g_free(outfile2); zfree(save_dirk); // remember save dirk for next time save_dirk = zstrdup(outfile); pp = strrchr(save_dirk,'/'); *(pp+1) = 0; // keep trailing '/' type = "jpg"; // default output type bpc = 8; zdialog_fetch(zd,"tif-8",ii); // get selected radio button if (ii) type = "tif"; zdialog_fetch(zd,"tif-16",ii); if (ii) { type = "tif"; bpc = 16; } zdialog_fetch(zd,"png-8",ii); if (ii) type = "png"; zdialog_fetch(zd,"png-16",ii); if (ii) { type = "png"; bpc = 16; } if (strmatch(type,"jpg")) { // set non-default jpeg save quality zdialog_fetch(zd,"jpgqual",ii); // will be used only one time jpeg_1x_quality = ii; } pext = strrchr(outfile,'/'); // locate file .ext if (pext) pext = strrchr(pext,'.'); if (pext) { // validate .ext OK for type if (strmatch(type,"jpg") && ! strcasestr(".jpg .jpeg",pext)) *pext = 0; if (strmatch(type,"tif") && ! strcasestr(".tif .tiff",pext)) *pext = 0; if (strmatch(type,"png") && ! strcasestr(".png",pext)) *pext = 0; } if (! pext || ! *pext) { pext = outfile + strlen(outfile); // wrong or missing, add new .ext *pext = '.'; // NOT replace .JPG with .jpg etc. strcpy(pext+1,type); } zdialog_fetch(zd,"mkcurr",mkcurr); // get make current option err = stat(outfile,&statbuf); // check if file exists if (! err) { yn = zmessageYN(Mwin,ZTX("Overwrite file? \n %s"),outfile); // confirm overwrite if (! yn) { zd->zstat = 0; goto zdialog_wait; } } zdialog_free(zd); // zdialog_free(zd); err = f_save(outfile,type,bpc); // save the file if (err) { zfree(outfile); return 1; } if (samedirk(outfile,navi::galleryname)) { // if saving into current gallery gallery(outfile,"init",0); // refresh gallery list gallery(0,"sort",-2); // recall sort and position 18.01 curr_file_posn = file_position(curr_file,curr_file_posn); // update curr. file position set_mwin_title(); // update window title (file count) } if (mkcurr) f_open_saved(); // open saved file with edit hist zfree(outfile); return 0; } // set dialog file type from user selection of file type radio button int f_save_as_dialog_event(zdialog *zd, cchar *event) { int ii; cchar *filetypes[5] = { "tif-8", "tif-16", "png-8", "png-16", "jpg" }; char ext[4]; char *filespec; char *filename, *pp; if (zd->zstat) return 1; // [done] or [cancel] if (strmatch(event,"jpgqual")) { // if jpg quality edited, set jpg .ext zdialog_stuff(zd,"jpg",1); event = "jpg"; } for (ii = 0; ii < 5; ii++) // detect file type selection if (strmatch(event,filetypes[ii])) break; if (ii == 5) return 1; zdialog_stuff(zd,"tif-8",0); // turn off unselected types first zdialog_stuff(zd,"tif-16",0); // (see GTK bug note above) zdialog_stuff(zd,"png-8",0); zdialog_stuff(zd,"png-16",0); zdialog_stuff(zd,"jpg",0); zdialog_stuff(zd,event,1); // lastly, turn on selected type strncpy0(ext,event,4); // "tif-16" >> "tif" etc. filespec = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(saveas_fchooser)); if (! filespec) return 1; filename = strrchr(filespec,'/'); // revise file .ext in chooser dialog if (! filename) return 1; filename = zstrdup(filename+1,6); pp = strrchr(filename,'.'); if (! pp || strlen(pp) > 5) pp = filename + strlen(filename); *pp = '.'; strcpy(pp+1,ext); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(saveas_fchooser),filename); zfree(filename); g_free(filespec); return 1; } /******************************************************************************** text file line edit function 17.08 open a text file: err = linedit_open(filespec) get next record: record = linedit_get() null = EOF put next record: err = linedit_put(record) close file: err = linedit_close() resulting file contains only the 'put' records and replaces input file trailing '\n' is removed from records read and added to records written returns: 0 = OK, +N = error *********************************************************************************/ namespace linedit { FILE *fidr = 0, *fidw = 0; char filespec[202], copyfile[208]; } int linedit_open(cchar *file) { using namespace linedit; if (strlen(file) > 200) { printz("linedit() filespec > 200: %s \n",file); return 1; } strcpy(filespec,file); snprintf(copyfile,206,"%s-copy",filespec); fidr = fopen(filespec,"r"); // open/read input file if (! fidr) printz("linedit() new file: %s \n",file); fidw = fopen(copyfile,"w"); // open/write copy file if (! fidw) { printz("linedit(): %s \n %s \n",file,strerror(errno)); if (fidr) fclose(fidr); fidr = 0; return 1; } return 0; } char * linedit_get() { using namespace linedit; char buff[1000], *prec; if (! fidr) return 0; // new file prec = fgets_trim(buff,1000,fidr); // read next record if (! prec) return 0; if (strlen(prec) > 998) { printz("linedit() record > 998: %s \n",filespec); return 0; } return prec; } int linedit_put(cchar *record) { using namespace linedit; int nn; if (! fidw) return 1; nn = fprintf(fidw,"%s\n",record); // write next record if (nn < 0) { printz("linedit(): %s \n %s \n",filespec,strerror(errno)); return 1; } return 0; } int linedit_close() { using namespace linedit; int err; if (fidr) fclose(fidr); // close input and copy files fidr = 0; if (! fidw) return 1; err = fclose(fidw); fidw = 0; if (err) { printz("linedit(): %s \n %s \n",filespec,strerror(errno)); return 1; } err = rename(copyfile,filespec); // replace input file with copy if (err) { printz("linedit(): %s \n %s \n",filespec,strerror(errno)); return 1; } return 0; } /********************************************************************************/ // help menu function void m_help(GtkWidget *, cchar *menu) { if (strmatch(menu,ZTX("Quick Start"))) showz_html(quickstart_file); if (strmatch(menu,ZTX("User Guide"))) showz_userguide(); if (strmatch(menu,ZTX("User Guide Changes"))) showz_userguide("recent_changes"); if (strmatch(menu,ZTX("README"))) showz_textfile("doc","README"); if (strmatch(menu,ZTX("Change Log"))) showz_textfile("doc","changelog"); if (strmatch(menu,ZTX("Log File"))) showz_logfile(); if (strmatch(menu,ZTX("Translations"))) showz_userguide("translations"); if (strmatch(menu,ZTX("Home Page"))) showz_html(Fhomepage); if (strmatch(menu,ZTX("About"))) // "fotoxx-nn.n" or "*-appimage" 17.04 { zmessageACK(Mwin," %s %s %s \n %s \n %s %s \n %s \n", Arelease,__DATE__,__TIME__,Flicense,Fhomepage,Fcontact,Ftranslators); } if (strmatch(menu,ZTX("Help"))) // menu button showz_userguide(F1_help_topic); // show topic if there, or page 1 return; } /******************************************************************************** Find all image files within given path. int find_imagefiles(cchar *directory, int flags, char **&flist, int &NF) dirk directory path to search flags sum of the following: 1 include image files (+RAW +video) 2 include thumbnails 4 include hidden files 8 include directories 16 recurse directories NF count of files returned flist list of files returned Returns 0 if OK, +N if error (errno is set). flist and flist[*] are subjects for zfree(). *********************************************************************************/ char **fif_filelist; // list of filespecs returned int fif_max; // filelist slots allocated int fif_count; // filelist slots filled int find_imagefiles(cchar *dirk, int flags, char **&flist, int &NF, int Finit) // overhauled 18.01 { int globflags, Fimages, Fthumbs, Fdirs, Frecurse; int err1, err2, cc; FTYPE ftype; char *file, *mdirk, **templist; glob_t globdata; STATB statdat; if (Finit) fif_max = fif_count = 0; // initial call (OMIT, default 1) globflags = GLOB_NOSORT; Fimages = Fthumbs = Fdirs = Frecurse = 0; if (flags & 1) Fimages = 1; if (flags & 2) Fthumbs = 1; if (flags & 4) globflags += GLOB_PERIOD; if (flags & 8) Fdirs = 1; if (flags & 16) Frecurse = 1; if (Fdirs && ! Fimages && ! Fthumbs) globflags += GLOB_ONLYDIR; globdata.gl_pathc = 0; // glob() setup globdata.gl_offs = 0; globdata.gl_pathc = 0; NF = 0; // empty output flist = 0; mdirk = zstrdup(dirk,4); // append /* to input directory strcat(mdirk,"/*"); err1 = glob(mdirk,globflags,null,&globdata); // find all files in directory if (err1) { if (err1 == GLOB_NOMATCH) err1 = 0; else if (err1 == GLOB_ABORTED) err1 = 1; else if (err1 == GLOB_NOSPACE) err1 = 2; else err1 = 3; goto fif_return; } for (uint ii = 0; ii < globdata.gl_pathc; ii++) // loop found files { file = globdata.gl_pathv[ii]; err1 = stat(file,&statdat); if (err1) continue; if (S_ISDIR(statdat.st_mode) && Frecurse) { // directory err1 = find_imagefiles(file,flags,flist,NF,0); // process member files if (err1) goto fif_return; } ftype = image_file_type(file); if (ftype == OTHER) continue; // unknown file type if (ftype == FDIR && ! Fdirs) continue; if (ftype == THUMB && ! Fthumbs) continue; if (ftype == IMAGE || ftype == RAW || ftype == VIDEO) if (! Fimages) continue; if (fif_count == fif_max) { // output list is full if (fif_max == 0) { fif_max = 1000; // initial space, 1000 files cc = fif_max * sizeof(char *); fif_filelist = (char **) zmalloc(cc); } else { templist = fif_filelist; // expand by 2x each time needed cc = fif_max * sizeof(char *); fif_filelist = (char **) zmalloc(cc+cc); memcpy(fif_filelist,templist,cc); memset(fif_filelist+fif_max,0,cc); zfree(templist); fif_max *= 2; } } fif_filelist[fif_count] = zstrdup(file); // add file to output list fif_count += 1; } err1 = 0; fif_return: err2 = errno; // preserve Linux errno globfree(&globdata); // free memory zfree(mdirk); if (Finit) { NF = fif_count; if (NF) flist = fif_filelist; } errno = err2; // return err1 and preserve errno zmainloop(); // keep GTK alive return err1; } /******************************************************************************** Image file load and save functions PXM pixmap or PXB pixbuf <--> file on disk *********************************************************************************/ // get the equivalent .tif file name for a given RAW file // returned file name is subject to zfree() char * raw_to_tiff(cchar *rawfile) { char *ptiff, *pext; ptiff = zstrdup(rawfile,8); pext = strrchr(ptiff,'.'); if (! pext) pext = ptiff + strlen(ptiff); strcpy(pext,".tif"); return ptiff; } /********************************************************************************/ // Load an image file into a PXB pixmap (pixbuf 8 bit color). // Also sets the following file scope variables: // f_load_type = "jpg" "tif" "png" "bmp" "ico" "other" // f_load_bpc = disk file bits per color = 1/8/16 // f_load_size = disk file size // If Fack is true, failure will cause a popup ACK dialog // Do not call from a thread with Fack true. PXB * PXB_load(cchar *file, int Fack) { int err; FTYPE ftype; char *framefile; cchar *pext; PXB *pxb; STATB statbuf; err = stat(file,&statbuf); if (err) { if (Fack) zmessageACK(Mwin,Bfilenotfound2,file); goto f_load_fail; } ftype = image_file_type(file); if (ftype == VIDEO) // video file - extract frame 1 17.08 { framefile = zstrdup(tempdir,20); // output .jpg file strcat(framefile,"/frame1.jpg"); err = shell_quiet("ffmpeg -i \"%s\" -v 8 -frames 1 -y %s",file,framefile); if (err && Fack) { zmessageACK(Mwin,strerror(err)); goto f_load_fail; } f_load_size = statbuf.st_size; // report video size pext = strrchr(file,'/'); if (! pext) pext = file; pext = strrchr(pext,'.'); if (pext) pext++; else pext = "video"; strncpy0(f_load_type,pext,8); pxb = ANY_PXB_load(framefile); if (pxb) return pxb; if (Fack) zmessageACK(Mwin,ZTX("file type not supported: %s"),file); goto f_load_fail; } if (ftype != IMAGE && ftype != RAW) { if (Fack) zmessageACK(Mwin,ZTX("file type not supported: %s"),file); goto f_load_fail; } f_load_size = statbuf.st_size; // set f_load_size pext = strrchr(file,'/'); if (! pext) pext = file; pext = strrchr(pext,'.'); if (! pext) pext = ""; if (ftype == IMAGE) { if (strcasestr(".jpg .jpeg",pext)) strcpy(f_load_type,"jpg"); // f_load_type = jpg/tif/png/... else if (strcasestr(".tif .tiff",pext)) strcpy(f_load_type,"tif"); else if (strcasestr(".png",pext)) strcpy(f_load_type,"png"); else if (strcasestr(".bmp",pext)) strcpy(f_load_type,"bmp"); else if (strcasestr(".ico",pext)) strcpy(f_load_type,"ico"); else strcpy(f_load_type,"other"); } if (ftype == RAW) strcpy(f_load_type,"RAW"); // f_load_type = RAW if (strmatch(f_load_type,"tif")) // use loader for file type pxb = TIFF_PXB_load(file); // f_load_bpc = file bpc else if (strmatch(f_load_type,"png")) pxb = PNG_PXB_load(file); else if (strmatch(f_load_type,"RAW")) // RAW file pxb = RAW_PXB_load(file); else pxb = ANY_PXB_load(file); if (pxb) return pxb; if (Fack && *file_errmess) // pass error to user zmessageACK(Mwin,file_errmess); f_load_fail: *f_load_type = f_load_size = f_load_bpc = 0; return 0; } /********************************************************************************/ // Load an image file into a PXM pixmap (float color). // Also sets the following file scope variables: // f_load_type = "jpg" "tif" "png" "bmp" "ico" "other" // f_load_bpc = disk file bits per color = 1/8/16 // f_load_size = disk file size // If Fack is true, failure will cause a popup ACK dialog // Do not call from a thread with Fack true. PXM * PXM_load(cchar *file, int Fack) { int err; FTYPE ftype; cchar *pext; PXM *pxm; STATB statbuf; err = stat(file,&statbuf); if (err) { if (Fack) zmessageACK(Mwin,Bfilenotfound2,file); goto f_load_fail; } ftype = image_file_type(file); if (ftype != IMAGE && ftype != RAW) { if (Fack) zmessageACK(Mwin,ZTX("file type not supported: %s"),file); goto f_load_fail; } f_load_size = statbuf.st_size; // set f_load_size pext = strrchr(file,'/'); if (! pext) pext = file; pext = strrchr(pext,'.'); if (! pext) pext = ""; if (strcasestr(".jpg .jpeg",pext)) strcpy(f_load_type,"jpg"); // set f_load_type else if (strcasestr(".tif .tiff",pext)) strcpy(f_load_type,"tif"); else if (strcasestr(".png",pext)) strcpy(f_load_type,"png"); else if (strcasestr(".bmp",pext)) strcpy(f_load_type,"bmp"); else if (strcasestr(".ico",pext)) strcpy(f_load_type,"ico"); else strcpy(f_load_type,"other"); if (strmatch(f_load_type,"tif")) // use loader for file type pxm = TIFF_PXM_load(file); // f_load_bpc = file bpc else if (strmatch(f_load_type,"png")) pxm = PNG_PXM_load(file); else if (ftype == RAW) // RAW file pxm = RAW_PXM_load(file); else pxm = ANY_PXM_load(file); if (pxm) return pxm; if (Fack && *file_errmess) // pass error to user zmessageACK(Mwin,file_errmess); f_load_fail: *f_load_type = f_load_size = f_load_bpc = 0; return 0; } /******************************************************************************** TIFF file read and write functions *********************************************************************************/ // Intercept TIFF warning messages (many) void tiffwarninghandler(cchar *module, cchar *format, va_list ap) { return; // stop flood of crap char message[200]; vsnprintf(message,199,format,ap); printz("TIFF warning: %s %s \n",module,message); return; } // Load PXB from TIFF file using TIFF library. // Native TIFF file bits/pixel >> f_load_bpc PXB * TIFF_PXB_load(cchar *file) { static int ftf = 1; TIFF *tiff; PXB *pxb; char *tiffbuff; uint8 *tiff8; uint16 *tiff16; uint8 *pix; uint16 bpc, nc, ac, fmt; int ww, hh, rps, stb, nst; // int not uint int tiffstat, row, col, strip; int row1, row2, cc; if (ftf) { TIFFSetWarningHandler(tiffwarninghandler); // intercept TIFF warning messages ftf = 0; } *file_errmess = 0; tiff = TIFFOpen(file,"r"); if (! tiff) { snprintf(file_errmess,999,"TIFF file read error: %s",file); printz("%s\n",file_errmess); return 0; } TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &ww); // width TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &hh); // height TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bpc); // bits per color, 1/8/16 TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &nc); // no. channels (colors), 1/2/3/4 TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rps); // rows per strip TIFFGetField(tiff, TIFFTAG_SAMPLEFORMAT, &fmt); // data format stb = TIFFStripSize(tiff); // strip size nst = TIFFNumberOfStrips(tiff); // number of strips if (ww > wwhh_limit1 || hh > wwhh_limit1 || ww * hh > wwhh_limit2) { zmessageACK(Mwin,"image too big, %dx%d",ww,hh); TIFFClose(tiff); return 0; } if (bpc > 8 && bpc != 16) { // check for supported bits/color printz("TIFF bits=%d not supported: %s \n",bpc,file); TIFFClose(tiff); return ANY_PXB_load(file); // fallback to pixbuf loader } if (nc == 2 || nc == 4) ac = 1; else ac = 0; if (bpc <= 8) // use universal TIFF reader { // if bits/color <= 8 tiffbuff = (char *) zmalloc(ww*hh*4); tiffstat = TIFFReadRGBAImage(tiff, ww, hh, (uint *) tiffbuff, 0); TIFFClose(tiff); if (tiffstat != 1) { snprintf(file_errmess,999,"TIFF file read error: %s",file); printz("%s\n",file_errmess); zfree(tiffbuff); return 0; } pxb = PXB_make(ww,hh,ac); // create PXB tiff8 = (uint8 *) tiffbuff; for (row = 0; row < hh; row++) { pix = pxb->pixels + (hh-1 - row) * pxb->rs; // TIFF image is inverted for (col = 0; col < ww; col++) { memcpy(pix,tiff8,(3+ac)); // 0 - 255 >> 0 - 255 pix += 3 + ac; tiff8 += 4; } } zfree(tiffbuff); f_load_bpc = 8; return pxb; } pxb = PXB_make(ww,hh,ac); // TIFF file has 16 bits/color stb += 1000000; // reduce risk of crash tiffbuff = (char *) zmalloc(stb); // read encoded strips for (strip = 0; strip < nst; strip++) { cc = TIFFReadEncodedStrip(tiff,strip,tiffbuff,stb); if (cc < 0) { snprintf(file_errmess,999,"TIFF file read error: %s",file); printz("%s\n",file_errmess); TIFFClose(tiff); zfree(tiffbuff); PXB_free(pxb); return 0; } if (cc == 0) break; tiff16 = (uint16 *) tiffbuff; row1 = strip * rps; // rows in strip row2 = row1 + cc / (ww * nc * 2); if (row2 > hh) row2 = hh; // rps unreliable if nst = 1 for (row = row1; row < row2; row++) { pix = pxb->pixels + row * pxb->rs; for (col = 0; col < ww; col++) { pix[0] = tiff16[0] / 256; // 0 - 65535 >> 0 - 255 pix[1] = tiff16[1] / 256; pix[2] = tiff16[2] / 256; if (ac) pix[3] = tiff16[3] / 256; pix += 3 + ac; tiff16 += nc; } } } TIFFClose(tiff); zfree(tiffbuff); f_load_bpc = bpc; // file bpc 1/8/16 return pxb; } // Load PXM from TIFF file using TIFF library. // Native TIFF file bits/pixel >> f_load_bpc PXM * TIFF_PXM_load(cchar *file) { static int ftf = 1; TIFF *tiff; PXM *pxm; char *tiffbuff; uint8 *tiff8; uint16 *tiff16; float *pix; uint16 bpc, nc, ac, fmt; int ww, hh, rps, stb, nst; // int not uint int tiffstat, row, col, strip, cc; if (ftf) { TIFFSetWarningHandler(tiffwarninghandler); // intercept TIFF warning messages ftf = 0; } *file_errmess = 0; tiff = TIFFOpen(file,"r"); if (! tiff) { snprintf(file_errmess,999,"TIFF file read error: %s",file); printz("%s\n",file_errmess); return 0; } TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &ww); // width TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &hh); // height TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bpc); // bits per color, 1/8/16 TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &nc); // no. channels (colors), 1/2/3/4 TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rps); // rows per strip TIFFGetField(tiff, TIFFTAG_SAMPLEFORMAT, &fmt); // data format stb = TIFFStripSize(tiff); // strip size nst = TIFFNumberOfStrips(tiff); // number of strips if (bpc > 8 && bpc != 16) { // check for supported bits/color printz("TIFF bits=%d not supported: %s \n",bpc,file); TIFFClose(tiff); return ANY_PXM_load(file); // fallback to pixbuf loader } if (nc == 2 || nc == 4) ac = 1; // alpha channel else ac = 0; pxm = PXM_make(ww,hh,3+ac); // create PXM if (! pxm) { TIFFClose(tiff); return 0; } if (bpc <= 8) // use universal TIFF reader { tiffbuff = (char *) zmalloc(ww*hh*4); tiffstat = TIFFReadRGBAImage(tiff, ww, hh, (uint *) tiffbuff, 0); // with alpha channel TIFFClose(tiff); if (tiffstat != 1) { snprintf(file_errmess,999,"TIFF file read error: %s",file); printz("%s\n",file_errmess); zfree(tiffbuff); return 0; } tiff8 = (uint8 *) tiffbuff; for (row = 0; row < hh; row++) { pix = pxm->pixels + (hh-1 - row) * ww * (3+ac); // TIFF image is inverted for (col = 0; col < ww; col++) { pix[0] = tiff8[0]; // 0 - 255 >> 0.0 - 255.0 pix[1] = tiff8[1]; pix[2] = tiff8[2]; if (ac) pix[3] = tiff8[3]; // bugfix 17.01.2 pix += 3+ac; tiff8 += 4; } } zfree(tiffbuff); return pxm; } // TIFF file has 16 bits/color stb += 1000000; // reduce risk of crash tiffbuff = (char *) zmalloc(stb); // read encoded strips for (strip = 0; strip < nst; strip++) { cc = TIFFReadEncodedStrip(tiff,strip,tiffbuff,stb); if (cc < 0) { snprintf(file_errmess,999,"TIFF file read error: %s",file); printz("%s\n",file_errmess); TIFFClose(tiff); zfree(tiffbuff); PXM_free(pxm); return 0; } if (cc == 0) break; tiff16 = (uint16 *) tiffbuff; row = strip * rps; pix = pxm->pixels + row * ww * (3+ac); while (cc >= 6) { pix[0] = tiff16[0] / 256.0; // 0 - 65535 >> 0.0 - 255.996 pix[1] = tiff16[1] / 256.0; pix[2] = tiff16[2] / 256.0; if (ac) pix[3] = tiff16[3] / 256.0; pix += 3+ac; tiff16 += nc; cc -= nc * 2; } } TIFFClose(tiff); zfree(tiffbuff); f_load_bpc = bpc; // file bpc 1/8/16 return pxm; } // Write PXM to TIFF file using TIFF library. // TIFF file bpc is 8 or 16. // Returns 0 if OK, +N if error. int PXM_TIFF_save(PXM *pxm, cchar *file, int bpc) { static int ftf = 1; TIFF *tiff; uint8 *tiff8; uint16 *tiff16; float *pix; char *tiffbuff; int tiffstat = 0; int ww, hh, row, col, rowcc; // int not uint int nc, ac, pm = 2, pc = 1; int comp = COMPRESSION_NONE; // LZW ineffective if (ftf) { TIFFSetWarningHandler(tiffwarninghandler); // intercept TIFF warning messages ftf = 0; } *file_errmess = 0; tiff = TIFFOpen(file,"w"); if (! tiff) { snprintf(file_errmess,999,"TIFF file write error: %s",file); printz("%s\n",file_errmess); return 0; } ww = pxm->ww; hh = pxm->hh; nc = pxm->nc; ac = nc - 3; TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, ww); TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, bpc); TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, nc); TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, pm); // RGB TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, pc); TIFFSetField(tiff, TIFFTAG_COMPRESSION, comp); rowcc = TIFFScanlineSize(tiff); tiffbuff = (char*) zmalloc(rowcc); for (row = 0; row < hh; row++) { if (bpc == 8) // write 8-bit TIFF { tiff8 = (uint8 *) tiffbuff; pix = pxm->pixels + row * ww * nc; for (col = 0; col < ww; col++) { tiff8[0] = pix[0]; // 0.0 - 255.996 >> 0 - 255 tiff8[1] = pix[1]; tiff8[2] = pix[2]; if (ac) tiff8[3] = pix[3]; pix += nc; tiff8 += nc; } } if (bpc == 16) // write 16-bit TIFF { tiff16 = (uint16 *) tiffbuff; pix = pxm->pixels + row * ww * nc; for (col = 0; col < ww; col++) { tiff16[0] = 256.0 * pix[0]; // 0.0 - 255.996 >> 0.0 >> 65535 tiff16[1] = 256.0 * pix[1]; tiff16[2] = 256.0 * pix[2]; if (ac) tiff16[3] = 256.0 * pix[3]; pix += nc; tiff16 += nc; } } tiffstat = TIFFWriteScanline(tiff,tiffbuff,row,0); if (tiffstat != 1) break; } TIFFClose(tiff); zfree(tiffbuff); if (tiffstat == 1) return 0; snprintf(file_errmess,999,"TIFF file write error: %s",file); printz("%s\n",file_errmess); return 2; } /******************************************************************************** PNG file read and write functions *********************************************************************************/ // Load PXB from PNG file using PNG library. // Native PNG file bits/pixel >> f_load_bpc PXB * PNG_PXB_load(cchar *file) { png_structp pngstruct = 0; png_infop pnginfo = 0; FILE *fid; int ww, hh, bpc, nc, ac, colortype; uchar **pngrows; uint8 *png8; uint16 *png16; PXB *pxb; uint8 *pix; int row, col; *file_errmess = 0; fid = fopen(file,"r"); // open file if (! fid) goto erret; pngstruct = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); // create png structs if (! pngstruct) goto erret; pnginfo = png_create_info_struct(pngstruct); if (! pnginfo) goto erret; if (setjmp(png_jmpbuf(pngstruct))) goto erret; // setup error handling png_init_io(pngstruct,fid); // read png file png_read_png(pngstruct,pnginfo,PNG_TRANSFORM_SWAP_ENDIAN,0); fclose(fid); ww = png_get_image_width(pngstruct,pnginfo); // width hh = png_get_image_height(pngstruct,pnginfo); // height bpc = png_get_bit_depth(pngstruct,pnginfo); // bits per color (channel) nc = png_get_channels(pngstruct,pnginfo); // no. channels colortype = png_get_color_type(pngstruct,pnginfo); // color type pngrows = png_get_rows(pngstruct,pnginfo); // array of png row pointers if (colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_RGB) ac = 0; else if (colortype == PNG_COLOR_TYPE_GRAY_ALPHA || colortype == PNG_COLOR_TYPE_RGB_ALPHA) ac = 1; else { png_destroy_read_struct(&pngstruct,&pnginfo,0); return ANY_PXB_load(file); // fallback to pixbuf loader } if (ww > wwhh_limit1 || hh > wwhh_limit1 || ww * hh > wwhh_limit2) { zmessageACK(Mwin,"image too big, %dx%d",ww,hh); png_destroy_read_struct(&pngstruct,&pnginfo,0); return 0; } if (bpc != 8 && bpc != 16) { // not 8 or 16 bits per channel png_destroy_read_struct(&pngstruct,&pnginfo,0); return ANY_PXB_load(file); // use standard pixbuf loader } if (nc > 4) { // not gray (+alpha) or RGB (+alpha) png_destroy_read_struct(&pngstruct,&pnginfo,0); return ANY_PXB_load(file); // use standard pixbuf loader } pxb = PXB_make(ww,hh,ac); // create PXB if (bpc == 8) { for (row = 0; row < hh; row++) { png8 = (uint8 *) pngrows[row]; pix = pxb->pixels + row * pxb->rs; for (col = 0; col < ww; col++) { if (nc < 3) { // gray or gray + alpha pix[0] = pix[1] = pix[2] = png8[0]; if (ac) pix[3] = png8[1]; // alpha channel } else memcpy(pix,png8,3+ac); // RGB or RGB + alpha png8 += nc; pix += 3+ac; } } } if (bpc == 16) { for (row = 0; row < hh; row++) { png16 = (uint16 *) pngrows[row]; pix = pxb->pixels + row * pxb->rs; for (col = 0; col < ww; col++) { if (nc < 3) { // gray or gray + alpha pix[0] = pix[1] = pix[2] = png16[0] >> 8; if (ac) pix[3] = png16[1] >> 8; // alpha channel } else { // RGB or RGB + alpha pix[0] = png16[0] >> 8; // 16 bits to 8 bits pix[1] = png16[1] >> 8; pix[2] = png16[2] >> 8; if (ac) pix[3] = png16[3] >> 8; // alpha channel } png16 += nc; pix += 3+ac; } } } png_destroy_read_struct(&pngstruct,&pnginfo,0); // release memory f_load_bpc = bpc; // file bpc 16 return pxb; erret: if (fid) fclose(fid); snprintf(file_errmess,999,"PNG file read error: %s",file); printz("%s\n",file_errmess); return 0; } // Load PXM from PNG file using PNG library. // Native PNG file bits/pixel >> f_load_bpc PXM * PNG_PXM_load(cchar *file) { png_structp pngstruct = 0; png_infop pnginfo = 0; FILE *fid; int ww, hh, bpc, nc, ac, colortype; uchar **pngrows; uint8 *png8; uint16 *png16; PXM *pxm; float *pix, f256 = 1.0 / 256.0; int row, col; *file_errmess = 0; fid = fopen(file,"r"); // open file if (! fid) goto erret; pngstruct = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); // create png structs if (! pngstruct) goto erret; pnginfo = png_create_info_struct(pngstruct); if (! pnginfo) goto erret; if (setjmp(png_jmpbuf(pngstruct))) goto erret; // setup error handling png_init_io(pngstruct,fid); // read png file png_read_png(pngstruct,pnginfo,PNG_TRANSFORM_SWAP_ENDIAN,0); fclose(fid); ww = png_get_image_width(pngstruct,pnginfo); // width hh = png_get_image_height(pngstruct,pnginfo); // height bpc = png_get_bit_depth(pngstruct,pnginfo); // bits per color (channel) nc = png_get_channels(pngstruct,pnginfo); // no. channels colortype = png_get_color_type(pngstruct,pnginfo); // color type pngrows = png_get_rows(pngstruct,pnginfo); // array of png row pointers if (colortype != PNG_COLOR_TYPE_GRAY && colortype != PNG_COLOR_TYPE_RGB && colortype != PNG_COLOR_TYPE_GRAY_ALPHA && colortype != PNG_COLOR_TYPE_RGB_ALPHA) { png_destroy_read_struct(&pngstruct,&pnginfo,0); return ANY_PXM_load(file); // fallback to pixbuf loader } if (bpc != 8 && bpc != 16) { png_destroy_read_struct(&pngstruct,&pnginfo,0); return ANY_PXM_load(file); // use standard pixbuf loader } if (nc > 4) { // not gray (+alpha) or RGB (+alpha) png_destroy_read_struct(&pngstruct,&pnginfo,0); return ANY_PXM_load(file); // use standard pixbuf loader } if (nc == 2 || nc == 4) ac = 1; // gray + alpha or RGB + alpha else ac = 0; pxm = PXM_make(ww,hh,3+ac); // create PXM if (! pxm) { png_destroy_read_struct(&pngstruct,&pnginfo,0); return 0; } if (bpc == 8) { for (row = 0; row < hh; row++) { png8 = (uint8 *) pngrows[row]; pix = pxm->pixels + row * ww * (3 + ac); for (col = 0; col < ww; col++) { if (nc < 3) { // gray or gray + alpha pix[0] = pix[1] = pix[2] = png8[0]; if (ac) pix[3] = png8[1]; } else { // RGB or RGB + alpha pix[0] = png8[0]; // 0-255 >> 0.0 - 255.0 pix[1] = png8[1]; pix[2] = png8[2]; if (ac) pix[3] = png8[3]; } png8 += nc; pix += 3 + ac; } } } if (bpc == 16) { for (row = 0; row < hh; row++) { png16 = (uint16 *) pngrows[row]; pix = pxm->pixels + row * ww * (3 + ac); for (col = 0; col < ww; col++) { if (nc < 3) { // gray or gray + alpha pix[0] = pix[1] = pix[2] = f256 * png16[0]; if (ac) pix[3] = f256 * png16[1]; } else { // RGB or RGB + alpha pix[0] = f256 * png16[0]; // 0-65535 >> 0-255.996 pix[1] = f256 * png16[1]; pix[2] = f256 * png16[2]; if (ac) pix[3] = f256 * png16[3]; } png16 += nc; pix += 3 + ac; } } } png_destroy_read_struct(&pngstruct,&pnginfo,0); // release memory f_load_bpc = bpc; // file bpc 1/8/16 return pxm; erret: if (fid) fclose(fid); snprintf(file_errmess,999,"PNG file read error: %s",file); printz("%s\n",file_errmess); return 0; } // Write PXM to PNG file using PNG library. // File bpc is 8 or 16. // returns 0 if OK, +N if error. int PXM_PNG_save(PXM *pxm, cchar *file, int bpc) { png_structp pngstruct; png_infop pnginfo; FILE *fid; uchar **pngrows; uint8 *png8; uint16 *png16; float *pix; int ww, hh, nc, ac, cc, row, col; *file_errmess = 0; if (bpc != 8 && bpc != 16) { snprintf(file_errmess,999,"PNG file BPC not 8/16: %s",file); printz("%s\n",file_errmess); return 2; } ww = pxm->ww; hh = pxm->hh; nc = pxm->nc; ac = nc - 3; fid = fopen(file,"w"); // open output file if (! fid) goto erret; pngstruct = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0); // create png structs if (! pngstruct) goto erret; pnginfo = png_create_info_struct(pngstruct); if (! pnginfo) goto erret; if (setjmp(png_jmpbuf(pngstruct))) goto erret; // setup error handling png_init_io(pngstruct,fid); // initz. for writing file png_set_compression_level(pngstruct,1); if (ac) png_set_IHDR(pngstruct,pnginfo,ww,hh,bpc,PNG_COLOR_TYPE_RGB_ALPHA,0,0,0); else png_set_IHDR(pngstruct,pnginfo,ww,hh,bpc,PNG_COLOR_TYPE_RGB,0,0,0); pngrows = (uchar **) zmalloc(hh * sizeof(char *)); // allocate rows of RGB data if (bpc == 8) cc = ww * nc; else cc = ww * nc * 2; for (row = 0; row < hh; row++) pngrows[row] = (uchar *) zmalloc(cc); png_set_rows(pngstruct,pnginfo,pngrows); // array of png row pointers if (bpc == 8) // 8 bit RGB { for (row = 0; row < hh; row++) { png8 = (uint8 *) pngrows[row]; pix = pxm->pixels + row * ww * nc; for (col = 0; col < ww; col++) { png8[0] = pix[0]; // 0.0 - 255.996 >> 0-255 png8[1] = pix[1]; png8[2] = pix[2]; if (ac) png8[3] = pix[3]; png8 += nc; pix += nc; } } } else if (bpc == 16) // 16 bit RGB { for (row = 0; row < hh; row++) { png16 = (uint16 *) pngrows[row]; pix = pxm->pixels + row * ww * nc; for (col = 0; col < ww; col++) { png16[0] = 256.0 * pix[0]; // 0.0 - 255.996 >> 0 - 65535 png16[1] = 256.0 * pix[1]; png16[2] = 256.0 * pix[2]; if (ac) png16[3] = 256.0 * pix[3]; png16 += nc; pix += nc; } } } png_write_png(pngstruct,pnginfo,PNG_TRANSFORM_SWAP_ENDIAN,0); // write the file fclose(fid); png_destroy_write_struct(&pngstruct,&pnginfo); // release memory for (row = 0; row < hh; row++) zfree(pngrows[row]); zfree(pngrows); return 0; erret: if (fid) fclose(fid); snprintf(file_errmess,999,"PNG file write error: %s",file); printz("%s\n",file_errmess); return 2; } /******************************************************************************** JPEG and other file types read and write functions *********************************************************************************/ // Load PXB from JPEG and other file types using the pixbuf library. // bpc = 8. PXB * ANY_PXB_load(cchar *file) { GError *gerror = 0; PXB *pxb; PIXBUF *pixbuf; int ww, hh, px, py, nc, ac, rs; uint8 *pixels1, *pix1; uint8 *pixels2, *pix2; *file_errmess = 0; pixbuf = gdk_pixbuf_new_from_file(file,&gerror); if (! pixbuf) { if (gerror) printz("%s\n",gerror->message); snprintf(file_errmess,999,"(pixbuf) file read error: %s",file); printz("%s\n",file_errmess); return 0; } ww = gdk_pixbuf_get_width(pixbuf); hh = gdk_pixbuf_get_height(pixbuf); nc = gdk_pixbuf_get_n_channels(pixbuf); ac = gdk_pixbuf_get_has_alpha(pixbuf); rs = gdk_pixbuf_get_rowstride(pixbuf); pixels1 = gdk_pixbuf_get_pixels(pixbuf); if (ww > wwhh_limit1 || hh > wwhh_limit1 || ww * hh > wwhh_limit2) { zmessageACK(Mwin,"image too big, %dx%d",ww,hh); g_object_unref(pixbuf); return 0; } pxb = PXB_make(ww,hh,ac); pixels2 = pxb->pixels; for (py = 0; py < hh; py++) { pix1 = pixels1 + rs * py; pix2 = pixels2 + py * pxb->rs; if (nc >= 3) { for (px = 0; px < ww; px++) { memcpy(pix2,pix1,3); if (ac) pix2[3] = pix1[3]; pix1 += nc; pix2 += 3 + ac; } } else { for (px = 0; px < ww; px++) { pix2[0] = pix2[1] = pix2[2] = pix1[0]; if (ac) pix2[3] = pix1[1]; pix1 += nc; pix2 += 3 + ac; } } } f_load_bpc = 8; // file bits per color g_object_unref(pixbuf); return pxb; } // Load PXM from JPEG and other file types using the pixbuf library. // bpc = 8. PXM * ANY_PXM_load(cchar *file) { GError *gerror = 0; PXM *pxm; PIXBUF *pixbuf; int ww, hh, px, py, nc, ac, rowst; uint8 *pixels1, *pix1; float *pixels2, *pix2; static int ftf = 1; static float ftab[256]; if (ftf) { // table lookup for speed ftf = 0; for (int ii = 0; ii < 256; ii++) ftab[ii] = ii; } *file_errmess = 0; pixbuf = gdk_pixbuf_new_from_file(file,&gerror); if (! pixbuf) { if (gerror) printz("%s\n",gerror->message); snprintf(file_errmess,999,"(pixbuf) file read error: %s",file); printz("%s\n",file_errmess); return 0; } ww = gdk_pixbuf_get_width(pixbuf); hh = gdk_pixbuf_get_height(pixbuf); nc = gdk_pixbuf_get_n_channels(pixbuf); ac = gdk_pixbuf_get_has_alpha(pixbuf); rowst = gdk_pixbuf_get_rowstride(pixbuf); pixels1 = gdk_pixbuf_get_pixels(pixbuf); pxm = PXM_make(ww,hh,3+ac); if (! pxm) { g_object_unref(pixbuf); return 0; } pixels2 = pxm->pixels; for (py = 0; py < hh; py++) { pix1 = pixels1 + rowst * py; pix2 = pixels2 + py * ww * (3+ac); if (nc >= 3) { for (px = 0; px < ww; px++) { pix2[0] = ftab[pix1[0]]; // 0-255 >> 0.0-255.0 pix2[1] = ftab[pix1[1]]; pix2[2] = ftab[pix1[2]]; if (ac) pix2[3] = ftab[pix1[3]]; pix1 += nc; pix2 += 3+ac; } } else { for (px = 0; px < ww; px++) { pix2[0] = pix2[1] = pix2[2] = ftab[pix1[0]]; if (ac) pix2[3] = ftab[pix1[1]]; pix1 += nc; pix2 += 3+ac; } } } f_load_bpc = 8; // file bits per color g_object_unref(pixbuf); return pxm; } // Write PXM to JPEG and other file types using the pixbuf library. // bpc = 8. // returns 0 if OK, +N if error. int PXM_ANY_save(PXM *pxm, cchar *file) { int ww, hh, nc, ac, px, py, rowst; float *pixels1, *pix1; char txqual[8]; uint8 *pixels2, *pix2; PIXBUF *pixbuf; cchar *pext; GError *gerror = 0; int pxbstat; *file_errmess = 0; ww = pxm->ww; hh = pxm->hh; nc = pxm->nc; ac = nc - 3; pixbuf = gdk_pixbuf_new(GDKRGB,ac,8,ww,hh); if (! pixbuf) zappcrash("pixbuf allocation failure"); pixels1 = pxm->pixels; pixels2 = gdk_pixbuf_get_pixels(pixbuf); rowst = gdk_pixbuf_get_rowstride(pixbuf); for (py = 0; py < hh; py++) { pix1 = pixels1 + py * ww * nc; pix2 = pixels2 + rowst * py; for (px = 0; px < ww; px++) { pix2[0] = pix1[0]; // 0.0 - 255.996 >> 0 - 255 pix2[1] = pix1[1]; pix2[2] = pix1[2]; if (ac) pix2[3] = pix1[3]; pix1 += nc; pix2 += nc; } } pext = strrchr(file,'/'); if (! pext) pext = file; pext = strrchr(pext,'.'); if (! pext) pext = ""; if (strstr(".png .PNG",pext)) // compression level pxbstat = gdk_pixbuf_save(pixbuf,file,"png",&gerror,"compression","1",null); else { snprintf(txqual,8,"%d",jpeg_1x_quality); // in case user deviates from default pxbstat = gdk_pixbuf_save(pixbuf,file,"jpeg",&gerror,"quality",txqual,null); jpeg_1x_quality = jpeg_def_quality; // reset to default after use } g_object_unref(pixbuf); if (pxbstat) return 0; if (gerror) printz("%s\n",gerror->message); snprintf(file_errmess,999,"(pixbuf) file write error: %s",file); printz("%s\n",file_errmess); return 2; } /******************************************************************************** RAW file read functions (there are no write functions) *********************************************************************************/ // RAW file to PXB pixmap (pixbuf, 8-bit color) PXB * RAW_PXB_load(cchar *rawfile) // use libraw { libraw_data_t *librawdata = 0; libraw_processed_image_t *RGBimage = 0; PXB *pxb = 0; int err, ww, hh, nc, rs, bpc, row, col; uint8 *rgbpixels, *pix1; uint16 *rgbpixelsx, *pix1x; uint8 *pxbpixels, *pix2; *file_errmess = 0; librawdata = libraw_init(0); err = libraw_open_file(librawdata,rawfile); if (err) goto libraw_err1; err = libraw_unpack(librawdata); if (err) goto libraw_err1; librawdata->params.use_camera_wb = 1; // camera white balance librawdata->params.output_color = 1; // sRGB librawdata->params.output_bps = 8; librawdata->params.output_tiff = 0; librawdata->params.user_qual = 0; // bilinear interpolation err = libraw_dcraw_process(librawdata); if (err) goto libraw_err1; RGBimage = libraw_dcraw_make_mem_image(librawdata,&err); if (! RGBimage) goto libraw_err2; if (RGBimage->type != LIBRAW_IMAGE_BITMAP) goto libraw_err2; ww = RGBimage->width; hh = RGBimage->height; nc = RGBimage->colors; // 1 or 3 color channels bpc = RGBimage->bits; // 8 or 16 bits/color rgbpixels = RGBimage->data; rgbpixelsx = (uint16 *) rgbpixels; if (nc != 1 && nc != 3) goto libraw_err3; if (bpc != 8 && bpc != 16) goto libraw_err3; pxb = PXB_make(ww,hh,0); rs = pxb->rs; pxbpixels = pxb->pixels; if (nc == 3 && bpc == 8) { for (row = 0; row < hh; row++) for (col = 0; col < ww; col++) { pix1 = rgbpixels + row * ww * 3 + col * 3; pix2 = pxbpixels + row * rs + col * 3; pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; } } else if (nc == 1 && bpc == 8) { for (row = 0; row < hh; row++) for (col = 0; col < ww; col++) { pix1 = rgbpixels + row * ww + col; pix2 = pxbpixels + row * rs + col * 3; memset(pix2,pix1[0],3); } } else if (nc == 3 && bpc == 16) { for (row = 0; row < hh; row++) for (col = 0; col < ww; col++) { pix1x = rgbpixelsx + row * ww * 3 + col * 3; pix2 = pxbpixels + row * rs + col * 3; pix2[0] = pix1x[0] >> 8; pix2[1] = pix1x[1] >> 8; pix2[2] = pix1x[2] >> 8; } } else if (nc == 1 && bpc == 16) { for (row = 0; row < hh; row++) for (col = 0; col < ww; col++) { pix1x = rgbpixelsx + row * ww + col; pix2 = pxbpixels + row * rs + col * 3; pix2[0] = pix2[1] = pix2[2] = pix1x[0] >> 8; } } libraw_dcraw_clear_mem(RGBimage); libraw_close(librawdata); f_load_bpc = bpc; return pxb; libraw_err1: snprintf(file_errmess,999,"libraw error %d, file: %s \n",err,rawfile); // diagnostic to user 17.08.3 goto cleanup; libraw_err2: snprintf(file_errmess,999,"libraw failure, file: %s \n",rawfile); goto cleanup; libraw_err3: snprintf(file_errmess,999,"image not 8|16 bits and 1|3 colors, file: %s \n",rawfile); goto cleanup; cleanup: if (pxb) PXB_free(pxb); if (RGBimage) libraw_dcraw_clear_mem(RGBimage); if (librawdata) libraw_close(librawdata); if (*file_errmess) printz("%s\n",file_errmess); return 0; } // RAW file to PXM pixmap (float color) PXM * RAW_PXM_load(cchar *rawfile) // use libraw { libraw_data_t *librawdata = 0; libraw_processed_image_t *RGBimage = 0; PXM *pxm = 0; int err, ww, hh, nc, bpc, row, col; uint8 *rgbpixels, *pix1; uint16 *rgbpixelsx, *pix1x; float *pxmpixels, *pix2; *file_errmess = 0; librawdata = libraw_init(0); err = libraw_open_file(librawdata,rawfile); if (err) goto libraw_err1; err = libraw_unpack(librawdata); if (err) goto libraw_err1; librawdata->params.use_camera_wb = 1; // camera white balance librawdata->params.output_color = 1; // sRGB librawdata->params.output_bps = 16; librawdata->params.output_tiff = 0; librawdata->params.user_qual = 0; // bilinear interpolation err = libraw_dcraw_process(librawdata); if (err) goto libraw_err1; RGBimage = libraw_dcraw_make_mem_image(librawdata,&err); if (! RGBimage) goto libraw_err2; if (RGBimage->type != LIBRAW_IMAGE_BITMAP) goto libraw_err2; ww = RGBimage->width; hh = RGBimage->height; nc = RGBimage->colors; // 1 or 3 color channels bpc = RGBimage->bits; // 8 or 16 bits/color rgbpixels = RGBimage->data; rgbpixelsx = (uint16 *) rgbpixels; if (nc != 1 && nc != 3) goto libraw_err3; if (bpc != 8 && bpc != 16) goto libraw_err3; pxm = PXM_make(ww,hh,3); pxmpixels = pxm->pixels; if (nc == 3 && bpc == 8) { for (row = 0; row < hh; row++) for (col = 0; col < ww; col++) { pix1 = rgbpixels + row * ww * 3 + col * 3; pix2 = pxmpixels + row * ww * 3 + col * 3; pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; } } else if (nc == 1 && bpc == 8) { for (row = 0; row < hh; row++) for (col = 0; col < ww; col++) { pix1 = rgbpixels + row * ww + col; pix2 = pxmpixels + row * ww * 3 + col * 3; pix2[0] = pix2[1] = pix2[2] = pix1[0]; } } else if (nc == 3 && bpc == 16) { for (row = 0; row < hh; row++) for (col = 0; col < ww; col++) { pix1x = rgbpixelsx + row * ww * 3 + col * 3; pix2 = pxmpixels + row * ww * 3 + col * 3; pix2[0] = 0.0039 * pix1x[0]; // 0 - 65535 >> 0.0 - 255.6 pix2[1] = 0.0039 * pix1x[1]; pix2[2] = 0.0039 * pix1x[2]; } } else if (nc == 1 && bpc == 16) { for (row = 0; row < hh; row++) for (col = 0; col < ww; col++) { pix1x = rgbpixelsx + row * ww + col; pix2 = pxmpixels + row * ww * 3 + col * 3; pix2[0] = pix2[1] = pix2[2] = 0.0039 * pix1x[0]; } } libraw_dcraw_clear_mem(RGBimage); libraw_close(librawdata); f_load_bpc = bpc; return pxm; libraw_err1: snprintf(file_errmess,999,"libraw error %d, file: %s \n",err,rawfile); // diagnostic to user 17.08.3 goto cleanup; libraw_err2: snprintf(file_errmess,999,"libraw failure, file: %s \n",rawfile); goto cleanup; libraw_err3: snprintf(file_errmess,999,"image not 8|16 bits and 1|3 colors, file: %s \n",rawfile); goto cleanup; cleanup: if (pxm) PXM_free(pxm); if (RGBimage) libraw_dcraw_clear_mem(RGBimage); if (librawdata) libraw_close(librawdata); if (*file_errmess) printz("%s\n",file_errmess); return 0; } /********************************************************************************/ // Get a virtual pixel at location (px,py) (real) in a PXB pixmap. // Get the overlapping integer pixels and build a composite. // Output vpix is uint8[4] supplied by caller. // Returns 1 if OK, 0 if px/py out of limits for pxm. int vpixel(PXB *pxb, float px, float py, uint8 *vpix) { int ww, hh, rs, nc, ac, px0, py0; uint8 *pix0, *pix1, *pix2, *pix3; float f0, f1, f2, f3; ww = pxb->ww; hh = pxb->hh; rs = pxb->rs; nc = pxb->nc; ac = nc - 3; px0 = px; // integer pixel containing (px,py) py0 = py; if (px0 < 0 || py0 < 0) return 0; if (px0 > ww-2 || py0 > hh-2) return 0; f0 = (px0+1 - px) * (py0+1 - py); // overlap of (px,py) f1 = (px0+1 - px) * (py - py0); // in each of the 4 pixels f2 = (px - px0) * (py0+1 - py); f3 = (px - px0) * (py - py0); pix0 = PXBpix(pxb,px0,py0); // pixel (px0,py0) pix1 = pix0 + rs; // (px0,py0+1) pix2 = pix0 + nc; // (px0+1,py0) pix3 = pix1 + nc; // (px0+1,py0+1) vpix[0] = f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0]; // sum the weighted inputs vpix[1] = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1]; vpix[2] = f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2]; if (ac) vpix[3] = f0 * pix0[3] + f1 * pix1[3] + f2 * pix2[3] + f3 * pix3[3]; return 1; } // Get a virtual pixel at location (px,py) (real) in a PXM pixmap. // Get the overlapping float pixels and build a composite. // Output vpix is float[4] supplied by caller. // Returns 1 if OK, 0 if px/py out of limits for pxm. int vpixel(PXM *pxm, float px, float py, float *vpix) { int ww, hh, nc, ac, px0, py0; float *pix0, *pix1, *pix2, *pix3; float f0, f1, f2, f3; ww = pxm->ww; hh = pxm->hh; nc = pxm->nc; ac = nc - 3; px0 = px; // integer pixel containing (px,py) py0 = py; if (px0 < 1 || py0 < 1) return 0; // off-image or edge pixel if (px0 > ww-2 || py0 > hh-2) return 0; f0 = (px0+1 - px) * (py0+1 - py); // overlap of (px,py) f1 = (px0+1 - px) * (py - py0); // in each of the 4 pixels f2 = (px - px0) * (py0+1 - py); f3 = (px - px0) * (py - py0); pix0 = PXMpix(pxm,px0,py0); // pixel (px0,py0) pix1 = pix0 + ww * nc; // (px0,py0+1) pix2 = pix0 + nc; // (px0+1,py0) pix3 = pix1 + nc; // (px0+1,py0+1) vpix[0] = f0 * pix0[0] + f1 * pix1[0] + f2 * pix2[0] + f3 * pix3[0]; // sum the weighted inputs vpix[1] = f0 * pix0[1] + f1 * pix1[1] + f2 * pix2[1] + f3 * pix3[1]; vpix[2] = f0 * pix0[2] + f1 * pix1[2] + f2 * pix2[2] + f3 * pix3[2]; if (ac) // alpha channel vpix[3] = f0 * pix0[3] + f1 * pix1[3] + f2 * pix2[3] + f3 * pix3[3]; // remove test of vpix[2] < 1 return 1; } /******************************************************************************** PXM pixmap functions - RGB float pixel map pixel RGB values may range from 0.0 to 255.99 *********************************************************************************/ // audit the contents of a PXM pixmap void PXM_audit(PXM *pxm) { int px, py; float bright, *pix; for (py = 0; py < pxm->hh; py++) for (px = 0; px < pxm->ww; px++) { pix = PXMpix(pxm,px,py); bright = pixbright(pix); if (int(bright) < 0 || int(bright) > 255) zappcrash("PXM_audit: px/py %d/%d RGB %.2f %.2f %.2f bright %.2f %d \n", px,py,pix[0],pix[1],pix[2],bright,int(bright)); } return; } // initialize PXM pixmap - allocate memory PXM * PXM_make(int ww, int hh, int nc) { int64 ww6 = ww, hh6 = hh; // 18.01 int64 npix = ww6 * hh6; int64 cc = npix * nc * sizeof(float); if (ww < 10 || hh < 10) { // impose reasonableness limits zmessageACK(Mwin,"image too small: %d x %d",ww,hh); return 0; } if (ww > wwhh_limit1 || hh > wwhh_limit1 || npix > wwhh_limit2) { // avoid image dimensions too big zmessageACK(Mwin,"image too big: %dx%d",ww,hh); return 0; } if (cc > 4095.0 * MEGA) { // 18.01 zmessageACK(Mwin,"image too big: %dx%dx%d",ww,hh,nc); return 0; } PXM *pxm = (PXM *) zmalloc(sizeof(PXM)); strcpy(pxm->wmi,"pxmpix"); pxm->ww = ww; pxm->hh = hh; pxm->nc = nc; // nc = 3/4 for no/alpha channel pxm->pixels = (float *) zmalloc(cc); // crashes if too big return pxm; } // free PXM pixmap void PXM_free(PXM *&pxm) { if (! pxm) return; if (! strmatch(pxm->wmi,"pxmpix")) zappcrash("PXM_free(), bad PXM"); strcpy(pxm->wmi,"xxxxxx"); zfree(pxm->pixels); zfree(pxm); pxm = 0; return; } // add an alpha channel to a PXM pixmap void PXM_addalpha(PXM *pxm) { uint cc; double dcc; int ww, hh, nc1, nc2, ii; float *pixels1, *pixels2, *pix1, *pix2; ww = pxm->ww; hh = pxm->hh; nc1 = pxm->nc; if (nc1 > 3) return; pixels1 = pxm->pixels; nc2 = nc1 + 1; dcc = 1.0 * ww * hh * nc2 * sizeof(float); // float[nc] per pixel if (dcc > 4095.0 * MEGA) { // avoid image > 4 GB zmessageACK(Mwin,"image too big: %dx%d",ww,hh); exit(12); } cc = ww * hh * nc2 * sizeof(float); pixels2 = (float *) zmalloc(cc); cc = nc1 * sizeof(float); pix1 = pixels1; pix2 = pixels2; for (ii = 0; ii < ww * hh; ii++) { memcpy(pix2,pix1,cc); pix2[nc1] = 255.0; // 100% opaque = 255 pix1 += nc1; pix2 += nc2; } zfree(pixels1); pxm->nc = nc2; pxm->pixels = pixels2; return; } // create a copy of a PXM pixmap PXM * PXM_copy(PXM *pxm1) { uint cc; int ww, hh, nc; PXM *pxm2; ww = pxm1->ww; hh = pxm1->hh; nc = pxm1->nc; cc = ww * hh * nc * sizeof(float); pxm2 = PXM_make(ww,hh,nc); memcpy(pxm2->pixels,pxm1->pixels,cc); return pxm2; } // create a copy of a PXM rectangular area PXM * PXM_copy_area(PXM *pxm1, int orgx, int orgy, int ww2, int hh2) { float *pix1, *pix2; PXM *pxm2 = 0; int px1, py1, px2, py2; int nc, pcc; nc = pxm1->nc; pcc = nc * sizeof(float); pxm2 = PXM_make(ww2,hh2,nc); for (py1 = orgy, py2 = 0; py2 < hh2; py1++, py2++) { pix1 = PXMpix(pxm1,orgx,orgy); pix2 = PXMpix(pxm2,0,py2); for (px1 = orgx, px2 = 0; px2 < ww2; px1++, px2++) { memcpy(pix2,pix1,pcc); pix1 += nc; pix2 += nc; } } return pxm2; } /******************************************************************************** Rescale PXM image to new width and height. The scale ratios may be different for width and height. Method: The input and output images are overlayed, stretching or shrinking the output pixels as needed. The contribution of each input pixel overlapping an output pixel is proportional to the area of the output pixel covered by the input pixel. The contributions of all overlaping input pixels are added. The work is spread among NWT threads to reduce time on SMP processors. Example: if the output image is 40% of the input image, then: outpix[0,0] = 0.16 * inpix[0,0] + 0.16 * inpix[1,0] + 0.08 * inpix[2,0] + 0.16 * inpix[0,1] + 0.16 * inpix[1,1] + 0.08 * inpix[2,1] + 0.08 * inpix[0,2] + 0.08 * inpix[1,2] + 0.04 * inpix[2,2] *********************************************************************************/ namespace pxmrescale { float *ppix1, *ppix2; int ww1, hh1, ww2, hh2, nc; int *px1L, *py1L; float *pxmap, *pymap; int maxmapx, maxmapy; int busy[max_threads]; } PXM * PXM_rescale(PXM *pxm1, int ww, int hh) { using namespace pxmrescale; void * pxm_rescale_thread(void *arg); PXM *pxm2; int px1, py1, px2, py2; int pxl, pyl, pxm, pym, ii; float scalex, scaley; float px1a, py1a, px1b, py1b; float fx, fy; ww1 = pxm1->ww; // input PXM hh1 = pxm1->hh; nc = pxm1->nc; ppix1 = pxm1->pixels; pxm2 = PXM_make(ww,hh,nc); // output PXM if (! pxm2) return 0; // too big or no memory ww2 = ww; hh2 = hh; ppix2 = pxm2->pixels; memset(ppix2, 0, ww2 * hh2 * nc * sizeof(float)); // clear output pixmap 18.01 scalex = 1.0 * ww1 / ww2; // compute x and y scales scaley = 1.0 * hh1 / hh2; if (scalex <= 1) maxmapx = 2; // compute max input pixels else maxmapx = scalex + 2; // mapping into output pixels maxmapx += 1; // for both dimensions if (scaley <= 1) maxmapy = 2; // (pixels may not be square) else maxmapy = scaley + 2; maxmapy += 1; // (extra entry for -1 flag) pymap = (float *) zmalloc(hh2 * maxmapy * sizeof(float)); // maps overlap of < maxmap input pxmap = (float *) zmalloc(ww2 * maxmapx * sizeof(float)); // pixels per output pixel py1L = (int *) zmalloc(hh2 * sizeof(int)); // maps first (lowest) input pixel px1L = (int *) zmalloc(ww2 * sizeof(int)); // per output pixel for (py2 = 0; py2 < hh2; py2++) // loop output y-pixels { py1a = py2 * scaley; // corresponding input y-pixels py1b = py1a + scaley; if (py1b >= hh1) py1b = hh1 - 0.001; // fix precision limitation pyl = py1a; py1L[py2] = pyl; // 1st overlapping input pixel for (py1 = pyl, pym = 0; py1 < py1b; py1++, pym++) // loop overlapping input pixels { if (py1 < py1a) { // compute amount of overlap if (py1+1 < py1b) fy = py1+1 - py1a; // 0.0 to 1.0 else fy = scaley; } else if (py1+1 > py1b) fy = py1b - py1; else fy = 1; ii = py2 * maxmapy + pym; // save it pymap[ii] = 0.9999 * fy / scaley; } ii = py2 * maxmapy + pym; // set an end marker after pymap[ii] = -1; // last overlapping pixel } for (px2 = 0; px2 < ww2; px2++) // do same for x-pixels { px1a = px2 * scalex; px1b = px1a + scalex; if (px1b >= ww1) px1b = ww1 - 0.001; pxl = px1a; px1L[px2] = pxl; for (px1 = pxl, pxm = 0; px1 < px1b; px1++, pxm++) { if (px1 < px1a) { if (px1+1 < px1b) fx = px1+1 - px1a; else fx = scalex; } else if (px1+1 > px1b) fx = px1b - px1; else fx = 1; ii = px2 * maxmapx + pxm; pxmap[ii] = 0.9999 * fx / scalex; } ii = px2 * maxmapx + pxm; pxmap[ii] = -1; } for (ii = 0; ii < NWT; ii++) { // start working threads busy[ii] = 1; start_detached_thread(pxm_rescale_thread,&Nval[ii]); } for (ii = 0; ii < NWT; ii++) // wait for all done while (busy[ii]) zsleep(0.001); zfree(px1L); zfree(py1L); zfree(pxmap); zfree(pymap); return pxm2; } void * pxm_rescale_thread(void *arg) // worker thread function { using namespace pxmrescale; int index = *((int *) arg); int px1, py1, px2, py2; int pxl, pyl, pxm, pym, ii; float *pixel1, *pixel2; float fx, fy, ftot; float chan[6]; int pcc = nc * sizeof(float); for (py2 = index; py2 < hh2; py2 += NWT) // loop output y-pixels { pyl = py1L[py2]; // corresp. 1st input y-pixel for (px2 = 0; px2 < ww2; px2++) // loop output x-pixels { pxl = px1L[px2]; // corresp. 1st input x-pixel memset(chan,0,pcc); // initz. output pixel for (py1 = pyl, pym = 0; ; py1++, pym++) // loop overlapping input y-pixels { ii = py2 * maxmapy + pym; // get y-overlap fy = pymap[ii]; if (fy < 0) break; // no more pixels for (px1 = pxl, pxm = 0; ; px1++, pxm++) // loop overlapping input x-pixels { ii = px2 * maxmapx + pxm; // get x-overlap fx = pxmap[ii]; if (fx < 0) break; // no more pixels ftot = fx * fy; // area overlap = x * y overlap pixel1 = ppix1 + (py1 * ww1 + px1) * nc; for (ii = 0; ii < nc; ii++) chan[ii] += pixel1[ii] * ftot; // add input pixel * overlap } } pixel2 = ppix2 + (py2 * ww2 + px2) * nc; // save output pixel memcpy(pixel2,chan,pcc); } } busy[index] = 0; return 0; } /******************************************************************************** PXM *pxm2 = PXM_rotate(PXM *pxm1, float angle) Rotate PXM pixmap through an arbitrary angle (degrees). The returned image has the same size as the original, but the pixmap size is increased to accomodate the rotated image. (e.g. a 100x100 image rotated 45 deg. needs a 142x142 pixmap). The space added around the rotated image is black (RGB 0,0,0). Angle is in degrees. Positive direction is clockwise. Speed is about 28 million pixels/sec/thread for a 3.3 GHz CPU. Loss of resolution is less than 1 pixel. Work is divided among NWT threads to gain speed. *********************************************************************************/ namespace pxmrotate { int busy = 0; float *ppix1, *ppix2; int ww1, hh1, ww2, hh2, nc; float angle; } PXM * PXM_rotate(PXM *pxm1, float anglex, int fast) // fast option 17.04 { using namespace pxmrotate; PXM *PXM_rotate2(PXM *pxm, int angle); void *PXM_rotate_thread(void *); void *PXM_rotate_thread_fast(void *); int ii; PXM *pxm2; ww1 = pxm1->ww; // input PXM hh1 = pxm1->hh; nc = pxm1->nc; ppix1 = pxm1->pixels; angle = anglex; while (angle < -180) angle += 360; // normalize, -180 to +180 while (angle > 180) angle -= 360; if (angle >= -180.0 && angle < -179.99) // use lossless version for angles return PXM_rotate2(pxm1,180); // of -180, -90, 0, 90, 180 if (angle > -90.01 && angle < -89.99) return PXM_rotate2(pxm1,-90); if (angle > -0.01 && angle < 0.01) return PXM_copy(pxm1); if (angle > 89.99 && angle < 90.01) return PXM_rotate2(pxm1,90); if (angle > 179.99 && angle <= 180.0) return PXM_rotate2(pxm1,180); angle = angle * PI / 180; // radians, -PI to +PI ww2 = ww1*fabsf(cosf(angle)) + hh1*fabsf(sinf(angle)); // rectangle containing rotated image hh2 = ww1*fabsf(sinf(angle)) + hh1*fabsf(cosf(angle)); pxm2 = PXM_make(ww2,hh2,nc); // output PXM if (! pxm2) return 0; ppix2 = pxm2->pixels; if (fast) { for (ii = 0; ii < NWT; ii++) // start worker threads start_detached_thread(PXM_rotate_thread_fast,&Nval[ii]); zadd_locked(busy,+NWT); } else { for (ii = 0; ii < NWT; ii++) // start worker threads start_detached_thread(PXM_rotate_thread,&Nval[ii]); zadd_locked(busy,+NWT); } while (busy) zsleep(0.001); // wait for completion return pxm2; } void * PXM_rotate_thread(void *arg) { using namespace pxmrotate; int index = *((int *) (arg)); int px2, py2, px0, py0; float *pix0, *pix1, *pix2, *pix3; float px1, py1; float f0, f1, f2, f3, chan[6]; float a, b, ww15, hh15, ww25, hh25; int pcc = nc * sizeof(float); ww15 = 0.5 * ww1; hh15 = 0.5 * hh1; ww25 = 0.5 * ww2; hh25 = 0.5 * hh2; a = cosf(angle); b = sinf(angle); for (py2 = index; py2 < hh2; py2 += NWT) // loop through output pixels for (px2 = 0; px2 < ww2; px2++) { px1 = a * (px2 - ww25) + b * (py2 - hh25) + ww15; // (px1,py1) = corresponding py1 = -b * (px2 - ww25) + a * (py2 - hh25) + hh15; // point within input pixels px0 = px1; // pixel containing (px1,py1) py0 = py1; if (px0 < 0 || px0 > ww1-2 || py0 < 0 || py0 > hh1-2) { // if outside input pixel array pix2 = ppix2 + (py2 * ww2 + px2) * nc; // output is black memset(pix2,0,pcc); continue; } pix0 = ppix1 + (py0 * ww1 + px0) * nc; // 4 input pixels based at (px0,py0) pix1 = pix0 + ww1 * nc; pix2 = pix0 + nc; pix3 = pix1 + nc; f0 = (px0+1 - px1) * (py0+1 - py1); // overlap of (px1,py1) f1 = (px0+1 - px1) * (py1 - py0); // in each of the 4 pixels f2 = (px1 - px0) * (py0+1 - py1); f3 = (px1 - px0) * (py1 - py0); for (int ii = 0; ii < nc; ii++) // sum the weighted inputs chan[ii] = f0 * pix0[ii] + f1 * pix1[ii] + f2 * pix2[ii] + f3 * pix3[ii]; pix2 = ppix2 + (py2 * ww2 + px2) * nc; // output pixel memcpy(pix2,chan,pcc); } zadd_locked(busy,-1); return 0; } void * PXM_rotate_thread_fast(void *arg) // 17.04 { using namespace pxmrotate; int index = *((int *) (arg)); int px2, py2, px0, py0; float *pix0, *pix2; float px1, py1; float a, b, ww15, hh15, ww25, hh25; int pcc = nc * sizeof(float); ww15 = 0.5 * ww1; hh15 = 0.5 * hh1; ww25 = 0.5 * ww2; hh25 = 0.5 * hh2; a = cosf(angle); b = sinf(angle); for (py2 = index; py2 < hh2; py2 += NWT) // loop through output pixels for (px2 = 0; px2 < ww2; px2++) { px1 = a * (px2 - ww25) + b * (py2 - hh25) + ww15; // (px1,py1) = corresponding py1 = -b * (px2 - ww25) + a * (py2 - hh25) + hh15; // point within input pixels px0 = px1; // pixel containing (px1,py1) py0 = py1; if (px0 < 0 || px0 > ww1-2 || py0 < 0 || py0 > hh1-2) { // if outside input pixel array pix2 = ppix2 + (py2 * ww2 + px2) * nc; // output is black memset(pix2,0,pcc); continue; } pix0 = ppix1 + (py0 * ww1 + px0) * nc; // input pixel pix2 = ppix2 + (py2 * ww2 + px2) * nc; // output pixel memcpy(pix2,pix0,pcc); } zadd_locked(busy,-1); return 0; } PXM * PXM_rotate2(PXM *pxm1, int angle) // angle = -90, 90, 180 { using namespace pxmrotate; int px1, py1, px2, py2, nc, pcc; float *pix1, *pix2; PXM *pxm2; if (angle == 0) return PXM_copy(pxm1); ww1 = pxm1->ww; // input PXM hh1 = pxm1->hh; nc = pxm1->nc; pcc = nc * sizeof(float); if (angle == -90) { ww2 = hh1; hh2 = ww1; } else if (angle == 90) { ww2 = hh1; hh2 = ww1; } else if (angle == 180) { ww2 = ww1; hh2 = hh1; } else zappcrash("PXM_rotate2() bad angle %d",angle); pxm2 = PXM_make(ww2,hh2,nc); // output PXM ppix2 = pxm2->pixels; for (py1 = 0; py1 < hh1; py1++) // loop all input pixels for (px1 = 0; px1 < ww1; px1++) { if (angle == -90) { px2 = py1; py2 = hh2 - px1 - 1; } else if (angle == 90) { px2 = ww2 - py1 - 1; py2 = px1; } else /* angle = 180 */ { px2 = ww2 - px1 - 1; py2 = hh2 - py1 - 1; } pix1 = ppix1 + (py1 * ww1 + px1) * nc; pix2 = ppix2 + (py2 * ww2 + px2) * nc; memcpy(pix2,pix1,pcc); } return pxm2; } /******************************************************************************** PXB pixmap functions - RGB uint8 pixel map and PIXBUF wrapper *********************************************************************************/ // Create PXB pixmap with pixels cleared to zero PXB * PXB_make(int ww, int hh, int ac) { uint cc; PXB *pxb = (PXB *) zmalloc(sizeof(PXB)); strcpy(pxb->wmi,"pxbpix"); pxb->pixbuf = gdk_pixbuf_new(GDKRGB,ac,8,ww,hh); if (! pxb->pixbuf) { zmessageACK(Mwin,"memory allocation failure"); exit(12); } pxb->pixels = gdk_pixbuf_get_pixels(pxb->pixbuf); pxb->ww = ww; pxb->hh = hh; pxb->rs = gdk_pixbuf_get_rowstride(pxb->pixbuf); pxb->nc = 3 + ac; // nc = 3/4 for no/alpha channel cc = hh * pxb->rs; memset(pxb->pixels,0,cc); return pxb; } // Create a PXB pixmap from a PIXBUF // The pixbuf is used directly and is not duplicated PXB * PXB_make(PIXBUF *pixbuf1) { PXB *pxb2 = (PXB *) zmalloc(sizeof(PXB)); strcpy(pxb2->wmi,"pxbpix"); pxb2->pixbuf = pixbuf1; pxb2->ww = gdk_pixbuf_get_width(pixbuf1); pxb2->hh = gdk_pixbuf_get_height(pixbuf1); pxb2->rs = gdk_pixbuf_get_rowstride(pixbuf1); pxb2->nc = 3 + gdk_pixbuf_get_has_alpha(pixbuf1); pxb2->pixels = gdk_pixbuf_get_pixels(pixbuf1); return pxb2; } // Free PXB pixmap - release memory void PXB_free(PXB *&pxb) { if (! pxb) return; if (! strmatch(pxb->wmi,"pxbpix")) zappcrash("PXB_free(), bad PXB"); strcpy(pxb->wmi,"xxxxxx"); g_object_unref(pxb->pixbuf); zfree(pxb); pxb = 0; return; } // add an alpha channel to a PXB pixmap void PXB_addalpha(PXB *pxb) { GdkPixbuf *pixbuf1, *pixbuf2; uint8 *pixels1, *pixels2, *pix1, *pix2; int nc, ww, hh, rs1, rs2, px, py; nc = pxb->nc; if (nc > 3) return; ww = pxb->ww; hh = pxb->hh; pixbuf1 = pxb->pixbuf; // old pixbuf pixbuf2 = gdk_pixbuf_new(GDKRGB,1,8,ww,hh); // new pixbuf with alpha channel if (! pixbuf2) { zmessageACK(Mwin,"memory allocation failure"); exit(12); } pixels1 = gdk_pixbuf_get_pixels(pixbuf1); pixels2 = gdk_pixbuf_get_pixels(pixbuf2); rs1 = gdk_pixbuf_get_rowstride(pixbuf1); rs2 = gdk_pixbuf_get_rowstride(pixbuf2); for (py = 0; py < hh; py++) // copy RGB data { pix1 = pixels1 + rs1 * py; pix2 = pixels2 + rs2 * py; for (px = 0; px < ww; px++) { memcpy(pix2,pix1,3); pix2[3] = 255; // set opacity = max. for new pixbuf pix1 += 3; pix2 += 4; } } g_object_unref(pixbuf1); pxb->pixbuf = pixbuf2; pxb->pixels = pixels2; pxb->rs = rs2; pxb->nc = 4; return; } // Copy a PXB pixmap to a new PXB pixmap PXB * PXB_copy(PXB *pxb1) { PXB *pxb2 = (PXB *) zmalloc(sizeof(PXB)); strcpy(pxb2->wmi,"pxbpix"); pxb2->pixbuf = gdk_pixbuf_copy(pxb1->pixbuf); if (! pxb2->pixbuf) { zmessageACK(Mwin,"memory allocation failure"); exit(12); } pxb2->ww = pxb1->ww; pxb2->hh = pxb1->hh; pxb2->rs = pxb1->rs; pxb2->nc = pxb1->nc; pxb2->pixels = gdk_pixbuf_get_pixels(pxb2->pixbuf); return pxb2; } // Rescale PXB pixmap to given width and height. PXB * PXB_rescale(PXB *pxb1, int ww2, int hh2) { PIXBUF *pixbuf = 0; pixbuf = gdk_pixbuf_scale_simple(pxb1->pixbuf,ww2,hh2,BILINEAR); if (! pixbuf) { zmessageACK(Mwin,"memory allocation failure"); exit(12); } PXB *pxb2 = PXB_make(pixbuf); return pxb2; } // Rotate PXB pixmap by given angle. PXB * PXB_rotate(PXB *pxb1, float angle) { PIXBUF *pixbuf = 0; pixbuf = gdk_pixbuf_rotate(pxb1->pixbuf,angle); if (! pixbuf) { zmessageACK(Mwin,"memory allocation failure"); exit(12); } PXB *pxb2 = PXB_make(pixbuf); return pxb2; } /********************************************************************************/ // Copy a PXM image (RGB float) to a PXB image (RGB uint8). PXB * PXM_PXB_copy(PXM *pxm1) { float *pix1; uint8 *pix2; PXB *pxb2; int ww, hh, nc, ac, px, py; ww = pxm1->ww; hh = pxm1->hh; nc = pxm1->nc; ac = nc - 3; pxb2 = PXB_make(ww,hh,ac); for (py = 0; py < hh; py++) { pix1 = PXMpix(pxm1,0,py); pix2 = PXBpix(pxb2,0,py); for (px = 0; px < ww; px++) { pix2[0] = pix1[0]; pix2[1] = pix1[1]; pix2[2] = pix1[2]; if (ac) pix2[3] = pix1[3]; pix1 += nc; pix2 += nc; } } return pxb2; } // Update a PXB section from an updated PXM section. // PXM and PXB must have the same dimensions. // px3, py3, ww3, hh3: modified section within pxm1 to propagate to pxb2; void PXM_PXB_update(PXM *pxm1, PXB *pxb2, int px3, int py3, int ww3, int hh3) { float *pix1; uint8 *pix2; int px, py; int lox, hix, loy, hiy; int nc1, nc2, ac; if (pxm1->ww + pxm1->hh != pxb2->ww + pxb2->hh) zappcrash("PXM_PXB_update() call error"); lox = px3; hix = px3 + ww3; if (lox < 0) lox = 0; if (hix > pxb2->ww) hix = pxb2->ww; loy = py3; hiy = py3 + hh3; if (loy < 0) loy = 0; if (hiy > pxb2->hh) hiy = pxb2->hh; nc1 = pxm1->nc; nc2 = pxb2->nc; if (nc1 > 3 && nc2 > 3) ac = 1; // alpha channel else ac = 0; for (py = loy; py < hiy; py++) { pix1 = PXMpix(pxm1,lox,py); pix2 = PXBpix(pxb2,lox,py); for (px = lox; px < hix; px++) { pix2[0] = pix1[0]; // 0.0 - 255.99 >> 0 - 255 pix2[1] = pix1[1]; pix2[2] = pix1[2]; if (ac) pix2[3] = pix1[3]; pix1 += nc1; pix2 += nc2; } } return; } // Update an output PXB section from a corresponding input PXB section. // The two PXBs represent the same image at different scales (width/height). // pxb1 must be >= pxb2. // px3, py3, ww3, hh3: modified section within pxb1 to propagate to pxb2; void PXB_PXB_update(PXB *pxb1, PXB *pxb2, int px3, int py3, int ww3, int hh3) { static int pww1 = 0, phh1 = 0, pww2 = 0, phh2 = 0; static int *px1L = 0, *py1L = 0; static float scalex = 1, scaley = 1; static float *pxmap = 0, *pymap = 0; static int maxmapx = 0, maxmapy = 0; uint8 *ppix1, *ppix2; int ww1, hh1, ww2, hh2, rs1, rs2, nc1, nc2, ac; int px1, py1, px2, py2; int pxl, pyl, pxm, pym, ii; float px1a, py1a, px1b, py1b; float fx, fy, ftot; uint8 *pixel1, *pixel2; float red, green, blue, alpha; int lox, hix, loy, hiy; ww1 = pxb1->ww; hh1 = pxb1->hh; rs1 = pxb1->rs; nc1 = pxb1->nc; ppix1 = pxb1->pixels; ww2 = pxb2->ww; hh2 = pxb2->hh; rs2 = pxb2->rs; nc2 = pxb2->nc; ppix2 = pxb2->pixels; if (nc1 > 3 && nc2 > 3) ac = 1; // alpha channel else ac = 0; if (ww1 == pww1 && hh1 == phh1 && ww2 == pww2 && hh2 == phh2) // if the sizes are the same as before, goto copy_pixels; // the pixel mapping math can be avoided. pww1 = ww1; phh1 = hh1; pww2 = ww2; phh2 = hh2; if (px1L) { // unless this is the first call, zfree(px1L); // free prior map memory zfree(py1L); zfree(pxmap); zfree(pymap); } scalex = 1.0 * ww1 / ww2; // compute x and y scales scaley = 1.0 * hh1 / hh2; if (scalex <= 1) maxmapx = 2; // compute max input pixels else maxmapx = scalex + 2; // mapping into output pixels maxmapx += 1; // for both dimensions if (scaley <= 1) maxmapy = 2; // (pixels may not be square) else maxmapy = scaley + 2; maxmapy += 1; // (extra entry for -1 flag) pymap = (float *) zmalloc(hh2 * maxmapy * sizeof(float)); // maps overlap of < maxmap input pxmap = (float *) zmalloc(ww2 * maxmapx * sizeof(float)); // pixels per output pixel py1L = (int *) zmalloc(hh2 * sizeof(int)); // maps first (lowest) input pixel px1L = (int *) zmalloc(ww2 * sizeof(int)); // per output pixel for (py2 = 0; py2 < hh2; py2++) // loop output y-pixels { py1a = py2 * scaley; // corresponding input y-pixels py1b = py1a + scaley; if (py1b >= hh1) py1b = hh1 - 0.001; // fix precision limitation pyl = py1a; py1L[py2] = pyl; // 1st overlapping input pixel for (py1 = pyl, pym = 0; py1 < py1b; py1++, pym++) // loop overlapping input pixels { if (py1 < py1a) { // compute amount of overlap if (py1+1 < py1b) fy = py1+1 - py1a; // 0.0 to 1.0 else fy = scaley; } else if (py1+1 > py1b) fy = py1b - py1; else fy = 1; ii = py2 * maxmapy + pym; // save it pymap[ii] = 0.9999 * fy / scaley; } ii = py2 * maxmapy + pym; // set an end marker after pymap[ii] = -1; // last overlapping pixel } for (px2 = 0; px2 < ww2; px2++) // do same for x-pixels { px1a = px2 * scalex; px1b = px1a + scalex; if (px1b >= ww1) px1b = ww1 - 0.001; pxl = px1a; px1L[px2] = pxl; for (px1 = pxl, pxm = 0; px1 < px1b; px1++, pxm++) { if (px1 < px1a) { if (px1+1 < px1b) fx = px1+1 - px1a; else fx = scalex; } else if (px1+1 > px1b) fx = px1b - px1; else fx = 1; ii = px2 * maxmapx + pxm; pxmap[ii] = 0.9999 * fx / scalex; } ii = px2 * maxmapx + pxm; pxmap[ii] = -1; } copy_pixels: px3 = px3 / scalex; // convert input area to output area py3 = py3 / scaley; ww3 = ww3 / scalex + 2; hh3 = hh3 / scaley + 2; lox = px3; hix = px3 + ww3; if (lox < 0) lox = 0; if (hix > ww2) hix = ww2; loy = py3; hiy = py3 + hh3; if (loy < 0) loy = 0; if (hiy > hh2) hiy = hh2; for (py2 = loy; py2 < hiy; py2++) // loop output y-pixels { pyl = py1L[py2]; // corresp. 1st input y-pixel for (px2 = lox; px2 < hix; px2++) // loop output x-pixels { pxl = px1L[px2]; // corresp. 1st input x-pixel red = green = blue = alpha = 0; // initz. output pixel for (py1 = pyl, pym = 0; ; py1++, pym++) // loop overlapping input y-pixels { ii = py2 * maxmapy + pym; // get y-overlap fy = pymap[ii]; if (fy < 0) break; // no more pixels for (px1 = pxl, pxm = 0; ; px1++, pxm++) // loop overlapping input x-pixels { ii = px2 * maxmapx + pxm; // get x-overlap fx = pxmap[ii]; if (fx < 0) break; // no more pixels ftot = fx * fy; // area overlap = x * y overlap pixel1 = ppix1 + py1 * rs1 + px1 * nc1; red += pixel1[0] * ftot; // add input pixel * overlap green += pixel1[1] * ftot; blue += pixel1[2] * ftot; if (ac) alpha += pixel1[3] * ftot; } } pixel2 = ppix2 + py2 * rs2 + px2 * nc2; // save output pixel pixel2[0] = red; // 0.0 - 255.996 >> 0 - 255 pixel2[1] = green; pixel2[2] = blue; if (ac) pixel2[3] = alpha; } } return; } /************************************************************************** Rescale pixbuf image to new width and height. The scale ratios may be different for width and height. Method: The input and output images are overlayed, stretching or shrinking the output pixels as needed. The contribution of each input pixel overlapping an output pixel is proportional to the area of the output pixel covered by the input pixel. The contributions of all overlaping input pixels are added. The work is spread among NWT threads to reduce elapsed time on SMP processors. Example: if the output image is 40% of the input image, then: outpix[0,0] = 0.16 * inpix[0,0] + 0.16 * inpix[1,0] + 0.08 * inpix[2,0] + 0.16 * inpix[0,1] + 0.16 * inpix[1,1] + 0.08 * inpix[2,1] + 0.08 * inpix[0,2] + 0.08 * inpix[1,2] + 0.04 * inpix[2,2] ***************************************************************************/ namespace pixbuf_rescale_names { uint8 *pixels1; uint8 *pixels2; int ww1; int hh1; int rs1; int ww2; int hh2; int rs2; int *px1L; int *py1L; float *pxmap; float *pymap; int maxmapx; int maxmapy; int busy[max_threads]; } GdkPixbuf * pixbuf_rescale(GdkPixbuf *pixbuf1, int ww, int hh) { using namespace pixbuf_rescale_names; GdkPixbuf * pixbuf_half(GdkPixbuf *pixbuf1); void * pixbuf_rescale_thread(void *arg); GdkPixbuf *pixbuf2; int px1, py1, px2, py2; int pxl, pyl, pxm, pym, ii; float scalex, scaley; float px1a, py1a, px1b, py1b; float fx, fy; int Fhalf = 0; ww1 = gdk_pixbuf_get_width(pixbuf1); // input pixbuf hh1 = gdk_pixbuf_get_height(pixbuf1); if (ww <= ww1/2 && hh <= hh1/2) { // if scaling down to < 1/2 size, pixbuf1 = pixbuf_half(pixbuf1); // use fast 1/2 scale down first Fhalf = 1; // (save 3/4 of the work) } ww1 = gdk_pixbuf_get_width(pixbuf1); hh1 = gdk_pixbuf_get_height(pixbuf1); rs1 = gdk_pixbuf_get_rowstride(pixbuf1); pixels1 = gdk_pixbuf_get_pixels(pixbuf1); pixbuf2 = gdk_pixbuf_new(GDK_COLORSPACE_RGB,0,8,ww,hh); // create output pixbuf if (! pixbuf2) return 0; ww2 = gdk_pixbuf_get_width(pixbuf2); hh2 = gdk_pixbuf_get_height(pixbuf2); rs2 = gdk_pixbuf_get_rowstride(pixbuf2); pixels2 = gdk_pixbuf_get_pixels(pixbuf2); scalex = 1.0 * ww1 / ww2; // compute x and y scales scaley = 1.0 * hh1 / hh2; if (scalex <= 1) maxmapx = 2; // compute max input pixels else maxmapx = scalex + 2; // mapping into output pixels maxmapx += 1; // for both dimensions if (scaley <= 1) maxmapy = 2; // (pixels may not be square) else maxmapy = scaley + 2; maxmapy += 1; // (extra entry for -1 flag) pymap = (float *) zmalloc(hh2 * maxmapy * sizeof(float)); // maps overlap of < maxmap input pxmap = (float *) zmalloc(ww2 * maxmapx * sizeof(float)); // pixels per output pixel py1L = (int *) zmalloc(hh2 * sizeof(int)); // maps first (lowest) input pixel px1L = (int *) zmalloc(ww2 * sizeof(int)); // per output pixel for (py2 = 0; py2 < hh2; py2++) // loop output y-pixels { py1a = py2 * scaley; // corresponding input y-pixels py1b = py1a + scaley; if (py1b >= hh1) py1b = hh1 - 0.001; // fix precision limitation pyl = py1a; py1L[py2] = pyl; // 1st overlapping input pixel for (py1 = pyl, pym = 0; py1 < py1b; py1++, pym++) // loop overlapping input pixels { if (py1 < py1a) { // compute amount of overlap if (py1+1 < py1b) fy = py1+1 - py1a; // 0.0 to 1.0 else fy = scaley; } else if (py1+1 > py1b) fy = py1b - py1; else fy = 1; ii = py2 * maxmapy + pym; // save it pymap[ii] = 0.9999 * fy / scaley; } ii = py2 * maxmapy + pym; // set an end marker after pymap[ii] = -1; // last overlapping pixel } for (px2 = 0; px2 < ww2; px2++) // do same for x-pixels { px1a = px2 * scalex; px1b = px1a + scalex; if (px1b >= ww1) px1b = ww1 - 0.001; pxl = px1a; px1L[px2] = pxl; for (px1 = pxl, pxm = 0; px1 < px1b; px1++, pxm++) { if (px1 < px1a) { if (px1+1 < px1b) fx = px1+1 - px1a; else fx = scalex; } else if (px1+1 > px1b) fx = px1b - px1; else fx = 1; ii = px2 * maxmapx + pxm; pxmap[ii] = 0.9999 * fx / scalex; } ii = px2 * maxmapx + pxm; pxmap[ii] = -1; } for (ii = 0; ii < NWT; ii++) { // start working threads busy[ii] = 1; start_detached_thread(pixbuf_rescale_thread,&Nval[ii]); } for (ii = 0; ii < NWT; ii++) // wait for all done while (busy[ii]) zsleep(0.001); zfree(px1L); zfree(py1L); zfree(pxmap); zfree(pymap); if (Fhalf) g_object_unref(pixbuf1); return pixbuf2; } void * pixbuf_rescale_thread(void *arg) // worker thread function { using namespace pixbuf_rescale_names; int index = *((int *) arg); int px1, py1, px2, py2; int pxl, pyl, pxm, pym, ii; uint8 *pix1, *pix2; float fx, fy, ftot; float red, green, blue; for (py2 = index; py2 < hh2; py2 += NWT) // loop output y-pixels { pyl = py1L[py2]; // corresp. 1st input y-pixel for (px2 = 0; px2 < ww2; px2++) // loop output x-pixels { pxl = px1L[px2]; // corresp. 1st input x-pixel red = green = blue = 0; // initz. output pixel for (py1 = pyl, pym = 0; ; py1++, pym++) // loop overlapping input y-pixels { ii = py2 * maxmapy + pym; // get y-overlap fy = pymap[ii]; if (fy < 0) break; // no more pixels for (px1 = pxl, pxm = 0; ; px1++, pxm++) // loop overlapping input x-pixels { ii = px2 * maxmapx + pxm; // get x-overlap fx = pxmap[ii]; if (fx < 0) break; // no more pixels ftot = fx * fy; // area overlap = x * y overlap pix1 = pixels1 + py1 * rs1 + px1 * 3; red += pix1[0] * ftot; // add input pixel * overlap green += pix1[1] * ftot; blue += pix1[2] * ftot; } pix2 = pixels2 + py2 * rs2 + px2 * 3; // save output pixel pix2[0] = red; pix2[1] = green; pix2[2] = blue; } } } busy[index] = 0; return 0; } // rescale a pixbuf to 1/2 size using 4 threads // if rows or colums are odd, the last one is ignored namespace pixbuf_half_names { uint8 *pixels1; uint8 *pixels2; int ww1; int hh1; int rs1; int ww2; int hh2; int rs2; int busy[max_threads]; } GdkPixbuf * pixbuf_half(GdkPixbuf *pixbuf1) { using namespace pixbuf_half_names; void * pixbuf_half_thread(void *arg); GdkPixbuf *pixbuf2; int ii; ww1 = gdk_pixbuf_get_width(pixbuf1); // input pixbuf hh1 = gdk_pixbuf_get_height(pixbuf1); rs1 = gdk_pixbuf_get_rowstride(pixbuf1); pixels1 = gdk_pixbuf_get_pixels(pixbuf1); ww2 = ww1 / 2; hh2 = hh1 / 2; pixbuf2 = gdk_pixbuf_new(GDK_COLORSPACE_RGB,0,8,ww2,hh2); // create output pixbuf if (! pixbuf2) return 0; rs2 = gdk_pixbuf_get_rowstride(pixbuf2); pixels2 = gdk_pixbuf_get_pixels(pixbuf2); for (ii = 0; ii < NWT; ii++) { // start working threads busy[ii] = 1; start_detached_thread(pixbuf_half_thread,&Nval[ii]); } for (ii = 0; ii < NWT; ii++) // wait for all done while (busy[ii]) zsleep(0.001); return pixbuf2; } void * pixbuf_half_thread(void *arg) { using namespace pixbuf_half_names; int index = *((int *) arg); int px2, py2; uint8 *pix1, *pix2; for (py2 = index; py2 < hh2; py2 += NWT) { pix1 = pixels1 + rs1 * py2 * 2; pix2 = pixels2 + rs2 * py2; for (px2 = 0; px2 < ww2; px2++) { pix2[0] = (pix1[0] + pix1[3] + (pix1+rs1)[0] + (pix1+rs1)[3]) >> 2; pix2[1] = (pix1[1] + pix1[4] + (pix1+rs1)[1] + (pix1+rs1)[4]) >> 2; pix2[2] = (pix1[2] + pix1[5] + (pix1+rs1)[2] + (pix1+rs1)[5]) >> 2; pix1 += 6; pix2 += 3; } } busy[index] = 0; return 0; } /********************************************************************************/ // fast pixbuf rescale, bilinear interpolation, threaded // works best when the rescale ratio is between 0.5 and 2.0 // about 2x faster than gdk_pixbuf_scale_simple() if 2 CPU cores available // alpha channel is not copied - used only in slide show for now namespace pixbuf_rescale_fast_names { int ww1, hh1, ww2, hh2, rs1, rs2, nc1, nc2; uint8 *pixels1, *pixels2; int busy[max_threads]; int nwt; } PIXBUF * pixbuf_rescale_fast(PIXBUF *pixbuf1, int ww, int hh) { using namespace pixbuf_rescale_fast_names; void * pixbuf_rescale_fast_thread(void *arg); PIXBUF *pixbuf2; uint8 *pix2; nwt = NWT - 1; // leave one thread for x11/wayland 18.01 if (nwt < 1) nwt = 1; pixbuf2 = gdk_pixbuf_new(GDKRGB,0,8,ww,hh); if (! pixbuf2) { zmessageACK(Mwin,"memory allocation failure"); exit(12); } ww1 = gdk_pixbuf_get_width(pixbuf1); hh1 = gdk_pixbuf_get_height(pixbuf1); rs1 = gdk_pixbuf_get_rowstride(pixbuf1); nc1 = gdk_pixbuf_get_n_channels(pixbuf1); pixels1 = gdk_pixbuf_get_pixels(pixbuf1); ww2 = ww; hh2 = hh; rs2 = gdk_pixbuf_get_rowstride(pixbuf2); nc2 = gdk_pixbuf_get_n_channels(pixbuf2); pixels2 = gdk_pixbuf_get_pixels(pixbuf2); for (int ii = 0; ii < nwt; ii++) { // start working threads busy[ii] = 1; start_detached_thread(pixbuf_rescale_fast_thread,&Nval[ii]); } for (int ii = 0; ii < nwt; ii++) // wait for all done while (busy[ii]) zsleep(0.001); for (int py2 = 0; py2 < hh2; py2++) { // set last column of pixels black pix2 = pixels2 + py2 * rs2 + (ww2-1) * 3; memset(pix2,0,3); } pix2 = pixels2 + (hh2-1) * rs2; // set last row of pixels black memset(pix2,0,rs2); return pixbuf2; } void * pixbuf_rescale_fast_thread(void *arg) { using namespace pixbuf_rescale_fast_names; int index = *((int *) arg); uint8 *pix1, *pix2; int px1, py1, px2, py2; float fww, fhh, fx1, fy1; float dx1, dy1, dx2, dy2; fww = 1.0 * ww1 / ww2; fhh = 1.0 * hh1 / hh2; for (py2 = index; py2 < hh2-1; py2 += nwt) // loop output pixel rows for (px2 = 0; px2 < ww2-1; px2++) // loop output pixel cols { fx1 = px2 * fww; // real position in input image fy1 = py2 * fhh; px1 = int(fx1); // corresp. input pixel py1 = int(fy1); dx1 = fx1 - px1; // distance from pixel left dx2 = 1.0 - dx1; // distance from pixel right dy1 = fy1 - py1; // distance from pixel top dy2 = 1.0 - dy1; // distance from pixel bottom pix1 = pixels1 + py1 * rs1 + px1 * nc1; // input pixel group (4) pix2 = pixels2 + py2 * rs2 + px2 * nc2; // output pixel pix2[0] = (dx2 * pix1[0] + dx1 * pix1[3] + dy2 * pix1[0] + dy1 * pix1[rs1+0]) / 2; pix2[1] = (dx2 * pix1[1] + dx1 * pix1[4] + dy2 * pix1[1] + dy1 * pix1[rs1+1]) / 2; pix2[2] = (dx2 * pix1[2] + dx1 * pix1[5] + dy2 * pix1[2] + dy1 * pix1[rs1+2]) / 2; } busy[index] = 0; return 0; }

a3lw@IT,=gM ].40a= `!x88}LwwR2 Ͷl6g| 0@(wP`N@!/A PÒ^ӿIϒ(m! j.sGyЅΰԕ½@<Tξo엖&CM0gXd< U^@(z (B~Cy %{Ӛ0(w`E;Db\3Q3w'/ڵp|f̌wNm Y_s!lfMxzDB n _{?0x\v ",wԯGw`AT]3$c@3ۡ躮sBUuӫ5L\A>EJ%UyDeUW.PBaSpQ"FɁmoQ3,xC 1dJմ?՞Z+~=̓@=/ugDGGC׹W!!au<eh ec>ׄaÀ@.QȗW3m>[7C8μ{"гok;Pإ?nlՠ\J"MpsS+ 2i,f_| G I%>mK(JJwxWfv᧣ zW&t"M935W\1۪E?pqo`v+q5&ڀmCi~:0{M |gk#LI?]xf^dÚyG^ }W7"1 ]rcp0+ rsro)1”rf۽| ?wVJ˝_Z|8μ#ZymMw_losRlww=}&O&Ov7xv>hq@xeEw~btOw7`𯱳? UN~]M0eF$̌^ ?yˬooX mIFKi܀/'mk C-z-4$~`іfs g}TGXo݌XʵB{PW^ՋP S"`%sde5?9#[8}d d^16.RY!V0"p(]<]Hxd1oz#xp̅=XjrYyhHDFFX"np 8u۵b6j:gg#܁l"At7h43hm-['?V.S2^SN:;:G-0`@[m@DD]ȩ,FY8y4v{A=xdbU,QGbe' D}Bc4K$}q73w9zh%g^]~3O9W)tK ﻥqGd6ˤk܌3 V|n,wq>,GP*]j ܙ&(6L <μP}vGBD\`r1%= +ˆ$86^϶TQ6fp+KYpn!r5OBH3#\ztbtO; [2ˮnXkS'B4]F.}Ϡπh8X98(aw6!M?W,]U&7^G cIkeO+jiTXtXML:com.adobe.xmp 64 64 ^sIENDB`fotoxx-18.01.1/images/search-images.jpg0000644000175000017500000024201513222767271016345 0ustar micomicoJFIFHHExifMM*V^(if%HH0231+0100Fotoxx:trim_rotate| Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 900 1000 0 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?n"XNqʤ{ v޻W^gMb_Ώ=R==XWTzԯ_ݕE[_883֋,u{u:{u:VnP:ދG_α58IPxV2ag\Jo?=qӟñ{u:x4#kǮW W\4ٮN_#TtɩOwK݋}y!C1(X|ty D״vix CZg_Ώ=R==XWTztn1R9GWNDDQFl׵gRiiѡ:y-gI9Utl=yƕݘpF v5ztF$TkƲJagvz(`(((((((((((((((((((((((((S}?_Uz]m֨Uܴ]}}ꗘ}G}KQ|z>۫^#P{'AdW:VaвdԞa?#=:̚]X<1[5Ǚʐ8k F_oS'fLa}އ>>0Z_;[%X}ɥi=jhcxܪCt O/>F&{ͯCd@>"0ʏ0ʝT$۰>X:EN:iQ>ʲV}Q͹JBvZ@Ы3'8Iɬ۩5(ol!meJFTHGE?2@ws@ʚbܲLk 5иeBm dzq<.EĐ3wwFPa#ЊPUά)jszI}_9mmи ~A^@+0 GQ츭m\J#G,._[V;MidFQ]QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE`Bj^&˨^([d x)'&2pH먮RƑ^CͰkc,EH'x@s^5%֗`׊(Qi [ei"(խ+ #8"Ɯ>ZZߴKe-|`9vF6F 8Ycӵ}6cqj.m % v`k ܲJݳƚ>_-#-lCrWf9Rz( Ub ֡M1Lp$P{g @hc^&׉ V\ 9;O|Zк֖AQw{{H@ עdAI-:.l\&pZȜ 8eGso;8ү|p2;d՝{AQm؂c"J20pTUrk8M;W^.VF4 2rI9^1xKI} ^$3[E\1vz(gCvkd3 ohrx&hfeޢuZGfyr nzg Pu-2@fGʓe 訢pZu?T]vT]qhZV&UUnr;[sy 3KHW]I#5aL׸=,PE_)I.g&{gq 7-ޟyj0YFw S[: ZiZ@BwL gҬkԵԸ4tU9$}ݻ@;عeM:!X 4szc3:ee<4XpI/yjӴXfWWy`D {V'eu/hez|c- |NBpNpE WfJfsmt3<ۘI"6yϖ{ }9XxZ0 fZk}|GjCww^'MMfBV9V8CL_ң!Zڐ<o-nZ}ʉ%{y.-%Y9p@$im;mV6v}]UzI`GkYVSIwt?orI|U_ d:G&rCpn-ahCW2#3nQ^\YCycfتрHskOL-/m|78챒H~7~\~]xgE4O[մ{M,#T bpN}xJSB CV 2yLT޴'TOm> (zASJִGAY'kkSgfb *ֳåY=&;h*#Z4]/OѴ.V8 r$dp;ֆ\xIZ,"w6nE|/܁w-\0+@1ȫSI.5{ GT]p?Imu:;We_-\9zsYV4Ӯ66KO̖G2#9?08'QeI2ۉ8V;[ibWuq$wnks$%aeV# d="PF6on%ĥe`031fi+[i4fdl%t@ 9SkcSZEy%Z"aiʌ77>ǁxx hoZ#:[5L)#Ku3 k;ֵ'{&Sl6 ;2#'I|>bŢʏx`]޻#n1ǭ%VuZ_yi-Ay4 TeUF9tuM.I]Jxf1bڷ]CeiE08$E-k=Svy}Չ6 #G$c)**F$2)KH.ee1H v>~",qRQ;Ax%LHbmNb,xi rSj-߄t$n7;<ٽ{9Mdw3=՜v A?3#/]Lt[4"kU9w$?+onVly<l͟cM--f[7+媈V% >YťͲ\̥۬| r1LZ(.cf%%i۽ц89ǚcimja:[c$vpQr$B䝸!QnS_-N-%O O2\yYէW F0FQ4ԼN7MΛ_by^G1y慯c(/5%.,eQS"nKVD쎬!/:XқV]1- }D\Z~Rr h~"4y.Ri-%J}!O^s6t_jcQKt5"a#|FPnKS{;Z(z+Fwo9(MZ#!'>Ƭ,sF$e98-R5w>ްoob+WEu]#݃=ZU~Wel[_cS{8aj}e-P*)<8zе{ k:mh[%EMr mtW y1V)AI49c hZk"k%D"VnyF< MhD4H̪% ;bwEM !720RA}j{[L҈R9I1>96[8?~7C"Mہק*D^BfSݻK}滏G,yRXz<X\Xl/mg&1C10ӁV#?޼?ƏG,yReZXz?I4Ej9c hrן']?*O^T@tV#?޼?ƏG,yReZXz?I4E&a٥ϲXf,L V:Z(+OB_'ֳ+OB_'ր6(((((((((((((((((((((((((+6{iAqkn%gW/ leZHKmPȥl*7)$xI#=&Xx- ?f:A 6F+qCKSQuynR(5/H,ͪ`XXx7jk@'iͧXwoglQTR=c4ji7Y@'iSlwZUn{yo$ajň'< gp: %מ )kY~ =\pffuj859u;;.IU[ x.{] + 5z3,rвO֥izާ:^_%d֯4Y 0T$gX^#l:̓$Anv9gh2X[6!ٳ})5 "X}`\yl:|qE[X?+sPZhnXȣcF7"{Id4tL0m @ FFWڽK(PciF6[mĴåȋ!=KOs]VO0>OK-w- HTM)e`g5/VKk+`r"eZM)lXi?G$oOP &wSIElXi?G$oOP*cW9Y:_V7󎝯WZon/ims R=j7Q6_(E*'B{hoI4[M7(fM)?$}n "6hoI4[M7(fM)?$}n "6hoI4[M7(fM)?$}n "6hoI4[M7(?1 (&Amj'+i-Z< CREPZz?Zz?EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPT*T(pmeP\PƒnTIv! ᥋#g<{n.uxwUYw\gdg@gϪgr%FP6NsȽkoh>Z#+<epXqVzaF2ƱkZ6FToLI_!=\p˪\$Ei#p[8גa&/<:?M1K(#h\^G_CI]JM;L7fZKEQT|T.XU _SL[V)Jl X-]; "{u2`d*>a N;-i湻[Y̏|X͍6g8˩i R$g`'d~>; {7y3ƲჂ22;WOkC{e2 <6CazqU4fio `"呡RbrE?=⊫鶩;;J(fps3SʥuJ(`QVApM1ԅ?(y[[Dlc8##qV;xi6HPE"wVbӋcJ=q6?<@԰?@g^x-Ffhoqlİ8p\XuIR14 7BxʚC>9=&MGJn,L$ 'I7_H MܭFe)byYj˯\+;b͎9TGY m JZʟ>H')$̂9s\ډ4 |94VHa!MJ]gnS0sO}.z QL$-w7͂yWiր' 0Zx ̑ BU& d cGm)^3#j.;[p圉Ab jkPl (ZΥ{tdcs;1 $r {xr#Ǭz3\qylG[:anoʅ=KLnu-RMׇWN@>y 08>?*VV*u+׸HE gZWwwmiiRD֋dZI(śpnK9'I$={M˨Aun6wq9 v=~GE:l|sA1q*!HPp{G䶁$$+0@8>Qa3< 4%Tfs -܂^Ukvj]oMBSՒ8Pm(+ypA9㩯Bi"$v̨?SKxs)c̿}hr׶˨z])e $TreyVƣs&O1\\xL<*sHaUEU#*m;ekuBXF:ceV[(y'_PU՚xH)H܁2I=ˋ ZvVtvq"`s"h^{Em>Ki${,4ܪrBUU*8 G@"Ԯu_6m)/5G86@ɹT J  FtaPWi.e\˒2NNxA--4=:/hm쒜0P_ZA$;VKv2/' J޶}+xgE;/mgRԥn rXԏΦu;="uk). tՅ⯻ҒO9uK}E#[p]ۺ<:@ ( ɶ\?D5{ăS]e aϦOԮl"ĨC6SFUѴW:8*D4{r,<;pd#\H <3ֹkmV5Z!qU7 ;T&@9ւ&(^4f@BPJ`)m Fu3B@ 3޶msj&;]f4<+)?ΥX8~Fp8<^<Ԧ棧>tbe,Fed/ mn.3zWV}佥CwbR:Է@CCGCޛGk7Z]Qi@<ڬ3P3?2 s[Z7 I, ro1 ̄9f1^%2HI o"r CIh6ȾL{d;m1>#['m.KSzcI/V7}0 Z6H<SԖxy^.GmA+USq5EP0(((̾"j.ơYi|h5!grX yn6&5[a=;R{u+[dV*fcǾk䶂Y7u̠E@ cȑ4!5sd4{KӞjB!3rr>\f//==R=fe-[KyLp2k$I7t _ōyL['0Wz{P3iugq5h[c1cB_'ֹk muNKnc!8=rkп6QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE`Bj*V{ӵ;ׅc{,, #Rv @4Q\>/Ri~-P1H(wA*#x64u{X\C,: ꨮPGn%I& A#KKq˨! eEy\Kk^ivBHmEO6dU  E\ߌu-Qr.S~w\Ru8e5KhfycxU">maIP84 A<}SkاH#] O +[A)4ON%G{uaUO9sJKi-t˩8jԿy oJԭo]WRѮ畀 N~>#ئWz>k!0dys"* q,6,yZ?r+R=UI>%(mvMQu}4[݋8Mr`;g2_:SF$ӴgS 3Z@d$$u!LK +R=Qj_ʭ|MѬZg͝pwyon+eC.c!4:s^=X\^E#ȨpJr@4F4Zڛq rμ8sJCj_Zq%, Iq$׵T6ws|Y>dV. Dmuutrrdq3Ot\Սݭ՝8=3At?EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^yo ^Awe F+[m$yF+@gdK.Vӑ>wgfl9mBmIL4/qg3N&:E@rw^#Iү-,S4 $r#$ALOZ7u toM֬Ke#bzǭvP". }ImhJQYA+w}o?(7V B4GlmCkf 0=0+jBj=m5EN0wn6c5O}o?(7V/{GC~Qt}*W{wB PǒvعaմneRf6 qV}o?(wwdi~բjG>*@ۭO9Y f s-ΈZjmJGrWĀ(`}o?(7V7w5&̚S4DDjy[ϖp یymWE+yGPaEq+yGQEvWE+yGPaEq+yGQEvWE+yGPaEq+yGQEvWE+yGPaEq+yGQEv޺O}-goL9>WmsƒэV3K_t|Ռeߌ’]mNK}* @T4 pdb(K{hvQsZҴI֖[ug H qQ@Ztm%d?t{9B_'ֳ+OB_'ր6(((((((((((((((((((((((((*_+m*K+ R¨Hd tWWX-2K+ [8;w-QsqK{:3=QUY,.O=H#MѦ[]oQbl&bOĉ'Mѱ Ps ,YQXԭpr:C8lgC5o5uh1$at]\曍V7)%x aMVIM&qŵ̱} !x* BJ}qҏhSg1pf0,#NI҂)QdW{FY'=(Lu]>e 7JEjs:״Yx&bD@PH'9-WgxWO97\rygH;FsQ/GN9bDTIQP`Tfv ;Kx!RDмGݑ>Sx<pFneҭK$r 89RN1=?Qk&+²1G gK[- ;eUd29 00uϰLc̱G:Uq1.m NһHA8+SW]Զ3OLL3GF3J<vL2IA3#9#H|5[XhG. ,y$S/Җ@,K'1S|tyS|tyS|tyS|tyS|tyS|tyS|tyS|tyS|tyS|tyS|tyS|tyS|tyՁ޺O}-nlN,?Zё.n7 E 0\b;}i_i+&c\s4A9PglF}ޡڜmoq") `@Z.L6<Ф%As)|Gswܻq@=( щ" &Nx;NG) cFul-./F%q} uz?w׾R%Su? |Zڢ(((((((((((((((((((((((((([[WVV_-z!bw߮bkyS;(z:`Hd\LJMPڱ!ebmh9 ՚T, ZN1UV` :$st'xux+n{6.B܍T@4=\Eg<+ OnVm)%{i*Xà% ?| 󊱧|AΡ:Am?-2ZGRɕd+($1N c_"WckV63Lw2Y6@%CPX a@yW/[xÐtIu4]E,#2c=I<띆T (YBG#P?? (=\Ezg<+rTP_"WQ@{{>a@yW/^Ey?? (=\Ezg<+rTP_"n0ŲrU^,@bg<[Ie 4UhmB[[y @0mǐ3I=]GN{)w4%봀0ޝ{TpJ0hPø# uagǪk,e8.2p;Pi_w2KIOqLGп ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (0uzԾ!Ѣf(;Lj$a!U^U k/j@%KŹSCH/0``2r0? |9IO71 %]D$x TEgmtuM67:&Omyjl[i##FOkM[<6k#Fˈ;4P C^sOdyض [MG̋\5oJO![-2k(d&vDPJSsڽ";6mFDkK+! @v'1%p"֭8vRBa=aOCbǙi^᳦׺L7 >!8w8և#:omH{Y`-& Kpsq]_[BM[Gc ѱcz'9׵5W7vYO cHe,AaGK F[y<'c>t1ʬҷ5 Ush ;K(%yM^c.Q=79wmw7zӿuV㔭|%|=7PōHJ{%zó/bmSFieD]_!NAGX/o9G?6_ rwVHxk2.mά2%lPe݀֏[ 4nw-MkaZ@^kvMG?QuV㔍67\XsͫOUa ^[@a3oezM!/#~.~$xSZ}v]6 48LbcӜhe(?[S{~!xOޣ}C.qh_5\0&PL goPAT'R3SY[%eo -am&R޻c~l:+] Fc,ݡCiIj^IM)icI fU+88C:敬hZj-nng*b59Rlcc͗GX/o9MiuVhe(Q\?6_ rc~luTW+͗GX/o9@Uhe(?[PUEr:+?uVQ\?6_ rc~luU޺O}-T?[U{[A!,p$eXhڍW2Է$xt{.R";OLZ 2)'E_Gclv6ˆ5sSĺxzG/_Okrxb 5Zz?EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^ujw lcH[ξmbDT;uuO z5Y'EtuS" u9xSioy%,.K~iǔ~e\q3$4i7\[-hTyt0>͑3\,wVghcs5xHQnKuxWhUAnk4(?o-% IQ 1g9nom{NŚ "2P Hl+|+_+]Q! O,K)g>;GK4a+KHD *}hkF{i.e )-ʳGXFbAcW/skG,Zl+%v.v2%qeiWg̬|*rr=W~mLn?bдfa@ۏgi lc-mk54tQ!3%8f~enMqx泸BЫHq?>m5ٯQypn/?&'&Av'8Pt9bkFGy"JC:p~#;zg4bXxZoBG}v#yMFH+V6SN֛I4涳[fkwYЅ,=x< q żF1HXFAczf< =ea$TpX8,=x_#WմO5(3j[Bn1;Qd-)ۏv8zֱ-. ݚ8^},Q\x;K["LeDa"+W-DKbneeR7>H Z浍_l"p Hӭu 3HFxd*7\ƓWkC~o\4uac,2YoYG<,끊}G[q|-jXA,i$p)yfBŸTVyekV`$eS0b0P J?.wGy,5Iܺmr۔Ă ;%,7&|Eᨵܬ)2!H!I%zxkHѬ4C{-<˼4Op3]+jb~ wg[3S J-COϕECxh<m45oo}էXgƶ<>eu9mEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW} %ƑᶎJ05"|wu{& Y, $ ^BC"n2GP{E!v{*W !bv IzTV,ҧu=b}-QvLm+HT/޹;oWZ VzYKn~d=z7Y[K Ӯ7#@ʗ0}h.w9 \A,g}5E[XMy-omNH '54˛>H$d E$T\Qz]gKtX5%}Ѐ$`"H yL-%Z\*JӰHa'B89銳eKWO)c~q޼BnJ+ Mj ,Ke46Kq3(C#8+e{SƓ7K$1w&d#V f=!m"mV[J&P@$6pFHWZ.jյm%̖Qξv];v\{髩h}B̺#dl'uKR/֣. BD+4RA GizņEg{g= uo:sR!6MYg. Lv܃ v’`z -txsCz{K]- ( ( ( ( ( ( ( ( ( ( ( .1UI]]rԿ褠EQEVOfVOmQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEU W4uQj&!LgiLtGNEdzMJךvϴf2onV {-#bu0 A8ER5}#L-]Q >zV;<˶Xny=Q@j1uZjR4hteIA?[Z]j61E)&p`ʞGNn(tQj#Os'ֵ?XK{goq" ,aLg?k?./PV7[Ch2$ݥe9Үm_g-V#? ύGޥ>6Eh?./Q@/@zKm_g-V#? ύGޥ>6Eh?./Q@/@zKm_g-V#? ύGޥ>6Eh?./Q@/@zKm_g-V#? ύGޥ>6Eh?./Q@/@zKm_g-V#? ύGޥ>6Eh?./Q@/@zKm_g-V#? ύGޥ>6Eh?./Q@/@zKIynnn.R4y0Xܰ*Rn?./Q@/@V#? El?./Q@/@էS?./Vok^!@5$H$*8aE* T~t_Ώ:?@QQ}:<IEGGt%ΏT~t_Ώ:?@QQ}:<IEGGt%ΏT~t_Ώ:?@QQ}:<IEGGt%ΏT~t_Ώ:?@QQ}:<IEGGt%ΏT~t_Ώ:?@QQ}:<IEGGt%ΏT~t_Ώ:?@QQ}:<IEGGt%ΏTwo"OZ0u_NtJ`%2H(ҧJo??"QxI7Vo:HW{XS$c )mصw a@"E8 (Z^bIM`M>uJo v}jgj6bL$̨oHvԨ^EՏ&ξAԧwr|Sq?XilCi z&2s0dn7y+$EeXG5x[M.o$qbEԓ@*-5rl-%qdyuhoZGiy1L1И'E<Ē̚ݝVKVx]ټvrsڭ?2\-snX h NSvn7efu vf۰kA4^][toD }{G;wRm7xF6Fr2PslYTq^g)e?Um2\(*GJṣ̪~ee <<ʧQQpṣ̪~ee <<ʧQQpṣ̪~ee y <ΟHetnC)5?`֬5 OB`b4`u,20@k#]5'"%_i6 "ޭPݲqZWw(+C=LhImc$woH1<: KztX/;YDlNCc![OY5;m/OQ ^IEQޤ+tRt8# 85wzEޣi&Ս6Jx ŗZ4ڕ]BhTBwzQW@=嗋/~\Z5ge,LDےNk޳mkڍ|iյv9z5curBd'Z A<0=Iy (iR4<k>!k^^xS䶖+M,*ؾH9~3*MxHZv24'uǣw REFTex.-džp|V~iI^129J}5)ˈ5/cII툛AAE.nc<+ojV6$Fe#Ғ&' p9*(Gyoxi>&Z^tcmq@6]O=kxg g&"8ـg\ⷉD%=z,NxKr&Qh4<Bi03*'siWhe-ej2y*_ M.LvhIP##?BC/A^{d P snOjYɩ X^;?)e0v+~6Ao>%dцU)ex[YCMM'my|(KVGC*ۜƻޫ4R ު5]_=;22y;<<ʧQQpṣ̪~ee Fv!+,UO8<.Bhz8yn̶,|ոhrAX'=>Z^j3܍fvfiuwF펿9.6ʢ#I+r \ǖF;)rGY6 kͶO덟V̞Cth6RLyPħn;vҋKȣǗ 0Y.HF&;漦{GPky~XR$}O_F7R] W0_ڒxA 4O[_^]*X)^&˓{?¥"˓{?¥"˓{?¥"˓{?¥"˓{?¥"˓{?¥"1;~K5FU^U;Xdgͭ? /Q% ϵ_ɷkoH ;[~_k~K{ɷkoGo>€(oo? /U&}M[ߕ % 67W+y6m~W((ߒ^mQϵ_ ;[~_k~K{ɷkoGo>€(oo? /U&}M[ߕ % 67W+y6m~W(I_%pNdDʉ4Z&$"1пm3޵ oGG6/Rr/GQl_O֍cDggE}?Z6/G+dK{{[b~rDggE}?Z6/G+dK{{[b~rDggE}?Z6/G+d2{{[ qo:# Zt[hb1]4b~rDMidZ[D1)URki4V$dXeA Rl_O֍Ùig+Z[H\JKC"kHg󡶂9Č18{S/FhăOmip-?~(Kchkt*wOzb~l_O֎Veŵnf3$a?KwB;xn#pYP8;b~l_O֎Vh-r-_,oe'=3^l]ME[%ʒ[x_}:ؾ9-HbDP8qKK?"H~ok}F0jMѱ}?Z9X-d!6Q1D֗ m1:px⟱}?Z6/G+2+{v9P2P)֗ qmd1錊~hؾ9ւRkkilV/0犓b~l_O֎Vdv֑GmH,`8f6`TA b~l_O֎VȉlVlNK֥HmeQę(Q}?Z6/G+dK{{[b~rDggE}?Z6/G+dK{{[b~rCdLhcdH9Wݎ`S/FhâP$ TB7'd`Իb~rA4qMnHaso $c5l_O֍u0RTg&6lu &cR1ֺV.31u)+Fq4ʄYI9 צBP+wz&}.%鴐^1|7R\? y7s-畡q)|N&mЂI©ay=#mR9=(AcR(aEPEPEPQ9/RR_S2Ȍwyh\` oryEۖ)o}sۖ(ܶw7@7?m< o"o}So}sۖ(ܶw7@7?m< o"o}So}sۖ(ܶw7@7?m< o"o}S74CT4}ZLLrF} ۚ{xYp,.!J%V)?-MP71[Z?Ky3KL2р=пw=V%mұ((((((w^(] FFOn570$f=r\`:wOI7mcu''(ݓfg.cuwH+<[5xF|a:jpKbhKŸۼ`c^{\^hܗOY%*[cB}!%^}/=ފ?oeM:VlF9n,GA*I) k<q;o3b[T֢隆,smvD!1S܂9#yG]).G\_mK=0 ͣi܁ SӘxKIWŢo%y^Vqnh[cE+WZ[t%qT#p0++Uӭ>Ѥ\tֆLQ,V)Lm9p_simqjӬrb6 yi 2rki{u\go{)줆yO<< w shд*ԧ4 |/*PmG׭-ﴔCi"42F$<~En،TKU}rJHE<@VBpshWBgy5Ι7W@d1=0jM FOiæMň~dFqҟq\Z+ɦo $E5Eoi9g\8$ԗc&KQIXȄqѢ|̙ g G_J+w%GIncHmHž¨aSӥgxC"m5d҅xSpęlߘ3htGQ=C"ڴǔ'Z6et۹bvm%~rg'kV+Ŧ$ !Hr Alw׷\3IY?T <rOzI.?(䶚=xjRIpNM0O+ڻ0q1R@3յ=R>L9 BQA3L) OK /c%>Dr1.XA83>ᵱM tg&Q} Gt iEQEQEQEQEQE-]Mbژ2QG(J+w^8xeVݙdFЈ8*AzS'ʮ4tZޣ&EQ_72 %u{U2ͭ_ 0s%Vz[xK&tM++"c]NvqUϡGMs# ;o[-n!y_~X(x/ҭ̞1YǬ%fpdPH=}ڵx+l|M]I.v*z&=k_$?k0xQfЦӤT$Rβm$ox}+ -of6ְê0D%K~Cnw?k>{RN^x y {vWX쵷*:̋ Sr11:m㎔GMZޣ&ᏊR RSks$WܗS|e $m_pG iy뚭΢v#k&HYXQև_Zޣ&EQ\=ZkQZ޵[KIC1Y+ݯ7whCw4+YQl [;qi-[oxDuKk[TYB\˜0^s3GK[na}Zw7'eRd <-k?zai>>$6;+ay2#>#=9湭s] LS ZXUBʶʅenaE%=YZ~ 5BMѭ5[-6S9snq^yjkzu fŷSA%<GTs iq_OyMj_%Hm$[~a*8oq__EQG"ֿI?-fN%յ+{X{9  czo;:۴qk<[•AX.ci:5ŭ=o-FU -*R_Gs,7_)k$qX5oVtǪl<䵑rT0\W ͦw~2֗O-iZ+/)8Z-^5YM*HHtuԧ̗(X q$͒VѼA/+ZO$1˨N|)QnUY$ .i_Anfu/ j?KZ^_hbLZ'Nߴ9qp)$W~\@P-bFNkhW ^"cXP9$rIu|G3m?-\t\Zv xpH Y6Ċd5t4a'soԿ%fu/ j-އB5K:U՞q4e2̠`+goԿ%Ԭ>6kPAqyX)+NVKdt"}[Z nmv'outXoWu`!GQ7kei4:j<7e8e$w;T6VfL5)ď=RU"x^N0_Ew 4L'ڻj 5W|E%֭^|Ne4h9bc?KZFmR |Ez\3$Eogn| ]rO\յ4/fUo_@]ŘQhkmaFmRoԿ%#*m<B }Dmnoݦ$x]6$qz暚e֘aΜ gfǧ п~2[E\пew=V%mұ((((((חֶ~X;cYT݀՚,b6yoG6WY> *~>&.U.L.FI<Cr ySxZ_5=*8ŽO$W T)$ִFX$Rk0i"6~Q*G*GZ[.I&x\ed+b*;BuݽSΑSq<%#z]_So,N!2dT ;O> wz͵Ζr­Q\BřIH+=1>6=>#i@0˜ sޮcL6p5#n']dI>Q/p4eKa#ÈԶ9blt˙l^10Y*/H4 \ԬZY+sTzo*dl\'5Zi*km.fAIPe ;~RsUKe-q^[=~KsVU$+46K!J,W7bsfA:$?|H'/aNԶ&H+gJc.u7tJ{zyݜfu2RYu4Wi+.@y gW Oq%͏͍ܩyͺ>P ;~ lF.k>xj=WBU&&p"as>5qkm̉CRza>e}&kX%٬P0=_C`.InaQwIi"zq^Fbl/ki~ڱ ߜ6 z:=[Ai%î&R쾠{BX$m#D<#,z\Mwqr|:F\X߸biWQm2 bhRvq'4}CWc T]vgW>mm9-}B`Ke :s9 `o/'5,U]@,ySWƏ$VjW70$JE#M ]{1\u!39r1)Qٵ; ɝ0z)^m4^ӣ.!k=N7AVB UoL]|<[0|2y_BPi-㹘f8U˜5]8%C C`g<s˿ jrj"VKx lF"O7HxX,8=^C=}muk5\&VS`(QEQEQEQEQEWS\u4V dWUkm<Njcz o??"|d2Ҧ wFK >-VS!fWFj""O0}}ketw5]JS4)03D%\2##ɲMEjhzΛᗊRdXVsy!G '9W-1=;~үlۻպDЎ ((cAh:l;--Eq m#8*Gڼ=+Ei1xY`0Ig9z>]_ XgVۘ sGGC:٘RinaCCSׁ)ej c&?}? N@#<T-kn ;Ax֦+ekHYcsO x0CP4P3TP+*M0Q`&"C ~amim-XbvJEG<\Fc%v#DC$K$QgvPJd`IESwaHUCK%['f@JjVrr#|oJeyI/匒F?N*vv$1$)B=ay$Y$I5EG$,Ro,Dݔ8=*t)s>XOSөTPxm!ZAUOZkOq=EڈbRz*t;mI-ͪHdb2 H8E66 +$jFNO[* nB Ͳ sgPzU(Ӽjyy,j-~OoZٷ-]Pǩ8eP0@*ֻ:(apbV*APq+N(3Fq1 6 zgҙ-Ҭ[$љ#jz(mm9'3v@Y~5,m#I ;93\U(2<%)ٴKZ[)p­Q@aɧ;8PUdp88ɭ*(g_趫?O_趫?c]h[z{GJ(((((((k" )|Gu[oIy(ح]y{ӹL+6WjAhvE]2|ÎI `tTW^7p05D{xbFV.p9xSI#/K8.q,k% u,[v㍬=2tz1E"@G@& me&W.$hUw]́sV/ygm麕ݕq= R"X`(gaY2xsEVUj &2:j+H}'SX;!| \9\B*/ Wg[] S1QړvK(e*#( (t^FĖˣZ]!im "F^L*JUa\못[=w hR>eF Еnksy hzŲٱYR-N36 =ܡjLJWuqiZFu*!US;<)4tW ?,|WSϵHa-O4T߹g s*8#ItQo&dfUiW!Nb9 ZDUE G@r~:.4"huR('ucel2=kHrYMH10(y9E O1N: PgQ\ղ/R[ϴK˹љ!Ri 끒$3C>LUB1bwۂwcu‹\-n籂L縺I[T4F)|׶:VU6D%cfӓ::( ( ( ( ( ( ( k1uGX5Ǒec*ַ\Ƣ'hwzV{0^\ȯ}8~'K6>[xm|'h W)}_8H[lQVկukN&}0<>jETK}/R &Q"yl7ˢZx l֒]y`?Qp1N+K.;mfV^$M6ٛqS8T_ϣd%Vh[`S>ΧB XäSHa4'F"86~#r]xޡjyl,yȠFH95VO^RXu5Ymr=1[2X=}xHy`x#⼋ͦ]+OC_쩚$'8} sy W]w~)t{Qmc}&{H"nTrZO˳x[#垵mַI"=N z61_x-/{m"nWWσuc{?;D6LIYF1FNvGEekvWUh9[F8Noon{D7𵥼'4bO*]8FF@8${\C$(Y$lĞ/tֶQKZ̓>C`9X%cfD(K{z泤>,4[GQ"ОԀF s(('qײxV>-̗WV]! w|ہ$qpڳ'uszvC8*dgҥ1{zmxtZ[iz[G\&B1.㟘1Hgis2! [vݶxіÎ v}%} 7!tc p>P@b:ŘmkoB~ۨ:Ul\BҤ9SsGTMekP“B4,;lW+׮>ѢBR?1_>jm;RA;'vM,}0DJWi s+GF=,ѵ;6۩h13@*.H KUwh{]Os^YDn?J@  [<xrF;CLV^[6\;~S㧲LƗyc2jۀRd(3H MP-嫰TOO_6Zw"$wtdSr'H'͟F :,2xG4^[v 3-4O _kv~v/YqoAw.zgxB[ ;Eb*ߥ Z^}vX5 zgQuncwQ>oS^O VEO.:U7JJ2Fb0A#<ݟŝ:?iӦԠL2>[u"]PdS]}Aүyl,ԏU]C[Ӵ/.K pVmV92[E\п~2[E\п {GJ?bPEPEEwq7sGJ^Id`I$j|b}jmUy##g?7@h((M,pBQgw8 $Smn!XE F((^6ڟ=e7mNKC|ۉ |zzʹrY-yE6&Lort z(? VB /]7lyi}'6$#ÁXкIne[KZհ|a{{qEVļ>mo5ſٮ[# e쬃X 8SK.iVk%$bBZh}np~ VCNRjsRVIe1wq8ݎ8[Ry-+r鶶GunےLs uz/ OCpRM[xFU ,~N]s#R4v-BArFEt9VaUkާ+xbR(&[Y,axsl8lz ^6i3ߦ/1ۘ`-Q+&(dxJSH{{GvېP?7^zt_0뚍Io "}eo12qO"(7X ;˞O'b8-Ucp I%I8%"šwgVXiK;Y_,Gds9X9)4RKu,%g1ڠ;֟Kk;˨nv#Bc3r$ynY/t,;^)]{,;Wz}7V4.z84 EC4<ՁdM{d![QHK쫀X/RFO R8,CXX}Yo{ymal[($d9$ r2:PEQEQEWS\u4V5aaJ*]oͧ]]ڣ| S|Am}ѝhZ_4W;ɬxZryvL`p웜(HS]QE?#Gx5d$: V*z-QF<™qb+C~_imԁ']s '((ek%W[@q)HhtSX ( ( ( ( ,E žQaLnjEQEQEQEQEs>2[E\п~2[E\п {GJ?bPEP63;?ݎ5xvK q%R)+8>*" ?bzٯD[{ TȡdӬd[Eٖу[HSwct+jv-n!qiR\lR#XA@;}Eh/ [h 0&9]x=c>aZ{&me+T[͌n䞦~&oq3f`x$&X{]wVv3Oy\sPtI$w{hXڡ1Gȧ*xrh8[55}J{feb|D˕=Tf }>$6Mw 0ьE'$t8GaN7-[#gzT>f-gUՈ,Hco>&mE%Tw Hj[ MA4Q@џ)|s^Fk[{;x\$''qU-4-&K4[dއƏ5Kh{]Kw3Hp^R}rj=CPmtN+M{VV&g\pzFϳoE цW:G4=$ iujVQm=`f;Gc*!e˯Yz HTdczWi~~k6 "_G"y1+NxmCo4ZRU "ҿ_`Ww;zUu[EgzȨp|b% Εo%k6zBouOΗ$Z:vuDM#0GiGАzڥ_VGi wlo$m%tjdusvƞΑ%xDmi;XV# Һ*/_^\-5dmH/l`cߵHV\3A[ا?xi,ihf Yd)$Yޭ8[j?xNrȶ]A:4$"S승$rӼikڬ]7WQ0uc$LctF*մV4KH=]E~*;HÒr:?.k\Mo/ I,R1$ lV*8`S~W!𵦩X !I 8>|Fnnn쥂.@JWKA\&ΕKzבchĒ"!A$jX9t o!COۥY\iTM~ީXH%$'+P\oOX`tn +Q<*|?_j~$i!si2e,=$]);A8 Hе IM$l"X>\ǥjAO͢\F,-ʙ78 0cp1C?^|0:gٮtx&pT˹4g|*N-LԭJ]:[ 5[]Em﵉/>"\>csSR 跏uG*Kā{wNr1V1eooy5Os-X*Q#iv݆eV4Z-X\t |Ϙ9^ y57DS5s dm;ss ¿kk4Ьʱ FpF:\u{{ ,#n5q{F |ɂs |uhA+n,Am]Π {sz7{-:I>lזI ^+^-W}tm"?5.{圩#3XiC`b3ϭ::(Q@όEmW4/jEmW4/hƻ+W(lo#3 $uw=V,k]B͗ ?6^2/_ ZGڃ]$&F1dMTs7 girGrNѭeplB]F]UcXk5 池m%J\nTH` xv/ڢпXۣ)Ede9Q]}le9Q]}le9Q]}le9Q]}E!s"oGA=E$`36!]S?T,q}sրg863JycGU _:nw{otPݤXmr@|M/tԥ&cvEF\bEieg8qmdyʏ'۾q>՘|m diАc'8r; F'qv}q>vj13B/.VH y K2 ʰSxWu]K.s"RFXLF=&DQ;w8@bGnݤcpƝm:Daoi7c bAB2}FQ &9mqZG}?Vb`w;,@Izn{lQ˟`Oz[, =qЁxK<(dKT:wpv;{Ɲ>74ZCFgSϤ?]NMF-EkLiu#G露ǵy֙So:j2R4YdeH($=O4-Av`? S}!p_M~T~kM d*e{h5k3H]N;x-,sk$@x޹0FFy[T[itð7n+wf,ob/]G)pJs9­\C?M2-gP+1lаnX;OwoW{kmm~#wzL:дhO>MR9s1xgJ4:]^hKYUc ۜSMAfyⶻGwݓY_ AiɢǨ5+PJm D8掾W_:^V`3%ȇmB8ҵ9$IBhdCWZӮ}4k>ueoksrP8c}E|D͠"j1ݦgvy^8n˷="|o?E-Fh"{)cWPIT8y?zxT1iRI-\3BǃP o:bQßt?Ehd= B:`2_J5cS7P6%xjЩ$Ra@&D/t6 WP71[j7$c#<(;WW%{/CcRc}q|5Z*@d /6r:M _1[ h\l.6}*?ڤ[U[ SgXv\TsYv WXm3 Y7qX i-? tBþuY|;us!9n\HpppͻҎHmM=VM3魪#:>ҨB5mH-~sN0P`y$/8[De!Ipc& 7Ht[Byi:Z{B$8;I]o]ȭmgeZ.EjRI`X?E'OJ>"zծ弆]|]2R4g9yCIn$7*c2 pk;fڽzjGy\L6"ϖcRpAT۷ntī Cl,5-BYlrTݸy|6ujݜ9b# :g p5h}SG{"yʌՐGH;COR ~ ͝ I<F%Œ$ (Ow?Vw60,~lhdb쭉aF9^\{=[\gWztqreWbJ?yzt掣{[6-jvd[7Z| #eeg=?>Mq)-_0|;=W'MzKha ^ZFyi%)\S:_P[aYݔm=rp:>$X^]MC4rC2dGcvzp} YԼyɩ5O8GyTć^ux\uY4{mFuӖid -eIw(baxu ׮`U=x[=#EXYA@6ޙ<SD58:;xB@K9FƯןZ[x?㷇n5ҤIkY')s wW g߈64-gK;-˹cx7b1.[8(5m&Y.N|Ȼ,#5s(zk\xjD]sSxAMhOÚfX-@\LP :|:c_\j~.|I0@\ Xy+j-B(g_趫?O_趫?c]h}ZdЅmkҰeFp\9xw˫K駔H24jcH$$c>q]XtWY>q +7! 9.?_Eh)q~)YnSÞ6!|5IM4[_h9fP,Z>ޟ$i'ޑrI?dQ.?_E0:+FAKG5 \߸hK.?_Eh)q~(/j?q}QѨRQG/"'uOiU._e$g*Q<$4v 8:U5k)~q Jim@ \\̒(ܫ'';m#W ӒOdj?4??q@sxUc*ǠSDjvG2 6g殛zL=-nmX! GrB+#Rӿc{?ږLy;.4YWS9ŷ`^"ݻYχo5K 5bEUuK\sMoW-?LyWH ( hGJ܀1c>t h>{knvBSQcӌ˼c4?a@wMg{{~5> v^pw{ RSNZG5qo,Y۽J2)BcU q\T>ºo$Zv.[<h0=:Vz ? iZiv2-Z5i O5H- FWI=?|3}2(^IgJ]܁%ƟhOG-ꨮWKY?Ɵh_-g~4;G??Ziv:+OGik?#ꨮWKY?Ɵh_-g~4;G??Ziv:+OGik?#3-j7k#KY?:ͬ6 LV?y@WS\u4U/ H cKGFXc*B"(BE]*?Z@V]f'Ou}q]>kvY<Ɛ|냐3tNYLR=H9##OA!KQ]2Ǔ۰F~l}8{χZơٵ4Vi3H=s@to5EZm#XQQCϙO]g 2J욅3][!4܆ 6ArxWQYնshn4 I$`~-&,[MkΉڭ#bʎ%ɐ Ͽ7~Dw5,m{  BKȖKK(#vVI2elmbm#ϻHfh] +[ޡ;J"t,DN.3JW[yO)YjQgu==𧀵 C[i/ %!nRB2>㸪x9ؖTv]OkٖS,m򘫌y8*yqֺ +Zy$-73L6id6w 89g?@dž<nPPf3́cMa|.uZ.Vl cX4o[MMa Yv7:#sY^4l~VbP3&PbGR;V|5= Sֺ$Q,BR2YdQ*A{o-f0FX4v8:S-?ml-%!r䷝<:f .Rk8 ʻy iok>!:-Σ\^zywcj&7eX8l< þ-V&%RĊʁ@l&sb+r?O][[OtmTrUBۗZixY1LnBG8$dt^q/ kMG|͌'}+ߓǖ"<^Fcb@E;$sB %Q@Q@Q@Q@όEmW4/jEmW4/hƻ+I N\+ʊFy+? r'?*O(TG#?߼?ƶh oG,yRX~٢1I49c kfr'?*O(TG#?߼?ƶh28#;GS{ lSԚp)v\FF!Xdg>?+Jj^?@}|mأm~?xx>a>6Q bQP>a>6Q bQP>a>6Q bQP>a>6Q bIg+k 2CƸ E MZ8[ 73׸@.dU%2 ~J/1?:Q}roiooo5bSI>z=aqK@:WG@V>`_i@:WG@V>`_i@:WG@V>`_i@:WG@V>`_i@:WG@V>M@-eee\!K8uIm[Aꭓ'oJg$O_XpGms ۘTyӖ )$$u^ =mXJYYDX.$y:vZn`h۽ E>֨&֞ne6jlo4*e@ *0; KW_|E׵ F3jdk8MHx&}G {&Qdm5FNv42s>Rhw!^\ oVY{dsOMȓź=Ư]GovCsJxKEg;d\"Vl!" $޵s-B'F1O&͝X[kR[zP$qedam8jߊ,7WZv%Սm#n!96c88+nڃZ_[uͬb2,7FF]z^v$:,@ ^`K1#ҟח/^>&^+3miq 1E0i6~gU7sⶡn{WFt 2xcF6I]N8H9 u/G{K 5%Xg,,Cl3+еDm%8IhׯxK2.47,f09$ /F/Civ2]^b&k n%Bv,u> 7UO.I~ ]?`1zXjz5ݕΆ1sxW=k6:fhņCƀ+X*CKP ,:4xq.>Яt#۝z`'tMbPmsI~Y۸s+qnSqՈl0ݜzez]CšŇ'|9N% kBIeneVeS[Wp\ _!jwR(€I8OzZs>2[E\п~2[E\п {G&[wYIQb3gk{]h3j6D0UDI' &wSIEcMFQt &wSIEcMFQt &wSIEcMFQt &wSIEcMFQt &wSIEcMFQt &wSIEcMFQt[yRϹ HأzT;m>x彸Bp2xd2lt|dؑЎeg} oJ4O G%'t O+PV<- yZP7%'t O:o'ϕ _?EV<- oic}ks pa\;+GP}roinanbQ(1˻ SE B)c `lxF[x}8ԟhӿ)?ƲUtV[[]23p_[}m$ A;hEƝI5ϭ"[}m$ A;hEƝI5ϭ"[}m$ A;hEƝI5ϭ"[}m$ A;hEƝI5ϭ"[}m$ A;hEƝI5ϭ"[}m$ A;hEƝI5ϭ"[}m$ A;hEƝI5ϭ"[}m$ A;hEƝI5ϭ"[}m$ A;hEƝI5ϭ"[}m$ A;k;z7HlGUV bPMEϭ"S˂6pk1u伊IGɵbB;HkO[>X𛉑ђ L"\zd`g^} JCne TZ>"\{&+'Kqyv;Dn<5{P}65[ɯ_MJf;!ǶYQ{·[6i[jwF#)˞{1F^Ot5yexlf9Ec{%F?kgh1ݥwd+szeOK/Jݰ1c'>E(Š((((((((((((((((((g_趫?O_趫?c]h}ZdЅmkұc_((((((BO ҈/sI!]S?TEޏxqqmm ,4n5[ͣ͠ ;VhhFU<uV6s]\ĻlePIsv?|-][Xsq9qC#3~zz/6thN}iQyxۥqz 㩠 H5OP?MOb#L=aqKQZǬ?/h(((((((((ȵפ:GZzMŮjh[kGX*?Z@5Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@όEmW4/jEmW4/hƻ+dgQet H #w=V , N\#ʊ>€1ŬJ_-gVvt+fg~W(Z?b%a/kg7N ?Gn>€1ŬJ_-gVvt+fg~W(2gV59 'ܗjqgԮ}2)v'thNP$X\Fw"a@/?:?K}I{x2$¼??g~W(KZ?QΟ+c#GYߕ ֿOGik_#He_#€1Zr??g~W(He_ -k~t9G?:?GYߕ ??g~W(KZ?QΟ+c#GYߕ ֿOGik_#He_#€1Zr??g~W(He_ -k~t9G?:?GYߕ ??g~W(KZ?QΟ+c#GYߕ ֿOGƭ{cqk-Ոx&+j?yךHe_h\>OIVVX*Bhu5WS@~EW_?*ƷU!vE騢((((((((((((((((((((|e:/|jTe:/|j@5XկvOVֻ+I@Tf؅(_It|)?%(jM]7? o"6_It|)?%(jM]7? o"6_It|)?%(jM]7? o"2_M)qyl6)VxĂ)f܅d$lQр=A67u*C!]p3h[xxHt Z?!%iiQV$:_?4QV$:_?4QV$:_?4~YԖ:'U3+է |A2[E\п~2[E\п {G GUI'oұc_K9GC+8/R=Q_(?Կy!yz??r (/^@{ŠK9GC+8/R=Q_(6 ZC* ltrz#s3H#HPҟ'uOiDGZg 7G 7WwQGxK?n(ϵ7tgk?cM몢9_g})XSꨠW;Yk o7Gv>?n(ϵ7tgk?cM몢9_g})XSꨠW;Yk o7P_[V7R٘`LI 2qjk3-j7h嫩 ]o??"C?c[XfUOE>Q\o^oG^o@14y14Q\o^oG^o@14y14Q\o^oG^o@14y14Q\o^oG^o@14y14Q\o^oG^o@14y14Q\o^oG^o@14y14Q\o^oG^o@14y14Q\o^oG^o@14y14Q\o^oG^o@14y14Q\o^oG^o@14y14Q\o^oG^o@կ}?*4~iG~J>g~J$ē3UmgҰj.\|fU97@<H/`JS;x1.j}s:P֓| H@ 8_3HyG?4Z,Ȧ{4дRFX8 5}?*``G?4?aoyOʏ3R!L!Mt>PB\6{ir`% 0|#Qi߰ҷϧTnu{{mRO.,&W =v }~jүDCɮΘ~EP~EP(((((((((((\ƭ H<Պʿ4ܺH^Fz'cM'Cm&[n(lNTiF V pHv2ä}SoM8ik*T z?m{}>>? Q 9`ⶖ< ;$ }/"ki8f\1"F:ľ է2]Egw}goDđY&_Zoc%?w3;ۯ n!R?J5 Azc?I~kw G~%x#[o'z߾+bWHݥ\LuNo,&Gp$ vy^ݾF3@뱐S t͌Eyviz5̖ixKfTw/k-4Zki֫!a$۶oqܞHS# ٲ~qNf(1_+IJ]Y)ӣ⌿4L@#elZXdM aL]J5Ѹԕ-!uXiPi8v(+ Ҡ]Ί*ߎs|5ohZ<QG|&5(䝻NBzJݻz|]52?"b֯k$)>d#K PpG>fyJaZfSlbVb0ZEcۂwc!aC+>$_n}}5Tk*ɀH©/A{Y$|ga)eO~_/fxOkՁ6[W8Q^Yiv_dV2Iv|vbhV7Eޖ#״>r<_>j^ S 0?IXmzE?HgiҠլB}B'\\M+ܣ ӎ[)ltR^xjxId or~S&? 343 364 0 C     C   5H" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x}.޹ MEEj>|Wjk/(Q_VGڽW_GGڏ">Gڽj>|>}z>EQ_QȬ{z,ڏ"EEd}ޏ{`5}}/+#^}ދȣGYjs4M+Wool5=Z][B]#Gȣdv_W|7/)`:FEuofEI> !#;Ax^W48xxYh)n-2(eI6.Fomms$eSx+? ;,e7wiH2ңK_mlpj>|>}z>N5}}/+#^}ދȣGYj^X EEj>|Wjk/(Q_VGڽ^2B짐,Ac+Gcg%c/2糸0\۽g˚0Q>6o fO,.w1'5N oRI=ʸl=8d+s< 0݊(1ﴒn? 6鑑jmбsBRpF@'zk^_Mesi1;6Qd8ی8($ ֹga;ح1+@8@x[s>ٞ]P=^M/{_mù~{_mfqI'ֿBܿ= z?ܿ= z,Oi>sAU#sAU#f?=}hIПw/ßxw/ßx0GO~ù~{_mù~{_mYoZ>}k'*oG*oG.~{}_?_?W[=_?W[=as'֏Z r9*r9* i>a-[Lе=æjE ݸP~b+Ow/ßxw/ßx0Dimu[Z-]a /IH>~3x-meȒ;h^ڲ6[2U 'vG;UVG;UVE?}˰^ׯ>,־8WUާΗ0Kl z>"?=Qބ4,&t_;UVG;UVK/^Oi>sAU#sAU#ӳ i>}O;UVG;UVE\I'ֿBܿ= z?ܿ= z,Oi>sAU#sAU#f?=}h7sAU#sAU#f>Ҽcsۥ ! Ӟ⋝eV6+u;g%l}sAU#sAU#f>,Hism! p=~UfcvL뱥nz*oG*oG.|k:W oz?*in{qO/*oG*oG3aU+BzaR-KEv}?>)nfO2yXh_w'ÏxcT<)U$7v>+S2֭cc2Esyoo+ԖUVo'ghdU P088U)}OD|Go-/~HRVX$5ayݜ/OR Fsy7.B dkyWBٟUȮ!ځ g׀i_CMC>jZOp > cqw7{uךmcNFҴ{ȣXʾb dđw~ОtfS@ݳ?6=qT~8u{uZ`\ʧH;+5 SⰗxV|!U[?iK߳s;//\l0` #F0pp'ZR_^XZu46anߛ5nS8瓊ڗTլ|qٯ~.5( b-֮QmvROŏ>ω=B ^=cL}6;Ecxi';~`xkÞ?к[c Ə& b gK 9iE_o?}Ey/?<7u߼P(t6K1sZqv`U[NLE{˸-PV=XMxk4J&R[~`bm.b8 _*x\]~McjvK-1;y4(i`cEkQhW_\Fukd5pRIK`g5VKv{SG8cX*s:זr 7CZv+ }+ԩ[DqV6`$($ԚZ|<`'{;}B&*Z<6GAO`M.wZjZZ[ܺ}Ur\*|^9/t.d{KMGO<-o#6EmK|H 7S_"mJI+P"b4YD9-I'zRWW^Q^E_7?ΟAiqic-Ɲ9\ar$?Ŀ>ŨHqA3# RO#unt3z*rךuX I;jJ*ֽXy:~Y[_6B־-F[Oh^'aed~}v&>}~V>[O-dF}==*JW0Fir4Hleme:3r{e㯋"[>TKHym۴JJwki~s(h^Y=K"2,:#}wu-Xi(C p}thۿ73 _"MUʟԾ]s ϸ(yEW>7fW4ߛ-"wcd_)gxǶF~5|ZhwW g`) (u:<*dp]1gqh4+Am< m$AH4vtQgr1КkěojNtWZ55ҢlaKyioZ3mĉ2nI'47/ f\ XbA'8 ֝{+{Iݕ^T^A UrHZnq+=/@M#K^H1g_hnbp=j[ZG m/Y[h\P_~-#ԬDHBQ7='xOz*hƹ[åCX),^DG]k$7๣Zv6acSB3(=dVP+F1-4yH2883wtvl,s=qSA*2:#G5MV+\2&! m-scMf- 'B-L[ZFFVe*ة#̰G2 m8Re<շXxf;Lp<ۆQv 5K]gӴ+He{ce]H? ТiuSsT/t7Q,/`kv1X-zY[ܲȀ4+69h~6ðh3^MZkh}@ ^VqBWv[]x> mGLcI˄$ FNhPϣZ:LYWǹMz#:?l?Ft2;t|9:o~ge4L<.9cV Ѓ53&+lmkof) JkkMiyW!I!WSzV+j][ܺΠ4+<$R?Ӿ#I$lxxNK9tk6, 0J6Aj |uiKYwA}ݸkDž5Z~QRT;]F]JwspS`tgGM'S߿"~GI𮚚~m 8RzO$SiA?cV$a;"@??8;ǰ"`U i}廲qx|+<:ԁ!h7e( c}.|8tc͗m}sלxCg;'LծR@ ~`z#:?l?ª_l];u, 3)Yw-nygGM'QΏ@OMX7BҊ/l|CEmvlp{ *@Z( Ok76ZΝomE4}^[:Ŵ0XXvw^&5=~ooTc,&bAl&A= < +oEm?K՜@h|ʹ#+}EER|OK>s5s1o;=Ci֮?\M#aJx>4Zm:Ute뎼WQEcx?>>ݾ^$usOw f4q j~ ׯ4KvuUlhiRVr9{WP}o-_>Jͨ]vJ#.4i2^HiZ1mON}U<)⻍[SD[d[̆tPI+1տSU)??*=-/"^ol(O[=GGQ B#\e]{FBϩ\j:|lWɜb3'<f t?蘫uj:ωkhu?ވJ$mT>0XnRFrzsm]h*Xy#3֯&: z(+??5㷗)k|76Z5#/,jz-&לW5:Ljl<Ajd\K 4M$hqW1տSUm)ok1Qeq7cЧ:}5̈́t\KAl|j.Qf|-ӼA ƒoL3YiwVޥ f(>L_/$sPҒ4 okCVԼu]IG3D9/eRsWֵcЧ?1տSUj}6OO:+VOQTcЧϨ8B}OZ&NlKAl`RQHy~7xŷQ°KcqۤB n b rcX[+35ۍw Lj"qma0A-u㗇 汞ŔvwwR {I/QH[h~ҾiK+H3#O5࿊?>j nK@aa0R>+Z~_[ӽ?Xaȥ6^K5]dD2K:o $\u?&ljNi: sAkS' 域Y\X`|0Eu]C"euGm^o0E @)=ʽQEೋ_SE;ό|1? +]ܺU)?!Q!['Ue0|ٯ\f|cn7y1^3 Q׵?j׺PPf>\PDZ]``I9_Om>Bٿ~-[>/My*{{]>7^z'Ĺ|n Ɖ6 K" @du%K0\ƾ|gymH[K "PyfHFJ!`X@᷀/| u;ucŵ͘#V K ?q^!ѓWk\Wl(?d ezQlSuۻ|j@ό|) 7m4+m+Zń9kG+It@CIǏW;GŌkJ綽A8\dE/_<3MGYm<_L.Mط1Io媨F['Mw[SB[1F[ |C#]f>WE߁5]׬Ow^R#5v@$p;*_|_ IM ?WVv1,s$aY"&o>DXx|/}KSm}2_]\Kn(/Ȟ8ve{YCI'λsʊ+3Zƛk _]喏xzuEE .'pw¿_Ox<;_]i駛 LW9sq3HK  ƵVmyfoDzW}\!뷮#ZC2kS󥵦3$we"&SХŮ)%5|ZCO~3յok:նzuR- $4+ uGZ|S OI Ik_7L³(Dʳn {myZET4QϨ8@QY~*v+e*li]ԢK|OE?\x1dKkk$l&ͭf b*yO{4.I8NS {_7Gyk:0BD2`mV*}{FBz_\ꯦ닦B.UZ#fno&?$qj{_7G5&*/iߵO5?^kvrx{N8ծ.DT>c=5KW\[i~=\UxW^oVA jZg/4+]oVMFF@=+3q+ƹF,'DPxXFdt7b撵kO6U]xWX]Pq|h'LK699Id0=kֿiR\g̓D5kWvd),o&/q#8+Gӵ ww%}HmE6ݐxWG#*?W ӥ—Zψs@?r F>,uVZci0 90 >}?xJKgK%{h#c}wit9R[\cFv>2$~UW{_֢z#.-JP W ̑68>WY~ZHu_s-Chϯ<ײQI;)[ouZ5a@1 r@x&Q mKRU_Okߒ _[OީqcE&:\ĂD0; kE(@ix6TW ͏e<'X'bKO;CoLkN|ְ7<`m~W)mCEL??Ga} {+cύ_z/a} 5Y%GP*^]@nA~nNGj7{jlx⭿:Ǥ\>nqch8'(O/úa}<ڪiw i;bNJu>p>AMMf}Z]:p8F؁T6`@N}jxWڅ)ss8Qo 7FDj^OG? MĸDRR|FBWRq޺-''^WzVi>{`_vcBX޽@̟PojitW>Jvz,rK#6"B[־3x^+-n}J-=Љ/,DdYN+/]ĹRR+ɍ\)Pp’qYOe}tzj)mR4w@3x߉#< ueuIvF]@BDJ=3ᆯghm⫭bmuIu9-׊~ld%z3CWWՎ˥]}FX̪#@885_>ѡ\E}IA b76+s_u]*]N8 FdIÙ<,l8ĺ\0mQQI&Q*'CA^]Ǵ^|W𝆹𝅥6HcPVf5iBR`NF*M⇅OIi]j?*EYdHi8y}i3Z4Ea= 02x?Wǥh0 >;sah9튤Mݾ㿻\0v灕MݱWo9'+/ /#[kG3C$LB:`ǀTs? /O2+Q;xaW;Hc7ǡ5G{\kkrIQ#&+` ^}o g/G]H9o?%hzݧ(Y$84yDPI70e k7ş KլWP ]حm";$sOOnz\ &4vfl+%kx'?;ZLv3c<   ~ ]f&]:V6!ob> %7|"WPGkw5sE̷^G-̒33#'<Ӳ}W!]_?aETQEQEQEQEQEQEQEQEQEQEEwn.f *2;1\ {1?+.-w7ג܋{X!h㷹Y̙$ON:?}t'G#!ӿEv-$&cd񭟇gk3k. lOGC?>m'ڞ7WkmF$Ҟ;[%onRMo4nZ0TGSﻨkme)f7B?e}aacvtWMtܲ my;7pyܓ~8Yx_#5:l6vJbԗyduTԹX=8HcC F:X٢mna{l7x, `@j{>UpVCo%hĘ <`ԽsШex2ks,pȶ9= u:/)>ZxYm2ѭzz^?(?㟅ig{E͵ͤ*chЌfcG7="=6kKhH]r 60f`ԊV\J+o?hjWLWW0R궑e"-hJ>` .tw^[ҵfm 2PHL.6(1-sz^}5wɨDCTAHf:}ń׊]NxQ_MڠKn&0P"'`:+t/qv6zzK}$-$Vƈ$ K|w[Rb-gFlI yE F2bgӶ_=V|KeQH %f2P8>~КMߊ4K[5t{9S|0a$,pz9[[+W㧅N/nOay)J‘U+o]B^ &ͬ 6/&c *c''`dv{ =>4po4nTM#4®' }ږgMlvw;_qQCQDdCpTC)9 QW7Lީ}X]Kjg=9ڲ 6a]EGo7 Pu E#I@Š(EPEPEPEP\Ljmf`X1V~xF4CFr^=ܨw; oi t\^E9zy])E~-Ŵ|֣HMFYj<9 3㠨|SƗ'mևu+8ol24* p^SwcǬi~=iylE["(E~k`$ִo=of -dBdv1tSo_.TʿkݘCt,C4`d[ƾtB[fխlRMyb[T$cdbnW_)hxv/խ|K^zޗ zU%fg'-S|5jwz\ΨڼQ%;fPĕ^33TPպVX[ڜ>d.xjDb J/* g9q^>Ic-ZGe"UBYI3E7&m,/rwKӯM坬VUBW_L0%R*Fs.QqiV9j0 I$W?Ox:k%S[Z٢/X« 2m+4CES)α#VVSH&Ul b%6/  +w-ow41ƶ[dVf/* z(}+o|1Jc2Ew ;܂p+ͯ|Bլtk)5MMLӬl|:B,i+!Œ'$m7+Y.菟o\__\:hv1d+Ard?SGjz(8ps4}~ ?Mc>?SGjz(8ps4}~ ?Mc>?SGjz(8w-aiϕ(bۿ֗M?SGj??ߴ % i*3R8ps4}~ ?Mc>?SGjz(ı`z)P^–%d5x|U=ŮsZ}Zc1v6f(ֵ?xKY.EAڪb8о%pbKּ7t: xZ-.21~,A'.M"<>{Ħ5q}롼%_2]O}a{mo<ɢNX${M>֯ |In=iw֥$Mus)6KX #h)hV[}:HuK_\† #f ^|*4ǭgNh&$V5PB]bFۣY|Nnjoj~6ŷZ RHAV7(<;)a؊;O~ ;9uٛW*~KڹDž}j諝xY&eu`wЖmn;GۓPXxfOE73VhppCWFj}aq-qxBZ`kVYi4+v-qҒvo=b#/XH5?i/6Iq54)+l@c׃g_u&xjP=pqUmxmFQ:}-[b4 :V$Ojÿl`ԥfh'dNA1F j_/ọ?7_oZts}I?<@c׶X4n-ȱT7ɐ35 Ğ[4?O5rM>C=}+?\O洛My_1{t:j(aEPEPEPEPE2A!eSre*'q=ge*'q=ge*'q=ge*'q=ge*'q=ge*'q=ge*'q=ge*'q=ge*'q=ge*.LeThr<2lpTБ~.e*?O7?0? 'v\hq=g# Cbϱ*w,Nq=ge*+%%nUaRYhݾ?6\h-2vW?r\hq=g O@U'Ö+ <=v8*ˏ?U.?T=ˏ?U.?T=ˏ?U.?T=ˏ?U.?T=ˏ?U.?T=Ā1mh;-ZR [_"CmoC"W˺⏊w~-5=b4f ˛{qwlqqVRAu>5{-j &(xBȌU\|O*+Ѵٯu'}aj);A'g|b;=K#e]iԣ:TR.)܊NV5*8[|T揣u-JGt 4 :jhYI#ák⋟x7W4F,tq1cc}GEW-%+ohcy)_ߏz3<5{H)X+r8jc9$h}GE|wG7(Zf?ө?O诗7(F?yh/u? ?yhG;ٿ>N>\G;h7'tW˛h<4f:s<4oQƏO_SQ_.oQƍ?O_:+?ѿwG?? }O}GE|wG7(Go?ө?O诗7(F?yh/u? ?yhG;ٿ>N>\G;h7'tW˛h<4f:s<4oQƏO_SQ_.oQƍ?O_KQL:#(ZG=SMIyL'lI9@)k~O*?Z[0,<c89vo߅}3pRTU=דќz mz|8Ȣ⛅mcnm+SN5Iٽڶp)6lg8gVcl_[3OnH$*b˜Rs؏Z@X}MHe2'%GRI9N&Chs$b'>Xg }@&,CF|E\iVl^}MexWⶇsU쯬+{kDgL|u*3 ӳW:;7-ƙg<'nȿh*PP0Xz7}e}hj4{A(߆+;ivw5F8= )9lQ7hc=,EQԼ2UI i$LUA"!7%P4jg_Ώ5? E3OG_΀E3OG_΀E3OG_΀E3OG_΀E3OG_΀E3OG_΀E3OG_΀)Z\kBGWҵkv8w1n㓎qMG68t>ݵJN? yѴUs[ooDzhZ6hGKY nryK$3xr֖}Y(P)K A }_RQBv7o|bΥ[x*]?Dtb+˛Gi  ˁ뎝G!_ ?j^M7LXL9chI]U˜z׿QBvwmBi43;]"~&f;}\[elfZA1YwsR~$Լaebϳ/ RPdydҾ|)xa]gGu/Razp~TP0RZG_k?󯇺^g}m`^oB#t 6Lcu3>4𮩡\O-744'EW;R[ȹyz2kDCVG0މAW_?_Gk>d?>So ?) n?/_'0މAoDMWtQ ϵ2AHȟ2یӐ8<#Gh/ܹQ'`?QWV𝦧wkm76{?qb1uM:ҽ8j8t(ƹi]NE@I/G~LD'eIkcNuww3wQM=3[t 0 G 0 ]UQ@ 0 G 0 ]UQ@ 0 G 0 ]UQ@ 0 G 0 ]UQ@ 0 VC_EthXsq(d0DWᚏ6*z(ϼ_6*z(ϼ_6*z(ϼ_6*z(ϼ_6*z(ϼ_6*z(ϼ_6*z(ϼ_6*z(ϼ_6*z(ϼ_6*z(ϼ_6*z(Y[&c?~[Z?w&b)X{O\MMVtr泰yb ^xZ?hJv[i6zs+heSã W>oΛM;1'}QGZ?˧ MJW6\+,0HdVo:_[IHGUnᱶM%Isj絏 z՟wF3}G{]fH4ͽ6.4na{XIGڴc}%Yy&A$r!e# ~գk*0Y\ xT=H~oΪYIT>YL}O*QG?Ud--mm`IsdnI±t/zFZ꺕ZѯdlN9\PV=藍Z?}ߝm@~գk)~գmc}%]k}:.ɡylN,QϮA5!(H@P( ( ( ( ( ( ( ( ( ( (1yIE1n̐x\{`ײgE e ݷ>Z:Ku>?O ;XX_jzM{8[hmŻ r ? Fkբ"q κͼ0~W|R4Mr/%T9C]ӵ2@ltzl%IF@׿ [gt#{U8uG|/qMPn95 _+yH`B7 s ֯6}\*ߪEo'÷-7^gVvmVV&_Jɝ9I᫅um_PʹG`#*_v8#9^5v>'Qu@O< ӟOxUsM}v=N="Je\5ǖb*Sn#f|zbOo $W3A-U ֦%vճk->3qtO2/Y\)"ɯXmnW$IaF~!8PkM^ HAn) Ň Z7l:~@9—F3Vh -RSmtܬѓId+7O׷[kZbac$`UMy#^uOKWƝtC*;?Xj6KǪi)KXv %V{"\]4-BF@ڻݻ?TIs?ٺz _ON@9>'R0ImۤRL*z8?%QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-18.01.1/images/jpeg-quality.jpg0000644000175000017500000012732413222767271016255 0ustar micomicoJFIFExifMM*JR(iZ02300100Fotoxx:mashup| Fotoxx:resize| Fotoxx:resize|write_text|write_text|write_text| Fotoxx:resize|write_text|write_text|write_text|trim_rotate| Fotoxx:resize|write_text|write_text|write_text|trim_rotate|paint_clone|write_text|write_text| Fotoxx:resize|write_text|write_text|write_text|trim_rotate|paint_clone|write_text|write_text|write_text| Fotoxx:resize|write_text|write_text|write_text|trim_rotate|paint_clone|write_text|write_text|write_text|paste|paste| Fotoxx:paste|paste|paste|paste|trim_rotate| bhttp://ns.adobe.com/xap/1.0/ 1 2 3 0 8 426 748 1 2 2 C     C   )" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ? jtQlg4xإꏱovUwn|5?X/5X;EN=|_BމVt[d"2!=Gv~ +:\(K?Ziѓ{ydb+'e:ܜ]O'c 1\ųcuVќNc[Ӡ19*Abv:>ܰ2JuV;uV"+B4}k=JfR:5$6}SV6.ij;C/$}j鏽_R~\޶wc9+X/ݓھ{Q.S1W3/А8_:BTd,x=^lLgq+Xgy\Cֽ]/T\+E)qiȾEr^-O.Pd`}kV 5p@1\ ː'M}'.gsN*&$ϘU``c޽Jy>B%5 bMyE9ju߉SOee 2Z)T: !c=^&q$}C@ &5.?OzLR's2WPქpIGR{{Wш [%ʾ-R;O$_<[/=+j݋2=+6͜@Z8oT7'?+Jm}WòOLzyV>h~n4?M%#'ub9yG[ iJu,x̵dz^ cf;jbPڅ޺'m,\ZxU/OB޾*d:-ㄅ\_^ey҄KQW3+O+cUnޕ*RN}A#|Vs z=cu@$I sH;>|ǟ?Vȟ61Fz~ +RaF#bY;7v/t7V9|6r~שwӋ:,Mڰu;p y^xIXw:B8$?ҾrLh߿͞S+DvsިCߜ ".qyX%Ýꄝm/#0>?d vE-:9Gj^6qI?m q7<2-0SE|iB6ݣ_u\I4+lKw~:^Ko,azrk*tp4T`#ŠDsҸϕ#'=vk!r1~^Xy{ikc 9,pUEn;7UUH^zJj{XƔ-OrƤaV9c*tL`j. ^IqԓLiY7N ~usVB+gϳhJ[uɧOvJ}kkZG8,I ټ+6iv B0ge&>ia_Sg[H|-[ŨHU'?xQ]Mڈ4T'=yzvኘQ*k8w]ZNxҹhţ'.Ʒlyf1Dk὎-VEiڴF zWmri,cWzWԗKo l]Eq9?μ֣/ '79݁/J\nuZe] q~ZY++lȝ[wZm^T't 605n?+cu*Âz y/GS>hGܖ҂k|D*O޸xrsZv8z.N(-O+XZޖ+9'ڰ$i&&Gs׬|3ujHTҧW1ƙjg6tI]۫/_ @-a㯅t""%n{ CIcXjzWIjluO>]x? zƏ7VVy#yּDo/.%$H8(s+veO²5K^YrG?κ;Ihё[ wZ6lL,[ בR.N2ݟM/اe1`pu?C_?|?'^l!ې}t_Nv$MQ(Q/vBoǸ,]s޼ҧ?1RWz!av+|-[!.|#}=*Hжd,|)է%Ř?j\6?W~=Er5 hGYԐ2YʫE(쵏Z'pcF={GU$-mf xsF̬oAQƣ.VSn 2瑎^}YvB|IԌ0ZtUQay6 Upk?=gVkΕ:#H퇈nd8j vԋ?^qT{a?E*w e7=_δ4CG^Wꐙ\~ҷd'>kN9'? r?@9?x֤Xl?.196+e_.s]_Ҽn߿$~9%Rǩ.by&508zkp&đ>$EFpEd}*tR f-<;-.7?S1Ͻy}׃U{UzKJgKʣ>߇qWo 1m^[}9=2?՗Qsp Sʣ\?B:V,#9Gll脏?gh !k4N(k]_i*prO-<;~ҽ 1Τ<>89V;7l'#MVF[{ʺ/ce-&7w?1QZ*-SFd(S3+ϳ߳^O}%l%H2ugC֟WGRq>K=姁%,Vllcz]wo O;nX@[zr:l1{P qҦ0W'RMV=*魥i@T߯%n175_يq?CT?jq7>W#9%F-3Fp2ji\yr?S\(,s>7⨶L,.-gO +͌:%cσt :H/=Tjѫ v#޹O16Savxu96ϭ9Qi9|kKcˋ]yf:|7b nzw? t_6yeD:ΛؽEӯ]3Hk__d! cx\R#;GZ5u#lsF3Ml&{žqmg޷ }]yxm)޼Jծ֣ HK+qc&sֻiE~eL_oC쫒XhUϥSPUyvAUWI}G?Z?0w%M8fᚫ Gryu 7|nQs{{ז~RiMIdoRǓ5ዧeZ+iDef>61~c(9"ruZ.芻c޹(89h֕>y88}%ў:J@}5^÷5C#W;@30ɮzvOCYdQΝ9hĘ G8Q_ʴ&v*^XVK4͍ڹPe W Mi/1Қ񃀾jj֭mgelM,F; !w#+0x7÷#m-X H\fYvNt8|}AoƱ0ʾ;=`: n_<&Nz{WOkt3R3~Ǒ2m/ar~sW5CqFֱ۸+[ !&*Fe3׿;SKr8\08G 놢3,wý*)F$nZL6A9]|mXP@)rڸS4dJ{CWx\yͧRL=hOC|?cڤVtj`w } 4ˋ/\Zs11O_zq|hgN4{/3v6tֳ[yK`8`O}Z|9/"508_Wiy< VNܶ;C$cPA;рW{[k_zWpo#>RHV8y?JVy(烒8^?G?%'b=6Z˴+ 1}KdgMdtי-]~W3c>V1uvr*!#?^um>-k{:y{g\W]z)WImG#]5m]ey 5[AcNS6ˏݓ9kR Xj&5)HM4}t{(ZCc N#4g.#?ľj$%VU`pN gڼYaIuSwG- K-&|#8ﭤBYSq9Y?[8eFJ>_?O{ie)4 1x Tًgp/P"[]Wja(SZ\O@b۰(ڛxǥ x|Keu7 FA{'#[$"##UJY1.Xc?ˊGZ-lm1i@5VP0kUd*4¼IkG,<'F',9UF\_||*9{g_]Zyt6Umc}kxj*gWɨeU&sz]ƚ-y$WI9slvZT2FI1¼5fuhչԚ^c gڰݞ@잜vx&Oij҅!{h?ycA !SpGsS~^#Mwe^W7o.6wҾrHv׺NZ7=|9۞ŒGB K\7zkN>CscX:3g8'׬h`.5)Mh\cp'w#2{ח89lTk)Km";F[[>R+`>ZmIx×,pWQr@c_?x}I ɻF}*S$zvZ-|%r@'־σoD$,^wXjܬ*f`O^/ជmlҺ5>e!V؊Q!g_\(l "^HIٺNpȲ)í~g~ܟy!uLk"d6wGk<I縅?SlTgtz+:Ȟ<0Pr:jhwmV}]GQX*2[{t?Wƺҭ^R%X܅b2UO]oVLЖX^aj.y>!~x[ ^+ڤ|Wrnuis-&m"m"c4eH'#=aao׊OQů节C$$u8E9OS^;ٵ+, Iw ̶1洼D|##^0|6ծjIn2奈{zmjic!9 u[[(PE F3Υ"Im*(sT&F8Ϸҽ8W.-s#n<LFҹ<A;CK}O[\hՏZ&kcX4K’YM)< \wsӏj ºŎ&1ۢv{z_7vhfΒfKĶvLkg"*'gSp[=$|>maGuv="ё^{.\›m2ZkQ'#MF'o u59u~Ht' Y'!~UϨ oix vcV*1séjuQN[ojr}{؉|ed|}Oُ';|9ym m+%͍)ScWx7[7O_-KC<`t _tjqQP[6ؑ6001WWCQl?;e=NU5<Ɛ>gn:cuYʅ~xcQûsX:#"-\xy/hͦ xFdqmp:f諣KSJӾ_2{ѢV˶Sܱ#^^0̮;p#JO*qWm9YX# 7luwV+T--Tvt&c$\)*mt.t]\A bHӇXjҀ5R@/sZfGџ ~ѯ_^y49u 31.?] .C4m]?W/JS.[~E]0Wڻ/k!a2V} xͳ@M0V$uD yuIl_5_GUI{zRE 1ɫ> x|YFC}?MkJ\xwټПjk|B}n=8?j ) z;{YWL֠pQ3Tʺ%>C). * kES{ FK1㰯8.PbDxݰsk|?m};\C,Hb1:!ZǑᎫ< čOkt[mny(!s(޿@ (o ې1{W1K,v3#9*W B'oCnd^OP2?\jLsտ5YjaNM;hwVvѬI,p3Ͻz<y.kt;q^c6:œ-F#s3`|償oscW?S6{]{ 0.'⺘m2-ߚ~a;Jk^)gG_aXr]Ƶ} Z}!pN3^TxONZC$֛Y'##֟e-F_Y-VNUeT@Dt@ھӮ-TXmRs8#OaԓXUXJv>d\OV`9e&^ HSuQED3 }='~%x6D_ m<׀kDw`{M;wD а8¿Սq~[k>q@JRz޷,4G$9v]2tW] +#J'#y`fPrHS޾4X4}b鑆}?yJeմ9> ah GD\5߱nȑ#קIV[_~}n^̨]qcU(vF] 939E<Jңh^I7ʸ ߊ3>[ {δѻ,Þ P z5fOMÊb~u8YހՋ;*PtkqhSf~۟HV}vʌ/^}+[y,C9; +U3/F *tlQo.JߺRYWo;֯dSoq$p;ZX' _{T*-|d/yXyTog} ^\!]7]^]BdiI*A,OK6Hu{%Qu*8@-߿17 kgS/vֱc C\u0>x}&u!ǵ|f$;q>$ZΚ\"2{^\g[>]#>JlWs2n'y85#VTZV7o_Q\ւ hGs&pyu(;kF&b )^Ao fTηtHj rn-1o,Y=@k|[q8v6ў|Sy7dd2Xz\ ,c#LWP=J̮p=?s+:5mY\[F+؇`=+5-=m8l`|6u&fnN$dGwpkIyny֯bdv(V]d%-"ְ(8b!ӡk@pGT-R}+V܅ zzw \3ҵmLn C9 I?zk7bK$`6go־e.9_:|簆5:=qΩ{^OFCd$0owLt_6j8Ugk|"+3c?[T:s[En%VEZel|( L6#}80 5??hV冇a@1kWqxF"k 3&C,lmy'{{_]|q_aVQ4jS}CB-^BF7xl뒲(y9tU~ز .t&C!(Py,1]?k [Rү|)椷H<1?u qabT`}{>KϷ=4?騪1cげI?),g_jrKʽqkGr%$$@=ԩ 2)e[|o[<3!a DS$ǡN[[8}#K?:C3M 2>3 ׻#w2o#}+a}O!Kdk?߮+T.p1Oux|&*"xUoDH21[*K}(#7mUwcIpVeB>ӐN~OӾXݻÌWNRhJ_Τ((fŲ"K `TWR= FNk٨bʐEs?upfa$`?:gSs&>ҥVxT`ǩ<|Ơy68%㒧I$trԗ%*VcV<*fbW긪䒣ӓ+fn5baqqc]#;+HQ5gsӺkV(|K!Q,{Ms8P0qSrc1m){!9(@FsVYҤ>kkTȹ^w=j)pJ qܒ{U}9=*d>'+rsr i:i}yv[1;!S[_` ER@9]D<2s[_s:S,Fgk)kjo9s%98 %xM־xnU).7-{b7<)W VKX3rWўgu"GUe_nWM i["ydj.-vʾyε8ci}Q ^OZi"ElA*Oe _*8Ոt+UǠ5U_`(^6qG"D#B{U1Vvo$p sk}˖b8sҨE?sҔ78-q~u޳SOJicp<v>V1YgT76mDAj۝G|gߊ 6xb% w(j :MHΚ |޿ֹ}N<} s޹J[^+3;((Q?h(.<%?6+;o)S{W_-t_~Dc{Z;Lح݁+'/|N,Uk +)b_ik&Icrɸ>6WB+ٝIr}Y0d~\J9$w'$6(`泬q?rGEbWA^iZ'*wd)N)Y_zͼr=Z+H,\0>J?rG]غޝ_\2o5 Dm'ҹGc.}BF{sWڎ_OW5 AH9?ҹ-Nʸw?J[PԂ~Jog ozuԬn'ڡ-$k9M"EZU|6*Ƿ Gju CVk2yIozsԾQ^mBt\}==븒< [1, =QYT4KQ8[ũrz4 RzQjQsWfFHs8sc9tv)XG] eчQ@s5Ԭ@ ߜWƚd&r;ak sZ'aێBWZ&#/G98 pzs_QD&g#51\%rEfi8l~ nmzWT6NVcO1r:ҵ-ۃANZ,bֆjZm*#Rh@G_>]6Ɇ*I &@f, 9LWC<1(˘]\'̱E|vz奏{<};FT mmlT*ŒXmCШ ERQӗ$`vRN7u}Ubђ_ZsxH?/]ܔBֹqP367Tor4o:Zj lrږ@`O\ihg<3I5^ ]s''i56+^@s\߯֙^4{\VJBVEOQIK3x= \I,O?ֶ.H8W5Jh 6X"0+ڨZݮ>aV^8pLTrX%V1՚ƛ>}AjPÖ ג@,'EzVQ,hrI7 0Gq^h8^_:.V/ln|¾{z4J~=VTbP4ꑀ{/gXHbHxG#S1I.Nx>쎨^^ܬF|F jl?ĸ-%Hcr*\̎8KRCuyY嶉ls!prS|_Ҁy)%U uq3!e)#߱t5w-° XʌIM:⼺o t-F=RYcQqttB7"-^ 4VA^}8UYiVGCobrˁqϯYE$1y˻XWB,-#޽NZn-L6IJcyWQIص4挘>9}Nu|ʅV$JN_G+پ|WR u MV{5xPrӨt߲ޓ_Kt#ލ$WP u=U/4$H/lg*:Ƥ1ki+{Yn =rȀ0XF||!> L+.EJ$@Iw_@p[$2q__ŪM&+5~T < ԳEz8|5xNbLׯK[ ~??Eؑ'jCFy9AҼDva]x#B>"=/Ldc M~[mvEFt߾E͗!W !ҟŷٖl}2|F۲g޶v\#-s3"1AJ[\E)8QO80:rAqv=ns_]$qAH#<)ݝrZl~}?h_< +Q`3Nʨ\x:` \p-X!ڒ~aֹ=CTI]?QU]R #z[27Pl:(@9ɭHXW,|#ڷmn8; j`  *@ s}+ξ"|t|z,m[TVP“b ^˚']*Li:V9֧E0zZ_YNZrы|nR^\G3ni+U.IFxAd #ck珋z3ish/`sБ\?gz-u%jY\g"=pxrHx8VNQ>E2>dvqYqMy +>ƭ[ERg()8>~f4S{VMXUqvw>#cJ{>oJh7}3M5H∀_AJ4%>(4-#`@7rǙ/~C,;$Ҳo6gֻv7RO?5=Hj]RK~ZG$\}h5E-NR\~{q$xeIA:^s^!Y@0=Y[ig,^P\ʇl0Ì89략?a!p$`7aͻ\1T1xd2-¬e$qIMHG@"_j]M"r,Daj{mLls7!YNf꼌_=[O׃Is🋮Xμ3+c^xL-lgK9Oj<#"Ă`A/^Tϭz: ӌg:~F2S._apw$@ FHF.-!RK ;>CZ9VFyy_ݮGFja.]v%1WWCuhEmb/4aI#(pxǥj'Nȯ<ո!0檽XIK@9ne }8d}zSsxt5VtuWgry=4̙7d!.TKU zv<2m 2~]z'w$h/2&cҸ?LlؓPZ*r8KPOApH%WV| ENS8W镴U<2RmÆ~O\ZKϞO .m{>5Mգ8?2 Os+յl"6Ϡ7qxwDEҼ9-Y{7#twkG˟?^}qB#ҽE-WrdwS٢)d>AS4'SCfӞsJӛ{qNZ iEݸ}Fv" m'ԖmjJFGi X^ҵbfnkҽPB_r}+ĺ4vn$^G5L4x{Us K^x?v',JY`kv`{gH nt{v6Rl|(s8uanV(dɾ+|^Y"hڞP V׾4s}+8f~a?*k|n uǑ$(̤X+N+{H'qanm j3c~rqp\s==|#پx6:U/WI]á\'?ֹ뒡s[wF<aF,w޹gsP0q\w$_75 Qڞvkg m#=+dc, 5b'8b"N nYc>N.e9ڸ7А˂ހ'V"p\!9$lp=k022'Yo 28a7b3>)|qjW>On5 F2M IǒxAk_>(E  K6^sUn6?tl6g%5ArZ䩩v"I*:̞>Gm$+0~XƇa5p ,1ʊF71Qkrm )<}4kDo*7 BMǛ /8Nkw|goWeQ ϵqVi7Ifb8^g皟NBqI*n&iqTaZmGӫ8F+LԠGBxltOl,&IV #"*=xJ WvnVG#{5(32wS>޹?3^U^hޜc3mE9~RA^A]?1ϷJS__~Җ\Z@R .qׯ7jVƟr27Q;d~u9K/^u;-GR=7+ܿ -|LY0^O:Ps yJ nkLWsy+ӉJ-c)? ~:_[\Es apJJpA~iZōܬI7cZ4#;ڤ1q~|n:؜ZŤV%k.]nUZO8Vʌb^ yR1o.w;rkjQ`=DVp(}>8[y$?F3+u p=¹{w'}#J{`x UwqUЃZBYk\\`Sxl,08veXw=+>xoX_~>+ry<~ҼNsx΅vdǧ?jQa$x_VP}:oE`F(B(_s_ͷj&/m` RP{^A٤V>K`1tV埁w7`vՏW1$8[?%gF $F!7<+Ѽ๬1jEZkl1w3{mcnN3yYSW.^SҭF"! 18Iރ=k#L\ۆl/ڭ{-:2eF:m*~4ir'ʦ\c;aW?lnUTpx51@mP;exhtqiTt9R  g5xjͥ)Áv Yt ͸rȥ7)2:ן?wg㷤Ì9t;qGOim!F{^d811|riH-zÎ=tzgkHL2_3,{if{6 Y[$$ƫ:5h5ǖG9;WVP&JM-|L5Q*;0qnˇ*;s~h_'r*ղ;j2`ہZJFV= W-9V֞F =|zA[j~|?U14F{1=.ICRF-P伊݀]bԡ>|Yxn朦ca0+uXQUP9fSzu_[+0+Lzc]Ս)i7޳Ƶ 1pAk;R["ܯ֚ŝF{h\~]P9c\&Y@%nrǟ|&"rsJMhn~sת~8\ wxĒ;@'ޝVT<yּޱ隝]dD6-m1(~5{}sSHon'o ś$t5yQ#:<+clw`m涍S,A,>ZMVKeդ"71U~Qw4+tؿ/s&n2]/YxJ1# u=-u-Mw?05^05_,^͜us7:rHyR9c_Q|soClqEW𳠷Hu۳  ֻg+Til~7eibI#ڑ/#eL֨7nUʲ9Z;|[k s_9?DFC>^VbR>g믅u8=&ı>0mÆ#>հ#޽_6wƙut?eAfrĜıJB掇,̻U `ޢGk_R8`87}BJ)OV|}Үk: \gV9Pjpar8os% w3oxjh_(\`p]% J9̒K16.mU MsE/.D[`IX^^_vy 8 [CڽS1H**-ވ0 hGJϛ >xUo5k7m>[sP>i1ǡ:/jVn;>ZjcEqjq9=c?ĚObپtV Ing,lݏn~ xn^8CՉ{A;*[ s7mu)$eXp;;ws O_ɁgB~~>Rq_L)>ƳxJP1|po{DHX`bRXIUsV 2!,LQ+絽m޾Yh?J ]t(.I8@g-ڽ82@!?Ws.f|FYEzgOf>wrNU_zi .F̀EKt'-F.[R]nb՗JE%TϷ'׈C<ֺb۔/ 1'Ҽ~<|I45X&mv$5~:+l$eCGE:+ԿhgUVk$HՇ_I=xUoo'y-|5gy* F~$s"X?|Qs} #YԘɺHX'N?|+hVK˽.'(qYRыf7:u"$w1Rp^~#e$7C r>]V48$e285SjQBтP@UЛ8͗/x!X@ vj/ zdwO{3a'?2vܒ;W^e7zDm [ҴTUFU G͓|&74R̚ u/1xQ\k]X =Z5|4Ɩڰv O%:^]:sԣ gû 2B4VM4%V+0xͼMGHd%K)aڵ#o]Zݬ4V!PT$8T>\xWսk2Fb/,M mAOk<;i K d bE, HŸzSw9jՕI݄vLh۷;Uc!rϵua ~])p9}+?gk)O>!{[9TIA##-\}_l{;KmĮH~>=a0+;cda1^:S>ghwo}k[$HR5+ KYK t.'$ai'Gq_Z"x#+>*_|ݮ߾5vt8ܣXDQIlɉ<=5 BF27QA.n<9|s t~mxw>5=Ep@ }G`APV\zokfv7<zTSY#~~\J͞y[:v3Tc>oſu[.Xwo =[|dz^궶_=(q^k⟏8lvtn4`J529f0zCҹU:cNǽ_~:E 12+vkG7q[-ePzv\aXFv9ZƊNuN\/}S[&]8[X[zO}K{-]\I"\rzxSp7$a kHM쎗uaSP|>D7\:)״_"5hpH#=jӣ("F#r1ך튩|w'n5D5DI_Q{&BN$Ա˱~[Gy~+k7-N3!z|jҴ6ZtɱX!$_9^lV.d=qdv Wڿu u)>d}&IE=|(UW6+ȸ^sHu{4Fv&OHߚڌ$ttό8meoѿn>}{Uxxx?!I'YpF%d=\o_YinX ,E}oAk<S *2癜j{IY H[jQ.+`'sڵ|9n##$\+0ޣ$RC͞8t^VI>` [ 78'.3xi8s#m7X|};ʶ܌9'|!Vzwh4 bVn@QMzW|Uwyq<:eŦvJ\] R+ny2xWK-J+\~sYjo-c󊺏zWEe$)0H09%$(:2ǭ{o }SwQDp3pGYio!Gb&"*:"x_?-CipdB azǰ5ҵJK.^^&9KFEM0ڱ&c;ghwF֎ۅF¾Z߲ݿ,Q]"&4H$'־,OOE~^ixosYhH>p`0ʝ=#{% Oo}~?=?Iu /m^}cEԌm| 2H۪/<+p暋:^*Lb Z _gu+\̑n}<{vmZ Jkb++eo7Wy$a3Ϧx*Vʖ]*NHnhox[f-$mi׻&LIL~ΩImVaʆI,:Erڟ &7W3J&I z'G-w ⤛ GM5pkR*(ER?jKSVm.XF׽öh|½'aP_aoINޓV|4l+Bw3F?}7IA ͇"ӓ9}|H)Nq׭|/BMeg=c`)1ܽNW]ޓ~XGGrŃd21ٯrпcߊUP]U.Q[R&,Zn,&'$"V'-nwc+uj9(,ZxKMtUq8?/ZRvf_VrD~PvQn7\dm&;JT *3׵}u/Ï —QE\5=D5߳OęH|)"FO6bee%n(FNPg.m858bG\Sx㑑 { |DҘM9/mqcڸ~˞>^I %gEirGh1^찳 U[S[Oi .v.e'W`'԰|?%v<'rdƥhx{x-o<3w\ڳ>qe-\C>h.wTw !PTݫv; sw iդm+Yv>k}kQ6Ds$: OB0n'?э~?գ2Q+'\Yxv!5\(-@6s=~}kG IFȬ8 6^ګJI\Ԍ%''c?Yp?~zfgr u[D{6DPقlz ewZo XOY >զ"3ak,?-bµHh|Zqy? ͟¾\IjRܽAj0wbpɎ ٥k޼n1`jN*a$#Μ;zmhg5ڍ뫻nGdb:_R-;:@=w~Aދi_5/o2 '\ӿ"R1M'W:gi{T:ߴ"R?cX9:?h4$uƵagiŽgvR4Fp?un2f~5 w~Иִb'՛,ua\TqNI=? <&E%m.H<`: iK{e0צסxg3Y'lOZE)K0ONoZP厇7"re@N¬yPz׫{v$U.+2u[O;ݴgofs5;?\UaR_eYbj<;O+ AԬ4GŶ@=ԭ?yQ j]NEgK&\}+$.s-ޘԬ ?$TjKx^x芥d~My8I*NDa>~K"ɩZu wdz#,Zsh ><-Z+0m 2݋HtWamj:zM/^YAzGoɏZ qgKoL9|BliK[kL|WWYjt%wt KhrInJyHFG W's ɼ`x[cҵ` MQ|UJ5u$|>'o:laԑJڋyu 9̈́~۟8R?fψrU0_joO'a#֧qc8_oJjWOW<^/K8GL\ֿ~#_Q}A]o<-y?-7XT^ZFGʠ?.xZM:0]nEHy' )ѡPC*tCo1vmXwϵoEe_ݑ֞؏Z4c 5k\LX[yut께Eo<rTq]ǿ|?h|Lo -=`ZlOm4s< ̊JN3j?W_]x/ 72YYj>T^tYP@J68SvqO>"|Ox;aK{(.]ʱB#e=g[o|:vVLf,g;< Hc̠Ʈ|VwC\լYxrS߅|Eoy;?m,ZxM+f7A {z@w_>'^U~$|={5PKH`ALI/;Tu$=kk?_ןJ՞Mna Y(fs(/p'忊>&eΉiw_>Weo7#j%7"P]O|'fcFk7ݽ54 WX幽i&r2|GN|51gˣf\_~C3Enb]?Xk񧉟DXdW4©(ʲڅ"E8\>Y_ iw7|'ä_j:n[i#˗S,Z|!tWuW/\s0ԤY|VVs Ğ;E kjWRYOmp᠑?tkoY>fܛ>Wer|b1Z,f}6"u !enP`Beٞ*z1Vn-p_A0u-:]ZQ䵴y'B7c " u5lp\Y\|f䏞-_#\][;<1iwhD3tcA,@'=x~꺒U6ד44NymFanO)_q(oo+}RG䢆s#`\^k?xm>A =2s^i"2 KZƭ<;-I{yͼ*j .3^%h>*>7{|bO_iR]jSY>iUH>\\nݎ6м?m-=s~ am"uuἵH vU&PmNxC_$iÍ,Zs5l o :kW|K񇁼+^\RVд {У lٓT]hgni(_񎙬*sg9TJ8. ( cZ~A%7u wml1VTv ~?|Q#m?:w* ڬ.ѲXЇĈ牆k֞%Χk]ÝCt*ӤgԯdSQ%.@v Mt>2?o ?|}[7q*|h ) -!ij~hcx'PEPEP`g86#yj[ˈe8֦a֒Vк|S/u ]Yoisdt\P:)/k5XJ-DtwLcsq#1EaUJyl]jh :Ktݒ+bG)Å|ۜ5Ѽg_]^wJm{ZxeL$I?$&p]me{X]t3b4HO e }~1X_xOT{{ose=ͬC $BT7(t|x7]yy?m&vk4kn6l#2>ڦH|w˻M7SD6W[Cv#)M J o ~)a—ivqJ5đFc.EEڧtj:a44뻫,YTM$QaTpH֤I̶9xK8fpߴo xvQ<-4ccm=a XB²O<,?b[a3\1alS3ny[7N+~||.7<Ӷ\>qwui,p,$1GUTi'/-׊,tiwZ4*H[1_Z\8V|QFvO O~_xƉ⨡}suk<es,qC",ˢ1Y_7KүE[%s9V(i#z08Uld95'i][_w/Z~ٓ6dtD IJy^úI|w.˻M7SCk+٭!bRCS8=}(c(ox1ڤQ%-X ̀Gx]IJx^+Q8Xl."Oou4Qv2sk5[Z7o:?ڦ:QuHY^OvYsC_?>!E74MI-~, nf Ұg~!+A5:XYWKk),Ѥ9t2dvgg|WGӼ=5k_ImwO6!e<.ЛFUhIP+wN|#GCP͵wKE r.ƛk㏁)oOƝofFQ-4mjr7%HPt;QL?~'uOizxM{Ȯ/!ֲfu9e#*QwsSeٯGMuMWPh"lڭ$r9b 17]lΩO 0O>m$-λI F!v>q3_)'"'I"G*={iz"љ,nWq# gOöT.{Y9Spj7|5>0(>Pc4Eݼ^.יän,dPx>[u_x7CyjG?dW7uSOǢ9wjk6Xw[+ߴ`{zVq'J{46l\.R&I!rQʱ 4oh{;~OtkK'/RXϵ By4i9tGx_S%Ƴw1k\@-^ hHbd #?߶~|SNJ]5kj2H˶ 2 +W>?Z/đjzG~m"dgPg-!g潛Z}>š?<;I*tNl8 8>Ə!=i-P{`TRz( kE p,.r hn-E)r"Q E0|96kx~(ԭaZ, 551 688 C     C   Z" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?)X *K|ɰ0FI*?pg'$q/!K̬jZNK}rk?xniZ6_y+G-|?1ugd*̔)r~X6o$rڊWUYUm~MFf8Voڗu Y#KU7 /cj>F5GʃNn7.ݲ/ލk-5EveK+yjWk2K ̩"ە~_m]75v mn]722,+$&e9>ǦCG+4m"]Syk|t9K8em$+q2ʱPڒ,oyh5~gyMSUc5!u-/Z->m3ZV/ռG+T^,|A4 ⅷUZnݢ2+Ϗ|k~гEPxt'TMH9qk,3mneͻ5[4׮t=6}7VkxlZE]I_<{3_GO5o@o|WK?Knt{lsƚα7\j^38oʿld[o_ݪ_GSekZ*Mqn#o>_Z|u'-u-kOōckϫ7udr>?GSZ/Ǐ[YMY~oݳ~ݭ^j&[U|?6 :}ލm[?Xf/R8rhyh|ŷ^? ׈'Ide8~_/ܫtFo6rx&&鶴~+7j3Wf}eGO3ig?g7n5&Xɿn٨C-oej}M4ku{lpk5W0rhyh| _?^vzK*9,m?w3L~oG16>|Qm5CY}xnv,~tuvxX?d`֮4U[s}棛Q4SK9vs@ۿQ厯ok}̾d }~ 7M5inn{mZo>>Jw|4yk IhZ9O=ai-KG0X|S/%Ih zu?<5i-cw_GO氿%,nh|s=zu?Z?`S_X_KG7|4yk IhZ9O=ai-KG0X|S/%Ih zu?<5i-cw_GO氿%,nh|s=zu?Z?`S_X_KG7|4yk IhZ9O=ai-KG0X|S/%Ih zu?<5i-cw_GO氿%,nh|sL HU*2P9EfMxfq b Byq\=E_Np_aԟ@]Neo[uV]˵ρ:5Wث6-kռAXfdɹWZuc/k:ǫX޴oVN'fkx_A{#&obHwW_-ݲʫWleOԮw(c#u~ja*3\JJҔHW[_6O u{#YYjݕjVM_n3/V]rnTd4y_T/CNk={C -cicFon5I5)|'$s\m'{"4}Sw [Q^olۼ-snm~M|ɢ[d*V6fcQ~oiw o;UKTM}a#>x7\t&ö7n]Ga_Z m&K M%d[wz}ֲnl fde*/A ;Ǩa7m/uVsj}y.n>][믶\@6gӿZzA ;Ǩhxg:&WOy6X_]yuEEo1nZ=ͻ)waܳCG4+d[o=:O]]cVޟ}./G>7Wxöz11:^X4}=:_?_XYi6V6a5ke,M3Ŭywyzh,??9$Ogg,Dwro+h,??=sKxgYɯhvzZ=]ijvi7q\sGh_פ}exS_'Y4`k%|6o4|7ڦdW}exGhx:LMfk{f_6ovpۥ*KUk>O揲Of>]a)|X4{08>O揲Of>]a)|X4{08>O揲Of>]a)|X4{08>O揲oԴ>4G٧j-nڭWm}{Ím&aYo1dYkgXׁ~"HPknޯ+6q5o.cAĞAW 5õաpŢãFr,twl}|2Ԭ#~ ԢOm,^m&k_WAgC+*/(>6ߧڷZ|kh[;ɖ []=fFX3V?vdmXs¿|1XC wM{H՗̏v|if YEZͶHYfjw43^it|+WGfiYG}~]%[&rFۿv[n[)oov|wWO%ݮì4mHHEo{ɿwKUE]V$ķ[W# K|iWXPeUe_VYZ^_K%K>>>5B-9n&WfYw˻j&ѣݴ3.$~r+y̬ʭZjljƋu\E[F!3/_ <E^]_j\6٪N<ח>5fM/n+zYR$ٳǚ&K>z&_kJꚮpGX+vUV?¿ږt ,ڣhwo&󕣙~o/vk[Vyx~?G-۬VŻnFV_1wmeo}xRGԾ%-cԵ( $̱ƪ_Ӈ}_Sm㆗k]jԧ=~[_w-zMy%(kZ5Դ6Kk-=l١zHc*֩;EZo坊Q5տfۻrɶ6eh ǨYhk m$,hef_n_o^$1-dɑd_;km[jVۺg쿳u$QHz-6^ʼn/Lu;XmIh|=]ʲ37~ouq~4_oG/-lᣚM~U%>^8 }qwF2mͻڿwmgHkVf*ʵ}⦺fk.=5  bFv;/7^?oh _YwhѬw &5|3mZ 3k-2L$k~kZ&izo MKm/xO2Efܾb]'Gq|ffXZFݶ8VhhQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQExJo,PVڻ|3_ĽST'#4Fo_kk &|^+B7[v///k/G~I_P$v4kڿ-y+݄bupG^ï.꺇uYO.6Xʿv?:}-Vlw03G$ke#? BĶ1;k7OmU_@}#O] tcseSdvb?խhKI%i#4oVEmIo/ˏUWoʵjGbfWkEe_Es7[_ گokCg}-LXݎC#d/yC6|?UmjH|_}m7KJ^9c7Yo5yW̎?ycVt8mv7K7KӺFUw][d|}EͷnZ[_ևK?htg}-;3Um^5_5Z%m-r2'AKI.]=̞cmWZuK%w{H/˱~M;F/֟>xg.*u?wKػEPtYy_參]}UeeT#3 `xӽi>o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZZ>o>ߵ >o>ߵZ՟|*Zo|L r.U_eUZd/I_IrK+yۚ~2' ? 'Û=Z?k̭nQ_W~RRq]4m ][t}[Oivneܵ=.-1cxV].5ᱯseO7q?E|a/5oڼ*dk{~{WnaYfEdUow[_Ʒ'8mY&fUk<ˑp1?o^,6Pq4,;z7?g[;]f'yWIyko_t_7C"܌굪|Z'uxUlV?3/v?iaOgwecev:5֯}I5ݵkZ:#y4{wnOJ<5o]6ͷsWr“gO(R?Yde/O:F>O^Pʎw4{WD;+R?viUO.͆qofƲ2j{*y7̻km!oF,J7VXM[U,Z:{9c_={e]H-U,)΅w^?bu ;I^Y$;Fkm'Bga:XYn>\"$\1ojůa~Uo-]rN1Ҕۿe{jSGCv_wWq'M61X.X@P Imeܻw/UY\)g5Z>O7 CI׵[]:CI~fs]VOkxItGt}'Iյ+k$mm~]ʿݹ_Mi/OMUGjʪV|=u?I{6߻+v:_ν~Z5.Yo)_:EnX5uYxQWD˪Ik^[Ej 4t6/̿/ޯ~xvk{he_r\34̿nmՓ}Oo?m=Z;_)6Y2VݷwͶ oloW>"62Z$4t,lvgA|ZմjMY4ui'kkUht9EuZ=* ?l4SK6o-"_Vf~oi?ٶk!+yڏƋ Z"fws^Gy#O|D[T vkPu46aUUe_A&5&m&[r=Xs|*eRWq#3I"{ux^W\Zkjͣk}}c v_3jɹdjoşx6Lu[9.YY~e[n ?_l-o-yoۛt13|.߻ZZ?/~cm Xuh2ymmf_x~fIQz՛J ueV>oyvjx4_VP֗cjBSB4{ 6;mj+_>>ml7_1mڻPQmVr6q̫ѫ*̪wz8 vF-=7H5Oڭ,*ͷ~ex=?HZk37͹dVvjRV-ڔ;W]$4}SwAm&Ѵ{V{vgZ?_uh]?LO4-g{=߲Ƭq.̫yݫ_i<ڃc}S]AݲI 1l5wO 7W-M<ƒݷBۿk}ݵ̷wn-[ m\^mDL|wޠ|'E>i׬>Y$s47WV 6fߙ~}Ym/G<uxSY|DֲC$2I4k5ѷ"ݯ@ox}"}&_ڮ_j~_SxGOV9Vi$Wv͵w7u4QE|>% 4KOAh5 &n&~-čs}lR՚!4%_n/ʬ՟j^'եbSs*e' rLݬle۶.jS%w.Ycڋ}x/5M^HdXdo=֩oa\Kkmw5^o׮mbhծ$_걌})?JuR>t>XmtZH2w^Oki"o1w+mcjUnI-oY[^SvyQG7۾Z:"wqqݬѬj٫eun۷+uog#/h1eMS=_/lZZwq-mUث4:y"eqj򷻅YsF\Vk>|̲y-WѦԯUHs㵹gk%牯ViY~ڹqp}u2B~jE;.S*/{;=/S`cXӡӼk[x,+q ̗aTMk UaII6WWS=HֆHϷZd-ZeY%o-mxtyōiNGšjW_efWlV,,0.nV_K\5Edk8{c-SYuGVʱ|ʰѩQS\`.Sӿc_Zݖ#Q~A: :}ŻEn;Y{l(˜*2!~:_k =>KM6Oi$hmM"̻c/ko3XѼ5r"k9#fKٿwq>2_nǀnI$j^8|7#MgKX e9smӣ|YkqmqVKt{k8Ze/m+BO1oͺs4]TxZi:ycykjM$֬ol~bۦݵkԾ%Jѵ~$i~㼸Ѽ32̲[5eJXtXfտgJطۤw/k[k}.m{΋N>u/&qT5-QYcKFڳx~裷/c_n[eU_eV<3-^.Gy3im%3[ǘLʻUwWYo3AkKK_GHk&H푾_r>._Xڅ.|%5qbkΚT- .V_rz3~}ollumNO6v]/Eu=h:+[V\C*1ͻoWt?âi:6-K"]R9chcUeE* W|\OoE4x7:is]5\ffivVm۾VZ]ycW4t3O5,t0ͺ6m|Ѳeٓϡmilhڭ7~_O9GjG믕?-|l75_ŏx{K]!A[j#]/ʶr/ݷk[K ijo$Y/?9.6i[՟kx+[I/m$MR՛sGpN/fYy''o7VvٵfB"3I r+}\_'x/jxsCo/mVM&PaTm#v^:77^CwǷ;EUUs|g'u?k&i[8fUWk}Ὲ[gk&ui,j2hx>Rk=f+]\-6$fH]wʵ: ֋ynVk4uZ6]yḴ}bR5zMB.;J?d1ƻv̫&/?>OMU4]Bծ|;k$p {fIUfh_KizcjZmmk/;H͵Ok ?|/ x:Cj[UUMk۵UY/ŋ.Zqyyg\YYuFɹ7ޑw6ݴ3fKImA6{w,&c%J̺}7Rm2wn7mV<_U#.FT%Efue/-e5-?ZVXU~ofZ>U~&Coopge~ [~"GoƲ[_B4Sʭ5{j ޴WI$rG"BL _Ug>矆k~i?-|FiqyDM's_? .w {!-yZLrN\{\]JEo&q47<2ƱfDᾞn7mf_isSid+Zk2̬I岴v^.Z49Of/ktFVdY$X>js6>2 Bto÷kS9SzN_5(VgXCq Kq6$?ùV[OڠW[rv׌$VKgk5k >M[-ݹ[k}zG{O{݉.qjmb_Ck'.$sY4߻VWEkj 3,\*WMVO3GOv?]XLpR},.Y,rş? #hݾ"])<X7-xOmP"|fokLZzy[Bh4?w1:s:su bHqU|e\i:InX~ 5FY{WwVu}k2^^ݲܿW*t2Ы(Rmx7QԮ;y8Uvy4WQ|fe۵kzvO6ݨHYi-7X|&mX$q;47O|mKvicT[k*ڧ[mdUcn]-p}{@|Gj#3J5QӏzKY}+^4㰺ia3IWRQqnZ]\$?hk|k|ukɵ⛨MP5{xj0%;2>t'-z>cy۾]u 87j{E>r&)Jͽ>k|9|SI#úimgkpۤUs3nU[~Z־ kC|#NM=w p&ۦ^]ncm-ZjY|Ͷ;;UIG~m^7x=Rm,+BƲ|̬Fۗk7o+x_MPuT~}yjB6~J&1o\/t5)t]еǖgL";s-{xmo4>췚~so&YU~nܬ?i6EȣC]acoPhjOhs_e4S}8-Unk/۹YwV]xFK{GC֯+Xٯ#;LG#*۶=F_u ui6-jVĪH2/ʻ\lB{}^=cCV'c[h.ݼ-foWfUЀ=Ί K~qqoc$zY#mۙ˓*j4guK?Y,Znlw_je[&5f3m=N/ ӫq&joI:4z̶s,*6wWi_oeYK/8y[٭y-,L{O3cQ3XxOYki&խՙM_Q| M{[.|E4k v?M}Io3_Z .4Juknc*O΋ dˡIkBnYwUmmo1ʬ#J:D FVݯο۪ I<&M's_ſO3F>u˖dg)VkE_x~u Ͷݵ[qƲUfkO V/t${kVJvi/t­.hdi x}ciuq,v7 VYú=+I4-ov˶cv4ծl*4ΰԌnz[?Hrŷoo5oxvVV,µ-7mkuU}9\C9 J=gZ5WjI *3}6Zn/fߛnԎs>$~Katóx~-|}|jAԮlwkQ7+YF_UٓSz{eZզկԴ{{i$cmǷnefG5{[]ƚ֫宊ơ,+izڔ1o]дۣPԴ}oZKɴ>{I5mmwo\Oں{[{[X.wBǵj 鴝CZ^,23-mnVAku}>kqc4r^n!V]ĵ/C%_jjXKtqVܪn ?>jOn0q6ch|w4jXYZ(7ZƱI$ 3Z2'novYH,KƗŖ6jZ-}ԒG|\|P>5}3G5"lڔG#m(f_|.=# MKSt5K[KX[m3fmMGex|+4ږklP|6P5oʿi U|Eg*pXIff7me_/i#m6<,9&<+YWnۨwcGDzFk{xn5FڲM$lm3/=Xw t=z?h3]kumIkcyY~/h歬x}otۥмWue}wV]}5xnɵ?Zw6ѯg4lk?7kkjBe~n׋"umcmVv|KjZݝz_5oe7 +W|A%v ՋkꪳM$,dI667^ GRZo-wy|4ڭ/f<7iw&S\՛VRfh |no|3As*cyF|YlQ&lcG寜vsyVUZu |MG2[w}[.tk~Qo"Hʿh-Ry!}>:x[E76+,Z4k$KmWng*,6߽֯㸒(ddo.Mw|C&K%Wtz?̑}5|հsic^1o'hj$[6]wК6ݠr+GmhoZ>eCmYm*Z.$ge]۾]ޮJxV3i:5|⋩乱f9~5k22VV9cxvռ,7:=Ԋ2=Ŷ5hNCG|κ5-SRvFvǺ ku >kyc5}*Md.g&9#[U6o~mFu-R颖cTbʤQP,,9iNJ<3Cb}Bmt^4 յ ;XgkVkv~i?uzOI BÒI[rFoc]bTZ/b]-x9!YK>ZC㸿uf/Ug][ʿZjt7b##R*%~|Y746oqH+B6=g-j)_nʻu~S|S[ uy><|7 mb5h%b4o ;m5կǿzw&&fkRa&gH]wG"e׏^}}ͳ 4(ԌhlyO~1x6$1xdTV~ꭶfXvm*2jOGIqqGyoGΙYU>m]'g77#v16ڼr^k~˞$𖩤Zi>uirI.qi#FY]߼_i垱qH/o zv^[ieU/rm>4έ+]I3izmԑp|媲ijV=4=KE-Yk/-KKPYԱ +CcU?X~H-n[|2kˑ|syzzC.emA3-]y~g^Uܿ7U~?|kgloYAyyym'$i7moqk'O-t'Mm[[G(խw3,km;UQ,xo,t]6aifڳ\4&~_S?I?\-ڷ\F3FƾL3mV?h feoklfXZchՙY^{c#k6q|D_웯{_:vE'l]Ǘݷv<]2GMKRĖz.5r.7Bֿgqq.eUԫ|Pi|ީ7^X5_[魤yVf/W🌾CX׼Dk#x;M5e6nmx3nghk]VAm9=c6eh]Im_[|vI,[E.oggm[̸f5ܿ*ͷmu*{ tۯy&Ԭgm.,!>^R}+Wh>!5Zt,1%3F6{; ڧ<#+xqA34z[-ŽGFtrnU[o[צh6ZڔZM#,V6o=˻>&d|H\Kz vypjz_O}2-FקB['ѫ*|w˭{gyṼ?,3[:r_ݮw\-@{gZn?yWnx=[zGwCXzS"+uHmX>7j^9l:x1d̬t37+/ Zƹkߋ5-CZ>ɤ+K%aUio~mZ \xYZZ:G$3/3/̫ƻWsw^4Elc5=:5iwZ+.VeZ^]Oula[{xcUڵEcxwZoTm:7JMBtۦ5lEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|^x7Ş77}JFk9-Y41U5Ok_x6 33/۹ܻ/x֯!{{ujl_ ύkaTՑhbҗtza3y b{>Z]njj> 5vCocc=3~w~U^B߶7jxWAg,RyjKma$gO2QRgi?縋㿌y>|]4~Enu(sS|0q=5R]Zi?vmi?Sj!>o.hr~8(]̪/>:7'-|ji^ө::1ڽ#.%ef8տ]T5m4Zf~v/+^?;uHZF*l<pJq#\W|5Iᛉm~-;7[FGQV[57w5pV.H1B'>c;xmj.mYZݡo~jy.cX;j;Ȥ>$g!cmvmՒmWSQ׮m*5aM;^kQWr* I_&V5_V׭|fefX{LEcqU>5L p$6Yt˭[n+ƙ,kuVVtןö. 7_k{Vk_f~ ⥚=O㥭յԍɩ,r4 -[ݻUv_-'þ ^iq∴6ݮ#UX߻V]̿ź"uD|&xOԡE9<}+]UMx^,lԵ+kI!lWs|#mo^'G ]{Am[kV-aV[Hhn7GYֹ/|;O"妺%ˣخۥԷneKĽ6A,Ԗ٣41ɹw2 nVViZ[_ x˯G5G-36v߻@ϝ/OG mKImB;cfo;"OʾK.ݿ6t><]GzGV6c [ûvc6.<7]'Zm-cmwuof±4̪|eMǫGn~äǭjI6i5mћ˓_፨zr?_^F'mw]HǺ5W%|[| c[kpin?^c=Ք1,2GBFͺ^Yku w6FdVxKVF?MCpI"ȲHѴZ5\=[H}z5I> X0>siC7˷vm߼ǫk}.m`+uaX5UX9-Z k\VwT͊[$5mX+IȺFטkK&MIvMg ]/6w=47Vk{>ݙdhYw,bYUޫQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEIʬE-$Xͭs|fyFhwV_/}oς|/]&M.,k2o[Ym_iXQt_kV4*r#6/m44λ[jͺ5u^cMY$/?d/DS}kM!V>ʻn$M㊌%?W=vj>jI.٬45Ҭ剿Z'Jv/uedU%W\Z?-ZՉk5\ilLmFrm[zMJ̻v_WA?^mkcB.Suy$𽎓~b<Ŀw7KqF3w?-Wcf)}Iqv[sZ44~(R䏽;N7oZEfڒ8<7${a6V$r۬27͵Uf!y3]Iy_3ކ*u2O&o/ofڵWvR r|ngB'l߲=GkUp:쎟_ 3kPU}#퍚iOOuZ| ;6}O 6IkV]Z_o|>&tJC$4ֱq++nۛrޯ;ůᛆ&ϗIV۶5em7xu-Cմ߱?kΒevowHpKot } 2{n47۶ Z}k w6ךm>4q"|6e{ ,M7.΍|_}4:cc5/^G&28Uٛ'>\K ZO]YMoq q*|?esHIԼ7oiٙIf[m˵_$wTtˆcon~Y?5 v0_ǩo"Xn9.E0Ч~Y->'վ3bn~dܬ_:Վ{g?ryO?Suj5]G64}>m\j@|+鶰U'ØL5Hqv:j7• y)]s;]uZOv;>2ǟxY>M5YU#){(+>h=Iwn_O8;w.CWUDk#o⎩Y|*?ԭ4Z3up> ~/}KKO.6Գ_ܴ-N({}gXǖ>_?%/?aS&&YoX?a2xmcǫeÈpcLj`_Cŏ˵h9<طmizfe%.cw3x㗂|AEl}7TRO.5͵ffo ~7i$M[ҥQ^];^(P+VdV믄zc'40ǥwûv?Өh_?E𾥢4{ߙw~7kdo4EuMJyMJ$whls|P ?MCufKi!4In|wϾŸ 7W^|/giXn v3m_36kg_A &;juH.&Ѹ[n|߼mw|v|7(xMt>ƼcuO˅PaܶU*Ԗw,k[5 ] [,~VV)8IF6UZ+vwl35,zN-fY*^s;o:Emu/ʭ;ګHuo.MvCFʭnei?ş5I<7%ux>"֮olqw}k64_Zo ir4z$,?eh.߻|Z( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (9ʆ?2h_HkǞ+5ci$oW̫_5($]RrI=ڵ_o ~8oc/4iJGxO\]An$I-cUO-~f_ ^MèiQY̻Hھi]M|FxORk{mwGbߕ]k"?}oLVY^,¬W *u)8rqkW%MM'sZokVs肊(((((((((((((((((((((((((((((((((((^|eh\ZHFVV_YY~eVM5Q~Fzf}qymFVm]u uѶε{5K+I%/W:5yьxYj҄yu׌<;-'÷!nxGn{G 4j͹?TX}PoH$YaVWvZ^(4]7Rm[Ao-Ἵʭuzÿ>MB[u}rh|UV kjՕjTZ1mo# թVc'sZokVC(((((((((((((((((((((((((((((((((((rVvkqjWR|vLx6>﵍JmliV5cZ,Zc[43/݆W7WkÓ ,^]ۇӖ&Zo"Wt7-u|Q}>u4 o'ʻZ6_i|15mgwƸ捗j|޶&L&>?Rjx4}i?լ'sY#kyqk?4ȮhfZ6__Zq=G/riՅχeEcib-c wʾ}k;+|i弍B|3{ g[˺VCEe/Vez[Bٿ ٙWP~ &A+o1Es|FZ 7mԾg]2#̭@ Ͷ"6ˏZy)Y۶v9Efz$U١ds+"*zfˈˏmWV^GX"UWu7hzgY'XVfz(}4aeܱn((4K"fڴ( w )U˹[w̴( ( (4$qIUj}Q@Q@Q@Q@Q@Q@Q@ɧK"Ļw3m(c\F,-"̻-6}R3*+36ZZ* ;{u+[+/SE2kZY%f6ߚ@Q@Q@Q@Q@6~ MkYe 5j=߻{5ⶥ=q}v9eue?Yk*o?c㮟6Z?!]ܪWa_/#2ޖ?Tǚ|㟉 aok&j|ͱyzu{oUYHf#|ۿW|h˥=jUkoZ;Weچ25k( <-8n/qxkrӆ4Ě4[Vֺ]]7-oL1^({I{S,y xF[]V՟ßZUr+WUͣxni4pFm_hWmjq/tW֭mdi۵]g6F:w uU=W_ ~7j^2\j3+\,m]_>~%i7oj,)t=KX([uo~ *;[$\?VŦqkz9\kM~ݵ|3W:a_-~X-6Oi2Y]Mfmku 6 b?>&xZ+Hk^(vk8lhٗl{[v;j>"jᲓ>M|pq$,}hfX~oWvq4lp+FѲƿ+7ezx_E&v s󞓨~(ItsKY;k $2I%/l0m2̫V?5eqy[h~$C%HmwMٵvnXk|7| jYZmcY>en굥gKѴ;;"-{_ߕh^4O .Yq˧Ʊב$jfV_]FēK^*oA>kGkUZ 732Ʋ7͵OkFѴUVeX_%M]K]JMAtE$]]fx>=.u> u ?/e%U]Y6 kȃKEPXշ/e7_SG?Ũihv2I5A ƫ$jd_mZ9?r2n &MmVkyfM7˻s5r{∼'6}kg$Y>Mylffo-o/WW{ ϧOѬr,+*oV| |sä}?,iH[?n][Pr~_5k g2u'uWORi$YF6X_ϛwl\~&QV]jKƜ,pDr/omvú^\0Y5UN>xgQdC>I+4fe5|7>#xJRtGG𽽥Ο4w}M$4$7ƟxsOxQxoP3f%mHY5[ݶm.Vi$Y5_[~jjvi'p,MZyjsoP֣5l%xX-;C:OJ&o2F{tvYku÷˂{uhoPWIo5,bYfKemc]U]<#?iu~Bm*ȶ7noo˻I^/C? >%[m{Rkˈ'feMI lͻljZ|=魧ǧ޴V?Ջ {#{x=[K.カ>I𭭯t_.͡zHQy33CY?WQ&:!Y:WmWO[iH.om9$Vmݯ|/syq/[jW"F[]: uh5V>)5ǚ}ÝbWfr5[]/;vXimU{Y&mMwur__þ,ԵR9mR;UywfY-`eVd5fUoF&i\1qc_?*4/>4 x}u uYAcns[ZܻUڿ75tU/%}bIՖPI$8dho%de[kkݮm>;,mo¬mѴ==kh,YY~*o~|{F׀'X[=-mKԖeVf9>2C_uW?k^45][Kk5t,#Ԏ_tw[%_c^xsIfS$5 ~4t>mcUPV񕽇{ R\-u%Xfm\GFUU~ee׏O#z^->-]&7i4[{5h~hVOvmӼCx]*6VݭPh\۾*z\12Ʊyoj(((⯟PV o:9Ii>߻ǷF⫶_4c?[?ǫءN2yzC5Y<'A~_M]ZVݍוtZ]ۿkV2c÷Wq?Ǚ~!C7G+|n84An!x7LoևŭFI/OeܖIj.nnSZ1~KUZ5IW'ʿI+I*4}j~|7R~Ѩ+-twv]9c/k˯ɓCfPG֞];Cڍj֦3n5yz5w]om[ѫW|D-QeWʿ#xft!H*5}pk-/\כ]6C IR4#.q*mY~_/OöWZnQj~IfroZms6~44y7Z$G/;^oMdPbfc,$_Eo-on$#]*ﺿ5zG-GTmJhV !Ζٕioվʻy˘'|mڇP9_5Zm3m_~ 2x mˎ|m/̵ >:u>}34v?#Vvo|WA7^A.u k$K"H.WgR4~ZQul).!-JV5M[}oP55Sȹ8ܬj]xg.IJts7k]C.k6EVVFfz%Ru>%/t_ UWM#Pyʿ-{}ߚ*ǕF'RR#xHǚ|CoMJ=?P5 8fEVhaE۵['˺5X~GVU-&HdEfUOv'[Z}Pk6WԦ82%wwB]'?-moR=+Rm̎7_/nݿ.7xW^f;.;_O.&u˪,W痺uS>.i|HFGI/Mk2ݤeiY#F^o:޴׍چٵkеǵfJUUj <7I5gmuOHhWU[[vFxRz}Οq&,q䴋4핾mU?k{]?E}5մ40eEHaInUh~VdU>˫nC'y~w7ٮ?'~7A@5Λ}} tۦ9-eѯߖ8 3g,>69gx[]m6FOxyF*mԖo_k}/9VI*ڶ*^S-Ym5+c-VJ**vej>xVZ}>˭\[_FH4 q.շvޠ5k~+jږgh3]Ei$ڻW#wZ-RA]P. bI|wGnf]i[jsG^yy}omcyy#M5fhdo&_fWl|7y7Vv|jJ7|6Mf[Y&_1|-ղYkkÿ j,2h3GIuIy#Go/ Ŀ#/ZbK}Oldn-nՒFUeU޼uÿ-׬坬i-綼xYkmYY~V]7Qo Ť~<3,vд;5me} _xMmkǟc֫g=YfZ5icVo/]>h xx4}ל[7 [L6jZ(((((((((((:zAuVO5EtohOjG#ZZqryxË-_3^\<ʱP{[SEGnȻ4N.[R{m%cX-7?v##u{xxWχ[?Oc$y*F=[~Zl3 T%=ROrKM VܭZ=YǎY6k9H\ ]^\񤊫ɬ^miYW[5yYĹiǘ|Q1-KR|׮C)#Zͼ)27˻wjVB&xS^jdZyK/K[]w3ADyO|[}٪Y{syuf5fW}ps>ƒ{$R۾oV- k-M$k:fVU^]nSlo4ǘ?j ~ĮeX$=?5|PCm/Ěݭ734rm]kqkö|i6iʬy\ũ]xfM>tZ-|jqNyM8Ju˘4SO߇> ԍq"A59+-zPkF'VS :6 .7BoV\6J&_WПV kծm*۪ɵUv_4Ŷ-mg#urm۶\5B'(5%/t|CԾ4]ys/a?6Fwʭ_ɹU~G۩%C%h']Ȼj׃g 6q"q$~[|ۣV{9XRN4gƛ x^MVIeudwڮA/. xo:}׊5HtKKmTas+GmXs3|zl~x&~}}[KԿ]6964w^]ݹv>z׈} mt V Υy3mY[V _gԬ4Xd^T6 eUݺ5Z63Cx5s?-Bm/!X䱵eȻvטJυrI ɯ5di!fi?}IwFЯvײ]~zB1/5IkfVk[;R|vrU|EA񭽮 T.&%Π6k4%InEU]/P|/>#wv7MͩOq;X||wVoƭZZ4;IuuI$rZUܭ"UL\_麦xFK.=ZMMBfc|ǶM.ߺ迴ͺ]^_Ih\I2ǹ~_d^X^ %Oo%wQ טm_w yo-w_ n<u,jRlE,>c5q2u~o {}h{ſ5Hon5|>Jֲ}?2feo29դ]~VZooޗ R]^;>Yo5I?vHm7@R^}o/Th.4ۯ}o+G2俴o|6jReuh`AP[?.ivǵWʿPtW~ԷXW øIfmBٚ5UVG$jۛڻ̑[.IghwlmVoTtQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEsWPծ-uZv^NaU^sڥVzIX=v5ffg4|M+xicܭ]Ư4| Dwvp\W^>6;l~ʿW[7vOZWuȳ0boyoglʻj&4F2=(xd)w-l4}>eVhݻL?ښ&m̵TmZGc_9o>f_V]MzөQxΰɻj{Ʊotl ?yVC7qۮ-^G6X ·VwŸ-0ӏ-35z'k6˨5 9~Wf J*4>G̿z&Y7\D ?-yoxk'tyla~Y7++5/gYsT6Z-;_!Լ%.Vo~en|`%CSVUkH|+njU)'??r}F\x6[?|đ{ėZO:WeYU[Z,`_ mҾ%[K| W?Hu3ivҭ"yr4{~eVU?m/52I}=/ޯ..jr5^ʧPe|Ӿ!i7 *_m}- }+V5YaZdgu[_'h5ٖ65}iˤdz\=._֯W:5F}ӦǛk{C}_o&^ub>"|'W]Z}nfUif>e-W=I=/'RR5JI.ahUt3/|/?c-|UO~]ſn[UVݷk|՛g-i֥:no&mmvYZF[i>@kÿ t{˨Lmag {Wn?z|]7|GZnj|Xki.|9hix害^_m=+9dvEe٫J׼'c_oQ lcI ğ̴?i啟ki&V;x[}.ݳ7;Y]/AmŬl8.w5+[h'k ڳA!֬oFn8efffm"qwU[ ' '=M}zC:M˵dfhmP:择A_6 j2*ǺOmUmG _Kt"qnh汆Ei?VV_ok8|5ڮ6i7W[/m~f_۾oռ+jKg+}$v~_}=2ke&rIqki&#Copmp2mf?wm似K^]rFƒ+[nf9mxΩaÞԭ'zxOY.n.E\\k[ M=Mimuw՚MOHYnwm/@KxF4gos'$s75WG?mkڵb7,k5xqn:gge%m"r|ͻk{wWEH׀|Xt]kDдpM[5zW|[K]KA-5{f_H.ZׯEפ[,rG4jѲo5}I^c/MhזrMxC3y/=_#U񒧉`a8_BMro[}yLVI>fܭvͯxgS7|h_o g\7i+=դo U!OZO>fo2iLjpncc/2=ݯ̿WWlLc&yɶ6_9I\Aj_HgXmA}kU󚾯3ǛK2éwaVMv^_'MbXVu|f|/T[k6Io$ݹ9nﺵ04ǖ^}O֩fU5[Ǐ͹plȾfWo*G+nz\9G~Pӗo~>Aolv{jm|FzFɻ %gy[rj+UcmzKF+ӝoͺ[sW}aH|+mu,22H3^k#Z?-}oGwsxJ^jN4u=#2jM7'EPv|ՌlS j^Tmn?'j6ݫw ֗i[u_A3/6N_+ꬫH7v7YEt9da}Z><[C*KTKk=Bė3yVt4Zǁ!nSX֬i!vͷ=m4vV >e'`K}B mVWGt$yo. \K8txݼ%y.}n};}u"-hd?5qv[v5뛘[_6F_zz_4R|D1IpyԌ¥]{wzM}xUUڿݯ5OYټMuɻvUY~{KX|/|'-ݤlʫw_6IlX[dUtmR|&nN}5ᦇM-ԛ;Y~U5{nvYYWrk|j7mbn_.o=a*tcJՔeS1=[Y[+Y\*7_3&bkWӌ˹ZW'7OoOo }+k6SM崌~_-Uw}ϵ[2/7:Vj鶗V p4mG3Gmܵ_P ĭV-6H!Zj+nU_/Ws22k/߲uh~ ֥V$K{{XXcݺEY[wn5OxGGѼ#ue7\mhZ=j˶oe Cu *;n-u%-渒fMж$Z=mmMr-SE?d~%F/yYWmGq |>eX፦f,*&Z-~xfݯ ~"%v®Տ?{Ɵ|eK_xIM+lVn3}z8[fMZ^x/7 |p],33+7*֗iug]CRޛ5sqorHpifk/̫.M#ǤxĶ=Ɨ%[\iq5гy Umxu{-Kkx,kvFYoi٠  CR<'x/U\ִ)k&ʭ#7U*ŏMYhjko}> [/m|VFvɷnKm'XC$:;iv:C5cj{5qo}:a:ֿs{}>V,VeHWt7nn MK'!xA_CC}%7V=een37o;Ad}>LeH|mmo{u 42̒Hf_>_ǹcV]y~{6g%4y._~_7_MCMVe;y5B(VI$aUfVfZfoR_ck&k.}^YM|6ݭw^&[g_I5ln4B>-mѶhƬ[}ε Gqqmê2,1̳G"~oq?qs>&l/*?.nomvӾZï^-%]Qa?.6_1U/to_^jjWqw}5&5*Vx SE8=6 ]ƭf_͵~z=Q@Q@5O6uq_iKm:YV,l8mmaKkXcX%ڱZ((((((((((((((*lYRwaq5omBht|;fڪYn<[ka7L˵Y~vCl?lo.&wǵԼ#$XndZtc3 ?o2|Veh!W'̪ۖGE;[B36j,`]ۛe^~7<#j֗$s,yS4l[k}⧃cn(ogo WN^msMs.lo1em۾jɳf 'M/_ik}s]jެ\jUo,mvƭmvWV.~"TԐZ۬f_]ƥ;7Io.}6P.XdefZ5nok<:=h7Mxdyuq 3Z,v3|ͻ5sp/mq7qկ(j>R4k!f5ԾɷɍK{xݙ/Z|i/._\xI}y"*Y۶Yv+{W KoDV/KPٺ5k"j|IGjO<+o=4][E&5[#ie;I q#/ޠ oi{gkq&hԾ~b5ҳ.W*~VwW|Uo?oGޒ9I^Ĵh0U7u5M;R̺vȾ^yj<(T%V)ͨDDgٶ*eCXknw/RC{Zf.ڇOH̿yTkGt,3ŷmCEX5WYW RN%\;x;V k%O"mz7>?Y|dzu%QMv? X#ۻj yMᎭ-/\V[-i.g ^_u-5`_96m2U|ZKgīFG1-{QGE]swn~5͊ߙk3`j::N$ k?nOWX| {\-osWa@|̫*[n.V3,2}x_[dԬf_l5k㵎١omkZYc}s˰&fZ̗ ̷I,v_-v(x ~!Ro|-D- Wn28ve{j\4'u CO ?nhU.W*_QêYG$^A,qY_i2Ax& ]Fþo2OH6߽:r/ uO[mò/Oha]ox~keu}q鶳4sHydUCu +/Umk/>M/ZW~J[';ZG"G34j8e袊((((((((((((((((,#o%7']O/]=W_$_uEvMҮw_xzYP?) 5Qw%^fG(~(#noxANv%/m2$wK ?h jK7,/OjtחM%Fƥu>(4kA'Ke<%yk;.<^9JM]֗+xi}>n9➥,Z]{ci4cZƫY+٠gLJ5%#Oc~E4e?$̿.|iu$4n~3>X_*^8GPna[X$ڿ.ZF]XT*:Q7m;.yK+EoC$Xoݭc>}go>,qokukY&L5wVg?¶ۍ?4c}o>˷oǫm=F}zNln/UUhd?]dgoެMGI 蛮l/o 9ffmɶEz;x]k5a]Jhc!kYfU٦\$n5IvͶd#lkX~-?å[Fllm&ɵ n:Xh> 5VI,1%44my*Mu ?O z,:ķX,MEf2-R=[RUie+_퍛n*|*F*񅏇an"ZciH[,F%^ X 35.!mm;~WvK_ڋGxut>ܸڿwcI!X sB۷Mk>H%[;;].ݢYfi/#]70>Zcik|1Y~XV5W?ڱWZ_YV] Kxb22YwkU~о ~jڏ2ƹoQxum$r75|{v?Tl5[A񆳧I5o.ͨIo}n3nhh?W\/\k&W& }9VK_nfo9RPіmF՚Ѷ?םjN=k7w |-˺O3j]m~ռ?R'shgigiџ75G{y}65Xs}cԯGxEo1vm­@|C7o5-3v7֙.v7tە[_'W|7|ALXyO;|ݷvծG7M;M0G8h-c6|Uݻk6ifݵ|F[X$[Xo-4VUUwFIEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE6[l+G+7vC:ET%cfo?֦[Ok[껷+DY[Qt8m൏ˏ|\/ʷ/kKcǝǣXU׈,,_jZlZ}j˧*jzie_wux֜*}ծsCI\֭ei?ը,(((((((((((((((((((((((((((((((((((_qxnՓ~| !,{_RkRZl̿ݯ~<~ӱ+CþY|ApdrT:j\/y|Q^5 &\,-Yk>vOi-⋈B.۷jk߇>:ռ/(AInff\ĭ~|'UzyWQѾ2Wҩ/#ԍy|OM'sZokV@@(((((((((((((((((((((((((((((((((((-~ߥCrk#uπxͬ5m, ""|p?CsxmҖKn>>f-}q֌h81t#Z>[ZC^>ug3Fە[הѯ%{%k+67OVܶ{v׵f41hH2ҫ[I\֭ei?ž 9%[y|^^cNixwJ5GTP &Y$O{hsDZ_Ẃ񥷙wF |S/O/''GErS|Slu.]'\KY'Um[oڃ6O}}K/V;Ʋ-h2hpk.{Y#}-v~+o՚ ݵkT?-ZVq5P(o[Ik8Wvof=@shjr]^HC3T\Zp\w/@s EJjz-%4.=)is&_\]Bٵo-"ȭ&۹H省:QEVt>#n5i4K9u(t+p2ޫCq$2K$-EVѶ[z%jc%[ZǷ̒O6ڴw--6v\\M$/V-;VI!fzF̬V tQEQH*TVP5<2.䉷+/-PjvwvsGzM(yu<{f+2=&kX.Vvͻm6OiqOicko[vբjZ[5kc Ͷ9.fXF}R ܬȫWf 4Vu-H5K9ݺ;hWo_vigֲ}Y#o*izYjVf co~;Itzmooi2ȻZEPEPյ?A[Vo"[mdUک4^^u-:;hgȭZ-T5sO5+Ȭm|jHƿ&e__8K6e]Ҷ͵V(_/ϙ"ˏmCc3^Ckpg7p,ڭ,nxmVXYVuK=oM,.#fxr}ZEc^xvuiچ_Oy}ߗvԚHchfmi|[2\GkZ}eFӴh iKլu6b\,EPEPEP5#Q7׊՞7ռ%fTu tAo-aL *^t{dW_%o!I;nmZe.^Yxf:#'IIZϙ AIuJۤ6fmZo"ۻr}4!keͬkoo$v /˹}wcӌcyV&RS>7$~MjO]I'<Ov}Je5/I'_K>g'cݮ'⥄vZwMm]+B׮4-W*wWR7Vfǃmuo]ѮV%m.eeݷutZ|ݵ=Q!DAd2.ߙ x^CfE;mzVyVu%V#oZW v|<ǡ唹jUOc|&:E.eHmխYt6^W9ª ,sno1vwA¿/ú*dXcF4~ZrU/xG{/O&5~_6ݻWx76nCZϦ43.Z<)gIuqk%-fm 5tVյ <6ߗKDc)K弳5+卾eeKikxe +t^/ǗZΥLV6[7ͻ48o_b!:rfϋ2\x_Fuh,246ۛVdYe>ݻmeFmS}͙w{j-J8n2|9@ϔKX MKXf&IK&O-pö&UնuZ|33ZáOnY||.ShK_@ֺM|[CmimhmZ;?$+3C ܫ翳k\K Z+^;-[fhV=Bg/s_qq 58jUj|sZω<>׾(Vֹ }J(8aO{ԯʻYv/˻mrNJ(^>R5-*Ymu)o/,ٵ?2eo-co.Mۛn~ۢ>X㦂1xz5G ֭֠'333|̱d$5ƓZk-?Ğ$ĸ̙ZlF_W99UeEukk v1vV>Ekd۫xSռc\5I#G}5]5ڿ/ݭ^#~>mSmjվǣHƭ'ؼԷ|V۫(k}[PF ӵO#ivOFYkզqe|jx[۞,IA}ŧ4+%we>UluZš{{mw+.wڞ>T|U'VW*׬̓PHn&pp kc_wmU/I񿋥zNJ4 ٶqy$]Fdhi#on_o5㏇z/K8mu=-uc+.Y>$~b[rsxV6IuB¶7n-ͨ4}[{yVʻY˭u9lG$Z ]SZNx_O5{W̑Vi6SxN[yq./]M{thc]÷t{Y~U=Y:泥:4^ VeYYxs^+L:}Ρ} 6[i$Kok)Y&WkHUw7UU>2tjEwu&i5;}Gj/eܻo>-/麽n+}&t6ܳMj̻UW@#x?N|KО$ֺMk kS~mfѦYf6wm_[wWtPLj&?QX5k{AceG66ouo'wi_]|@wk':ܒ\\u.O%kmR4}sK߳GM$2mfHY[jAMmrJռC?>4OmeKx̓|ouÙnWN hjk;ɦWɸvc+.ߖOݝ徉o<_nUח7mU$3H*V:K{~j׋uuu 7_V3C/g۵VZƗxEo7T۝^i.&Zf4N[Ē!A^%4R_Ե%cT]|6fK] ռ;y4ZVo$VSC/CD_^xK׵ ;Ot?j)cmt"c|m_x05q6Mt?i~".g٤?m_ץU Jt4?I\;&J-'sX:yZY̘u_4[ }'[w lVkrI+ߚ}}ynjq5ZImukYǚCOKcOM+0ZfߋWW -%z6/^nꚅQҍOicJ%$ Tuz~VVԸWu[h51qyx\[VKdܪۗt7\xz2./VdUk5k#.3}o ^]Fd *j_#5 ; ŪOWCy/XCVpѬm. ber[}&)/M2ݵui6̷Fuo\/^k1}ͬYc_|7|EGE|9}wM_4wa?3o٧.Zq*},bu/ōյp5j۹k .Ed,_ƾ&񖽩x~%ծu.8f.n7ʸw %,[~ݶKGֽ?g.S_k@jWR·1ڪKjƋ7(kUŤvƿ|koiŗ\jy>KIǫ Cݟ4[=KK||\UIQG.*Zr5׏pֲK?q*z]xT H|/k~Sŗ[vϛo̭w-t>,h:uϨZFg[i7,[;뜴NiO?޽2]]KaFo=yگ,čKnǶV]Ukgd#jɷ_~jֳՍͮMKx%wWs/<>*>ǚrh nK^+G4S71W7j6kZ`_vtЫ*lt>5^}_]o/^x^M5oI<<2ݵO_ _}Kz]]X5pno}cѮd=QV8YEm/f]d%\Ũz<gu K(4{eiMmY,}&[4qUz}b Ŝ4mo&om_wWu75^Ң[9#\lv__ǍrDz-x_Rԯ47f۷wwf_kKI֏$nc$qinGW8W-[5ú7z+,vвۻo˹U^(OkM_Hq}Zq}Fyq[o̭_Ix~6~(;XdW8۷rZ|,]sGAE,tZտ{v@ھ]~&ֵ=SZXmM>+顴g$[ylۿY~(^ĭsVڬƟ/AТ$[I4kfei<]7¯PrV;{KKHU~m+2Io)t2_[ê5mwYUguxį^x|7| .$ҭlCj[ɻIr.[:69/K .4y5/.<}#ykmfyڞ&]7?:.bMJ;/m+.꯯x(OYޑçgs-/h/7T|Fb=k6zek",7jx^~ xSczZwMnEo[7V׼=I6Wl: msoxw?A[ed^ݻ@[5/WM<$ -go:Imfʱ1ռex/I֬kZ}]'R.)/$a9$fwB6ګUZ28-_.8]Zx_Ö:#L8cf"mP15m':OhzNkwK$UkUFfWW^ ^ &kֹ-?RЧkHaGoˇeUf>m۫7}Iޑc-ս1 ۖEWj⩬;W[k׋jYƷ3/dWs>(֓T~0fd׵Fu .,2=mfGt/5ďk6֛ZGBH쮖ݺ[l3I"[nߗn[Oy]-ue΍f%O?nvoNnm7TúDc}-m5݆M_hk_i$ګk_<;q6#Z.'˻VXf6km}_X+ñxO&/mYl[G;n(((((((((((+?!ujW mgoI$5^fجݯi~ %NxVa87^ ,]nC_S=S容?VC˱+n2̬]IO_⯉at]]:r*l̿{yW);}, Յw.cM\OޓCkY弼k|~} wM"2rG۫Y5œOXeZ/yC{A8o$UjaF2cq2ȶ3w5_Y,ٶI:+G}wj y&_(Y#kCjkl*:ׇ5Gdm۫ؼAσٴ{_.ou]i^b9#y%<o\57[hv*41&x2piFu#|QodբYg!Iw$\|%k+{|ʲ*jK~Z9.o/c_#ګdEG]د#[cs1_wc7pn5j,w ++|^kljdŚ^m.aI!]{Z/x{Moݦ[&Z /wth~ _U6.馓kHׅ1p4?J2/"f_k_"kc{ExvuXhn!|7w=_]7jp)c $wxrM_CizVnhdV۵UUV]7}g+K&}G}Ci{I'9Ze]_ݷݮ)@n2e#宥Agu[idvy{*sGԮ"Ԛ魿լI3*i6s\|G}vH-mn46I<ɼHF~f~y5k^Oѿ!=KP7P*۳*M$kwޠu8{OxFѺi$ɏnYUUmۤV_WQǬi#ukfmZnk+~oZ>I.,#i!l*=__wĭG[sRMKKԴCZY!gyqo 6ݭn&fz+Oqլ+izmP\xEݵ Ne5/1fm#,jyM⏈eOxeufKY-VeTM?k*@B^MOj|IyJ+;!VkI&[k+/^ouhf=M'G+eYYfhZ_w7>R|`O5 sMnwK(s"вǵr]F>M_I,t+y,vS$Fڌ ۙ6+.дWGgG , BPV[̎9[*#9|G/Zq4wKG>Z7]Sύ-HճZM5\KG^-f=k/urI>PcK{"Ef_kxψwC{g[[.~dIrmwW3{|*5tqUղͻxvfԬMC yH?M,\o k4V:/.ڟḚο7쥿SKÿj^& Uc| Dki;7KMXlM⒩9}UiՕgK}M1xV9dkۻ[Uz7ޯH+?:6=Ǔu?&ߺMw:ֱmhk[w}WGo_V 5emKX2*jxMw*Rcd/xWn> x7ׇ|A.%xvkm{w\/K+h>VFeNJ>h2֮;B_2o28c+U|?y&{5$ݵVN*3>A_øzgu +34[eo/6ϫYu Z}JSt |/Aխt=2 6Z 3,{ޮsƟ <mhmRP߶oVXfl2]!&%mխc y>fowoWY^"~\VmKKMU6oѵr0¥eᶰt6kykdYY}7ݿ5p~|@񧉴ۍ7FOnm>5[{与i$U|UGt}Ed|Y}6k{EU+]FZ=m7V26>qujQ}?/ I|V~/<; \qM9vnVvׂ*aa𮝠mwQ7]6w|5{~xST }njT΍WOhU~{wP}ῇ:nAco y-6Wusz/oAMլ|v?}dZm_QFi6Jy}Z¬U6Uji n٭CR[$Ok䬌Ws}ՠ\« KV֗&-?&I'̬ʻmmԷ_ǭ^EZi\iq]/|Uo]kuOwX[J;6;+Lr~_Ue]{oZ(㋋Y{Gkk={E-oT9$akVUD,mܲ7u} ;_zO w1Iy4SitM$3I6ݹQo°.?n>u5-gOKUUWl6@Gx|5RVt=U[dXcIIj;k6][nLi 6OYeEUVzo\j?1YYUmk~!|1iF[{[֛ͣ&VK$-k9s/| vKhUc[8c6enݻo˵j;w,zխƃ;J).UO+Ck<;ImZ4{Ǫ5妻qȪfЬg qV?\&:_'ŚJ[4SL/j^[qy{cu䍼ٕVf]m][rթh>-CKE5cHiUeݻ~.*mt卿$ \HЬ\G#,+G˷-w{FlXoZS^5±vzT>hqhzS\5pE'|MkH[mzo >|}^h|v3j n_ ~\j*?5x_Y˯AxW2\i> (z‘8OVCC`)rW? 5FwW}f-$E =DxebE;zRǯjvE瓨/Ex?rga־YdWp ?Ewb>3d;\>ZOҼfNEvUu-w+00: CW%o}/øz'J( _ڈζZҢp_ kAEWsyNXL{?y2h$(=(?ƹaKS!1,rsj=+( ( M.ddԁP}N⟂";[|B\FܹǽwQ@{ScnA2F̏FS_H> dp"S>t=9 5ly=L9Ŀq{2!0YVՔr,ndzexMAoS 'w*h+Euw%#UZZҦjT|FxڶDy{)҅~yλzۈgxPtvjQb*=mdTI;d2)āS颳MP=F@fV1U?6砶NӪLZngؓҍ`&=zh?`8{Dy2ϠqGBi1vJ!@iD",p)6uqNYiUOB]GlkU (me_+P3JR`n5dɼ^HtzZN[RdԗhU斑foŹ| 8b?C)L*`љT -nԄm۳4Ԝӧ մz+v(YNsR. ?.լyŖt58ѧ*,bj"|2u 0zvTTK&yL(WObGI ͺQxͦ:a(c<]t0,ޣEvRcF͡UQ@Ű#zMEd?v0D¾1m#IL{5.iTx;ɧ3KSڼl<,8M$ (ӪGZ">LX ;vPVizL#0Wf{1(86259-8,Q4ND+fE7TeV{b,#PdB%=?S.p|yxٸ3tҏ*]ű\䀑v}a dTS *?pexSvܱbXjqA󊚡ṢO(_ 8:#={`2A#8M%mĀl<)Q0̢klˊMIIg 30Ǚ:?nK7<[9҂&g0|AQjsȌSJGj8wAxs2vrƑ\k=h{~#Zq ${0![ NLτe;xkDwP`0 6VB;Xv?yߣ3C=.Hl\ #w"$oabZ/j+k1$L3S9v+a22pbP Z˝WA'd_ϼAB`%3/D"sVUtN@ dxmߴ6a؁h (sBIT|dIDATxyu?}{#")."%JB) +-ۅĎk :E :V8MݴNHBNe4ukjQkJ4ESgy3{ǐ#.Cr(s;ۚP2rs_@YO00 +9~"&rD zVYk-BaΛlW~?|~f,}:scPBC]h& !񘕌v0 &T Ji ֺl$ZW3Y.G_h^0=ml/g< G,J&L&-ݝJM[ "F;h!2 QA|RRNR%xU^D[?޺ k{;}B˧R؟Ls}]bb|<|tKXɧ(FXD}[0D/ ,/i;nS+}T13<>+OX x,dL155Bw_Ʉ^?ϱo'爤@2N6^&LaslԨRnsyΜDt֯ ?No{Jk{qI;Lm6'l?;e8Jo:Mx!KD5'Z#A$.Ni`|J1Qf\-Kt#UDJv$ŗ67{@`REtg54n\(OgX+DJW5L{7<`3Ӊ.>R* n>:^ r&IMg[gS9zݕU5Uރ؉m4R-z{vZ\!ShQ8 y†tB?<{&3,^h_B()! Cb!F$#M|PwL ``Gm緍O!AtݩHA3K+GZOH˨k?-=ݞÏg||l"F23kghze;I[ =m:Ȑ :y;w"IK3ع]# ,/l3X㸺7Thu ʤwWkIDC''GE(/uw~}l6M:Y,M@Pu^d(7I6 ~8~˥ڥc@YB :$xUVѾ'iGih Ng)w&,s2:] фL&}GMmN)tl3;+LgHYjp:G&2tJQl̲P>r$]` +!uTЛ`1|CjR qK5xø^9O2Irǫ@|&q+Eo&l~۰2&!$AJm{b%݃$! ~)tuw%Z'w&Ɔb̕Nv$CyJ3&u| 4R;6@<>T(Td(0r~5<3Sl}?x~._'ťesJi If.=JXѡ-ZmV>wn z/)>ˤس |KGQ} Ux_$1ҵV3#x$.d!'}k(EW>\e1~?H# fZ!LV+@[b.ggrcC8tmeeo4Zݞ}3p0=m?پu2skfN-)\Ӱ;a&G>$x"dg7]{ɤSum^DinOqlrmcR;Nt&`hqPaI2oH<8!kay+*>W Ǿy*/h*IuLL\=N1D&P^JIM006V +6a݋?(-;b*@Tu' )x'aRqT$,H,B/?t綱P]sfwv.^W*O`yBώlNLLn"eCҸ~0 .TCC K̗drhfrpR[DRG _Zbc4=IH#B;PDRANW*F~l{Nn[6 h ˥3Oڤ:tFDLm"R>&,ΠB9tO(_cz PJE41#0L" "@F Rk@Uհ4 ۤ;snkab12C*y2œ̭Л!grPt:||찳.=G(7;{&'@xDc ב2#OzםG5ޚ/pitkb)qځD߯*Zc\B+\\klD7WXs2am@ FbueN_xPdػ}auzzoAx $i=)g}V al^w/)i]-3Ô~w0>K6Nփ4DJї @ɀ=SO}MǗ!\>*K%ghDQVI%b)AKYC!LDdCﴝ- V W2fI&X{guL)n~~'1,W..& z48ͳC#c8> 4[ cCH%JW8Flf5DnolbK,Ork8^?ѫ E1a1NR@s1 -cµɐXO 8A@#=e̹ղ[G*#{~#u W2xb` iTۅJ2NY4*G8qE鋆 \]0HJI=L0o~@O(<4(nY^R3i+\;֊(J!%A(qZ+%nXxDD@H\ay-/}^9+Yi'\y0WEh c_DSkEFůH)m'ԇ߉)b8s?B;oU~LtX;=E[? qTܢݜ,2RHo{ӰI8~Rj}pBlYJA`J(NؾS #;Ʌ 䳣~|iRq"V w ai Daxםf<]KAitHpk8@  |?1tqٳ[՘\R[\>Z\.gAr4}Eˋh!^xhBT*(yC)Vĩe0f^\ E)hR7 MRMdz&8%Z% u[/ΰTxm9!s[Xi4 ZkTH7/'fPJ0_}@ fspb O@J)i!%!MCkӨRRQ(U D];!V&5ǏV[fBJE t'8.25>{mnTa$q) Z"#RT=Mz26qTTtG0)lz;G܂iĮw=ͤh4SjZEhwe'?H%L&a؆IwdaN0oEQ%^]LwG>PUO´R C_&Fwcl8@"snJh^9 2kv}o7Kb{B\gA$l4oYBJ.ZJ"}].`0 %Ha%бʮ MO_yzl;-O0WJ-Kퟮm׾$^i7Vܻ51+i&z)zV+IH"X[AmПK34]\/`L/v|(PRuK|bj|Tx4$e@ ;R]L#TڑTBI q*vjkŋ;[B눗^Vq֚@*jfrSl؋eڷ-|jJcd-,ndn/{/Wio_x&X0|R,|A*m}Xw`mLWO1ˠQ[n`tdM?R*͟;鮳7{/ lh\ռ5yI?4ʮNl+ܰfR#ԝJ<%`i"뮍Ցdhx//i{)#_l]d^qB V "N(7}-?Mg'5G^JJ!d\Z 0ټy?vb"~NgU80d&x0$9.? c$ob!GVrdp,3lbd1N[Vb\ƺva/<[u QijieE7tj+ :J4\I٨kV#F,7ʹWeO6B0wd,`h JY,1ŷ]c,oF40w2r瞽c:EG8QueD %cY\<֚Q) 5/|wִM Z?g*o.sODMFN"ߡxIcl o?9BnzRRYY\ _oxnAjq9Ԥ3,;e $bhJ<=\Dx5R\.PY)( Oi՝7on%w}a~pthKْ{D Ҿ獷[̈́mc& !لMeMR4-h'h ZLqivRJhol~~ 2OwxG.G&'O`$rVmmOPs Ri74jU< 3Z诚FgoWmw}i9ӹk\6;K&Sҙ tl:IgҤ])m|_kVDn5qZ-0R !<QN~vI޳ks~+, / J=eۉdl&M<Da sGnk( 8>fhJ;U^6& ^GFeBmZoFӧ9 -ZPMHsV7k%b 3eg,mD+'hGgw{] `;#IENDB`fotoxx-18.01.1/images/view-metadata.jpg0000644000175000017500000031253513222767271016372 0ustar micomicoJFIFHHExifMM*V^(if%HH023170100Fotoxx:trim_rotate| Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?|Cڤ<#Uf$ '!TVg}LIgdQs> <运~gg\OB4/*G@<运> {{4/(OBys> <运~gg\OB4/*G@<运> {{4/(OBys> <运~gg\OB4/*G@<运> {{4/(OBIl罷9Ud.y:Wizo [hl*<$Pxҝuť¸%_4/+4k{.5{6fgp{mwڠcdE;RԵ;/5ub%'#Qml+sOB4/+WtOh%4 𘕚C csgfx*) mZ농w 61׽$!G}xb[_A5S_ު H _UKv~L݄͝1 v0qq4}.qi_Q 4}K\Z(?K{!9߷hN χm"Mv}"hщ\p; si[ap} 7Ww6ŚΦ "ěw ,bF8z'm,5mbēh Y+eA8运> 4o ]Zkm]f22%T"y'qk.TQ-\OB4/*G@<运> {{4/(OBys> <运~gg\OB4/*G@<运> {{4/(OBys> <运~gg\OB:1gv,T;;A Z( Fz `sq˲Eɂ? to$yvuaWG2\Ams>0FYt9-fiI3sr? ,ZX$Hz  CH%bꈅݎ2I/]Bk bd?*oʑ* ՘d" "6u`URN {8ė37@@8k]G+%D|Fdt sXG.5)bX?s8?ym śc9W=eY8f~8xF( ( ( ( ( ( ( ( ( ( ( ( ( ( (>q֍־௅`ͩ걃нCPŸo }h}kO7ZCQ ?*"ϭϭ{)kP?O7ZCQ`<yy{? jG)kP,o>o>AC(? jEM֍ֽ5T?EAC( Ѽ׽Ÿo 5T?E7Z7ZS ֡PŸo x&F^ ?*"S ֡PX}h}kO7ZCQ ?*"ϭϭ{)kP?O7ZCQ`<yy{? jG)kP,o C]Ե+K;ǚvU19k? jG)kP,c+wS ֡PŸo sǭ[i:٬M ayC,C`WŸo 5T?E<yy{? jG)kP,o>o>AC(? jEM֍ֽ5T?EAC( Ѽ׽Ÿo 5T?E7Z7ZS ֡PŸo x&F^ ?*"S ֡PX}h}kO7ZCQ ?*"ϭϭ{)kP?O7ZCQ`< rz4hU2@{o)kP?O7ZCQ`<*[VWMV1Gq%\{)kP?O7ZCQ`<"K1v߱@-6a;I\ao{)kP?O7ZCTΚ+JUgFj7f ,x<#؃~6~*iOܽw_AC+ @%jpIcx*sOzΆ46ckbu]zQ]' QEQEQEQEQEQEQEQEQEQEQEQEQEQE@嚸 Tn-fڜY_5%a2n+tLZRxK!5!mjKZpw墫@ `Io csF-X{K^B+*Ɉ\7dR?1ZX]"B&*ON Ik,4DYCIm XC{ k[qEs"!r فAGTw^׼=H}PH,ݕg[gyG|~2ھ}>irOkm& !ϖgw5ULH(GQ"}*J(?<EAz֖Vs\I1QP2O*fxLޫJ+A9;,]M}iŧ V3u f{rqeNܾMb?[I9UہrabݰGsV:^iOhل[Z2Hc,@ 9< jZF-ZGIm 752.Kn''nHS2uG]R?~E֢ᢳ qyꎡ.6%_"QyV5)5;rxrkpOu I .ΪGN{';FN-[h>:'zyPI2INrFЮ5R,pW!XdgU]{X7ŔI5ɴ"# Oҭ|4/o~-cIQ>R<Mrt-WVԯEZzAySrT`BtkiKJvFh GO}~kS;X,[=)Sۀ{qzݻ/ltkOMU"Y^,mikz^>igkwo=F1 H7~^o^hiy{ ŶhcQb 4e +gz O6(ֵoot(anO6~v7Kv;(UFIyvMnJ^2/LO"x`^('o%.szF[j`Szg/>t6WMB{k@kdVm;cSk )@(0NTp'cTO'cY-M4JP1`T)ہֺ{_ϋRY,mJQvLeMCwGu9ӓMu}OXI5Y70!7@C]Q"QCcBѭIum.Z2OB:Wtך*àH-.Inb@TNv_DQ`5+O.R[HnyBB`cӷҳ IGo&}R_2b3AamE/hOuv׭y4+ TቓWu5Cu=Gp_t5˫;w-nFm*Ǩ9R[_:? S۵4n|'Pqnc*JYxȯ&τO=o4A kt9 0vNs(8<׮vgyG|>%Gxȣ(RQ@(%Q"<EIEGxȣ(RQ@(%Q"<EIEGxȣ(X9o5xI e}z~as"Y#;|{FI꧄J~-ֿγMtKVX̋q F# qW-/b8^mfǏ|[i[-A$חT<nzxx[]_$qn5.ןtUˤ4_y/՞Ey]E]w6q MثGSL@:So~.Ӽ54&;8\\0! n'idD,Z}5Ys]N &`L 5~#ҼH3/ x7Er,.` yUgbVpqמ)|c:eIA__k'NJ}7NK8tX crcκes0kh4WF&_6O {aޗM&FN2Vvzm +ʵ?:We]j7ZfyUX݂@ v:)NtF0 N9D?I}tz+G/jzo=-S\[IHj@޳o|_ZxcŶA2[ H}"jX f_k#g;x$wXKPI4;/m&ACc^|KB{0Ibeg^ P[I`tNɢ59k\I6(N ((((((((re:GpˤKa%ٿU{{iVsKm!n.ʳɗW$'5U2PQEQESW6U;[wحIl:ҭ]UnL[(hE|lp==9xmۤww:6H;rnS< xW.Em>pIbafr`3Kk~5֣ {I<=/_4UӮf|1kq<0]Sl[Ph 0\麞aq#w7Wb2&wP6b @*x{:?í+C%Nђ20Fؓ#qT_šW2\@LaKF x =sD#kǻk9 ;BcnsU56Z{hn:v$~j gI=+þ%mcKC[q_1?w12Fx׾xK}V:Αm]_H*e 1`2l0Z_o&B\,r.~S^>Z#wHNmN}*;8 Iwgʇk`'x?s /RMAOޤxUQL6G.@5WO!^-26XdpP|7 sD|ߧꎷu{g>FDszzDڢ_ɪFbՆnٽi KڣZtǛOtbn"f6yAM15/.Z(mg`;Whm~t?1G=83IU+\h΋r[Cr|!U=xJ>(CMOfmy0jzÉug56=LV&&\` i~ǣ-vwj;}bHC~yN1s#RO3ۨQEQEQEQEQEQEQEQEQEQEQEQEQEQEsn\ZQ5!$n8$q^]x wZ\n5ynbe!`t#j5p3G#7R5;#B-6t~"hߍWV&l֬%֦i~ Gֽ$ս0*:jWoǖǖǃ.*+vK/8tvN3@];fI"ж0p%ȱX(im91t!N,w}_lyߏ[->uYŽq\ |EwZ.و !rISOW$*lߙRiko][O?kmoNf ,?p<<~($Q-4Ɔ6;FF*2?Kkt=^|OGm-]d622ݶd vӒpxk~8]+Bs,Hat[xpq `cB|C[!n`o̐m(WH]LqGchX?Z^Wjm%OWoSV/dԮEה̏ Hmk}kEtXt9, Qٻ y,o(] pŸ{_C5|+us|>9V?V]Im6uw"8 rvLuԩ n>(9((((((((zC_MZԭNc&WҢٝdy3 x8#?C_=#Zm;{MVU']i rqg%\æcz1]_=:G$sqyS+z 6dgxCxHӭ즺Wm"L$N9c!'3uͣӖthm I$$J .4zchq= IL:df^Ojjvc>3=IH1Xmrs)\n+͜˥:[3 "9z\NK}VOXk'dRr_\6g+῅<)C'rӤ66@\a!x3xqLZeQ@Š(jWka]^H"[ҲGxEYxgW*acB~CQ>Ú6hqIkZޗm}_GVBu 2 7EjA'LMԥ,Kk;%%F5 A8`%?׵Z-#vhm&K崍0';v `Uu[mhtϲ%I2㌩.8SןV/~$<uMGmCL$jml `ABkZ)B֗Sib$/pP r[Hn-]SP:Lj,25I$<Uh{}zOG6$0aO\u-qE}G˥2[d°H%WaoDރhz%f?4i%]SxĞ#u3urhzV0| qNx?sx 9-5)tfJyL#4L l:غ粅;X.#:?$MKڬ6-lV3[vw;K*;k\iVpi6{i4rqM !\N1M̗]=B??{}8<ϵ@M OFwmcYiV\)C":g,goRrz]sZ ſmt TgrCK}K[|Wddʎ.-aͻ4dda ':RxOx;P|M|nZ .9MA]Ŷ>)Եi|q<~Z prH<V귺W+5]Bi\B4R`u>KkHΟ}&nw0DV"A0r @W״]OKerr`}B4rB ]w3jav6> =?Ǚ!spy8 #{Qx]=*5ڦ-dp ^Go{d^o ]hiGKđWX<1^#k72\Em K`3 U^ J=,i+mejbX?H<ִ|E=>2i^E~]qB=N^_ֿ(ZMBB-.ڐ{y[iALP ,@Zn'Ywk !$`\|_5x`Qӣ-m<@s >xkC;ˋ'4(呜RNݎ/?E(|@|E;SNe,mMwI\ȸ{bԼIhڼK}3~*/xoĚbu֑fuIXKp *l#*) r3B}]X]0t4rta?"F~'Q74|Sd#W$>[cr;OjCi:Gm83\>%^Z.-ml7q+WZt}K^4mIs]'r:*;H]Vid\Y qn'@?:jv|)^j7&ђ|QT[UasAMkRDk;Xe"Gn z{VX#Tƣ=۝:c+onJV ČK[x[4|_ivRjUݵM m>c2e`xeZ0{hҪ#ce*FN+|%.=ދ Ew C M5v˖!y!]W5ڝ鼔YD§D$&Wv.ן]0ۦ܁<䎧?o'4EPEPEPEPEPEPEPEPEPEPEPEPEPEPEP=k!?/Xb:lQEG]k]PeIⷑf]0RA#jUukV-A $+)e#83"gaW<{$JdԦd)Qs Zvt#TUƟ=2[^8I ȌW9 F>_?u\,!ۻbyj—ܺơ@7O8O쒴wjy.b[C,+qo*|:ԭ:Mg$3]\67bpA<)+_o#y4o\X=^ԚBʹY,H I :u[hEŞuyZ[s^$Cm?1#k=x2NtoT=IGi ao~DFXjBQ V2#J'xHcۯqߧ^MKj~)iDsO $WJvV]89laq{-/Bӭ͕Ƣ%E$-'s?)8Ex_\񅖹Ėq0)6 -+_5m]^5ƞbF27 )oXm'95}-O;"j///!QzroO ^V׋c-n4&ܹI<~7~4l]_ug*M(mBkC$: z ڕ41X38 08UûoԵsz-_8X=`ؖ%'?Rt.u=JT \ZiEHb1ؑX1xi]i[ /t b9*L0=kt'ėk7>sJ5#$Z?\qYXMbGFݻOWo,;K˝9bϝ{lٓ*s}i)b5]cUF[K^KamEO=NOmuM^Pkv7AHʜsw'g­~X> o':}$7WH#CUGAf/<+ZipmU()k~WUe2JUFvU@Sg|MzWwJ jS0q}._zкXh=B <7COxSIּd1nݰ\gt|2]CS;dps9xWC^kI! rNTQ;@]ƭp7կ&ӯ5n٬eċT.q}kJ_,_u"C ېs꿮mcSe] 8a63^H@](l1yK/xHުƮ'KK`f] $+c_²]u]FIo3Igq!W *+>u3:=kStp;Sb(SsOEk6 \\i_\i|`̄aqk6DVHETGzc4fbkAٯ?:#N[9$|9`X/o/. I#UT=i3~ 'E -qu1 |!lXqR+OOЗmӴ* ¢:Dap:{g- #յ8FXZydF㌚ķ{aa4mKT&zm$e}rA#q3K_(OYWS .Jqytmy[9'qz T0}+3]Gwkk7aM>{y2\ƹF,w.}(ڠd dH_Up{HmKm6DP"G;Te? fBQ+ C$#Bx)k{2߉|eCuZfvpMuv,Dfd pP{IxP,sy P4j6bI'9i'9?wZMgS5.-W8yU'i# v[ĺ4O eRͨj6t73O) ,[U9rHL@x} JLs5Ќ;цҨ? 1^#xUl;vpv77<к8ïkӴ/5/ɝc,HUY\zu]bH/dYA#5K[;X,5=_Oh_Nkic.!vD,C ֻ]6 7NGmmU_k|NuXZѬ\]Q}XB0Ky=]&N_RO-Y[Z0e FX=6_G6"\hwl6>S4/]y?<2}ϓ(A9_׎mx]vPY 5pCVNҽJ=PޱjV5`F#v6N1'sQVQEQEQEQEQEQEQEQEQEQEOkjtmQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEZzKoËMwwsr#.?:BZ EEZVR쵝oJ-ȧd59qat?יouֶ7l[V9dSqZIJx!SM>}=!O&w6嶘ąXRwmU9m_?oH"C RI rlJ>T6 ?iiwg$ҴQFH8Ka9Q7 o5')uimwZGs.G"K"`H Rӽ?<׷mk~/(F>~珟w'H}Mk`K{.wtM~f$r:T}QF&BiX.;V;3{;_͗],^Gwip$QCyUP-"8?W[?h[Wm&uY e1;玦Z,]Ʈbv{kԦP(m+qV,<Đ&HcӟJErVl9.SGK\֦5zO,^s6?ZҪz.o:eqkg[6P'W)U5rFzUX@eb|G պ  1S1m4<^vM[^07I gUݻZ V|oSt_ͣYh$G}qXO!tO'!-J!zyqM7ve佊쫸#zD.Wa;O O X" kV]mf6#GG%Ys̰cgAU=o`]f/x:qeHfX|2K ~n;0{|K/t3q|U̓|:s[vQvpA m^џK4TyL;C$1%MrG׮+2?^!]B-xX Q|.3ĺxrOJ+.mf*ٔnX.QT_,JNԵ{/4;}72*r[xDҮ%I,^H\3eKdĕ=W_ hzE_7RIrY|T121#O x}4;5n­pV5r2BIɭ ( t8$R촇~SfQ\.\qK@#? (cc^i֑\d.Hc|1* t潛]-m|n`쌄2y3:D+=J}&iDlŊLr;ctiW1ivwP."f H Sb"#BS{lrKm4p`ЪJ1BG \@;>ޕ诵XoHO^ fFa8O,9bğGG0H?-U.).ˉPCǨNռjvQ n TO#*мy:Z( ( ( (>%P/k?̩JQ7k7̬Qṣ̪~ee <<ʧQQ`ṣ̪~ee <<ʧQQ`ṣ̪~ee <<ʧQQ`ṣ̪~ee <<ʧQQ`s̤2UO2񻠑U'=քs~RI?#Rwb0mcE[.j5V&9MILqq'36-2+FtlA]sEUfTZ6aw.%8 A`P_AxrV+G$|JgEV>a_ϰ,r?X")bAhмe&m]RYM1prdrzVI_IuQ Ç$`;qt;;b\]Fi 1+eQr|48?ġϿk2:߇G^%FgV;ONzzi,}/si%rd$rOpI}Mݳʑ;H3I4zhk^ohWv WckidHTx=ς==$%Ɵ=$ģ\ⱥ;ͤ鶓is"mzKy;H:t?J}㛉K Th]:7 =/FamօAc*:|qV4oM-Jy.Biae{)6nR@9MK_3H+®r ϭ+i枋ALսq &|C t3fR-uk{mR[{m*|yL7eѴ_auZbkeNTa#Њ~tK(tGKL.JDl<Q|z~W%o%RI Hd_蠜oX~ >b-3w=XWA[ Q@³E3SX![i#y.1*A-6h}A-dME`{# p e1ns)5t5QdG##ޯEP-BUٵA4&%718}(Cƽ_4ʛuZ߆?D 5tQ`<NA_G(Cƽ,‰?9k~hr7׽EOQ:-uZ߆|QiڊX^_J[ aw3ڋuZ߆?D 5?ı[nLC"w;8L2լbxd]ѵ!!+qNsD 4‰?9k~koAm51$l&@\c#۸8Jmt:Q$VpѼR(7|ȹ sD 4‰?9k~k۵]VI](26ʼnO`fh"jdNJ>P.r;ҰI 'PoNA_^/ޡۍ*OIo3yTӍ@q^7n8q C 'PoNA_^c;-C6z]7VMuqHFJh9#WUE‰?9k~hr7׽EOQ:-uZ߆( 'PoNA_^E?D 4‰?9k~kx'(CƏQ:-{Xr7 'Po{uZ߆?D 5tQ`<NA_G(CƽcFw!UFI=x/(CƏQ:-zm43Cy?`x2,^b ˖Fx oao6k Doo* s˿D 4‰?9k~k4DӼ:CefnCnq?tq?^i:2uFrUH9s?D 4‰?9k~kشɷoyv\+(Ad$Rkmԅɏ:qu܈9!r>S=(1 'PoNA_^7tuPQp,[ʬ\\df&r<-Pm%y#ҋ r7 'PoL|sO\Z&0 /K)юos( ?~6g9`uZ߆?D 5:t{K=QnV f9v}) sohVU40Z%$Fvz30Ї,He|$#mKinemJK*r;yEK d >$hXQGeWlVHuQHaEPTTѯ*pj#)+> Ҿ}jt$-K>U7T6:c-5KWm'A. sQ*X̅I$aP $qG4E=B%{>V-< 6Ks5qjaF3KlWz~?;\iaiL :|'r?5{F!#'S1Fd)>PzMV =j8|=?\˧Ae+\ܴtrp'm^m$$I<RNm_ILz70M{Ayr0vP"m_5Z7 ~Igk]Rec,v $Y/bOh6Zw2YJ$k(D \q]~g4tMCL2Oy9Y~5kOZ} 1g~}Ga= cVo#?I|wcf@쫂1cѦBcBSzPaПG(V< zx+m7Wܾ*[-nX!&SÜc![~3_z6]ksvd 3%X ۊ%r?f"#Ef@i]&&uceVK{u[Hx`{py+T|VsZݾdҪT9f8\װBHՄ-v^DG/1 O5mGjV/zbԍR,B,Ѭ+`NN3/&M}U&ķwh8TW2>6}zATQaP@Q>T)= ou3|̵֛vr@rY'>d{CsyeĐud%X.N ](@>%"[{iFVUz*=꽵՝퍄ͨؔpe\g9ߕh͒yo_mOj"s V^vYOdzwQ.RF)YJd"81I @늟HmmtM:Vs-㒰<| 95+e/0 Ķ;qe(3FXCcq`<|xզ޼(G888z߄?ˬ[sg9#FN1 ת\YJ>n1SM:oّ_̔>S@X#կ$żP[I F| `x8=3䎁%ѵ XFWct]chE;0׏Y bEiܭ) =pvT}͝yo_mOj;cF=ރ[[n^L(Ub? 9m/K[DTFu10B0NI>XʊVR}2v~T[Y;nJ"t_?\>xU"Q^!A& aW9ìCȯRj2euJS!Ɖ LXT:_aA lvn?Zi!l,Ki`f^7dpH#rke(3FXCcqqe*c'Q7XSC,O-`xt!k(!k(j:?uȮ42; lt|"w*Sld䓏NkZWۼsi]IB$zGrGrXk j?a1BKGU! 0O źegE"@FXg@k!k(!k(jkGsޏ$TD6Xg睊WҫxAo'DzM:R٬yf'_0r0rqWd?Fd?F=wǞIO?İ1oK;Oh6}0c~>JiWr;֖ VO3k,. OI(3xGrGr%cʤ~-gWO6u#1cDI2տ#5jfɫ]l̦P>ήx'$A"/'Q7'Q7<{'C@-`[}JlY;bsGz4p4[%5ī$[ c֏NA _o9GNA _o9@XmGrGrN!k(!k(SuZ#?uZ#mGr[..๒)Z 8 +t$Ai(=QL K_Y)Zkre;.uk>uI&k944\.19J@utW)|@5ۼZCnK%Üa0; ZJ3*7^4Es$vimzʳA Gk"3)#d>fmMȺ6_0lq@hf+۬Rspy8gwm}I I\q@EQEQEQEQEQEQEQE2NQX+&]!=nȵF ىr{'hbln$]z0S Š((( G\ZZ-V-ڣmow$~k)Nkԟsj9}JI/&Hij>▩hgķ,[NO+KGۣg ClD;f=OV~.$_ [3B:N}˧2%YU +K+%PQ3NI  JimZCkH0 9v?3~f ERQEQEQEQEQEQEQEQEW:Կ贮]SEnZǴJ(hP%딿-r> ܲxZMjy!e wy| ppsZzKXd<4MX\@4 m""Dwm?Þ:TsCǴ j>I}팳N%u""\Kme֪x~-:\i b )9) zV/#}H5;c[v<#r9V:YoĨ.`-zaBW*ax櫯|,˭9O Z$q vp+:K6i("zj}-t B(0((((((/xKTK+vRH28@9n&8"ig#FYh`Xúbosk ޼WuWL>[[ӥŬd2p=>f.JnN}N7Y^6 2 Q\[.졹[]#MHbEUٝpK0g55Mu!DkCDY&W#?uajb$O r7S¤Ks&EnmFF̡J?v$uTQE!Q@Q@&R `[}:4ڼyF7(N{q^P$|4-m>ta n$u;QK`2q3ȩá ڌ-[@H _;x-nGmoj-Ou$ \ 7y8;WP\ij2~"t{eWpQTw[DžhaC;=sj]Xܼ] >hS`Ns_Y~"xFH;ȧ ,e,a]i4M"@U=7[ӵ+,Img6#W gr29:i_7*$Ô6 `v}( \SFJq@EQEQEQEQEQEQEQEW [o"Y7BM.1HSw?wUbp2L񌓵|Wܩ?y \e]C:'}?^ECrkfΉ_OPĎB ӋǤRĬ{.#}wwX.O kjwPޣo)Y]"/WrOdD|^41}%黨?7[m#Mk]>=ytMsľ.#!hMyXE=k꭮ei4ZA42R=)ЃЊމi -,d[e`Nt$ן!RjS:HY~v]ۚuϏ,ii.GŦA /$o뵿!M8infH,dT3կ4|cx]t;/M[|&\}86"l G4_K#[km$a$E۸q{c4i5H5̍R=ʼV?G}>m*/h%̺j (s@^^ItVծUmo I"dpsަLJokKW;yXG,y܌v34-!w<5C@έq& ^'_*Y;wK ^WBX"mbnQ^xJּ7Kt6gu%[i"M??#c  (,qmcIe1E8`8|mJlXnsQ֭m:]Jag orY*~`02:9yF=2ݢe'dPQdz.Tu]bWI].Z(Eme=m7vƿ6kqJd `YJ2)܄<{t8Iho[; iCe:r/[U@YH!=Ethz{zF/⳺i3W3wqɯD<1_ Zxv=wOK.!R3dt8ȫ5Хl5қȏٝ"7l'8ɪnb];io@)ocX\$zD(PJ:}]cN}[#_xEž6ܠBIs{?žgyfXmo[i~2!;XCZV>+Ӧ'u7EcUkqݎ0r0s.>)mi~;.(<>K} B$F$ 䢰ۜlipOIzzy.m9=*#+zu81u)˷S?jFY#QK=k{6;{i!,e9GLZj+27oe4IF1bFw qWR_Y~.5ؙl e`(FLw[_A]@[sJnynAp82OtZ]sIWJTTwf&u琙GjIY9{NJ7AFi5g4WdK}#P:袚EPEPEPEPEPEPEP\WRҺ*_BuOi(=Q@Rxg~)u@t۽ZyJG#/b{_Y)Zg5eBj#B@ݒr'q-m2/kzAw{kq}Z\J#`w߈!c}k,,Q0p+ (=y_L5ɭ.eoQr^7;d9cIӯeҥԱE Z7pI?ߒ6-DžodDn,-Vb(um?tjD661leܢWVl8M)vbӼ=_k`!RnV.׾ ZiuXxsUPV˂ 4u]-w9?ᖩixϋwA}5:u(P{sW< _r-12,v8sn; A_GCk[q*y{7 +q+)Erm-[QE((((+?_ͣ]ǣ BnA1hx+Bkڤ:.u$ oe79ý .Q鶩4-x(B9q^9].>.d# h΅vmzk{qʂE0v<u-''HѦҭ[b&ܬ0&\<RP^Fhx[LЧ&V7"**c c99Zow(IJ JfxICH6;B:|/ǯoKR[Gr]?ZԪVqC,Ek.4ӥZRQ2Eӗ ͌lV}zu&ׁt!|H ō4>\Q#Fc\',y5RZ QEQEb?{m3O+OZeͽޑOp"HIebv/Oiz?u;ldԦYn .C~.?k~j:r<iiPX.5g YOxk\OEyIȾ < ́ws~$hm3[r\Pyg>b?=ύ&2 sd )$m)RFGLrtCvSKRK}+Ē d#$\=*tGFD:zZ53pY-Iދ6gkQqI>.CF%PGZHET,ODg8Έ4]SϿy.Pb`;Br82s]^:ZgL:}Q\ wmY r@s z*ׅ4B{V;VvduRG4(95==6I[wm_(^G{j/ ӂ[ y8wqw@t9Ƭ "I{l`J 3CA<|dƾfc$qDt,̀x<.|+6_[ZxbmfB2T `xb;Gq͒Zy+Ggd2Lc/"KG޿((((((((+ _?ZWE\WRҀ7-??ZǴJ(C_?kV74u+|ZRVLU6-=g\iCmR]›:uj}݆n L$hTFr|J㠮+Bƹԭ -NLw7A*K߻/l/\ {%o]Z? 9֥jVX b5q;rd'cd,?k_t^%lfOWcnV_`aE0<ޜt?_XYK-.pdAqT+8ri=+^6EX\5;J>pn#M]XnɴS×gQT4*e?&dy?v>\Ԓx[Q&h:V1j2C-^~m봲!먦p:_/ZG֭Qͦ$(v.,Ÿ]H;r((((((umj JcXS Pm2}:۟GXp/pL6\&{2Hf3zW\Zu xLUд i^TUUS$ Ļ=ѧ6HX%wb&wfn?w:Y1Mp]iڵEi X Np\8s6.$2\jIo sۚi?r4 ]I=9>ؑ ]Y1گWΔ [ӈR.s;g/(3g;|_z^kr^~EA}^B+a㫝RGu}J]B;]FXbBF|;IKdoFƻ_Ѵƍl᷑bP RA 2;ZQ~?((((((A7-4s;GvDƻkk\&hI1 c3o!̵gCy7?2׮M!I桉EQ@Š(*_.-qdy_\zphMY(QEQEhu>j}WIglm"Jʹr=޺zIRAieżd!G\(KSmoٚ52Payw.Wnn(Q@Q@Q@Q@s!]KJ諝 _?ZP{GOEbZzK [_h1Sŧ$n05؃T7[E5u+$>9SV/[@nX_ڗ?-ڗ?-07h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKn7h/mKnmKnTzn5el.m|y+O`1^+`̡Xqg].=bLVӅT Tvk`qK<[CQ4}SC-lo2(3"H!]U9lW-|,Ru;Ӽ˘^V#Y|dw gROPG3"ECc\%#bx4=;y 걛2`osXd8m?[m;ʸ8*$:IPxg<~YKiR%j۷;1ϵ?Jڧn[k &Ki]%[AL,J7lzZOD;6rF>gO}hKp>j)H0K ӚQyK ~]m}ͺtgjwiu M+EQ4jXu2I\3ӂ+Vڿu]:3dOe WXVO5xӅy@8Vf 7*NT+W0Evz׋uOh-oiݩL窠JeXx69T-u,gZ|wEN&8dMq>~Sz 5?#RI'V۩^^0um..ȶ}vvqJzkw<hqmgdHq{-78Z,vѴ,_4O8'yI(X,raBDBi)=\)˻[]v7`I{KMݖe`ai`2r}}[Ş*a}N6Hasǯ[^m|m/2]]Fʑ(Fr@䖨|/Eh#imnb{s介r届mmw~ϛٯko//>k*m{,H[NOּ s/dyB`z%mE b( ((u+Ti]s!]KJܴhQi( GMqiYԔR?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ,GJ(?,GΤ#Ώ(I?:T2N;ty?S=͒ceE;Y>2 &k 2mym,- @gѢ\a{_ᙵ˝:Nfu;'qI(VV{XxKo XЫM|ݴk{~x[Ѭct4/ufv%pgI=ǽnkzuYigKwS]]U #g$x_ǿ^}i&6 x#oyP0O֓O (gg__ snx\sXC7F9n5 t+QK "fA\l=M?U^ kkp1E61={/_6atFӞ큖)1q㪊7M{X{F5ETPH9;B1^u8%;[X,22Fj-[mTחN-D!y$*XsR~yvS6uc{J!pS6Gkl^-[IfK2 *P1YiM ذqǦ#x^O1EzqU>.F]CWw=Ă1̂CrgvzyZj):knoj G-C,̌6l7~l.wWx޳qXkZ69ه#wZqZ\ZvMn)R{s@E+C;M֦mn- :8]iVF)oxDw}A{sm{*3URT`&W^|S8I^:yϳjKnO#5n]jCwLS|R2ozassڶot)Ut.<H % r lxVVOB }.yf?iwGcn2 Nם|JKoŞ!Oќ}iPC;Fc{=}hϕ&zP3RQ@˜a%1P+)EL9R>(3O0I|04# EqE*V(@ *i3i4NGZgT;һ*eͩV6ݓsN.s}s;\L3=+qJ*Ȋ%Vnr݅QL((u+Ti]s!]KJܴhQi( j(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ x}iFk[/ wio5q_,8,Hs׿jE\ ]RMR+x5i,r[ځ9eu8s{7M-3:w7ZbH.ᵜFQ 8 S_2_:5vS '*T$bXNh'i/5x 9tZψmZѴ^JݧAp\Ӝk{]Z\ sAAu[jjvHfhٜ1vb-|-mKR{"iF#vр+?-nn_L:%>p?C YLI1G^)_2>!hBm*Pͱl#4/.h36o/K- ž2+OiZ~.v#cH`@W9kz>"_[&u}+P8$!vy 7\/om+OMLm"OJ鼓f~ϵO2m.ux ֵBk=A5(廝mL  r?imdKۛh%'Uϖ2 o|=umNkMs_1-F4yQ|N,>"rS1i&k;Pɝ_`9߅x:wwM=]Y@\JE}mM{#P)&~ֿh"2J v?(\$MqLlxcӊk_ -0x+k#Umyk3|=5+xOJ:\D|/vp ApV(gx\[]Im0AXb ~$'Qh"zj6Ķj)lNA&mqwo{|>j6%G.w $4T~'m晩giw[Fo# %X;NkѼG^i1^I =b>7 YxN}>6_H"+{tݦtS"eG ob ovdž.<5ajw*4*y|p,ܒON&c&n֩/Xك2Hg+ox0`Qf2Z@q bg +㿇tzuڜ&]47 S!v&к3_S'! Bm -3jB}VR~ۧvWď}eF猎+g; ]nahL2W2r_n3!',=o=?R'uV]F_LI/w4I+FAFtG `rAWO^#'gkwvp}qlC+ w潺{QHaEPEPEPEP\oDU0lÏwmvUbxسM/_񯊼G}is44jikk4Z9ڿښZ?45W}hOc_M?ښZ>y֎`?ֿƏM?|UZ/_񯊼G}h jjikh/^i>s44jikk4Z9ڿښZ?45W}hOc_M?ښZ>y֎`?ֿƏM?|UZ+[htxṁ-T(ARG8Qұr>]\%ͣ+At=To: 짖S_&DTJ:\eOB=w8ϥ.`F)0YN;=U6"4Җ1fCAv0+4Z9LJ-dcҴy{lȊ͌dT.awe)R"mdgǚ}hO>`;7Kq~Py9 '4>ׄz1¿&8dHOi>nk?C vXpzLWC_񯊼G}i44jikk4Z\c_M?ښZ>y֎`?ֿƏM?|UZ/IdNd",+5ך}hOcX<JknK3/-p,|y_R+4Z9ڿښZ?45W}jKh%e S hjikh/r4bᓮ#ifʷ擮װ,}_SO__\աkx QU;ª}.`Jjjikj[{kE2#_Ojy 2[lp #߅EA3KrX{QJ5.tOIz[EQEQEQEQEQEQEQEQEW:Կ贮]SEnZǴJ(hPN(*z7e<<ʧލṣ̪{@<<ʧލṣ̪{@<<ʧލṣ̪{@<<ʧލṣ̪{@<<ʧލṣ̪{@<<ʧލṣ̪{@<<ʧލezlmC 0` q]VE'c#m(7"\4k ؓ ,~Xk^b[D,$ecmaخ$W&B#' k: HuGNЦɧ%ŎˇGq".A*+ZW67mЅ)G4UQBs.27cc"#Qm{mq5:T;Ng'pF9Jv>"F:Zt30&d`{|WS[Ypkl]cy]Nt-Ë?8\yCl=wvך5Ҵ!Zs$l62@'=>m7$"5l#vrN Ϡ7zfXx6mB|F$M8cײ641[If@ppi6u-VNkM巖vI6lrxyW+0Z?[<צx[jw^Ikv%2+8=/(g2K܌l``<-Dk<= 76O%BJ Jkm;MLJ/Uhxw_5CO{ϴY,Eskźk ZyBFvIҽG q%$ѐlX.vdz#KE=h'#KGF@;_dzё\OF@;_/v#ָ/v_:.mFGq?_:?4(Bv#3kreWO^FφϩS)s4c|qm gO`kMJ߈O]}2u3*yc||v;[luy} ,[5*ޤQc*I>Y9{^'G{1[u`UfV߉Ucnx;3][T$iK EaN')i/ݓ+Jt}QHȜPv*7p:\姅;- "kYfڒe2;v|Uח6s.k5=OlQr9{ZV?Yo˛K)BI/n*:. ^]-Ã}s>skzu֤y؈L6JW3+E|+i^ VZ^j]}]ȎG2q۶r5/~'x~_njŢ)"תΣHo[oJws"?V5OkY@f`RtA2;Y5&V1ƫjw:Ű(С JSנ+4}ONefE Fk)}/QÂYRFc>&]ZB;-uC<'t׺<6ۓ zoH(>Y$Nj=W~'bԯ/+K 2;HbaǯQDW-^N[=景x5HuInm`w 2H>doҺ(++QEQEQEQEQEQEQE>-F&ֿ4?MݨY1gV5E̚Uۈ̢ 7F5|4錦l%Ln09<xc} \[A I#]̓pxoxo.{ٵƋqݢtq@pQ|=xžM*^[4߹A$o88N՟?- V]M4Бޗ}щD \(xrOz̚mv~9 A#j{lNG~Q&i:no4ҧo/keN{ OzCAfڅg ,1cc^V$ӥ.4ai- Ւh"7_bsI oayR8t~m캝֡ QZcr@rG9 :+lWR4jOH`qfkfun^E4(łrp3p:+P^uٖ9;ZH%Ny GlZ5ߊW6Iq\v]5ɂ$qm;#$zPQ\t'3iMn a2 mAOCӿc@WsMinشۭBH |gk~)uSxEOCS[ 36#D",egFx{^Ƿh+W{JX]+Mut9Y00sֺ`QEQEQESU@2lmVa*Sբ,d [&<ǿeКc OA={/b~QE!Q@Q@6YQH:Y~΀uFW1}GX$p\$6q~^1眞 .,*V _9Hx˩XH_<64?\xMkq= [7 %D,7`H͇"dU:x7M,)q8?5$G|n嶀>pA$MF)B h9_֣uWFm{- Ku1B|&R݉wm7_fD8&[3E.8ι# g P7]Z_֬ OiWIkvIe ##e-Ӫʙ~`) F+۸IHqu^nH$Mp !uf$]G{g۲747($uymuC:OEҊ`bZzK xY]pi{KjQrYFzC_+!]FmU4t{bH"GuG%FvMi*񆥪jv")J-%wGP,:YWDGӿ5;kgi T|zn_Vwmo )j;Kd!G6!qID֮t=NFI̷4UbY H c#Q&-IVOv$Do1ڸUHPJ[;\G=BcXaS >@ tVb]68k2 a+@)KH##T-񖵦/Ul9uo{fp18ey -NJ>mG:Llza(v* ªB뷶qfuWuI}YI<i 1מ7iV^LV<8HP/̣g,8j~Oҵ Q,nplm4ZZFh%Y,0KzvTFg *{ ڂ<P4Z:jQڢ٤k no^t@M徍q]^Kx3챙e.07 p2}+ƕZ6K]G_kx-bQ-ݘIv+g+s[-4eKsؔD$۶K}0$w N]jh\Au+  U dd]_yK]rkI:[%FOcNl5KǺym1u{Ѳùkom:èMr * 2pqEH~^GRQBY>yj4ҵ]Z[&v {b}x0epIrGg> }_LujZVְ)xdɿvb6hZo[i:TvF&D, `3dSJ]B_ x [Xk%7QyO@( wztOdi~Ž:*3|Ca3gZ|TnOΫ5|WFXv4霮`V#g[^1}MK5iP]-e*jlRgr|7ytFDM>Qc0e$*ϔH>w/ťGy 7P1mlsT|Oqi+}B. !kW3}rWC2cNJ;GR *j KGd򘋿&G,PH >᎛Xmt?W6- -zd֝ԯ4 א\Ki%mJʮg߉_YK.`{|Y`rzeN1y]P~a<"5}4٧a]*pZ쨢 ( ( ( =?<㍦-_ؤǦӜBWxoA" ާ{m=¢!nlcM:8 HҢ) ((*qa1\ժqm,%R\xJ4x/%y:lF;-zikjm( [v:WNlO?+ciKY$H|0hH ʨ8'9c.i=}tD[$dr Ȯ+\ek^~٦cKB!Q+V_G]?K9 i"YfKx'Ҭj77MWQ[̠2eRe?r:ݕڕZuJ̒yk3>ͼ6 *BRVW(\Z\yb2HFf[9G/xT7ʵ Kj1~ L@NO9o_5{tOPZyYY"tEN(i{wjV.bX&Җ,N\J~v.wm;Im8kKVƱxK]ѵri5Pdb9?xu'UtF= ku˧4dtګPI\wSJyq^ӵ&$[shoMPk1k:bsfUPr+Ҽ iSYE{2[>c7aIktY [5 EeěI# =[ivdewbڃۤ(3 2d tہ8$z6V$ 'ď çh`5!yclӞ]fkX^0'@ʆqҴQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@zbu=U!=ommp~c5G: iYPČ=[np=Hu,5 ?=o hys $ԯ4.qK\l ?0 d8xR+vS%w1"V6gQ<;z槧iuox{FW*H`ݹUm'P}R/K{KE8ۆGn@O^$FRH(kwGf_ݙG$vWox5D+dy fC DZ +?zmQΞ%m:/4t.Օqキ=PUns?..lIps/¼~?RWnEI"y7gқ]\|ƏniD ytcmFR6 -;5ĺbUզt, ½y.@##޼<{Cb=VY C _u@/v"򆴳 _KAfc[i+2o*FF㞧(pѴ!Ԯ,lcPk 񞆸M3:֟zGgj ;Je/y|Ðg5QF\ B{"=_ʥ}濕gyR@}濕gyR@}濕gyR@}濕gyR@}濕gyR@}濕gyR@}濕gyR@}濕gyR@}濕gyR@ DT@j)P=k!?/RxgBZu;/QU]@ 9z3/#ث:\_|_SNmtۘm.mcYĐ:)%@AKx]_Ru/m"ӭ5V;%sN׿W~_j6Qeg'mrDafA##[]S"{ 0f=ŀt(POb\eX&^EvH݂\abˏNIiZ \bMRni2Kʰ (_]m6ki.%xcTi9yY*k߈*=."'71:ǜHJ{ִ5_jznѵ1Z[^ZF9Pg-F=TKL]ޙ-Gb(O, _ @5j:tG9X<~@2T#4-UFR#.g[Z0&iVF\?NjΗ%sAt7Vɸh:)ܸ^FA 0@;z( ( ( ( ( ( ( (2|?ͥHϨ}K>B1(=*hRxBLSVnk`_V4+Jښ`ݼ6N%D#e M7RiGq"GfA`@E'N]Bך4$ E TF;g[NVڅ6;td,$9{im5-jhl Œ[` meHi|u5KMCCODE#*y.:sCwM%V=gZ <9 HV  9bʸM^-[|B% ;q`|Xnj2$VvVFs#!K?u}Yu}*pX˪Zm-ʫ@Q*F\g1M{;%cdq2wQew68Nbz-[&:d Guuv5W}[x qYo>X\Qq FYᛁB|]J?ׯEr}/LƍZ6mqcrgWF 6טxö:˨jĺKF׶.Bhs֕EyۭB;_Jקe"\2RєU2 0k{Vmϓ7zi.m(KJyĚ5K; &y;DEYmPAfe$pOQ݊W|4F]xidfI@@*Fri?5_Iesu6r\ŧHcSi} Sz^vEyq}kXM[Z֕m] W^Cۤ)C) *km&qkEous(Q:$A q{q˭+\_ɧk\i08yX37+?ܜdglL4񽖵}kȖ"#m$Hȭ.Uq2xŶ~3x{q [晌{UbF8% c&?xfy%Ʈ5b~d_k呁$>uۏDVVO[\Y4ys7Au* 4zUZiF)!]$2FYN1 ZΡ;CYnD7GI;s1OlVGK-?UtKJ+?][i 2PǴps5/ܟ @zBI$\Yv zc j_cgk%.gg*w069ɮ{>-fwMƺpKwptUF\ TSڴu/];O 65[|eL;F=Og |uoI}>!F 1gS3GeoKCjv6^jWnfitfi_{aC'^AD[*$`_\y5L(NO__u V-[_$6ЫE˪c!xN03kmxQemtm޻VY! ^@((((((((':!Ԍ7wn{CLz^f&}u{mm++Hm$H?);Ӽ3s\WzG},v_gl19xCzޛeOmrm*rAqSfK }$%[MpkYS*lHZ]}qtd$줜Uk i>4_]K+c ۉa2C9|G\nMx_Ru*fi)8M_j~'_Zຶ(ܡJhIsmR5mdxPioH۝@$UzW?5_6DխXkx<¨e*{’ ^%\@-&PaP?W7s\Oѭƹ}Z^\#?@YI{A޵ 52bl VLs^5ۍbʾ_̒\,\; u0|2NQ@zφ|xPl7:ۋH4 ^NI55׃tk `%NywޒyfzWWzV7od7c8wsFT\}C/lcpVwd4˧.gdiUeWsb:Es[:bS4Ӝ8Y8NIm~! >(V;{gi$yfhcsW#>UvN1b l7r"#qoqu%Ƨ .S7gY$Vw~RsArڷuSK5yK[2'a'/*nb֒z_]QL̐6($zqOĝŠ(aEPEPEPEPTQ8[o=NW*eڬ?hQEQEQEQEQEQEQEQEQEQEsֿRH,4Ok-3XXn'h Pre69]\7%!;tF{{o-#ޙۙ|v#Kc4-:Z5eR[޴3d&0- g&xBk _.̈́[rpZ8иn5zjZpY/}TƮF0[$ j/uH-~xjh q)c;9U.i'~5Yl5C]fi02Fpx\Wzw:,b|lC!% 8RkMs0y"սFdٰFrA{ծ+Nt{InMI%2LAEFCt$t<04ayAG.ˡ^=kɇW:$ /@ h4dCych)ɮHҴoxŗ1tdY+XL|nc#)u%. }1 <ަ+Q)}faTUSc14f9u9Mve5BbY`c)x]hoOWM;5c,i,:#-Kww6Q1ٹP~lFf1O$i٦JtX{nؠ@^Z$];2Mkḑؓ =z{UO^g]Q~.&5#Fy9ݖ{NJYtYfx+<?>RAIΞ ɭr]BU 찦÷C{9Kv x動p\KI^Sucmk(ҵ?=ž=Ȗ/%go ́ʌN0@AYj5翴K+#:WU (PލoKhšL-C3Il}ḟ0ǵr~#=j[R_~g]..BMq$2]34 ^}M2=%~y(v˳͑zא77~X4X^MҮ+H_hY>pz͊!յ߄ɫZ}}R8&&e1Bp%w _.Ia4q0K녒y$ɜx@M[` g Wotit?XciK--c{ 4Mg? KJ#*ڬmQ$B_v=p:QO4MY}B1+ާ*t_UMI'bQGEuƮBV^if!Vm>D[<$[ "*F7S[5Ӵ)`mY;C^*C4|8$qJIO>f3 Õ>wW (Q@Q@Q@Q@Sբ,d [&U-]oǜq |7O+Zt>?hQEQEQEQEQEQEQEQEQEQEsֿR촶w.--/kiaIW͕K9K_Y)Z j0eZ[MWXD)ʒzk]NN< ifmȒ<6Y S2rfxrš4K;ߙ+o @}+ڞ%7XY_hZeŝ~z`t+~ڋ5Q7p O9Ӟ38ɫōnUL63Mž[WEӣۨ}{kYHK?zJ8A?DWZ)4%nV񌧯tOm׷pGu{\ʨM|duON𾃦si\2En$^~Vr9<{Զ7Ěwuzɞ;ƒI9dқwwcc/[ָvscz'9vYC:B `g>sAubZӭo㉷D+z5Es/SMiYFR c8j/hXYŦZ%@I 'j(žu}7m՛riF>}_R̐) g(rjKe?&K{I~Ux+dneCt ] xgCeMHEmJ:Jyil}ݍH`NNW]9OKM>ٓG=X.0 ћH:Mc6GOޝf<;ko k]BBPd aIxoEo-}TɴpHy((((((j^zsTvugpA^,ך3 YqYKҒ@{_v6rPzpi($gK ԟ0O!2s-ƯڧIg4Ze,Fܡ`A+k=Xϲm ^yI H=>AMO<\99Y?1E ꗙ:Ԟ]4 [d*H3x# F}M#R\AJT6v;[5  Mg[ kxsI,nC({LJtKMybZ|:rxZͱ_ھm.}gYz;=j~qaʞA# lSz1-QV:;$lg~Ѭk=#O,rElɞ qӢcLMs*}2X̨f%;}rxJEEkm"+Xb!#@~(((((((iekg}< Q6:y-M>4;\c ++uSէI䟳H62~MjW,nYV+ zn ގgK KAKop!fe1p{0 Vq^SE&etWG]%'mu=1*IVZ?]۫RKXк+Hsi1_[]OV>,𞓥ܝ_Jg07O5[H0# SUdծH ΄,}71d ba>\|xk]콖|( ~ _A}/:*(Q@Q@Q@Q@-VL6QʰC_2F苞}*qoBVKSMecQn{~&;SgvWo=DfDg8^%,[omַ[Kk7iK[SE,#0Y9FŞ"_ht I.i&x]YW*cn0wdAO gѻ]^wcШzĖU<آgM?! w\Sm|=35 ݮr'?֩'feO:u#+Σ_?tYTȑrVHh .x֍dk7eUCgg9$;|Wz%Zx^u i6v߹s"+!_ ʝwP/ivW+v=E $~TI[{mi 2k.}toZ=cHEwm>dEr AȨ5O:υuIO&o6HmӍsNzҕ^_~=( =E-r MUl5[3\oYӌb/&rVJ(fQEOVc&;SM\;rܬL8կWCᾃA\+A]?GSEP0((((((((((rexAVMQ#P@*\Gu995bC_OzOxSQ֬-ƻ4fG ҄wɹ8*@8rR@{^gly[Ej.5KNX6@$sޙoC->{-mD72m>Fa:3ZeBvXjIj oKhݑd/% ub9>| YZjVԗ IK,cݞA#u}rI7Mq$EKrv%cW=<ĝZw`]#)nz yy_M!ӡ!m6Iepp x]|Τֳêr }@C:Rf o^GRpzeϭiHu`3kW[Ӟ?aJοkyw"cHX4bK5FUn7= 9_c12OyiydQi'葼L+r$1pZߥ#A;'fG߾p3`RKd z+r)%lo\qQu[*0FF82q1km67hi1n _ڵooiX)n~hZkRމ]l%_KKi6[\Xɿɻ GjOZTXr+m-V῁/]VJꖍΤ6U2(}7$TxQuONtX5im&B|V!GP){ AG<2[=ѭ淸hch7N(C ( ( ( ( ( d":<PmfAGNߞ#9kU,%-axá# ךM\:.o}%֞&*s@WX4$#[+!u>jj$y`sN(45 ᔟUh,~-mۀHCm~\z\IsUT斸,? <8}vIZjŠ(0((((ʧ[ՠ'*k&."1]Z3Y B2 >w}ioraq$^tjtefXj+8.PrOJTRSTj-,Ӷw[i$ur(e؀zֺ  P<]\z^lY0mW4?aKſX_1IRz*>-B1iDyf{o*Ca}wqHv t ?_JkV EAtKPAۑ@#VRtm6Q≢?hd:U? z׋,{- OddFr u5,]N.uZo}}:׻& XͨG1=XD(4']C+ze[0ā~p+w M^7ye݃4 ϯְ|W^^`7el99'X:RQRU*+7w_~a CÚ>w18$!3@O>ak^'5,-c w:ninX[[;0ѪhfPV%>0~5~Wsn'^"X~!V69e嘢(f&Yuu Oxd, ۹TbCe⣹tE]뀬K?7qӧrz k6ͬI(ojvzt֫0KCaUc2: r85LWCV(_ ]_=GEa^wzGMyZJviwZrmR| uBWZx f,,uwO[H X6\.$PȻ:O6|85%-%)oȊ3~mVR*[i6&|6o0aH#6#[U4 i5/MhZ$  '*[V\Zd&CZyQ<ˌg M'Wεn-.bh^7y,0 |˟8Wu_ rq37:kj0Xh3)t_2)g_+.3lI'V6/Cɷi+H\)xFItXz<_&I%!|˗B\r\UtH̚Cc?/:.'jumRSl) iP^Y"HEʞrk׃4{zq_vdR,ݼy<Џx^@ (Š(((((((( ]fɐ?*ltay}>uiϨ̘Ŵ ,@uk&FҷFS6pJ׮.t 5 KhH珼ɤm*7^a㼏X܂3qUlt@S&+ɡ~_ˑJɐ>I"\ZlLomHCT=\Au+1ss'fI`lnzP@ m_B-4O⸂X8.VA9==kԴ駸խ.InήccK) Q,AY~i7zvckusAn󟙀kJC+YZ։kam V(P".I'q$M-̥֗$de<jz(,YBYHP2pϵ_((((((((((((*OWj?Ns@+A]?z?koP#(QEQEQEQEQEQEQEQEQEQEZzK?xS݌vz$6b/YaIQ]딿-r5_.g{5C`87B_`{intZGMZ{+{{9 ![lUCpI3~,u飞ThGp+Dfp$*0Ga>t?xIꚸ[v0q ¨ĨݒFZ.i|ۭ䕣cqР#`=RC(^Z|r&OTYghG[j~ WKῈ045QE%-)I A\cKmuuyʗv^N@ ~^|/hmTNԮҬt JǔRO;ӿo :;=Bƙ#Ei)Dq*pR qKǚNodVTM6wxK {֛iluK{{7ȒL,n*T1Ԯo1yVv2|$+89J_6CVke~ՔPz$bMuzW.~ j7tmJg<z{/'~L}[,}I]euVӯ$_/IrPYƝbV."K9cm{ˑ+ؚ?D!5S3ȳr呹|߽tGGY4 K S&۵[n;\& Uctj;|kܮY.32`r1X(握\cu˨IoIki7±SeQ=3]~m2LbC1 I8>t wV]eawiq -E Dq*N0ik/fE>}GЄx=>]WOhxkAj֓;|f2c$ɓ c?tsRbju\lry,)#E8p~稨GkZ߇6-1 >xf"o8Gԧ Z ߧcPad-*ZE+P'Qqt+oҼ7y{\j-(ׄYUT2rGs48Cq`\)I.C6 {?w%aYSc2:F c'7N+۴{KUg-7I#ܣx=>\i|'-ͽʹz2ő"NF w=[kk OGdJ>ԒF㍃1f?i_/&[3: g>qp*D1j~Vj+i:ޣ4mU,2 78_e'ZrOmo {*,k$az8ۖ̋ އyhFj2oXq#y1Vۂ }-?OC]'Ox.n XQXw+y.簺HJC::Wb{UksIӵ[il<֎GF/q=jV.xBK>,A Mbщ13 S+}rV}/SyϞdHH̊b63޲tk6#lGyɶgɆa(P>Or>1Ҽ]1 ȯ_ˡqH*wc fS|I8Hg/fFi9ii_袊C ( ( ( ( ( zͼZuhօ2GY7+yk=pjk_[i[,g"K,A)qJ>F-)})kw pQ)8 c|/F`Hՠ[DÖRCdU$Yz?5mRY4mV5+{9#8X|lu \>~O5K*[[Z!pmc9uQEQEQEQEQEQEQEQEQEQEQEQEQEOUGc.W;<ʧ-`U8կWCᾃA\+A]?GSEP0((((((((((re"Ӿ$ccc%F,'‡@@Pאkm=g\kG?H}@[j?p P<^3agRX4l'3o3JX.SrelBQґX}" %x<)jYI]%ƣmk$֡]vM5;H bv 8<m#OLRmN0C\>O~Jëo]C?i,p6`(g<C}׊SzPS,jto["2Fnlvױ-%^i ΒMw l%T7^ !յ?ŲZ\_U /rG-Ճ\V)vn/ҖtOkuk&qz0۲5,BhHx_Nֿ5}B;-F¶JspyIeghJPඞp`o|{IiAk7K`4%QZr6r KE7o_/#Pյ NQRHc ȭܰg7BB﫚C5MFGLcxĞ- (Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@SՄfO0miSUE{w60?hQEQEQEQEQEQEQEQEQEQEsֿR[k:|Hɵ7 #H+cvm=g\k6\6.RmUˠfN͠:T3{4N]t5ƣȡHX**O^z|Jֵ{䮋{n-er 8>m~?t5h@]h;9R ti[g/٭ H BX`X#<վ%=c&O n5[Aii<$Ψq"+;Ԑu䪞gIq##u MN-VQ{y%X`P*n$&M@tѭċJYg*W Lѡڎwa pE <3oO*5eSm斚Ejr,O$'bv|x>Wutң_٦h<2ldlŴ~\) 9in4]G*վq,XarēQQk~gCms4egGpjRi&V]; ɼnݠʹ_NwoNMi< ga?3~闚Ԛ׺k/nlT\O$qv~T$+%FEJ/#~F6f]LM*}rEI̹ɵʑ-:-.qͭ -vZ몺_QEQEQEQEQEQEQEQEQEduIH .lrxSRHPNVVt<0j=FA/~v_rwcqFcrzV5t-cgJ nVIꆷ2&[܍s9n8S}+,[hd|p |ˆ#kdcba-k5ONV(X) cSzwBJ > jNuʛMmuPdGrX c5CZo /5d;mŭEuW,UٮQ4SW"+BHgL+ ӚxKé:ii.Y^cs@,df,r %tFh$hm ,|Br=+}Wź>[>y$R$!&9oɄ_9n|]XZ\ tea.8{x^F2; l?ӥ㙷( &;tnJѷ⥽|]4*dxam#OSNI4˯v[Ŕy;K$Cǻc<ARm~mzզ:q.5aXL/7o߃zsEhuSmĀ l`9dύ.ami[_I4v2 ~@zuC[5?¯5 RPE(nx\8_MյMF/Sz\wa#X]D%,7Ͱa/ Kk+!u6ͳp[( 2)SSi&hⵖY^PD櫇;󚧻%-~_j^/kvewz5Vvmiop\5Cw`AERQEQEQEQEQEQEQEQEQEQEQEQEQEQEU-\?؟COZTvu} |7O+Zt>?hQEQEQEQEQEQEQEQEQEQEsֿR~񖟨ii[MF7̗ct؁eA,~﩯dre6EӤ}@e`;4[y ߇拤[Y\d2D$1r@:4_Mw.AbeLHrO?(+z~z4QE0 ( ( ( ( ( ( (1;yuxڟۑ '-.zH;qSxk;{ɬ[ͅ#vHuh6 Z{gg9j/iF`6K")tF~vwgjzuҮukm HDh%v ]AQ1# _AI}`]3gGlmP|#Ì+|#[fcZ]-W~RW?^j烼={ɦؽ]42&1%I</$}=lu|VNkjB2͔pT.~gbxηkm.UEIdErg30qUs=BmS qX$籶Դ*Y-a[{tWzF *R]?5]{_&uc][Z}{v,rJQP&Bdwd׆&dڨ` 㼶᠎FLj:uDQE ( ( ( ( ( ( ( ( ( ( ( ( ( HcU=VA\V_y?koW= |7O(E ((((((((((=g\km?>b޽,WdfG?6\mPreu_NË=`hC/x )-yuOTs\kݍR>߱2 c`y派b'kkkϪZHpAP7u5Vi{=孅7Y$MOTo4{ };HQ'+fE  b3xb<-Zힵ~m5_zΈ^"Is1R,BA}(R %]| ^YxVMӴg_M ]Ymv (ћ,k-6 >iEMhC@$@~ntyŞukv'k&Wib"UCg qW<\u7ᴎKĺՍ<7ߔ3]7tc_Bc3}1B<v>`֦ZCQmF'&\aGFוEI<3˨\ݽ.-|S6=ժqcg=A3MeRȘb NnwKM(0(((((((((ӶQ^* X>ȼb2Ws}3kW!`$r' p{eAt:}YVo&%O2FrO5}Xiˮ\X?R6ӻI+A\i ok&9M A0H5uu9k?\E7mf#z=kw"נ42ls)VZ.744ObX|?ww=WjA5uOxWOnf0}1G*_A^GMέ.%eWGyV 1x>Ě%ݬw[$$s0-jo_hZ6,[Y+8qǫť=CQ#2[AӾ55A}u9y$2ܚ.5 {{{~ʺick{uirbpÑֵYeaA"Ƚ.a%_;*(qc)xET]6m_OQbmZns@QYKKd5CڙXS9 Ҁ4(z&&iڃơmnRR'i8ֵh((((((((z"I X/W*)<ÏvcVA]s_}tMQ@Š((((((((((zC_'NѤFѵ׻k Ap!9`L׹$[6r\KI7;xƺ,\y*q^W~bSmSF5>W/O?m%67DNK\8E߆a)n-&kɮ4G$AО}6qj7v!Gg(Wq &61 ܞ;_=ݭHe4`ᔶA*dۧ^-^X^lp1t}vGZah4_%?ڵbIđ*vl,co]RIOC  y7+YtM_Tb$~XH p6gV}DzBDqѲD7p>@Aa$Ѵۉti`./ ͻ2?p^E]0s^I 028$P8I6"jۙ<ffwֆ/Ư5M_kc4g7moI Ȫ6@s[!nqco<;=8:;RJ1w#{$(k8vndhKenǭIo]2kh~n$#nW>ϵUVE`u$l^o]O89;*,\3GBHf&dı~fhdzU;/[,Ѭ e$xՔd@VmƵeY=BMG,(ZHhY?6@TVh֬ψ7cjHx#/ oEdԼE𺘢T6{N@&fđhGlih<$Ju|;{Y<=nB׷{K ad6;bFNNFj+˭2?d-.، 8a1؊-3Z>hntffl85$H&5=l~5K=SyG5o&4kvrU ?cJ>CׯZ唞+|2k_D-qa\q+n<[Y錓3\ !F~v2pE܊sr2=+;Q|3{aVSDA1:Mj  v?fpZ6_: Ώw֛wm%+$QSX"F$F 'MuSM)FxWP<|#qrZ&v(4y?L6?ZKmnmIcIhިmOčb["AlpLc'# r7W{L񆬺ms\xz[nʪL\X59d*G1u3pc2FhY(j-]vzn,|89<0xlq6S-hҖ}n'UIcISw%+tVmƵeY=DsG,(ZHCv=Qfp!TVo՗:-Ώwֈu)"gi⌎G1iQYݔ8Gƈ59deicyݙ1c ?塚=J$,q6S-hY(R̟[RXh>aKqYBRhQ Ңe֬xTY?6G՗>-Ώwր4u)"g3E'l~ݔ۷8Gƀ459deicy.fn fHG3GB@OUE{w60Gx٬1$lZ gΩfO,9lG~  F @UQ@Š((((((((((+nYy0Q܁Ҽc#e<~w(O>MG:?{/Lu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hXLu?hSƽc:_h=WZ.<O?1o|X=WZ>çU֋'SƏLu?k>çU֏ﵢc1o|Oﵣ:_hX}ڜ6EF QH+^VI;?EMK??@4V,XqdZd >c=Ak[?Hm,H^? :-Gu}~? -` y d?4 袊(((((((((( AE0=(`z (Q( AF0=(`z (Q( AF0=(`z (Q( AF0=(`z (Q( AF0=(`z (Q( AF0=(`z (Q( AF0=(`z (Q( AF0=(`z (UwXXpv3V,ž}}+%.嶂I&B9$H=$/}I?bi,}J;-KHfVh(Q8Ki#/,K,I\@ =$/}I?VBwD !O$ 3B¹o/-U"שпo;G?/mhNj/i^oK.6W[CoI^- g%\GjvD+kKX$̧{*8>8'שпo;G?/mh=.K]Q {YtyF]T;Wt;}nB@KoK]. [Ag T@IX訢c ( ( ( ( ( ( ( ( ( ()g>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|j?QE}ϏEg>?PڏQ@|jTDuevʜ(Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@fotoxx-18.01.1/images/stack-paint2.jpg0000644000175000017500000015335313222767271016143 0ustar micomicoJFIFHHExifMM*V^(if%HH0230,0100Fotoxx:trim_rotate|trim_rotate|trim_rotate|NE0Photoshop 3.08BIMfotoxx,  2000 2000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((Z" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?U^*4ZY9EySSKm(p4J(xi€)˜)@Zz@!OQHEԪLՈC:"X㊷Tjeq{Sc aJ-s5Ҥ[AEs{WG+_+p:#*_J𭿗FT?uA5B >׿_ɈCTZCTR*M-<\:4Oi˜)*E(H4*R*PV#JbSU$QUj$Eon+SN3E}zVv|tJ,9kN{WB` ?ҝsCM3:o'?/ spq󬯎?n_K)4GSGTGSFSP(4hLJ 4-(~i9E8x4)P!žzԋLQR((UiES"SLըDIO9Vb=v@SH:TڴMYXJ z~[yR LNjz?_+f^W?NֿOqP(,u*5fDsiҐ4 1R`zQ@ȱJIQ@GPԂ(0OT>N =PҊh JBԋLT5LEb0=*$: `Oү@y*B9c~O֦Hװ"@B}*Fbf;r{UI@U,,HnW8X|:DDV[h6v\O)< r=zӰ]d:2+\~g8_ul>S#|dRG?fe?*z*J⠫1RJur,Qf=B#})H)€i 8SրPԊR-1JHA@D:((PjWU0/`́Q^y'`Vor)Ԩ*fEQ*JSR‹[Ij)€W'Ր)€*ETž)`5*OZiՋS,^-JHLD2=qVb  { ^]tjˈVǵ1c#E@Oj$bUV'f2*)g!TrI8Yj}erxKH"Y#a2 KPBĠ@SϚSE*5";8ޞb|Z5$:-J2Uk4XX:4j7_j sMΛ$@q#5ocv0$+%ĉlyN5|5H?43!IᲤ`T̚ Q(mMLW_ 4D [yrzoV1?-FGӠq1Aq +ω!uSie*N?xW>è#w8w ciuZbsoh;Y2mx*sª1ob7b@52,'*gg]*}4dYKpv۸{֐'׬%#V&86-[H2!)vjŝFeI~C}x"7,Agִbѣ(Hc#ۊddDg$M|ԢzIVAtF &ECE7"E&(JO|⋁&ihHit56qw9S| p7Sך> WOU,om\ּ~k Fg)*OQ[:m,mBm&5ۃSr\Z=VD$Y#qe9{k$ i*OV⨒E5"T&+13^yuDq$K(yP u"R+[I1?-1(iЯ3b=U5qLfeeXA;ENM>U"ߊ?5ics@ݿ=>N!4&β>QhB:\J1 l#r tQF}qqvdG 4EUFI*xj|(%B1x\&Fkx!Jږl:2?z|.ż&kdZZiXZ}]JlqVsW"d|LBC,Kk~kWe&ݴ/HSBV#&/u'JbsT ⺭j6I8BQv ԶFi6*㰫^d =Ii>ͧD.JO,%݆wI8ݨt_3VI- ḖV*SEv$Fv"_X0F}i GWXXK|\ z`rs?#Si+ǯSOO}j>t8'Q %yG-V{')"T:wW&:x-暵h1֤1h"šMs>&4.>qB>]N: ZrpH? ƆS^*~)jMK}\Q?p;UXQLG  x$M4rpYƥ<ū='x}˨SWCkC\<'ݾ #` +ATWIjݑ-s9UTg'WMe);]w 斛CZv rBSگ[hQ jHe?V[ET=KQ%ީ}SU+ԥ._,j'Z.YAŒU$]֓B4$i~S\7UUmnV?y|C꺞{\t۶Es;6%bxKQE d1O ַdTUC<^٥jg5QMO*POJIc ȇ8;$ujώdĶD'AmTtM$f)3U{5'{T8 1asZyq%ݦ4tq+ĂkK9Ԗ`V$5Ca e ~c?QTuqTzķLD;j桩^HC4x!YSd%/n׍~UgTTVRSDn8 ?w`QX1Њ>2j?J>>ނQS{O>"˗@5]Igq/"47Rf {siҭͼ3*8 0FGq֨~Z_=eq3#\pze$w*CѰ ;޶8 (,"E Xք֢{TJzIVt*A=wT<DYQ囜8֛ءC⥖jV`t &o^\ݺbB, Z@\0*><exEcdM|_Gx˟ 'kt^*@v1,k\&`YCMHoL5YWYɴiԪ:"d5^" gJE8v*^]E|\d? H%?U^*+F'ЏSaЖh$~┊EAC͕MFк]X"hۑ2#犎Pdh>'Z_~hFܪA[\ȗhdzƎk(fiaȮú .|} :oS;V=d$kҴcL$_:Yf"Sp^0mMa~\SZ KD\`kMTo-ˀIo\40{38 _& R3> "ѩa@$zUT2`*g#i||re&Ѥfy>SfseEibb,c9:upŹU2>NB|:c?'C5VykP3s{֊]pCO0O`-lJ)֢/ Vl&FEۂUHmiZ,ZnL ұemJk"NBJKS%S]nkְ,x{yT"* xvO^AoHyf[י#&gIeCѸfͺe?۩pAsMxsg.6mZMSlVRkmV?\;_B~ha; o>/$B++rA)ܿT,+[ u8F$+#'ǽT]D}ailVNOF};}!]j?xbV1F$vl͸4 x}8­:=N -;K"bSvz(N2ʥo5lGZmhsN;f#q~[A#m0=9#ҨC_0]zgFzP}2?>Ӥ]X_]r\Ͻy~ǪFWO/w:=mVX0\JrwP7i_S׼+sm}\mvc]BzP PG⺶8e?0>BvrG6є3X9U*ڊKR >ZyxJqBoSbqq=ȌwpXUȭ?x zS\d[y$\Eq_X  +h=Ie,k;$$dj]ЭSs4pVڄVY2TUo,r0SPImecnVoU-@OJʳ^rU&Q:tKQa,yӚo𵛞Y>'ϒ컜<k]Z2~cޠᖰmm=y zS*x\YTP@z=rJSb`c3;kDh#(w ?.YMPvÈ#Z^3FLg(2wrG+z !3Z6ǵ9a۝>PNǎ%TyД ⵇáŊ*U\dt_ 3nbREsڲq›s$ʹj<^h]]ܴɵp@Y F*28繧egu}v~zFJOZqsX^D?lʌ%:+Mc(XoЪI} V!evm\G{A+:+:dH662ø4Cd5#P3S[ XGz;[YYT0A*@iy|od[kMB1v\ zg9^\D3.^R7|ppzk2 j~{E(d1/Y䎹9H{ObŒ rsNՑhD0"aB`U$B",@zlc$n.l>QힴmSO塂IL}?iZt*iWviHP|Jqǵym^V϶jg :kɠuaFƛ L%(TO?j<.Q BYeKF:J<#-ӡ/m%\$3+}HRCim~BFHaȮOX*a)IȯC>+:HGF8^mdfFQrAɧa >Zi*gb,#bE,t M@r)-LI;X1pi]A8VS[k;DU*I0 \V1\$jɵ戤MF~sIXj71oXF2.f+ՈXrdFyױcmR4 :MP7lȬέq=?Zӡh" }sZtg[:vxyr灎t[|9 W(EHRz[qgܾcoR]X9bQA&xYISjXx\3j_)قBMn7HBw1*y#zVO}$@c3ӑȲ:hRtj4z̠=wu J<5<'$uHjN|:d鏯e~\~u$o*q`}3hun gI`~H-Ɠ2FcnE@&!siړZTWEteS*`0@@*G s޹TZlH$}kv+- "&:3cWB+5 ,Wj}嫗X*X^jU+jlf)  t51Z&Td9Qѕ#= %[%ET naDcDүC! (lSBeR?NE:KV}iER.wc<Sk-jT4 hO'cU*X^zu#W2t8;lI{c u?3]caG^mp Z ;5;{g Q(ܴk^xj{9w2]* X(brM8p/T <]9KSm^vݸ/~BFp*Kۨma.W$6jgDfTݺ3=M?YKS>_tɛ3]hꩂjI|o+[2%Ct&\)m.?Xǥ,VR~sz _!đ`+dXdV/m]Ee|R2 `[AN:YDwabXu?fCbKԴżȏF=tzũk-W6 7 𬓳= ԜVvQflESTuk}n_J8#?|_ 3P]4"Ac9Q,%7vK 䈂T#D7ᇡ={+>ue#6'jLLc[i\_l!m^Z}iV(H/63y$WWciGX2Lm[z2~dX(>l%sMlY-.Λrc"z+~?θҧm~ܐVd^u)GszJ}mˊ_jͩPI0q2X}u3ҜʳYx b?Ʈ&)*∓W#?Nzl\\'d&\F󇎎G3lFdztp+3m6 6EVh^}|r i;g֫\bw"q4xNӼ; O(fRr~80}?u4i*Cw3nB׀'"qL,x>saoszmh8 h=/59/csm#~Upsc3dducoxϏlWu=y[]Fk*%x9 9y_N\@ - Mn:ͽk;Y7< P+,5=ӋXвy39?ҼTS<1/3sz1k=iob©hW,[mzi$U\Sl73#g>o ,p͌7%n&0ddWh6B Vr #֔7V8IIn|oigqV dr+I ń*A]N.K2DQQw:ߞ.RATDf}J1Vl^$ X*R]OWG[<mHP`cPٯX2 Vo<2\N$,ʅQ:wmXN3Px j'kc^+Y"^&!<5ۉkm}h"Z}jFZX k9- #)-Bβ EP#o?iUk;xō69?g\Oj$ ;'ʹҟ"P)3Gh/ 0|㨮4n dןW>v\B/m+;I+hgRnCߧ\BLHvg;rEcv[}pI`xגZ0OjVzۘ{.J-̪: sxKĂh@SLÚ(#yIc|=&Gc[k/&?+]8y+'ơSk'\v:kڬaoFw!$(%ٸY]uͪ\ Ƌ$zqE|7 {q{<0LCV#zCuD6ylQ^u>LPI7`ہ-tr$1Xg3c>^cz^(sJ~;k?o/v;mW$栏ƻ &6 c'ֲ"Y^iL:Y>6'mCКm)ΒwbRx9ҥ `X9N{P4: IJIv8ݜ~ⷞ SjWMݭ3'>FMzvcK; mlfdBpV@ݫgzZ[]̄HUR$:ųۚl0l;U)µ8 o^GӞ1}Odnl7iD`*b@ zO&:;{:'D8ʹB=*G?)1_SYjdPQbŷ_ԍaCEkSh ڈwB0(@1z"8e⥍~Z[*ci-"(35_md?+)iG<)$o)7(S~5皿u5 bK(.I4 #vr7RMI>Y\u_&E޼WqP609Yp$J;Wj|2lmfzDoU<|˜<>vԪI=_4}?OȗpxaWQ닠|?om|.x@'@~Gϗy F*TbLnC{qV4w-|OmmoTȨ?}9\jg9iV-:-,1PJߡ\t:f;Ȥb;W,՘"9@uc= z?EQdBdxɮ#+5? tVӍG#]szRZ*n ݝ q yq"EŠބ`b)&jUyjmXQRF51*x(nxڛ8Ujī:dcՙj9*2*(Fr{n3B搋~L.}M\k%#gf-V-eB|x5TԖP/m9?:> }6>WX+=k{R,^0~.>}k?+{YE8;1k/짘UnxU]x)'VvBAڽ;+؉Z/v`[#05Kšj:Ʌ]?fq#ζ'̑g?QSxU|7C!L~Vm 8#ׅ} JRpշ+8"M 0OkBxA'֣T5+np7+QԮFʮ삸D<9ZLi` 2z K-f㰌Xqrߥc+;u>q7Sٞ_A5Qtuڷ{I訉#mMeFۤcZc#I$)) 6S\=JpjF#uȫ$T.*yK,J}#uՕ}71vbr*I"ڢJie0wcoz`o1 zE_p"k#WI,1 &[ȊAd#9<{XQ7du']Qi9wӌ2ӳ8][CoowRLI zKNʼJgTIdpN#91޻}q$pǴMu9OezEn\j@1SBFZ']cu$)!lW:oS-Jgh}QhTh2*r3RȪď'sm_ AC  bh@&v{$&S4? $_9 j?ʾz2FEăw֝#+)pI,}M4+HiӡDCXյo[+O  %+q8xr^;QTmaN:ʽ0{[hH8u<^<8Q.p/7fjW=ᖥ.xdX<^A0v=a#XzVcBxw[l=YzzntC ;֖OoY as?17?rɺMF3ҪƎq,QW߅%mAehv&de8_04dz>%̓V獁d[iwy%rlE)dG̵;sOU5-;5Whe!|R#<צ~6UxH? ;+}iȃ#_Mt? <!DMٽ?X)-zƣ-/?ՙI.դ`&=,bi2*XԊM\iؑE;oJ4I r}+;5èP?"z^m;rĆr V$!54mm K SQ>nibg;XK-6?B[f,}1Aȍ2V+Imdcx "0 cn?y;32C$yf,?W0Crc=ErIwPaO U|M;7װ&)|3Y ZxwGԼ?y72ĿiYFビc5֧uU3tKP̖6"f^ zhwDĝ '^Jk[Ú^ ~S{>9+H17'A{>γ O/\։Mq.N\B&a émȪWh\zX50fԑ=qm.)fEh\hs 8FǠ5gxK zxsϯsSy3[i6kYzkm*oAM_̅V=۩}f=ypVsIЮ|ݙxPOZ߇@YGOBQ,T"ݔSzװiPq PFB mn7ed 55XT#Pߗr\c;$bdRr?1LmVw.0 lcX( ˥rL}͌}j/mx-U6%x}_S7NKN/mq?ezdumAѵH=8[b~ \h"Jс?VёDH>39UgDY46mQ-IY:Q4γ;3 }#w1I5/,q,n¢H^LBSve/"[k'#b0 _:G:gyо۳~xym6oHŊ巄h^g/ƫ\ igv2qC%3Χ,xcQT=+P+ҵ{FDi%i΋['으[j! .Mq,~PБYW*,eG\Ux5s7Bi٘[OJ+8P:$vhUJP01ާBv6=Pf,l;+ .vV I_PNxU`py? e.`b8bVM#9a^co-@pHaʣ f7BEUX|3B]:R[Si0\Fܼ*դwFKG_7"@ 33gҴYjOfg(2[}*x/PM"`)^ > EXL !c&hhӾ믧h{zi V%a"['G,nu8VwI]i;5-"9dHd)}mЯNtFҗ v-&yw"8EvFo4k'`3**ps8xg2V?*1+X0ry%^?Y`Y?Mky`Y M2SZ\FQ}AᤑlCo]n0F7o퍠K~Uicc?ʉc!`RW9=n]'wK$8??+֗& :2w]L[,=sҽ r׊n9WvPBe4Q;ZN;YK{x"5SH,׏I,)$#^Ꮇd$1+˿feŷ}]S̹>Wd{ʉF{UIdH^Fa5y 'P9R5u={~Hd%#7h;Npʘj}#xr)dbuAw|x9֥(HPr35 ӭ 2rss,d&v>+tc~!Ac'?ja'RlT)|9}KXXOb9 LA߄u$P0Yڪ2_K#" sںa̦ﱄpM谫t+w t~`f @=+lj7q.zpMq·7fvR8㊔ci9n\E ypfy}]Ɨ$t&naCҹ_\\L3xL yOQB]`gM1ZCzzC$?ƒ|߲3Y}| >`n81<ӷJ8H#9U=i9s3K74cik0qCZH4 %"P(9Ǹ$flIku9HPgnfY,sylhOLi#Go^ Qo5ͻѷϥB;Xv8WM+U1$tӻ/Q]×ĶC h56 ZFoJ=ƇoZ_0u(S׎y%|D},fM|xQJD\Mݗ,Yj `}i`ZjɦFB} c+4ݢu+f7$FL_wN.t#&{Ku$6bE{ R}]A/֓&jW$٦3qKIw#؏x1·|?9vb8@W;ϻ'5kJx&n2H {~:7*ګ:f/FW9GMJkd:ĺ hv11OIN-XhŨ WCm>b3Á !JjE ta,rvz/Ttq"e)Ȑgh|Vde :~`F2>>[Iq=L gAq \rIb>"+&_Z>xR{ [1;}q^3?^j3;7M1 ڴ 7'd*pиy5.9=JRU5ݱ\-Q<^ƵdԚ%>g #}k~BUāN}Au1xneR@ *]9ҔJ*Ȟd߾`{(Y‚׃//0EGNެlWrc.n!3?T9r.nT|k7ִpʜsH|⼎1,@ֽ&ͺoW`Kg5bKy؏UeةTtdWג+.J+VdTwwnhI,;cɍ nikNA⸄: _ZT#:jf(oYDa*վng 9 y*䜌WcHRVKM{yi}6/Cʵo8kD_ r28)odԓ=a<{FwG>UU՝do75l5sxl立SRjJ jL\DEZ7+oD7h|A0f e?i&n9.c9F>U OSړ#0<WxO3N%Ex}Rud?ꦀZΓy896QU#.Y;9(5t/W mk.7J"샊_ڌK1=V}AωoNbTtb6+ ?+W̒K.@:R(nuHFKFHϥp_&Gy?^$kEOK 0iUO 9гNZy?4.w8=T/xSۚy2xBcbSM[ a=xFGLV0lbbġկo"~jy/#SoR |V^x8%ܟ>C ڕ!m9湖wz4dbyy`cO.\uhb%9C!4Q+Wa[X AګPO<9YGZ> tKK,0lS\;P~'r=+ha-3Fv75=GKe@2#'=8-֋]]iWq[\@e8(wSZ0w4UgeuF^"6JLe8/'6%@9geu*Es1?/k/kAS(vG_J>4'?]x,aPtx4,aP(P+EجzP/)r '? ӳEX\”|cN9(ßa^F 8.ǭMy(4i݊ǬMA?o'Eqo?ɿ[_ɿ¼SzP_) _yrV>+g`5S<& EZ= |Q'`5S?yҥJ#Ї?8|J'a3WJbLC#& _Y?3W"{P+,GL3Wz rP;/Y @+C6m_W:w%?v0=+Ŷ{V`{&MP6'oolg;W r3Oo>Z^&v2OxO˃]xjryV3G<{ԃvG;c5:qOʏj0t#Ap?@sӇoa^6"C7@sP/+CSv+ _9RNZx9,z.?a@şa^F)Eac?p& _\ɿ¼S;C@oP?+Ʌ8Qv#?n& _[ɿ¼Sm& _[?ɿ¼SԇŌ& pa^^U7MG?ɯ¼VU#?<|L'a3W$u2G@O?g'& R!V.:R+&?_Xm@kX9O{P+w,3@h ɏƹ?-*Jas ɏƏXM@kZQgE R*TȕhT(UiT*iRSI@ĥ m.hœ)ӁҊh4LBp( @)RԊE*jUJjeZTJD :41@Ua!E95z;Fcڬ$}-TEmjAl=+Ea#ҘŶ"ۏJX8E@߳WWh٫ E]/ڱhHq+ιc9{!TT(UcTR( JZJ`&i٤(4iQM QH (!œ)8 QO xZ)UiꢘE * *UZTJV(*j(ĊHhVVqʘ YH 㨩;PVzVD*A#0[sHҴDCҞ"Fw[°׭[wj1i~03?iFiWtq>Ē,3XKrUQOXGje}hjfE8}3֧ ;A{O1Ҙ\T O-qsҜ&M(85&PwP(iԁii(JT!ԩ䎧HJTQ/j>4QтذFhAVcVZڧX1LEVzUTagJ *".|A,TjԂ4@(XFA"q uIE"ǵ>.DR> iE0#jpޟNՏޞ#(OQH)zT1jUrR-5EHZ*52 3S*$a1r{*Xv7(?Jݎ:hC~b8PԪ:AS#n!y[dH2~Ok0t .p>$U 'Hӽʏ& ʓb#UͷWJ|ZLi|~d6pORnzd{WwgJlRW&+6Uijܪ dQq:ȱI5-[O;i)EFRH)E0#jpޤJ)h0җ5%(Gڜ">#zӅ8PǭB-T}k1[rfO֩p6i(HqU jeZ!>:C@V O Wjmϥ1{-2Z,7Wwem.U ^ev3atKE[uP riʀ>bQRH4"tOTS7RCH^^Z}.j.bZQQW%I= )j6}x3! AQr( Oi#\J7rEtM,uDJ`Z3\Nju;xn|b 8ԏκOkZ;]ܢ$RLhS֛J(AOjxa@JY\zԋ "Q2'ABNjܧL.7ʊ= `hUaINHE8iQG5fW/ʾVv-d~Rw;*ƘS,RM @gfw*lKOCB0{S{ ڞQo׮{:uqYFN:-Ƙ^}ѿH)EESER7HoZHe'uc4œ+oǁ9>*!v:5 ;hZQqգ\^/g7pqutZŷ,`׮ 2NS 6 x@ RVW"ȾijEe:c b.%LAnc*Tc.iF*bcmea_Z6qFS{phb%sjSX14u͢MR%QbF^EX=dW#*$EOZ%Ƿ5pX(Yn]!bp M QѤ&vCbN})j[-"˺W |kנ<~bڍΫB3֜ ::@T ˪*@)TSEQRߌyg^ U6 &48in76*YHm-J9eXuh*u* 9Cȥ^GıkG>{/?^/J=/,\1mYqVSS94dӧY2 29sR:95 dD[xΟk+:!Yr:@玕+;e #8>i- ;sbԥ[@^S:{D=崸" p+ԿL6VѫjL }?e3[k2Ids 7sqNL̂Ik<_fZ IB lu >v>T jRjnƪ@3s6=bjW6HlE,Akٛvck %2>YιW)GoQFӽc+l⾣qM V:BNq<){-<lcpk/ _zn-4( LM:Vk6TQZmҹ/XxT][T{VR4mc<_1ihSo/C_GA˕[QgKFhI eڅg2pE$O2:>GP״C$+ HA"-᰷ohd},Vr=."W3$9V>%8c`#vFRd )Up5_e`+3S褮\-dUv?џTP'H!`~&4JE0ԆzԌm; 4ŒE$VbTER}i+ξ ģUF E~em5EYD@;WWk2w9m+L{P{آ+;r Um7[J7<ҴV xSz8eG1H*Ѳ6,F2ïRNZZ;*=Է˭EI|-C֏f|S,wIe*z%lZoqvA^x 'MIȀ/OM8pr=!8TbBk-GJHo&6>H6s_x )I']B_TJĈջq}i]mwu jϘnӁUInOL v]B|3ξԱ3HI$ gˆ3NA"2 ) jfE>Jf141Kr~?>̽je\ͲhKxDUu0zOSͬ˰#DR8&($wɭ  }E&HRd1 `p_\B79pI+'v)4IEko1xJܒ $~Fm[U∅?wVSE#˨ɗU#:sXZۣ$o^ LC>k?a8.[^s0nty,2 +zn+n@yLCHg7Bb)R7|J}$ҰdUԾ` Y&`Uyp ˩^5i6?Lڼ졵+[ӹkqZ]^3:nqԶ(iۊg:?sI<~7DIGFXn+/|8ұ&UȮw-jSYjHZ\zTIɯkiCH(N.T1K= \O!^ ^ğ&H۟^XxHTefeU Yfuu8?qo?:QHj3Npk.=nX,eme~ RzYK+Kyp̓NShNJBB3xTmrO?bj>*M1ޥbYB`RCfT?dPbpP7vm/Ȼʦ| ҙFa!3]vGa%4wG[[XF,k#Ԛ~G$V6*NW5YW`P>J# }wWU_&,,Cs?QW/2CH] 5X?j,r%Ty;h~A_Shγ6 7+Gxf)%#D?#^C 6Dr$yoj3i-/#ćF׊ie@;z,۽1LtBlpmoe 42"h Kb[]Jq9!3vٱỒ%Nf+RE#m]+b:nch=da5}iWȿ >~?0kM&,O"ݲ_,6H=5Hk}te"t#o)ۢ8&FIwIש x<#mZQ>f֐'懇$WerLW)HGL g7F 1P8EA0g"hI-ZA̫''C~Ɯwmj#җ@:xhZ70jE0d;r@Zn?Ռ}!I1QcʌAf'p^\y=j'b؏3S褮\WWDw/˅d3:k7k](~EK˜G5)*FZXmܧ-jt-5=,0ǨM }iŲdҡן|N83伀=5N:6YowPd1* Z3ӗN"ew:|}jDŽ9ky~ig/.ecF9t-##lsG"CjI]Y+o+5d+IS=EH"+Hcʧ3skv&t"'_;]KpB~zNSUX%U75hiv!Q[=+jAs~))1\EJg1FԳejv=,MGcGc\ůU~{[T*JacΙHrC4L7㑊,(-[L!^@XqZlHl,} ,._gR7gtfC8LӖwc?/W5h_D FϾ"r[}65?|N W:4H&`mgnQϭ4KW:GM>:RI(x*oc p1 n+XKZN.ͫӯ<=}:qıA# k]G18V\CԏiU}Gm\bX;]9ƚ;¨SWWwL3*XL=K}x$ kB-O mQʚ0K%'@ 䬑R+'?{R>twTJ QLXzT,+no5=EGxЛD2@*x'Njvzƽ9Gs vL9㩄:_ yȖR"Jv]퍪qhىss Q;Wi\aCdcq=追pљwK 5@2dGF÷\WRQ6216+5Bi!܅Ҹ=`f]ǂPp8M5ھ^n9joYIuy (I=[bqѐ7YΙoIkvoOպ:wluHFkY^,;; M^a[w\us9Kd1ds+ 5ޘxjS֬>[}^jM VCqYLJ|# q*׀e6#Ӧmw/?>˸튞~'@89G:#6ZUry lC9[<?,G|O־1pSޚ<D>hBdr+c\ܱѼ>V8.K ߐlIGNH9q¾nV]K.e FNGrAN{npGFۜ穯?d=W>wHW[+ vq?-tjQ]vV7ۓ&i*3yfc+.\_HIȝW'jb 9y/{/hU\ 5%Ie8mhd#GK-Ӹlm?Q6Gz?1HʎA8pFg1p*KQ-QRymp<:f9?qK/aGjcG4E" gBasRmyԙlek+^qZR;D;0+ܴ1FOָxhwI!AP=!W6d$뽎ތy6hCI`31ڱp"<ZuI# œ f0Mۛ5Z‘Ļr[8>y&q(\5A|4i"(pV[d$Pz~Uθ3䶷9nOz xB蛆юYJ,gl(@7&| x¨`\ H“MF¹|:Sy!\~[&'z0[|<9~2wqm/-w)#0ζ'X [$1tbd]; lEVOq>: #ʹ8Uݪx4Lq4!"Y5Po7^ƺMY4!kVHntZiؘz(8`0~ °맒hsq*D`*0g`2Lՙs}֦6O.",t08#7_Jt>mB#嶷DNA%-]0Pr{[l9crGVwK†2.3v漱'\g>Fe[r>S!'Aw5L([l* zgh,<`~Й'ۚ?WB^ũ-ř6-HT`jo ŭٹH%f@x=*SF'+%޴f*1Cyr>j/E<3˼|Xq3Yf_#PoAw{`zYjKk쓼͵<"ob>KKe" r<,F7* .sFDKAOn+N>1#~'S_)+QuzԿJi85G"1,J@"""EIDtԤS-a%P)6АkSCmi 7\O<0i 3*D!axrAznPcV{gPH98_f[ (Т[G,y'랞uāŝ6]aZ3!K([8mß|T:tj1e!O@v>?zOq$J$hQJRZn7#VTbtZea,6F ҅ sֻ!ӥNxsc(iǦnOcr[xF] c?*OxM%I<6s[^ Glv('ɮc(.xտgG /~W¨$I/g!zp+:vUۿUs1f,5WaDg$"6|ݻg 8FP@ D tG9ܾyWvHa\ᇇ;Iqɖm֞,{Hu~6*9ߒAso#*0UIk"eXPЩ8bz'ID<.N{WsfKx]%FSy%W& ^4ʏv.=O5ZD,P R"i-$G Ԅnyұҋk'l)(nG, ̢ PYW=z~OcG0u;DMuv}i宇%(CrvzXVVr˸qJ1L6[%XNT"YYv8ݟj\OJ2FWxSpHZ_5ҧV&]%:[KpλNp;c)ut7,|f:t5{;I]-,˜*4$ ;sRto=H<kӨ<+soB"\!ǡ+Oz@VEe 9u}}Rx|7質 .3(zgaג+nfnR=a6o q^aiz 2)?+7eLלV翭>1JWSè>Ֆ3QOClp*9FFk ZΘvlxvo@զ88ICmGj n?yWMc-`Ti?*Hlκ'nD*+iťǟ67^-At􌘣ʏV<^<4ı'5mQc|G,YnC{W躼ժFI8pS WYTEw"CXyhc#ӗ/ sCcGN=,!QfBA;97ŤrZI.ZEԌ's}aq m.sYsUEc$sjf02 }%an*'Zt IosAY"Ԏ;B/}C 8r@?e?P7$,H8  msJð,Lx*:ui hҳiL*p8 %'z5g~\EHz(7cݘ@+e7᠕4~F|+seps#6ᅝ-#jyjYxUj`El9jY17MTM_])#wx7R."gc9Up+~S8O˧ jm+FM"8Huū /LN3]m౳{I# ±Drq|T@H* > kGw4o+h̊̌b _-drxkKQ4<bS̊Kұ=<,R_C~J#&lo؛W?oxOj[W;$=EBvgdiqg#3,aw} l.ŧQ OGྏvߕ1ѐqӐ}Ǵ/%S[cdw]1I ^ jPZ6 .&C8sRI,XI,UV={xhZ=Ưk6LHi ^+-w^b"lr#9Bs*d kt 1?zuZsΰ[0\HLӮz'+Ma"OΤd];(`@2:)!7vȊ2jUԣAF՟EhCTc3Hz|ۣVH)Uר]e:?#9;%%*\V-jh9C61RٳMm#3AqV)V QhGAU)& 4+Бү]{l7qL< DR=kGLSWxķdYmRNsW(UI]BIUqSϡG|Ȝ*+&+}Zq=?#]ǎgBuϭj8"A~=yէke w.BǸ'r5yov x$ OSg&1ּ~$0vUk^8m)̉_gf|د2Ej*Bp7gVr4o!]$Jŏϰ:͇f`,SqW\gW}k~lŋ4u5F] {(R*}m} ğMǦ~Y1+Ñ❶GZ\ &efUU~?t;Ɩkwy%Jή!߼p2~\c5濳ZmY-`?jmHG:e@|Y0o tfY@j3- "vB=8Լ?,}ixqΥc{?;hcGӃWak˫tnvl*<9?Z i1\i 2Krg˃œ7RNཱུ%YmO>Vʮ#??Mc>Rk_s?ԿJTq]Ŀu/eY,B+"E̿Z5p/Tȫe(}zsV1YIjm^S8ǒ]VRI+βLQl< Q<*H$SIẄGqȇȫ[U JQ0+DN=:4ڐ_@8:m޻cx3E{X,w^775w+mZ! ȫ)kcAl(֛;[{Nk\Xos~IŝYgMh!ȱW|e~{tFㅰ.Ēow垃ڰe⣮Enwb>#aks ixG"k ǣLb.T&p̚/ĒK(춳yE*ACMg8?/W>VK!Ei]-6Ztk\F38ҟǧZN1IG0ƨGZnKG,@y3PKY 9"u`Ğ})sc$͛v wgi|`HҼ=L0wNqJf+#4߈Gn^;}Pr|Q玙ѐTaEM9_=AQϣOOhgܬgZ/f[+p"̢UI6Agc=u2 kxmP0?:{c 8EߚKtyc8Dr 1=䎻G{H<[ۗ58bF:/*?GDN=zY*)yOw~mм/+,IHv!h _3ĉVi ogP6 ~,OJF8)Imy㶳.19z#ukK۷) mI޳-p<ֳHjnoaWEMJmomè sNiwernYW~2:c=AF_ ZYŤ`y'߸4Rp<ܲ>[kV^]̘AoKdX 0o1UHOZֲtv-XΉ~!:6q/GE\2r=m7B[2_ uT?UzjaTO6sMRU8G)?ϥ{M KE :kIIψn1Uc̔p H=*c*ˏ\֒ <=Ml>PR cʓ[VN*),p08OLဪNd ? 85_.<9Gּjl«}  Ңɰ!qX|w5rՎ=:ܵL:)Xakaf%1"n2SRPj🍮.%զvu bw:]i-Vq{`{W=v_T='kcižv§ v>rT zmޥ;3Ӎ(I[u,m^ܻm3ng' ґ6@ $*np:au>YƍǕ}܈d(lq] s<0,ϵQFzn9~5⾟-!M-2l3n@H>F3˾<5ޓklDG,G9qW|E~ˍ?rvyz#h$cU>,޴,ê#PI_^ďk3P.hNF+?WLǩ&kuX#9:WY6qewsw6~M2i璤 t~.L]dt/YX GZ:>Zܟ[N rbZkkOsZ Gh#`vl׃h>Ш*zY/Hf4Z뗓K/6f)Qsg?hV;%RWjN67nSs\^%3}MZ UC$zԲ⇴nTRTu#OKM:`{ħS&O[2ݩUՂ{\QP֠1LqX2E~g:WHX˙QKUv4|H?8zEMrx"c5rB/dFUϭZ-,Ԍ`trH8%,_;XJS!=@mTb[Dx~5})U\~T2Η~tP6Nœ&Dj뚂LSqɩDFblF^t( ==*=S!PJx$LaZi&3iЈR@pBC}}Ex' mCscOyJZ?:zƙWVI.BȻ#9cXGez, %Şs}lIlL uGz4ֵ$"[_y'%Fq֦M %~ 0Tr+OGVZc^YKmBO2I,,bX@meSx tz?FHΌ_oxԿJWt?8lRW8g*|[f2 Lhv)JE@0cCgmDz !TRʟ4~֜V8,gs̾1x}?C^3/[e_DZLiV gU.a )2 ZW9;Aҕ>}HvKH7cy9['?*`l֑K \L1= "m.4#݊,1 b'eី{TЋ[H#ɁGlj[*_:&yُG wW_-Bgƅ yT{3:]:,LHcݸ1Pv(IZ]֦V 85_IqT/Ӂf[.}GO%+j+ͼJ_Gl$"z ؊ii`~e MCMS|/ڮXʠpO WSh }BG;zmsúVHf} s?+^;o$1Y-ܒKta:vӹ|Zk+=}EuocqGEc*\W׉aQX(wIr~jwOk+tc E4F>ܰg&Kg?ytn?J<\5wȻ!K6HNJCY|g-"e-0QNNAtR5cR#:x3|UkO+FE[& e >pk Nr%&P@)9FaPκQI\8].(NG$ެB װ ;]ˆppqڜUTtY/dtnW*vE=]|e/N5\G%.17~=q+Sht* i(wx4XO]lg0zī/h&pm86{ R0>KBm5f "5 ޙY1 9Tӣ [] nڰJnN6>t;) }I?>TR3ʁ~5š~v0n{kI-Av4kxI8t<{U@dbTI2yfQB5V@h+G}7Z[}xeP2Żћ>xun{)?`/}7E=jv7́^5c-| _&xk:|HRX\䪇 ~1XJT4ϕrEXQ` 4MX,Vl+mRzf .h?g YxD%e[#ټ'e`~U>)jSZ)#l#W_/es/d[C؁\mg=½ix/ű,&'*v+ 3ѩ]sZTl8apk+R_g#cme){ c⽆OM I.cx7px(լfc-ni1(U$i h:[JMYE'`n<898ט cw͜cZٽ@Ţ9ڹ- O7W2|ffi2FYwSR>%wUf6T]6A6mF3* \7UHZև>8mJQڄ upEfpfSp!h%ixm#ݢOۑt ;+mmR*?xzX|34#kcq\!IRǚ~i>wm<-|ۓc:~:uxkhm3 1+6WsYq1_kE-Q3'oI\>%6rI\^VE +|Ee5nnR/楂-⠱y$d]962T'h(FULH&&R*`D?8N=)Ѯ?!MXa<դIb 8fg[П6m6?^N(@*)sP15&RQK(sZv H3YF[4j0qI%*2\Ҫi-4Hd\dq֡R@9b0jVw[ǵxtbޭר/E2kp dcOU}?F`\fٰ2z\Ip{]m:@rZzq\vTނ2fzKU5It+pk@2㏯]Q [m`z6K+{lT;a[rFi~Q4,8>kt*xX`Eg}>LpQ*_ZZUxw}Ҷ{[MC\yw,eJXGf3=я_ք1ܙw=dbsдiDڟߏ2BpqזxoM&k( .Hu9ן^ Fo:7bʌH9#$W "[=|T:OR򓍤d5:`u5!]JJ_Ԓ nssM;]Zi ́P4BEܛQP.:yDD\FhI܍xpk5gU8} bFֽ†Ge};~!}Stgqf((E;Vrbqqэ4_. Ʈ[$ơq'cMNV\ő9"qhj,qqLoﰑ^h_9I.FF^h:YcIsD&pWߓ3Z*1VhEȊVxK%𥶍߮k& E*棑NaB|̚UP6ɐoQ{8?ʭRKrsUn>J15cV6+6OV7UU3\ՋdRjޕ'1`'J2X 0<iq"S+6ZFGwʞNzWÿYjvm{Max9uy6@ʳ8_[A5>KKoc,%p9 dv]7;4 CGLW'j*#slֳlKfZ4ow2rͽ)RT^69^̱6##qkh.!xiy7FP:=h||8 d2!yk`eŷG{8&iUecr, s f[_C?GUdC=Nz?*E\>ZEdQjs2† U[_$7FoH&y$dr[MCtnGֶלxfF$E0[ ;du#ܥN]#H"b/Μ}]E+x(li{Xwcӌ)S&c?š5~@8 =;c]^tV|U}q'2c`2@z~Jm{Ql/ϒ֥E}^G3&"D#j咺s}DXwq5KO|I\PqvYSP9Kk)lQrP¨%!84B* qD=|;y\4(onxMh`SisfVӞL Պ(w =ԮAوh?:s8T$b`bKZznu{ l|ĎJ<'#.86RRJ94> @J ʼ|1Cp77ːq¥Wtg"nlo8tkAid}+K+}SV W6ƻ>E )ciCns^"6($c «6]I#(Ӟ6 jR+GQfmFJ3L}:U!VOӔ B2LK].Mݦ\+(OOkKCD##]9*I*am9xA^q,/jB [˖ IP~ο ֏&eΗqF+OSZqN$# epHS_)+t?W:IXH+4[)^ҿ$%lTM(?xg9j!=XUȮ'Nb&i@-Wv^U#ܧJkyts nm=G>1 NUø?NTSBA#L|{̼:7Gq;NJWO xXc$AYrxWTT"ˎm_bƸOlޗ xxUԬ;D/0[M<M\*)Λ2*#"u+E]KKK|ѡd9d~5WL4Pt(` dɞ0 ô*9\ڶV'if>_ƏhىN:AcS5!K`8?34f-Ӑ2 r+hHjID؜ԊB>ÊWt&[۟.`m<)ݠ#J &8ҜxW'0W r@9$s9O ƽ#eG2(b]AUSmY,=Dh&[kkeF۳RU #xIx;sҨc?1ttP%b8 P>=.p *+>xo$!R$|`}z5#..8GoqTͩBY{s+FYzڣU*1f7ylOD9V#҆THY;9*q+jٓD{ꓰkJ&͡0Anri3I Ù. 5YڶqSLo Caա<Ѷ5r#]J7׵zƦI+ۓX=Vټ9ĩ^A$}8+:tb  ywO׎s@2Ս̖1O 6;_Gh:zƋm~e_5^ĢNIv89>J]Uݑ+B8Kqx<ߖcDI Yk޳@`֏H5R_ORLUdd8 j;>ȱ#}k~2¢uc; rQcxY-K08O[ԗB²if(I^ʹyf§:~>>Ovhs/W?>3IP=Ԏ/^mwvڌu K8; sTcft 20pG6Jf>4? [+J7DjPDuA'PECڋ/ v̲i`@Ugs=p8  yj0 ##©''nf̋7 SV2,/=ְr } Gd q~LV=$cFE4&7O$v3~x호]mm@WkzS-̰H@#'W͡=NBYƽmXI*>V75-L ySڹ)I|8yձ >H&Qo'Bv ;@dSdҳ5ݵgDUDp5`1XhH Ͻ^:3`G&rxM ߵ5=cobBI SJ$Ry` Up' afޮ$z ecuL9榲aVmD+ι3[Hn<|٬Ę)l3?[sY6yֲSBwڂƳ?y%p[5S ZCZ{IE6#;&eg"5$:j;š$:GeDÏM.zP}=܌CulFl{dž2qFGk{vg֭Bw=ቄg,w)sVMBX9Wg\G c.~5?Ef:.DJ_ZM|ܙA:d1 V{ J uٵC($xǯziCԆ9xeu#VF$VףA 2?%x7ze.2F*gւ/f|O۩r2@>^`3z?hOW/bz^r8q:#ӞO27ekZm݀x#^HwF w{(_l]XpAȮZ$uVoa}5g1Ս:9 _Z*GEJ;MCl*FK^|XQUiJ*N6^ukDӬ乀䖏(}G=+? ZCVZ|Dl"c?xn ^yMOڨ|}uSC1zXM|먭5;9zpH1x\s׉ nez:°[e[|n;346 \1pzKFg,4SD()-C \lGv߭i[ V{ sսq~+fn1=9aMMe)躐IO=̍$6;r2in; Pyw4N|rZCU)郊@Aip:zS.yw#q>t|Кd-߄ZclRD[?*UWeM^g8>ks4 0[hg0p;Тyۯ?K#'Hmڕ>-U% 0v j0T|x}O_MF?''(=s{)H*pӄRF͏z쒧< DΣo9ryT0}WhG_Jo\~V,iP61^0BO;xY>Ԓ X?;bbF#ʻ}0@D4.[sqּz rݐcG隈 Grp #niWi5`AJ O:&ܜ~u6Dpcm w+j ׀So+mp A^V;O|~Ѹ>9ɷⶺ":CgvItSg]5/'M 81 ;Ӱ\")ԅcvXU6֘$I)u異2N-UzR*t?+I x,c*8l:¯>QSq샞]̯\O:&Id硬h" Ց|ʳiʿi%dc-Y`{" +ӊI8nZң@9X89ce-("Rw۲;uk!eRCV|W>lҴ3ZGɼ੆$MŤr{.;Տ5C @qW=i5˃v#li`s>ZO7%KFcUv~c}~4rGzc8Y &VBsFECdGhr|ɫ4cp3֋u) 3'MNNFsDOʼOzȽP.[܀ɥ-%R|0 >ըCҚ=PRw,7$ SB_ ¤RI\yOyb/rKx2$Sn>SӚiy܊ 8= PYyJPcҭGCqqŹ&1iMnm?!;IL֥b;$;E9?r?ty;_+= e)Ft{5ʬyxnV+awUXnfd"GwH*sZ'}Lk`^C$8ǩ=i"x\K+\ܮ=g{V5kQfotoxx-18.01.1/images/embossing2.jpg0000644000175000017500000007225613222767271015715 0ustar micomicoJFIFHHExifMM*V^(if%$HH 0220ؑ*쒆0100(2015:05:03 15:15:16Fotoxx:emboss|trim_rotate| Fotoxx:resize|ASCIIfotoxxNfE~>  Norway 8 1048 808 4 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((6b" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?8Inir{S2$<z[ҀK=*3Z31FSAёBv(g5#p Ϋ z&G,&[MaEo1`r vm)jz sҝM#=s@\ԝIc;u4ę<_>xjUڦl9nزoEZ[{y&͎kyF~QԆǸv=%FO4R=e#LDMRzg2إ#48@ccsOƞT]P!h'ڛyd4I4M)CQ).AL *I99ޗ#֚)xojZE(lr¢#4s@䚏nSP=hc1iH9)!RE05w0&r9SI4JԔ(#84Қ :tqh)=RgҀMaM4 Ss@9l@SukMH)V+Kh̒9K|oEL6MicP~JThO?4Fۯ$Oi <A,."Va8i6i>pQ[R|3ZÉ4\Gp3ƺ!B+hiXE,@I x∡0g#yH|0x^3U Z.2 P=O`ɯ'߉t-D LeiJc?JHǫ:f{I k 1K+8Nȯh)*js$)<H=񭶟mIt!v1WX1.h-uzѷAlSaN'5d1i[TE@00:Tj&Lar3H'dcBƃ ީ2l}s¾x0ӴY[yOߡ XjZS$FD׌Qq3 L5:ƴX[y[85,2GnsL)NE+rG811F[?51GZkpA4pǭ+E5!c41C*"4 "{T$R3eq@8G}h7`Khhi1p387i})A9@wfB3Ld LEk13QF{S>ځ3֔ҼŘ Ah47(2&Z>|oᱬY@d1rHB(u.ȨsN'Rߴ6x-GKmF N9^]núޫH#B{@=BԵ+kxd d;;gۓIfv68\EWb$zu? )u ~6A/ /eOLO)]PЊt gQtaxF_;LhQFFr͏z>89p} :ް񦡦4?:KuMOHۅ(4]y8vXc2I~;x04{6;~5mKa\DmH϶kҾ5߆€^K6(g5sXؼ9s9p[E,q[Y4 @<#z 1>c`~a9WDB#Tݙa z[#IM1s|e[Xխ[MJ/ܤu[ȟNYӸ# y&cX-Ϸip@%zkYsLBdCd5zOÝ^OQRD8#.&FiOϩ_ dsWlmkI&:_'A}3LҮ/ֵK N@Q=3ǮkQ>f_6I qۃṠ{8sJρ֢u';XHqTI>4S֡c= 4.Brr!M.)1np*0}Mf&EWE!F:Ji24n&c !?):SG&}xG_.d|{)$"yx8ԟO_L~yrZ+TL:ty'lWWn5xwözFwZqD Jئl3.$6?vK3T#lf&?{\i6 ߆P`Dnaԅ?N)$i6h iUjSKpU B"GaڼPCGsП`A*(d%5h[h|$Nsޫֳ8#>]9⦲p?146b:U~hme$ˎ@+:/%x' +R[Fl)v}1LV;+ۋeӠqςÃR0'm ldR$&f[9Dc Ԑ*"&hVfƶ$+Ԭ6AX+K<[wq#yB[u9<qWG"PgAecs6]FY 1''=1M13nt1//'Kʲ¤瞄}ijUr`gN 9,5I 󲭁}N\iX$n 00p˸q@!]3P_wޒ2ehWvvggQ3[4pr?Z~8'=$dȗ(#`3vǯ5`f=Fx?֡LI@N/Z iW@ e≌Scҷh:gsxr"\I޲Ex\-i\Z]Iy z`X5 iqKoIM?8j~ :%Smn.JPi~#:k)4P8gX–9mcӯf*zT\Nٖ[<_sYVW+w, %(w#ޝk8Y|+ lgE&߆'̗ w4Yf3S+H+F2p=^ψb]2!b|r(T}힔zK8s|ON vƼjŝ#FV$;R |ݫltgT֮|M(嶪$@?œd4{Kx~ C͍#pn9աĺiש0$@4,j;FM>imٻӭSJvѡR79NMg[GjI" .$[Z\#ak [3}H&Hv7~Mms'2y'=*68 uj DhsQ 4@S.L xqE1\RsKth42qҒ?v(kǗծKg=S~g/=%פrXyF]ZYO9ܸ[1ﺑ[{I +E8lō݂Aq[~ޒ>0>IG;pH^ۚ-sмB4{KXO 0,g@8Ngź3OR b4.>haW4Ma7x(: >uk7zez'Ӭ wrrGLd~AX`Ҿ07Q;gKvӜb!e$2 Wʞ6?5]bh2:J\M fBv^NsHGmZE=Z $db)8%B4S{QNⱷ0=A' 1 EDsǻ[=~7F/AIĒ)8.4솷0?g a+ĚЭ+}T{hD3HIp@]u[CPV\7  ,ð=3Y:<Op!l8W94\yXb=j_Dfcw"<+"4ySg+&6%sD'uҵkm3GKP(^kޘ9^oE5]o^cشf',9 eԮ[+-)tO%z J}׹ywsI"Zխ7S 1򨽐{K𵗅<=nj0UXya$#DOa+NCV.,;S .f,pR jzǸ sE3.<:4Vwk:1#n;w^FSqs85m`'f!O٦*~]B٘eJoТJ 37\wG-㼊16͝B'f$u?ySh&|-^f]FC}m܃޴&ǥM=frLO>i";Y8{z^o.{-Oqx^CdN+NWF#4;w,fIȏ,?v =W/P|eXZi$y 2N*bA:n_MGlzU+đV$,2 is_j> oa,7zzzVZǯ*-,Ld޿8Q; vm"bT!mY S©vcѱD4nƈ{gR<1#nװfy]sGN!$ -qvvuNz+*=vƼ@\ÿ \xUB2nAY+rb3ǵg|Rap+Jvor!>Oy ʞSMSRFG,P_!&"'i=kMr^o-dx`pf[xr,[i'8<+cYό/k%V7V,ݱֵsM[]WύżsIQrxn|B,BX .nNҽ0uZ. aĮ=ʐJA[Ii`Bb7ˌ'>Q[}rSx_.FtudAL`v|;yM<2`x1^*EXٰdOD.O$HF:V9ePQNfm7ANCޣqNM(vo֓8 ~8 Hz׍|PaV G_b:׊jN/4ȱXE&*'QWg;o&$.-{,~cטZY[%HX>b7Z⦴n-fd *؟]ޱ{JtkSf<Ĝ\j=|G96-np77N_c(a8,y,sWޯX+K]׵&A \!TWc_k3mc$Ԯw7#$z5}2-I4f)|y^f8)ݓa@rI/ Y-ȭq$WPtk_gԉ8<Ʀ0 ^^I4(=s[>.>֦ϑ2@?OjC? E[GAq4~h 5;KD5(/ GΟ*oVܻ2}\ֽi i <_^Kh=2)%z>tM'JtTE+ɮ.]2z⺦ *[uWs,0埔3~Z* ̌CS dKOo FG-<ױƽWWY'&VS&yE烵$xY"o>:Hf %l~UψO߶7˸.YĽ#Dp3d,=c#X60l~ۛG ٤E\ې;dК m 4{n"Y0P :r)˒91ד?K+ s}S"[encm2*/'=Oֲqst 1H`9 ;+]z2 `y`tf./%Q3 ,8 GKXXx3[hyP3Pk#Rէeudݺ7M9?(6oyOqduƭsm('6B>{{0IsDܴb5]>Ѵ.hf5ܼr:~i^ۮ(G%[aYأ?l $ZNxq20eq<Jg6et;I_x4:5(0Sӊ/,v=\ȲĿ.܌U#Ow1\hcKre1k_+|3e"(mL }J#Y4dfP +Teѹ#4p1x9j;-E2: Zw˻e!n[ N${7L3LlIQ'eC_J#+", =p+>"|GcF+y !$1f,&Zd(O9r`xROMծ ]7UZW1M koq$:kay$Î z> OTI5 dL <ѥ}Mѭ6k[w3[[.p}umKYҵs FVI':lIbH?k,3\׃i,Hy%'=uM#QdVA=窚ule "?Y? -v^]B%w^8n`;\5-4p8H;@ICqsEetV ckB#9[YqJZn$ ~FkD]K diY+u x<Ք1 O<y{.ya'HlFv09Nk}\O@T+@.Wއi:mt"hC+l>kjNfyˉ㍤Yx枌.^8 Vͧ\h\_C#b6:.:{Pjcu\,BTL!}u{kr$U! S^hMв܃} R) ѩֵl;inr"G+ߧyG|mrO,yJZ;x36cyrȈlt%W^RL֣s%棢j!6onW 沄Q7*G5]-w k  I8ڼNnCwߝDqA^o|3>nsr*mA=HWy5ռV)lѻRiQ4f1(bGy2ybѹR9UƨJcL=OI+[mGL4v7)a0#4WNJ°XOz r3Z,_[JcrdzV,U?4QLp^i 7~%1>IcR>5\UާpMکһU@n7p<5^ 3Ä}R?JuT2j89w42ݺDRRv7e8?jiޣ%tFOe)?$YUc%('j,j ?Zy]}%H%"9 t 8v.NXНrd ɩ]'m@Dž\@Ɍ1l0q?[~ܡ]k$zY _G4 $S'47P\˩@_̈lk>Ć dLH P@3o{XτfEX%+{] ; gyW4Pm**Ao)yx^k8+Q4&?9g ƹ-B=:8.RCaX1]/nhu=xc,QF=k[s,3IVy|obup9뿆,#ErT~oKK]CH(*1+Z$i19V5˻msOQȠL'vyQnl,j q*1cN:'֮'[N|Ú@lcq٢z(#pi򌞵,lU@ O]2;Z+knIu] Ҿ9'i厇[jIM,+\Q'f#;+n$մI:+@r)l?ql$R+b{gaZiZε{/G$yvUp:j^ 4[/"2D]vN5m7č6zWk ja9"0g|dblp!}>H44KO@)Ij=.OnJkwMk (fvJڞ#F-tlS,[hzWj5hR[x?rNN8s(C4X xAtN..nuu 3wc֗Gsv?R[vzG[Zm$q˗p1WivY:y}6_:l1 F+2Hec{ާaoVZԐsWRۇ曍8=Es# Ujtv X~d^3JjA}O+eA\Ffct2)]$gd">1EumK{U9ހ\t]f`@PT7fETC# fBr+ys`;at<l1UKo*#vB7fYh񴚄!3+/jz3i6q ZThL?03R+t8XNkiH/Զ`ˈdߴۗ5ķS#97 (y<[u$X>[\3cIVS8.!k`޻L ?Y9ݓ"qOb;9zqUټ­Λ]C|CUGqP=Z2ۯ!bo'ڷgM)ҭY3#f󻏽5o6"n9Ew$VSOqj >ixHQ~ 3I[6RRk x8c+Nkǣxiun;hDǝ+ʵk][R=喚@Ȅ2kI4Q-QiI* f686ǎр@{ZeL*Um"#5*\<@QqU当1%eCa;"~xAk;֮1DBLd8̉E:+6AaE ;Fq> "gˁ%Af7K=Hv(\Kzg.1Q`Fg]][;x U߹ڳB5'bKq$Tu\U5mKK[&.oHWb(~{u^5y־ψ "!dz$bx"368o:0cԌg)KԣG{m6sֹ/NY-?37:V' R}A0\MvħۜtR&w⮳=meLuSi/[Ki,Y?1,yxWpfvD xRi!pEdÁ Z7Pb /zȦ`ݎᔱZ Y KQY{X2HMvzjݙN15Q؁7 zkpr [챨RNy5Z}/tc;8U1i[q1LW-^ii~RqfX^Ir!F҉>$_kأ,dް,O򑵁9UrIq.!3YmF܃t䎝k09,\%ܫ ۽mGU٦ZBXѴ?nb!8'ҒJvzԞFQ$ =jS`=sUUoy9k+0  v*}dhYz K n`vȭMu{}RNrE,QW'㈯Rhp<c ڵ  Zڭ7BMmVU2gyg' S{.-6hfK@cTڮmcliwdW+]^MԶТw6[ʀ/ZbRPty|PEF9 ¼<$'UDZ}__Ą=E?k_hxQ&ĉ'\/Y㺓Om2F|8+tƲ]߼֗, +#_K`:#*9"hBC(ncfH_PR\l3.Frsߐò-}fuRe1HPr28湟;7u]?5sטb mTS{m#u]WH}vKH#5E%PϿu^"pʙ|'Cz-7^dZ/ l͏,aԷ_׏lq  CS[f[1:9܋&5cxd).?y}խf *$yoUZxyW>!#&p]N+<"<=iۃ&#s?7:r bI<)pH#B:K!tk0+Sk8!N+|@[|zm-8+`W폎?zZ@L{%ǜ4^TVK.ԞK/.I\%{IhkNKHYҥv_? g3\c^+ .5Wam6Uc[>i@ji2*-ZʳY#._y1(o@?+wI ̍c#m1簬]2H-nų6ИczZᵭ5,.Fԯq-P)"vڠrOj$DGfƦ\: j23Xé %빖bEs E rI+AJbCU_FqDQqP(Vy{EG1i06OOcTi3CeX贽lZ?a b?~ikR (KK~bx$F+Ȭsַ-| s?Z6&t)m&UTd鞃P\r-p0$N'nskBxf3Us*q(1=$hcc{jlJWkWgH׃^m^[kcapF oxs\y5% "$ g57ђWz sNQ#9N}hOdbRNނە2>vQ 7 [ȭ6ӜT0>f Uy{ ܸH"Y50[UgM/3Piv.tώ1ߨ?sv*7y`g2[XCw!6hb3QOHo)XiHY]w qT;AA$XpByl8ƶ8$)Z৑gk!RT)eR@^u"RxǨzxO'ܘY9F-y=p!$tTkR큵qS\햘l & UI"5P wr14 H`11}4X WzPDR>`{RVH _B mqL7ZS;f@[9Q>OPF0rv1L~2jw*֙uvMv'9IR@cqipē4@5,|!!Ɋz]枊8IF6Wy好̨v't=WvO,Lց3H) V$ Q0eF[*z`jԮdx\C0,zg 72u{M}fXRݬR…]V.Q%$sQr#6$; ]ATCIܐy?ҕA8#`i&8PyTĐLv!SZ\Lg~p*l0N1QwyQڢ1QuH'qӾkKeW cS c5F1V-w Ԟ޴lVcc.NjW\]oWiEbGV`N^:kCr++ *3Lwpq{.Nqּ ʮH'["p,yG?tb$S@}TO2l{,obld'7>̷9=k(lӜp{qZZo%A=ڨEF6W?o$l_c T-$;B^Z<+ ЌV֣̈́OT5(=դGAj5Y \|FA@lIqB2Xk&7(5!k*MFܷYcf è4ur'֛NH&Vbs}ic|z{yg܎dl[ HyQ<b(;Ur8$OHS -cԴBSY,>Gef98SbsO5PrBJ W=]#fEGI#; )s,PЊ2Xi ͒XUvbnLnA9#^-"șDzdQ+1riDwAM|躎i֋/][)0G\ǽ.P3J23Mm(1oqhfIj"U t5+Bb@bNE`Լ[]J1'>fi&Ŗzi}r$A*pM+K.%.yG 2]OZܗ: >*9'@uz`qX!2$3 2ƹ4ERf׭&lg:HK~ T6R$k,pNR}IY>i51?ZÚKˆ)*02J#=hl;hёAҳ?y6Qk +3^b$\dP\ugUF˥h ^dr+E/0?qhVdңƻH,ұtxjw%d+֑,-e7MrʸOUCnķElJ FJġ W'RYdۉG$~4J-стʳn< Z6vn.͍|K){RI}Yq v/!rrY1{ֵ"7diUY2<|!fHIY3􋳤$a"21-d%QrFOv9ePqǥkAy)`Z$ZCNli)WVlgZ F#ae$uVHd3p͈ԈբjRJ1P״S,'>cWu![G[u?X/׎d|1$㊦%a@7YlEe2+B".Yc[YፈBvu3ff夗ڍI|Z녚). u.qܟ[\KLG+׶E\cfrvI Yck)!2#O*Y"6jq9W~\<ҾDH'd2z}*+s)t*;9Yؿ`lyLIs–ʖŪKc׷,ke-;! |#komFRy.G5 _ۤvWnĹGʪ4#3FPQxc?jM27.Udrr<3ҝڋ1e_I5UܨQK@_ (:66pH$eOjMn`3׵H",J Ӷ'c[F) , g~+jWAb# OJ1eç@1AKt'e gB(Z1iK:,3Z/rNeXQ_iVs78 !y]Ifo+e$j_3n'SgktrNBoRM XsY>n17]\qiXM?6+{{rV\1a "XfG|=jK$E1$&~ii d㷥I 1G` #y&O\Ki+aD*z z Hߵ9RXI8c 5}E c0%׵V{|iloHd*Iѱ8΍9w7_(m.^lI9<+vVO,6zkjP,+$ UX63魥szRyrbQ۪$hc;YC ˃CebvqVII\i8aPbT,r0A&E }=jo(U~A`+NcM,+ݤY`rYC2qxڕIS˅Bv ϑ.!K6L|z$'9&7tcOYy 0J ;H뚑FGH^l_+d sY"B=&\*\nYyq6ыiY8`OsMFt5ʬ 2K&@}(?%H0 7 \|htVE dy6cdQ+Er$ra#|E%Vp$Ssa;VP9aympr<9>XF60xȣa%]ϣC磠$^Hնɧ4xJ :+K2U{Q3 裞 A!}dd*<,&NbQd!U'ҷ^ V8 i7b?ڲc\r2M:h9m;ՋN,>Y2$af7H [H[ %#<I:$ᕷ1$~5Q,LԖexHU'B3㊡*{e669UaT|p~u`iJ瑇rjIm.Xyc#ZY@m皯>\Co3]:TG( {o p̱&rqU-\$5ګ<眞+.! ﳀ6k[!WKw`x2GzMqn?v.9&3>=6YK.U;ťZu7Wq@c޳,٠a*vu3cP}-i2\ڢĪ8@6/[`ۻjb2~;s|r'8&ta I?ۜ%[%? Ŕ\*H?LU1/8R!܍Fj!6 'RҎߕ@BG$WnY̑Bp\ V97.hjx DW2ƚqhkBK_<{~"ngc[E֥+k[)8Z67 qC,b8cǭ!g'T 6dz)XDF,ş5N00q\{E"Qvjo@]T(b'+l 95$E@yB9+BXSBy}ꙎA"&2NhR&蔬ɜR@ jA N4QM%rȠԧ@F*7!ePJ(ҹ !7Ug= (Y$ybIM(>tjk`吾QHKbNE_#CآS|! *xCE]- 0 (@1H4Q@LD)8\ڊ*?!?U>s`QE*NZ$t0h`fotoxx-18.01.1/images/adjust-RGB.jpg0000644000175000017500000002764213222767271015546 0ustar micomicoJFIFHHExifMM*V^(1fixHHgnome-screenshot0230-0100Fotoxx:retouch_combo| Fotoxx:resize|sharpen|http://ns.adobe.com/xap/1.0/ 141 500 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(0>&˭.ageL-&GFF 'wBx= 5-NLeԯmlc^U #.,Dվpkܺy5gt>^]ז<9SX 56_JMyVKSn\œg @'RwM+^&V-yCQ֒{h.f-a }y̑,;]nnۉ#D̉YHKg?15ַcl5R=Z8>A/$:Ikz7o[3ijT:X R\ E_hk7קq.4M'sՊm?{d, jvu2]<ȾT#'h*cLzSҦ|Sgis>;\4OF%%X66rv8XϠ]^g%&c?e+*߻Bq>^h E`-CsqǺXupn+#)'zoP5QEs2 ť!ѴWO>UQGCEyįo?K+?*?XΏ%g^e+?*+?*y>SEyįX?GįX?G!0N_z-ȅ5Y`H%Oқ2s\;rcq\ǁouuD0pm`X-;g ׋6 bWf3h̃ =k)+qp+5km?MoǪ:Z+o[O=Fzrhkm?MoǨ4M5?2۴$]ZRR-6)f]D<[7)wLcSwN٠ ?Wu'4B!_zntwKĞd;.\}+X]4a&4M$̲"rHdJ@)wa7esե(Z5TC+ ocGRWc}yeiI%g g2 '`T:[xSWH.⵴5Ib$ds$d di[K]?/z}FD Hi2FXn`1|dg^Kow|:5xᲺeή9xVH7m^ppIsŋ5g7Z[ `dVL(\oS̠Pz=kεSjKg U'2JvP #٭:R}~g^e+?*+?*X;SEyįX?GįX?G!0N_zm6GXѝUQOa^i+?*}CQ5jfVfM6h!1qH?J"M~ Q+}H[\M:a!=zPJ’ۣqOmr$b|Gpzᴙuwh>7 kiܩ%}nro7W'm?tW5޶\z& \zrQ'l/(D\BPPrc4()UUszơ7!bXnm̽vqJDDU@}VGu4M*+{?4C&QLZβCr]iig$Q#erAdzP75}8j6ZĖa3I q89:㜃Ltk]c#Lc'SxC3Gu#E G+:Y|R9wK`S[feCʲ,?NƫD8VޡRxduOn#Ap>ө^f,3O}GTWa#\nws@I&xKS c 4hT32 aklAaG4d+yfszH7܈%T;vHa]e)azdJ"FcD#a1GCZxG}"嶒źDmSgޓŞ9tM.XlTy#`T˺XHxk_d矒,c=G%cb4JUq-- ;% [A'uthQEQEPU*F=WS[ :VB=p3@_GMO68Y3ϡi I(DQ *?s&D"A|S4˓wd0#Ik ",[^k.U3 c~@v",JLѐ q3t54SzXQtWNҡK)QiHN[1#= kWE5%ĒN}f]d+ 8n( {ZvOm[^;<ȚB!}ӊ'Ӵ}ޘl-ʔg#Uki07p9--]R[$Q#e21OC4>m]NѼ6A0eH>9;,bh,-a#l*(nJ\'eK{}BFDC"8*x5JQRVeN;c g@G).N.Gk713zT™?oB@'R]߂<΁GQ g@ G3zԾK]}c g@ 3zUS.xwH61{#XC!i9\gkwVK6H 8@}+}2` = Cgime(#'qX('׊1PV9ʤ^>.BtUTIϭ }l?]tPF.+XEQEW- :,ڭiʇT! {{WSEy lLI-CzN+DIv!xu5wPƱmzvab!tX[WX6]~nzt[#D8 ($:MPPJ7j$N{^EDjFΓ13zT™?o+?Rm#L޷tUt[?*:>K}v~S:]H4{eNjCr>`xקBSZ2VrIZ&Y Ԝ^a)ѩZYۇe9ZGg*X~b\7ڣhz9BtTϭ QV>.Z(+G[EJ<ŵj1]%ȣCJXh9$Os>(?ƾ-E$Deh7Kc<vZ.Cd@2E9M[P_-*P,/aA \!j$Pɒ'54ih^%;ѢrH sۊ5\~&@d3)X%Xt$ЃU,'ZusL)vgr<]x N3//--lܺ}Z̶M}ʫ;$80x9 b{?٦Ӗ.+i~ .|/jjKyeiz\rpI}I7$'v){h^`=zaԯXGGHRBň x+3T N eZ;9!¡@O8 arp$!'|j_ksi'Smj$f[f:eMh't'6WR#3BNprA5A2 4R -QYC#$ҪjԬ{=YJ\Цv76[lzޫæbOvfh| uh&uKqkisw+JvYLI9Qϫ,ciڣh/_^0Ct>e$Ky$di#11\. Sk4ӆ&ne*$ز݌q:KI?VWWחwF }u1 &TG\pWd{DU' g's(gصZWfv9bI5rXbv R] aRܸY?{cKA[_Z??+o=]n@?c ?4 V<}Nl7Ա[굤xZ[ eC#ޕfi ?tV6ֶ C$@Tp:RկsE{PbT؂_Hְ3h1Ia%'y9ԯ@x`H^}{֒[*[[`lwG< #QzIڏջ[ϵ_ȷkoTA #QzIڏջ[ϵ_ȷko@_?mǨ o=[E[ߕ <+ #QzIڏջ[ϵ_ȷko@n/nWvT2d{jQ3g ʝ@Q@ߎnki-VL*8ku9|'wZo\]Guls[OU !G'Wuiz>mRG "^s羡w$je2:DT8%FO^O #U-[EK}&Be؁觜Emq=;G;kXM2G>HxlX7?V冧s ̏ymu^Hscҡu2L%mXViQz<>yJ牵n*kd0KA$h 29ֻҲf4/5KHrJ*ӅMp7[e5 ^"!X @q{W}U䱲Kcg#$VsSkf4x^Z'-K럕uWq7OX;-o_w~l7Ա[?jX\wٚwt(ӿ,G-o_w+?-K럕/&AQ'rg W2q,$YRgU@qdXI0­0ya=iiVP,QK Ĭz'Iks o=G$@mG[-Q[ϵ_f$@mG[?$j?Vo>"}aHo>B@ 41J"+Fih(Icyd$FIA;ym6Iuqh 2n(/. ]#X,e0ܱc#~a\Sznq\<_j=+ϼOC` ԦfxvhٟRel#Z&dҧiw@ځ=vTl._3 EM]vM3"G|\s/x㞵sXּ/g?.umugo8Yef)XgdSz2SmEytB0iy-THycEdϴv`Ez+Xi([|/v b ZRvW.1v쯉]e|I#V>:~Q^c'GWğ O?E~?yWe|I#Q'Gߏz,'[{hLK OJhHH zqҹG.n-z+ekqkg+Gy%Ҭ 觌\"$bxy~zʼnyPF-`y=+shk7_g'}Yhk7_g'}Yhk7_gFƧxOfԏ(q bfV T`${g4(+p?"4%$Q1L~?@m۽x47om޹摘%$54 $8=E8!bNzT>L~?GOzRbXw54.d9%G4y1IEG4y1IEG4y1IEG4y1IEG4y1W5wV #mxt*{MҢYn\mkݷ >j4%4%4%4%4%4%=eWjc?<ƀ% '29`K#'C4y1JrXJɏGhc?ɏGhc?ɏGhc?ɏGhc?&?C}ɏGh7he.>"fǪS<Ə&?C} (Ə&?C} (Ə&?C} (Ə&?C} (,j*&@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@WO, ˉn-Þ݈ouOMOz^t]ZwO׌Ak%u~'Kpth !s]5i9|nZms9^(q=Dӽ.:.-;z֕E.o59|@Z;{e,6j: hqjv&jm${sJvhZCM=$q"x䕤w /"۪C'$ 3{=NK+i'dLm0'@*=O\F/fms+ O-;͕j\ٴEb!$d}kK}_R<3}="KKt'[9cߑUœ[j=7 N?tK?]c]RD]`mEmfډY *% vz- me"l@&X' ŽHs5m>?L9-C5ƣ?cP$&C`%CHj ho%My5Ӕ\P? N~%IE{h.牶4p3w$gSm4 i7SxuOEm$&FX8 N?K8"'PXkdE"321n;dzN:Ub2LnX;1q?{wt,$qKsG/fLʗ?]ռD<@.M==.~ X"A1Uء"0 -^dx]"KǷ3LCg8 `_lK!@|0Z\j\unQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@PE_H E ί[>gR_ M5ţY@m$ifo#1$ {%zM>҄9Lr`IwǦ$5idBXd@5'M拥^۴zeд'=_=A>? 3xl]3RWVvGmn`h".6o֯}h gr Ami,/*;JT+B*,jzoX-V;uki[(Pr$YA䑜lg;ʕ ?i6jLy?ʝdPB=Ntخ"ӭA4C 3ML7?G#g%ݽZE#!=@9_(]«2 d(ƒJ 315 345 0 C     C   7" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽӾiDSE<HIBd@FQӑxe~}G+:Wk[G<#M#ŚX4&θ NkKGռ5Ii>!̚ r\  3J9v"8/DEl>|Soo _u}.M[W3ؾ RÿgJRx _MVΛ_kK/.m|\{!|#"]g]XFӮmͤg^ct|\x87 hZ]gTJF-kE%ų7^0#);/5sl>|?W E,ޑv<0m"ڌgd7*GB.r[9f_Wgk~)'լe|hfV ǵW#)r?qE%1l>|?Zז#mrZAwnk/f*yjx':|{n \F*H#n 9$2y㻭/q ]`4[2(aOcޏ{ӰDQ Wjl}'?DV?ڽ+ȐHiળ$}&XhjΝj`#f&a6W_#C^`R; [5xK=^Ti?Aۍd+0^]}Kdob=Ku5ޛp27[0'ֿ%þQӥԥ{ۋE2#)U:.*֧$g_5-VTM: oJdqyYl7iilqEvU$SK4+U4+>ѹD!ETݱ[na}^B4H"yD(P;c |P-c0TԪ_%u<|q5VNFp}TL;;c$C^g(>'FKx\hIowÿ|U6G; >*_G.~|_?0mwÿ|U6E\I'ֿAaW=?0m0GO~ø z?aW=as'֏Zq;_kø z,Oi> wC&q;_kYϟZS>|PCVV]x0"q;_kø zkahύo~?xQ>)I'֝ZEim'$~ZDrH9K@x}quufҬ^1j\90yxܪ~xq_dø z?aW=|m~?tsU-دaW=?0m [m. Zi2G+HXpǓvO^mz5=2JZ+]Xum ǵpH+wÿ|U6G; >*_G`G _1xn1*ʶ6X0 w99[>#,mng @jҨ$&*R@_sø z?aW=ϋS7Ӭu}-.lf_!"WvƹxXqjDR"#>сq=_xø z?aW=.PGO~ø z?aW=;0GO~ø z?aW=as'֛$Xa+#M#wC& Yx#tT`Jo=[7Lq)>; >*_GM#f>ՖZYè+)ssuƯA5o8s*g vfq;_kø z,~E׆o,"8Yt8>kKmO=ݢYH{}wC&q;_k𸚪X^_=}{A xl=[A'kfl1g.K37V$E~ø=DxWwgՄU5Ӗ_ڛnqgx_7? 閐>h'Rw+%l KD8!|qWvKvW@29/3|@Ei=f:N[xI.0(<,IO|J<1o:U#_-Ŀ CH'x)j6KuOUa7<G\{˖"kW',f[]WB;hTd8S-nh w$0eao^.Xo᛽fM"F~Li2QG#''x GK:iUcS,yEդc@z a o\Ʃaaqc<9ڹƯ{c"I+&V[+/?x^Y?w0mv;>dF!%m?)z>xkW:]ز6LvI8ﰞ3/:z?Yߛ/GVo)!N,d[diɆgQ&2G')o*C8ҩanv{ᗆ_ (|9#K B-Y$eV_ ѿ~/.F %Fc߇+;pۧe}ۗ?xVsSK/KOK6D[hٚ8i3k[/?hbL60'o.j5~63nzċnwm*IiM:A:~wmP$J|W!T`f /?hb=RFF37c(? `zsKo8uK*&|jd`9k]֭Ziv|Q淋 [tG,5ݱߖ j/?hb9x{'zk^X[_˪ĭ$! Fl|Nq]ݿ?[0Pġ4 wJt7/?hbf- s:x{u4o1G_ ѿ*g'FTZjI/-XP /Yj˦ivsk?̗3lGqcV?_@]3(W/?hbLEUt7/?hb-QYmbUt[Qs׆ll( ƸϘ1iuM8EIJ\AmlyeH- x=)EUt7/?hb~/.Feg P*_ ѿ~/.Fj'T1 e6$d88OjjkPKm.K %x6tEUt7/?hb~/.Feg P*_ ѿS[&WKFKk敕2˒%שER|?4 |7u}BJ-5V[ٖaAqwb@&S++KѤ>GPB4[⿊cJ_rd{RST>iĿ7w^7.9xrxR[+dKX6X4I3HC?*uOwMy?_-NMk<*a bn-/mmAES~/OW C?|a LQE xo:ew{vo_(|M@ ԫ#q՛"|%Qu4Z\5,_+YW>$]y?TХ%4VHcRp02zR_O5?@-R`Ek]O^$WW֞cQ\Chsω?笿Wj]?/^G}М~BվxR~9$֗uraDְEf/ӎޟ0z+g¹' O(sO/ڟWEW>$]|I=e»S z(?o?kFI5\EvL03y7U$ЄOo-1^5ky.*F n(+w?/t_W>$]|I=e»S ygÏޫ3\7ڍtַ2mmfL<{UbTBkkg5]SjZjzS=vRG1dis~$7!\zv¹' O+yG+YW>$]zX)U =nh{?ۘs@ukP}9Z!:ז[\߭OOO_U-u>_ 9_W>$]|I=e»S xxg.?<}O[(ڬ}d6PS_։MN/yG+YW>$]z¹' O+7Pu ˪+5/o<>pF+z5PiETUWTB-oj%Ӯo4> RT,J]"劖8Qh;Oơwݼ25b1ʒ b:rP<$@r/Ut?WkiW®3=& ]Λ@\Wk&тWkj_o^o.-nx9ШN;FO L43* Ey_供GI|uvXϕ#n:6ppíO ]ΛI-^!MwZXBRH$]ey*w:om }ɴnz} }ɴ®3=&eZ \^Nrѯ [W+ [,H!;c5;Fy9(xB)H g]],|2qh^$2C2$}!2rIx?S&\Y]XVHo&#c\z( }ɴӶEy*w:om }ɴW®3=& ]Λ@^5B[ <]  ]GO-ŕmpRH#WkE;p' ǎ5H潳L̈s!YIX0 r)C-īkZ[KB#,:: #/,maV~uI'd\4,Hp Wkh]gM{M^a ]ΛG*w:omz} }ɴ®3=&>LZ<!o <*`#F%&u,3H$/K2Y]y>Z1+i2qz#Gt'ǰk^"muxZXO%B"d!BXp1 }ɴ®3=&ӻOvA76Ut~(4AyY_ }ɵx[Qol5uQ]+d}dse@=QR\G#xIkSXnN=ޓޑg74F;>WC$ʲ=A=d*-)&A~|c-M{XOZ gUא-ڙI0w/m}G^e{lZҬo>l,p6UI',Ws[kZE4QE%q^)F kOv0R`c^O |g-þ5cKF5] 7g՛ q [Ğ2xi/z}GLﯵVӠGCkYͼ|Y9Vؽʕ85 7E'd_d~>,s5;ֵ.t܇-p˔I[n׮V?% šGKag6Z[OjFO'9mJM('Š(,lkȁZ襯\S y<3`_ϻ 5_`.NZҼI⻯K][,)orIzQŸ(_6F~]y|Aa/Fih|Gxn/$յ;Kt0$vS !$GLRי?_k_E@QEq^5B[ <]  ]t~5B[ <]  ]K[>>l<9h_j _e%AX].eB5,چ{k@_ \mM$$Y7>JKziWO4լ 1V*A Vÿ7XoAXC#Y$Y$n]ݙKiKZk6%QL()'*G[~/ m /Aߋ@@|#<+]}|M|G4{>Ud]ݴvjXDr) K~߯$f/=M'SZF: y)8vYIqҚ_oY-4AES~/OW C?|a LQE xcvK ^L]F62pKfrrBdܚu_ GYӬGug,c-qvw  R|rTW3H?Bt_xo Ƽ4~XJI Ğ !KI,R[J@o7' W~?k2?yw(]z.@ =רSb. ;OC5 -<kKM.-jć;JzS#]y7M[F-a0[H lՏP/7?OŸi3xBIxo^3Uſ-F2Fh!#Gmw<9r?oZ7SBt_xo ƳcsQ'PnF^I8Qdby%Cy9cY8"2抗s˿Bt_xo ƽFycU;y?/C4]hx?Bϰ_* P:^/? xg)i0ᮝ]GY7+{)`!$WBlF[M+œd^;y$ξT;3 zǩs M.Yu#n$`0(ޟk}% Ewh ;OC54S] Ewh ;OC54Q`<| cX- -%cEO 9ŸDI5;;V4DIsdP+:zPW9_M)?(]z.@ =7C~}SÑ\G rn~z!KDi۪54+~7 z[Bt_xo ƽF,…'?BzX(ZC6qnB!AnqMl^@c_DJRN=S˿Bt_xo ƽF,…'>5sii%ڙ-4xA[2 e~/O(o>?/1ͨ zZW\jsx͟QCoibQ;\f?* ē{lZqk}U/ @`Jk^{lY-*0I;Cӿ/Vy7lm$1bۃj*vU=;jӿ/_;;ժȴ§o_ӿ֝?ƹ_[^xE>#p̜gCLN5j-PzO :Kldѕ;ɉ35R Jk*y$* ~|^Ow`EmwaOa:G*?2TbʒA*2%,#^y/.kCz++qrAfy-2J®Tp8tofHml@cl$SSZ {;o]ۜMP$g5u=V_kN_\w*vU=;hN5j-v?Zw?5§o_ӿTCVzw"[mw AqZݒ#p/0M6ɘ^I>ة1/|KuEsmp̗VE P3'Ctxco$Vhul@ue @v,5h^[\—~Qxar0aw:>XZ@ְ .(3rI'r7 (diDP2Yw4~ RX[dtӊ<y?4mikkN5j-WEkN_G֝?ƸTCVzw" jNZҦe!YNAR=t>m-5Mi'- $N6u #e\2" ;$" 3{T~:o41eH 𷄛 xΧi-n?p|3qtZw zm-@]c 8,$sV?S ZLkN_G֝?ƸTCVzw" jNZ5G<0ˬ;>v3SS ZX~ 1*kw^j/k$7Vj6L`wFdw (ڦ~5 X$֏Y\wGҽzW[Eziyy8 }9yR=A拏rd?߇xLW%ڜ:j81\M|f9>cYבx˟ ]xOBUi-/aRȤA $(,wܭh_'|߇<:%kg 7-n m{B'y$%"'N3}?OoZ 3MG0Bq'oxZDŽjfmcoɧ? 󾾂oEr<C~(?lx' PTutW) c?7_2ǂoпe@|k#N^xA xA74t?Ux.erA'g-TONYd`f'R<g5gH:~wvk2Yv&y#o `k\׉|kt=_ޗ@6ڱ 2Wdqrg+{KQN&¨-T K^_+6#O[ B?U<C~(SǂoпeG-ߡ*9O 2ҩkGu+t42$yw$rFC8 "?xu}oN呢dKˤlh>y.Sj~}%npU~Eg$^luυM׼<|L'L{ű},o7zu[ B?UR*=N]%]O  /CPW\/_H/*-ߡ*oþ%<mknr+ X$}&QRE2iV^WDR̀Iګu'3qnv-:"-MF44Wov7׶\ZYhxyWT9l9 "o ۟c>7K 5FkXney 2F<ج7y\-N"E,$ [+_k@=h z!O?,k'AҼh2;K_P1Vq%I H=k芫[M }|A_ 3^Eɿ/o?4_k@=khy7% fK'zX9u}_Nδ'LS#Yk &Dt VoICD5t27u;mHq~W!a}i5Ix76D ľL_Bݞ3@ExƃKxzX|mk隅kek}O0d2@u~%!aeOci5ĘppwU/#77_}V->里t\B.w[Ec3d" Ղ(}oo̎Ed&+K񧂾)6!=ݬڧ5 I$y\f௎SzƴT&C&Yj 5 `[%eܥ@g5-=Ubk'EgkVՎ٠:[YH?*n \O-x~xN.=f{8] NHzz*o}?G!0Ӽc&]1}zP\B P%q2xŊ={JHthd/,v Yc ;T5։y*QSqn-57!O΋Y_Of^_ y9=J=m?g-]%Gc $#b^^m_ h'OT.fT=&ookDa#n~$ʌ"wP;HM+=5{1}lnw5bg%s1\炾*x6iec5Ķ^l$B Fr29G:+ƞ3 WķeӴWd8R'޸嶰<2~{kmZs=8fBl3v>\sG+(Cmyoq4'l*!`[2'l@7G0 Qg?pW ssk}.vGdU>_s<{ŧ@fKXGY&"Ě`Gg?ۖߓR\xSeW$ BN U׼ik.kayIdڥǹ$@}Va rJR=GV뇶Pfm!.MQZႾFzg'S'SjIR Qg?qĘ~|<ַڥZXv= GrK0IR+%:&gL9,5>SDdԕ#$-FuۖߓQg?6Y\G ŪA*N\L DSN9MZ4ILr,C 4X խn%XR23W+'XklH;+Z (Q@Q@Q@Q@Q@Q@Q@Q@Q@ scھG𞧪xs~?Ŗ$K G@F/3O: uTSOZj ~wz$.˾\\󁂠G5A^+Jޙ&^C48V,O2~3roܛZ*=_It: :OKme-NSΏ uvUWִQM˘~|?{]3TՍq4>-lgqJ@H1m>!x:Sx_HtҵO ]$lhNRQXpH;K9I+m-<j g[խ͈P(`$F8**o?R/Чx ^SGl5?]Ǧut:֛k2ۥR2inῈ|K5߂,5$io 13;fqErk.±fQfQ,$dBeMh;ݴWx~&4> tӖ /7WI Su$6u+4 \i}' x>I #}r"{}ZFpuKN-jwᔮ#>@h "Pq^o' 77Nt|_4+_;'­ <_WpZ$:O{ec4 .A[/O]#_RZŶVy 4D;8?ϖK5@%?+e 4KVj%Q mk_$Tg]#_G$Gl!?ƫ[i>Z /Z]Jcku,#٘ dT2:zKVhH'ӵvLb\3 UԬj'Ҫ[i>Z /YH' .A[/OϖK5G%PIt B mk_$Ue{n$TA@?%?+e 4KVju'ʇR@p%tpM'%PIt B mk_$T[i>Z /YH' .A[/OϖK5A}[갠夛Hz4@MjA}meU$w+?A7z[l/m%S .&br_=|0K}$I=:{bǶ"- As:Lϸsl^o6SӮJv6E9XH `NA~_ojNSr@:]ux㕇>%x[3OrPUiX_jl2ƩVSJ?H5IxV'f?%ׂX/wOk׺6_,1 ӱ31 $qB x nۍ4϶G3Ea :e=*Sq<ꁲW Wk n h<3N!E-ge|o`Swhq~æZ]^mYmPI!Ks)I]k/%~D7mnDUL嘌Oo&_׿<1xIT XK=HdQ-3sU]NjEK7:@&P9* s_2ᗈu{Fjz0<<4jL$lr#F}k燵o +S6M Vo//*ס@yЫ}Vv Z[iא{ig28'V6zeoح#X+p*287ڼEBZ:(>//*Щ}VvϵxK y#ynsBqZ}O&%>J*fotoxx-18.01.1/images/printer-cal-study.jpg0000644000175000017500000001504313222767271017222 0ustar micomicoJFIFHHExifMM*JR(iZHH0230#0100Fotoxx:trim_rotate| Fotoxx:resize|http://ns.adobe.com/xap/1.0/ 1448 1362 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?+4/S,O+mI]A;n!X( 0^T4 tKXӠC%Hў/0 ['-4? iӝ'CMBI7\%q(gphר=_?k,lⶵi݌O2'x7#Դ Y鲾m | KxAj|mم"cs)SM/=?7o6Xh~sIZ_i{UoSvc+[ú<[\:M8 u _2-͓ߚd~Ceì%`4HKv? ]eCOhZwP:P]iZ~2WwEpm-5@}Ky8wJf'mm-fS!|Һꥩi:vZ^GۈVA04f ⸈Io,rz20`RW?¿[m->cϙ%2ZsdtKALwtW =j{ln/q"+^iZrg$2hOY}?u{*Y#8Z_oIiyR\r C~LX ( ( ( ( ( ( ( |%?O]įﵛh-FI`=kǾxB>(; }U%=O_8<5)mwxB~7*zM+TlFk<г˽XIZe8YsZ:.u햳Q,2Iڤ'8ߥiPQZAwn8V𷈼T<1j_G[\$,Ġde6IU?]| {Xjdo٣egee|y$0(ܨҵ]k,$ 4%+0;s8ng|:[H|Awf6מD0XXFP bYXEr5{[DMZTt.Q 6#PEC_K} Q -fGK`A Exx]Ώx/u nWRHXo-d&hKc*qu"/[{e\K{jU.oRGf蝊>XtzgYh]}Y[A}G6с[tدk-te7]\[˸@d~};ˢLI hHAN8iK:{{Eӵ]N9$Җ)%L&pdFnKgګ*Rl!Pb/--:H2F =֊O7>J|c T|K[xz;mFO,-N XԐ8x z+#_ t]n`[^'Y, #>?Oy/vCm"hvC=9'ǚ-hwjp<"VE-@x988rľ+\z KKEm*^vv@8M""98Vc`C=I^C&EMiWvxnExƜ `28'A:νKg^kCbqč25ʥn˼l2 J+f {\xSICȧuIg4^_^[̣G*sϭ Q VE(ռIhkk:m:#Јm+=& &i5O4a֦/ i7Z=K8X]""@G(PG71Efh}4xǫ.(ώnZƥg n[T}>ⷚ58F&s`^^Q\.PlBّ=ξNokږ&WL.R3R!@y7cxsEb]WNY- $q1A䞦 U6mŢy|09O(<3WPeGw"TjwAhĶqm$WӤOr:[ @II$I$I$jPL4'dY#u*!)P9B.Xķ1yAphXsdqLt۽> m.fK9NO?)y+9?fVEF[[i]zeijX4IwtI֍rԯ.kKKk+h="V1?nZV/kp/H.DI*b=3TK:MQ >9$ x(((;RXe"` `9_G,m2Nҭ@C¨=(ׄ[!4gq$Cp4{Q9"4hQ{ky^[ĞiIy;bG9tP5}fH:o1qt4{2\_pP8 WGEQEQEQE~[ZrV෉L\I#A1aƓ>.Dvf"?KKHa\~2 ֯@Q@Q@Q@Q@Q@pCvRWTY5aYL8a" {PҪZt=k42 :0#>:W$UYo5{k{*L^ڸnm5c'Ɵm614[gEQEQQ$"&v)` cޤvjI뙖1 ,4ỗTKY̎f@rpFYFG +ǧ֯se22cXE?t8uKзV1ʅ]W<ˌҀ: B@;g5?ÚLp RNUG,4s'|.ܦ}n]Oȧy)x |[xm/㍌wZX{|[u]zy-;(Ž·,m-m!+{hT$qDUt((((((([{"ekM+431 = 5]J+{*htb-:9Sf$W9-7mnrkGi m ^fDi) wmJ謭ah#d Sᶌm p%ơFOS^-GM> .-mt&:f\YZRZ;<}ת2=O:-?k,~s1ާր6kּ?[N{;ƗZu4yD|p̥9<[Ep% ??KZQ@-6DŷWެzV&@QCGx9+VX]˰#(>&OiiZq[`k <O^[h hjկ--m-RCoCeNb+קm.5;MBhC^Z$&NQdۼc;\ 𿆮Sҥ0RCeSܛE3EƑA[vnCh;cz*(Uih ( ( ( ( ( ( ( ( {{xnmE2VҴ1Vq F8P)mf2D4ЄjqAڠt8eO)[t*Ԁ95vickwIJl>#((((((((((((((fotoxx-18.01.1/images/make-waves.jpg0000644000175000017500000014466713222767271015713 0ustar micomicoJFIFHHnbExifMM*  (12ipĥ|bSONY DSC SONYDSLR-A100HHDSLR-A100 v1.042010:10:17 17:24:19PrintIM03004 ' ''''^''''""'d02212 F NV ^f|^b~0100  b'  P2010:10:17 17:24:192010:10:17 17:24:19Fotoxx:trim_rotate| Fotoxx:paste|shift_colors|CMYK|make_waves2| Fotoxx:resize|trim_rotate| Fotoxx:retouch_combo| Fotoxx:trim_rotate|ISONY DSC    V@ABCDEFGHIJKL`MN!"#$%&'.(h)NoneMLT0 I@.=b~ ar    .  az"X|"@=A@E#\~=/E`'6 6P D M !??'6 6P D M !?? +, @ @! PA   @ @@ @@@@@%H& @ $"@ `@ @ (!@ ` " P H@ B$P @B @@H@ @ @@   @@Q @ P@ @@  0 @@ @$A@!$!!$@@ A A@H0X@P@ @@@@ @@ @0!@@ HB " @ D@ @ P   @@ 0()@ @@@:A @H "(P @B8   @B0 (@@0 @ 0(  @@ @ P  $ @ !@@@`@A 0H H  @ @1@@`(H!@ @ @(@AB ` @  B$BA!C( @H@ $H!P"`D@A@@ `R@@@ @ $ P@@P@@ @@ 0 @$@ @@ @!T@@ A $@ @P! 0 @H LA !@@ @@@@0@@@ B" 0(P@@X@@ *B $ @@ @ EH0@ }_l?߿_o?w_߽{{K K X K @  \8<^^^{^j $,pJ&vsfWH~n^ rb$7:  .>obSEhzP~0A { '/7@HPW^fltz  ""&&(,,..22P f8t`P@2"~zxrpljfb`][YUSQNJJGEEA@><:977420//-&,q7h p$"(pZVZZzB, w&#N/-N^s$7: ohzPj \^7h p$"(pZVZZz!0Ph \^0A^?_}w@ 4B4(R(%t a\\h(R\@@@@@ F]m00p0b@qprpplmnml_0#so߿{woH& {e **R\( =F 'Dq4Ma  t?wiS! 4922*2:y.r:Kl!:t4t^2K }:!B[~ &)"&74-!0&''5(     #!~iS.vOu[q0$Dtff6 s a BGds g$fs 6 fs E` @! t  851/,! D  t I ~ P   ) ! 3@M^ r`K8 f  u @ ^ k e [ & Z OM.RerR& / u T O   5 ~   Wf '@A> z x M 6 m n > B & HQ t #_\ZWH A  d q 2 P  v S }Em} rx y]7 P V " Z 4  w <  Y 9 } MLsrm%Q  "*   g C h [  MF Ru?!  u  ( . $ s " 1f E7+B$[%> [+fH.   N X t g  a ":<UEj H M i 0  7\9#uIQe 0 3c gN(wMgVN, >  JIgzrO *1xT?Trd# 4<.b oQQ) K7|Bs\x7c^.z#=<!)o`3C|pt5B {6-@5CnZdEX md![`S;?yP]uGSa 0 -TE&?qrC3 ,#!!4PLVS)EleF#.hGAT2 F/'~}jc]XM%+4<2MFK$4?"\w4/<8Tqd4L9 Nv ,( qdS?7)]iTCIHVuXJ( B}~xwqxz~M)   96 pvwXV8xfadk~x|{s\Q|t}vpfL>=2ivtmgpmwneV?)2:- Qgwkzk^hmscP?;'31//%5K^vkvad`aRkb\32%)(#(19?BGJxZ^[[Z[WRLD<3)tkaJHLWailqL}}voh^TH:(zc#otukrcP9>N9wVbU|fTE2{i2AY $D=+xvhCb wZll^QG4  %/972,UoG7nY'  5E<LMI=ns;rj"7,03$  1B1QaP:?:Wy/>'9XH->B5#0>ib_yVO: TA/ys2_gs{x>@f[e< |<:tC?WuK kpk sWG\qsmQd0|~_SXXPJOf{mdeDM~kN?B3z}{ihVZWTQPQEFvK]05<jb]d}Tqwko{z(]#?d@7!u`awsdAbkPyad=JRt]eoy`OSSPYTD>_wVbedi[TZMKUIV^[Zhoosw}mmzskjx}sUiv_P\]h`b`fnnpxq]QFWs{luaGECDBcLccmtXjje|yskyxhRHRmvG<@D?@AOr`\bpagoEceb}xumhg_\HKEDB=CIHFCCFpxzy}~CZXYzogYUSPNQRPOORRQGTrRQVP}ufZVWVSNOPNLND2.GhxhdPHCMSTPKy{|uqj]SSQSPNJJFC9-$#"$-17JFJHy}}{vqfWUTPMLEAC@?=<;;<=;H@:9u~}zxpmgja\QNOMFF@=<=<;<;;<;75tz|y{x}{yqkefgfb]UKEBB?=;8:8978886ns|}vyv~xqkjdeegbd_\TLHA;:988664111iit{zzx{wyovuwifbaad]YYYTXSKC<947544531   ' ? M J F E A 7 +  ~ W 1 }H,RS & : N Y e t! u b Q 8  | G ^'6D9rY L n  x l D  @Xi[% (       ^  %/UoeG$aw   " / , + (   P   .   b(n'   ; C ] W Q < & [ ( h ^ Cg"g  9 P [19 i f ] Q 8  xw ` = ,   o VM^.8)X  A QR O U T J #1  q  G[4> b w {|  yi X}4U  \ w n .nX%]`3P  Q    v/}m*[2kq-X  ']%hf"tjny[-{=9pe]4i~D {zvsYQ{~ropevu&AyMiYzf4ggeccdejscrP=P/53P VoZw~)f Ye)t}    9F 7*y)>TQ~ 3Tc<5(    +nO6!^u)6MoUC& lRIDHNZd(@PL:4% ~x3D8(%('  }}xxss)(,  }||xxs{xsw0*  ~||xruohcftttqrrnnd%18pZ][WVPD;, }_0~rhX#{ )8>LeomjhcUD5%b0L*oc/'.;MV_utvyzhT@,A3npE@&/$;JXcoywuz{|uZ@(uwD(TjxbQ7 f,AWbdrhjhb^Q:%jd92 &;M2o^^XN=*Ig<zeD ,Lqm62)LjZK(k=vs,yWAlVp-&!%+D?Vm.B5`wdtldzY}P*R!Qb[uh`FA~eEt#NYS`_^dm[GLMl }>B]bO{t~rOC[ ZYgUMIo8-#ty<jXfi{TR\L`GC.9_SU}~e\h~lfiijQlHHE@?&+}iCB:TIwHlv]jg'l@<:7778)(M%2?,3]p[\ebon|O{XR\WZa Pw+>QJ6;MUltNzBlX}N`eQm,R[wS\yn_IR[s_LLG}cpxuf]_UPYFR^a`j~{w{s|xokh{dkqvvya~vupc[hvpoq~xyh ne]]]]h}xuttbz{qQFape^u~  }um^J6434<DKsszyyoplidddcfeaqjcbzynhffecacacc^[ysrmif``^a]^__]teeba^^]VVSUtje\b[ZYZWU&g";Cyw     i+jX' EB(q' +GY!($Fc>"@!&,' 3 OZ8F:*d"03Mz[A*c$&(< 3g/<    G D[i!   ,$ 2USeU*(:2o_[ 4 F" ) !}KI ,   +  &      " %)5 - 4  "$  %%#%#!%$$ #"  b00.@(*0  6702121502100 ,!p0b0,! 3A AASCIIbb(b nHH (2< :<6 (8D82VP>.bbbbBbbbb8bbbbb.Bbbbbbb.bbbbBbbbb8bbbbb.Bbbbbbb }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzx! ?( ( ( ( (=b .ah~*8QH#)Q@RPIH )k 1(+f8lE?+:gU[$+9_ܓ^V,( LI1EL"Gup3 +Ԫ,g~cURGGੰX0a%v uUtRUUm"`.nnMC,] w(8 E+^e1LCduQ`|ǰ+VR:4) IE!2J@s=1\~d87602W5gΊFy>Byɭ۽BDEiKc:.[htOҸm[ŗLm豯SW~[ O.+H.E`K =7_2!=m 2kЯ/ruxtUvYD!Ux L._^`3 ߬$yLOe%=GP}ED!B1܊4vf\⣞{ ><0N3 SꌽW\@^ V\V +Y(Ԝ|.F 7|-8p +yk%vs#.Mvvיj^DQ\(_=MYw?0_ - F#JAݒsjJdLj'yU5b; y橭LQv ?UZs3=G1Cll˽H V:,o!z%##!l҇i8zH~{2{[7rUOSXp}hhiG7o/s.x}rH,Zm}*!0 ng=h=sBLہKjE wb%2j9$RsHdK| }M7s+֙OJvx$vSlp{glX@\P+.zOjB~]z5"X߸S$HLto\deOuJh=ҏ1ئ! ?+yL~ *;gދm=iX<~w|zD={{RE"XtP). 8{$]߫~Tݫ ҏZ#1G~ƀЏpiƷ#)#~ >> 4),w_QHc$o0O[zҰ\hNSI=&rGC:) 5U{dR( >aٿTdL}O׷ҁ 8=G)3c }1 fG`O! CHM ?O&O=*R bOn:ԀSp(G0=i{vc4B}~QE#7?){1viOJM#ր@tR((((((*Photoshop 3.08BIM Zhttp://ns.adobe.com/xap/1.0/ 1 2 3 0 8/1 False False 3 False 0 R98 8 6 2592 3872 1 2 1 C   (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egcC//cB8Bcccccccccccccccccccccccccccccccccccccccccccccccccc6" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?袊@4Q@r$8euW2[ZdQʦ4>9lV7=?lCBaKIKfC+T(~U zu~R>3EbXQFh)XS, YX,CI0*.1>Zw}*AcSJĹ]oqTtNьޯްH =iwy;+(5 {6Yڷ Nk!HOh䵽C%#ֶyv(++K#fOGҒI5vαc;Bo^?ZHipJǍ_z$KW[؂f#v`1kX#'wVo1KP}(.xvR'cR I\^ҼjEX"= Fn?ŏT@hQCbx 5(sAV!ۚpO-ߺ*(aEȮڤEϷ i{rt)\8nǭWkT!jI: Rf-`S Vdީ tҞ;dr5o1\"sL{`ʲ`rZq=I94QZ$V8wQdȄ+[hw;m&17O\BQEQE0 ( ιshbWB(OaMqw{w$8 +Ѭ`xlcLϫ ֮*ZX2rPY&$S9=>ˏ8_x~!*9Uc#b}+ig*5`L=Vu$*[` ?+O? -2I$,uwc,/?ʭBjtI Ca^Ͻ/AZJӵG,j LAi,BIBlRfׯ·ɚxuFv 0FG:c(Irk>ĊxQ$SrKb}O'\sW0vT\s;*}z{Tk%FzlѲDQBA"9JvA}jU䝞G.Ib{V#2q=\_PJȗFA;N0O?sҔsU@ ,x'_ۆpPx)s< p1M<zΗGOʐp1tPg>ޟZkO8jya `1qB՘$nq+K(((y![: KG5?pW<{? Pj9=)AhĠQ4}hI2j[Q,xsZF&3ǟӃMzC=ؖ!}SsSrOsphcqiqϷ[)3¡"V-px$~| U}7.?'88W$'z~"y^Z<r9`'17bY؞[A'ri*YV`b_:^enVҬGL{VdHM&i +W,jslK(V88buINxʽx`{ aŦJjĪ,x )&IrO|؏ ZRz;hT(0V=ꥋm29 }wUÊ$$^WlP ʗFlqҥ~ɿU#δAP.3ΒYw[vԅ'nq˝˖i'=C)B1wtG^?Ma'ޡ5#jUdMÿ<=[x:T>ݪr i̬Yci qqH@gV]32Ogҕjƽ;eTW=v3&zzi0Ax=?QLQ.ѹLUqɚ3IE4f\њJ)ÍP?տ$'qXHJإ2օ45Y0>cj#IDO@'iRZO oDct*u+-؃ְu6 {֧re+TZ~WaW)sX`MW8k1Ui DO.m+ˆ?6zȕ*Rj&oz{TLM= CPE.OΧ$/$z5i8HU8U?CPEqmyg27HĜsV+{c<$pq=NO1\}hHZ->皣&/Mm:g/7hCHӌc ToS#ޣk۶* A)k.rѫ`j/+a߈i=SOF7俶:U1 goA$ 9cjo9t}Mwr >l'-y''iAuhUj4aBǼ}R0\gOJWZP@4ҶCO5Pґ\1{d};q޵qNVT 7>QP"䄾\~$4-X33;|l {R8 ֫Rn4U昱hdzQVdQFh4g Q#;RIfvFIvKR6?3Nel ڤCT8kg@\_7-,f#8ROb8&M>"< ei 3d*hqw=+ԗ)8ץuq#/˸gZhE=ڨii籧(xJŋaE2I y4PǽOrW#ގP͒9 R$`rjFR| ktaA(5$qC (=}@h n@Lm)0HmzBO;i\vD~QqDq,`H֭ȨX)9 ;R,I0hto}&X*z*JD A.a (9#/=L\R ;쀠j@;IPmQ\3h&0XbNc8+MV%Ȩ,U=)2!; ĩ Les##'ci>H5V3"+|ԱuR(\њJ*̅&G)@H#`Obg2oϓWd۴Ǔ+^dcYbf=|I->>@bNTЅs+~\ӕ8J䓝X3}֕.b rNqP$ǵ@ґ.AM%E aMˌ?jzk!ZFmkmdε,7X4maڜ1rLX& #nx-»^H8sOB;%Im5b/ϚIX$`c̑W7 23ֲ|CKj>[dU 8Zټm]jM\څB @*ju?O[<nw[=B^0T{tId߀-*Ai\|#c,ZU[P-ܢ0FFP v= cmUA&[_n*kYo"Jz&GI@7+E~~h:`6Q}˱ EBN[;X.=@P@?/Ζ^o%V Pn3`w܊Z%&i+`tu&;5#L}뜺wpwHx:/j%OLB?~R7$O J#Ҕ& EZ–97ǧ7;3@E'8S`cg[`TO-[T3N'[ }}2B[E㓚\47FjK( ~ ܲm;TJ3|el8-q5 c2\MlZLMrM4vM%*JFgf?yn3byx8I&MP{U]$юj(pI8oAVcYe3HOJarBoIoUIMīmż$es֖&f%ImJECE{-׵ ԕ[VXXdgR܏bبK,RF,cOz|XJ`wsޞz7`tjןE;]vGi`I{mihl+Ny$n5n?=B| ՑPlf+4JrK8gҩ-LДM34OJ?Qb.jDܔ5#9S# u'KFIK|Lvsҿ;黃H#bW;j-sTn98 ڕr}>濇8$]$ȏ(c @ PvΑBnh]1zNiZԯUq >R'ak" | NVar̭&E#5{ԑ,#mձ+.[+slw[) ,L[m|põT 2JIa%XgRSY-W'{瓚/&12BC!Nz`@YĻF:⥋ 9#Fa(#?Jb(Us:t-w`j[-ӁI\M,>g=OsP4nC,|sP2b}ye݂ߝD@As[X9 G9$~4ИQQL8`t4=餩$2t֟u s銎ԯqzEHTߝ;8R5P ;CS4 e9ZXBĖYrpq֯ qTYvM'^NAC(>gv %y2zEG`W|UB10A3Sʇ" pIME~3pIʈسf0Hsz->C(HnnF@`UKG8jITtcPi፪yUЅ<˹._U8p-sy(iжz6-dS0A=tSȱ4G88rm="5(F͸繪Š,AHϫ3E?_ho^$~V%mqJQ^G}~xf};;$;$ܹfK[v2z.Xɤٛ9 T}NzԲq3֪AӞ9fG=:Mj4$ F-ڮJqsڥ3ԃ=w~uVUzԻ n70Z37#:Tr;d<1MY#m+"uғZmj&p{RGOJva8R)# =O$9|ހ$2H@3Dop: ~*AmU,b(%k\rc?Փ)ZŧtBO" sϡ^ˍf>JV?9u?NsUU|jyUmRTJHex?JLƖ8d5Y;U_@c-UKkk[ɿo$O15&+Ig,քA 7jҵGl6h@P q}ksT8xZƕfA[k j2$mp'8b?:GA":=1szC|}j QQmt {w:M ,!/,#-ީL뻥],UЍw#C[NV[AjbʻzaRϽ6 YCCC® N 8|ĩ%;%l.N oPq 3vx*VKߚ5ųr;r? pŋb|?vKU:UAvʡc*1jƝq3j1nW'.k"%g?Hs;VxС~5vٯQ9YW/ p*N&iby ň#gUX&F'R:?3̔~4ǒjƭ6ЛHdc0bZ ۶"ʏrĎ*KRTu^"pz򬛞LfljҊ'w9(qXwsM&b]}t2)isל1fo`7FfsVz:&]Z6y kc)#9HnpIp=~]꺐FsO&~Vs׸DÔ"'Қ)z1"A8ps1`IhjPe7&`z[+UhCfυ[9W[;&??WQu{n*LH?/ꝧ.OM'dk zM>S.p xnM,jLcHy.lmI#]nQ_[GsI6:gU]:%2#Qic&:P6Cci\&G|TwWJe $`N1 U\sJmm0[\u3-!U rw9Hm1 8Z hcX᳔"Bѥby#n]sLJuY ϐIDFH47^>{w?0cq,qW1Dڽ#}z֩AXR\imcB IdN=#bҗ+tB,?Vf ##5x!a2F{S'gvd ɑ1zHOuf O,ѣlJQ)X4#"@.*2y'n؂PDFI]XPO_# <}i|BQ\r9Jt"QHv037Rc֔1n"3ME\w iБFcJ'5!-*JDC>͋A vx áW)p6ls!Q.|♘>꨿z61r*Οi / ,bO5$Gr*"x5B.sI)2vRg@'yE6 _V˵!GT-50q7g=I!HRl-Ž2sn'n\fw$mj R-jp}jvIo Zsw 0j֗30`HOjn'3B &Gj1bl {|1p{ڥڪm#xTeEqڇޢ"ǡ2ְҷY }BGW?*S?ԏ ,G~bj3ħϢrB#>)ѺWwsR9@R}lDRY@?`FC0U]v8 >0sY|b[jI)p['}NcB7 jաY.;3.&k6-hZ'e-kLLj0drx= Ei]2[sWd|C|֖4r=̋PD 5PHdÑR(~ҐY[~A3Vޑc?pvj PƤkQ e۠@r:$E/B0Eu22WOIҭP TyD#XQ>}i@5Kb~ѺcYa+Xu(&($Lu؇ȬkKm30W'YsFkनRKdt|j⑻J ˀGOJfnϔ?w~(ɒBJsVP?5?##SLLfFWR3k*\Pʮw)+6eAV;b@W\s-&ff/"9R?ALC5)7aQ&~sJhH+W u9r~n2z,ɓЏ%VHHa܆\vUT؍GSs(Ei/?uL27֐:!֪t-Ȩ_R@6&B n9 8Q"p tH #?w{TP"?J̛iDmY-ehlE<͏#ۭ}sU7'qicrcy<;,c++Z͌ĶR"Ex56t,ʪqTVkB7Rj[ OL&EE>M̟RO^xzhdC9Q$@qޯߕI+sk5Nq֐ʪ~uMQWx,V8N³I`H :QrܿkqZ^~8]ėq[*KaRh2YU:Ąu ʩ/֛YȾCW>{\;u[݊in#Acc gҸp 3V09ҏ%<>txna9Ċrsj@s gH @!G&΢;gэXrH,Gp[ց #BW9O1qHX"f_Jˑ{ ~TenO}yʛ9>Q#D~!$7UrA&IRUVy8Q[ՊQ96TbZ"Nx cұmqctHҘB/2TSӭfj=>\PUYl︫6А,o(@9& Z$+qA巟’7u\9F3֔JeUy㊣4Z!ieOdm*]gч1Y?QYqʈdԦSΊK֏ 9qVũy!br=j播ÑSR~h"t^15b}HVM#\iĈIY PrQI;`ʨD3QG0zm*֣o,Ʈ)z7j956'*|F{d%AeuI[K׹#\#bksi*~Քu;0.G#U2I>ˑqzj) :ԱPw~ɂ qB%CE?d<x~53N@VKWpN:S$V>Io,\JV2&H'`k؃*i sJ͚'GVBTots',zE;E 0O0;M $$JiB5f ;{j,c(]NUxA$E=͛]er{>Z,I!i݀:Rz4 yz>[Aɬx;_g?҄6tj֤ڠ}qO[TupD\ݗΏ'?Cd7;_srl̈Ndꩼg@kB#n8CV#+\C)Unz{jIBpGY;7.9#\G3?~8֗e 3c=cZ˵vK]$p w:uHmj:-Ep%N0:lBs^TUu*Ľr O aQ6 iHEF\g857'{yvƄۅɾf2V9zi\DVLp3RBZFZ춍rEVT(KgvOF~luؑ R<6ȃn0sϵi/;0\~|UdC椛v@03*`+cB`rC(?+7Ȥv"‘qTS] B#)uvgS$`QudSB0S"]y(-"M9E1kOj%FF嘞?J,AO#? @UD[ BHAT$sJ oޯҭOxVʚo.2-ΙrM];!fx'8DػThԷ$>\֤g%*z HX˵QHJø'haCsSRiw ft08qQ,J;`o{6qRU`2*aq;%'8E&E6*9O)vz\R;zS\"VϹgn%Gߺw7 pRN?@ {1Zlm 41 NhT v&?#pǭ+ ut<D`'h;S.؟Z*o!cԝ_F5gҤ֙|7Zv *\8p1Wgp̅0*z(yW癛rW)d23zf]6z ŶA923 #訧Ĭ$"#xۦڭ0fu  orko%=Lcl_jXmR#ă관2>fG@٪w=$ne[Q:s +ǀc*jpH#IϘqMk9XqsCWvQ-ƸX<QAœUtQ)jS$)?Ê]gTAbbOV?1a h zdN1R-vP)$2lM<[.~L.=A-ژMeԆcVd&*kAv[˜2<6laMm;݇J1/.p {o٭UZI7#ƙ?TE]?:dFyϭVߝ(VXEj&4mix(%dQIipѩ Pӳz0Ff,2Fjmp !Ya 䫟5!Z uj(r?롫+p*)Yo??*c~}cvdGdEI:*~TR22ѷ9݌RbbOoJE9$r'Kl!8y\}3U'ƚWi61M{vEfݐ=E XN(]E\QF}_J,=e1u%8HCzT[l$g-4-9-!8<wŏ(QҌ?L,_?id>}h(n30*uy#<}Px2 vG.G&'9ǥ*0UkypfdSo0@z~\ҍڐn E-OC't3+ƘP4hVnM6Y3!Yބčy"t4`aGzR4H$zsL~T"֫ltg/J$,}*_6s?4yƢ-J7y Plciwy.0GnO7qFjӊj&  zU@$Ҫ$y1)Ґyk۩Z܊˽FA5Wrj6,#0(F4ULI4N#}Zdafw#pgYx$vREyaH:gĞE$ zDž?Z ;TfΐBBWŸ-ښc+}{Pv}ٌFa+*5,p2A Pb*'N]Nz@ǝd5b.Tw5B h09U:Q!<#ŒS¿u !O;bLW0TP1.qqM`D =ltN``py#ԟAGZ0 ~n :9jrjRrF~NnBG42¬2ČP$ޒ)8<98(CH`9L[uS9隗9hqKR`$hD-lv7sJ9\udMlbA,J9 @21h˃ۚ@9vgX=8J 04S\hF( hAVQ=)pxҔ+LeNpSVāZ0`]ԟΩ)I<ɽw=*hF= +Ne-hW;6$fGN{~Tdu뜌pAǘQ⓱_1RYɦ7z*XIݞGzwnAI oRWugjգGcP'iҾkrp81 z ^~/j͸EJC,yќ7>jԆVd/LYFI[ GHd'W'PAײvWh 8bq9iع,qm5 і< J)RH,j7قҘsi0Kn9gIcH<z }iq_Ρ !#4C7><aODrkMO9@JqUe#8;ǭ8$SB.I&58'OƦ TFbj+.H\sӗUčM8HEBCs)'j6sٱH1?ҔF0~T#S̭@ ("Ń鏭wR$( =ABV%R9ODTuNNysϥ~~T`zc$4 '?vfZl I QNI:qT$ufXqRLȠ ڃҢy@9=GJybT=A\L 砢";_Z'?@ U\w\WB)ʁ@8E4b>aNQQNp) 㣭;nF76>8 ,e 'V4rgEVѐ +\R#d1L K8w0-U9ݘҮBOSǽ!IEMjB{M8# d1=vHք"dKo+HI+}h;eH?!4lW>S)\m 1ҝI?yޘ1NTS'rvjirH'чO#4h6x8H#V TdՎ(<;'q:SV#f)iьnJzyH?LSѨ@jZGn\iv'D1c|§yl:P!,zmNSqHb p3J*fP"4ni!xJV(³1T;r{lFB "3H̽0)BzS QnEsNE!qBt@ئ`jg lF)#NM~OCBANzy61SP7pZ()1E@XE}hcڊ*D*R֊(a9Q>t#P*@|P>4QL^GsyZ(bYsUSgU"(ӃmoZ1Tr?1Q@Fh`fotoxx-18.01.1/images/colorchart.png0000644000175000017500000000512313222767271015776 0ustar micomicoPNG  IHDR/I e;sBITOtEXtSoftwaregnome-screenshot> IDATxAj@EQmr"h3] xE!c 55PRc%5PRc%5P:O~w~bލ@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I')SL2e{mƻ1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1(1 L2eʔ)S:)_*Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj JǓa})SL2eS6@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@xp>4 L2eʔ)S:9_*Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj JǓa})SL2eS6@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@ڮ7mL?}_:/2eʔ)SLw<)_*Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj Jj JǓa})SL2eS6@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@I@71/Jj Jj J])oIENDB`fotoxx-18.01.1/images/denoise.jpg0000644000175000017500000022471713222767271015274 0ustar micomicoJFIFHHExifMM*V^(if%HH023170100Fotoxx:trim_rotate| Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx, ;http://ns.adobe.com/xap/1.0/ 600 1000 0 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((:" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?-JM-%وr1;W GTu)++6;Z>/ܿ/"Z>/ܿ/"/2211yy\7/7/ ̢̣w|_A_E|_A_Ep^ee ~b( ~b+-s}Eonf䎘fZU<j屢_l ue<sQ C_7/7/{qjV2_c?*HXf'`SԼ:aMR^itZZCP`z?}-n_G-n_XԧYlIJS#> Ҡ׵;{Ee5[g(ڡH5t?|_A_E|_A_Efj~dEJ]M.Ft[V4"misq}A.4حf]Bv[YK!z,=1F7?hrߘ?hrߘ6;K6SO6Jpnk;Y0ͺe4@#_/7/7/ uo[ m%Z]gDVSg =8T촸DZ}mn#c8;Q|_A_E|_A_Eaj-.{>K&D|\qRk!dKff *YJ(g7/7/ ̢̣w|_A_E6O,fMyzp)gw=]Viiaos9(M>*xrk0DJSK :(Z}fs'׸>V~wfb,ꛛ-zP#⿊'?%?7/[{" K΄ EuZQ-nI68S~&sPQ*T:cA}wˇ? ],%QQRĘoUNL]ł<'y?u-z=7Shm4F Qj8՗.e^hm5oP :QTyaEPEPAJ( ( ( ( ( ( ( ( ( ( ( ( ( ( (>CI\?^?ៈkvgO,6D`~›o,?#QaG])r?›o,?#E<<M֣ߋaj,QW oſܰ[Q`822S~-7XGZ:VOoSj~l=9Ҷ#h]Er0*=\և)r?›o,?#OP3%m)o+aGܣ\i$|*͇Ϋ/eܷ1d|I1mer*)r?›o,?#J?Aܢخߋaj?M֢۾e[2kJy-vg115%׌u/Q-{+qt#w8M֣ߋaj͟Ś|t:55O_WiP6/~?P񍟑n4m*[)3)R{g oſܰ[Q_p_gE*?ڶZyl3,PwluJ-,5{]*[{{ǷxVۆXO=ߋaj?M֡+xV6@<݄B0i5 ?b1TE-slǷ9j›o,?#G)r?X.peew[Q oſܰ݃+taZjv,^j PAW oſܰ[Q`8B-P$d\.?m!{y3"ʮ;YX7#+7XGZS~-j}]ڀ.2}HOLk9l›o,?#G)r?3\e4VTCtcF &w+U Oj›o,?#MVN@$* a塂s&;LT=۫>xʤBaE=BO ( ( (A ( ( ( ( ( ( ( ( ( ( ( ( ( (2oOU{H˹<+=M^?^Qr{]SS|;v[2 [ig*UJcV8.K LxRr2:Q^cVԛN7w+zYh `8l3k!uhrGlxCd( Ñ*IsxZYc3 цgK&&y3GGgG\ q#w/ R-%G$FG 9S}.>:+lϤxf#u=i%Vc NK$>;Yӵ_j^<23)Ul0Szm<>|Ocf&[hmO'"d.ȋH~aMϨ<ֱ֖I9UfY'v=xCCf\.OJ)*NFN65;\ĚmpN";xd(CgqZTbxM@%Ϊn z:⼫f+i77Qk@΍.cr3M8kzjSUpUpFи'88h~{6YLms0Mw1Gj_Z.sE买d5V%d9ix7Ğ#.+y$(]L[e20%za\+|qv]˫-ҷČ.#lnsUGfYMRKh/bHl0X7J#@4zhz{Hʮꥎ)M-ĩK݂3xnMB+-b݀򄍘o2!Jz-nY~i򦯧4w(b}F:Plz[%h⌐݂2}I-\mZQs?ע7@.A9`yjm?/T:݋ح*3m!uǶyIʆ{{py"bjozXx2Wk}tfKdQ0NI8t,g^ͳ.=݌v2sׯ=? ky` "Z俵_2ofMT2n=z] e]ONּ8[,q`C$27/瑃a&uP\Cp`XuǥI^Uj:޷M.4 E Wð$9=Dv,4?Ra]_Ek$~XFr ~W$qKY/pmfV(4쌃g;XMKk#SHꮱX-m J<$[2@x'9,=J<jsͪ%ZN(;]av ;W@Q@(EPEPEP(4PEPEPEPEPEPEPEPEPEPEPEPEPEPEPM}I*˷[J(v]"G4,SZ' 8:[@Q gcOGƖtͣml+dVFFgH`GkD 9,S- MG2ts O]ZQXwo p;ت^%jGcuᅢ&T bfbsxһj( <'Goo vݭmvQs?\P(tS((((((((((((((((((ɾ?U}I*Hw]ԭ/S׵Z;(b\I={Ʋ|7_zZ6~Kۃ7 q].[;f#'gBEQӴIӝ*1]z)=@$NTQ=f <8GS̉8'#~Kcgs}ui-ҽ7M;'nVĔ>y,#RHYQ ٗEfqZko#}j~mo|I :r:sV.=oQV[4PA'p]I*9>ip 7ƺrICqͦHE`>_]⹋^RR r#>9s}B(4vJ H01ɕ/һZUd7@tԽ]˴!#MmfMZS}ZHdB\|ʘ$q9|u,Go]_w߉!x&Mh6~SӌZ&t%3CAQh+avc,7k]a9"S d@hNҸi1?#^b%WqNP:y)>IHߞ2W8ڼOL2ZiRiM 5c3ouѻ;:Rx $k˽ZKվT[=0_c41ʟu0ϡ¸(W!SGTKR&""HhFDj>) A[$k( rzs&it}J+{>Ø^I$nP\@O4XW;+̭]~![ԬT,L03( ѳz Np5]x AIHT,q#pNWEak_}h̲W{"dV lOVr#k UY E wFMļW_5iukӼo.ri:H2Ijm~UQ&FIAKuYƏKuYƟX<,w v~4P,b1uBKp2n;WNr?ؗGqGo+ "f_I@f*>74<9SaTu ( (7AEQ@R Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@7'X?U,=Ģ[o$nh?/R3Njp|lً~:del* 3 sWE+-s^]Y;F6?6AuQEQ?[GP|D<l2\E3cr\yqf7qkm;M?n.kn`@]߽jWkkӸWu\qsdڞDz~Vÿ&㞇==(JISbǔ0:R((|tP: )QE&hM%PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPM}I*V/OUz@gv$ (żSrP) V_X^5#|\\<BX9:SEѯn?g5&U$F|Nj-OQ\NI.T,Csp(tGC\uE5*+[ G WpEt@ |<@~rI4QI#pT 7Wѵt4M[F[ u gg'W *RJt/iQ6sg]G4I fw`|6Ӝgvd-C_Н&1Iq I/85*xf-;Q' V6g|*0NqO>]8iXK)bH$(,qԼ,da!l[\ ;O2tKqL[i I;d#i?)Guۏxr+v͒6S ;\@<sTFq5F[Icvm Sl{UH~ ӵ +H{=MmCGq+D򳇍-Wk|28Il7EP\ǎ{"Z A-ȑQ8 9Q=맮{5β"ۭisr88u-=QX4KNfZeϴDT;nW\5~ss(̖,c#OxOSQæ{hcdIݥhJ&T2wXzTZ5{a[_65(c70*9~Գiy.uiF+T[)ɧu +9 |_kisW2o5 IȌ_:A+KrSQCsa#[ K)yfqWtd_;' ?w#]fYy ݳ[8 0F7R [;I"8-/&sE]T`cuߐ/2m:ŷZc!Di#$Vȫrq֡?[GU>%[-.4f{Km*6Th|8׮Cƺ u=hs7 'iǩ+6Z4vNmYX1"`74tSOE:Q@Q@㠢QL ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (2oOUz}R~ʫ 2)U :[֖*(gvpgwΏ]5Wz/v@H"4hSǗΆ]^R}=-.[8g(Q_ k+ţz+1׿JѶFr47mڨ(222yͪ+Z-J7L[w d2F@Ȫڮ,rOh:XGz/L>_⁝5k>9,]ⵑUteu qH|QO6F.Ѭc?3%`g"6h([\mqao'n97g=;13T- d So|IO4rWo<s }E>VS㏈AS4|B#DQ^Ni9|xtbu zJ:OtsG+&_y8}V+YI>`@7h#EXל>*YisI.xg$TQxƓ\ƾX@Kp9y错Uq骼7^ cgݐ:1t4?iuo|xSi̅X vVz]B_->e5bf %=/u[<_#^\ geK^ixH>Ko[UC_o'˷97k]ՏVg-B|7o|GQ^O Ǐ؀`s5QF|u \9W7> x?K ǏIɜXEyxg-Io|A]?88&+(DTRZ!A+OGߏ*r{Q>ƾwr6arV@麉GpTޡޓ=}I*V/OUz6˥[^.)2)<'?7^Q,Mvsb}5$i*U#4-b1=Fb1=ۃOvv˜1k<Լ#XBg"W$Z]55n/$]M xwQyh3%ބ<etP+]}=kU[Y-ܯ\ZH1x\܁jivZߍ4=BCM:O,asD>C whh \_(Q^ea\]YQ+*fR^6snHE2\##?l?筗.Rvw54Jd1ʐ RdqV횧Sz߇wrRU&F>٪[/]G=Ʃ4Dfԩ"?ߠcTu$`*HR((|tP: ){C|2Ve9c95,sew-D|_tǩ>ˀ&6՛g۵ON [>,YUsq2Hmi+A {+4]s? h.l̦ɠkp%kvqЂ+oվF/.b`kr,xp8nВ=:jtͶ&K;pjIu k4󶞄 H$}qk`IoK'BX! v]ɵ/i7ڔzX][L](ʱ9ÎzdEᯉtǾtJcբx\^G۫KafP?ȭn!\W& ~x y=ߊ1m&Sa+ٛ>O@wSO\͟%g6?o^ M í]jKw# Ĭs f^5|׉4:VVP,(cl6#t~/|R5t$ = 3]^G]R i ,ꄀ$+ݳ]oZN} %yL`kƯᘿfK+V; {f9]^`^aCjth1 3@;ZiRFӞ5g<7C:jU"x/y6?4? 5uv6SB O.x :M7ڤT/nPma2>;Xw6gC3L:BdCe^.eD$XFb[l,4H渵`&H"݂|Ǟ0kitb]ZWR *pFSE4{<XsA!]g5şEwkkk]fO1gĐn.rύ4[1k^(ZFR\zr23\G/}?&5Imỗ@+A<F8 J$Ayu8$$THP<#_i}[~&"}0)_S}Eo B?{־q[y{umуq؍^+Pv E6wBTr7s ‘5G"T~@7vt༔LP|2Lt|&xWF?PU09MU^_ԟsSFҮu qTfw'szF?/W(4ئ);VR{m[dj/˿G]##t.er^30p[sC:Ɵu  BKh~ A* gxX} !Ӯ fD[1 RGj:\w5.s '[G`X>rNA#q6km0R֕X7ܤ\mJ;[QYcXe<SQV|#I01JrO-@5UM[Pt˛)mnI ҍEp⇇MW45_W25_ G_/G+mO\7-sU|M|=W4}^5_ G_/G+mO\7-sU|M|=W4}^VU7S*O+j/Ñ+.2yryT ~99CV.3r+=ba rs7ݎxoZ&il] d<fS؎Ƴt[̭XxIqPQz.uүu&U@:weQ׎rkW}~^h6VkN"1$PNJ'LfMHFi,vw31UQRGjŇq&ZEN4YF\3nہ9=O!_Q\e-:[m[yZ+,EU,~2u-t}Feř1.GƊ%HˤYB,M1Tb@#0pzWIE(tS=+~D-8%@2{uI= |a[+3<$X]L2K(!,9и=~Ҵ B5XFwA'zWOwE%4;O> ~BhDwԸ+*cyC!p-1Cx!/4k# MEM~̂T=:%ޖ46M9 lЏ-q:“Dд}6C=׿y䗞.olΛ7e usҹ} V"RZ*KVG)Ig`8'#N-j5Jo[te#WM׵B׊sEӴt.lqS=riKD`2jnLUy~]u].9Χ #l AcIYY=jof]R]F] L@U{u%9 0GnE, pG`=+StC,vsF]Oj]F[2[;1H`|.UlrOJØI7OgtPVZ$q.?1\=_rI =Gu2}A Z _JK,A2/ \Iq@- tB1p+1<@qJ5ͫ4*Y nzCXE(6}=AA$0m+/*B;Җ m+KhᙙDU\Xcx:xԩt̠3#3< c{}W4n0USXu#ДUx<1jtV:aqi OJ4=&-$i.Wa(u+G=ϟ29uC._~lSiDJVYD,89~G}T2%Rgh/F(բP Ώ_dmmʝ}y^;N coo|L鰬s&#) $N ݗE3H9°X / ~{" %hxUhu^?z:V-u]?96q־cZkcV3_DT,<: ךfnƉu+3!݁a/Lm{[cs: gZg3UXdv̧aͿ]ޝ3^\-v !>R\4kT񶤓KEg[T/~[_@dD-,#qa7S ϣX" >Fw$h܍C@ eY:Mfs3Hhf$G${+3Nڠcҹg?ty0ey Ͻ|ėDQp>bv9N[?0VrpGW{ST[.Ǻ|u΋ueo1ȹ(>\[mJOKt4%v) 28TS22L+c9vL be}/e\}rߗ{>@5m 1t]n4[Ҫ#1l;dC큎)4Vr2HcSܤDD#{pIsWc#eQ_7liV_1EC"H(M="FH0ϒ('ɯOV/ִk * lCpkj>?WH8 Kڮ-Ư[q$Vd%bá ЌN-徠.Rx[yH#(U@F3RzW{E V^x&Y5=OS!Sc6żF27[1 M0smNgmLl2n\QIE,3tV+yc{IiMl 2N:Qi[R$1بٟaC15Q@}RӥZ=b~dA$ZvfGFVis' ^9SC^ CP[6L6wˇ`jh`o-`$U)$n2#_]?]?yw'=_]?]?99c_xG3 ?_xG3 (]Ö=_]?]?99c_xG3 ?_xG3 (]Ö=_]?UzJ&8J[pG P01[Tɡt)4jzݍ$*_k$R _f?KZ5Aؖl{dko *Hg3 }iwg,~>` 6rzHy[bh$6X^9` x9 l׌<7646~ 7&#ߍP.4VMJ 46#!6:{iEpWtXu;u[,riqʂČ(<j-GN ءIalT6w21i:wTB8JEե5 ^A_3 a3{u]P(tSMbXY8$SIiG]FpAc)Y͹cDk3f5xcMwεxM[++#:WC^C4M̩#y4! IJ5ܲ"4X;7?w%F~KF[6N+ _ZP@B;ջ\Y> +4fGAۀ Lppk|C5p$x6ڠcNj/R 14_u98ZE x[':v9kM=xП=`f1 EdHz t&N}NL}(<v3܊n#] okr[@eܗ,J3S!E5݅x#%p2$ c9]KTU]YBDoU ~P@SKL0f-͹S^BxJFT|htukF+I <̜pvc:~UZ|H[b1Zk4PۜʻXo՛䀲}a#$rkȴ _Mk٭-Nq44mA݉Q^e]NāY8/ج($1Dϥw iMuZJaTT e:."#eu3٥]\6@|vX}CD{XFd{6ėb$?w8\Smt7F8 tVb:>~R9+wiX)'C[El=c#zuZ qAN/I(%Bܷ}09?a57/^f@\Bp7FH/l߳4l߳5_RWRjIA]K {G~Ѳ{G~IA]K >'u/ƨˏ?Fˏ?U'u/ƨԿ .?.?T!kRhdt7)FAT`RJ\ʂ#lV(}?ƍ?ƫO _Q)?+5@6\h6\h)?+5Gؤ?Xq=ghq=gjK}A-k&47)RUQӵ4nP][$6T6Iuoct {.?.?WԿRWRj,l߳4l߳5_RWRjIA]K {G~Ѳ{G~$|zUEG3qMj3[0.Bnbrހ-߳4l߳5_RWRjIA]K {G~Ѳ{G~IA]K >'u/ƨˏ?Fˏ?L.,RJl# ~@U;Q-I1 Gh #&~dc={?ƍ?ƫO _Q)?+5@6\h6\h)?+5Gؤ?Xq=gi񬠟1ՇOIA]K ݖXs#E!MX`qh`#t'ݮLkku`fC%W^#Y[E(_*8љN3618ŻRv4|'o_jRCobi9?x~Gg?-qgTƳص59 H"k4<| Y-6FT>cw-<('{;[s@Gi8vj(7<}W̎2Ly$?w:` @exPF&!}NqQ<5~"_3TI&\[.^%؟(köN %$̬G=UvhVBrJm @ ywië-ߘD9ֵu1-0aafh'a|kdd[CsI(0 #o>戒"AR #wZ4~e%q%aF˟ݐ18֞ǧ\IPnGUYň^>M$WzP i~.T9'yWcH51ʼ[ 8z){:dG/"i:SKL2.eJܼxq w+qr*4:13v'L^.PPA5R!$|̤=9IEy֒[\\|bRl@##|UexcB<ޥ8WV#j  ;}s\F5%W,oWf$=F:W5A)NpB7JťS\Nm|F@'梖L4dn&YBg`qלWEh:ղ-QBѤxQ X؂N7HZǂ+?k)bK/wi|#'91XuQ=#P2 @9lסRBǨǭhwm1\۾XJ1$0b{M3U.cn/m'٥;a(iDā?>m4fM>ɢv!*@ 9c)4;KȠf)srTrѿ2q v$$5Y}ipDdmr01_xgLswE'" @1 g47Uzŀ#6*qws_Tr؁L?}=zr1F [x_˪m /f҅Rc5Plw ϣ/t+}Rx4}J[ 7cq+Q; N!I*wcFIǭeh>HEĚܳ=-093q FH'fT/Nv_ V;K;FK$lfH#0>l9a-{Fe4Z;;Y >aykpgbbv7 i3Dj?Rӭ[~<7wIr:/T4y|5 ɦCOw-*dH|T vLG$|iIdH\e*I,VxrfB]-)ç%qQڛtݔYmZV$N3I U7+pxQ^ɪ[E$67sE<7q`lu̪sˠ=3PxZ׵/ 궭HsuռdT4dkz/)+_][$/P\ܗHQ'dGlSºt4^B\peNUU`92kpNJlr@$=j qCam!2)H$ `~R\6vB?ƓVT _XU{ɢ[VG)ݷhqVfMof4'hŶ`~?#"x;MkV{{9.WrX*A'qW.|;\q+^lunIWvLuWdSB~F}uJ^\Cuiƍ$@+Seft+]#۩\I%pkZhK;ԇL)+; Wvnm[۳ Eǡ dd;Er!Nf-)MщGr_?t[oAw^YD5Ite2E2tMg9F[d%;؀sm5'FSO˦J 6Mn/,txSKW!FHc2A*1i#-/Kf g-2\|iO08@41 Nɨ,E-]Ba$'9pK< v{H6wֲ^]\mm6M. 9#W+ BgHZC 022{Uqm43JgY~n?*60k0ی9'4\5[Kƞ}B{IPV@1^]6? 7sM,EgP.!IEAE1+_:ݤmm*i 3/?+zMOҾ=]qyN#o3*ea̚#^4۫ |(11zV,oK y^{9zp喦:_tk2QۂifR>A_Dkglo,TQ;pc}ku^۬2OKf (ӵ%#& i"Cyōs%x7 $w{pP7w)#d3]44ٛ,ʐ,ѕ$dێ2s\M5!Igsj)zӥάwNH'uK2S8B[VWܲ rauJO?uaՙ4A&q[21R9/x4\69YϒMyWkL5XeYQ۝?\I6qL`pr ʅaU==|1gk|ɦYd0m>d֬|:4vyדZ4aC(%Y]G$Uόtp"K+K(f2 pr-8qq>]gL! Au ʹsJQ}j>cKmk, ~]ц$&m=Z3=reoC }hV;vB#8dg$ڗ&|VڠeUdҞZ ["(Tc!*~c$!&z_{L[ ڃ1RT83IW#<lo5-Zl*r1pO=j=kIfӢ6 BT]2U-wTmvgr~҇W^5duA;|1=u_&FX~ξi7r!#2Xzfz%<K{m ]U`M8 vcUi< r]$DIa~Z\2nŀfq׵]xyGfH<d 5'6RE奾B-cn1W,glb53Kpp܌ )P5;K2qxR_N-3?8c6+lIpXTLa4a]֕xCe&VY#l{]XB⺛ Z48ch`Bry#S^t4}[LXm4v2K&Ŝloa=n俵a.\˕Qw=V-EWpT(k˂2G^37J07 X! 1lgk27V˜svI88XzX9 a'?Xi%Øʑ.ezG⦦){Fsx i<|S ׌p@'S\ӧG'!Qx !SЁϥjy"c7/7 %W<`{W3sI$Es( Un0$DHq^SWoqytt2^%.*m!XsZ΍ GxІIȰ $$ `6یW7Oww5."g|(nƬ9c>ZXu ?fH<||s :QM&zF_9d`=?<\Kj%O݉M'gyrIJw5I,a,,PI6U1 T< IeMl`cc1kͥ%Hc\4SvA}7E A+n]朶+fg]͎c9*nP;ɞRZA =/n&e[r _vIdU/׺kuhdXzOVփ%u$ YEm+7XTx5/yvP$`I#_LSo h<%#N`gھU[xy(\K! ˜ kOoh"trca~x?J*TlGgp͓zʫJyl72O?O xnnf*Ck{v[/H m>}9hYUWC ZFϛ'iKI?e>r̺5ŀQEϧ>#^ $i Cz|ϵis ~l~Td5Sd' MaIb7!pNӻ]e0$lMxȊ.4?h$g:o5("IrG*P rϱOOm#劽H@o HދQ$̓zGF|-nH)2Ǹ7i5/iZn7kFR1\>1pA|I=\TlR,8P\,t~d4l5xo !1%dld+q?&9cQ{p$+c#J.= ͓z̓zPɬh7V&a3IpO^a"ѮR?5Xϥ+̓z2O?%F8=g>2O?Hy{tȸLR$bs5Wqz-Xf$ZY$+(p9'WŽyňVY̹V@>'TV%X7nυ~@P{{][LIPK#J1]cJRl:FOdX-!Hd+FU2c,9?jtR&PU%d@P~ Hf-g|ήA>_+X7s!L+_a$)XGke'$ ?ڂp,A?MRbCIXÒv+f`eB9x<{k;[9]R~uv̦#J Fތ3Ϳ=P3wzҨw_j.;K'i2 Q zWqm P ɭhsJ39-hT 3&ݑ1Z;'0*7-g8 Zɛ`BՋ"HG\ncXTO95śUg}M}#]۰J9>ojq!{y  B I6eeMM*X8-A7^6b^gbRfzJGtc@gr>S=flelrSx KdQq$߿=FzRO"[{87;@GCY*OS44lnn@( d7]c$1ɏ} =~T}QD18be3. "%/@I qx/ =G[YuC2-B)8زg#̑0pU=GE9zt<ۉdy (<'~TGԟ`o?*>~T>~T}?7ޏ7ޤQQz~T}oH}Ӹgֵeiv 1u`WǽjGbJ> _ږ:%{:;^7@.sAzH5]7.e#ZRLO{u5iOF6qV8!yo/=2xl̐ʄY` X 0!Oc_R_M z1Vܧp#g\u[} VU79?{FQQi{3@ ͬQ&hFT^ V?8 !Jvܨw՘־ ]B9&gRYc7Σ XfCpq[;M5uJq;TWhh.b۞Zt}['զXof<,-m܁#N;xv}f:A.뇖I@XdlWJߡmq@aAFNJlpO=zTm9d_>٭`d (.rsWi7sAq%ܶ巒[Hp#,c.Qj5ӱ4lhHE̹l ;# ;.M>Ym-hIFF8"B5[CrV۳+`-W# 䘛# fi)1oNIjBȭY r'bn:f]5HchH;? sEAwjmx.$ I$WKTI6 *X N99V8]ʌ8=F j3=H亟4$.byv#RDa*=W0j'UbMLMm\գtn{+.~95PSZ6`tH%/' g5^x6tS>0B$t+aaoH%fG<2u+ΣO]Ns[C=WKxm̈gLy%o7 y$*D]]x +<⌧| NFqGM/l-#}=@fS33ܕ{ Z/-ic{ar13Ԍ1Q#̗t,[ K^\O4 F%0ÕrτDLK=4yKuBc#H$HuҢI^;bnb -އ$^0xզccɲW-A#WFd8\P>~Ԣgϛȉ.ʇtǥ`EPDH,pTKq#d~|Uq9 +=& t^rAbQA7eF>1 ]Kyc c`M[es4<$;A"Qj[鷬"}woA2JmUek'jwQo1?, ٞ b9AFy:fI@3]K$f\%p2o#kYݼf{vGrPc8.3Ԫ/1I)H-׭" TNNw{$AG6"sYc7{rq_fc^Ԗ=i#̝#r\= mg[./X:[A$0(|Xߐ:=Y[ӭ ۋ2{ˋ_* 4 W8 N:.c(($/IqK;\CA,\6"P0FpG9Loe`^8eHU$88_/!y.zxRMBךG =&T̤ T"Zgd[c$Pac2y{J}h<=F3%LA_ZƵ.dg&V2 {$`\Z氶X6+gm_lX` dzQ^]7u *K[^^x%HVUyS%0 yxi*/am5s|޴puTo \J+O촨 cM( ڠ'By+sci*%oT܀2_QqN֊]rh΂ Q<1öyY *NFgYg5io;-fgyB@8s0rhzč}i,߶ob8UVR@n]@ EPO~j}Ub|-$3K{}扊9&VG_H5Ëv5.|#]H,x@Eqc%ʓ:ɣϵ s;ۙ.#f O c>qX'[۶h$_$q(gmeec5HG$jL{?v@ZC-Ԓ%klBɅ1zcgN36Q'Sszխ<2BL73@1^9PbK5 򭘍kx哂 ϭnklmj,$'}"{s0kٛDEm 0M#I#C2f?-2Qł1s@i73j$Vc*%)B:\tjdw7VD̖I!&!woFGpıL-$lw pϑR0iy%Hڑ}௕]_SVyR 7f09GI4upY Nyz{#@HИ0/T&p=GNRvlǤxH}7(|;Ki[+ g+>oc-Ŵ̐=Dd>r\5xQCOo(ulGi pۊnvrBcdES=5ժÙgLG-,\NPm>W@ڜ |58mQѭ.6TǿrGytGyq3Bb4U;gkF/n1$xᭊ՛- W 2CKKh0%:DH\zf4;~ Ijh fhK #FaO <>T,M02SWj<[Mo|˂Nw9뚕Wb22Cs󑟿CN|cAuRɖlIkXdp%s!?P.7y>c/e 3=k:j& q9( ^V2A<]]f-y|NT rBBXȦgpIp c+OQmL ލ[Ʊ %ǘ_;asjv$vJ2*@H2\0zqu$7%ќ-wglbT#qI--ꃖvR\jIsrF-+ $wT)%* 9 #+3X𾙪kn{ڒ1'q_Q /-q֦msSN6$jbL6,c99$}pa n\CuqEi:\v'p=뼺,ZOwNky 堑 Q&A:-Y. njlÑ=Ah;Ԩl:'UEP0%z`6 ]e\HѠ8$tfQe%GSZadph|m}E(.Ⴛrjy˝3|9lq:fc=ʐ$;ltvߔw`F=j#yi6$rM1xbXgvzcrxP9mpU'3]CJ6 Jڤ9~ccwϯ4aX7V@ R&ʳN6FmY8j5liG9'L"5DexR2rRL$OJ-rC!mpT<`I$"`80 T2|wufDlf|eF+FiG?֡-毈_ַk /k[ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (1<' M?cEmV/ ׬Fkȴq-xw(nXrONNkXeH'J0[]]dRG"J-<sb!czl K>6k&˫i]2$ҟ{ƥ N^E1GЖOjIK3cඞHn{ nn8BA,Q]%Uhc!a>a@=?1mI ȫǽʮ6:xS Z{LDHdc!@g>3y7;HS*`ڪJ1%pōdž~:oGn128f'>c8<jiƅFn\{hW/4#[ 碞= Ԭu;=I.|x3)!K'9j*sc g'wqPI5byBfd1ܐ$<jcV6jy|Nb/3${3rX G +F#u++igHvޠ=8X`eH6-AOAZl R,̫$?pާW/jSh"`6 |9=ǭrڜk:ow-id6rF=q]mnn+A>̼N=k e*1 Y׻;!kֵ̯ 1nf%$ɼ>suik.HQaYs8v5GG63UI6RW D ncLֶI]3[ pX8sRJrM-OEZ~F&?7jKu\w5zXs v^\Z4^l}a7J?h.,<:跢8^ mSu2B\wl /+B-HdZA8`U9$2z"=ErOCȬ[a&Hhg!l Re /~0E;7idiO5QVs #\Tj2}G?I8 FmF9`Xcby:uk_&I܋H 3М*8 @8`5i<~? kvIŔǰt?l5Ita9>ʠZ*&wnRr1G"<>\}}Z*!q "Ta$g#0*E3\0}2(j*Awݞfwlޜmƻ@-ПKEDn"VrÆ8>#- c8PTKq "ąHґna`eRCTc${r9 MYхT 0N4q "ݖ韭KEDn""Æ >#zD$BKpr>=Z*%v:H.*&'a{T-s -,`*lNpOVKR둊d12f7@D%Xg#0*n!merR> 3co= \»K6X ПcJn"" ΀%DYǘݳ돯 q "ĨH>4- J 9Q2(7P,f(O0 $,DRݖg~`d@Upq@Q H(I\ǵ q " >?i+j|' M?cEmP_;Z >DE.\[2xp}+&g.P3+|o+>K" JeC~&%YK-%Ktjr7!]!<¶I;/yc;ٕ1篠&fWG6㝸]r"QZܑ&2'lJF6UiYwX2AP nJBQsFp~lsN?sqxM-uN!2bm'p9?-R0!˞e2K"<I>+ҤN &)4&ţeEKq)$99smfds9Mkvu$0Қ|W,A5=Ϛ=qn}( \YIh#S^xƯL+wEP4"3(ă=~jʓClBb"|-SfIW@#A`8tmusͥӼedIb}SrT>g3KC46rn>z,Q1f}VSKV<Ѐ'Sv|3~^p1ǯ-aSv+JѼ)!HV޿wIEޅu&(%DeVLONLi]2uR;ѩB$ĹPF yVD)0uSdW;7muGmI8@0dzֶiRnn2[/ݽ+WaXqOC$wRVgڹ'9IBM;\`8_S=7'0dZď UdyX+_GI`[8UW)2*-K>*'f98ʱ Gjƃ\-Кp_F\]VyY>a;su?ӌt*(6hs~Y%.dn̏[hZu|}fKhc!RrVҚѭM8|cg_C׭KuMt$Qcb:,kJ͗Dy Os܇qfN:qB *H TEnzR]hOrLM)FnNiQ@XWwm߱K $ QwfEEVFt⴨ ,XdX0=lj]Y9D{g lehv[sMiQ@&^"?V!k}KŸnZt`i rN}IC^Q[[};RN}N4$hL>OtTdڏÏZݝވrf3lb˃X{[|ͿQn纻;xj"7R<_dwo 9⌰Cs&w'1˨[kȰO|Rvg.^}L)J2.~^AmCU|=u[iˋ˸5ݶ0xߜip)n.bu{G؞fx2;tq;.\槶/{yıi1175t;=Ef0[_C3J#9.:jݗvzjޮK$qȱm / 9`g>:ԩ'?iSS{{{aV,."ʰzǭ}7PN,-ū$#*R=?ԵF-k8\*f6~W] R<_Dֈr U焴q`f $G\uue*ɒ&y%+T#j7Or+E>%u!h6ŝvKa/,ra@#$k'E}b{xO#ͬW Aso5KX51SKx<2K#+#K8nVFI.a_,EssVK:S[/N%3<#@v* 6:k ⼿W&F.5M #[oM?Nu؟i;y8wχ֩©]FYh },o3Z MVJWݰI*3ČFzZ/+b]WC{[!{eUFVURv'<|"61xuO^'pƅ%=CbpX/rsڳwwEAmk9ԓdG6@8dݞ4~mb&9Zt?ir z l|w=OZ#7g=kK/WM4uj2+\# F瑁䞃/|sh|#Vw\dEd8{c8H1rO n妴X-/ GJu|_t ߰C:V@@}s*A8-Kzƍ4&n{ds»>qiņsHPB}5^OhN.18Ik>)Yn0H?}rlX:5$_yax<~? ktsY+/o&AKWV˒B2*9FH 9E2`1 v5%su[Bݦ]\#bBnGֵḆݚ%7E-/EY ytfPdޑ czxfZ*ޕq AbړY M(-#x8Q%a-$iss\:ƛ /%pgڰ>xX3""@nmhî/wʮ(9J;:5?TI}9eQ*țNGM2k)mtz2 1Zs&u-ñ#Y}㑁.~$TyeyA}[Cƌlg]ԓObVLڹNRuֽPҮFq#}7W[+BpOjĹ76dP #uKjpޚ&v2FyQ5?_Ң4Ic<K.e:H\jd #0sՅTZ3SZcM燮5m uy%rd֭f`c.֎gH5mbXi-ѧjrK2䍼zRZJ EI=2qzYKy?+[]TwnXa sIx\4'd#&Nw'89Pr$W3zoz``pqqZcxs@ym]39 y{cۯ^&6QCwf)-subQ8$g4J|P>[ZPk_iI6a [689 ]u /5hd\#B/`?707/Q.WuusmU&.,Lsh&<vkxjܽ@~RS~^7*Һp6<' M?cEmV?QmElQ+yrK!KA.f q'ON+&%<=l"{4 _U m. gm 1S+ឣhina{@X/5l|ge%|7ven_ 3j9$ 4nIxCԵ<;6礗kY opoJׄ+j\4?{'.F*.ojaYB [+MekPi7:iSY@ټب[nUp{V>4L4$agQ×Z;Ww^Qhs7yZ|Bt6Y[[$1%<1U#oOG.,c?|YQ<]\]#h:ج\6dXGڶ_]6#NfIRt0\ 5a4 S'$qMt6ĕ/}XOvmegG\eH}sTM]l];TvMXV ;MЏY*@W+~B=ᧇt't. t`,h>go'jB5Lm<+IxIĈ~4#xl?~}i$yRLDs0H;8Cqٝŏu(t/rB]d`A*axqY'skwYJd-m"${Y$9#n1U@j1WXݑ+(֌pу]'BnZcf @j]Vզio!q b:o{UI~4Pܟ"Any<XqSX] fU|Cigo,l{kKo%oQL\00״;,cˍGuT"88ǥv6ƛX,! 3h\Si4X@&@,Ik?QBeFZ`dԣHlnVp3 "+'rc([|3Ԭ,;r6􂀆,G| m<-K=!u5UZyl HFMW$6}~2Nzhq${6 ~%5K]3IKkv o=HF;%}NO a뺝XHEɴ* "Gt֑D;..Hv4nxƓk(oml c5e1l{)&<P. ]#5&UNQG䚵兝gT͈cNNFOn.̲Irw_[Q)5ÈqRVO{?;75^Cimu!2z(wŴf&UpK6<ԧU2 I/4eͽn|ȋo q6:q'7˜\o Vx,R8 4xfhXr >݋(%+Ǯi2KEa&"#>PD]ߝ9__J ^ ` N"E&=I }#Ÿ 7mQ#}*H#6xi:"v(vE~ڸFwr|בFi⯾_.bG-:}) $3aנ`1[Z4bKM{4s ,@A#sX!ct0 `"" <We,KwS, ,] ɧ I+['*1}z~ Miťk:{k,hV@3,m{Z ~5KoE­wD[Oyޑ*mh9LZ$S$ABF 2;g o [-$-uk* H!8*0xp&^/mE/j޵~KK[ȓdŜ8bUT9n+Bj|+!y'q,w  rA8BI#ڰ<_ !mFԧ.G!QVm+iQev;X\\XJM)^'k>+9 {{ vm.%%x0 W~LMV{h[|̡'<{WO׊n&:Uͤo$6A ŋlLmEN?]x֗w+l9\I{mo@gmՎpؠB6xWn{X+uywՔ)N3 wGw2DW7v/u=rRRɪNKkL88H-u=o-m?1w6Ӏ>^wA&}bL.Y6yezu-2O4ˉ>HL[ ʜqzs%#Oo6{7 a$(Q֣$ g^NrFE86֖]zO~ O ZvKI&wu9ڤ'+H-$[Y ]t2{sV"hzgedrI_~ҵm+MsG;[u1I0Ic$gk)#< Tzi9;FϯEV4jGUZ;~G_~#][;<^5Xr2;}+tĚ֡Xeqt_>9۫EMGUs•ݵ]c%^YOz|9oV7¾Y<ʐ7:z[UKl^4D; \@|kjPKr* 8܈[MO)|x@A_=knp51c=^0<jHryU1m21hqZgy$pAڐ3NxwaW|Aw ?Tr BFA^w|Km6p,<6kɄ\Ug5SY+!.@9YyڼW5W-wNJE;ƨ>a+-$: 2+|F"'|t>L.$.'xH7m8]R6yY_jkgFҒEF$ܲ(ǽ # +rmCd;GﬤVO( p8Er}+vF)k/ ZJGi^GM͢p}?m|m,[f_ַ _ַk3 OF&Z[I& mn-y07G#io>ڔl=FzվqM/t X .NyNЉǧ|Wu;?i> Ÿp72Tz Mwi &+##CޕZǜxOj)WVE@ch>dz 4)=k&-ݲ\ĩ, Z~1ۀ'AujBRRيQS\nn?`[ooo.-0e, `B3Ԓ18m N*[< 7\/,JL31{X9m7$8z#.Tu*LQjWmN{[Yu{8@%SsY>!Ӵ]Ozx҈ѭ]\ @Xl>2$VW#9!co+?TNNO:Tռr\4[ _ JEu3FȹIWKڑEU*؟7~3ރ}\5攤[ rFs95t"i }X6ws d@vq'C]2=( ~2\Է;37Bѭ4kجe2y?sz΍ci0AG g8A]ZArG*ȋ#cXdh}~Q[5?ih[T!zWͷMRmH 7BxL0.3.[$v)tBLSWSC F,rBwokZPԕenH,Y 7!mɵlr+Գ7u^ǝjԚ\P(U5ٜN_E ֓"Sqׁ^#X\Xdp?z8i9ne'&QN!ֈI_jMc* &+?ɴb, REi$Pڕo.BmM)qԸ2^K$~\j-7 )qOIfQœl)KFf1VSu 7`jF&q⛰&C:S5ϑ9Œq}j c&U:Y.gᯪ.{斁'\_x]|P[y53 cp98>u7! 1yO#2Xcn}9tVWZq//1 #qZ0LV|Q@ 5MY+B!|> +nXjc\.펯+ Hz7[i$L *\V,<=81xN;xnwHd# qg/ 1Z3BC쑑\5 3ݫZ[^Ldg' 92r\$F8ٰIj+MI0\Gp i:揪SެC?*HhW:~[MJLm$m ЌV_kiz|z}!P2~^A]U_>bC;P=0d'd ;3oghܩۘ {~5kx$QEqαZ%Fٙ#i9=Ok4z= ]FB(ڹk`59v¹V[ݍRgX؆ݒs? ڏ2uWVs9M%O8g]yc6["pO>OKr #mV@9c3rMG, wÕJ-P1[®J 885O|5u{=`k8>A)k[5-#_/k[TLV [dv=U$&&t4%̤ ؔoʤB.c ̲ƭ A ?bQcL{qMv4zc+Bܣ4Io$e?CMY<ωƈT;`]1@Va״l/%m~_ann1DyArB*G@2j1C݊}W..cYB)' Uuky"h*AS"cHdݮ nzb4hu6(&KȖ(mEO ߕIqXۼ5hĠ3< EQmZ$0%ʬ!"լ]EjҢ2}@G?@6\gg⢏]$tޣ@>`@tVt֝\SPFWk4k/7&~WGZ0\%YPB/QTWWg2\5YUY>#c;@ܣE18C ^&^"?V!^|@yQgyaWγ;j^Ĥj'%fua >M>UծD{#5WG tFPӼ3ojXDѦʷpjiny' NMܹ+Mo:D= y?'3n⇇;jTM\1N|'%lHjw^9*boK89/hS& Z˽Ye!J}33l돼_F\ #3V/ oG}Ux{?R%n1  E}ddO+ >{-jtjxHfE#VyeN%D !OsY~#9ߏCZݬ@%D1*2̄c6-_;;G7}qjY,mQ%)#ʤ(]=i#O-4ڀmV @H ETͲ_e01n,-dVbKI$\&f*Р>X2=Z*>f=,J 4|O94lmeiH"vn m+\EY+5;ȳ12]@ >…WF[x$2 ܆`O4Phmbh+xǻaUnǦO&mF"4||wǵ[*f",F(03>nn=ϭ+;*O c@b,D| nJiS >Wlaw6O4Ph-cxVDXz@Sl`?g۰c*VK I|2'2"@w*TP 8Wvvx"-#JXp{`U(mwn;X;xB{NO|EUOF lnQMh6EGymc1Sb(K=2y5f4AX&^"C_$x{LVAO_[>3񱱎KL2'9jZ7ͯ)|"|]}e6 ptPS)^[UjVLQy*N;iX&]9+Nl5m*Ksӑ{iL B9mE$ZAeNswa4=4iWR27Սj;^m6=F(4)UX}G+g#d0FI=;Vxwev`QL2ة'9-?(qkO}k߳p'y _1#o簮J|9}c pF]9\:[ d'7كFii([ʁ?7.q'GS!AcI@ %59$fC5 {uR$2=Gm|;ծ|'gp S1k [v=F9BֵV0W>"V;=~OgRAY[$jg,+=W'hV@Kkơlɦ?-U=ylpWnYE4,FI <ּZ&Ty#'/kzƼ{duJ(y?AұVOYqMhoz3[ˢ}NPS%YiK.sT2 f(}B8V-]g@q962('"r*In7LtRsTWU!8&C165*zU+6.Z՞۬رU[o=+$LQ%IWKKpD^k dOA] e29T$LU'pOA]V,xI69]ഋʷ{P0OV?|IlH6wX6dWgWQ:_!0iDnj:+^m'd6A8V?i(o iH"I8OG9jgf?-16 TߑX%M6f M$Zkh;2 Pk=-CG A HMe/1ckwOwrF}+eO5E }ǂ+VYybF|s??!/鷭S6㨤feh դoMNG|]}k U`r j@tE?kuj̷ v {g9]MPF`"#.mNJFZs^0'c{J[dvK?Vwo^XhQ^hGe qA=+o_+V~*Nt/(Sr@yX +*5g1$rXLM b[GA o+:-B73$+LqP |X ?_ B{9%hCy`i#pҿ!mW-4;A o(ҿ!mQpo_+QE A o(ҿ!mQpo_+QE A o(ҿ!mQpo_+QE A o(ҿ!mQpo_+QE A o(ҿ!mQpo_+QE A o(ҿ!mQpo_+V4'"?2< +MsY}pȓ< A2Hj.#ҿ!m]`_{ `xC3,6G' \35kCqe:t9H$yk ҬmxX{Um>}*= |;}VzWxSѱ4WqjDOYVv:p]LmE8pIA>׷Znej{yX$N8]]O Zsy!u`x.h@(O]j(Pr ߅ >=:u:0aRZmsb 8 {&$k#4T)bkO8#ސ}i>0Y].suoH@^ᶜRi]KzxݽSpTgw? RHA/Xźe*2O ڲmyy/ڭF0^@ ?.H GN Ep:]^j:qmd,+!37cV21.IⅨ\iP[Z!W%PwGIErWZ,&ӮQ{[WFp11튷]_Rԯh5kkkg#ː `>KQ_`c[\xF]^L[}LG#( Sz=+RWj4d_hPeL\ܡa('w,8K\Z8 8f]Q\A?O6ܗQ39id+cF:9Z%&{{Ko&7PHwPۏ =^QR]>{XgV]q>ǖiֵ bc .²;͌灌]=m*{ĄFdyK󮖊bş?JUm>}*n}CRZcI嗒kޏJ=Ka+'2kc 37R?%`W gk|U[$ Z$7wWxwԙI9'?[]jdfͅܞ%κpZGt#w"Ry16)z'A egUxM6%A#mr3Oֽ'ßbE9Uwos2*z%kb 鑜W=5m3,pF wYfuG )^=/-:c^DܐWEFHo&̉!@ ǷZ? o|N:]ѓrZfqHLoقIU*ϕ$e&BF]sYķ~B$1sq].q0H*ŌZO23VV ŕxJ8H Z$F| F*Gi h`. sUjAKlz^$4t\*}Ma,(, cںQ4L:}ڝ:-<O^%VL+-;B_93fWg zLi!Ƃ+9uZwy5Ưxw/l*0S,T%r*ݟP{կM =ÀNV2_ ]|w} seM^]f0@@7zb<O.v{fQX%qk.BJN8=r W+unK|wKxP̤L s=PjA9\b3dgnDL15 8`V)#;OIU\vzעi1M>־\/n8w#yaoH]>;ӗEMI%Ly ~'xl4M%0ONqo{ H&[\'Zeēg7-)eW5gUʅ4H wYvIxhYqkL@j߹¦\;GKUZ: L7_5gHD) 1 q\Xq^ȯnΦMN|JH=꿌.m/-Q*ݑߵ`ǹIE)rO5NJw:MoApF87aǽq9>9YG sI*lv2[\ _O/ 5Zn85GI>=mRԨX:}ʫUm<bMECKod T?d KoMECKod v/R[wKag9:B0c=j+ hO4;+FaHl1Nk[?Z>m<,a_/Dû r,3tbO %ڵniBH0q8qkW?Z>m<,Qt뫫dinM&wBrvu<qT_xr *ʿi, Omx'G-~74YhdZg[C#P6XT)/e f/"u*FT%'Z%O֏[h=2LFKh$j@I?b;)Ds'WSnݲ<l(ŽBGYꖓ *AXVۭx¿[KC +čؑx·ek7[ \F7Fcc\tI6;Ş<;d1*&hKEyj$rKm(W9sַq#}_nMgN5W[0e@SqގÐwcydu_GA.x9-&2e;1cI%`>?/qjioU*^lk,SwG\GfL֭Oj+b2W+c:㐉XG9aj &wG@u gEV>m くҥkbm[ %ps֬d3{Pdf#jlA( lZpPF~omQ:Z~@Hq[iQv 樛fi\hLVfh׎1Zh蛕MT`q֟2+*F~s֤cjp]҃edQ̇bĜB(߀r*Y?v⢎\dYXP $ ~y| e"'uZ͡kTU,Z$((((((((((((*;Yл}IY!^&#T(̊ˎ}ۭH4SY*t Ybȗmj8:=E]]Z\]f%I;|'14h \i[RO\xUMlwuw\yNQ>^sl'֕5zJۜZXÍ}rb@ u_( unˆsGu`0 ԁ]ΪM&w*Cmj_ 6]hOs[xZ\f!9T[HIxg9lB3Qnh_C+F *|NJɟVFPW;~Q3+'Sǽs%^6R~S(fDtU*ۃҞ<%J)'W;pXIP$䱟څ"V=jOnd(ky"I+j7'R]N|qTW0(2W4%*\파`,Xq4YQ88s_Y#^cTIQIP4s wj Z_lKH t#*'Hd&E':e#AyD}z}bV?A@]; Vixf$[j _3YKdI9<̂t9o,j]sI E R/NhI0?|44l m$,NBlpSH`F: {um{}ZZγIrUXdh #<Wt#m?Q,U^6F%8DzWxgI.Jr6s`pq^! xQ4ggW&*Y#v<ǺΩWwZ|GBҹixg7MR|#4QptV$(k*FT>6H *c#8=z+zO~5lē..?}Zd<\uBQL*Y gҜvY¹\ v׮Mʍz`֚]a#=;(77{J܊,fotoxx-18.01.1/images/netmap-locs.png0000644000175000017500000002162213222767271016062 0ustar micomicoPNG  IHDR@:_yeIDATh͛scW~߿ww/=XN<3y]ů~O?"yS*T95)&gx-c)ms4kOIqciփ4%5cS:jt1)K*cqL8Rcq(嫐M*YL9P̙;sFvsFl>`qB0B.p)J&4C p.%SO#cemHF75& dcTԗ_Cz݉AG'%J'Cld{@ ʠY&07X81 ;Ai))[ 1o0  ~)l| sJ*%:ʩ91L^W{֡E8ڂ<8d:ݍqO1(0o>5ӝN{9S?jI 9p:%rjAI :l`  МU#Dg =4f[w5h(̚;̫1Cp;[js9%rf{8#yiiT3S #4b8& [S/v.@ nJ|ܪӱ*/*DJL[:]juY LyPۗOH4ױ9m'=7k Op52JC q]uP8Ihy}` r:8"QϏ/1F9G<1ܥ_ث2g?kueQi 5ⰨsCT5TB緖G;Z[,)6fv cB\oi>Sm"4?eTEj4gnp#@dMbZ(jx+MۊXo@Zt6,#+˾J-NQO{3dn_"۲HT m#0Cl4- ?-:ň 8zaRE6`R B ryEHR4 [$y5÷e=m1Z{WaLѻT)U\k5 absi܎SX<jjDU .;%K4a,(";o&XapS$ ϧt *=&Mh:t#`oWݹipho'# pbMVrd+ sŻί[W5?#9Ñ:@b}ЇHWYu GWY !"Lq7 &ىv[?c]\!^rkQoDŽ!J9A:vz!o9qeCzCS$x !]=$0T¡h֭kARa5G{L%y Npn+Ӗn]@GA@0Z)b1dp5SŞ!WdYYqwSy.ZE P",]L @+ss阚'牗1\VDܼZ{28!2[%2N@MQe?D*bVb$hN"{onbej̪6BaӶXl?ꏴVU>!RG+yZyinmK55OOψ<DY95H-e>\im9rAS\qsAc)U Y `iL1'0Mw'}n53|-&{v=: ߌ*dmO-u<rn63*ji"LN=Ӭ*sٺj[ce<.m^Ruj'H230M dh ^FG)y\layuhqQCUz&iqRfNufI7@0DwSU7v\v=b-SP:%'4NSx:`JJgRG /tӪCtnz>GF-}܂i6LDҙnHtY@ffheAF^.)LˈGkmMPܩq8NlL| %9\X>,Њ"c%onpr ܰ5`d2HP5Z^I[sGH<bhiR ikDGağ jUj [iYCrZ7˪&sZ>dN$mi}37_"!ZJԄ'NaΎt-፺>.q xaA}w/~*H@"~@y 9T|l@ $f_U͢5~ńZsZd\YBdTFmVn`Êj9ڦXD 17'on 7% N4D1gt}?MqwDQQdУGj,Al6ȺPZGAD=* gc,K5w"\ sG.IIs&E#N] 9Wkg'S Ђ"1@,{qg+afA+E T y$PuP)lEY~wĻ57WT$'CuRzi02Asr 3+ar&~8lϐ ,#0)۵yye(k:ra@y4ӏ]8Xx΢}-&_f<~#,9hŬxO Q#r% 02nG}T*'9Dr3D>8 ɢ:i; *0%?S*Bd?c_: |h4-9(&V.rJ% %}BRo@mI x;TrVΔ$kQl-m U,&LV].'Sa5D> ՎzΌ>=BF>f^_>3 2'T U uܡ]hCWUնr+/i}~^WV*/q{UeʕI@,8Wg?p;xnuEWؠҡzPmhkD!_ɪL 2A(*6JLDm3T{=mB MsI;A@*>f3T8]Rԡ֮`TGÏ40 iL+C^1mXrqD t'2;$03ٗ f:0hXF'd HwB=o@qi^Eewчx+ݲ!)9$WjV[PHS҆<&uh5S};<<&.$xLI=(@k'^_.L,Ωc=yBF{Mm}@͖cVJg.Ks `jdD:9茊o?dO~BǦ TuDJЪ+ʘZ~dID?~ωV'^ L ;icY-<vQo-핑(Z0piarJ޲d)DnV[mӲS|ǨC'F4Y:Tn"1eC,)(5GQh1 P_bNzM~rCɂrgIx L|68/bv G !E~3!L 0&tFEjԚ:" YXzXx.,(ͫVJtVhwBtG kR^dj+'0@,mz 2u %r--ra@1$L]\1ߊ>9i#r:Hi`)ukNf^YeOrC7WT Dy@o[>{Ҥ1w4T;=BH?X!`w 6iɏeuM ~ ~tS730'ÄKt5[JN0v$6ep"?i&0-eA4;8P1 )IM6;fCT./D!3}AEDjMa,pET,E@n% ~~[Glǯ?Vh{}xU+=>iu'mgaLh$sCozU[WiZBT f揜Lmwv[).`zw;i$_֕ESo$Qє&Ȑv 粘2I )A s/?1hHۛ#;PisM/@6fۤ=IŇu^kU+p9eڤA]V8&An;]^&ȕϞ_`涷)F f3왕s/jG!97޻wOoäwM߾Auxp_-=#5Z[";$fߺu7X * + <fKcN̋aY3Y{^Lpphdѧ5 t䓻'V"j-̡Ͱ[˦WR O}@^>1|u/PXXI3ݤyfFp!H4gv0I &y޲M~k褡CivZӇu 1_+fĥTj\#in糽unUg_̲U #97{{`? 60 _ѣ=\0wa{?7.c&ȆvHzj4A\SJSϥdN W~[cC KW>So[[({nT w:[ c ʍ+Ykpk.CP3)Fwq4^4}p$T{v/`Le?k`i^ Y/Oz lE_R-S.D}U P.j3jɐ^.7oLugv6LʊS|ՀB"?7OZyU`;f+?m :a<69|#]"~c"7sTrP)2?PS32x~DO#Β)5så~iϱ>.[oͷ0͆|6X3ZWӧ1raγ{n5)° ЙkϺ;reN\}آ:|/j51X0#%۹m###s }gHrgxqkP4FWwsk. 6ZH׈*Z`sm'LnSs @.YKtEXtSoftwaregnome-screenshot>zTXtRaw profile type APP1xmQAn } ϡ T+Um?^BuDx}}}?E 5K+ȶb,M+F>H6=vEHaޒUZo)g<Kg-*M¸+ ) ,us9$ND@HDPT'n2}$C[,&ʚj2EvoD[0EYn7 Ks} 갎vxLtA ΰD]B7=`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }Zǀ1iTXtXML:com.adobe.xmp 591 728 0 DIENDB`fotoxx-18.01.1/images/image-locations.jpg0000644000175000017500000030705713222767271016720 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230:0100Fotoxx:voodoo2|resize| Fotoxx:trim_rotate| Fotoxx:resize| 6http://ns.adobe.com/xap/1.0/ 674 1107 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?kO &`ExWj EM~_@[/aiPs|W.=KH26"'ǩ<5t?j$x Oƥ2גႸV-4=ֱ5EO0?h]7񜓀>]WPp97mndXB%bI晦ٺx45֑[0vVtv  ͬwkjp}ZU~BLcܓ޸yO3]Kqru \]$0;RC',d*j6sg'R69RIfG0WzWm& u=2T81Y7e53!G/.S"|˒wm'pZ7sfͼy`429r8O?hZ7Sx;ޏ;ސrDހ4|{z}hɠ\ޤ3~gƏ<>R}T>}hRڟƏ7ޛ@r?joQ<Sx8<cGMK6{+\CzoVwq\ަ7<'|hoVG@rhoQ;ޗΠ9MQgy֝hoΨK_Cxvqy4RԾ{xy9M=i>ުq4\wR}T|zq春`̳DsB+:櫪kVιu'gbWa<2a:NPcG—k(gs0ҹmIZ$B̂DYT2 9-"=atu&-;F:r&b3 _YH-li;5=J <( h~5}Hbi!r10=Z|>p!wD]ٷj Bkő2ʐ;\9ȟj=~L5 un?u]ܿ#~f㹡_\J@08ԗ8·n4CDSӞ F[žG^'=Cg ~:*81¶<];;蕷̚R:v_VMU-/tOg[D:9GݞܿL&3 ⻽D w||#43_wr@81jG8x3Rt%׬td"1'V!FIq";83GO_ݟOkfM[X 3ս+X.-)SBgNL? Խb1IG"vpj^Tg_wD>vpj~T/Bg/B5?e}WQG";<}OT/9s?G 2裑;<}OT/9s?G 2裑;<}OT/9s?KNL*(A>'S_j~U4Qȅ>#SKU4Qȃ}FLQK/|9?+Q9s+QDt (AO/x/RbD Q^]r g^G!zwQȃ!z>UG"vp_j>TzG}W{E9zG}R=(AKT_wQȃ!z2 Կ]r gfL? Կ]r gfL? Կ]r gfL? Կ]r gfG}QfLh;8/Cu?e}QnLh;8?Cu/e}QfL(;8?C5g}QfL(ȃ!33PwQȃ!33PwQȃ!33PwQȃ!39sO|?r-''r+Ƒq MKKu,EG\į C"d{Ycb.OAsP5-U]7k'&xt)g⥝ԯg `t~Z "`FUm-'¸/'H}v nz U~\|=ڏW5JSJѫ}N` ۭg8MK/ez|QY"d5f1IlzױAy^Dy^[1*g tE=үZrz?RX֯wgoj2. dm䃓AެO@{jlէx&b1&ߺBb_C`0?j'PnU~X_In0Q01~b (c@cF?EQ(QF>OPE~b}?1@c1Q(QF>OPE~b}?1@c1Q(QF><+g|NB.ErcsF9ǭsؚ߲!]G,~] ۏָڇ>$Zkqi7N|-pᆕyˋūa qdĊK)9J*LF\e^, +i!հ;.}+ma=Fq9x#=|qCm.x|O~֭"1y,GX}~|*$߈d.v'ߓTPt#ZtުWCo|Lѕʝ 1',?CSZLP>asI#1ʇ-6lb.ш߻p؜u9ksٟ\/1G,?NjqXxVIK^"X]xDkVsڑy |H[,T%/1^fX.zg,7ߘ^WE}?eܾyX.zo,;vtqE{4 +żmUoy7 tb)8+4>.]* ml`#{8D1AM#fg,5}B\ ;Q CM+9m=ƟOk4)3Fz8}kǵ_ *bZZ[oȊIVb#Nw?t}?h C_4Vz }&%q0J$uNK ku&dWOpsޕs?h~أXw=KvQ ;M+qF(\/Yo/1G,3ybbSr,1^[;޷}Y]Qծtke+ۜLgc?Z:o/1H~)i/+o;׵C׬Vٴȡ)|J\$5|Wi+k7Z}HX?;Gjv}k OMwߘ<1_7|9 _:`c]F22y+qX%7Ox.-) szϪi?+95qs{^"Gkm5.b썛s.x _wZ\s|-BX\W! ϯ?izg| J×zv l^^El0o}I844.AI6q`gz].>?~bZzg|Zs)5[]"Up{89|/(EӭgմX"@gxEϭ?i?+]~x^ h@,FY'W$h-bz;IܺpȪ>}Eϲi?{@s-zC,חE c#~%|i|x }ci)^'`S\Q OL+5 4lhad_1a >X8j($I$H2V@b)%q\OL?t,1^YKEsԿhiܽ. jP1Eӿ{_Yo/1^`X.zw,7nWהGxsW"mtѩ}.I(vs,>rnrӿ{/n*^Yj\MgO*o|d𦫨Zǣ]pi{q(̈́I(jϤfiܽ8|Kqj:ޟ$:mݎ&6&c}G$/aE[WVuf"Fˁ[.Z,+S)^b6]=Fhܹ铟Ҿs?~ $Zs<Z:-V{ܟ?g^"'"[A2vAşF :u׊~s}FTcoZqmF}~U.IN*zKo 5 #!2,Gb/Ag J}ZhcKo$ W'Z5oz֝iMmo<2]!`bW,FO#OK}lt Ia]\O{3BeA#EMχLZ=zS_G>ܨ>خ'Kkڻ6ƑkŨ[!hvw*J yZѴ]WWa6۝>D/QˉY6<@=Ӿx{:Xj^y<Qa~k;Kk?f}-2,θbW9ۂ8X?j^$JZimk eřwd@Ꭶ?YOK3RujHB#gv,2;bz du>Xg[ӦlLdԲx ^!ֿԷ^[vLSRV|:u+RCuֆ[N\ē}W {NƐJu mˋbcP~A鶄?-Z-Kwh[tf՝xڜhK,Fd/ON'>&y]b 6MRy8Ef,W y;׌xk@H%6-c& Fc`gWw{c߈V+տ}5DK;\Zp9V}bx7U?H2݊?.X{$׉MIӯmm1% 3)A#-BIZ+ s4ß moHTMsڀ{V/m3.D/kr T657pKEihQO :嵅}#m6H.Vc1C= 2|7@ӼQ(Csjx ^-bڛd w7N}k F6y᫈kk,$O a WvI=HnC;tO'-"}bh`Prķ՟|9t??mj3^z c߽pĚtz{}J=Q!.#q h pq5V?/\*sN}5"FֳaTgpCBZw^i X6w .unp>O7 YQVaʻ~u kڵݼ^-nBGQݑ-gֵsy=F}N'l+(tҞ̮];XuKӢ7'؊n>b? mm#4[&; *1Y<9\$LԍSyF'Amt\%σB|)w[4%nmcIa.yap9q>tE-Rm3G%j {h<r{G YmZMЗ @\29o ƱM^L}^KSq`Kxʫx%>My-_i-WVi'#ڵA}~3w{dMC[:MC1!bbV]f_[Ieɧ]^Kwk$W9e3m=ՉY!mi::%s*\M(ZA,ac~xHФюsiVvܫs+3O ç(`{s@Qxnx=5Zb rrvዔD֮{um`36y Dy B_ZVK [[͖5ĺa#>bO1Bэtgj>SEm[O[R9>ox|UVr|< .w+mwBx3>W7VdW =iOSVS^iڻȍ9:Sz-Q|;4zuelW9χ|'/W䶍sԡe.NU/x_WuGX.c=ך-] zqol=7zޟ}]b>sqv_.Y\c7f7_o5Mr1k|` qǮ8Z{ig #: mO'kk$=[XD;!V0gRk!tiz^[YXAa*F\o+Fc>GNGG.l[HVG<1Ttn+|34 [)t?Tk7n-<{>oxoJc9$^LwW/W2ŧAپEio iߐ۵A[<'f2]*]J.0vC0o1G#]85*d2/ϟ=x{&ƙr:E- -AZ< /O]Ee:$*-|(\'w=s'zE֛<:󦹞|w=+<kNj[5{[Cd 玜V x_:biQ ʞ:zW Znj ̷>,uc63g\zv;M?YM4k=[O&l,TR BvsƍAnuXlmo( lRrMmu<\iiC/n<. +jz(Դ-2s[v-bYA¨Q{) A^ )Jq1|W-P0 oJz-n Q/5oMZ]M6+ho1)! /`n'oZ|wDvȢ=Vflמa|amnaХ1kz!͜rcZ ~ůYay,aĬ~fOsּV/>8ɥ[m,/s4|PH.m'YᦧxZ)줎LHU-BqTLS$$2n7+#Ͻ:< ,/a(dVT' VB/JZ)å-myYxSPm-G"RN=zvR@O \\35;XuWB9\næ*Xx[w8A>-γjq.n H N=3T/K5M-e]H9 7ҸKᇉ/gҞOOa @vwdVzmm6M7H_}ԫ0v>p@%Kw4qu?o|Qf %Kq/a+oEco 񏔑d~fi AyKsF54jˑR&ʜaH# ndn兓v2sG͓v&zO|1jvvwެ; ` 9_Oʛa*w74 24`JLJ^̮sOۼ߳Anok̓9n[rX|6}f )Ry򦠄3?ohλ~752ħʜ֙r >wϺ?74g]ϴf2Xgϴt?ok73Š: ?f_\ϼ7?/kZ6lnG|o/kҤH^ߥ|kMxdUIR{fF? ⓤQwce(R.?#VPY+YPI(\Z[~TDohIqSA& jSF_Y}ȓyh4]!w&Fj~Y8_MO|_O =E>y'IIc~F<۫#>ǰdQEܯgy7h6[&)lG.#/oɯJ8bx#?4}'kO }S_Y?O'h 5 TRx[uy5Ǽ7#] {JǠҬjdlmzܥ^c~s4ݓ~}]?,9VnFAR;՛Mt?^ӭi{b1nU+M(jAhp;WzcHv67Y\R\+m>HjB47P)=(ކ rzcE}Q7OG?k:Oj>\1<Õ末 GORA#19?tTF8\Ҥp(X٥?G٥cT;Eױ?OW 돱 iI'?N[9猟ɮ@gӂ9GԼ#~?4}'kԣRm9_§cߑKcpyH'kՖ1ncnB/1mL,Q'icx|_.~bPI,ZK2/N̎tZNqޘj9Ujw߽y̗fH?KUe>N,v[g-Ʌ#9#͸|NIi}H!f9>C?.fiC))9ҋFX9p=jPӁspWi uF158vBJcjw7sSwk7#HJ0Ng"\  5$wHڦE X9e#b>>@Prj!\z3枖Nx#q@6w_߇_*3vv\)ho(0$>/ZGZ ~Mov5in#G+zxys6M5%{~GV5z<s^s]gКpJ3^6&kR0Z1dS|ܞ5UME:1"7ۅ]c}KBLT7"+w늊CP[~Uk^9hwLo#nq֋ܤs6,?gIfی=OjkMJA nG_|qg+j0<Ož SP[_קQN|l֥$LRT;Ue_ 5] /mB؁L_ZǒGr4F\?Ld~ЧrJ[V%B:%umgdGDu(Gdk^zԯ5 =qCpe7ze-Uh\o*KڼXMm0yafXO6/ޫJ31jHfS.j,3F ׶}*u]ǠC>1ݵJ~eSʏZҺUh.c$I]-UA??ąj6hs=+~>h*cs#%ZHF25adx #~U3#v s+"R6^9nG4.Jg Gp~1]+kV~$V'~2hte# HF{N1L1QS2*3 )`NOzL&9.q?kv9%wgwznlzdF߭Dˌ c4(XwSU"Iŀ'`˱~j;i6M;DMoqq5(\.y;Մ@2N^>cl  ku>Cv1[u#%[붍s3G kbP #Wx.-ͅKv⽇Ú:ռ!tLy;W5V ntBTMK ' g\>Nh<_gumzdSVH{8\Tgr~@թ0=JNBn`K R'Ғ7 + @jŹA![N F*|? QsC-Y7wH{Tf^8p7#EΥ}(3$(%юdh.챠,N0\?km.ת?Cy?گ<[]Ѽ0c\DD"܎|jL௖̥zb!Gc ;AVBzd'rq\lcg:z}i`Sgk)6k.IgګIZcj659s^3,By LU]Vזu@e<$S+޶EXM:AA֮R##սȌi)֎QJq=scP3| ʕCpð-tY\(,9?Jm:D)7ݢeN23\򮑴i3͛g(@º-:ͬfSFI5!S\%7=QR[m5{uv,FFje9P}Ɨ=34˙c t'ߚҖ#D/n9kM9e3јҥ)sOMJGoxaR-b!t*sbĞM%j6UOҢh3L0 4U sFPF)Zk}1M֪q?ZB`k/_kҼylFk7`g-EF1> \ѴkI'>+1gOq54ym0fO0o~hh}1O uTH9u7Xkv6b^|uҰ4{$ʫ*1@I={+Wb3ܕXGIATFMΑz 8[8]u%6W<[4Btmb}5?,n2XZO9W>$jSϼum]Z"Eb#G s7?6bR̓0 |Lwc`䴙y:Ud w3o +tX7o-1rN݊] ByYi'z<ŝԱ!?5N꿅z_ÿ C}B+Wܦ>ҡMjj x?5gWZBj^u?tzQ^ BFiBy{rsU3Ω2@FJ UA={tyq*"#r+_V )MICqA~-v}ʖ0C#Ё޴PgZo&?zg 5~=|=u+Iij3FǦkjo05Ou6|Cl猏; ꗶ^iNԥAj\elq3zo f$,gm"U_qWc|LI$:F:D0|8ξdk0 YlX}A1d^G_~6>۬rݸV? qʣ1I#Ӿ;~0li $--8_.귳};NkѼ=ۣ+ӓ?HYnk*"x'𬵓]EhgcۇK~R3L_[4;tF$<0+;h֟ /.K {W|wkJ#jcUnVQw:~PD c9<gk\gIaW%ҽ:ZUnn_RSEMῊZuy,KT[=ih)ʫo|=jp9)cھU9_r0rcոDfD!yh8s͎Z@tk"|]#|Ld~ pH@g`:kk2ߤ\8;wW8}IYqx✚Œ=7262@󎢹cԼ?gM5$LgKqkZukZҶb(Ҍ]ϸm H<Ĺ}0MqƻGkT}em+JӶ˩+nO% 89 +=;T|)5Է/=Y\n|x^jVZJ\vbTR{]ժI|efcR\YrWv>ZjWbVd1::o- )U!V!H#Zԟ- ?q,{:<Ρ#/ f[޻"d4e$@}uĽFF1#=ȻTW#yq4H -'.q+R\Y|2&QwN?Jqќđ0qU|r?{y4uVkyc7';FR:p: ?l7>uo-aKE^W|11*ԐB+jZkOx=wNKO.{g Q9ڗ~$w%`#78>(ɼ9j#ID2V+i^[}v{=kuw6vy-)E@85hVD:,vJ^+y$9OJܨDxbgQ$q;\`uyig4BvKfHv89:^dZ-F Pw}Tc7}OtvotO[wq4ݹu0v<_Gosݾjufەyv#}]_:it-A'"ޱKἎr)EnNc*[\x{$lҽWey%ʠ1EhĤzT}*Q"gҼ^>'Q]ۢ,Ƞ63z!_gC`KF ߞ;VN*zc:B8>Qe7I-.R2~Vh5DgH=H%w,^VRO$]:I{ ;R7>SF,6tiv_RGms(9}**gHG;OB~TΓ2eG+G;O8@vb}'. ( 4Z}6GDqr5hszfYe O7R)R~к.Rq21^H} Y.1J9^Uj+iq\Θ^湗fI|qIīi#ӣC]$ tO*Z˪01#dqu^ޙ96r'=9zu[{/m;Tf.Jk{{VZyR)\ \*=F#rw7V&O5#+_`5[7VѺ Xۀ dG1ujw>ۦ%dcW;1ʺ3;穥{&S k 8XɪUFfwisȬkvn'V[,{T7SIW:_XG}{m8Z4+YRNBxC{kڼzM|{8ܬbSG/^3ѵ;[lVIvǻt}1ςdIf&EB!8oƤucbCդDle3GQN} ȱ\*JVzZrC9=? 5jv7^mya<~TFT#`DJ1USE NUti˴W[kpXPz5KI5^sNלu܇IԱ%NDI^kKQx3,M Gɗ۽Ւc @ [ q_N'hn󯡿g/xúS1 Yi#Ӂ-Ω5:*TUIa')JM5ouw<#YQY<_3]6v0 bK:Qg|D𭎑{-3pϞ,^5r8qYu[]}ryzYN5G#55ӆJF7W*J# x gV.9Rk5RafHԐxnqXRV# A79]؈"ֹx~4[sotmncW}xTjR Zǃ,|9iiAVޠz֪7DcY>r+4=tM-FK^6ؐz_pͪj!\_3\ W ^1(U@kCD'1@m|S>"x~}?PvMnqQ_|5<%Emu9a]|p6godRUis&iNMJӥ 11ъ )y(>S\s*g.ǐxx5:WŔh,O g%Ynl_Zn=M%"cb1 sHӕ6npF\vϽKypBVaо ̪3$'<)X,?g\KKIᐰa +5֧A\Eng0^NGg:nen#lHe0 9ZvQqHY;t֚=yq%&Is_0OsN|3A(Qmz^j|3\jv 2 0O7HҬo-/e3BC8$+;~Vg4Gg۸?t28c]-V]u.?mEc)+&:{5ʹ67GkY ܓYxr]K#{i05@OZ/oy+]K^{P>L&Q:; r*jݑͭP4%2ZKy!1޶hrY#o8F/㱯KAxI0W1.\cz#i /xGȭ$MlyrPƬobK&$xvֹ]i1^h"k-PcJZծ/%y,PZDj!$s9u:K۷O7i|(Tzԋ,¼۷q<սaH$CR]~E&M\DQ:rL06穡>ј &Ȍ0bbAX1+i#ϗ|s'Dnj6HkHQ[= }Yi~lIpZȞBp̠ g-Y-V7Tg@r=g=qGKsޡ{=3[hI!WP8ANŪjZY6 LW*qV(olrH}P,/e$$z=tCM-ϭt}Z)n{a<>Q6:gڳÂmN\1#9GBNm~4|3^=/|D%XCϯvTJ[:zVzdMspm3m\Nk_yWKX R(rnj|pzkeb# 7=r+4˫t,MӜ9ִv(}٫N [.5; ^aBFUsk;Oxc@Fva$zq?3u/p4"&9cK9[O G=|5j4|LյXZ ={Wx[$q!+QNC;/3Xm}Ee2I'0 w g9=-fNXDjI Ԟ|x ۉiA ^1@y/hgO/,͉I9XJSHVյmZCˠ\1k^KiR+D%p~:+Gt˧uԕHZ4 &I/׫|+~#_Oͭ7]W!I;Ap;Rd{Χwhe(ؼ c'ںQvq4sC֯|O߶D1۴NJw^i\[~H֚\PnelLdz)xRhvM3ȯ3]~Mf#FOQWOqҽZ񦝡$2*XʮC7jN'dLK]"X@}2h~S]LVzl첑c}6ڔ n!ӧVIj\M*wM[jm%_gw sOmHb=zW̿OJ랣=/eN*] kq,㕬/*s]@Gu҆^Oz)%:RoO(zW=Ҭt~?P dm^{TY,Fs\L1[xv\ XxiҢv%ڞ uGC'Zz R zu1)1RÐFzן<:q2c pckmFw:}}2HN$֦O\_h~|_k M}mn眀qv_> Oujov@rVQk>(Ž&K-j׮LwIzvh5Hudp-NVwZz t|ZD6zd޾vD#~|=5 ungҹG/[æh~!J  Ͽs晍ek<:0X2n$[&Kf@$G'1^V\C{r%?}ՈҶE͋Hm.Uc*zCs<ښ,VyuegŴUUu=Wҵ 7wFoE}7.4H_Lp Xf&. uOҚ̠-a$9D-ؿXn" =H&4}lǸ~x nemj#0㞿|xMWO# gv RVeS۹Y~rJmtPSj|ӵ]VX<[(:{1xSb{8d+w=էEcH@ο.9։+s>t= : om$,ۈ~?Լ7]^hq4.y%FO_3AɢtWtd3yǵ}Kin^ ܰߴ YcP H21S'IIEXsZG-n֐]ȼ޾!+&㡯V4͹>Ү<uir(Px4k wGm3` !`dgtЧ9;-z ? caK@@MQpwՍm`P:qڛl [k߅8*'xz^EyiVSJ/Pb׮kXY^'mLjn3L|{ť b̯w50y0%>%-9j'ڒ[}ayKKq>~]RTi(&i-['bxC,4*#5h_90ھda$%9xÃR3]N⛘DH(p:*}ZOcb' >UQqɮYFZGmݳ4?uA#S @,$xWNAbI^5u7_$r[?JESDbՏM}^P{acWxPueeCo&q}zV4r>CS,S]ga۾Kw29'9n(%3i.,F5.q\wvnc p8D[Wn24ūwtkFO.__Go.vl3&=X8VjctN[Aoq3t6܌:~u_ I59{{bN9Osx?1i5Ϝ+$ycxjK^`׷d,s: ۙC.Xo/|m F!Tg'ga.\Jʿtg~uɪhç[=&uDU?<Ж=OSQGҼ[mnqMX'|u{D乎K35]@+9uO-šTC1*8ֿ= s2=>_~ 5/U`ԄӪ\[,, I'ga'<RuHfyK\:񪷾XgZp}C[1(7w={RoVkdtz嶵yE|"^MdEk%#Mcu|IԵi3vmE훉#pp ~xkxxZz>SpK8O銹?ךnڅw2JppYF͚;v[j踵o!NC[,R=+mg_WżF3{Ә-\Q2-Ɔ2x nAӥy'&s&5/$obn8Cev7rjL*_R@tVI#@8xʜ+Z":Ѯv@qZ'B9yf5YUE[;Msھkh׫xS\ԬbyJDT_nӱ;_&^'׋lA5uuY#UuqA~0 xFwtGIJw#vbǸ+M[ݩ. uK9epHj.[>75H>gڧV*gT{WZL;PQ sp+s>z?URRoCU 1<anQxG$-MFcz7H"%ubn%y3s^X/{yA ~s^ yo8M?V|Lh{G|Ge-}OP@p-#EI$ 򫏌ök86yX>5i\ZEݽ<Bt k<%xo@֭;Gft˜q5'2!M24ʻh <'yk Քh¨)uI?T7+u u?Y7Ć)?iU~G$R5\氂&03In*Hh kqXo4P5F+Ðz2WɳN~]MUGYjtvΪ\.BYH$L.33Ɗ1 W|t7:J@׎K[_"*3޻<ڻ#Yq;T^#r\1`*ApnTw99ѵ.QYp<5Mp|m< վpLVab5m[#=;m'8yҺ#A }%Yz( 5i^ ԯmi[~vߗSK0qjt,VdrJ*q|%2l>Yک*O3Otwt40$'犱 YXcj@Z׏H YWr؂$=!V|֊rʆ\JX'Z"dVIM g_4- XFrrXUuQ=8<꧇lNQ[:fqupÿ8^NAõuo`}̾lMiEP@U0*G^,2%Qnj}zRk"kBmW GcZm}*@8Ԏd]ћHFl׎|O0jֺ`=z9lg>$hwL\sv4eqdG4 $|+_^Gځ%򗍻*I_¼~#kh.IxuUmq"m>V^$WͨmjPӤZ[xz\Z`I<DwvUZŶ^Xl" Lᯋ.8H-Z*O]9!i?FjLRiܣvtg C.}^iׄWhE=yOg5vZt E-Qleϩ_`~˧>xDIun?3c+IMm&a68</δ|w3E=p1 3VorZ:$H+c$ImR_J䓝B\M-ON+{%c&GHlY#5:އi6Iyh0e|:ْZҡxч.p=AͨiR<0G<֩Ř*mnE Ԅu HYaMQ^ۨK(e&+Oxrpo[rcmO^ LqiJ<gdE/fj Ypiڍ Y;?Ezgb2sxQi|o$DQEC PU7$W5 a%6d;dnwgZ(rSijt.…i89cTto ZƉ%+y.I8ip#-MIc.I &J;4fBb ׸ɭYn0,@ڋ3ҩ\;[,'= *w"!*ڬ~bNsL _l;YsXu5x]7H~Vلgb00p;cҟ-b{R,1oyF=ʳDO_:K#f>c$~4˓!P[kIk{#uݕs۽;V>TsZuv=۵wa~lq|^RpMyfi[5&de(#=r]a^mQZ3)^mm/O<ds(ʼGFdSUZW>崚6q1 G+b[c{IQf,(n c[t"L5+I``esʴtúy@@6ѕ?Jx]^'ԭneIn")}cJ}VgjZ]\R-,y]F;3!A)^coϦ>#k4 U=Oζ-%iz֓4p{RǗjzA2;\Q&9jx׉fmuۉte%3׏lnvz$ /(;A^|5&%ͅR:Ϩ2sNZ8g[=E_Q.l|@TQr8;?ʟyIc+li_#O3 !q\]̲̱7GXO_))$[H|O;IW_sEnL2Da`g]RGճvP] 5WSRt~a%.g[ݤz0&+ŕrs_W-3G Xe0=ic=mm۩Ϸ4 8w,Qe]ܞ+υ>#~Ywr(%{[BuΏ($\s1^㯉w>"h:'PZ{o1L|.܃GZ]O]?\z}ܾҼ{ 8¼#kg69l)C nFGҼt%\f ;(oⵐxR+Mfu;XOojEx F]e8.*&CJIԙ6TTojV⹟HWW͈~3#K]`дkBA6AV漫Nbu*qg?Ҽ({{#OoxQy,}a\L.Mt}s5ֱH`J>2+Fǚ۔S֊'w4ošxl/ )@yEnt2CklbYKc'r_hTMc-1Fn1;Ǭ2jbeʕSǭǕ=;v< e%Ђ!hV0=Jǖ+?xB^Ny <8sMmY]ZΗVD9VOC]j4hj:7{G 7µ>%ӴUfS|w7GtPYpA[j.m #lR{Jm3g_+`Zw=8UV?=!Lu܀Iv<>quʻE_O%q@քɅ±϶W󵷼ՕŜr$@ŧQ领1Go^za|hp?ºㄊUG¯gi7vȱ'=V\ L/R0д9:}<¿J. alXTJ%Ҝf)Mn<< qmw>WSzV<8;-7r} GJ&l-}ԯV50.sEWYi!OFg;A$N6q_q .(QiQF?˜6&7X۶:f%?Ғ4N>;V6$ccگhIu!>g5,WDNʹXخxxSשJGY @m?(δXخȵ&|⧞iX'Z:gf|%V ڎ6ä<6*9$Nex|OA<"?TYleOH5@V2t٘svV+in!%~ʊsGbng'U! T` Pgeck \6Xĸ$Я?_OЦZ/k N*ss*˰Lk_7Oy"vK"1W].>ZcȶPR<15vv<y5M4T^U[SYr 6_jWg W9nC UF&#D[DuR]'?%g7y%H % R] 2N0 R!Pdbi xUt)T1deGs,,_NkNk')9ܪs횲fZlg"1B$[{ձT9#ZRDs]+u˭B Ú[+'úwuko|-`[g-~`A 3ɗ$DT;OJX-LY/me$Vv'c; v,2K rd ? hym3|*[2l1!idw{* "EG2)~IZG4`n~aǾ+ndJ<^o{wc20JkFv ?5eemdE,W**+| uJik=wvR@X%XK?;L;sǚշUA(Sˡi] qOb½iu~#ඖ6prP?:O}5oSu82ǹOҵ>rX_Uka;$'B9Pi>3E$rƂ>X+>"[:x<ֈXDD08#8+&ӱv/vZΛ}@?qV22m:gһ x KRg_3ilO? #%a?S+ dP/& *.OcV̮2GW➲E׉5兞r8 . iG/Wjͽ; 341D]1 ߃Hs]ZGWW4BϘnzi++s*j罵Bfww`Y+ OFT !A7X}4_@tnapH*M[5ʔ!`]&h%?${Nc^[-u.l^B)a{m^i( yb?҉M Z2It~iH߹'Ǘ5Dl-$;Si%n6zEhnDYQXp=3T-n5uh4E8O@}\=2d_mO|%H,GJI6dq_(x֡˽3Q] YOYdiFrݟ^/F6DSUPӿZºCA4C4MKrxw=.YԬXsr@'vxN&;'dd8 Zh>=i~%kwH܋+1> l}܏fZ5]C$6e!!\pNy .fok`κεK%fCb<9'y8O7lFYQ1|ץϾ?+'PxYX B?yf+|~sILᮭ,HǬ.Anx<W;PuvvgFAK栀*jO@Zo'x'o+^RҹgE=F} yW-d2}UŽv'z`\u%弐LXdR2#Wyc^kT =}OW.e, Ǔ9r^o_˪i4T'ɧ$.-v's̩_5 ±r˱Oe¿]"0!$fIO˩y|xT96gd9z)iM yoJKy|?lQEܥXpAx9ƢV=4bR^Z|xYl&6sC8r~eͯxkqC,q0;xIq\_%H Atcӥz6YS/u^_]1jw[S*>f|:uS΢{&C$/^)桒BI Ze48AQP8 <*IP8 <*G@|(󏠪e.ڀ,yQAU<AG}V(=Ϝ}q[.@<(󏠨ڗoJ>8 oJB?}q7qyT|۴F0 TO7dEi%~q}B>!_cQ`~MyM4R&:Xg+r *ʢmB>NߖC-*?Sh9dS~+ ;vּ֓wcZT1$y瓀>,7̯Me]Cy>ZݑGE\Ru9ϹO(_g1h"o p^U!sLɐ*UA?:RWAr@ѱa*7d\]vUqe'~Nc1S,q6M$/O(fMuK̜$S*Q3!Hث*8 M,&559*{+2JyQ ~4셻taqelj˸5ʗrI kȤFTMpW~]n njIfC9^5żkXm699V"|< Ō\H$Fx8n;t%RЪ<M`RW> ]Xc0>b+RiV?.a.\rzuw,[[_ 5@kv6л:`2A@[Vʵ9kkk5In-lygҰE#"Vߌqi$ۼ`1ιohvOϒ?K o`Z8,Iq,M }fb̢!0=p~^L_.=k28h 1\9+b\#ABt<՜hIVll\t`?TT"l~xjLWKl8%nZZc.B~hl֘X>+;? *xr$MΉ }].,bgb3 xsQ=I$YFx'W/iq}e<[YȒx.hfjaok~Bju}*侣w-)%r y5ޭ-w:i,h+3s÷jimWNY lLrRCVѨ(ߌ-: ʐFW|%7BV|w8k [P$),Ky*x "Xs"x5LO/YƫZJ'FT3Fz~uO/~ڄ:iӴ*Q_N'SD91?_} V:},N;ǀOs]TeR6ZU_,4ۑ^C [}'Pү(l(3ʎzVt 2,fr#,OEJ\G,r^ۤE ۅo**iI5+`àyk gU- )9V`&r0H_MA'e<, s+'gY~xkil"_s\M+ r>Ӭ46k 6Ĝsb~cOW϶ ,5"6]Ҷ\^k윯 r=dt-U)zp+ϼtncԴX=rWlnhc+u-#P+=G*d;Y[0FGR*V淅"[ s(Wt;1OWOҹgEaC,ȧ.GDޮWtQGs X1_xQ>˥2c`5"_:q%Υh#zPKna=?"h9"i$~g'W 5>%@ .l*Zi涵∙ XW|JOP-zj>+2;B߲ءv[Guoula$*FUG+n-A{kZUYvɃW+LֹERmB\lyu}M: o75b5I#ju9 ̼֩j3qZ $(V dZ?)Eð|Kfm>ӗෂThV?:>=hPC|Kvn@?Lo$?m uk}zP(<h0dqԟN)`8Рƺ{OðrSNoH >FnO?]Wڇj;`|X#~Zq7V߯KGڇ/g»9q_JU+;ς-''kWj{8v ;bB r"bǩ'GGڇS?O:%>Q A49u}zz:kkOh$~Yw_۰| Z]j_QTùM6|5c+x^9Ug✟Èdž-N}z{}z/q olFEښެ:Ǒ$;Cs1`rpϥ7KԥGFOB?5?b/ ,eNNi^\h-mCF2!2r lc񥻞4ܣ;A29+b_ ,e3& ŸۺC\iI:k9̌_cLQo!th}' d<ӿ6ē^vzc@š"LrGo@׵]WNK{R?/n;?>vWoklFy-sS=ZzRrL_E5x>qEW\4>/[xpM # }-<5R-:G+m' [*umI1sTMX]Gqo}~)c9 _ሜw9IKcgxM[ ?(ns1Pp5ct"B3gN+?{* G\6iZ<~w oVTcf*WDF[!SKcQc^C!9"*l7.Hx_vؿB֬ɌLxj;؇vBL³ OsZcc.Rt.'?իۻ뢫'=>Z>BlA#Y9-Vwriw0LI xܮZ+g-ɋF{?4žpC9Ez4טK'?Joi*OQP>u=LjJEWJgp!MA_ͻi] ;+Ҧ9NlE9VN0J?*+S?С z2?Mk$I,QFPiH~ufe5l;j:_m?{׉C8ĭ/bNO^[oc2kLc_,*&uz5OG\$)Y^jVڌ&e 2H!M|h7X +os$$d\tঁhmbO(]$V8S}_;,]Z% .XlD7 "왹uI`Է7`=Pn5qv˰yua[ ) }"2إ{fM@~zSOUY<'2ٿ‹4?k,KŇeO)?EO([I?ɣI?ɬD]B'5Oo $&K9G˺7Q]‹6?ԇS߬_?oSſ. Eڋ?Ս]ԿWſ._ ]ޥ?]g 7QtIXO(Sſ. Ec߭/c'”xWŃeO(_HzެoE|YB+?o 57Hz?g ڟ7Q?^‹5FV?",{Sf ?g ڟ7Qtic+?o_н3@lhGxV8?]E|WB]iZS_/j G"+wSV .SՋ]œ0pl"is~t0"v6)?@ $S%pzm:Zy"5=n{|} +) y>IS&yŮ7myzEy=Vc)x63+l[9lNӵ+=>(ooK-i!jĀ_FRZ=dS O^cijh=7W.~0"d&#vi)]خK+|jcݳIlko?Լmo&Ⱦw݃9+')CLjXeL76!BuQO j ?Zϳ񎙨jwuS_7xtkq׊\vrE[0u!n$0-ճi> VYKh" UH%GG0ƦH?7O~i9n>k [ǰ'4lE|5mN0>@K0Hcn TMXYGWO%e.g|xRSr'ZuiyWj 4zT:7_jVSy TzŚGuI_Zt[EE6 A߼.{< ?hx5ΏSIhx5΃Ihx5h?}tos_>sG0r?/\o`:_hkCtohk4h9~=MosK}OG0r'ǩ@|uΏ:9?Kֹ^֎dG@ֹϷGG0r'Z>85 _z9_֗p_upSyϷ:pCq4Pι4s!?(8߭so>_f`:/ߟZcg5fgU`O} w?hK _UE*U$Ck6kn *]m5nmqҳa< l-0j}Qc_&@E::S))ih-"Ө@Q@|X'⺉w-S?OА 'Gg}Ԏזp$ '׻|/[JgmPO.PIi,ev#G93鯂V<1uIoBXY+ $>=H'ҢI=ƊƅİIje2E<@431v#ԕ~'cLS^$:^=Z;'msNMa,_y-w? {, 9m9?Z_xelgHZ7`W+Cu5>G)t%#KCǩHo+8'8g1}iQ!kNm.9_bFsҽ=> 5tӧZ imRbiH/}꼜?/|FѯfXK\13#;;A:>H!Tag#VFFԧ,4*|0DG\\(T\<ƨF<|zn8T\?^KGA#mW9 }[{G+Fi4<?彿•~ʸW^uya V[?G+}XL> Si<1mQ U QK٠ᾫ x< 4kn&$7;P]'W|Dό<\8SʖWUt?v7wzolgowHآ2&Z]e/1:H"dyi|qm|OO[@e9#aw'" `vHq!F>O=|V!G7Kz nu1H#]+m[{[G+֋˥k}ps?$lALcs^<)ٴgS])(LcG9 #ƶ-wIe`ыcz?YdJ|F,XF:oی;oM* E*d9٫G.ӵ;56M6[]!ǿyP9?]M&lokzdq\.p 8R*|&Eͪxirf2_p9<MEGhL1/ٳc5q9 Tu_½2^GZWZ?^E?fy{ɚ 1 .~%׵m;R<.H;~RE*H*p}E{V3XGpqnˎyWNt6څ߄^+uu341J4(ih(/JZnqN(}NiQEk [|py5_&'1y:ZCrp}v FG&infM2By>9# Z8\9{A<23! uAھ|s@Sơ|^46${'Y5 M1,fH$~?Z6b -D V \Ĺ5m˻K[Ӯwd_pKT_ÚZEzBK}Jޘ\]u7M+0PŜUʋy^x3 {? r&Օ}|lF~;Meok77j:,H.̭%Ԙcќ?4I&{KHo(#)4GQ5aW?,x-tKr$$i{X jgZ[]oLNl5 QYܰU_fppsqVc8]q;%ݣ)w1?)n沼]ND@LX '1% GW&ٗ[-"B,ߌSZ?)~|EiM|ok'Wm]L^1iHh[1Zo [chGB.Pa4 @. څӮ5q}-nNq<׮=YSKy75 WjC-0\YFv j-\xmG'%!/w>*ExV&E;0PAU\ EW:Yu3h0:Z`hg\3F23^w~_25մdIp|gm>k&i[\]%c3咤 y^ė^-5xhߎz[QEQEچTkHi|O&fnĺ뵴K[k`T)/OcGz(E1Q@|dz6|d6{%vZ|=P⻽4@tQ~zf3V:PZU8PF @:RA4KM=9/AҀ \F:uR1ץ.(J1@=E1si *]!Yg8d#V^s۟.XI!{ {Tm@Y[$U31,d^WOYKSIkwl12`R+~'hnhQ:Y n3F= 7i:rxNܒ(K6XGROUQ_F{⫋ =%--KM/X#ھo}Vɦr7"*vU1'*p\/|',VcTwYUf_2 &ߔǾs_Vm?~(^ꭨn2G$;pĖ89)_ ][OӬmvqn`>[GL}y^iˢ{Wo2d ɓsot[MRTͬwio2$JD@+ }ϿJՏ}^7^XɻN)2.T;x~xFymLvha)udWl 'ҵσ<-aEzRbH``d^i㏅׊<b п$i4c@O>O mOdi'-ry"}MK;xm4Ktqј9SSj c[k:cJ|p1ǵqzÏ'/f$,áxFGPzu0z2 I?OMlITy~m]2T\Gv>QyXEu[(.OX`m܍# ]?^cMcm6L~cxv,;J }MnA{{S_\wP4QE ( ( ( (  ְx$:K[Ul^k.p<ɯ^cI _=|f)oSR/Oik5H2nJ9|-o5H-mb֥V.LUg \Hmˉy&\/P퐽q_p  /ߏj!0N^9/ "Fr/zgxwKtb"eCճIZ֮.21XjAnH|\c<ZQE ( ( ( ( ( ( ( o ].ƑG˘THŠbcf3^^gBO3DFFmV&4s*@Ͻ~E)N$E+>Pi@(v;)r|1~,{[FMmIfuVr{b|561Zis^XHs; >iv:6mf0 m'ןƛ,KODK RLqfUq1=*ŋp9|[Ua;lyP 3 -?N..u[ky >3|k\O_Km2yݓ. ?(5lJ$3m4q.A>x;~k&b?ji7'pUA'xDu{)K9omԅӬ&1t`JֽWUѼIF\)k kCnpU6> !] u+e v_(/nr1=<6?lHь@g<ǚkiCk>|Uķn:uOݝ>\bO|=#G^'$^uN(0Z/5}&-fkq1"90:3T|T-^K6-ȑT#8`>j=_Ai֚^6 ɹ ZOxw-I~VTT m5HfTaO^sڒJc (~p~8O |z崻tFǂejZ>&vY︉N>gr9By)hڟ$O7ѬcEw~UOj\bKd&i#x 3Z)c#HYֆAEpLi8t\  nmSԤ]rcV<+zכAik&u[+8V3}9O$gcēKy7.gAp'B~)ZwTvᛝ)ZRS$71C:i*ZGAUo|m]Y귆eŕxj%woÍ3X/u[)k+ea&?|e[jmjSEU\ xs֓a-N፹F9⥶<1-V[V6Y30zU+G +Om edV 8¨nK?i!G97<496hd5MSJm-,4-dr}9V>srφՖQcyf[x'#5FञXҥNT+E xCSti58iٷ,hG G8qDtWcbAxcIkp n #+.ݮwAip5D-u͉ PM7Am/R%X*'pGG$MhԶnbIb_-B2 zf->k~(k׮ 4Qx'G_!&KP63s95ѕ I{$fIcK+CXo3+$ێ;&]ûm P!h̙#J?%n{Ԯ/ ˬ ZiIq?ac#Wmi/n4WQkJKLYpʣ#1&6 ڨ}sexxᮄpyea0l-Ѵb][[@m{;@Tr8=\ΦVrQځ\7Retz|B  B1_c[kw> @\6ۿLY2IrgcFi "k0kM\xEѕʉܧc>$5uio7 *}zu|:[EX`Hc )V2N~E|:~\xG4] Z,B3c\QK`"__N6v.ꒈH "rN?{kWn|9gwag}n܌чizm ]K$cVQ;Qİ< ]>.B>(Xe=?hdUxo kW[iS-6]Ҙبbˍt3%KikWAy%{P[ Ӛ__捦jpcO&I$9bP 9'8z#ᦷSPqvj6Gn`Fzprz:Ŧgũ߬ʡS 5-={7gl!m>Xo>fHwe 9>M/ݔsh-D"!˻v7CzWqC ]',}F3WgG=iزc\NPQEQEQEQEQE|g)=/QtK+8{tWd..~^Hk~"k tۯ4;jqOV}+`b_}w%։i6oy%[tem{w楇7Oe&"Y'C^+У ]A+! njZؖI77Sezu[9xHֻjzXwdOٲi9)1l֌)QEQEQEQEQEQEQEWw5=*LM^uhll-mɀ~}HE#-i~4B$06.ʼbH;1RɼOcOmҭͧƑsy|  s^Msė|sjzB-09<{SnQ<iski~4aoF&KCViw:ŴF=Z[}1Qh2g4Ŝ^1Mα>zL)4S)U\s~+Cխ÷riU^bT#q\6HKN CL:\:4;8lI0Y@)ZV k\u!$g+$|9e= jzCݍ>8epgy'ƯP6hZf"][30w sR}E)iڗ5ji,)m&F1v^+}>@R7vB"&AyvcLbQ/xqͩ&s[+L@mV d_B|ߋ>ڭ]ۆEMH<38 Iknm'Eю7-ʩfx`qҽWN8Ihb4UQ-tERQE-UӮ",l|GӭyMW\n/[kD3/miW;a }ӌֵo :ᝦ֬c$aU?W.Vk2 fB2T {ERQE+ W8H$,̤38 '? -g +ꖚ<jG)-}ݜ~;b/FiWpk*ْ칖6Bpt>T+;1]<5jV>YŲ1!q 8ShԴ|3uiEu RAt'kK|jϿ9i_ tCFuV-[sd܎ZGWn'4 <`ӱ,u(8R.+=XCMwO.$˒6BY $ ׹qJ 㟧Zޥ zMig]CnO{c6E {Qֹ߇ IxZŭtk=Ie>.ufn{WI'$qNdc$G ;,4ycξZXh7Gȏ 9sr?hk2zufV7)!?{_Ql ۨ(BGB~LIXo?q >QIR]v?$^Zjp=Nĺ7<9xj7: .V.ޕp l3@y[b$ᙴSK&mF;cg(ƪ6v2{י\6Efwa(-u6p>HW͜=1_\G.j`~zO<hVޡ}ek&[ x-b;N?)Р]MV񔨾.ઇXg}z#dciTUxt9ɷKZ/bWm񢾠Lއ?EIozٷfQyߨ# ?LGR<䜯YQ"M-F<^tgDw/cJ{Lqum7 ӭnxc^$xK^NG\mia \ yi>xnVMbm5弗,nOj>71bɼ%syͨãa ;2\sڷ+-iΓqêϩE#nLZLVQ0jD2Fn§x'aaw6&2kd۰/nF3M;y#өQEQEQEQEQEZ:Wd9mmkPI%Tn3_@?<㎵ǟ%ˈBH^\# 3ҥ?ZKzu  ɎDwMYFw(Up 8ji[,n| ۖ;x犣{BD`:,YJ⥁֧|@VT-,e_:ótQYNֺ~, x[m5e1v勠;|'OZ=DgHE%U7ϝہ|z xS?mWKymooBB-\gIUk}[@AÃYY%.YZDXk =cZōխtӦO.+ `7^|"ޫam}v\ɽEZHu8BȨC9\>&u?K]!%[g@m`KMӿg[Ki=Yq{;t)$z{NмB.=xI2zڀxߎ[yK0 ~yk گhZG[IǓq܊OqQHaJ{R:=gx_|EejW+5ū#e?Ҧ-ھjcxpE)O}*C_]CO\w6ȩ%u$5bž3uMQHgkGb'SSɡj#hHaESl||&Cl#6·qkֻ{ sןz|7 1%wh .6ץoi쌤1 m¯J;q-€6jʝun8:fv-zS̟ZXz| @)v~fEfz wq+{&nؤh"6M&Ӡ_$NkfXaz-A ]]p|+ŪxSw:$m2m嚊я-*5 >??Ɗ࿶'𢯕7??^?ME8?h]υ;3 ˻o߈~NiVW11F?ޯi:'z$R͙a4`g\4gZGNjwwY|"{ZˢA3m$ }|fI#etae9v5 //u8}YJ.Yv}hGlfg.$O69MWe+*v8b&j׭_nKF\#=+l{\~4+cWxOzuthV\˻d|^{-~kMQeQ%/\GtL\ῒWčKѮ@O*\'$i}<8#}n8u;?|Y_ Eptku?cg7q9O=K3:<a@@8+hm h=xVMIAZ>+MѴK.Z;9@黃Eix"> n'{k)"F,kSiX `.k ʟ.|Ծ!x]jc/jJ_ERǤEu$R%WzUtoomTKӘ5‰; vS<g7U޿5{o&WE+Ǵ#%2zZ #Kn<sG]A|'}j|w62{&VI$ĕ'ڶa)|yCVR$!OZOZZ((0(((((@y( *&\ ew;νGqiW6K+K]șA/v2/%xUY5A7f p4QAc/ j?qgo/~)72qjnVA#Da1k/ k/FܝVϩJeq}mfq ͯb+R5']#0yiD.߻>T״Vz.#  ^Hk] ML\#Lw|Ò: |ogeiⲄGW<:|(jLViJS{S,5~86Vۺ}Q{MFr2w8 u kz}ԾӾ`% fKaNqGN+]5 t4mcI:R獹/ u 尌\oldl }r}w[([ (`jSgZlw@OAۏ~ZVNCoHPmLk̭4 iQE,#f ;桏6揦{YW^8GONojH1_ǏpO\fp |uc謶~4X܀i\}i ~.-mHɴ'xנ˭:u^F2]d >odid, `VҲ9WO7d.F7{X5HRk9)d9ThBɕNHNsU'%Lr5z-H(|Q~}Pud]2P$~GD|k ,(;sj<>yQNYRW|N]ڶ#]}SźJu[ !ȫ H~]}$u.b*AfJtޖUwq?UZtE7]9@&+{rj)_V;^g=EB+oX3UgX=]a,6\\r1͏'9+:픐[mWa/y5RO\TVt]V C 78*K_k~K[x8N#ʓ{Wjڊa`cW]X[kRVT:m]v>kO^S/L{$Y0p+n2&H%͜}~\">J571a*ƥ+X_Fд\u<.r㨯/L82dRuE}uxzodI^k\UU|SKi>RV3 cܕrZ{r@<wzmI{[GiꬰJJt>wuCD Vi+h"Lcmpɨ/Η:68D&i#9R1$y^-𥆟 Նۈ "379\ ?J-#&kgeLƓyW Ta&kX0|a#[ѴYi>'/uVO-$$O&(Pq95W/>.x~J;&<Dc g]#'AySO[MIԎo؞B +v%'Ŷ]iG;Wɍ| pqJ9/ jO[m.aso F)0xl=:-aY<)7YmqLfU.[PRC$?CV&o'7m}z~/lAizv_K{Hr9෾*^5y3о,m jWEZeR㑌ՋZϊ6xf]B[ɈpU`ȑIa)>3^t%aŒɉ#<~8}m@ <  ݞ65A~/io/Z}U W{ƿ{$])/-Z+x_jo3)sm[sHZ,ws @@#sI_kgw--,^m>dBUv(I[h|}WmkpE[`,~x+'n};\Zj^kUdQ%>xv`JA]A/Eݢ[ 1\H&&֎E ==jzXgzOg..[{UD\"JٖA۞~z~h4J`ovţGdKh6aЕj[880bF:JVIExVj^O51.g:z95kNMz׭mf{KHO˿#Ezm׆kԗC{I ;q'߭`|2vMf7t\Ml]rGBR}IEQE ol Q+wҿ/?+Q}UR mq~nֶ3*aVdžvo3S.2kWZKTq1Qeq=J6;ſE΢f[(zG!muP,oh*d=wsӚKENHNwt $?z\J4qJXГ)?"evsu;2!}OZ7>,]MCC[HՆIʷp} |"<>SO~BׯIl5+N:dzW?~s%Nc/47F[)2 ~WC/"$ QX݂3^|%yմ;[}HNgBU`0+ >zOQFǏ5jn_R}tt:ؾ獟fv~\sW3txIlm"G_q\bFAѹ?|:mmEU 3F#Tm<;gqGbyHQz>X۪>+Z\ktl`wASVlҵ%hgCSٸCć/R]B*^pAnqd&U㓚|fLJA4VmEuoz7E]:O£r.c~uܚQy-xZՖ)-Z#4%~), J߃NU,σeu)IҼ-.a/?oߺ#LԴ֊y*zx>!j6iA]]:nOXL冬tg?c۶Bl+4FChtŘ` 0xTOz3ߕ?/QG< ' f}6[c vZ=iӧBJ;rO~7xU-Mvw+' j|W?7.|6Lvk2ԣ/hq~gٷfy?n=>8#RWCAp+~ºd[Fwf>|o~v#IopKL^F ់?E5=[IEi$Ck&sgʥJv ar+ k4{.gЈx֭F+M-4(jQf_-rgOW-HSm+ 5r^Ij%S/"u8ԯutk߂ z.T"GPڳ a _Q/E&_=1{VKimkmԉv;DU熏4Uejx8Mc/wl?-TlA#AXkf_6ƺ;[ XO}< qy> 3jK0_6",*럔ӆnF3²`~UjWU`a~+oxræQĠ33A_,>5' 7 \,&8yn{5+ڌ-r-%GyZH@6<k5WPy/Y˧D}+^џzty.$3 ۑ#85^o'ý;1F sk, |#Ї^NNӃWjg60G%F9L~b@$czdo WZfbYԐ7.Y,ڴ==eQw6 ^:CLi!Ӓb.< >ými>k/-u9Q ̒=2_^\nET?_} 1G(&O!'֗__?s3—W?cWE8nD~G v ?ux#;?~~_֏ڿȃT?_܏/]^?G.g}}ZvM۵C~^ Hhy/ޡxwn.xgW$(ba_-=]b];Gs,Ьc#)5j6nҏ7q!Z}̚ hSI9?qC@{y[N|V2vt#+UVU\;_n]^ݏH<=ᦸq%څqц3ިEwRO [Prvȋ9$8#<xgK:]Ci5”;!1,s{wx[F5 MY98iZ\XX寑:u{O" hYLr8>xst=n B-c[!Eʁ !b>Sڸ&}oF].m| RaI8#Ү =>OIVIdlFQ9Tjb40{#KNmwjk#X{h-%_VUƈxD Mט(h湤X4{8f9vې?A,5}rmd[ n!`@W>kYП 5kG_H> _]~?_Чkv#V# W?cQ 1G+p&?j"+T?_܏]^?G.g}7hݫ?JG v 'G>۵C~_C]^?_iņ?nD~G v ?ux#;?~~bOG_APG.g}8x/N ouǗkN}k%=+;u :r( ~u?OY̭-L0@񏇼=jxIH6%+ 6:O e+;3F7G(owrj֑7Tkelej:7z]_U#áw{¯{:548%d]\#;6#vxf~¾4ho6$n "8{YGzMϗ$%b`w +5֔9Q/i/_? Yf+?|kgoKUGD?P1obH ii57K$;Np^4cʵ4\;唜ޞ^W>GV ?ux#8?_&I|IjZ}urZ#``15jNuۛHöJLvVQTxsZV|.g}O?cWxoj0kڿȎNr>O V 4n sQxlj[,88 >AZϊ[뻇O{ldn%9Xt8KmGJIIJG2mќ oBM30im I Xg ֺ^mZ)ϕhpChT>|bQ ?1YG+@1:yo_t/G#] ?G.g}/Δ~/ȿt?3¿<n~[56TCKK:b+_ דB }2dOJrxZ;d &UUA[8]&^uk Oiom+6rB߹W#?ZE$3 a)=4#H.Xى+@<$tRnN0YMU1x}[eQǿH?`Mywda`|{V?BgvoUh{94< 5/>Kؕ# qI)]]Mqq{VSt8'|:MF_*Cյ&,HpTׯ5-T/Ui +O\ӮͶ9Ex7dxK JjUBU׉.^{ vlysd.sejVŠz+ssp3"nS0PaZQwfoh#?c:{넶w<AZss"J]*_zL/Z)ɸqM+agdy؊f}/!Դ1Jp7~1_|}ZnFL\[_ZFcJ4OR&MQXmke2?XKq]#93oj<D,u[BPǕ40ۃǯ8S|.ELE {#u E O?e= O{MG2?WGpȇ?SR)x})ب8oARSQ ?bῑ7~Dژ/*xA"zjED^o,/m ׎O^+8SߊiZ'.5 ^Hx,22yzQ<:O2=8|$^Do@n$cH߇ xSizeܖE^EPhN~23^-~5Ib-PT 9\MuxOUMR10ZA*epqZr!ic֫3XEM ny y$rc c߃ MmȲ7vytd'CqKZ^E,pbP<Ҫ)[k:]-n5}ٗ<;5_Sæ҂'G?_s?;\Ӧ-)x})ب8oARSQ ?bῑ7~E7~Dژ/e/Ͻ5^{ j*(ژ/e?Ͻ5rx'ᖜR^*t}̹'+_ S^llei,o细n -{۝'IiP[q˹Q5?Rl'uU*})USWQGpȾ⿵1_yʾ{ j_ew_Loy߳7#[qW?mIX$:s6[xFnZQowE!68a?J|]VZg:ƛcS'KW{ jQ.TG >W7K<kyMG2ׁvQϵ7K<>?k? ˧tl4"?174rO^X7j=-vaph8XF&~zo]Wĺxf&fV7gcƨyID9*o6e!FP?f47 .h漼V/P龧AwM#rkμM LdLu6R4pwnEp9M[IhB=WμI6.d0I; Z g\W Wk6K=k՟vja -U[#Z0s^x},K7ZKc?Vg~j6ڕא*Њohl2% |}xA?-KtoeJ#KQ+>QJ[#|MoMn|Q(h?~(gYChr:Pwp~>EiPAX^/ߛIC,Ce bXv .Чg%,ui^vب0sGR𦌨iV`tǐ]έooT^g*n;O+'pm!_xMwkʗQ:D2 [8^3O*ygqy-DUIcc44V{s_IHn8̥TpNW=;V:x],"]X]Eh cG cI|KᾛbkhaW+9ʷ=$fqz7E|'7ۯnAo/o7mved$VhSg`ZuAEP0(((('_/|xռam/1!^|;d9";- [Nn4&Ǩ2ŀD4yՏ-jN]w6wwD1*O2 2Kk/M0IT#1_Lz-罾uXlXm< B>h_54ݡ A+7x8Ix%-*Tq~+|EX*Nj- x쉛ICpCLz׮_K#ójWmb #h8''2|@i> _@ڟdh&bGJ QӖDgx"}7|qIOgqoMyF^0Nzײ\7i4FtE5cG ,79t hblcQs1KE+QE(((((+>:xŚ/jGMWms+SB|R,jhRj0=ԩu"'QS|t2Vtkw^iFJ+WYt/12E g Knej_FOy-EhdAEܠ–_ڃzui6v1޼eD_>٫]h?٫T᭮ep05#p >'XMe, #Gy쓑X>>i uKKPH ̷)BnvN T޶`cBKּ~/'Om$݉yy"Hg5ް>w| X٧+㲨IzEBm<DڵEiuu>Sr|ʼ!p;ףQx6yb.{gSJCv((/ mapSk7Ķqskr[We/q{}uoh/ԭҭMIj_ajiWZ;YKe%Mɏ9yn'RRR[ (`-~w~ܭ!__Պ\'A?ߖ%]5uc<:rq]͓ҹ pC:<;AףL/c"HnP0ﶀ`շKIz)hZOΫ0c#/1߭2Qxi!Wm>EtFs5~px HP3ǭEn|čp78?XKԋrP~,T ;KU)P\M< d+%)M{Ý2 J{T-hց)8=TOc=i2yҺB31Uz>{NK+rmy$X+V#u_cӚÓJ˶v*{sI"r<]dngq6skVѦ64NR1޼?;{d6ʈ#9?ʾyX]=xcA ڬ Ig+%ƥ]ӱA2 pX^GmY rR{rki][EI =+gt ꒅ=3Vײz1\Gm r+(=s d95W#>vmj`:>ƾ֭n4,M#U Wk.Mޥi #ǜ\uWэZ~:Iُlj|m{_L3@+WW?u\Nb{zۣa9l~GO4 ZïZ8 ,{:bhF[O#?_O"]'|>n5"PElv/ {տ*_a4X Bחihfc\y8ќ>ב_fv +1Z:oΡs#y\9{)4Ͳ dk>chdP&*7|ߕz/J=+^Wxo qhҲ4w窌5]?g:?7>'Z.nkYO LbtXI|#zzV|k(+T *Uv<Ԝ{o74{MB EZۘͫ|)?u0̶}u-7]V7v=s]/kN$xlQQKn=$c ߉WPѭ; RsrMܧ8\֯>㯇WTwiM%6NHOc21NO:aKL(((((n>5={P^64y Gkm5g88'_A,LRߎIۨRn"gfcٰxQB\JxbIrI6%aqor>(Q}Z~mE $V۟HrU0JԵKĶo`s,gROqC؝>-χcwS+ycc]Ÿ'nWŰ_d%xcyJ[yp[XPrS?Yx_-`Վ=o52ELF 9X=*㯈GȾb䳲`KK9=([m拣k`ԯgj00G.g}FY}}9fBAaӚv tQEQ@Q@Q@Q@Q@y__7[:u3\5CroRyyֽ 7MDn-nQHyV+^V1h=ORz׋YY< !%Tme֧-Ɵuiwgon"5\d;,FxJ?1iY\^Ηp0KP︣BI+] 6īkRNO"$ d -olMeu{uu)necPaf|B H#Ҡem dk5]kYMY-ɧ<#JʂL 1K=G_.Zl̫}4xћfOBt ^!M^E\tPʦ8V _#‚W&?Yin74349{q^P_7%Ο-֝G7˼}Dpp${WGrźOtin<'pT֮DwfwO.%RKfWR[ W|<^𖟡q%VqYz|4{:A*4??ËIo?vK)Qx |1I~0iдSRӵXmF2̌[h 3x楹{ǡIs2ޙ%'eޱ޲5ŗR]hZK(]'vF֢IVݮn7Oa  %a>pBf|Mk?[6Q˲ݣwC]ï>?LVe+sir@x[K{ۭ>{ie@F%x ɮo^(h2:MsS@c*Fȿ1 r*V{A2={w 4v2T9м/A%گd10&XW[jѢ[ia v#y$񎘫koHм}+c:D=rʱsMl&zpc:>> ( ( ( ( (=+CΌ:Lz˙nTA?z2 çJ \ljnrn*3Ԙt>,r-@Y˭%2&z?hjEޤ7P[smٕIll< U )ٵ]f.e:~1a^϶lχ5fNděp ߶1FK{]+}OÚ]ڐfCsZ,)gwڵ"[mm]cO2{3/־Kj$^kU B gz4~dzxO!l͡g=sM'{ZΏ +u{ȓ_o ִ37Zi3|@֠(Gc2J~id#NI7|=oN,uw60?*q<*R,✣د>WKq4\Φr>U -ahEo-͛ 37]׉[gW>LZ7צ'4dBYF9ա=x"F;S)%dx*Gj}ΟxpЗ^Ze:@ vOShccXc m0Qw𥟋Ò\LhKWsX z je=5Wz֣ꗗfaz!6Ǵc1C\7&+C;VQo\IjmUa^9$t^i? '[o[\MeNŚ3PnDJnk<*GDqpJ1 q?Z]:Cz~ou{mFFY8 +1hv+wp7^ rMCg>hߴGQwEueدOUtWg1?öZ֤I/4ϊnBBV 74ݾkRXiVhbHs'r1+Ѵ hƵciꉾ+yf<:Vt-ORҬ5 RMF;ܺ p@ѳl7V= - $r=*J?w:OH'3,2n;[',nAExV]R^=J`Z2)n1Q{~Q^kZ'Դf[f[sg#,ow @(5:Ķ%# T EPEPEPEPEPEP^MDfҚPy#ML&(?9 ~"Z~p\Ow ,̻"o@7~jX< /}q7tӧN֮wZL8H9]߄K)GISRKF{dwp:ס?Ư G1Op.mmCN /KŞ'/miKi7+ɹLb2::v8~vR^sꁴ`_P7Ih%5*f6:S%Vq5i|d^|NÑ.VoWꗚƋFkmSܓF1+1vx =P]ׅ5L6E|ė}f&6(k],m*$8+6w=+1 jͶ>jޭM4ivO_^"@H)hk:e9V c*~QrBmT+OVh%ڨz7wT ZKݻ'j  _A[FcSbE/EM7>vø*uZƅ-j}Nkŋ?}G]nSb_ _kɜjߣY؏pTkQ|twkʎz|/iH`s} afiZ^o-6~޼^:xJ湃EŘQN|=k_i_%wryVUbWpjIuN&/N}W 3Iy-K&fnȊH'J]P ';r/Hgi6F?=Ge믇wM. dOKp\fFMgx#7i1s/N5>5"lIٞ' 1`bl/?9 1:V:&fX[f&Rd~thxoE!<;^ѠF+r#oUC ^C^gbcշgjھ}o$x_-V;s=܊"LO;HZZ&=N}Ka{ /724b<۷p<؛G?xrF&X0`}^PoX.P:O^`w5_zŮ5O&m 3l?dwZLlhܵNsW]K֯Ya NI`zGÿ^|[uӥ$.;X|2f 7n]:WkRQ-7z  vu u=Wςlcw麽ޣc;Gm; ]Y`[ɮ[`Dq cּ7e;ϣ2 47ēEt|! W~=ݏ^^hZj0M(Ѭ.AF"5ov CIlmk4+pFD&AJyR&cE. J>$fb5?2]n/͓, |21⽿z巄O&ZIkM;MY^ONٮe5尷<5v}3UfYrASG|y~^P_ *Zi|lw_B{ŏxLִRK oJc-S֒Yz^9}:煼M3i>01y8_Ѝ+9EYiV^iſ3Xw@}ԭ&>\7sivd7wE7"k:N} Þ |f@4CXIrMv? ='ysu.O] ;,ŤZ%۽N/5A5j_|5.P%VJSWĭOL<a-GsyKI&VMrk'UO g]i]ZOERJWqIޛ~%%nu Z)_yД@Xˏ:iI_JUdcHFLT11 G/."Yeܸ#@/|:\XDpe~劍1{\?)MEu f maTm~ls\Uk9O rO0 ;a HZ=Y %K\B Vt%H P~E~>)7w+K-sGQ؜MtWUbbwc=]ѿjzN[{X-]<,pX88 xiP{8Qt.J{ O\} a5xF ^#ELǼ[\V6]׾j)rKݵC4{Cg % , xw_ 4_[Ue,Wj)\pqToZV4wzF+XL8‚rqJZ^$a7Hk;^O}EM*$`q/=|C,E/7bIbFr>#X?g;iUK--{ɋ5f,ᗁ5i_Zՠ !)'O.PE!㖑g)=h܈( < <3BƫjbY}2˸~ɑ~n(((((W3曩j7vƍC1s&~#xcšc}N-:sgsC]Dhu1^gIþ{Ƒgj"]g8zT^ 'hZ.V+ S8lzm[6G.n1q|'h25}bh6bn #SԐ)I+Q~ x?4[T1bЦb2/l5 Ú&K+ȋa$eG$ISIzi`ȖuoEUnJFAX/CdK鉪n 'Oݻm\]Gk4 Q0UP:OjmXw$=)qdxIRUNl!>8ZpȳFXd9z[ }Q@R ܏ GIXmq'p{xJ.uM.-"R&9-Оyu:5š"D=7?47K?k+IFBRJ(ئQE1| g~6|f~>C`Z<;R~՛qWW8Uӭ<h-7\+"JNAּ],?Zh=՚X)H[Ev$O^IqqTԼSkxH3mbodipY"XT g n&M3_J+(scqך ;/Jɛƙƣ)XuꚆ;HҼo KsjrO~/LSß-ėͶf+ZHĂ=ų*[89_̽5/gA/z Ϻ#W}ͬM_4Ъ>^ؙi0Tgn4}Z7 [os%Ѫ>lm 2E|o#b_ cA /ҶZoS|3a{qqH?E0:#p0d>Xx5[Gu[-vjη-vͧEh5HmVϽ| _ cOvr xlJ1cW7ƮἾ4GKlt Go??[!:Ho??[!:Hsy|iƟ-?R1F+O4G7Ɵi-?R1F+O4G7Ɵi-?Q8_8j6xNeFp% VFA / _ cĸ,K.kP;he{ FmiBjwd?z$:2L| :W cOtp=OUQ\:M,RK{V^G!>i8oYi,ҵzp{Y]ƒ]/僞KnLښhӐNƟ i-Jd{UC?vơkkW$8ٲU1ب|Ju7rMnѨ7E1RB1 OGG7Ɵ i,> xGKW:vt#ukwtd(~\xiA-/>(~\y|icA /F(~[yiA /F(~[yiἾ4GKlt1__#:Cy|i#UeiXWzqKe`!=3W_?%G7ƿ i,>/o\O@[qk|mT8w'z h5x/Co|Ms(4ue۩[Pww_':?~5GK? ?n&=bUQzQ bᯊW1XjO&YGHn,|ϸ+?o_ҿ[?%:-{|Lܺi9(1qz@CՊNO[׊ݖsM6a`J6 _y|j?5OKdtޢ? Qpju,f񬱰W1 0==_Nx.Ie Z " 7Ư ,o?_Y?RF+OpἾ4GJlt1__#:?Ἶ4GJlt _Ia۫E{2daFx0+Wsh^ԵI[&X Ji9c?ޟ#:?>4GJlt-~bW7Ɵ_-o??[$N(˴3GG#vz `~W4{;Do\mrfEOOkm u1ڀ5-'#W_;Vj@I#W8t曎jE(GJ\P:Ө(֟J( pB()@S%x.|Zi?֊($?icyP88EcycyP88EcycyP88EcycyP88Ecy4CkE}}Q@mae(,_}/ (pQX(Gbࢊ>e(~4_hE<֏E<֊(k~,_Q@b HqQE751E}/ rZşb(Qm*ݵY(kOp;w^_QEwb(QnZh5aVAPSPڤ^Q@ :Ө (zEQE9zREQE:((fotoxx-18.01.1/images/unwarp-closeup.jpg0000644000175000017500000005375113222767271016630 0ustar micomicoJFIFHHExifMM*JR(iZHH0230P0100Fotoxx:trim_rotate|paint_clone|sharpen| Fotoxx:trim_rotate|paint_clone|sharpen|http://ns.adobe.com/xap/1.0/ 600 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?~iR{beqqUi'+Iԅ-~+CI`#|K&;ʹqE.%;%0>S+cOy#E޼ž!Ym*۰N}qeC,Tp$xuZ7] %wEJ=^2"ʳ բAnIQzw,~t''fnJ-hUze=$)jKB;kH!bE{frv1.xz~y U$iMߧ"ʪyi>emO {Tm$ '4_[e}FW~Uz?5 _}ԮԚ5ܱ2+A4ZFqqWlձjBwn6v=+?*2:>#[I yb\}'ޟՎNHB#HV1*QpY_AQpit7G7Q@"y DZiK[m.ݬ>e QqXܾ;!?uU<ʳmφ^I{v3rVaTq\j0{+*LTQlW)LuAA"R< jw񁊆qlnA]q+$u pE.?nH3s,#oҚ٣>evWmҢmsW5o|2I"ϓ MTQm0ǵpW=zt3n*ǑVE\VOSeUkpGAIk>LxQas` >-.^HWjo?)5*[Q91hwh x ~c5NVUٚ!HAf&5Jrv |Kfψ'cZoK3텖"dV;F8O\s3;n49vVitrgc0B0k׼/s=k2,7!z(ӈy^<_w\# \8/ xj[[qxRXF䞤]ՋG=Gzz?Tcb12ϞfxBp1Rgv}o<_wGy^91%GdsĨp@Nn1E/ J48`~;ף<_wE]tfm.xSe(#޻=zz,7Jt[Ki]7 '## B${15‘Yv.3z K۽*0[]l pKp}1^;ף<_wE]Ztkoi"I4n:T,4-LEu)i6 g#$]gy^=pǦi1AK}@j7ǽI紵ձuu9&:Wk;ף<_wE]|>a$wrr+.__,񷎙 c,p.c"ȼ g ^YxOĎnյEvUER{*߇fԓVu)/EBH}?;עi6V?+3<_wZT>E.rsvoBA|,fI|¿~Ez8VIDim"Ew!2`bN?x* !$_TRl1mwmG8g޾OAK.pTjo{Q~nOП\DDCs(#jFU&W[&:e~an񅄣JPc kQ\:v7o}#*(1lI'2p *u;Ώi y?h_9T+ᕇpTtj>/]ZIuf&Kam- {Շ'-.m=TMOOf{Kı3)RzE^ '>,7 .XI ; ;ksHԱE?WEP.ܼY< 0Q nMk)VWѼW0"H3Dº2;Y&wnFeKc*`\5s~[Y5,f$ѐѐ'<\j7~9ӯgè)t0TN8K=Šۻ"j:EΡ:tq^(ke lcZٚ.zZ`~%H%yNŸd}~3R@P2IP[_Eucq ͬ9p02:^ivڌߵꪘr3;G;W2]ru2ڬwBz_wy"3\]J}ĦV{pKolU0$(9=O{g=nQl=O | m۸;gZ> 2}qXmlgAUn=ίcs8,ؽcydPܻ)PK}qt;VŻmSW[+b`df叐#$ 9W#֯ml ar3]@Qܒɫw}yˍhƫ +y [d1]lju+J%u-.!p5iP3b9gTVqFt/q4j7 ZXqQBʟOcЊ~jr_iZyvF~רcʬ`r8;WY p&e--q9Uym?~!}mh{eZv^֮`$PCyh Xy~a=)څljR[Xn,n{vHЍ0]YzP{EGݛ H  *k0.p<26ȇ )EBHaڔfsI9HsuB/U}gJ[xDv*ȧ >>][Nuu*959)t=GzZx +esIs򯌠m:+3&ndDҥ)9GQތez%0+У6ЬgkcY6tWE +|B9vkC8cR7.L`5K XKqWAkj㹫ϵimqfSҘxH ^sy9Em?δ+Xh!e _Q #:jIO (l|W~H-v"/5k/ɦ3n}G';FZѻHz_6]]}\4^9|зx҅hW;AּVm[NuP*+9){V'̯xijZtpET1dF9>sRjEёm$ ~nI>7Z$`̓ T[2+ogž3w8)=u?ҹZeue.^^r'^[E0Z[l'ִOu(OS/~.+*ç`dEsPد! B+>>sT.&JȊk=7N2mz݃Y5_hr9joDi fwPqA( VM܃S6{|iq+stjmG=Zn }Z-NRz&`Ip{7 tdϘ:3v\T7lx"ȟgs$ N}0^pJT,dK&0]}S;;X;2 b3tG8*T>$枹WK(̹Ykk>ܙki\)Ԛu;SzX娵3ksmhL6qbIɗu,W< WO\6Tt9Ki/2Jq#)KFr1re\]%}DΗ/DGlO"o@|l4>Y4˻ Ȯ`{tRɾ!bd{YXů//X6-YTnU9j5V>]h/lŤmu$"/"etwvw\%ݽHab9%[UluPմlCr$X8;>?G/,ůdx།n|drIݻd\-bDlCC$s eF=b]s/*8'eΑMZHtmgM&e%d)8=c`'3h;y6+o0 b6xEyv5 Homf[> hܦs¨,[X3|`tpa&>&-.[5]6H-2C?.9뎕{J4^]i7ַ֤[L#ܤx_W癬,$L IϘa sS'MŴՠ%Vo5 NrУ}.l/tWk]_NfVyBaU8b =*_SJMQ%tǓ[yx m ~SJՠ{–b]1qqp͐Ns֛ vGG|NVG*džgԚԴ,8R9tR Nݭ28cykƬUQNWDOދ~Gjת'+F {LN儠cgz׀~[jOpÇ#,_{u=*[_}RvQ!=ϫZnzꫩ+CnxQ[e-Hƻemu#tֺr>TPQi*Y`K{4F)ꊫ[ݹWr-;党MBD!f[S$@Ju/ $z ,06)Vto9(,+mr#sHI pA"c1< T~9y䶾_Z~^pO rGU_ \{6@Nlv;P8T}ND ںz8R)=^i8'< lJ15{+Bz 5&6p lȥA);[oBlJ㤲g>ҫYj3,SB>jF{K:0wGpczb͋iK#@]c5vtajt{Aswcungia(qg%'^FĺՉ[d `rOs^j+|vSY= IGl'[W[# k6ZKkOٴ%5czTՍE:ѻ3ծd_{.zO[浝D2!GҺxc̙(#CYֶx~m2*¶BkJ*s0|TH8⺝T2opVw6lD sR}kS3/֊Tg7hxUPˉ]a+f XL\oCJ1yX׹r! Y,@Ï2cN;WaY؄/[8aJKɸ1'I9WU(gw7L-GZ?*cl:c *Ε2mh/ebFIz=+B@4 vQñ4S9?dӼ}Ӛn<#>zCirƫ&LWY!`wE:O-RMM%k$i f-!M JIh6Qi $qźABڛ[wiv=N[hv,BΪSО3Oêj>lkfjb&FPYHO^Z苦\Aͤ\ſ6e<˅ 8CSݟuC{kn,(evo,[KQc0fDxHzeWY5([WHA+2"wC`pzпWh,Y.nK5K=.&K R <砞/q};U څ[b$@pZ]׺i-ҴZl& |eY/ARMF}ŅQy9?B0wgsVy?;fCn[8Ь>Q|ɵ4O[XKE UoN޵M (Š((((_åw* ] t_jJODʊg^.kzRkF[1⤖ڽkëH[8ng :ێ}2{tVHd|hu*+Yfd܊rAؕRX 6 'зW5۾{WtKCZ79EEisգk:^ӵۤ Rp^}D#ޣi|S֢MMŃWCX^V\f,0m~w6Tv1bx+8aUz9j3z2i9KFf17aןZKL1V) =+IMcDdV F*MBe4cs(IȪX`2k 8(KBP5hZƃC}k#?62ts6FAy37Wx~⛍6h5$y>]FGOj|nAr]R5O$c^!RE 5Χn$ko?_@|?]gZd䆖(?N? 啋+nņXw$[/ +]Â9&Bca@;oZ md73N+NgbfvVtIږeXL-f[4qZaӔ7)"y^r w t݌)AjvH SCpi~VrlRHk5&\1^ 4(Tl䞕4<\ [;edl[J G$pzWr}E|n OIfŦ$-I :+hy͢)(((((((((((+GO2?V%:qgt A/FAI84Qu;N20#akxH!HEHBP:*Sj`VUJ/7-d ^1.y\} |a'y-=q ccoa׆#{NA5yPS<ң!eVeWIJPzؔ>4 <=c.\_Ƈ28*WͫXbVtj:Ԕ籄sԒGԁH'LnU^cC򏘟jmHT >Qֹ$ȼ.W5MNK(# 2IGmwv+HܷQ(l2ԯ@<3@*ke@(lIm(UTIJih<hqLzqw. B j @I}H]JۤNQI)ri2\hLXGpx(%>X *7+mh5)XRG6j,dS%$۞i2#A[LߐoᇁfgٜhvrH ܠ$CΊnc<5L[i@pzZF?y ЂIvjVFW'u~OL\ֽu-9{oKi33Mh& /r+Hُ,I3BkYFkԤ|W%qw2[ L\cA~$wWtTW; % mǨ_Q\$.ݷKuPEEs\лvzI.?]o=@IqB}?$wWtTW; % mǨ_Q\$.ݷKuPEEs\лvzI.?]o=@IqB}?$wWtTW; % mǨ_Q\$.ݷa4-Ƒ[,  퐞v ڷiR?ZJ:!P)$VCSȻr7jrDOkLz&8]Z 220#vnJ?Qk $ß?\gIsIA rݢU憲Yf`:JqnC 56>B0xj)i8Qֻ=iḿ#5VKX8QOY,c-$F1i|X͟24Y}o,t҇/sg°tQGej͠٭/80[ + zQM5$[iiw3/fR:G<m$ =p\~`TV\s[\'+eQ \G GQ\M=3^&AI)tQ^cҤ\ n HyjRTQ1(Qwc_4#qjܚ.RsBO],JT |ҟgJjyʰ:+;j:UŽx=]DYL.sX~>3GU91M" "E*qdb} 0۔֦SY!!%V2j]2C`|3gVMOS~ A_kW3\^۽Znw+CMi#pLrYʿ\W?58#ҵ 8X2׷5iQX |) oJgIM^)K;c2ڡsTn]z\$^6{ V{] C2Gf W)̓.[Ys{yM~fˆ  گ .ۖ*~mTyW_m~[?ۖ5Rq}P}tː?]_XC{egcۥU3%"9>ڴ-5M&js\X7&*NsZ:Cg%p-Ѭ8qŽkFكDeMEiTܞ%8<,5'xKZ*\-sHRwLp2YLq}/ݥ$\i]5ip Xሰ,͌洌vbS~φaidW$>o¼wo8b {>do`~-ׇ1p4$9elՆk+.{KsOWGC$gzp {潋Kq:3^ehThiIJ({"E{ ! @5ql I(\)950:Ӛ)Af:5*|֩8Y6iԍzɬIK&kֿg4 {Ei8,T(5{&]^r^Swx}Oxr 36@)( ~f$W*|T.rS 56% yPN7k WLIt?R:a˰i_'$0pn#}IvzlaVXcFp5g~/tNœϥjH~uaHpsS(FHvmmi Kd~tw][Y\-̨іS،e[֯R3YZܰrfU \/<%*}wR;&UK|k>iԴk+u.<= OV7;khQ5OvVp(HB,rA_Ej ǂ mq`ۣY&=?\ҒXIL8_Km:ޟW?E#}x-O\1\ÍP63ZbB2]4frpǜ#[Ue!Wz>hý$jAT) JqҬD$PmR8$VRb/#'u?,0@[=WikƤk減S 5jIWNss9qs9{J| / Ίד ndvk> jΡWmFJhSEn$b屖Im3,";w`*^g>;q3,LŌHqx5["bP0+%!c z&wi%?NmkLFYy$FAރn01W!"@oϿOR/.w69m: }MKg ܳHn>ɾYdS={[+F'TLEOÌkM3TZRDX[xҜm+#ȁG|6?y;KF@hXHI8&ewx.R~$9g%$ p' AߡڣmFm ZU'Q ܴT~ϋo8ˊߴT~13BԶ7Rj9ϖxSqU*r7eYZt7wNpqmh#η?Uo1>Oj`e[|~8EiOd#\}!ir':^aj>}Ce ')C zĘ, EIy:-%u +2du~FiCmb.p⽷C15#BȚb#\KNW1,:NW\UEթ15$b49L0m ےsғ`2d/\1hb] v^W_A[-(RkŞ&bAU2INKlENHn71G?L!GMW<ɫ[ۍA/ ی yb{_ƶS&dL .yV>T/Wٵ$xn;&.dB>r[ڿMux^oZ^x7s^;ٞѕ4YYm5 1C HBµ'=+/^kXޥ''dKVq1e >}c~u?0͎Gpvzq\iy)d "&f1galQwNN{z?Q.T/۟s_1Rh?%dq5}a'Ld'5S:U2ޥ["epF[,qGҿg\SA M,.2¬H#a4a!TՌTfU$QSV?TfU?ٟh3AEOgZ>@U-btagf$RIkS}V9}N#ޣ3gd ȇЩ?H}V9_CMsYEۚ Gٟh;]b,CIq8>lm,pƱ}fU̖4. 6KI%ybn5?}V9_CMsYEۚ Gٟh?T?$SZ_Y\ym0?)ufUAtnn/)n_dj89gn["o:51tcښ N[XnWZ~ZٹtY8Bƻ{ ^*Kn@v+7L^e}bk3e{"N,F`CMM;~^6ĖTXnu 0?#ֹ#L6WCvOIy_eu(YA+5٣PT~[1I@=?Z-XrL#7N\:9ʯ?]&mo9S-ۘWesoӃ+W<<=ZxIj>ERME$%cc Ɯ_(i\}Ê_| ]/uoI!e+>5m*K8u=Ox@F!/^I's^˯|tf>T5`=]WsbMO[RLbfh?$$W שF?%]ҏDRz]\yJ~~KgWhG9r {t'7vb>FxR6ro-\\7U<<v 䟳ƨ ?5pgF-^{\KhXΣr[)`á{ RձT\Ucfotoxx-18.01.1/images/overlayBL.png0000644000175000017500000000161413222767271015536 0ustar micomicoPNG  IHDR@2cJCIDAThK 0T<< \^H\)dC_&N[U)g_!(-B@<VX!X s ``(-B@<VX!X s ``(a0S.u<“M@/e:.b,?:kY3 BYa "HnJC(޿cd$rZ1ɥȒiW]ņ. u=}XPD3+rWǦ*埈~!qS],%KR L5 0Q>u:BiTXtXML:com.adobe.xmp 50 64 ?&}IENDB`fotoxx-18.01.1/images/file-save.jpg0000644000175000017500000027242513222767271015520 0ustar micomicoJFIFHHExifMM*V^(if%HH023080100Fotoxx:sharpen|trim_rotate| Fotoxx:sharpen|trim_rotate|NE0Photoshop 3.08BIMfotoxx, ;http://ns.adobe.com/xap/1.0/ 600 1000 0 C     C   U#" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?*dWr6G%@=8j>,Ӵ<2T^*D̳Oォ㟉_}o:xkq"}f(^z;׋MFG9W+F)_~gFZݣlu]"j80ElwCIId.ٙYy., u*ǩî)8lCnRFxNj| .|c-2xFM*pc)9 k x GW::UO$(F* h /W/S'Pw ?ºfAYC (G ۖ~:j?|ßͮ,4!ﮢe%EIl1Smƣ}>пOQ7ֿjhvܮZSݰ} ,,|Ӹn$8a+/➏=GJ4MfÐYǪCiO3^<*ˈW*ǹoA[Kc+_<~:֠^5/i:o!x[b+7|_m]] vtom3\'&o7`[;NAlͷ~ҿݏ}?~xG׎OC^. ,H%[a'Ҳgdzxu˛}.?Kx|?͔֨[Y"i ϷZ|v_hM['| >пOW>=<zVxҿݏz>G0XBu?G| _k,m} bޏ6ҿݏ}_z9| >пOV/_ck i_| _k,m}(Bu?Xk~sCo/.#y5eO|J٬ K9OXf (AEPEPEPEPEPEPEPEPEPEPEPEPEPEP^]m/aR_[]=ҜȪ7)?Jk>oo3$$$P}xG|௄%Utz8L}| N,O:H,2,lW;&s܏L}xG|>o<#E],o<#Eksv99 q00q؟OnJaoGmȣپλ_^ ëDb8"ݻaȠ* sgv^;}.K)c@au$Mzmȣ6?Q3|Uŗ$K[`S퓏m>fdǣ8|;<ˈfyvn`cvzW}xG|>o<#Ec񯂴/fΟC5/|]<;"[xAS+"8Fle9^Za"Gh9ѓGk}xG|>o<#E͇:dZa"Gaλ?l>}ַmȣ6?QsOl>( {6뱓Gk}xG|>o<#E͇:dZa"Gaλ?l>}ַmȣ6?QsOl>( {6뱫9;+hk$F`]kec&QEP((((((((((((((.?3X$|+KY->K̏mQ95?ѿKx|;7H=amjd5oi',Ecck[?~-|Muag xNK9t;yuhηeUAwQ9L`?k>3_QK[[_ X&3H<㍋ci( 'I{=_TgTQEIQEQEQEQEQEQEQEQEQEQEQEQEQEQEyZެ QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEqws]?75v{[Eօ}\\j~ Y+Tgˎ8\FPM;d˃=/׊/>_OӵFỸYVf::ojyRnb"XsSj0_Y^>*}ceyl.Е*%Rn0+r;@z&BiS:j+~9;ꚟ 7WR}ƣGv1l0GJR] |Y[Cgqokq$h)g]#\ʊ‡^mucl,WTBo"_7=<1dxzʳh)q|~ $89UI ZJry`|&.OtuJyݜvkO]S2qPYJKDji]4yڢF/{'Ɓޱ s>rhԶX7luRn`]xBm-ڄJm`bo uڊ&hZxV\YʬaNIfܻ󜊷|o}hZƟMJ{/xW 3]Q@vW;࿈gqwoi!ɚm6'XTc] ( ( ( (7|//?[ՃyZޠ(((((((((((((((/PΚ/:}mgK,-¢*;3f;-Ԁ{}?ѿVUb@+NI Q>ߠn>bgÏFEinNo^Mm $RJѢJ|=_-c yZ Pu`]sFQ@9J+;.Uh_Vg!|G.=W[K?Piu[[bH'_*7 9WuĶ Wj {]k1kg}-ی]A>bB3(ʐwZ}-t!rgZ^0nfKOju'.zm~Gip b1[~;o_zxm|9aw}dikwq11JyqĮ l2}UE4pMIwG>ƧX\hZݤW[r#rGΫ1("ҰQE (((oVkz ( ( ( ( ( ( ( ( ( ( ( ( ( ( (8C?3\GC7i_ OXdaIs]_4_ 5+@c Wz#Q #NO?КDžqOi> CԒ~,1Ud9bUso-nnKKAlm*Lo#¹4 =S'_I\(a]{:l5uqsX- 0 $4 yS_f?xGbҵn$ݶ?~mP nri,};>*OTj?c Wz#W>QņMZfw$wUjRNW.t?>.t@]ZKZk׳H"h3۶`H#/K;Yw|Uӿ$OFD}jK9/vxe4Ʃج´ cN'gx.'?>>m%;kǧڂ\` $1+|{N? }oU,N4[<9wߖBZW~'襵#]x, ɕ >n)_u}Q_=׎ %翲CnjguO%R9w[>S`Z> B}ǍU5 4I+2FyCxb s,@}%MauMVI /%UX$m/' ;K߈MtQ_>X~ؾZYM:&]fM+5_H69x;9 |A|PTRP[XcX%dfFۑ2 '"vMxk<x? =yAykXͤ;UڸdWzI,֞/^wjomʅܨe,fQVw؞eW_|^K /`""֡C%ږcrTGFۈ6s]_Z_j$KXF{}J;[#H_v<MQ{%HS/|;᛭s\%i7|Eۣ"J Ll`79_xœ`#zmŅ;kd\/*9 ^mLq~_?ߴNsxpxE @3߲E+/[?/j{迵|ux=uɮMxd8 K`.F~.d=Ί/Ś-,HCǮZɱKm3*7c-T^EtŠ(0(wX>(((((((((((((((75~^ѴK+]7uVm2ȁIY#Aצ_toj =cBռWtOO}O _H^U&n=랴k>&YjBKm;q5>jJ-ΐm&V :Ǐ'NJm0ZeEr^CEW}Zk ?ZX]i--nDh|gWJ/A|/Zc$o _n&bϑ9K#+_ Ҟ53Gk;J6FB҈u $$׹QUw}|2O_ &6-I;-ԲH8ny9m|5i7L,5e" n\gv{s}ɷ~ܶm$֓1m<1>PK43 <>d6'$u:Ѩ|O{|l4rY[@P!Ū鹽hN͉.V~GMZ?e/.<!H`W[lB1>Z4du ?gf-o_T*ۘ-4eP[aVI6g纏뾻.Koǖ|?!wK3moS( IǷby0r—k袛wi%QEQEn_^?ַ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@^?ѿ{×?ZM>+B[d< Jq5s]>]?|&4[PuB&5fxIfxI~7u2xT5OȞ}ͭlI̳R"[bZ~'<+|BM@'ԬVYZФ^dum8|Jս_Es>ee.wwkSh~}^[sp1*4{w%H F үfxO/"I+(׼kWזmI4]Q[U浟q²XF}Tu=;C(m}Z9n7֖`W9r/Rq]og=t/_Y6wñIO4WMmVV^L"oA%| ['u'4ir_X_\iD86Q'؅0Xq]?JwI_ܺqd/ຘ@W,U2iV7̗箤*Z$A|C*xo÷dE( 8szw'ߋ.>qjSJ7h݁# Hqz|Iz~!xh,gбzs^c%W]:ѷWfe `nю ?f_Zx4'zd.kۯ~p,uګ_}{Eeg\z-D~~`?Qξ8Q׮xg$p!KaVP8C +ҭ Znjigu)΍GJjN3꣨nqcoZO7"ckv$m'~+|PNZ\x$KWΚ]jq11#<.?xYW5 7|;xgckw4m[2(H}d\px5o;?!]xVw~ԼGl힛 .xaB31I#9_;o kpZ|O6nc.,@po݉,)_[[ 6IIR4:kxW/ N]R iZ0.,7`xgƁ⻯Is+ dz[ac!+3HTJO~Oϣm]#vmV [yc]Hcǩu/I.>w5- lD]QY 0-M&mGoiذۭ"K_9|Ԉs c VVž) 65~o ħC |I&jڌ;~6D@#EItKI~|+5iZHI6ͷٌď'm;6u=xÍ C#j?c7,hAĻzOc9|l}/>մ|Dג}0ńpf;s ;4}/Y?N%ߍ}YG^dQh HP"w!Ct$9_ 1[|BĢ)l!PKItIn }Ə @Fͼ|D/ּTW^Ŭ&5*LPn7c Pկ\B/wc'm~i+^y m,QK;mW' I=$+# RhdP$lYOB?i+mZ\𧁬s!)./Xp8oHNq{mggxOPms[K G=D^iY[L `_^?ַJ ( ( ( ( ( ( ( ( ( ( ( ( ( ( (8qZWFI![hZYvF'ۀH8=p3uk;AwɪnE煡DŽ?!G֑ml{\k>#.zT>˧JaS1ڢ3H~CK?ӒrK[ܒy5}A+Zڄ|PK9$2K">b{?P<3 [bCӷ R8u?njpWqGs]&ѩTJ|i=~f(WZI𯋬4 .)}˳C_d9]\ ZӖv{+cXO]zaº~ҴZOiӴ.~s7D)^I8 x5< [k]kmvcE EpѴFUUPw]GC]?۠i>C_E_qYn#ῇ$xoMx^i"k]*4F`|8MuE+}ck7@DZA} {:|>){}*;dvTU-*œxZ$%>n>Fe uCG۠h> >m{{~k> 8GH=mO\n.ɥ 6MlVQ1)cV۠ht `|1G4q͸eĞmt8ãM? )ž|7orIҬYXt, m4l3?Hh,2]irA}iJSn?vrN[_czl6H ACK?м fvmb3Z$Nm,"@J_]Rk:K1Hg,Fr4}3o|Knwi:֚55Ų܍-< 3Ym: ?Z{N;T^02>lt n };Y_i~-=tk[H00dg|4hΎ[I֮dԬZ3dTI`I f~CG۠h)w?ɧQncF# `#x>^462EeC3.W71$t ! U$硠V:_ `xQĉpr ( ( ( ( ( ( ( ( ( ( ( ( ( ( (8AStoj ((((((((((((((*æUi6д6NA8\[<2ַ0MֿY?IֿY?^S|jA{/xM>c]O-2Ĥ)2iTeNsP?!K .zEԖ8s;LPWb`Tk{#? 'hok_,_\4GQuZ\3.ccnS 0bI]7KiBxޢ [ Jj,1M VU's4wS_ץ#п 'h,D"I/, w2x/Zi}ޘuӠYΨyf!ǖ'Ne ՏkBSU^KٚY\hyf9>Jl zCx_^?ַAAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP?ѿ+m4#._O)C<?C<?C<?C<?C<?C<?C<?C<?C<?C<?C<?C<?C<?C<?C`5M7Q+d9#,x\#+wC<?|?j%ԵXPj_Kw{YPzb f|Y6|88&R[+8yL kY;,#+s_O/O?G"o<ϗ5CF𾁬0-OPxVIf2i 򊕵z.XBA9&I> i,z]/MQl4!Pe aNy;<?C븬i%"Fkc6Gabrطeٓc<-Ӣx7Bso[\nȎ;cjNh 3s^<?CѶe{kz)טp6`3sQ{@Š((((((((((((((( [f[J٘b2~aShX [c4|ߏDM$v{>бz!ByHRym<*N}v=|[[0+%UhXѺߓׇ/(.#I Y$Yñ;__N^ڭ﷏V8\H Ϧ+Zb`Szbu0U]Tz.?]potk5jw[Ζ4]IU!Az񏊡%E֑75Ӭ#M]NwwG1c?n?thXvHURQpÃG0ZhXѺߓ׏k7<7H5Z73]e5yd*E+}Z_9FVK|MEDeG0X?]7_ y YT(.' h$DR2r [jb/nfӴ,ň} {.?]xLx?A~ؗ6.NVFϐ~H:WIOuOikvdӮݣ QG7z-FѺߓu=a'xXİ#е S+z°BFH`z}G?;?h<)[#X `#Y囌ϡn?thX˾l>$]Q,Iմ_;%TTl0?j/_eἳtɞ$Uh̪Qw|±7_?n?u / jݍķQYG6r,Y ga^w_xTrKdEPLHs=(Sn?thX<P-Y䵳'Yc$Hɕ5GF &>^h'!рee s)hXѺߓ׈S7i46ymӧkyB=G/?CQ=sFz~OG.d탦^A'RҟI{m?NXc6XW9 t$pk/= :͞.$ҡvIշT(mp7_?n?u6?)oM-.e)\FP۲'#WC7ċKtoX6Z\Id@N}ha,mhXѺߓ'l>s?]7_a֎`u=a'Fz~OXl>}6n?thX֏Z9.?]b}G0XѺߓu=a'OZ>}h 7_?n?uGcoFz~OG.>}ha,mhXѺߓ'l>s?]7_a֎`u=a'Fz~OXl>}6n?thX֏Z9.?]b}G0XѺߓu=a'OZ>}h 7_?n?uGcoFz~OG.>}ha,mhXw`o.~}ha,t,ד CCIݹhE3MKFu#j';֝4=bB0wG 8k; h^p5į˓֡/<7+`#IN魚r֦['*i5-WN}Ǒ|qs؅hcC4$Mz!}:$0ȝS88_xoQO- XZj5͍Y׬}ItF\w2j41mE6e`xozKZ.zW?eGhf,ʤlA=#b~B_xoWO,.h_<: Gɪܭ 9GUXa@rkiؿߐAEg^! _s &5+u@ S"'p8R߳6ψ:^jii3 {|_x6 }'O ?!B@r>q-| A|3uMKT\\Gn^ž|6igpq^O O_xoQ _fx5޼ދqgsjvk;G"I!gV9Z<gľu_Dty[@LkgrK")-q{/<7(ߐLǂKx_{k}<9oډM:;#c6$.eWľ!MjڄM{a[ 4RFprG:c?؟ߐn6_ 4D]ZZh)% Ag"~Xxû]TQ^Yn wF$WO ?!BsD%dԴrpZSlLO=j+־ G.eoxIXk{5WVRcdG{'<7)?G7)rKax߃w uKPԬ"iwLLl+g`bxWK M5 'M֞nLB T%۞?!IO XǟNωZayYKI8Y?q&V>z/> ФmKKd|sys Mr>ؼ~Bx_g̓~˚σV4)5}3^d l.!vo*@ʞҽcïC~}Ys82lLʒ;8##v3Wb~B~BJhW2z;x7xF厯]#Q|'hvֶ[;KZRяk> : OQJ[WرF\b_xoQO |&>vV&|Syk^lѴkXKu6#(i3aNzכ|?ּ?6è_x\&vlEHRy!G/<);۷ܢս-sn׼w_2x^Ԭ%}?O;~̸3HY|3_m}Ww󬛼1iqob,$#^O O_xoTI+4[֏Rb~B^?!G,"zZ??!I?!G,"zRb~B_xoQ 房m?ޣ֏OxoRb~BY4Eim?ޤؿߐA~}hi'/<7(>r9/Oo'/<7(h>IxoRb~BY4C֏ZO_xoQO 9d}zb~BY4EiԟؿߐoQ 房m?ޣؿߐr9/Oi'.?!KO 9dZ>IO ?!G,"zZO_xoQO 9d}zOxoRb^r9/Oo'(ؿߐA~GO?q/ _OxQ aٳx^v/c]ж1{;H-YQ\-e3?:䆍?*G⯂ۨj'tR֬J^¥zTM/VG4b> 6q//*ep?z3gʏh??*o|A=MRGlNG_UPϙ//޿ƃʱ"7CNM>'09Pr_Pϙ/_G4cGoC/*H Ө~S3|/~f4p~UKYh_uNC6_$g9;ܱR+?KhUZQ|"Վu!UsSk?ƃ=5_?KhUCʏh??*KhU=5_?ƃ=5_?KhUCʏh??*KhU=5_?ƃ=5_?KhUCʏh??*KhU=5_?ƃ=5_?KhUCʏh??*KhU=5_?ƃ=5_?KhUCʏh??*KhU=5_?ƃ=5_?KhUCʏh??*KhU=5_?ƃ=5_?KhUCʏh??*KhU=5_?ƃ=5_?KhUCʏh??*KhU=5_?ƃ=5_?KhUCʏh??*KhU=5_?ƃ=5_?KhUCʏh??*KhU=5_?ƃ=5_?KhUCʟgۮXpΧ-< xjM2kjΗ7\)O(tKZm!;?(״?Ѝh (FK9H-C#F9ʺT+Լq#w5][-J[9$B+ŤT]>h׵;¾ >^OYkpMR l!fE9׍,GڍRiR\.&B ٘AF2$6C {x0^-\xJLYvC>emr6|ALz7m|,(2]ܕ>Y9~ֱc%K,'IԮYmxR:H%(^|6эת4;hU!m\$>+f%6IP!8!T2#:]`_:ܚV0=0Z_|$ Aݯaί47Wf2<p9I J#y_H\i,oxJv"`8'ZXoď5>IмFcI1's_]b(уQ׫oc3zjprӢ4zYEA&e/١; @~bnI&y z}9EyiRo8ɍ$qvW'KպW2-*S/5;~Fڼ5yI0gf@WtYioa*Eu<ʼnM·:dzs5rҕ ;V=k <^]=Gڼw`(e[';ScI}xPrݓU7h[x|Gh '3Db|Ҫ? rBZ",B:.I[B&Yf{I.ol-ͬ &IgW|O`rx,m`RNZJ3GC0muB9^~ΟK kXn<10m&$/=qK60Z}RJo.KĦ,嘮gˉ}ޗ6sac9Mx9{jZ}zwN&)2LP?I `fτty,-ƣ.>yq-,ݓ`:^{ =k4> SA**A,>}k"oُC"ÌL:RUS Bm}a/`T}{tyaxCh#^4LHL{jtm;Xy6YxoQɈby N6c|H'?/'xibPԮjlɳq Hp0w/uq<3ck~nSQ4#%i,m_(.1<}<dxưܵM{qO%ծzekHRWNÂ~5J 5JQi%Dۯ xi#j7vEy?_$+oZHXAL>%McvͤthcD>ag''5|EM_ƭ&a5okqj;"hױ0 Q%]oS9fZ6ښwLrݢ _*iU]n2Hbx}oX{ǗYFʱWFʱ_e*6P}l(@QleWFʱ_e*6P}l(@QleWFʱ_e*6P}l(@QleWFʱ_e*6P}l(@QleWFʱ_e*6P}l(@]qq ]=먭ߍa ¶uF4?ЍT?#\]W;tm>\nZ0 jȇ=9\+OӢoKFj_V/EG+_ӢoK(ZS]<_CN9."Euk&)(HlAVZS]|_CN)., sZV"z?u5e_iG7% 4"CF~k_Y=Vkt_Mt´ :/ѫ߫/GwZWV_UZS]|_CN).,n_4jֿh՗#oV/EG+OӢoK ?ۺ z?55eB&o謱#9Q͓qz~J+1Rh@ @?ހ=wZWV_IcF_Y=pY?_IҿO?@CF~k_Y=pY?WIO?@CF~k_4jҿO?Fu5eۚ zwx4ѻş~;k_Y=ۺ z74ҿO?@CF~wZWV_\.74ֿh՗#ѫ߫/Gw?J ?Y?_Iֿh՗#ѫ߫/G?J|~,+$n_4j?55egi'w?J ?wۺ z?u5eogi_/ѻş~;k_Y='CF~],+$ngi_'wZWV_GCF~M,+$ngi'wZWV_GCF~M,+7x4wѫ߫/Gֿh՗# ş~,+53F~k_Y=pY?WIO?@CF~k_Y=pY?WIҿO?@CF~k_Y=pY?WIҿO?@CF~k_Y=pY?W ngi_'wZWV_Iѫ߫/Gw?J ?Y?W n_4jֿh՗# ş~,/$nk_4ju5eogi_/ѻş~;k_Y=ۺ z7x4ѻş~;k_Y=ۺ z7x4ѻş~;k_Y=/CF~M,+$ogi_/[yuKQ3[ЁV) 2('k4,+$sEowMwZs{14 ?_Ap#Eb!֍gG!ցP~X5sNu"Y$Z|GU|Xw_O]ek:NKm~+گlq%1 ȴ08@k?\z9o?_,OCoS [O>ky!AMX@-GJ;#dA!ʃy!'ͷn_ hg ?4C|n~|AERQEQEK[-{O C zxVZBmp6cڽ[-{ŸvV^D;UWho&9Ck7| *j_'u֓iП=Jhn%1FdUs.} |['/ {䴔xCrŕϱikɫDw,w>o9 uEoro} XC[ZWfmu]z >4xK_ k֭g^_ms7Omv=8\^M~n5kt\'xmXGr%?.+3iF2:S{umW'/?]据x~ Fl[(D~'#D[o ㎳=l=@m B4Q.h X3q:Ѭ3q:< .O+I-1;¤qRZv,mi01F ~>iKjnh"òp6>:G/]KkZvs d9]RH2} 5xBiᛟi=&'kI"ƃj+7.:w?o|F[TM>42ܤd*v1Zv;Z(ki^-כ5I<&HbdY\66X٢tKxl_DuųXܹA'p#+V ( ( ()ky?^1SY5f ^&gЙUR#>=[|a`&DXDmGֱp5 |I/ˤ>=מ Ӟ[9X-dd9M9:s@9o4_^O\]=e#DsqY?T_ ƿs ƥ%yv xm\{ߛDGe&b k_ 2r\dd{$~Þn5['Aφm縌y֓$hVHռ!B0Fpkq ⸚=~O8Q+6N܁: beMg9'_ c*,s WSkPGn2Ƭwh?]U@n5M` /]բh?s09bsZ^nC(5DO&c+}Wq] /P4*jjX,ot'je&]\ھk!7bc5;kmS@ymZ7[Mɔy V_"oL[iπWڝLjz v [Nՠ-f+5,̰!k @[?{3i֖^8=WPϨAA42hY&9PThSGk1Y27[?1Y2`J?:i^S ;hcsdnimB"98{V&eyiHdnN>U/7[Mɕ]5'"mz ۫b6ЎǏkϦiKcvѴl~[i̚핔wvEԒ$سE"F}:}垧_i)4Z6G%V%|tQq0Ċ3[if?ykgXw%hw7&-1D/^>cbeܵ|7-svf6#{/i:Tc VU %X7һյ5{eH 2'e&z> x:c]Rae7a^@Cw'ÿ?񩄚+L?7g~mޟ l ^N(4?Ѝh (? δCYfuFE~`ji^ u燭g[td/,, X`I>+R_ZgYUJ,m#I8UBAOؗVu#*L('jc3[ <?>#nxHKhȖ UOls;;l\O*AoM4ҰT4ԭݘTIMyO >.~ψ<'k?h#D5u.ys*azC 0zE>;ǗV4Z;Z3XO>oL_l6 ėR #mPOX[i7`j֐Ե}JMW/_Dgg( wu.BoV; a``qzW7OXtE]'RSY: D lrU^O'~~͚xþ'񇁬(SEo5 wBxd<1v,=(^( (Š( A yoO7tRdžSq$0Ni IugqzdQڧz5oh~ ʂsg{kuq~SS}EW:3Ğ5_x: GG%*FpAM@L|ci" JԆK3Lt@8wcО=+Q1{ {d}mtƼxihlzf=̖qI4Mդ ܬW# `c4ֶv:+kW6 i2F47 R6q^դ4?cD}:wϹO|-VeE$ƒhT>HE;} { =εH>m.rrj4𖅢\iEΟo$wx՞1s#L;Nc U'#SPN2AL9خ/'$>7Kmyxڬ:}ַ6-]ZCg,{>?ck`{ײ{KiZۤ<30T qܓGAs/"RG +3/"3r<qiP5桼,u~_dtI"0XȆ_+ Hue(݆/N=x*S񶵢 [{i4#kb;1+ sN5:Z"{O,Pdlq%'tC׳zGonaVw; |_+ SUYq [ xKճ_\\HPBV-$n[wjZ~hd|7Zt\`b -x[sO]Ϣ}LЭ4K*/9E1 c#qߗ 9W]U٬ ŴT.g)n:nP-1>bzsitxh7dBg 3bWߋo'u$r?SF~)?jϋ>S{[[[K9ZKy"W Yat;H^=.7Yk9.llA>8SzWA4J>I-2Db2@b=p3+xYEu =_EK2!h2\| rk]h,o8m9'ށ+`dgQ]_o&G)#sPŸi-叄4+;dx4RHC+#Ws6OtC\/F ?_0ys$u8An?Zf+b-4b-5x'ĭR=?úķOjovsH@K#P2(9=88G$#@b-4b-5j*N|ыA|xHvzΛ)a<.T`Iy iP\^zƌ^~ƭQ@q{?1{?EU[h[jWރo߃oTP/E#Z/]K[ ? ~uo`&Ѯ[lyqLT~#{TYV.K#!P} Uo༲1a Xܣ*pFA u_ :x3qvm61 uvAO)88ſ x[M'm%/޴3#\H[YNr ANÞ(|^%Hj&Q ̊`d# hx~ a{t"jm33vbQf$ 5~56-\i\a$SY$1Wa+Ὴ/cw߆_ir*o[{_*h܎I#vo X?|1_G+:ľx 2;g2s*0YcpOSV/8ýϳ^o ӯMn{+;7}y;;_><%}k=QMƙ52 2xE 63^m7_D 5ܩhʂ' rs7ςi7lf[ -~%SAk(a63F~hˮ@&(-K\7Ϳ|Ez߆ǪiFkQtF@ à W3OĿSŋPKcI K]>=^Vu#Au֡ е'gfZ"7JWhDT"\ S~.}zus{:sZzB 0_ޯ+;9_}!>W-x/Ǻ4 4nPc1\+2k+FR!'>2վ>x^)|I845=ݼR )II, G x*@omŮZ'}bYXq]NLfV2p1+7>mR/|O=7›.B䱳DhPWïu+0|. Iy}m>JTg+6I73rjsl>!~>xL#⅞ܫJ43> 222g|*M I/~'_@+ٮ^?4=mlY@aܤI'_O Yc[w m]C#)ϯ6] 𱴏r =N^[֤<+:Y&4֌V/:4b-ܼy|_k[D>6cv6VZK٧-dgג_,.Ï elh7_}ω_>k|Kqij^#y~6Tlܑ_9b2z]@__ѾWfm]5ZI%x;ۉAk$1gNi/S^kipxឝ6Fɼ[ N2g jyf,;^j+o)?>k~W(k~W+7,%z$>'EM& 'HXFf5hG @Ҳ.g~qeߕ ?ߕ EIGM}_/+k9)2DUuOh-Ap#EFŏC7|}h@v> 2k@ՖAi5&G2άTKc&It=A햮κnsViWCdug@^gwvk Ȱ.\Y=$hoNIn4$so:c^#*\o AʎĊucÿ 4-ZDnrD <9,"fċ+WՌ/acTymC17K*m۴|uǁ7wsJ;) ֩%l XI#5=tMKU.{ NhA!Og+Ƭȩ袛(C ( (3@IF2mkȬ?k K֎-)iu[-{p2&_CR۹پ6{>o򏦑k՛ڇnp~L 6r0pC"Hľo,-?V'ݛ 9<5og}eҵ:iLeDpр[;\7ZU).fx =UubU'^oex|u>,kBPFNWf+FN8^ռE๤M,;qy6MBs9rRqݽlodY~/+4_vlώ3Gk;o&;@ZLs[ZGA}Z}UϘ)0S 89dylMK_= 7Ǿmcm}3 o?/;G6W[h|C=4ȶF :Eh7 &[m!Imn/H}6"xVFURr. ĮN&IIt-fe'FTdY6RݔfW}I o?/;X^7>@Y,Cet*D20esx:%nJKh˨Ip­3n bK{]GL_:tff#9Ϊ6F;Ԭ(WL:}Ǵ|:᮫jiWY){k3ȐF EUe$+4_vM>gCڶw3;e F3s]| m-KM &PԵ.vPm Ű1Mg_&9bێJz#"]_#VRx`hwc,. `/fx[k iZk6ZNk[EBW|3 GMmhfnD'ʭU,7tϛk'u6[eRB)۞[ qG#䚅]&Yb)$jo}Y o?/;X4 _ *-F^KHb\Ln]vgxCbҥ>4!%6OQ'6ypZVeamg-̗7R<0Fϗ(r2:\JWIwoϱي,lE=g_ o|?cx#m<; kw'/el? +4_v[qwhLe6X@Fyt-#6ij$ ]$(eiFMJ7m?3+in=+4_vlώ[i/Wӵ{X&S j%,On9 ~"muSIi$av>m542>H)_V1瓍<?᲼ >:Eh7 (C[vy?lώ?᲼>:Ek;[tl(E*ߊ~W)jig~[ xOZ3 {:kr ćh’O5^sPٵ-69液A,Etjb$ki9eKƛnoaNҚJ_O+4_vlώ[vy7᲼ >:Ejkڻ>(-4k;[{BeB(q &' ΰp)&7c*U$՛Ko3;C\/F ?_/?D1cCg!ցhx%ӵswVfm޼=k}g BqE,:+K2-oqDq 9,|Ev :u|EXr߷'i!؅#].Kˈ-i" ,xkom'U0eu<^#hk;H;6)5 >9żPH.#{ȣ1_7Tvn(HѥUrv&jY4`Uno=v0 _4SvW7QT3ѰaN*Z&oM}^mZ{ u[xT# ZG0UeQzoSM쥔Y.4fښ-Ct Tz~'}?ϲ(+'ź uPO7s eR8?K:6wCEL~jkQE ((M~qo,0c  5"/m_dmb&dKuc88_ETSE$i({9%}G>?ufS/zEuaUsmI8$j߅<=O^AtuKN5eQʍ&91sWt?QŴ[oTbbCV؃]ÝgōbM?4LDm]xW,7yoMoGP;jjs*Nu#g~ ImKw߰l9`j/|/sizo_g|}^Mcԅھ~|=ֽ]'j(',{@5| '.oAG–,,Dɻrϵ{R<*R=&9.1jy#CU5Xs?Wݱo͞9ȫZti+MMmF&! bEO=M} ,jG:BX~罸Uݸ< ]{s񾍭u=3Dͩ],f;H' C,2+? <[N#iy>JgGaz-ٿ@\fuū`tz Q&6=xz֕Cqx2>$ɚm=VA;[0yjs+niz&x~k|{hp C,Q/woK"&䪭tՏw4C.+[!߀c9$ Kmrzm&խ,]Geic x8#/‘ ZWY\/ 1GU}`Ejie\<NڄNKEqɷd61wKJqLhdo iEwx*$yqR϶C#̥cog^4LӼhKm^\\vVK  jx-52y&l%E kvTb5+xo5<5x7ZqH s]>|)΁kxDе}"wykrmb >ӎ}T'NN~K&:qVۧ|ַ֞v`4XصIJѸY@VqY7m,^#MC䂨ರ*z Ժ#i=^{{kޙI!@1ՒXv7v_|BMBVi_ R1Sn{vmkZ΍$j]OW r/ ֊-Aor - g%K(<CZRҿW-y"XxLu_iMl|`Rcw9BI/>>A )ŷN^3bu8T6rZ@D,capϼgŮxX %Cd$WwK&~"kӿ[/V_ ayYg?gmfv2xfzw %ݢm8WR U Bnp]wjتPNtW:߁xs[д}KCm5=ri-gH̎"s#/ܿ\?:诿5o|(еI4=R$xtY ]X[|B>_~u_lx~ai7$𞁪6֗#@N=<p t:%Ѽ=yhVް>b6bPҕ‚ >֏>_| E}x+&}ú[kZimk0.#/8/?ο#/‘ ZW?֜?_E~‘ ZW?H -+N}rC {3h {}etP[yʐʒAPx*zS/ZTю-aw^kꟈ[_?; [!$褂7 f)]|)a`|Esr"E(HSxf=*K=?LOpgʼny9=OjV(kRIpX OFD -5|!FD4rF6*N>`## gh?]~u ?^Yx4:!8sǽc>$Tr+?&14Q|/|YմMBӴR4t3mI ӳe'xu~'hvU,5Kh¢!V_d qq_QxI'Z; HԼCs=* 5|,R>KJq\2>oc+vW7Y66+ګih$h^$E4OȺ,aM_WUI|E?H -+|B⪇ap%k*S~u_|BR>KJq]N}8οS<5_:RҿT7? `{o].+W2ۍHAXWjiJMtmGS}NC\/F ?_;>ŏC72 f3z$M+oHl  ]i:5sc&=Fp<~2q]OtOC||l< g ‘r>eM}G.?_?wW^oMmѼI7dut${2p+o$Pq(3s ߌu]G.?_?>q>iR_ f{K~{5)"6V(lj=3՗& iNZ Im$,].?o?>q>iP?oi-LR { %\tf=kl eM}@(l eK}@(l eK}@(l eM}@xESe X]_],Y mTUDt:]W5 m9#qe(z@?uX5En#dLdlrs xk|kW_ZnWz8޾OhQij0aP%4k;o bw2DFl2I>kZĉ4kNC%H'v< ڼ/ľ/_EO.qxzw[t7\,IoG\6@D XzzcitC:K!L`lT8{Ȕv<#Fy8xxQ%ݶsund)5xC>uڶ{M ׶*.쭕u9`[DQGpΙW;y E1yN!]Ê-t-6 mᘖ8UY X}I>j?crT+~_?K|CikO68|Y[Z\_а{yTW(!PW5߳[ZxŚNu]ԭRIC oįOqp|x5Cm*ky8&dd@qu fOm~+cծW\1²` pn5h"O8'ƾ=iK^x}IF{i4Q Q5~&kfeC}7J=Em->桎O*5ާG?OM??_3>!wW|kyGyj3ed{Q:F*ʨVEF$`5{VuOixL2rnuX..#UId9yKH lKV-n!$`N8⧱,++H-"yWH#r@I$5-]4~ ~iW <2 h5=Eicn4nfl~3AvqW'¾~-k|YK&U0auXJ-4 .K]6O4P*}X <# E.-C2X'%=jܯ߆N7^.^@{WRt?OR[-<̪7ۑkuOl>(:XVY şpK$$H`_c!w 2Wm6iHaJ9³#4sIm94 O)HIomm|{p<[mϊ^ ZiūjB;yA4I ͅ$0 Zh_M2A.O C&M?Ia:6O>9ӋJ[XdC@q)yy%uR@my VvoLۼeww-oOOOI|ckiڏ>KYjP]k~?{q|Qؼ)8oDp-WoxpJydCg`u٬g`7y Egfy5>ѭ a *[ #N1,G$Y{m6_}eߊ׈~ݴ]5 X@mjmV[InD@IK!’q~ǿuĨ/xkⷉM{ݚgHnѭd1\˴R@(H潢YC\iXcW0Y*+1 ;&u|Ax[w^UŊՠ0雧UFdBR7%N{~;׼]FÝ'RAg?JԭFYKj2>hǝ$x>FprNxfnIWd>H-+;˫,i8^m `p3RIY߅;o5O঻xFš>jح̖$v"##,5_=kP?Ꮒ&~([EZ8lIMlVv *!f9b@${ko hrZaF[vȦo@AsC['_ߥ~p|/x.%3wtMy8 kȿt'l-7N>5+]C9Ԣ_͌7{-FŸNqx̞ Zc VB$SZvv$Gm ,q U@8.om"[;#hX^zgoD2%TCn+}+埁3׋j _xm:lV-Gt<*)I7|k2R=ꝆiO+XZٴ25*Ͼ4_pz[ߵ~<|f߄!/\6>tl>e$N@ l 9G_FZX΁Lռ9DY&I%+Sckgol.diĩ}ljxODxbm=68ͪH@1N@;NVb\V|㏉wz.?ZMXOO_Ly,FA(ݧ0'nࠃ%^1Z<_uxIJ]jjh]A-M FȪ^")%T~^k[;Mc?ETU?qUWT}" B4Q.h X3q:Ѭ3q:Ѡ爬]^$ZtE-1IRt$eIQ^0:O;qKDNG ϵ:L^;Xk-:Ɖ鷚)m./vW1.7dFk¿^kb<5GĖ<2J˭XN.#݉#2N%pYL_{N}z_#*+_w5/h[CmwiJb>Ӻ@6eQwG[Ŗq_HmONՎs r #=>LC|_D gQ^7&EGd 7V}U2.@nY-a2ELEPOSը=ŠNXmoz>Ub+o<͆rІhcG+|faǾ%׼ N6oRXQFo"089RMs/KFY[>#/:rE麽.tʼB.Lf5Ò'R4Kmv@?Yۡ9&z;}=~(Q?O@5=fhx[xث) Aڧz-Hw.&<? Ěu,Vu>$(tM55 9t^>67u1RC~Ȧ/-u6.0zcZ񯉵fm__hma)#>Ųwc'XZ-p]<21 :F<9پ:|+oji^$]ef2HUD$r7dr+|G'ŸZGe$6։kkϱ5c,K 3w坷-_\O|HZ{}jbi w oڏ7+}WOjVX-5m"ib;drc"mᑅd/x@7W=5? Oj-?-ef5IuKǟVS=.NЮt~[^xf'o͒w <*6pդ~ØB<-g?Zđk~5qvW~3sipK݆Rl[4A}뚎>#H!VY"(_A |G1ԴKFЭH |䳹29,{`<ٟ]tCQ4ۻKY+/xmds9$nn@ 8+bs>G-[K[ ݕ}\fmϴ1r+ =ƻO)u@װI)D.V; _˿zwǬZ~ .d$G}MUmF@h0a#'8]|d O^u Ŷx<=uh6?+ d{1 Ӧ,ՇtaVM{Hn$IH?9zowŸNZ}]u}WAg[eDrVDWڒm=t7 =W//_ Mo&Q]z\rXz#[sZ}偍S!-υ^|alT}4/7՘xeYž,w—վMcH%Tԙbhwg"%mۜ)`vV߱jbӮ) 7rZT2kOm?4}m- -ZZƩh.Z,-o%I${xTF^6 zMqCKmϬ-3xq49Ωo""Hl,;K|^O|%?.x[mu2o *[j_ݽBѣCt vk~F(=5*׭>4|eҦӭ< /|T7f;|tJBqi'W/>cNֵ7z #Jg [/dvgP@_#h~~Zj,!4çۧ|;RM;>E"IlL.߽]JֿRwXUX t iZ} ye`#n+ѓ^#-τ|c5}>Oi>Nڒ!#2}~t}Hun䱖³D6\M|y)BV>N[t+6ݧ73g©eU-[e_HM)->* ( (>`;CWz}߄-m'N]>_BOK.gh2Qgk0>kK[SL7u6Պ5zFיwd*Ҵx~$oNk$1o=Ldsd[ Mo_sFKT")j!Kb7HͿk)`~Kkn=$[f߈-MsƖz~huBM;$hm񟅟oy $y_l;*<׋/n>Eh^,22Z-б38Fwi(kݤ4o1rOѷ5ȡ~^3ᤚ>x O/:,m.DkL2HIly b|cY{$мL -/w;73.EPÓHw |4Mf?Y õJe@#NRǦOtZPԵ[Vhviqؒ3OzF_+OJ?+:?ՏT* 1*ƝeokְEGb4dY2s'[,}>v_-eh_/,WW7LZCu?byd"T$:񇇼g^*i['F|cܬW>a-K[f6̐M9~VrZ/մc+]QE&U5OjU?q?Ѝh (? δk:? δh>~$xZÞ!{gw۳luq$L0ȧ3 EKZ4i7z^coiq-.E*r5xrw'Zkۋֳo䍱mJA |G4&{]A+Dlh*gmXAEK+}BAG_C05x|G=*n888>ƀ?B}gOqnn滼b5K;&,<}W“xĚEaI֠8;L`Uۉ89^/c c\!ƞu?G_#?ggmx q%͎y&iSK]fb&,yC9v~zRWaߍ> f14w7 8*G9KԴ?zΝb ]]3n2:1 $Wz׭e62=;Oԫǝzxhy/i/|( Z[Flŧڶ27 *zW?v}}4e sRG_I _wv}K 9tWTl@>Ծ|[q[zϧKEG;[I6HFʰ`}+َ|UỻkZs (gi"2EI%rUp^->xjzkVk06Ei%vꊌ` cy#\ݿWٿ_y E|5ௌ$t_#~.Nڗ/kiKaVPt'w⎗zOďMV:?NKf/7"VV v5cHHPI /^;@.Ή.YĺS\}8^D0w,|y[J x[/][#JZuΣk`2{ȱ 9&] Vm'Y_x: W-4e1 1h. pHZCEiWڝYb|Ipk 5/]~!hvo|Emn1 8{e^XxO¾|7e]OZ4#Pd`KpUO?ĝ_}iYB3M—y۷2Xpkߋ$_ڗ3|M|PM+JxMoʚZsF4Jѝ%MX7{+o{,j5нSk >hxZ~'WajM׋4;mWIkFmFBM2`:מER;xf{Y&I-Y0F> H(X׋Ggϊ:n&xV~\RZ&qU] =qN z'Y:t}27  -%d%GoQ1K_q=o~sKK>"?i0j?/#Yyg})(E'ǮT-}W72KgQE"((s%B^F_^ڎ DW`[F.j:6-Z;YvCO{sؑ ^ TӾ#xOWl|Q^rekbPK8Y21E $m+خVm|4|jRq q%EQ lȯ~kWw|eONm c#(7 ܧN _{uݛ~_;9/$xF;~j:bxyLrG\ckzw5&S|7vs,HH#kd[/ y&?٣Cyl>]Q⾓# "m佳>tfgkwg [j;vA5_)ҩ|gCM@ ( B4P,}ht}hEPEPEPEPEPEPEPw^?_ֶk-RhFTdr8Ƚ׬7Im%9 Vd@ _***q?Xn_Eyo-|d`GJ[߶+iǥowv1<7`㌞^ ~ў)mummk<1 9"zlL[i7z^em ŕ+43!*pAo~{J xVHZ8"[*rr1=|SԾh:: jhTn&3jG@mg5嚗O )u{;NHKnM4rJHY` p[I7oF&}<֬$jVXm= ݳ 1@rG9Gşu/j˦譪O T;,;ʨ$'zkO|&RGu:Eܑ[Hqڛ{%& %S#i{p{2"ӂk x'þIƃxsKF,:Mv)'$BIA|;weucF|kZ-A_07yO ./5tIPGomn{'I4rMiRw`q l;[T8Mޱyg.zcbwFt l?~?= MWY}OZkMRs5 h}bAiha–1]=[hѬT. ѱEё3kjsXi5Dmk#{m|vv:oq9Uk y]kwu+)#YfR#Q,6q:Y m&J[=Ɲ Ie+ F  k^7z{5Ƣ-o- #A's`ݨJ9-C/ hQF4A! _1'9QҾ ;ЬM4Th`E1HB I$gb0I5Z퉩>1h.t{/7UOw-vvx`\a$+_{ KE|=a?޸yy\!P\:F_ͷ͵^D'|1uD撺][1|ۜqQxO᷄|>?|-rmBO:M'Nծ_9!F<אk?v}bMPׯ4]}:ӤYL*&K#)J~+Z]F8ot>ٛh#q S8ldg=7t |woo3c6~$bnz47Uonm#iPvۼ*cO@ #-Eke]r~^?f/ x/dӼAsu-&+=ReX|JfhU>~g?jjV.:U 0[")vg%OX&zo6> ե(i'%j^r6< G OυGR\ԅdԐ`U g_!k=QEIAEPEP1 񶫧j~"𖅯jZkﲼnflNJ^|2~]SC:&fӡ@|Jy_KKzfc[,Ԯ-'C$v֯ss0WR4K0ǪG_x_>ewлHA;UA KN^nм+_F]#F4%wmH`-OFdPFU;z7RWr=GϦZekk![{ e;[^E|g&x(go(bDKiOؔN PKu;fhv-_ʏ)&f02IMuz i6VY]ರH!@UDT@+ ~ |7]xxXxTHqm\)YT#aO⦭ijjlٲ]C7 Mm%I[2A a$Q"6ܫwMy;Uj_"x?Dҭt1j*7L{mVuĩeUW8šW_Kj?#uW {Y%!) swqN\?-/_#m>:(cӥټ,vݘ̐#c9}^Aoo-uror=PM:C ,+WlЕfQE >V E.hC\/@fuYfu@Q@Q@Q@Q@Q@Q@Q@#{TYV n a#4i% c9> ?O@5c{k{,UK8>^W]6jZ"YY݁g8˾'(9'<}/ů ۹I"A6G_b:7}:V:u]O&qW [lax<?.#>ŝ<1Pf[yHYZgS ,_8I*\SRGjJKu_<h6|+.s~=սDȬ,rYX|u1|Qkqx_N4;H'G3[ƁBҾ)YP"*T*tRԭ.⁊N1{շysPP[Z-4xß<{Az#JMoVF^F.;@ '96x^&5 qs){cy,w\C喅rT#+}dOJSM5 } ̶d\eI;]uy-ofqv, N9m~M|}>i˾>*^'f_~H$XgO$@!V}#3o[P%}B)cԨ4PA 9F7$}E&Mo|OcĚ+kiF7vZ9 Ϟ=H ŎN=? M'񯆟W@KXtLi>d_ #|y_oo?goE\-{Y㹁X\:ZoUoUgj7$wמԼ7|C6 ]}P[bUڠB6cxY|5xz-nO#1V C+0h'.m}9[(<ԇp'MsHHCp:?~#}vĺĺײ:'&ݚNxa q#??'Lju[p++뉕N_]ߔc'9ۖz_hpj7k:|wp}K<~9U|?~e,$"V2ȡ^YH*;InmV']?/[|UGƞ kFƖIeh`F0A+o۽@|[?|I' il ڎu%˺vADO-ҷZխ彽YU^bH@NXn_9{Z>Uxz^?Vww~2zLJ J"!ڻWVkxw o-[PY?M8dܩ*N6}/z=ܖ!ҬneG8Yfc#ֶdr*ehr_OoE'.~vi|uD}s |#y}m}ZC}QfxY zWOu&yx:OkK<}i W۵8&=1$i7_j6s6cci[A?1TjV\ / .[h<N+Kh&&dx(>U;fPxUY'7>'-rq H((OC?>)ּ7MVzܺqhuWEk,nA3 r=+G O{>+Ԯt_]L2$%A@)@z M3l\LoE?J.gW =I< 6V{íxW\3[5IV$7s$* a͖}᫯6WڏZw>= l,0v ljZYwhp ЏPZF_Xլn4ѥ{QT%IJM]Y~ I]]xGekO;o\"A-VE04daC?7s-/\Ok]/G-rBΈTq@pw0$//hı3DIeXdwҶ-/ KiA& =A_{.V"j( z^3[4-zX56J-g*| #xG?^6>6Z|BF ^.5<q+oqto(J]#djēGa_<'lm:P0h0-xP=t/ꌰ&xg+Bʂ9r|5[I.%}x^JWl<*NWk1`4Rʶu~P aZ.)tRm;%>o3w߃]];9TOfQBOncp$ ~QE_3qK3PQw_֭ER,* [?4 ?_Ap#Eb!֍gG!֍QEQEQEQEQEQEQEgxESe Ox̾[PxESe jiqw=XE~7|l׼5mCQzLC:"/HNO\_E./C:k4IV{1 U ɹۻl~5#||BJGgf=brI<|1u8gpJ[ sN28?e3oj3"wC$%h^ur^ozmڷB<5\^iV^$"yx`WŚ'~t/~B.eh@ 5hdʰ G=i^t ]E$3P`$\}NOSY]\Iqo~=kϥɞ4[ UkǕ/ljo}>w19i*}~VwBwߋ~ moƿs Gtk}_Pm;M:ʦ{o8Q(wfڪ_~k7= ޢr$eE~+z <;~<=3 .٬X6T.Mgg|E+{Sw6qy:~no|C9fTv?)yD4l7#کi_/DvJ? (4Vjt<4\M,2}$7\Z,6ٷ&Iw__^ďƶ3zT}^]7Rխyab[Ɖ9\g/ovfmkG8؋UǦ#[eq$lṰ/o5xoOhC'kql m| c n/<'W,ͧ$N)ǐ8@I!GҦޥI_o[Z!Yl|/ŭCJq; wO}Nu{bA j1`M8" #w µG|Gepȶ nC#8C"h3ax3zv?|V7x~ZeƉjRnF $; 4;򤞫?Zs6|=h3xXktvz.aiK ,xI}B '= mV u]kDžu9-dhQżLvC*c־u;WWt_jv>yY$B g C:6 '\Ӡմ(uMZE쿯{Qr}  /tMFQ vR*+L!H 8pÊko?|m{{O7'JҭD;O8^ hY؍ -}~xH|3i>4ȵ9tR6}-Գyal6Um|$$:v⛍ 1hIwpMiXG-;7v7W;xM7_ǺEh];Ws"5ڃhHf_f%׼/58lV CU1$3y~tRۇ ` zmY4$vQEIgdž5nMI~xs7JqIv}D6ҹLRwߴo]+O]ִa5ː~&PBYtrC:"oH-u/GG0shNLy#'MkශL$z}ꬥG 2>9?hxkCд$y^];cXg6_xKKЭ'XH.dT=yg4U%K_J[S௅z xSO'f:g.Ž@]ÇVh'˼z;|áHt Y[ȴe@)r~QZUM9o< \:V[p7jzپaT5YhZZ;HQcl䜓ԚM+ˤRo66袊? Iſۺ߇VU;gCeRH88k寁ïڳthW G> {rz,Q,rWpZy }핾g=1΍ʡD#X ~_xS?e[=Ɵf9!hڒV3kmv?5lu]ehW˭<]@4.fsWWhwV~0/dm>i&㽭-WL7KRMq_O<,5VrJ'ߑXu.|#>;cgl d%8̲c#5+?Vnja|E^#<;JdXS L|a|E޹>qO`;ԕq6ѳn|/1^~QE&U5OjU?q?Ѝh (? δk:? δh(((((((;/j/jZx I0E@*,8>?O@5nH^hHiUE,LXP^2k?k\V^Aj7V q>mfxÔ],ݷ;wg83ǿj1ɧZOf{#qg &C9a4oux5 :RԮbH%g%Up 33ֽk.a>\Xu]Y+H&8VW{\8n'} EJ滮.4yI%¶DWv8^<(݌d7[ۋV{iEudy IkfW'R1X3y?a x;Ǿ&zmU贕iu[TF&BBd6yki!-ᑣ֚j%{oW)}zS\3';t'#49$-} dmE_]Wwko߄]X.d7|!FY>ji>ᾆ{#m-H3yyx E|g6cx+N΋ZG{.!3[W%9(;9ƚml^dTx:l9Iy,TOIo)E]kS.i|Aq]:-'UiVV߾cm7\❭ <:`712H[NHe*1erF៘~"06:ʝch~._j_in2&=TWK>0:a7AèI,,o^HWF\V0F Sݿ/+#=w7<3}=OTWeҕ]K [8ҼſB$YYGgThI򘴬G 9>/? tMWZ1yn::ܲ@N]zW'~ x{EӼW jgSdJ,q']/i(❵v6-48'/o!5%gpI:y 29_>!K|&b}% =+1lv|*r׼#rZ.m}J**F,y`H;㿍IUzm0|.\jeta.q+i(Ǭn?ɋH攟N3>#tiwZ6~aGy#noP 7h|Ko Oֵ{X˹x۰g󴜀IWǾ/ .HEO8u-̊pbn2[b>.| σ/I<;h[$lVW#`Cq?y^T/n寖tQEQ@Q@Q@Q@Q@Q@Cw|1<15-RzZ'$.\°2*FqFsZ-|!Ɨ{a&jgm=٠CI6dH?oG~4Zj7eZJw#z\' #gDwϕl/|0Wʿ3uo;mo_|H]Ak^եnR\$Y!dPUy>~)yhtIל|5m&wx@ɮa;|*^h:yYGpn"Yh\.$6|c:6?/y t ;P!3ݒJF~e!_Jjw7v:%^CH%`  '[G_:('_;[Qec4K1wxF.Vkc5*IIosCǞnT9^7ׯ8Mz~ox^ïOJRƑ"s~a>س@i%eF)+CZr/D[ycIIt?(4 >V G.hC\/@fuYfu@Q@Q@Q@Q@Q@Q@Q@#{TYZuVA I#`(@IY^#{TYS.!PkGf&I8~M/^q6s@'@:{o7/>t ԤyI_B/~8ĩ`'ր?F(lu=?ះ./VvCG4aIhGxg !3T7z/jHTKI,ȏ@d{Aaє_J;zg[?fud<6c}g4E.E1~6~ռK~jz~?\\"Y8#kKۡ`ngA8F+|D|Z3=?RK>"lnjӴ_ L~hIJmd,%"զUb~\6ܝƅ}/RS}_>I%O i'}gjFnml"qu=%VdAڝRm~v (Š(((((-|oPxWF'iiW>#kZ V+_ eQ C\|q_|9Yy{{}tⱐF5ŒfTA`y_=5G6@VBlmrk>~zFxPx7ÒFO|WM-¡$DbK7˂ܹ?0z+[f斿:?nï|p죻O[kqY7isIqY![G2?z xi][Phwr/47"%#u.7 q<듙;75ߍ+K &t?b5W N>)񦇭xI4?Yxs&Gs,qQ0U|6GZ*qi_?ɣ8Kou0*K >V E.hC\/@fuYfu@Q@Q@Q@Q@Q@Q@Q@#{TYZixZFdI&d0H~k//j/jaSs<:wY}EyEĭ<3h-,3ww0eXI$<75w|9ZN`O.5-{c'u9nm{Y%ܝ2η +G/JۜqdgĚki6z[D %IZv9[6Ǯwv <3uɪi7%Ygx2dtt!Հeu!AV/mcVZ𾰳Fo.s{a.kGx a `FN+>QxSU۠xvKD.ehR1\,f bN@RޔW;Y%mQg{?? i?{/u<3]\\ ,;( 71 r7_'+K|jw a5 w99 ~4_',o{[y`1HU\ AS',l5/mv] Uw2VMpvӐ0Zlo鞱~_ S ],%elKu0hfMu/:—ivBsE+^cXg y~1x `4ZC?apy;c=+AM𾅩h)Ե}ttM#,jBo@$sI+;$/ɥR>OZKM13$LW25 P4bc"GͷP2qXAx__7>:oXtH-wWi&BFE݂E֏Z~zz'EៅN:w; Y5دfn{ԉIeVlG#Y+cO~u.}Z=rX[ȥ0"JcEkk MsT676#ea##! >Sk/_֧ \x.}'éa'~tC [g3H K䓜k߀<9;^ih` c`O`(kM(G߈{;R+Ho%ʶ,g͑V5 X`2ŏ5:xz]3X~n.s╴#9ٔ f|{ï꺗~'N.нPqK𤐥-}~h>.4] kM:H仚v2m;0gW˿ 3y+;mDž,&WmFCim(q" ҝ3e#`bO4Kòxg>4K)4o$R Ua*pl;_/-h+O%Ak+I2%fhpqC>'|uFXh1š_&Ozde~Ko r?*@߆Ut iQ;H!YFwcwٝݎIf%$IпkOxSᕆv1[XuG߼B}Zz?=χ /˩VCF:tM[NOC*J*jM^iW}/oqm:Xd`x A fu xwnoo m۾ Gbˏj8WgOX:ψ6uIo'ԳE,B'ּ_ǺSNKHݮz(PwI0˦:kc,3,X7`dADd[y`ڻ"8W.zqǂޞ*^&8?ď Ki~!ӵ7g+']I.x^umg~$LiHf]*3 P x^ofZ{É=3^O;kuM8)e1cIp IdՆ$67,xaps$}*=3:f̶]f8bҥ+_ zu6yo8?Zׄ|=s櫬] ew3Hʼn|OZ?٧w~ O6Ƥqxz(][# @p]gxZ S-#[65qmq%xY[bԐ q2,+eUb0:Pΐ^_Am3I\)ap;(z[5|mu#W5FHij׿m<ho5O9m6ޣߌ9[;%I<%yc@Ȭ  WO]Ӵf_fe9†3GK}iv\^]k*I+mRH>Ҷ-oV>Y_~#i-!1'>Jm6XӃ2*rX"(yQ"o?/º^]|g~זQgA%ĶFy*:H`wA6aoqGdv=}#%q5?Ni9U{߯VV_֖>~ƚo>!\Pr65K;)5Ν{g,,3G l O-|ACH77E{au6I# g2LptԖ}e53ÝFەp2yM֬5o!XpIȩJ-?z6am-1YHw`ހ.TW\L[M?Nns(4`kHw6܋zkVH7ݬdh\0"n\X_AxP JТAY_A=aɂ@5J8ߐ o}wʫnz`uE42-p"f0; hQT5=MLb 3 %< 3ӵ fI9nᴊCy('My_75ZτFF&vB$R,gԮGOa=#~  j:kM--\J6(pakڦ-+9N 5=y'ۜgԵoA[/x#< ]'o|GY!VIaA**>bi.|W՟O xjK? k_xԭsĖ%%#%עͳ}sOf1ymʡFO#R͆ew hp qީ맧{~6_Wg/ֿ G톃'54ϊ-4ٖh|ս=8m|y[K5INN 1旯qa}KLCQg 7P ^P# wʴV|~ eM_@6@AI/tua% zX(.7@#z~:>!_km1f|Nr1u~IdilG@k? isFmGze kTxq)(ռ2L^Lӯ {P0$ߐE.)_fdwcz Is[5]CO_h|`5mDFVӓ-6wscD˒θ1Pso<þ:iO.=R;{]B^G1dDa1SocpF 8PO4} אg(VIݰey>5n}~{OHg>L>jڇu]Ng%3d ȡPssA\߆< }A;AԠEKe O Tm2ǡ}k\[8(FO#V/0Qw]VRKOluh[+nCx\080 .nsCTꮩ Ae3R{[[..PxpY@ ܐ*};U<@@?Ѝh (b$>oΥVAדb] bhy_4sE_hfݴ2#x]H3ӭO vj24>KB))f8'4l^k}:<~uhak8-:E˛M, 'qak_4OMryrv\Ṉj͵Ojvu^k}:<~uxǺ?m [Di-D[H6[I#xUte`A˻mpĥ۠25ߝk}:x[W#iDramRĐ8lcv =1@|~ty뒓^5j7}v3J xTpKjd?t:,OK-kI-ĥ89:5ߝk}:>ծKkr[R'fA*@9Fh>1IMƫ2[h(Md wRD־ծH,.@GYs:Lzn4 q4ƩXjjv{v-%N*U  (zM 1]L"icQQ@ϝ&}w>6sI]ƹOI-#GztFwK3 8YؖQ+|/[Mvnc^}$yQQ[|u993HOA!v夫=k,Rʺ0#؃QjZZbG$Y[d0@䕰Naހ7sY?V4B=7mzȗt GGݷW BTT{ֵCC0U-'X\ǝ&=7>8ǦYנ>iAwmot;g F2@,uHiэEUuRHAe`[V5x$@r&f4 ȚL5/ڧ|3n'&i j*(Su3uCcA!%!8gcu$}{FlnN+m/0y'l#qstj#ҹ?xlxJOֱci-ܞ!$WbXPPKǝ&=7`M k˼9⿁-η#\&Wʴ HS!3|8=lEDigA:׃[KYL-sc{TJ7ﲬy׋פiэA W@E}Ǩ+8qS>tkiu=ï \O%]3yĤ$JNNLnnH~j*(Su;uCcA#q ~>1IMƏ<1TPp0&u4Ƣ%S/IF4 Ț@}wS>th7S45)!NfXTPr0fv45̥L<`EEuF4?Ѝ_tok]3Lbm lw?$8?3Yeۈ/bH9VʟpA /ڵ߄5 >W}?xD0O`E?yh]e& p+!3yVl'{w`=*dEEχ4/jY./ 6yʧj2YH|`n1lYaekki8Ğ. Ίgt d$'z2"īFQZN[wF* +>jPfƸ]rV6wZr6<0řB!A 3^]颤7E L6ʥ·##j[iXkҲy Fy)HJ|$c]V O=Qyq 2zrK8` `ۭl~GJU*U@ϑ5ML>>x m*H.cO+Hhd*GUP_pc$n+|5iD4IU俲g\ҋFUQ6JVM|? h?xwH 5;X~Η35E`aIFk_V-3xXh&'mf5 8 A]壌yk0 EL} jק+Õ!O8OjCm6PX'Xe^k~_.\ Miu5:oh] fȠ^ͫEkv_} 24 HvZ/_CC|<Fmhs*X d`3)_FiejVZ@KԟR}O5s&ƚw$SiS q"3Fҡ6b~Q+jB{[Hm߉|?㻏[]iRGKnxzYɌ-9q4㉼Y'%юq^,cNx،T%F -gm}^{|g֗E5{]~ AqqKl&!h7$h[v#7qn}9`FA 8*zĚnWS/ 8Ni <33:kIpcEs2W?cIo2ٟ!o$U6/+)U[\+XRcHaBqUP:V.#!׵&Q$[YF1Fb|g9m@gKz$~'P5NDHgC#;iQ/mY|K:UծtCJ9d c† @5 g?TtӌeZsF @$U'? ~vkq4{RϋK5;kwLdCI3ܤR8k>3YI|[v{xK𞞚TtL2yHƟsjsxCDZ(5E$ T:.hf&ag*Ʒ ]N- [5Mݶ($h&X|ED,f! #%_uҭl|g/`s()D`;~, |7bXIuw=шc]qU{E{ݽv4̖aX]JAK h|9|-< +_ibX ɁL o>[CR#%o۟M6go;\W~?O_^h:t3Y1hO"-NΈQܩ! AlUoGRlo}KA]AߔG%991Aܧ r*{u߯BMo/OtZr1 c>{qn6BEͬW KQY8c:W pp~O.=b-+Nu 6fyn Vd7FK势yy y~Jͅ|_qumx~/ajk[֊8@oУ) T&bYZ׏ct\5jf,1H"9fT~w:滨Nv N^}.8m#y"RY2\ßٖO^uT7QIat빥on"r.UcKNk~Z$NqK⯎Z!toKu5Xl泂dW'2C$+ؾxS?2/tGkkq}-oNryf_4hzD3Zkz\i#.;w EUέ}YܚTK5۪'s ԀX4CM; /I~o*_jIBA6gi$ApѭF%'> 5|,T:*[Ai㹳-I9e N `8$W +z˨OxCL{$mpYcN)VMf 4ؚ2˶D`b+M~I]vknSqgN֐z 48AI.vx[[d$1X*5. i6mbY[݆Eׯ 5uTW= ?~$hB5&|*aZ蕮_ E*4rj0TGc]5QEQEQEQEQEQEQEQEuF4?Ѝ_tok :Uyd7M2B ntokc=v<Wld7QJ!Q'V.xB>kt.㸻ٗ3B#PGYzROFVv τWe[;q)"sȠǚO ]ڂZ-A=pn`3 p>lK D0싎c/F1'Tl? ^4ax{Jbo!3&H@ IҼn{mQ * 6"D,ǜM޿FK|_TVG~,>xA{Zfqm.T)bO\z_s\GGy):-<=Ok i#ǩD~f;P'zra ]B8})nmlT& g+lp9ƭjѯ/ɶD]Ҿ?hO/xÖ[{''|!]Sj(m!X(߆,p$ozXMZ㧀>xŚg+ɀwBg1I|r>; ]K^GM~+y`˹gf6xo|L</ĝD],i@rb]̫3zc;_yDMǣܲ)9f_ǂQ ՝-^}iկOu{-Anj,u O\s`p}(^Nt4D6lQ7~ x W~-Փ2 [Q2~O] ^ loH Zm-!ۭ0ʢ!睠sqJO/Bn7+~Z1VhxVT[Rz+r8$Oڛ?}GJ)nlu}{h!628Cd^S^l+SnL0Ew +cvկRTwDv}kďCqiakVN\jI ;wdYpzW$χ!Z1s`dGp\rF9w ~mRZ6{M16ZX;$ǨOr5_6zKO7-2ʳhahFࣙ'SdG \c?iq->r:x{_vkMJ-VxaQnä@by?-K#o7hf{c. rvS??yu"HҦ~"݅-aF;lPqѽWM~=Wgw"7K_j6K&k.&.ksm P 1{+9:74ѨhZbIOI$܄R7{McGKI>˛My̞c@?·XijMd ' j=j}aڮwYxHҬ{1ᶯj?Y>LJVEʙX!pi*ow+;΅oޅ-ƛq-wVJ^3 L` Łr6-A2MKD5ׁn4O ]jߤcM[w8L2)8=kM=?o/ ketJ{ayRM$dJr ޟ:>"oa-H~;1v1_+'n.g8bKXn`ey1>\U-?ֽjK6idЬɵKKP~SHhbñ);_נގ>_ou{ nqO)qnBFGSfm]c6#iAo&;o``1^S~'Դ'jXj76y;L)je;B r;)S-gcD& GÓxKo/l'c:&\pN~ho.,/McO4 c>/Zt#vp@;NZ&&rvj~?{ |^(ѯ,`-e"(v vfPK:+#Ӽ9jGyk[br69־tu f? +{=e-K:\*Ac P;%]?x\Ѿ!k/iA. [ ƅTRrIWrztz?Ɵ?C9DZ)i&ic3\HayChѵkhťKŤ7+ݷT$WI#/״tMRx[Vr;$6×*8םx?[Eע/F_J@KE2H3RoQiCY>&ioNN 3>Z̲M,k d9x7PލCԬk_gOm-խ9y[EQ޹TUc_Qm?/oQH((((((((((((( B4Q.hSZxXo®r{'\Ff _UşWf=K\?δh I XJhҢ3?'-c 4hҢ3&-c)4Ӣ3&-c)4MZSkJGOZkNG 6Ak O*(3}?Q^kZk5sm2itu#XkbƲ|?Cki}‚8h#P0T=M >A[/O:(7m?G$?%?ƴ I XJhҢ3&-c)4Ӣ3?'-c 4hҢ3&-c)4Ӣ3&-c)4MZSkJG'MZSkJGOZkNG 6Ak O*(<,zM6d/b$1? Hnq*$?!?ƴ I XJhҢ3?'-c 4hҢ3&-c)4Ӣ3&-c)4MZSkJG' >AkO:(7m?G$?%?ƴ I XB/$?%?ƴ I XJ'$?l!?ƴ I XJhλkq$+m^lQ;۸BH b ?ڟ#$?!?Ɨm?Y?tx mO3Kj@hOI XBT:h"S1`1Vu5G]n 0I#<,ct2 }:?-|=wR6~[]eYlKf=ȣ_!_Mf[~ FHէtFefC!*H%X8$dWEosF3?J+M7z/"Ҟ3.Uoyn#MI z`WA tmvRk;?r:9t#1Ĭ qSȪvmO'=xfITԭt}.+kcİFnd}"χR`c{Hu}M@O 149 268 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?*PfobIg^xKy#fJK]X2$9Ҿ1tBH!y>}Onc8+~9Bc:mVZDZ]m<#H$.b}^/THӘDhfHOL:,(_yL^_[CrŲğnz^tUTsp//'{~=̏ȝ- `CGA"q.  v$?o,Ʊ R#i6x'͎p^$m]Z#mKF蹖(+3]<#حe[ga($# +ѭ6H<^5/#:v$2@צpHR7^sci_| oچ;Լ s[¾tx/F,c換 *-{<_2Wk:s},9wE$ ^l))sZm{Sꟴci_| |@t5 [[m.wj#y. aR@9',ci߃5/ˠ y-@ !I #*d~m{C/K>|E߀Z׌5 FrjfW\Z[$}ɴs^'Mnt,m.k(w"GmV G0CϴcT}ORO{E jYG|OPU_%Ǎ^x{]6ZAH!Cʪs 1|5u o&Vxvy.,ׂ#؞ܿ_m~63cCwX[Ksr[ĆI%j($w9OA4JO zM6^ YK:sެI[ƾ(53x&WMxB ?{cewoLjv>խ5+H%wG4;]z85?WX>$k>0m$ x{Jm&{FJP.R.6֦o}M՞}M{;؉dH#' }y\#̗~?i_| >ҿݏFB|a,n τO63`~a$Z?iivxbMEݶi27 wsp*FyF3hrQ+GWAGK|.4;;ujvO5ܓEYj2xk;+ºD7w76L[&;"um1'6}u+\Αɨ6WsA%’ v#\_ck+GW~G _i_| `c _+/~ z9DEi}ފ\TS"H pA1^[|Ե.lIEnxh"\TQQۄq9Kt9̓Hrj"p!j~o<#Ea"AR4H櫈y]ldMҺ/Gmȫo:s@*=ġe#>Z }xG|=|*wċnSuߥͽ ;r*3g)zlkM1ߝM%QXA$Z/4mhout]wW5w[^4KEKGjǁ>/ឯ}xkFm2q2 ITN6ֻo4mhoٰIukz$ڥЍfWq *i(Lqۃ9hR~X!{}4@A)VbWRP v?Z/[dh 'G#-3rme-}-5Kͫm0ı#+M x.R ,F5ao#Zn4mhou_և!|-𧇵3ZӴu=:4']BA `3Zv=zMSQ_H5+խXȨC~ur5mhokE,M48 (Eo£U݋g1p*;8 yRMHRͬc#˖}iZ/[dh 'Ga·}?_ջKx;&7!M@4{6`?_Ro^v{{g=ԫ I> ßlj|uZ^E `3IrR[ԶF> c\J788_ C N· }kiom٢Rg>v )|n8cko|Y7m?by|\<*&O5#r><Ќ*{7Sh/ twlf2E[Sf r\ yO[=|9z\;KuyQq  $cwJTE֗Hgi". #P־jObJb__/<Faiҵ"sYZYMsf"[ZY: XsTm{&M?o߆o|E4Y4;+gs~/ʊb2#'?x@G"? DƏVY%%Re2Gdw,־%k>'u:iZ:}ƥ ҥH$.byĦ0 ϩ~Oiw:-֥%ᲽE ege0oE~:goZw-cVP1Y!,#`>'k@ԋ ( ( ( ( ( (<W<1`=OJ,i o>x`;$2:oxczX,~hZڛthVE?7 4^fel_\.x-lSyߍ.o XuB-͎wRXL?('OS_3爴6Ǥk-S{Kw"I$Ei9{S)~"fXxGJ徙ey[d#,D%i);6OO| l5hI靧[Z-t= dFH^ ?Sxgytnl򣺊Mlh̃ 95|MsΉ4u 2 /SN;kеlJtF2pp29$fL_μ|)=W*<* QAz717mQE (^|; _4b<fj6\y\nUeUEyƳM5"B5SE]-&e?gF09~l̘ t1՟tkKɢTy!iXP̠g~bM=W$($&7ohzE޴s ,QF30`Kʛ@SdnH!9Kuq' bA=W]'"ލ›fdH>2|#i=R难NbM^"G0Hm[ȥh6޹եִnCQ==yꮬ_ &>;,>j_,^ۦn efyko D|(ᾩ^:?,sѸg#G 1 ]Vu}zg$2׆Y7%T7V .9zV>ѼM:E֏E{i3i4R(eʲ# Er??x+_!*w>:S?%Q {3U^3lGx /R|5u\C:Efhq!X0\V:_O)=*<?Ǟ C躯#?eYLt51nW][< sF\_ &?%5ɿ*K/ ~  wo7MƅnLڌ@I7yX1#(rx4η]BҼOizVo(bpzp}/w`յ/Ykz֯N,n,[$.YLǐMQav:G4MAs<ܠ d ? >*xDu_icZ4m싄2 %$:5Oz 3Mӄd,[qv3gXWOc_^-e(w3\Q!7>ݜ`Uɼ/kf]&e} WXj~.ּ?o. wxB tPY`ݻ>`@Wnȇ?06⁤\C_Wb7z#g9.6dddcCǰte<;cnv_ cſ2h!+çҹ|C#xVk{4G$&!g\(H=8h`M3ehԗvJm h2HI'88 _k]Udkx8>Ds(`AdށQ@|/wMy?D-uRZLsgr>dVp?MJڧ]AY|7]G6q3[ʷ'LQtf 7 ơBO-#Ц+0HkQ('n ~ע;4_.7[G h׏<+_4=>&i} 寬џt+@co+=XK|,eHĪ1ȡG5 m-,ѲE(u1y9?IPŔej ~? ux,t7]O5k2DN $7bDVhPo U]U?U~oO S/Zx¿|9þ6wkEͽ+UL?hEܻJFnO^25xM/Ŷz<zk˪sm FiX+&#Q @8Z$5{m[kYZIrwm?JA$_ ]&#W1E߀7vmFU{ZNrr}B1劏czgí_ZiuI#_" 5ݞTb4y M}1Efi޷ S. kZӬtkHĶW} ShXI A|ax?Ug~*ZM/Rݭ QKG¸%K]cL>>64V?`KonL$WHV,WY/i>!мC}#Z/5M.k[8]6,\y&GS?ɸ&#Q @8Qq]m+"w Vf3oAcLn>΂UQ3=|½{_W'`-[J4U?!Ǻ>qa{h@3ں`~brrmF*://XokyfL廂 #Y4[;ȀV$^ |L# k@Om%%Ţɥis0hĞ[m#?'둞>xk7zo.'{)%aq,{VوV(|`WS֢*y[I~(7ŷ_|ĺz՝$ mH%YdB0aOcRo\::4o_5E"%קK)PX<,$#+#iW⾛G&D,Ks ̗'Bbq)a+ W+& 펃oo'urJAll&<L7 Kڍi4 n. RIlѝ%͹U_OCm-5vWO[EX)NpdXoƣ1iɻi*px IM~r{X[Y~3\u=|+Csq֯g5_7A]e N9ouPxVqyhRYpl625qhԞ\_I|?evpOE>((((((((((K+gbʒ2B5Q~*͵"{WXFyOEPEPEPEPEPEPEPEPEPX=[GbMlW+_ϨʖSE\JvtBWw@pҀ%BƉ;LQ$hH+va&_x2:;ۉ>W`d,k6ִ&>>ޅBYZhVַ&ݑ}s#"" y y~Z;O]?#9ſ$K,IIMs}k|&}R9gtK Ϸ#v1< xR~ɩsk! c[+XK {#\G ğ t#H,w0:r* <zI(ГZ˫C7j scj3l"R w6Hw,4jYO|?HMwg ƒDb K[[U}+XӬo-4iٱIsIipE\$K&EO?<cvSIxim\y%esf^If* ,G~@KګOҎi0Y\YN>9y6ce 9/jmCSyu%ҡ[-yRgd՘A|q;o24.[ev7Mstb<>yS*|O_j˪XOqF]ZMpYev'3n߂-?/++A]@].]GBsk:[97d>qE'ۮ_4Q@ۮ_4Q@;Y.B*Q@ 4}~%P4}~%P53;bYRNMPhfotoxx-18.01.1/images/stack-paint.jpg0000644000175000017500000003746013222767271016061 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 221 250 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|| 6~3gMZ KI0$zF}Ez~">:x? =#o},J'ҀYR2jڼ^4PDau;{fyd`ޢW#~}GO"y< &ރe&ɠO }&LI `u4ψzw}էu=a{TL%TJPJzIVm_(aOw_<xK=*mJJԡ-cYɲTf$*c z >R~5Ɨs Ge' nï9G{]?̭3M)MC VZ0=pk~xW~^$ ֳ6'Zri3G5 TXpǷ=3+-[m \iq Mg|#=^e' }M-3t@{k2i7*CpZM6ief(ц:֏FMgkQLWPԚm?*̑yg!FӎVj7iC# !>}go¶ͯ[,WxMK1`f^Q~|4O&U;iD2FG rM5oקaOl>|Atm͎[#w&7m#8k/¯j_O]7H1^Z=痶HLM*Ml2[}'?DW <7gi^%t]+]e-lK2c1OL? '^[G⋽fuO\x;NJ@yE`r(DW5_46nO?)aO] k;I/qʷ1Ԟ}H]/x]Jw>MV",8P tKZ=j?jC>f1@~$~ziv9 nە,e( }G&]dvqŹIĚſ Jx"$\7$9 N0Yv҅VhvdNW'MOJg.m(=ة;GC&F9QFK\]h]=ŵ[f|ݺ4=GCllĈ, -OOʱM—$a^hRYms3O _yb-WyÚXϜ~s-`a;L#8=l?J{ߊ?Koi@&7;Np7 <-mFo`gEy@ 㢼Lj5)WVO͸v ,M\WV}ҿoQ^E}Fh8_?_Z=?gZ=]7> ߋuOk΍zo3OlnjtIK/&(mV_ "c>;YVG;YVEh|Q/>5e}/tŕ[$2|K7JaA|AҵWZ4:5+W@0`}?gZ=?gZ=.|U|m5M_QҼE-ޯ9+mn)$'Vં;b3BIIl^H%>mrWۿ?gZ=?gZ=.^>"o|>' --2'p0(o.k.lDZ[;><9Ge;YVG;YVN'KXsੵ t?M`څ]/٭I.4$XNT gIJƵx|KmzܵߘfIk)\pJ,_G,_G&kۺi> _i>%__&_B&'q|$zcںmCߏ=ON.g{A~cn#D/ɭ-G׋5vYQoDvظ y.E08,_G,_G_wjzơg#rai2\N%1,@ePF+ID W[ajךԦnlⱺ*ۦY"f H&=1NøşkøşkkoOwH_ j恮ɣy%źA yŷ+ Zm|m[\9bgUbYNdҾq8?*q8?*(Crσ5jlęlN8u'R=)*zfA??? z??? zvb*vi%mǔǐA#o= K;.)u jywϨ9 zq8?*q8?*e4%t˅IS>>0?وJsN[=J5=jYepɁj,_G,_G; ars 7o:qxK^۟iC?_ ; z+ӳ<UNWumQt+qdT{m _jov:fqKsv2HBm9 9դC7jA5 ڍ㙼:S$ $P2G~0xNԴcR<;Χy,$v@UPːH-_ ߙe׋$>t- 2]xPm(n>`Ig!J_,ylOĺNKr:[AIi$c1 |TF z6Asu{IVy!4}C]&("} OlM}{iּ-H8-[<+ 6u#߻IjQ^}(g@(=ԑKLu,m-3glkj/qI|;u Ir~Kݷs7W1Y^6El.KDe;vO\f{whM-MYƻoBU{X-m2K3*w"kZwtqѠ^tig1x݁|3~وU<`MW+O]&T6wwu((L< Fyc[1f9}~"3÷2)-n! vx1xsڏuv\: }oWU=GD_.` ꐬq!|.*/s .OU5bSQE# ( ( ( ( ( ( ɗZIi^K4w\5XldB֢2k!"7wT߆Y~q4q+H'dd3ȓ ?^O5@Y%x{JY鐻hHTHP9 )V INJiNkb3+Ii=̷3_޸7nǠ¬=퍵&"W0::d|Q\ 7mi-%=p}AֵbYxPcm;k'TvFulnFҀ2%܊5_jr-dXFm c[4ii6:n$H/dVXI{kYEm׿Kֿ$P>W 'Btܕ3:;7oݱFd>Ym׿Kֿ$Q{kYE05(ZfڊN$x%0orP6чCWiQEQEQEQEQEQEW/߭&W O:<})ݷ}6+>=I_ûZ][k.Yc0OAlhk 藚e,|R~vb T)_Οx^/fԵ٥!47Mǘ$m2+,6nW-Nq~4~mkuZYZK5zTX "-rG߇~k>^'έgumB->:vm KvݩyLhrLf8]\^{M*M:\QӎSKZy,2N202ڭ\E5/{uMJ|;tntHD*#)__|6Ήh_יe)mgY- hy22$F=*VmLoe/=¾#n;m/K㼁pa_,|?&p0keKxGMb_dԏ#wYPB[ 2I?&?_8\Pwbx?]z kVNҴAr.bhUUBq885_?炵[~ ПAm絆1_Ih{5e}7VxFɦ__'$nmJ--R7 -`1s\/r"O4E 4k6AkjmFe $ Yo7%{>]ߧ{^-յ7KxWTm WG4nb.u8l55>k cOjV|S}TnoX#$< *_}y.x7Pܚnx#O1Ri"FrCeԊ{]XV^ԴM^Iq5ȶ+7Diei_fc=jο}/YqqNҹ'O@_bj>agW6PncC$pI*3%),l7Pø<@bj>aիEQӒs-ơqq)[wl5U(*R(((((($$PWEcGU;8LYOpkƛiwsoq=3\[aH1`\埌o-55MZehϑFUýAS}˟ܯn~>cO`*1Efbv £@8K؜#I-^?;ŚֵGľN㟲C+;cCtmWSΪwf88?|JgחׇW&D ŷB'6UkJ+բέiVWZE:']6G!βI! Ǘt-5^Nt >~"dҬ[t"۠htU8qU'gq5ucJ(((((((((*äYcwMGU}YMI,,/H;Tɧ&GtW~k|j{O6jsyܪL׊/'xDto: w"N{tQ+|pqJ7/_~{>ܢ>6xObC^TaDm _ˆGJ7R動KoXQ _?B֥Z7,+>]Q_"WOе ?+ZԿ FP//KoXQ _?B֥Z7,(}qϹ'TWȿ-j_Ji/ѿaG}>EkR-WOе >?$\__ /% ZhKoXQ?a rO N_#WOе l) mZi RhrR}qS, oCŦ/Z x<N+.$Jdp랠].xL|L}Ɯ챝cNq (,QI8 L"i6Bħ؄( fѷUOMa'61[<WBͣxyu9Zv7:%9Q@Cwg1$H6r B*j-wNV$)ff' bHI4<ks{5]6=7}ַ;hR(cUEz :!tZc>ZFn?9_Xa˫E"3#n3fѢK>♵"]S4p̠d[y xVT: vE"e  ;({ķYzC;}ԑeO{] T4]jk+VQ~7~َrzX.#_ǣIyy54ZD&nnXZ+Ė0I}v]'F+O0/O4\(_LzRCL'{W  }>Cx.}ܛS1'v^4량33nxx[P`k4h<̐ N~S ,FhAT*0=$U*[~~mݷ 6_w>9k6_r=>K=-ɿYŢ*̱_ . ߇ծ>שKA%S9Rz>VYꡠ(Mҋ` >6_Ҽ9@]G1{bmї9t+$%%%8ʿWMʿWM}qEz?5??/>GUE {hUE {k(ѫ#A/ _| K=z$ K=z$QWcG4_W{*IW{*I(Fi4)?2/(UG2/(U_\QG_iR3e__Q׿&e__Q׿&?QgʿWMbbj0@Dٷ59h+n0 ⊙c4?54)mwnxЯaت0}RFy2d` ɮb}׆iq.s vL./CI[ᢷH?|F %͎د:ykV5i՝\{phm#zj(EP{i.UDwsZrL! ?]W*7n 쫯 ?3{|AJʺ*7n 쫯 ?3{|AJʺ*7n 쫯 ?3{|AJʺ*7n 쫯 ?3{|AJ"[Y.nd !fr3TKh7,vYyd:P;~+|{'4-3W=L~kkXqv3.M_OzDžt]!u Ab{~#Ȍ&ӷ .qϩk ZmSZj3FIssCQNOu#vv64{[~i X"j0}ޞ-_=_xl>Vxx> S R/o, $@1|w ں|X-9nb<=[ilͤct"2o&7۴_'>u? ^C6bi2qlQ"`B85x-vMƳ͗D  J$1U5v -b09doj:~6}!ѭ5tK`UtvQX5 <mq _HheiuLݑW`Tps5R|"*G7xSBѵ[>ZMc6wdєôxfp~[_|ogm^-Xl;>M*7+I6y$AkZ5ޑx˹kU;+W$E'Gx⏃?# >ͮX%ԍff5[2EX,T2'7Z@3že$Ӵ}>;Fvyn<0y-rNI,0x,!~ KV΂(((((((((*;}BAd=Eb½ Z?E -hE½ Z?E -hEvEXZF,`pTZrDRҬAZ%*=`qEGZ/&WkG(Z/&WkG(Z/&maH-XPPQEfotoxx-18.01.1/images/flatten1.jpg0000644000175000017500000004005013222767271015346 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot02310100Fotoxx:resize|sharpen| 5http://ns.adobe.com/xap/1.0/ 189 300 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xjb7cÏz(5ږBC.lqT{kۻvWɟM}a |NM YgJE[^p73+/~ AҼ7s;2]rA%{i| #(BP>x5?xZI֓IY[kFY7fE* 8"اdsξ}G+Ӽ=1h֣\k:흿-1k{^MnK`XvL}QͪGlxbRBFOa{W}/š(aV F$n4Cq$NdYQ99M7ƯxWONƨw6܏,NT}sTg/_\jl>|# `n#+W=;OAY*?.[+S#.uxKB|3 Z͞ou})-GG;mjK,k[I$/I':?x1Mnu_úU̒Oo,& 4gH 6t}6z[|3tS%ȸx"ȹRIfם6O$y(aOzwů-axOMNmRK$eIgHQCC'c$jMc?tijSo {6rݼpd0|i$Q2aOl>|t/D]2J[h! "8ȑ&t<`q\ڽUMX7 *K$5Ksqc^>|PB=Wqcو[M9VPz2[i;@?Z'xRD(h$/7xx+e# jKI/59 >mBTżJB{8P}F)ZW6{wF@B˸zknͳ:.ךeH+ ie";ߦ9#Y^[nݬW@gɸ+;0ӡ;[[g;-펧pEs-ރ8o5 c69\ƈ0ZGѱֺ/zz5^ 95X2U`xןGGYkq>Ú&]OW:c\B =$} O|>s -qAw(WeOkSO,X%T?bzW'9*3dFKb@|nQm(m;liե>hNM}k/+oG+oG.~z}_o_=o_=as'ֵii4;[Iwj">,TDZk_w7oxw7ox0'ĺдf{M&k5OREb#=El?|ykX_ NX,m-.?|P3Xs|6.s|6.G㧎ϋ?'EC{n$hB6N$4<𾭩z_f'U5M=)"hW+oG+oGw>wv0^jO/ݟ_d R񦳨Is4ܭ_yqI(Lq_zùsmùsm(f% n_ϋ<[;'ӵ}a<,ZZ`y&g->'ۗgu{x7M B^ o_=o_=.^>_h;? +׋+i"Vm2N^ d:9⟊tdqگ 9 z? 9 zn-_?'d|C/Em;C\2hs%vҬ-,xg R{c|J 'ZP Cqs.l`.zskw7oxw7ox=wg:?xW$V3so[T9FiG| +oG+oG+ቦUWLc+`kF\]OϊZ6ʻy@==+q 8C 9 zClz+oGp8(Puf9/5bH+ώxzEᯉZ$ >9~i%w1Fv%xPr@>[G—sh~-[]/\񅽥Cy$q΢EkcLd[hM;Pb|1(kRCoHcXJ&2m-Ä#pWW_xzz?+j֟_-ulֺFs˔|0S; 2G>H~#FBWowҨn2crQE[NIm(ą"+<)Vm@3%g< _p?Ǐ<x߇MۖAmUq asbJtȔcx:gJGqR?O> ?W Un<,g79M6 0;^M2 #xt6K^+].dbO\o,񌂟whl6ֵW}mZφkC?h6:i%&-kE%t p2 W7*EUe?3|S5[P|gx3T|ƍ$m*eQVRv mgLִ)I*AZښAEdS[2SNmnwu/+ S[2UmI]ZKKS0ɀm(lwPs]=exA_Y$?ihb)hd#ޤx>+hV[;4R \J 398\ <;ƾizOE/;%ŕW'f< $Լ[W>u%ޣ4tAYh Ċ|IG4Z ޽ i,Nm92ɖY:x&ƥC~nW /~dhDE @ֹ=i7wzީ +ڋ+}Q7uSAϩ~}X_j@ר-L6O8PGBI+ڋ+gKMټlWFKɞ9eQQ)@ G8cVi+^#ZUGgmgcuz͝mUԒ!i ^Ve++ oWNM\#6s"tCojgIW z?_?\_H_?\_GW_ _#z+/exe;+q=^/./G_?\_GW_ *|G)>_Y fmx0o'fw{UMq[g|?ŸxIOH m^-# EIsq%X Vo_If4ɴSSRȷI/Y 3$ WRWm@_ u Xh_[{[^D+-͖Pk[Q2?&Z[N,yˇmx!IxϴTM71(k5H PWK\(FyINF'x|1Ք\X~).rq[FNORM]̮_<,Go=σ [_w(wb u^Ե ]?#j7z@1\JI,GPr+sÚwB.c#v*Ð}Szcdj]WwƥI(u99xk48QaTG d%b t߄Vw#5=B{F ]@`%+֣|[?_'YErZmh|o3]e¾O j?ŷkQ-uP' ?5fWAG5Q@+֣|[?_'YErZmh|o3]ed'Y [fǾ1@?Zmh|o3Wk_Ge(ӮUi-IbVrZmh|o3]e¾O j?ŷkQ-uP6ᘴ n%K' X\GճEW:W5~,4V>if*An OXobJY xΓ'ľw\i\; G@6%ʹHGN1GOc,l; Qu]ZG]\M5̞=qϑŴ&1@~+AWVyo5co.dU-m72K#)h,,[nxs:-Fo]]!{HL;$eXצ~Lj>!ӴC>[ oz[n;Σtk-'pV7l|_Xk_۶lP2 >dw#`Fd|]7w~n"n|kvI0aR\f_78/`s#G|[~O''5z EuCVVH6UclʠO,A<hkK'mh.xF].ΐg?hh܈  ~Z}YkZ;֍;9 /jo4K [t^jz|v-n|(=ozR3TmaE4t>=ԥ_[^*ӬXdNKKBba*/7p-[G6$kx" NX͐_Zt}뺵sH? G0?_C_$x7Qa}޾G_c#bh.iGϹKLg?޾G_z[?'׿4 й _M>/D91Gz[~93^_)3‚ ㊠~]zA Dv44]jJq#+xB>@q sVoc]DtRUVYzc}"L!gR +uV0 y&myn{tYg )e7V(5  ԬwQ$ >Pwø߅,=#{xq$kg'/#q՘s\2?e.?e.m??O- .wQ .wWSi>߱fϬfwPxK⶞ &1!cҶ(mϕE Դ^KeSD @Gs s@{9Z4Pw?ڗ?㕣Egj\Qs@{9Z4Pw?ڗ?㕣Egj\Qs@{9Z4Pw?ڗ?㕣Egj\Qs@{9Z4Pw?ڗ?㕣Egj\Qs@{9Z4Pw?ڗ?㕣Egj\Qs@{9Z5&}w_[ivVEy.$1T,o[Թ=Rrğ k/#Q3G@.{})Rqѯ-j'4Ԣy4wƩ.'MBxEQNԹ=-bγ8hbY pU*!y6 ޷hޏI`ֲ[ԉVϖdRC/ ~К Υ v M7U%$G mk> Wž#smڙ՞G!l`b5;Kм5%ΗVRGq$o=_h}v9;X:fuzG)N8'ִ:D6wڅH,1 3F6;K.4_AԠ;j lQH R9#>W|ľjւOmm̞l5ʍEk@J]zAt -ηym I4 𷇢TX/)V'7mO4/'e !H?9ĿZ 406 272 0 C     C   n" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?5PA'?nzc_VDw_NSXXS  |ǩxK[#լ/5kԹeZrp1[ U5χo ė6I'"S AZ뚶o4x!n`xQ2#3+Qp4Hǐ 4v)=|?|>cӫ\*GHrn@V__y/q/)ĖsNG0z}pST^S*5msfզB˱c1:qh)KW'WyLȍ9)l>g^\ 1MO$[֏Zu41Mh}ha?G?_G#9 [֏Zu41Mh$ctbv*ppp}"_ 6t'NҴ[})kK̄ 3<۠1MG?_G$']+K:$e>WռNS72f#گGV1sǢ: ce9دIb???n&$;U~_|GmM*Xcv\FIA d,y<Ow<O ,VpOkw.ƪ6hGS$=b???n&I4O1[ۈmu5Dк_coz Ek1gGnxY 1 F#ۯsh吹r:MmC9[5cb٭߻F">2,[d^Tt+R<ri5 {rճk@#괖ނz+Gb٭߻Fo5|')O܏c\Md%α X滒$tv@#d\mg59'Tmeex\}Ty-"A 'i%ٷ`m-Oolݏ#S{ V"W"kfKԀl̒SIzիyF'diRsY MIJ\RExf>-|v> M4mIL&I$;yLժ0}y suߍxF{Ljާsmy{/X㘠.Aʩ9s< SxڵLJ͝ձ}'TM nF7-&U]YL1 髡mG>=xľ(IcFo%Pƒ.&I@1_9?G[_ ["ڽs2H < 4$oS+~(|@ִmwZm]_ȴ-Ig`r[>5|CռIxCKӼ5SnoMFR U;x哐|S-_/PnдW|L񧋴xK>Di[dLVBb;92%N ?3Ğ*] ZZHOϷHDK.ge-X >rA]%AC_!㗏|E;Yxz~exg$ucr4n#rJ0 7VB m4YԼq+ojz.[NKe#V%M\;Piwv >TUx(VY0@b|@&5&ž,u(/#XZ\5s ,v*̼t @&> ##𮇬Oىnwl d|ɓ/bWe5&_/ 4j+oNn'$/8 k:z^e崖HrH:I8k?|/}SL [ +YnQ*̳O$#Ƞ'=}k'O|j7V1YizX2`BqT1ATdžuX4i4 4^V.'bw̲8z hg 4Yċ>ctuԥ˃"LPqQm.Ǯטve3GoX_ v@--d5:z1jh^V4/!X]B;*9G}-^sK:2A: `.m*wpB +ؽ姉khI"tK a>JQQh+DM^b__|9t[-94heejOmzҼƒ"(@ g?ϿCkkk3[Nn/C*oXG$G[խ;:-sn.ವ!i`#"UEb̄wޕ/xoi?Y0j;OGV&p9Bo? u-7QxYۋ/ZGh6Z uq}9x[:tCBtx+J҅#hr]Iwo,r_tyz?Ss 3=$H*},nZ23W{L⺗Nm/ⵞK[dXfHܩ;]OO uv|E.RLƚT~ӭ 6nw;%eszw~ʷ~ W.v27~dn#3J#7[_gw]OǥX|G54[ȴD[[hIHo +OLivw(NFRHH >6oxKM'}t\xzljRqhc1ıNۘyk>+>ScvQbc(VZ5VS2>e#E7u_3I[_#$xK[ѯu};:-c_O"U+6?n,m4h.ZB\< V#'uKMwXf OqxiPKixeᴆ$+J~vxV{Odu0y"M2M$qďԀD4 ( a  W8<؀#og#OIrct 19?+AiLY/t_HCU孞;1 S1G'w?Zx|Qd]_bу9ɽIzc黩hE DX1GC\47Ev8EvH$FAݸ8 41I]Y_yuB]im|2aEjj biwyD;#|Ư/W9Di|YXXىEݴbFb T Q]m-j&ֱ ke]A$rsgK~jjqͧ_6elu[Ec "Y1vJ4{uO3դ5 jLVz^i,⾑jWm4{y|?x,&ViY>$G'0ps_DO~ƾھy l.=ĤG10fzn4c4:ڶt,o&IR',\7 p۪asfle Y2C=Ρm^i:|Zsuf 匿|`W?to/5Fh7DZBAm[Zg q笞|Dl`rTw;T 7&zՒE5Kq Č|nUʍ7S/4K@2c7G/ 蘱CmlMywu욼lHc5ktkﭬٯṻMB{U2L; ќmo_ב||⏆ue\^n+l@lGI'±OřKMž WfdwUC2>CʃOSVԗRU];QR+WxA*1Q#O88YZf)moʺ(bbUp$"(F~Qͦ"'0hnRvWi7yaYٝT' EUm-t{FK9uKfyFE k|0koZ|s^N5xku'VKCw"OX19ɮſtj5K鮭~9Yv:FRT}^ggmoy߉7-xLQGe#egvf9W~\Y Kgm?@[mimFIs2AHc '7 |> ԴI洷k;R{ mI^Rx?}˪jmE嵮46ő.!V *džGCnVz^~!gi{>b[ i9]; *[x?-[Mܞakaq=݃M3HYH6kmfjwlV,V yqn̒*+h-6[[H%'=:#*JRYn>aTG|K@xޫ_ k66@K7zFxЅk!!]}k|F 7^ORiotIEldd \~ xcźj15oco$o9%ݢxduy;?|7Ѿ?k[wyy]uss.]K!,*o7Vz[_h 59uKM.K4n} x8յ.=&oxگ!#N5]RYUk91 X=1\;"M?Z}ޕG{ymr[ Kbr8A)jMΗ Zw>ĺ<&]ב 62ǭtVA SjZA!TxP_|K{O xD,D)w2* mIEq$msLִG:]ԡ[Ҫ%9+E7?<1OVtyuXm"4]N& rq@xcx_zO-`ʖm"+ѱoPEPEPEPEP^}iDv7˜Q?95z yx{\nbKi.QpO#HAS3I|UӵSĺޅzMG2E%D񸌹 R2FpyC>!_Y/|;m[G 2۽ْ95 Ăk?E@;__ Jg;a+S:̞EVI\ӵNұq#9l*{6׼3Z.< v+I m p6DMv91//"BZSz^M~Jם˺=[ZEg q`QW TW|ae˽2#2KͼX,ڎ~ AVTv+k&%2!1ۍNq[ A:~O_2qť:,'WXZIdUhٔu猟]۟K-_Yl&rݻ0<>v_ȗ%gHԴ;z\ΐVg&Y$Riyrd xlj>MOMwuEm}]Zxj+KOKX¢'J(wzc[G_t{XmoQբkgP[l&!@a39א)xJtҵm9z`k$/=sd#|a0ѷɂ zƭ'xWRckqkE_joYxGUdЇʘݮ.Ժ Qi[\(<1žZ}+QcZ=G]i,|xvZ0di]o퇍ae_DžNKA#1/V"vTBZ;~Vv_ϰh? iR7/B5hɧF[yc 6W2+8>~4<41O,Io+YrI})6PȊWW?B?U_iBVk;2TU,U V̂cq)~׊4H3]\{K;FngEv)3}/+]>!j~s]7 }j9[g'j +ܓk翉^6|yMZ_H4#k\i2d%;=ݴ1OMu7J0.Wn2#+Ǜ_9Y[ay<%>x/ _-sׂG@.b-r h*xQm%Oh(((((B6r>r< 귒s)984EuWX}~VH彊7֖D)hbPNy5 :eʹv)B9mH%HDͅpG}Mz+o ͿƏV<<%m6{WPof [dx^6 !PBsOO=:-;[;a\Munj3$lfߙHX79^ ?oJ±>WOɩiVj3%,A1@_J>"f?ON?m¶jCM Ҿbzٯ7/ 36ŤV3i41+ l~c^E0<tM拢ipZD42ZK6sR@y楺sk=&];OZ^KA'F 4z=k(V]`{=F=BG;bnAOӿg?׆Oeq-uq[_6.MpeӞc̭lE#?k^~-sEf%;,lr@D s Kע6uw| x? ]k aE-i?EBn[<ȈsȪ ka&W6VY)hl2yx_o^×zu9^5w2ũo`,avp7_ğe; >SUHLIFdkEvzZ/ڧqjHIAeiqA-$V"K'cUBarx~o 5[lϦ1^hy?hުCqxSX Y5!N7IX;?_]x[-I;4|Cu4gmF)q˜ =hw啷8Uf|9}w.~.ѧta4,#'ޠ+8`WcMW㆗kKKk.ltln帖IY'رn$߾BwO *z%Γi=+8q-p9ğ MeҼB'D5\9Me007O*ݮ.V?3G=񆷦:V4ڌ[@ip# [sF&+ %qKo;ߌ789w}csgm5G F2Z mbޟShi|v\ tz|C煚=WIHۭA٤B.a*&5k`T_FKz+>io:0}O}ZavElՑ|M3Frv&|yOX]Z(tᶺ/ IfR slgvϿi?i/|75X6?ql}$x9!2-n#Ngukw^ҞN0闾1կg6,F#o &p!qVlBm^H tzLJ/% a9"Uu<ezWE^}K-cto7onv쉧1e# 2Ikhe/0) ((((([<Uu|[jZS"_Dm'anYC@E`h^<kjFM>U_)Xf' p[uNEtQE2YVG;Qf>P{Cj1Z72XGwD66n9OExI5+EXuMTJl13y`3A䎴)h +?4!jf1csocߞ+S]/_G>,߳Ǧ>EPEUuKMKoK[8^y8K3`?P=4Q_G+0FX p6uEPEPEP_4h~:>-|v 4y!ԬŬh^d:eVWKŒ(y9m}/\hZGaT+nPxSWMwVfg|yoxZL6O_ E}:68B(U Tcvl̞2aY|7o iեu>2Cuxx-q,~+xiv׷tx+ʊTٙt˶cte;|NZ nQ[TԖ7#bBIݶf\I'Y>b7(@ҵK׉gox6|Y\#+F m.͌2&!9.pĀr9k:xH+KFmV Hň!\em3KZo;O񦤾5.:"waZ3!~vR q @4/5c˿g=zTMe=V~,nɒb,MDQ Xn5~|YgS/]s,"pX_&rX΃iK4k6;.WDӒfl-DFq ,:18=j$4Z'w??+C|@,-Ο%'^8 w*WK/E.D|M{{uLZo2En|5#XX6lJ;@2qnxZk:ͼ)Oo1#\O,TUfdOյ-B+%j~ ljl S:z_]j:GgR]ML׋ Wjn۷wy~(-:VyMCc̲^>Q K$Uocmk P$]# R@Oz_f/JN<9K6*4k_W>Ks~Ć]g(3SGU4?>&]j7u=./I }9[ε@fd3FBOdjC l]I%NBI84h\ʋ&hF c(ur)kj7 n#gb֤HcbM| F(V.}'MkxU=S%[:}wIOd >U23DEͺK+yh4 ze4TDVHvl'IOm-u5(|?P?xR=;_;mJ7y 1(on9{35aN}깛 [Ks$O/1+5줨%1ubG۞1״}sVXv ΢Y5ETR,xuB*It9KH$D1.zldU5{Ilo* i⧉4O Ekeao~L"\;9Hǻ yn9x/KYxJIbT*RHɐܕ܄8 ӭ4--aF;@OMkvm`7`m=7c4;)+^V|N>$[|FmMw? y-5y67$% d] uo wqh=>q]O 3, BxQA#,SNBہn֥Ҭ vE1W;˶֟s+TxAíK RV]L7d5bc#eMf`1dA+NoMaHsmHⰸ S Hi2X1zwؾkLJyw~ភxOLYisi1"6)%Fb7n cLK ?Zs>X\GӠѵ%XdXkc.慉ej,WE09wl eI$+7k{~-յaj;pʊ#]Y8'=> jQE"7biz~e]*_`iVv1WBo+v}E G븚χ亹rk$x^[֕+s/LO+oˊm+_Vw;c|C]|Yim{=v>5d`W9RrPآ[ [kD׌6s/]?]="ؙH%a${Wz}^ Ɵ Xh2=?Ćm`4o{`K0 q#0Q[&|:_ھ+]/)tWMԯ^ݤ[]۱ض7nva꿄>.]B𦫪\xBNVE"uV%NTH z-RGUSH_$%sN?~ 2||[4|Q!.]/MlyOy{4~ N\Kefu+ 3T(oR=Z8D;:,Q4R]rwWT%e/鏿>֯|SB֮|795-4-Ώ}{W* da1e :>#u[$!~"\G4ԯZ$eƢ6w8'^]?= h|.mipF;BE4m/mgWէƹ|Q~R[ݭs0mnpٻ95M5m~V5}:Vl+M[ˈMŪ`ۃ:34y=H隳,-.s{x?SmQ寧Κ9D{@õ}kE4o|FG_0O]xQŧ"P~IEğ(mT擸?NkS,]N=3RZFAtvdFB8l0ɱ$EM2  2 kKy8\Y[\Q;݉XYye+]M;Rtqu"Bok`S+pl{KUo_%k i~wE&_ZmCQo͕] ]^O=\ FP$Y![,t_ guCX6@2|p;ZJ7m_7^&{˭e~(A?2܄6D[00! Di`@;c-k|7qquE%!Ms8{[N7 .OUo_]|KĖug_xfB]elF̈FR\Eҟ3H'y%}qa}coylek,onVcV(n(PQE|⏎:?-x@K+YA#8-qqJT`u d{h{쯼a ݖ7t{ͦ|yw-҅3?Pr[?VZ.B]YѮכ-8g>Pݸ)ۆj/ߙ I[\?hK@waUFUSKxVkY]bxS/0#n=?ɥ=žy409eUU#,2\ 8Rw;J+;׏ u-5{YZXϘɥMw$Su1ђ_i+n!i/o{{ҠoOH/%/x\k<@Ov-h iXvVz#ʿv%žWtW>?|CiZt Yx6[H.^wr{U6 _ў"mqA}i[v}h.o>.hv[Q [>ldq?_gZݽZ.T EF.EĤ9UrP'9i|Az:%ힻ ƓlgF'vPvd12} Ex7x߆t'J5Ex#*ͷw2CNHn-oo>`y6G-;mu=_|;;xXP4+y5" &PA.IT7Ïڂ O_ j7 ^h~,Uݽ j`$t%-U?_[_֧tW/OO]R\^-^lr%5 @HЗ"^u-FĿzZt;B: +uٖwo.s#m<{hz )hqB;"@IkǟΥ7$5%o[kqop>HҾwvU:_K^Ҵ)4cLe? dF$o#h)nF޹{+Eԭ}ZsuZ[X٤M%Od Vb{h,.j+<Cxo.e jj:uQYvnmHQds^N (Š((_ Ӽ_'Sō-me.bTݕ.91=|o>6{QO]iQ6[xpHWfW,JS_Л_|CKTii[ 10* 0l's߈W-OiNkca(p7'"+tgQh_Sf`F쮯I+$~Zi^uB[\[4ŵJ0Ń(ۭ%%{{ɿ_R>kznO+hz:ƽqY_yZ0gPpC|׎[/7W:l/4٬f+m4w ?tT\ q}ڎSgh.K+q}Tm&7+<ɵqï;[~1x@\[k״Evӽ>v4[%* *F!uhi~}'-fS [&[SNsc0UQIi(993\t-GlI`,cf{۰(0!'=UnMA5+UM.Fm!h<|f>bÒw'y1u_1x{ӥ]@<[yd$ʛܴʛsj{ֺbRG!-%)n0'RV|!PϬ| Ҿ Q^j74d.H+x3 Xnʘx+AF@Ūk jxfVE+nmg䍀A!!xU`=FN_{iӊ}?fZMY]4%6(Ib@f'j润7x~5Nu=wQ CEl!8dc7=_ j= >cn`|IMT3ע׼'Ɨ.uhM^o K媓a=Nk_dK9~ ď=P[M&R%p)h׍[i+ !~|7޳Tul(Smsi-%嬐H8܋îym|%hj^u!u/M$6,+Sf%VZSYCúZлXb٤۔i ![ eHM'e͒|s >|w-,`j$r%7_%Dd0fQf+vXs<]fX%oKIeC6swS+``i/5}k@K>~G*o=+&M[Z׵+t-n-Ln2EA!wtH~ZƋ_o3hv.q 4my*J^^K_iv_V)«Zڟ/ab4F7=R8qw}{?jzq\xB0I]@+20x*y>ke&öv1G|;\Ek!:^stǐ@t5`ף` T8mvX4|ce㙵Zm%b?7gߜgio<;3Za<{:Sr ƭ= vZ ;=:TMqqj)HLsFpp0zɬ?ig<<֬;s{sm/ IJKd2l%FA U^ZG|;+Foy{a7"z-u3&WL@"}n}tmjoqaqcs[b[h"7(C/C^gW 0F!  sWpV[_և{ETQEQEwox?[ik}5{xn-Vh?9YHo1`sfv:v!y.40.5Mfq᭭!adИs(1r౨QJ[ z{:m_XVNmzwq- R@^yrs*ַ70Jt{MFk+[Sđu" };,>jSv08%Ei/i]/JKyOr-uDl#2dp_/m3~%|;|o+Jӵm#Rӯ㽹k>ֈKqÂ޹m jVR\NBNK##!GB#Vmq~#*.и%b2gZki|)gŪm&fA-ee>`JAhNZ/#VG""bZSؙk[!"Tz*W?uxѯu{-*"IKporhQxQMcBkXEfw$ip!$8E"(7r'oI~ڰǦwfȲxz7j5Y;{e?6؜cq:Yz1oƛ,>|W~&Lh.'hǒ#x4y<'⋽\}Q|5i>n Kq'Ղ csm{HM}{%l4`/XF@xL:hκuo$~LkonАFΉ̑V|_=B[O\SR[#éX}HMwgG?xqi~#闰[xw~!#Y}sc?&\2|~a| 7VjcA̶okwp7&6{׉6mֻI3,gUS*LД.gdKo9Imko̵~^ z{-r@I`XZ+TkSOt^ִ&}N]Yt(<l])r $Vv߳m o餣ݵqƒ.r1_-e.g w1V>=mB|EiMtn^F|r"Av 6^ "Q.[imo`Ե'h}@I#HU0=ϱI8f/{~{suuҌ:e2/ {'<`߂7+O( ]|9jzƝjʺ.U>k%\r>E;p(0((f=_$oxM&l-/ qCDلM(;\ 0QG[U||R~-SX° v s#J&d.[HPb:}O֕֬q|HF@pH@˴`ϰJ_ |<+y~^vr\ȭ2H$,FK]HX~Y`ko D7G(8ŠVlH~ |1R!VVG$"p9X-Nc><^(yCKYyv zV[-i1AiSOhzO*`ERa@;w߁>>.м.Y&ٙ_צQJok~C{g~VZNnv%` ;xLAI0FpYЫ|~?" [ O[~_Z+x͂8ǨOox/4i H1[,<AH<Տ ~͖Ht/kti95ʠCcRJ/4aESQEQEQEQEQEW5|BҼ1c{ E2ZE9Eb̄⽺t^ 'Qυt ZķVږt)n'AKv7|)O_oGHo)6n2DTki՝ S7.t-F|>񍖫yiY J}ONO@Cq "$7/p^χ4Aj uV\HѐpA<xGu]?O\դDQOy yNTv1uw}%^wo>/ƟH.}N;K2H#fYB7܀y niҋ-׌𵾱<^1M2a5؋I̛ǮǺv;pr3{^1OڄP#Ld-cZhK5q ^W$~\2eowwvл`ĶJa7L ݻɓa+>(n]Q4>-/Ftk9],Fx uN_>iαNZ,.]TBr鍧xWvo_{&L6g 5Es,uІզ9`lIde IQVĿw=؛H /۸o,ݨKgETATFğ('|l嘵{g\lYG:+wZˁ~^|asBźz"^#hV[tī1튧o3NN7>[Z7^Hْ034e([UY>hvM Rŵ;qw-֞X J= r~A/9lY/q}eatfK9.cӢY9gz)-km(e&q^ZKk=v]\?l,.4i.-k@6^xKѴ4>W,P\Y\y,nXaJn|l}3z7|]ΥuK}D7e~J噜^q|k ԯ4u4Io"-lo溌FFzWA@?@_M֭pQDJr7U-6Wi~zuqkNi.ZVI'b8ASOK X=F=2 2ᵸ"xi#Amg`[ I OK3Ş0}t}]"I44Z((}ckM=gkg%ωMx`{xmlb}; `O6 ]COsHڴWdJX\U,r#Zprm ׷=/gp<^ktŚgූ]iukjd5ӕLጝ9{Wz[f]{:b|eeX/A3; Σ,qY1~WZ.=:KY4Ltp\3,djL5x. _{t)k7z|]J?GҢ-k=o>.4 _{t`Z[ *+7z|]J?GҢ-k=o>.4 _{t`Z[ *+7z|]Dzo?-k=o>. _{tEf`Z[z|]iQY?@TR*P8ΊZ( ( ( ( ( ( ( ( ( ( ( (0|{}>_ZaneEꮱ1S+_ &}V^XF;7%rpOc]wā~)UjzA[wmj$y2?dO^ss)n ?ZR{[q,6OB|/BΉ?/~(zjRZ>W  .U'b4iʆP8}NݧWw1jcLi!@3%n4%}ggV>',-.M+@k++K.Mf[+d4hX~@4" "_k,Zw$+y)c~ez^j7KhyNdQ7<ƽxzsN#fL#k~URB5vf[)-qG|) ^)|Lc]{XoxI텻\n!a@-n$97Ěwu; -> ~-6F IAQQEfotoxx-18.01.1/images/overlayBC.png0000644000175000017500000000173213222767271015526 0ustar micomicoPNG  IHDR@2cJCIDAThA 0xʪl \z {"ۭ  w&y/Ɍޟik>^j$'0BX5 @8hq `@ k#q\!,a @F 4xns8{0o״Z9Txضok`O'+ Ei6ef7 z.1zTXtRaw profile type APP1xR[ )zc a TU&l4 1'sK9~Zу%O~BN@p>[k>ȫ#rdQ%u-Ѣ>n!XfF&`fLnnF]Cj2S{"f0'7Ã>NYQժ>4a)ҤJ--22˒4B6X#BD55ڏf-mvnW=AR iTXtXML:com.adobe.xmp 200 256 /IENDB`fotoxx-18.01.1/images/batch-delete-trash.jpg0000644000175000017500000002044013222767271017271 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230%0100Fotoxx:resize|retouch_combo|sharpen| 5http://ns.adobe.com/xap/1.0/ 117 239 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((k" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?6*"E[ v}, j[I\oKVd/(}eZ)?[?nYwO%)C]Ţ%)C?[?neum0H̪JTCTKo()#?քQQE1Q@Q@Q@Q@Q@Q@Q@C1cU5TmK܆ QU/KfqU·C'$ zʩ#Li,CDHCp)޲xY|?z2-&'o{ ApȓLD3#SwmI"?V"< duČ>f.ۇLr:L[xܬ_[ T bP']֑/rlG; /.bI;>\hJKI${u)?е mT?ؼvXvGSHvNiF϶L $(7~aVZd:ndI#uv$S;Gמ^ Yi-_kHSzD rN8ۿBOFuO]!.Me3ZFѧF%pM7t@&Gx|ͅidB#z*M%Ji6z@*^寄X^+u Җ~CE([ A$mߧkv[e-±{98la²}FMn'!oMjPDjjqrgpbIU8x8.3\\C$s(^R}~ S&zzlvpWȫu{i<H _ЅN3j#fC6zg4+O6گ+/ oeB@bw180\;xi{E)s Nr)] @8Z$l=i ]oxp}.eD\hP@r{8%vsCGG') I.,`'2R+?3nfcv'SZmdUvE&4y4=WPV1$X\51;7aȐgCT4)f[)[e&vr*)}viLF =3Mm𖱧 {vKX̣+lpO^tx$Co2]Dd A$@]i8N=),ygƘ%UpgTF|!/lAM)޿2{z~^閚_$z=fj |ذFSI[^B8MW}:|(Xngo/瑌{guuأHb(Qc5Tt]ǛZ~nkQEfaEPEPEP0cX4ݲA|P6=ףl?z}ͲA|'^E3l?z6=ק@ '^A|P6=ףl?z}Ew7nGs;l?z6=ץ6:햑v|~;?A _o9@?mzG5Z#>BPO?d CrjFmzF?-5ay -TdYTЌ6=ףl?z+xPԟ`?!k('^A|A _o9G5Z#6=ףl?z>BP,/r+˂=6/'43!U-QEQEQEQEQESTekVꞬqgIc?Gčjm)4q=6؛X*95"V{O,0"VNŨ_i"`p}j[(uNI_γh;0(9m+ 5k)ѯn\s"6(2i{5'WOjliPRxN kiqH۲sԏi-.M.]Vm(G[0U6Ϡ:cj}Qiqm۹(iB8Zڄ+oaciwvm~$i 2Q9Ut mMwPrR6t9W]9=qNBĀJpAV`AROj뮶9Ԟ! KDU$^_яItz/TғCV{.Yfp|>1X[F?@"K%uBpOi,-u6#{kag٭ rH9PvǥKᨬoPIm5D=5|(g4K^Ȑ"Je>+k&LwVG8NV)U&<͋= ޴eya5ݍmŵߘʙvP9)$mvyw5żvŌhmQsV W\Cs4p$ovtu9sz RkX궚Mw8IAƛrq$& xYg6mˆA"G è5<Siqz| &7}ҹU?UYibSP-My@mێ:yؽНڕƬ0(((((!K$LpTԔPg/6mvHeT}R+NAH?Q-Kl:(3Z Gص/ A? nFSfaDH7+IT*PaKEfq?6%!$q'ص/ A? ϱj_ebԿ)Ӣ3>ũR}QkNej̒Y$m$e1 b(fotoxx-18.01.1/images/stack-noise.jpg0000644000175000017500000003213313222767271016053 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 139 258 0 C     C   }" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}Xx aOlb@U1'}g6OM4:xdaٗri.J>氾h|m߆Vߝ%?N-6̀$Yanbs x6{?ڮI^)nK/"HPʖlwo Nl>|Qdž/ᯊr×ZifylNe=G=%Vp1eʒ>zO~;xGKR.SeOyr2,Fҿ"D1#Ғ>Tfm>Փ}uy >}^/t_ & }^Ꮔ'uo _joG.ۛ8̒yO6z+ |7֬<Wþy޶uo4 9*7BB@ݏ:TDG։u4&W8׫P^u [Rlv7bIU!~]]DBh $prIZW:'xsP35R7oqrEy勲yP^|W/|Jl[ A4_*1Չ;N;1t<; v8yQ=n<3ĺ6-kKa9c>TFP)u=Z+0#5?^ ڔjGf|/ ,%y[_qNj> 緞)]2FUdt WG;,ǟx'GIq/,YRXM]U??P+t#$yjO~9+w7oxw7oxNYMT =2RhO^wIL~?:Q_oW=F+DxsIn}QTu=^,Dq1C8h?wKgˑ݁մKDU0OLd}VnfkqoWB?`oٷH~F+6}i \,!Dd F{~"W~,^"񍾋8mcoj̖?l4kyca"fy6a`7/xpF4%潯GOqr.cVkkYPĉH*v1qR?kރVުe~ yK`r"G T>B0>%*jz߈m2Mb뭨W6IDHΰ0 )TmvbGWw-vֶگ"-tYO ~%HhYfX"[pcl ڶHok16;R{]5/g {9dX#,+ [OG&fy Lnkz=ZNkM:ۊx!skž5|?ohކVoյ.-=g[֔}`]2f`\.Ko:_uMzXkmi5Q\"bp 1BOjNv[hqf27Iݒch0\{F> ɫ%<ڭux΍yڦ ʑ[*] c}w}En鶚yj aчROTdM9%uKvX0HUUt-φcҬeF Ңu*X{;;{9%6H {'ò[]@q%$n6pڢמu-,Ǔ:mqjNvĞJ@W8j 4=Ku>0{[C]YmbrC5ՇՅ֥?gԒ(fPQQcrK:)[Oӭ4T>6~p*PO5iKxs$3แK oα#ԁu:ŘlIDqm bk}.6D= 'PRWD[`'Qj*Kd{)~5 'ۙZZFЁ$d?lQH }".E&89|wu:((( _)6:6m[Ǩ[$y.\]}qOjZkYZہ`cD{/L:z`uo x3ëM&H_?~zmeiif{#˟ 3s ]1B o~o5m -'EfpVyޗ`9hB=N@ Mu?>Ȧ^(.'z09Iw";ZvoN+Ky*J:WoID FNzIk- qh,PqOsG)^_|?~!4-o\xi|=ZOwx\ŋKtOM[v64 Mgmsy6vpy!HWdUX'9:5/-5=up)!rF9n-|@!ӼYuxoo;8Vl aFV9 z~Z]SbÚc;˛M:+4.R9YKhC 2c# ^w~)EY@Ҭm즏Q`MQ';r4=RW 2+VYb1%c 0 cEmW72ONGO쨡Z _|;C 烮M .utV[{Te.ȀC668nmhA}CZ=ދ=I&,D,XmN@ RR~x _FidWA]O'e购 6xGmy?$~ (l-;"$:j0u,0UP\ 7v^!ҦZ.uMOZkǘ^ ~R2xᗌxSA|٧q<Rsee V5袊Šwߪմ7* Mpj#O?€4hG4:i? t Ѣe9.(FsI]Q@/O +;'v_G#O?€4hG4:i? t Ѣe9.(FsI]Q@/O +Mng,Y@mk|;`? ?uW%OkRLn'.'X# b6hoPMK9ϗpaFU ?uW%Oh'w4\?ÿu_/<}kiia-Ü[7vz%ŽlI뵶m?ԢԥkKM2stcˆUׂqJ}ki]%ݍK<$lV V5e=SA.K? [[x?P;H+>#!F쨮W¹nĖt^Ef&{8豰3oҴk޲961Ʊt xY6@Tg_(;MS 3Ǿ0xb PxsTe&F]?薃WVZk kr2pQ>ʠ}o ~741zvUҮ-#ڻD vqq\M?MaWuN!/ W?|ge|#'OLD~cf$/GCB7mGƾ-tOhl#vVߩkخ`e.f$p|͑s1񇋴#L~vۖd=hoЛ}q4݌5£T5GY鸕n:fP5j8lu->ŬhW-|kH22M@%MvQ񦚚_M?"KY }YivFyY-H +h@nךOq'CVy}($cy((ۢ((((((((ewjQ M-, A$~VʛM? (WR"]7^U=@.K5J.ߺ(͏)A?Xu56̱Bw E#lKml>|rC]Uvm7S7SNXM 1 2 3 0 8 476 552 1 2 2 C    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO(" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?dOFlugP޿3qcyx%׏Jp4[_޷ԩqoc]Fn3sz?ON?b4 toKO5\ϱGdj&373vi J0s}mHcCۖo 3[?R-EO8uؚ?N8ufq稼7wD{;WuQQvg ^>jS5ڻr}IaE$˨t?簾yڻQ/EY{v}jafp_i}]hԆː}oT±5hõwrƙˮmuoX$(V_Le._y>_xwƔ]\.>=?vJ*.`m"Ȳn}zQ*v'\kˏ]7ë|P-(c$uv$SSֿ(gRdGaDj) 3+ξ$jzpц;ע'{{yżRcz޵Fo\uK#sQ3qzUvO=l-O_w?+ \u WcSGf?.zsk_ߥ ak‹$SQjyUZAk _K;Ǎ_c;k@u>1]hm,[Y[1KN?+8XơO4 kV9Ƴ~~O5}Nu_6+\,qXձƳ{?ҍcW5 v?c+|آcƪO5oR(L_k[߱GlsVQqX?uj9rj<꺉~ebXϜQt;յ SQWRKaVQ (?EЬSSP? z? \7;O?EY-HY{'F(ZO7N8*O7آ 3:W6O*cSkGU՝3M*CiEЬWcS {jIWRt;?4|t;1@ ]1?}7MnP  ׵UaYԛs'@^>Uqg4\,`u1}AGWn?D%z.rut;",:E 0? q#'ZjG]1h(3OI襤OITɕ'T8b`ԁ&8YRX^sr Uc 6VA$g(q9ތT[Xϸh^9Q]A$y,0-b+=m.1ـKTi6L1}%Ud7 y4A2J _j@!WP iD1$@F+1,'^ ar)aPpNO>€4*+;s3s9-&P2!ҌVzZN-/"'9SO8Yɶm3*C@@/ ;R:2(*0H{6ޟ+g B.?Q jCcw4HiPtHiƘhLjD¥jaVƼp;SVn+[  w1)4丧kr#КG!t 52M2F>9':K+ni\xRK# N$d o7HjǗ =tR:=82G'կ1O4dA#bǿZd:K9Jmf 3ʤ!'$sץYz 5Zb~a'=G^홙:2=Eg̻D2FW?{h~TVo$jH\'No6~S= pj[\Acx,VL߽9P,DjQ'9)kp[q>I-o0 Q!"G݅_z`^xry$~4M"$Қm5Ƙh5!0hSy@ kP{L1*ħoʊE"OE6e `4P_``Xv=S8ơ=@yGV't$K۷ 4(0ގcE-|r[Lʝ m{9 E:` t6;3Zp'@Y8']_8c<`}(X!Bpݞ0~hEp'ץ9zYi#-;q88==1E4:*u#A3+q׎0h<8; "TsbGQ?J>n8# As@xIFG?#֐['OL9$Zǭ4:3 zf8iinOn3 9ތQ @0K9-{F=k5l@ HaRWf8濥ipsIBK+wR !?. eJU}_AE!5yUL_1W\c?4Y),8pTw# f"{[*Yff9 BUc~緭XaL5QP$RNNp?*>*C˷!1PZ?C[9סo[OWS\?.& !W yҦ{ p$r}?.ԀfK10B@ᔜ9[48v1ӶqQm~h|9OPL I4rm<~`x5&;Jw*=*0Xb@ў?_R mu%Q3iP@$Ę2ܞ2\R ԻCnmcʓǧ),;zؖ?>4/p:\%>0S VJ1s>t: )Uр!TL9zbmer9.* .W=EQ[[M,mQ/I{BK|dcP)RC f 5T srA'=y(CReU[m$1 ][}8(C ߐsyЃ"dؤy^SH3 4ͼ޿8'/UCgqY[9^;sʳt`ɸIW ڣ8QUbEE{v y)ap%W{@r{ϓ4himr0ab  I!;,+7?@STLw<25jv@2,oʊOI=mi?يʍlX6q{?[Ģ&r2}F? !{h|vhs(򬂃`}~H}&.8 M3fBJy$'y4XeHr ?Ÿ[$ol04'NG5Y/Nz3;m@>ݖ#3={SS+ZH' h;WZBw2ǎ@ ޟüt)!@|:t?@ Z Ȕ1׮0,v>?6{@jik"sq8*$g*?DV*b'uT8 9#sŴsր$ fePF ֤DRX0IVߵWsuRH4kjڨI?8ޠ9 3Zu0BK"s@Bؤ{C6=O10"PhQxX^d7=k_Gw\?.&oa?M@YX"@d_Miz CHRAשݫA$Cs$Jw:eKͱf [|g#q ~_ZO2\x/fI#iM"E<F%Obkv3I޹ ֭}'~tĝ2P"=ߺ-9c$oؑޚ+o2G3Nid˴2!K)M-dY+:0@\ !|lqs׸@mzP[(04 V-{v Yyh˕UZ7$z(?ׯ9Z_>܀,n_ZU [P*dȐ[u|ybYTGdza}>cyfS$$x&<Tg 4#2FcvPǖ!s(I`7;HT9''A4K$iQf;ʑ#vPya2D'-*w֓L!\*[ 1U^yp3ՒG\U6$;;? u4A|p@HOƫ?(La(N?u.O>.W 1?{~t=ջrcT} :Ymdܪrp4\g'x? u Zd\xϥ0+İcvޤΚwO1,UXSώ:n.dKq"հ''v/2VU6Q8)'K<$rYO_)fy`Q'qʫpd$1'sAS aGn H$/*'Ӵօ6/+~OQM=圊e0#.p 3`?*}GwV-.g4ිC"(b@%@$ğVl?֘b2#+'ե|ɠokoX%$ֺ zP#[ğOWS\?.&fxD}K]N}AکyW 2T}h%0m͐t4vv=(^pxynܳ!88?;̼>Z>R*1jT5e Y%7/g}=h5P={lH\Hr8ZI"te3N P_iJ$zHh]joz7q ?I[;{נdq?NisXӗPFۭPF$qG)?c(VKxA=*٘Gq${>-DdO#ٳݧ%[UQyTlTsnB?3RJXyV91 %k %rXu,2ۭ2#$`ʫr̥Bd:ƀd;[uvءy|-VX!b d`6Z0QvLP"ǼͿ?{M\Q@I?'lƜI.(d< %b %ٍA14w.r9ii$|:⁹K|?js{ʕay,x$L9׎LIݡ3֪/I#pLv.~|7!3 ' x' ?d3\X|:隒4ǡP<$+8[0늍s2xq)n8œ'q8U{7 I%IuX! `AL5Y rWLx`FIQGTH>Pz;L{Hi3NxIXWt`co&8n?*lfنÂ7PGI@֩ !SL6L=29?Hp>K zS  }eIs郞c 8+GpU@qמ?N)許{Jı?h ?Hxx}018ڀePzb?iR?oP9Gي̇ 5@A>sn0V,O_~etHsc=*1[vFp:0·oZGPϯҀ\D+0R6 6Lk3ۅau3hP@ ێFϽ:c{ r@ [` \$w9~pXnl`g>\ O5TIp0w9"A7ʹͿ0oʊAtTwQuܣ8jTTe‡0PHh\2P P)C.\985> *KTUg(`N1R!op(=ORKad!zGQ,X~#<͸8?Ni*HtĂp o*J#+p#F=.i) MBַc49=ijXOÏsJ&.A;:ʀ&&f%$ sB)Kw?ˏʥ(_cwd8@Șu>, X#% $Q[9><}^jR]Pzys'oswrF͜~\T1yXMsԏJϜew99 r=qc^˹Ks~T!77\8򩢁c39>&kwe.Bb{z~5yҤLc?E篷@ *R68n@&L}jQc*}qS#8CM*|z3Imu2$w ʅSyFFGO=4cbJ[?=yqa~T[t 3OqtT[h$G}LFqs%7w8"I0ehА%Nך >P!xSyN_,aVECB|đ۶i=H0'nv@ sHpv# q>oʡK$9T r8i ڍ/m,2sߜ\L(['#8W͵$']/?*l4;HȮ˜އGy\?0&o]?M@*H]]Uss<՞vPa?ʐ Ϲbܼ#ZTF(ӒO,W6jE9)Kf;'I$x±$'!@>fHIf'ЩLb&zAF[G+H"`:@6#3ɰCϩ=?zĩ#o88?1͚9F=s&HX G#? `: HȬ#Q'zO*RxC0NrG&+Tr>#ӁIf6O}IQ]N}&d$2aPdPeg}=OcRSFKAR=ؾ˹)$84d1!?SLkXGr <֟2ZB_FjZ9KddP@U>G}W02l9{-ٵyc K fBY[rs2UE_z+__XeV ry$qQI&O˱774;V!㒠njCd3چWU^Fއ>€ozC9 2n$(wm zS̡ ݎȮ 2'8GP}@O /{cٿ:dݘ*8V g$c:;[]PqpE0\,hf-%Z߳$GSVmёn>7soEǴͿLQ!h9pr)?tlX#*}4>=`KTȳ6T g!k0Hwïs}pH' I03K7@-}p(ZV-9q,snmr cښf<>nO#w42(HAR  :S~ԡ_qϭ^ɦ;ڀ+ ]=?Sxp !"5v# sL q_D\(@8^1ݏ뚞yaa=`IGjvxH &nPC,4- 0H ^!K(2:PKFc<`/Q͚>CaAOCf eer=C~˙lDpm6EROєPVEPD'rIݜg򨅝o2?(^jI H ?J!ubJ ܟ,Z%)0 {Si0s#r{? I°J2˭ &{VO$RD}@˜098G7i(/N?S~w,@2Hq4@#Uaw7v:`銒;оJ[@08⩆YrgS۠u<*Ɠ: )%$G;㒿Յ2G)'VR2C^Cn듀sR}_;@ i|EG=sظ<~$ӞEF H!GQ_ۛaEo\;>sG- NIF :D2.p7d UfMVj).{f,N7';q~Ftݹf 23m~S#>^ 枬!e*ve_cMk?)$ݗr9#?(b4`͎2\rIlcR/sK!@3IHT6⁑;vUq3v֝%rc4ئP#mj\mH9St$gy?X3$5xSC'Br$К|I!t 53CyʐH50TR_.7O#@1ǔ\R'_lК`NNqݏ*\ӌ6>?c 8e;gDH2\1WPSg `}}3Vu=?~13r+#@ #;#V ]i7_$e Čq#'v%v[ Zḍ$ ҬX*PqwL1wm_2n>ƷrB0q߷j`Hu&#$r%Bddž)a* +-ۢF9Y.Y󡤺򘈾pFFs2^?J{^A#N=qL.&39x?{$,tݗہ1[Э o 23T«,$̢ Փr85Ud@; -樷W88=pH~t=۪!.cZp9+M29)h1zulU$]IGsļUG*zO:Y h@#IСПF0I~߱u 0?PmIYHbN7RC`[>HP2g!: v\iz*,wRjGDm$PH< z݇G2bJszӧ#XLe&F 7ۿ준޽olr=\=׊dwQh6d?? 6I-U&Ed1=?*wU`zs_6qn#P.ez=9I@d9~{2ej0w"ɷ n-qLEԒHэpt lI<*I f*p8LK9@pF=uH..#T?I IqL ^?Ƣk&08$4Q,0@3@ L*%A<ۯ~6a ~\9?OA&^ݑ=鰛YO$r?*`"\\|9;G# *|wr7m8QcM}qӖyRT%W' 3ͻ ::d9?7ϸX;ag{qlp*kz$Eh ʀ.U^d@H@=Z xT~lh<{LTu8~}*l'8p~kUk+OF({f.݀vzJ;lZ'S#9'BG( }E6b28J`?pJn mGռ 8QɨcWt ;d0v"lH#=)f/.P8)$Rҵ>jqAspzT3@Zmi ,{4=lk=VlL@U*vv94kHfO,To9@X3) 46yaP GxH ]9硩 +x!r"Mdԍڢ4j?0O4Ú@&iW4r|_QD/ $V:jDT'-~,wÓ0'=0=Q7ix*C>dcg_5Z]"Y>Ԁ6 rouXmpr$V?Ek;l·a rW~ysLcap8,ɄiBr0s:M*e-E4[ݪ z|:v|Ć?t.Z%l0X/^zgP!g)`ŵU]O\totmQ0#f?*`Qm-ҳ#GAy4m2:\$P J9b{yXAs/G;)bE+K0x]G?:@QBLmXB%J{?JnPIfF$~l#m)fQrn x9'֣n[f+Pn]m;:u U ;%)񦉮wB=47s#" r(WĿOWU\?0&fisR2U UӀ*7V̠%``(!Uk%v-nz)1VE.}Œ~́&O˸Q f@[.F?ZrKjc!)@3!?)#Hb\ɷvqЩo?Z`."Yg@ biYT9#thmq9/fKfmn7=#;LuO~)@ LwF4oICX0R=)d{e%r ; d8可ʀ#eX~%Y Lv++%kxc?oӕ6o!_b1g>0MKۤRG$m!!'LxRyk#M$Cd`K9Ս oc̼Hq袃-BUPI dI'ذ3(o1yi|c# H6׊m{r,rO%4 i"PX'g&#`w6 $_dUckyO UZjƪ%uA~S{{v}|a"1R3 {`Tkmg<$1a8 ᲖuFEXUH SP#ҬcFDi6P7A$qɫDcyeRx]85Xŧ*w1Sr>m-VR~9RAj)QK9 rj[ ,8 ?E>xA0\1W|΀,MaU'R;Hkdd}`v3ϥ=:Ul>nCd$K*(?goEjtT7hCtTw h }P*+nXu:ٮ7$zSR;DXtSSgL1^dŔavf$c'4aý"-}0~mhʌfe1BD"ȭpH1ٳ@KP2I6k3]p㯷AU89 G lqIl?:1ɱn g>E蒫<@cǯ)-H `@ )bFmƀ-Ln@18'=jX#hI_06qǽFHO F~bsXmVa0l}hh"esNcR[@񣡜 fkps?Ztp"6n0O,G@gkUUr_ҕ~AlO}w:)% I,Tӕ!>7ן4lWr@NkFx6pG<O2pf* o0r L[8Sus܄*{`zT"Ҳu`J줌O9tl{?:+xsb3Azzu5 k)HQWi3sנ$9aӦG_dAjfu2$V$01%Da7EHXgL͔g3du,HlL*??#m-#֠hl^@_H-?׮D&b@L +gځ݂pO̹?#A [1 8P֐ȫsd(gzxws~8HPg6~U!5y>\[pE'O=š@!)pI\d>+HJzʧid(mg?€b6[h>["*w8? ;s]?`?o]?M]Mr%КHC8NK vO^;uuh Jp UowXW.XOgalWyQmhnR98'ۥajG˝tO|~uiEq?/ީ( 3'Ý%{b.$ dfs!i ZRyM^JqmpOKq-~˂r1<{j5񀫷6Rn)sR)7QN1orq#?17-`9R*Ynw 0G!I%ܾrdL6-Xǖ))I^?"*Kp8.a\q'ʩo8O?)?RyH,ppFq_zn`<%UOFyҡ'z 1zǧVcF6OZQ^2psD$q VfsTk4 |<98 NH4rPF3TRO" P4k\ 3ڭ g9 h5Rrd9$gP"6 C\?QQqKvW$ԷVb͎g~ycW{≜[mbA|8c*?,44zv\2<`*H%`WAU8XݫF](IQnGL^K[Ӫ1m2j][),adqR^tpe<J/B[H#7uU ?&4wh36Ӑ3mOQ Oh Fc~8IC&Dx9?Pl ''Кuˉ19Ґp3A__L2+e8cM7-'i\LA|5o-_NK;e"!#{ ^GpsH~Ԃ"G4lnW8#I/q fNՏqkۍ'P8n HH*-q/9hCzO}_ds?dXR78"WPjSFl6ܥa(> b5$`4UԉbLg*oo.Rl#5G0 SׯceSsvo>ʾSs.;~?A '}ſ4I&In 6sj+ ¶I6Y s1Y/fk{$|e\u) u𞔢E~$] Κ~0r?zWur#]J#c Hm29\΋߹wKń!?g2z7o⹚馴C4&0#kW Ȼ'arI._<Uv$?Gq9 ?x{gK[-ΛX{T O&ʉ# [Tָtˍ'P8ϖvFx53[$q opzs]9O+x|<Ӟ5OE, b;|K!t 5uUC \Bjf>퍳`U ,y:gEpwVb{/4K9*FXqx?C WC  89!G_ƘWhpInV#''*q6Zcy w>|~ %+T/\`?SK+*8Gѿ1L6wF3\:0V$o<%BNnNXK)-l#sSc*ͭG-kry$uI:݋+pcj'N}XEug, m0O_ʜmt D+?q۹ḿs4> U0z{eڡo>ЮI V9m!cվF g-O^)w.VbN3gi=p2:TM-\H2P Ǩ%*CCJ󑴎qזEz fOSs҉.oȉe±P;W[RW!q@*8{Pp8 @)aib1})חWIp(N Tgn`Jx9"?*M /=9Mh, #%tjAwyM\CqrvH/ p5jݕMh൹.MM'{K*)$yoEj)mǰ=?է2F\ < @~x,pi#,D[mv;GP7 ,eHhy"/~gtrˠyZH`#[Đl["h"j!MCcJS*,\Bys4ys5v23#}F3ڬV<<, h.ge%휼d60qm͍imw=AGȋȋù yR:Mzb&m.'?gbc=zlyw4ys4XFLU쉒n7eݎ<4v4rfgVSGSElmu$n}RB &X-9lys4ys4Xw2?߰O% o"/"/ }آm*4Eik4>EE`&kV+0P~+ž91O4mi4we$iB3y"/"/XěT=4cȋȋ+港 "$B-C2s5spx53 ۄ;Et>87΋-b/ pJ'zRIf\CnPFly̰QszZx,\8=ol8}̾QQy0/ɻ9B3JE2nX6-8!۲~\׶GQmu"px ^D-̆EYgB?0m)FFF==PYGA%˗ \cXVRtU'& '8`qh*YOjX`-uhR>~꓄sNkt!@zs hhYE'/NkaKgTXx1 @Oj&&<i7c(^bcCEOOc4WBZ(RWwSޔ峚FFa‘$nyQ-='=9yZHV͜*`y9ywtڤg)›͗wV-8U靧5'_h8gix^Fhb1_J n7쁓+t͸Mݜ{vIߏ֗g\H8?jVn )d ZDvYIUCx>] @@`皞dU>cd@JQMdqLURaqOOI\ (ܓq@KIyON*hDmΘyt?ҢCnl ԊM[yD:.?]_&ձ{t*|kx'r8~w'*9~[H]9_%}X/`."26Q֫9&HoLiTPkvλr,e99/N2lb YF9,1ڟ{NZ`(?x[v\sH6ӭ^3ܹyF˥+鶒KDIDc&6P"iiDYYo(#YkY#Z$6%f,u,nB<0~ V- [sc_~ 5K;m2em:8!("r\pʃhlzv)9,DBn!?' VGYFa1tH1(zQ" G#p㩫zd _%6 :A+(b.D\H~c2H kȣX22VR;h.WX`kk99Hv'+cpVu+!,(Fhf`T:l:D5 0ђ9 Xg ~z i*b0`PLZءŻ:# {2{&Q&Ԗ)V6 'J+3s=4aXQ0%m'9?02֧^!_M,G ר%/-4b HPI[o&m>6r;Rjw [\}˺uE.T+{ɫ_"s[',c!po_µ&ZL cszޡM._?s;\2MzDvl|QOQr>nQ].x+3 uڦӮYQXvdVSAe<;OTq88H?Ȋb$`?jKl%)\ghqBֹmd*ie aK+ZFJ@UdTiGmذeG~x?QIyncZ1rA @M){gf!շgOMfҸ{bCWʧX!w F'd?-d"'N8#"ȣ,žrA?NoMޑ?7N7Է'#9`3׹9Ţ۴lۂd10@!ɨ>vbŎ Hj rC2hjs"Hdž`S]lˠ(b1#2Hߧ"J̅-fO([!rs*de(&e;sqW٭,²AxC 0X Esʀ ,8ҽCgmEo322sژS'+$, `($B3\c?SN|R3OۿOڐ-,_?Q%-1a)9b I+[zS]$!rvay9JǹfHY:IV'##;~y?2Hl+qH FD1ǥ9=0jɀddqjkPCI/?ʊY_QH TV#4hcҥOi/-ecG(EG 3rɅ9'iZAdy'3GO{R\r퓜~f ssӯC=ɓ"av,}1hmʀGPx8,vD:?ΈlHg;Hnwrw.*Ze,^Z Ӽ" s5murĨ0dn0=3@j+-u 2121Ts8#4.{y"}^ptr'_ҺɆ+o\@9_OVҬ84eڈ1PKcyҰ#`\mn #Jirbb,12q]{ufu\ m' jW^Qۃm#2C1p+v{[{6{ Bٻq'ޣoUX$8ٻqHg>å[ĚXt _=084zq $Ԩ,vN0GJlm6Qɘ%Dq G*ie,8KR%A)@Ҙ"fԮ-nZYp]v24{w`c u;yKGc3Ts?*bf/@3 mtH*t!̗/cx~UJeyoJP2Ǩ\2aǷlWJDDaoU%R0LB[x=]~b#a#=yA^}h<+v%F?h T `V sg?hϯt^Qdҁr&КN9keuKy@.[\*D$i!CqXrmCoGE=Syυ`e'ܺZ=y;s}xi1t9;Wvp-NV8ʞe TBvϦOM{p%LE}mI[`:`vYQJ^tuA=r(73?6߼qԁڇde9*{{~uvdm v=Ӗi LPE܌Cb ''㧫~3,Ά 1RF%…TBUG'?1(n;}=xaR˖Co@88̹X$=,aHKH{~5G4rw/n &9-da!AG8Znmsu5\C5-h5ݥd8ONOu4f;L3Y:ƅl 䎵NhemǓm:$pt٥eYJbfVBЭӦ)k/DsѳJ*9Eu|!Z=6Z8.T6;3qYV^A !Ш0tbߵw=)%}aqoaڕy`H? 9 s3^"*84 4񁎃V$%vUbx9E?Z;E,%H3o/ DVoM~Ѕ$JU$X@\~4 ͰVڱY#9'9d  źXP9慻iT=ra f{)%dxضv<EF.,dXt! ۀLʥi%,<̐=:*+Vi((P_e]>H>Ӄ0OKf(*jޛuu?RcCSR*5ŧh!2e Qm$ Qj~lIJFn=:[˄M,6"r`H,k>4bx~wYI.Hgفӽ.yy%%3$2Vߜ2q]HН`)yZ kOE-DE6!~}*I?BM"I^xP+a8 <Zfxuzkӯ5]YaU`ʭaW2Z~zȧ ubԒ1:җ?*_ݳΨK I1P,1dtfp@4Xv^Pj,h4Q (SW-?ʀ%ʉ.6>J݌椨f-?j_;ʩq,OەvwMsswy"żB.¬0욓kw\ z5fڳI 1@I":AÍ7O -j r0z=a kGLۀIK -"\gbbhZ9Nx#Ҁ&"s Oa֦ B. < .=}3Yr9ǷUx͎ +NaYڸl.3O97bKşҚXK\sǞϧt#n3a@)$iAݸԱ*)cϽ5mʼn㑂x9},hcSf]:_I[16}8m,r*q0Auɵ~P~)wg*K^?ҁwbbӥ\ɣ&(F _ ?LSvv.!aU̚2h.r*8DB2$i)6 7!_Q.!X;e)<* ufĖ9GJhDbY9)~X4O6AÀ7I){"*98ʮ_U۲FOSĻ!DO N$ebv8榥 msdp F0< 7$~o4f)pgsf$.n"9?*hC fyB2РzKKI@ \|F<L*?f|T`dq{V3 Y?"33 wxC \Bj7# 52d9Gu$럖Ѭ4(9Odn$u>΀o UM*>DrqqdMݙw'8?j7Z+4dvl9?}dkRs"[~5;iqcp~Pi"9!b#H8~4EhcZM͜dqPaXfr:v" *>JImGl:w`E=ͣ9yM6s~eWgy -~<dg>_9#+?*C:J-ZE%{l$rns翥jS!&7K\dJ變[K&/Gפ >OQ<_W4Ve-9x,tSS/)o G\BjȻe4eUOBڂ u@*U$/ouzd+ L@̠"9,΁}- 8/ik-݌AO.a ۴\qP)zX9[plHf<>1m`WZP{gũI2T?+m$u8|;FIqg%_nz\Ǩ"+-J2Nx=)VE7={?:|sK Wk P?h-eem̿1 @i>eh1l8pMiʜ}ssTi-k4$dd);SL,A=xT+u}KRK~tِ \pv1Ow*D/!8i |?0tϮ)7{9^Yaڬps" -nbMH0}+޾`r#;s+\k 6OoTOu;ݕv>SJC7i+ l^orqlHy=QhYLz1Q88q@fi!f* 9kZ+SLԄgF *4P {yGVԀdt5̪-dbiC랕R8/H> 1^MG?/'P*?/'QIv7M@h^MG?/'PZIy7]y7M@? Iy7]Kɿ(o$ ^{o'@$2k;fܝ q~/aThg6T=+ψb#y?dܤ"|܎9œ/ҁr&&Ob?P3.R T|EQ @ۜaGGW'YEʐZÅCeK O^#ǘoo)nR"7U޸)[J-UR=qֆK`}dp<@ +)6m]zn^ʐ "D zmD nIl R I<~q̣ Xg2j#N?Ja]LRRIevT^O>' bZ茱 +o_ʀ* 'vANpO^~ZX[ʢ(][y7au}@QS1ˏAP1}[ǝg5Vf0 8p=?lJ# 㷡*gf s€ i/QI1'6+B U7JW֫6YHDyoEǼ75Z*B 9s(&_\r<4͙s1`h~g0O';?,fے<(QhJ–WK%VPF]teyǭ`A8/2Hrrs4PK M *=i ++TdsX'U"."\P1F9ך}]vh|d0aݞT{qҌdsҹ}> KDrN|p=1KgKVk`7 ꫐px)PbPaV+o7vJ6C[۾v3"@3RpSQD@P/*GA_=<sߣQj>ߣP*E~GE~@h^f?Uf?U]Ɠ^"AA[ڻh)\~= WROH Cqp[g5SF(Һ rLG}\1p_ S/ ɝKy7̤CU~r,J>d}ڨ)@y}}3@9.\'?jgf`_I'< Ι \.3؏JX-cܖ2{}}L[P.ʜc>$6IV`9Ґ,$- k8}l!0?Aޞ̐Zm9'yq͋r\zc )YE+v #K5YFA}TB/Nyj6bhe>a~CUelǞx" P ڡy- 8!<ӂ8Mo !  X4V2y.[$v?4A4mX@?ge 䁻`}#-ݞSZ.QLx0l4 ?zO^?|x XB{?:li bVjmIm1 d_Fsr? dg1cOX R7. cimdr@UrG@ԭ+&C%?D(vx+vF/Υ[{Y#@2: E' Fi%{]79'$T&rr0ǯ9̯v $=wS M$)I?+OV푒0T8$rE_PTt/o DVAPܛ} $9*dVkrFFh@5+sL)Zc]H `㰮C" PwzK%G4kQ'}J)i|]'?b?#S/)o G\BjeK# YYǺA!1@<~KW"L^NjY;m3#veacm`?, Ei:_ӟʁ4+[gc\B?_6(e]o@Cwc W9ڟݳeYA@sVXԏ8F9,TI1(s bl5g(o• "87Bsc˥TF`iMŀ܂!2XpryBeK.H<ʸ t7vs,Rߜc"?:Y"e/l78 9<쿥"YK-WvT?ʀΑ+1nq™lHߑR@d l#>G^_QC=>8Ubvր'8ٔHFTmN#_j3W"MږQP@cHo3*k)#hʓPmg{ }Ÿg3FA bHٲ2@K˪$dJ[@Ut@sҳY F?.syAO9 u1 *Fx?~4[}fS :JE`B߃PFm9$TrW$.Y%vghر!x?{{LH61OQݫ+hLa'%r?,}s@l2ۅ+1D_OX+AW?+^ 90*N!BZ-p@?ZmɳMw(K0 }~t6 ]jĪq?+]XGvJAv᭰Kc@F?c -ael0F֦~$`2߭=.,"0r$ r.~]YŇs@ vؼ'zcLAsIRC@>xvIdg2iK#96M^fLaGnvnc1)aO4OOTSQ?էh '*d?OEAu4a2E MA,ǃ~kFĠc9% 69vv?k1(((()i)èRJG8yQOMUZ%̱s  FE(Un IcH9Snܬ0}%Fn S4`OO!)7vQ(8 U}{} VkyHۣ)5-0*}{} _xVVL%b\ (*5x5>*P\)W{ UL V;\D 0&rmԁ ?H /&a8#Ÿ0ŏҹMbKpryt/wxBКO!M@̋Df7W5Qnl[_ݰs#&]GU g&=9e$sϧn#^[ w-sw_K5ݔ22pc?9\[c,F=3Mn`O~Of'Kj ew#(ڠ(r1u @s9{soԦ'Vܺxg: Ad6,:?Q (^mߡRIHXq'4.$6YP2>oV~~SQ3[Lfުy{rOdki.jR<X.z7+, =З ^ܐUp嵙 *q錟¤ެ-V!03cI~-h&Vgۏ e#TI:o>nH9}K )3>,RޗO64҆TmV?cs@⮃ZjAP888΀=g9;Nӎ׊{\ە`#[i$d~bΟgRr^RIs|7k`u㯷9ߵSx/@Ӑ^Q>țF@z~FK=NpN_/Mi"2B7]rnt&l@g,s3ti-|E{Q,1H9`7`5QU\20vU15 ej9{ >OʊI?*(Q?կTq#İ(!0=8Oi/#vC` 4 $ԋgۦWwNQ27 {h) pONMԗ *@ ߩ3R1֨Kk19~]!Vw2;u\#~?B:=$ |-4`m5RR@ E-R@ E-ˏJF]z0"2|BHG#wpB(< ϼYnl#h\!G:+ye5 ֲ1f6\̊_>W?@@$PCt4"8pvmc#-WR 4rJ*xR2ϕ9Q4Z$13*TuօV-4'5,\VinAY5Kɭ"tF_rA<֤ɂC{Dík] 6F-Iy7֤u Ŋϊ{!G'5LsTaڈ;0lv[Z,<"@6`pēלzWKDqH %9xۯ29>k͒a$j@>xHW$su ޠ S, W˃9 ]?b?dJr"]7?b?#S/' G\Bjd$%`3FLU*?*}OV2l<5!u8s@ $,-("Ti6;yg:P*IT͒,cq@u"[,1]sCqH <䃏?ԊUg9B1 %[N!c'_[0D(O}r[0ӻ#3Xf պWOiu#8?}uv~4N ?\";ȸQǑϷ=BA$y:E#.*1򁏺}}N0ܳY``7`D-nb/ʠ13hG|k:8Sܽe qcMakx:+{rKX"@GDɹbNAnxy'#\Q r#u3[ ׸#P1&7wPnCߌ=媺B!I#HDJoEǼ7iJ LGr1GS'tU[|ŒӬj8aҟƁmn81~oR 7췊<c??*Dv˷O&Gb6e@[p"x] EV1H-<e?+Z `v[AAz Igh]iPXid-Vo|[h"\1$S\ƍtC4aW#-?mœ%D%+kaX6Mh:lmBH^`:F-$-#֚B:} t7v-8 ~1 x n$g mXb9$.Oʅ.7?& ɶۜg}7i\XePp0H#*KXF#>O覝G!ȂHcUiEcwXK&\¼ߒJdMȾ@fAvsoHǸzTgǯNu,L<>˩_ɱ<Ҡ[!Dh[>Iqy  /x ^ySF(J޻)@9"o r@?b?WY\5M@̙/ܑp鑟5F }A!E((*-np0'CaTF|$dH ewgM֓M$`H'.=in /8`zO)vڱ!~T[,3ݒQI k V@9K"d?|98ⵚC-A8ʑր 7$_J$Gp.J1c?M[}=@Dn!aTgb$SQ@=k [E(IQtYn.ZVKc֒nY[yr\Eh3ֳ Oں#=qD*#N!eA?4"tqp~g?$;?骛Rn#%,sv0 !&% ?浱?Ȥ); d MK=y< gךYz_^En.́[p-m[; WnOF-lt`x'ՍD## 3ǵ9I.W!8"s=aNwNhl݋h8ps߭A,v[Մ?j`\cbք -q20eZ6|I@I/{K*)Ǵ*)y*p4K,Χ^-`>QP(حlkŽFHۏ3M/6%KL#9#qTA/#ӑl"t\sp@ Ch%)" Ҧ+xdw@$dZESF<Åg3N+b9o&Y0Dm#BIĦeUJi?0vPϭs2.ÒCHi7$8ƶinA= #)FNL$za+9$?,6qDgҫ}8J4͝dMHL7VSvm0onN=??K10[tp#PC*;P?S#|,wXZe\);#8Q.tu~bq t#ػ 죚ͦ+" _i6΅v$f.\ E(6Q,vnk@m&M,maY PINugMƔ[OCԏ FN[ u,obHМ2GZ]$csdȚlri|ȸs>lo4E'$mi^C&~>F!FtǧE9FI'I+4u+ ~u T[,X*?Q}l$$m84>[eri 4klT Z7hU=/(22N1jhO`8ݐF=q\GzEP;E@,p7Oӡx45`00{Ut _s"0ٚln0}1'+Y_M]}q-8"Bjc"o(ܧ'فMHN<5bDiaѳ`*Qi~\i\H u&e_q)w>fi3rOW4V Ƴ;n| :ޝ4`E$ W>{z 9KH%MŘXt9eܝƆӒR#qб?Gm,HHw9?hvFd2WF->IsAM5sl-pGi,JoAݦ$I913,r֣i΀ ?/֞6^i `*A DKm''"IVM#o%iX(RQ*6JI7p}2Gta_f_Ҟ_OHeVVB G=mbp 3& i6 +'''4&u+М ~E6!Y\P%唏eR>U((`@ۂgcxm&}8ddHA 3qYY'h'Pl!epI9jD Xx#Jn!2[o,0qF/'B>PgI,钫;#69GO)[?xYpJ`~/ Y /Z_Xt6GCHT+$5`^ d,NA=omIXH8 gtSE̓&յ97v:ƌjAv6nU DW$ h\^ؔi|ͬFB qLdL7V.m#䵅'a-I[8hJq{Sث9$R=r+7p4{N-QWqjKm "F8*;<v8$M\QK/z*(u?"*Yy?E:`T~Qx␃"+ʥҭت )mž?"j[ +L$aPOp1Gs\@ua,ɾ ,K-1J~BFGxGRNv~?w5O".rp9$?ʴPzC #cj6>nEdFGP◀03mhpRp9#K".eCzu\ZzSTPTtcu%۰H'iEmlf.DA@Mɠ wtAnF1Tþ77RPoʯM&(%GBQ  \T!/̒eʖ#L&3 \9n3Nk6hZ7_vq*ᦚ-"x'i+#_cDž`Gy\o5К*dq\h蒦5v6 )xʝ}"[)B#m_C@45A6eqq$d;G(Kb1$\qJƠnQ&]#^=J94 Fh{њJ( :PYsix(4ҩ>ܹ#44ASN;Jiq)/GN4Ȉ5 *7 ~y=( ! *E=h,ͿI6TRK$J R;q RPu0'JJ'JJ'w4fK%TaCP-iM!4R`!SHhiM4a4 F(班 υ9o+Ŋ\ʨ?OV$BE͌ss֧O \k0WhM5 <.?/?@fkM{Ɠ;}%c64/%k{wiŖqmaS.ֳek)OP٦oſZ|R\OP@ =Os>L?Z*&7Agj)v/E"ȘoQQSk>bW]3K$bU_?JLi8Jm?oROeI AGGR¬RUrs-+a?5F/vv+Yޥf?zO"P8{2*!\߼]*~/EԿSųyKhDY|KcrxRsIc^?zO"Uz_?#0Q7 p(vCϩ>lWO#}X׭«RR¬RRUY#.n/$\HP  / z]??мh:C\JF>ER:Z_ӿU+Ш(њJ(sFi(4f\њJ(sFi(4f\њJ(sFi(4f\њJ(sGPK=)P&=;\~Rԣ?QLZʃagwC; (fotoxx-18.01.1/images/viewM-check.png0000644000175000017500000003156213222767271016006 0ustar micomicoPNG  IHDRxx9d6 IDATx}|^NY\$wW0ĸЛ %JB !'4ct`lزqöl 7ut=yoonOa?͛|ټ^oti?sOJC@X$@#Q)?jzqTj+*F[vnj:ZL:9ZZ6fа.K^+ EeB~(Ü(p8/hu?Xr;CMNGC V5Cg ߷F䎟Y4xS3NfǛl8"P:9A^/'1Pp9Q$\No5Z@lE74o^5Ym7^qP뚻w3kӔϳsNƋ@_a@bq&M'F 5ζ*۾Z>z U[2ǼrH|3n/tkK0W@F帠` fAsy pZx7_kCᄇ=aDʁrO?%焳o-3*Q.[?oNV5>m >](;vS~ļcA;}pz8NKe&X\A9M R FuGsgXcΝnO8Ϡ1&f9 pi9~nO߶]]wxJS'2s%sYr RCy rmAP]ebŶJ'k-c'k=(ٗR2aSWϵ^ف|]bsb .;cԂE!|oI&7opUߦ6K5rh<6u3TȝSȗB+u8Oa,7~,kPl ǣt}1߮S{[n2м t؈SZFO鵶$O\-@/s! {XaR\3ل9܄e67M^8=tZdqAY\;n̗Pp!A|ABz`K6fB,GBǧs=Xv`4Ts\_$͓]\uތcnDۡ>'&AwiîLY,-]P%:Hc7Nէ=m7.oAo[FB,*А?_g$AnRK +5 !ӎqn(8cea8]SW9u'+bӉ(߄ZWo\Qq&4%"\JjZ{k]OU- |1/Q Dծ\~˕HsQƌ)OiOݦ W/$עXKp@i Ek1f&kCs:W6yxMNɴ9ۯ^p_Yqlzj W6֛^2R9;3^_XDpU]==[W (lr ߭k?+`:S Dw׮{E%Ì%fJo,4o*ɸ\\k1: n"n/qZ5&1ηh֪Qi>'a] &tʼvj5W+vԁJK%ey\~ϼG }ܱ[ԴBh:ϴs4_ҌO>ѯ}tpW!KO"tGBXo64v]_LJ|6WZ4-؝Ked)9a|=j7뼖A67O6d)rF4 k)d惎 CMtX~<"fl1Y=Ӳu'g -CNN#SѺԅ 7;& ]. d!?4},OSwvN  H G}xʱEe1\ZR$d3fUmdFSfd\TXMU{uӌ@@cQG |O`,v_1Zf"_!_Λ[`{Vny˕ks*NQENV2k&P깠&:^u(X(4Hk䦋6@͐qcd|\\|+,Mp6f\&}8(yjdz7y]$tgE{MZx+ ؞|\&ɺ‰ Ϙ`~J^Bu0Y̾1VNTQ:Zp tXn*]O,gInZSU[_3RcN92o˜`!0pq &:yD&% !& L#er^ )Gܦ :YҞD8i_'0/G2J DR aGC{e s k 5Bgb+[@E6b@b1,̞RtҶ«[UwZ;.ҌyfWP4m|9S\ ?\l\½\in+ ;*r5A39N/ -%Ste,a[c4+͛nn 4Ȁ6lp^nfX9,oKs "-\J X35AXekEM.z3zrmkEKEkB4j<}䇡<&82M@z3@ޗb\zgW)R:5MӪZ'y$l`TIlOO44!L}[f:0͕yR8S766}G׼V{~d4~:Ks|fD%O@юH&XM0~ MIKh h7nHe0e|*b79h6$j.Ă)䚦fP e@!/_ 8Y9 qrC5V_xYQlh:^%E^0a;U$`H%x=(a}A/pK#2Air>MJDEzɿߑ6o+FBࢋOF4i՟1 KU'>qK0\\J9 NA B`b^r}}Q vB< KkThGD<=!ETt4{w!o. ToY"#N= ZB!\V:O.\g2EyK-$h0%XWLzi#3jyiq YX8x·>*SRAMIK_T#οlhH:sFB5p}b) \4;C%G Zyq"NQR`j6wA=)rQ:.<LJCM5Lby|C+_RK;yg*\ ٦w^uK-!?$p)$Joc)-1LH| )f 4?@p=|YG-u8b Wb ԯxK`Af~,\ VԿ2+&`6”FLȊ X\>kHu.kWnd~@JtvK.Jo)M5sMֹke&c+Αc}T[Z!@pOD:`fju ?Y_ȥVwN~3N@P_9_pYUL~a)paaG \|&7|-o*Mur]Դb0!\(tn]YG1wB?#jЅ:@[BGEJ.̄ex4]sSH٧EIrp1/ƛ $,-J3\I3y1*r-ۍs&L-@ \8bee3J)SVLX~Q_};mRn C[9pvk_ Z|3oj[i:#9IyUT,+V:YqӅA9-0YG P"y֊Ll*/8z7 \C#Lrc<85[yJY7];7ͼvb4ތ'LT5%6OHj-c;D"cZY?L٘WJI$q 8Z+tU6Ƿpp*p F/\9a]YrX#8G%։?5>֟u)~]VƉ . p 6cbj=͖fP JX\mww޵rat*PYhNG_x*W.%C7Z>9 .$EJKSeu|Q\Z6Kυx׫醎8M,*d08Y>UpDBjQqs ~d L䟕]&lj[^_6\>8F o?;:?$LX&=Z,?Ss# *(H4G I (jv\?w̵>pZFc b@a~r{O&rwoxʟtZ'Oo& a> AUEn/{2I 8fD2ż#3|?X>&!^hj$ӽ#՘C.Ӧ .hs׌jxp1^qbQ4 w(C*SSZoJJ2YkX7Ge/- JzyR@Y[:YNic<S4p`֤Qb 4C%0\͏nᛟ*؂\0 q'/pevJ*\䟱n4g!X9e$,GmLME;8OM>mhtE%~}>-B?˥]^/[,-gb:cNxry 1lEZ4e:&Z3~*WHH 9ފ!1t.3Bzon49"!X'a\Z{.+H ĻroJ}# :I-vaR{\e}H`<"-<)}lnf%iJ^">L9%osvpD@Y>rz]e*mmy쇙 1Qn{/ar #OqqŷnԬ&湖vbO8t`E*hF9x*IcȘAL+hQrֿ2Vaw àt1aYK79J3mRtF+)`LiI/syf=$JGbILuoo#Ϳ]TP{?˿")@ÇY-e6 pIm; pyþ oE2/5Y=+T;iWU,|X^MqhJp04,u%$C2?(TQA4̿RhۇtZe5dQmشLu/#48[`aǐ% D#lD ъ"F*w Y6wzzvqmgi.Ң;u!+xw65<4U' ջn'WhA) xѼJpH dؿOm3b@ߵ%hμ^teGCX1.inu}|eAlUJe?]9a֢Ye%1^Ra r=d6 r"!Ј[W2F\jDmra֘VHkUvWj| U#UdQ2۾_ ~BnUfo9[ eq8L(3wmDKE0Ĝ cߝ/M“fQ cX殯w4|yr͕qe%2)X+gNynLPZɥ$O,b\r"~F) G\>iJ+/??|,Տ HVюTޛ)L5u:]gv^?TJmB_7U3κnay zlr -icy2m>vF‰\fIA$ W-;S?5Sc]1[WC]|Wxۄ`J2E- i8hm$;pVv4gjp]=>WF}pm ssy#ϕi%0'OgYTՂzUUboԉM󒃊 34#oY|&-ICߗBrEjT`5֛:mڜɢ*ߴtT>x@*#0̓\y?G G?S;_Uq$tz "֠OŒy74ܰbC-jsκP׻ Se ]8`5,K1Yy ;IE56I/(WA𝁋{,ǒELua$~8+5F:_[VA)AVbҚGKHpZC ϺDmV1dE0emS3u^Slւ"﫫:T(JERflnn$[ϰwZ Fa nyt& L+ƔA{ବp^Dd˥7&,RW), (a:\8:0F .W/Oln%WyY4m,0Tsi?L _v!)͏W2Pj( YT:_tHv&qpZ`'sC!XQ@Dhhs<%?iM>Y{E+!hhQsSɢRZW1nv.oMejOJ]7'Hη5,n3&\lj.5y_hx r2f_0:djsŷ\o,S 4.u \0eUg,]#;|[R߇ GOpk햔Tbr$KoFa!ĽOM^ejAg9q/W8?(l3v ~ܾtɢ%1騹.9Y}+i7_v>ߏ=,( ʢ$8pqoGas햧[qaNcR|Mk)su̖,m[s`|;`)GS2˗kTx''+5,8 |i?l,rbY 7é>^!"X<ܰڸex0J tZMoϺc4gJ1f85Y:k:"ԴRyސg{g2CLGѷd%|p< n1d/xOQifx^ʋ (whYѶEO^8//I>LC.6L:2b7h8l&s@gSE-PB%״H>^6~W:Yba'-<.l]#r p!X܈/ -h$?2vD[kpl۹BGmO񥿚EF{ >^!bF&`fLnݨeH= m4k6#~5|mE$(KlA CR&ߥW#PMF4h'@iTXtXML:com.adobe.xmp 512 512 =cIENDB`fotoxx-18.01.1/images/areas.png0000644000175000017500000000347313222767271014737 0ustar micomicoPNG  IHDR,,Z pHYs  IDATX ՙAn0 E3nM hAomM'0-RlJEt^oI~ ▨'M[bL`ºudk\)<1!{c5/P\w' /9i%6&OX+{|΄e4FA|[ ȍʇ9RxwQR2s`r VyW)=ҽm+(—^HT-A2rJ’2mQڋRԝ^B[).ahZXa*p Y֯ CVDU(Ĉ妓ʷC 0 Dj Q/powPZHaԅ$b QV!$ 8Im{@!)|`#jPLr<ҫߣDPQ8 ?V Nn`fX:lflr W. ݯ6HWal'ݷK0Qdm z.ajZۛ0t ur 0GEi^Z.A@]d _a;l2ZĻ@Xؾ^ڃN"Bn6rT}lz_gj-G1j]Y"̃ۃsB즣8gS7۟S5ߎQ|0]D`obڣ$74bm5Q)]zAya%v9VyN2`4 P;`>m q>*L "StIME;+ FeXIfMM*bj(2ri%HH2015:06:26 07:59:430231Ƞ0100Fotoxx:paint_transp|NEf6/iTXtXML:com.adobe.xmp 44 44 0 z `zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-18.01.1/images/mashup3.jpg0000644000175000017500000011340113222767271015211 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 491 290 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wz7!agX麎[^ ^nmVxFzg*nݕG?ڢ[F$ DI"RxGm9c:>ԯ_ٲ_ʕ\%X* <H׮xZmWL&ҼN=jαg>GCi/x'Cl>|}?¢rSٽVӿ$Y^x)U>:pIH%WɧYKC : Ackb)]꧸Sv+(aO{>|3xs_Ӽ?ubg{*7o9R0Edx{uēb!Zj eCFi1H +]?}⺵&aOl>|QоOMtO\WOZd۬~L n<IjO *J躦Kh7]NX$u2%8 .y#+ɞU >}^ᯇY}?<]a[Z>ilY¬E$7vq㓂8?Z4;?l>|?z>El}'?›&!h2Xd޴|* flnU1;(cWšIiFGSʫC,2>zt;hFX[9>lۿ/<Xȧd+co:vq"H"]s ٨A,&?uf\ɫ:Z D8E:Z[m{=wEni&نbv0V)$p6)lGl*s829?-,F'/%>SF^Ίߊۆe|Q_Uvrp9'ֿB??? z??? z2n~{_?gZ=?gZ=as'֏Zq8?*q8?* i>}G;YVG;YVE\I'ֿB??? z??? z,OlxSׁuu7TCAȬ,_G,_G&Z:_xS{Xiekhav]qH#*=K㿏5}oM.Hzl3ζ6#beTHB|*O}qAU#qAU#ψϋ_%o U]4 UP`Eg.@ kᛏCzEkO|n.P˴aCm'?yøşkøşkӸ\SiuE֚jjWۭ-.`،rWy/׏4]OU,E]13r󢪤[Ȯ.T35;YVG;YVEy/՟2|(zƱxUTK 6RC*<,ɓ;8m^:j y˪ʎn}wÏwÏ.ϊO8ޗyi^!{ +fh⳶^o̮b.0+IЏwÏwÏPGO~øşkøşkO*^KO \6~>_?gZ=?gZ=asr"ZfoUl? u9턒J6p{(owÏwÏ0 uM.SF/*:=OZSk$+;8 m`PI$8'5qAU#qAU#f>n|3Z4"c"NNFybMO> Vf`dzq,_G,_G-XYWˇϽ>oፇYyY]]io.0j} 2.@N6d`ẇ_g7ݗ|-'PnuKY/0Qcvmdð,6v*+L5 q&|Cy-VRD$p<%l|}ŗ;_jw1y|K3aA$( hx@ִK^Wja%.tY7EBʬe;KwiYO4Bj$ #Fi3RX.r@R*-GOe#GE̡ 7A“"ͨ~YM+ Z{Oq>_^ S(*eȪ/d_G8=h7X}m\F%/9w39Panau_[";jΝ -l;m2V)r}z15dʾg<h_to]M/{F[8(Cw=q_!k 5GOoK\nBӼ9{ն)1lĶgA Ov]SV =%$#ԧA{hD %rM$-=tkHBL(F'Ҿ iz JK!G`Jq cZÿŗ|Qxi}Ewsku5/o,6x.aG?5/ R_Ihïj8v--{㶁ZY"ԞrxĶ6X;bID)D㓌WE诫GKxn.$dCn9RW(c@;k<[Qu{@ڭ흎(OYFJʭ2@ePOI&ϬVݯ[d_"tm-?aYԸlgn3W/5AA`mKBk OVqiod -ʗhݚ#媑 P߃:7[^K%k$kwp-UenK;3"{-ϩWH&49 Jy"[yL Ͽp9P9'^^.dHuI^K:A*xȯ>-5+ ]<4 bnvUX RWc^ RY)sB>KcDmҎ/ {Z7~'Ӑj77R[C{o-~YȦSaPY\nt St xc_Vӯ67۹Ou&.?!.A,zm|Ch{]n_ݝkfwfb˼ G2p̓~ |NSF{;.IVh$WOj>&@w\H+rq?fxxLfW[-th~QK[^m 9Vr RRF~N~sq~ c#IL-M:y-7""6QriZs(?O>ʼnԞqhnE691"l"nxFڵ74{җMm$NfUF&&A ]FK2%ٌc#=E\[-+A_[2됛Y\N8i^4Q 3P1>GQ^gyI|:4mE@Xjj\[S$VG=H٣n68tOh#O>ynʈt~d[de &WYѿEs5KQXYsJ \!ڋV#s` u"='j:uuH`,Um o+;c0*@!+q7=2ԭ+1LԐA{\'?x :>qrEѯCOeic%wQOH"*hn)N5 35˔==In$`Grvrg~~\GW'oMд M2c1#@A"X~,/oJĺF1m=H4(C4~b@RpQƓ澱Qbw f7G;LO#o}PfvTW(,,'4sIv:Ie]nUS J`;A ;Nn-/im.8u W24A-`ˈdsx]~3';$TY"J:0`x 5O=hZro2r䝨;FyOo@дb/ K664X}B;y~Hal2'a!4~з'/5H=zk>_/OYxl w@Z37|ujڹn-mt E}\@VHYBDm5w¾/ "{?Ii?\%ճ 7hsfȥ;+|uAx{^=*_jE^% )RŜvzmoRÖׯ6d+g,ц! v9W| |N޷CjCR,e ܰmc_G8o-'\UoSEVh13Ev\;{-: @ k{ksei=IeįlJ !PJ uOz-K<.l `q؀iمv(2+x-UQ` _)]7ᶎ=$V^/Qq N$Ct3w{nO_Kxw]Mܫ9?,҂zqSZ+//b\SwϘwx-(|I&5YOnZc U#=+~|?Ú\-m/q<ݨj?{?Y|-d.ć:%͂V14q\ 9R>NçJ_ǖzml^NPf@S;|TUrekz*z/q+i~}>nK ."+ddʔj˝,q.P5.; F ";%Io5Hŭ@'_cRc5yzmX7>-oCWmGx/HӮ"& "|{r Gi}ZZ:|ViWVrFM02I#kr~dF<.p'xJmK]vs~U+i6g{mJ^Zy=W5; WsǝυyڿYwaU}oo̕#~j5}v"kc-,vۃ=fkֳY%KJR 5ish @->ӕ}! ԣ K{ay$WDeW];  n#T"B.~}ߠryCJ-nLڭ%Н^;# yI2q^Bံ彾j:Nz^鶾@؂f iCd9j;W ?(tٷxNON ;;1ەPxz k|MI6v[ [ JhKjd կ6V;*( zҿ_O_ M}qj}b'B(,øcb8|C/ѼA|! C&n,pB_aGO/h-\|ľ1?sik|+Gh^`u$aY˧@vM(B 3KM;&0jO ('K[yff=5^ůkE_:R/G$k7`D<;w{Х/7R<^ᦟm26kAAc'D~pqз^{_/3隬ZY^A%&Uꊢ OmR:EVTU#\慮lnnFXwpHʜ߄wo]{ ^[VM-E(P=\лό5]4tj Z~y[lnCz?k4j:5.͉h.c*&y ?/?q^|e,,[RAk`]Mqp~Ph4VРnĚsZ#sjLeb`>uh_ ~OkQ^ދ⹦5 ;Ƒow8-mt(`0/QE?4gb#;/5oxG|7ZbHe >)*Iꥁ5?[:ܟq~j}S ( ( ( ( ( ( ( ( ( ( (8-K#xLWA{Ipś[?wZO?k?Ѩ9'5OO'j(u=*KgU. &ŭR4$ᥐJ9jNj|9y%enoRkSqF(%P0@sO?k?'5OOP'U3m"{ AI%GCPvdq@0((+ KMO>!Y MŨ!Ug##yX#UI77Hׇ=3Aomo ^ؼGaq-8r]A![sIqM\&~g5FJ`Ǜwy*y!Fb s܊Uݦ}}oE&OhmT[8 $rw{_kZ.]Xx^ PZkr}cX[HUw_C$}[ޏoX2 T3ZR#6gjI?|i3Q]K|].t[tX[o9sÖ 4^4D8fP~oCs/먢/Bcľ<մ5{xh4_|wh!uPv ?އo O ֶt˙- [4'|xgÿiK6|[ψv@᳁m!V0ElLrL#dԸi<^~;k_i_5/:Dk]n)섌3L#La psyl.֗<7mVm5ĩ!cr.p*nVL?*ycp.@|oiiDž&1F3ΗjOL <-_6Vpi7ta%YX؊;Gs G"` `+~ ?f7Ilͧ]GdmF`NOѴӭ}*qx5mBMkYuMFFinb{(|ݻ3}7e4s[yiPhxr[iw3馵Glc,P3Z510@HaEPEPEPEPEPEPEPEPXPxV!_T:jqD/.w[KϠEkD'KK7Qz;ENG$cbx7@]CSY6c &7 qg|+^lmmeE 2EY6lM޻Fs]EW5 ]&+;t2qq 8 f<sFPG?4/$i7g|3X&Ho#~0$dcW/K(/,b@eawg>MZK[]>dٔe19S57~] CiXW:,co+qA⻘4)5KU>N+r>S] AWGS/[^4ׁs _AZu֫#('|;mkhOĮX3.2B gCW젒8Vuk)t?Y.wwoqc<̗,I! FvvVb mJG~ |19uC^d6xWH_ dIq)Ѥ\c19c)|tM[H`"G>ēzGC{MOE$V/ Q@Q@Q@Q@Q@Q@Q@Q@Q@ wXљUQO@+I>%f5lEId֥ f$A`xfOڭŕWvw13[΁aA\Uǁ7w .o[qCgZƑU.`b_,?%uo|+Moz [AmڋYῺ7"ڀI-\o ]fƲxPZΩDXͦ@ZFq"rE^HnwSѼ}x SIle,2:6>aՠWn8.؋I'URh[B~`~p1_uW\ |Ym⋏ٷAˤ^DvcFAHM~QK-&ۢ?4ѿߙ??4ѿߙ?`s~1GeU={S|qMf4Qi(岅-, :?\`QE ( ( ( ( ( ( ( ( ( ( ͋ĚDRj) L~UzyPHvI;ONù >uxk> m^Ovw./[:7P)a)ښOD}myOS6T5|˝gFu5-kƺ4vZ6=0nФn,`@&<_t sxN7X֍RZ}%e!PJh 'oֺս#)U#!\$p{K ZU+{U.E<0"Kg[OmWA}95_E%tk69Kl2z YVA<'-axMҬ[ ]/RήX1X+)<ߝCB@+]|pa57Xtwɸ ; Ql2llÌ k~ <9I7bAc5B OUD}xJ NhN$$dN?ѯ)O~k}3Li)L]F~"# )@gKRд?gxb.U$RWLF *͒^ݵ};i_[^Aq="UfWIj6W<55MGW_uNҭfB]H+d!J/x%C3]u=J8"$E#2 TKvpEܱq3\ξ Đ%f'2(8ܹ=Et);|=~}"&O Oci:-vЁ0X26XZ۲=_L<3ڭ֝ywl&~nFݾma-W*K*7Sq#$tȯ?j6.Gmqx: Cr.R",Ѩ2$1/ӴA`נm%KwT#$7#u7p ⽷oHiV~𾱪>퍔0˽$G _=O4>o\ԵKx6&oX֓LRa(*pG{"I˽8^%XˠpI!YTV G+So [\5U}{T8#H1D*FI=k?x)U^3s)R-+š4XdH⟊u]Ej1ZoK4i+XY?m]>M[f;OhڌV%b˲0Hdf0r9}找g_H#TƏ:n%5 k Dm UTl1De"n=u?9mjηKk}4o"]:l]ɒvG.efG\o`ׯm:\J-`M#;^F8u%ޛqKRmnBA&q qTjZ`Ӯ\Xo%I:-s9@r? !yjtT-ZcDHLtؾxgIֆeYj{PGɹh:(]Һ:Am4dyfص YsoEՍvXCy@ZJƒF$n+`zit'_~՟Tt}QpTY$r`tnw/7.=awMq%ܖ"iF!flkp#ܚ +x/E:T}6Uk&1[Z7 xvNK->m:- Ew@)e Grwb2<\ 6Wt57YOOIVʙ Y@n}p+CVz|?m 0/RׂIo%ռ;jr\Ewq%K4hcF'ʄ>OL`I9R;hºΨGahz#_lRмm4ȬnRa&HX.{Z?we۽CuNKhБ4Qм?evok466wRU^^ //Ҭm,kKWPGgI0`;@#UWË <>&[$gDu]=Αpߖ3"_ OF+27C^e_|5qc-܋ VkXnaNuYY8;V-nv9'/t}rtnrɕ|pO#ym_O <7*; ȍ,l yPN85/^#5. TslWh;N*>ьU])'2ZmZCo<{ xoz$N NAv]Q"9i$*qUҾ0U|>oZOàIuPM.kLQL˸X2}=SGHiFPZxq2mX#dbi|0_hImy Z̷nk%<&K6Kį?/~x䞼OGkx?)tVQ4> h[6YgJapx=|qsxñZ"eNea$18zk意3^ͨ}.&uUUUpc@UHRq\/Ě觹'D3 P*+Y@EEP3Zxn@}")4ɵ Gl+M$nV2;>Twy6}}Ljh?'jtЯU><װYKuM\Jά8 ^p,x7o|ui~BךiluDEǐۜqg#OWniVΓI]I$ų#|qV~V@H-o"@ထeqK~GW翏QkzMGImRфSY,nG wv DQ[x $MplZN>.lyLIIB n뻎ž55Ak^_X-#ocD xN|D&:s.'VƱFB\b<9A3𝷉^Ygjѐ1,b;C7rk?l?.id_&/v_Sρs ?H? 0uo*:4yW+?^%ij]m=2ݒq+rQE((((((ږkiV]DO$ H җXׄ> j-^,jl;.,nxSm[E>RhO j^iZwSlm;uio,`n!=áuljo'o,.4v;xGsNAUguWG ,|C7t{vhɨ,̏{.O"PHHח;4]w릲4i* (ʒ6wk_ޟW|O>~2ĺlZ|+4-o3ly8玧>'j4gG+TǦfhf j]@h˷$e]Ʌ4} E|ۡUgCΝ 5[k][Klo"oee**k^|jgچm%54.u/=#%EdP kn5-o_e@<m'BaW|hw8wju.o4cnn`Sr1` o2TW>x>*@uZM&[]`7 š(8EhJX|cxwSU[BڄCsh!'y$1ÁSﭽ?ӖMB_@( @'7-tK==Zc#,I$u^7Hꟳkyin698 p3U.Q^}+ao /5_97n~k&xK t? ݬ6oWYL)PT3²_j6j174Hcx4^៊uG×^gI|WdX& 1i o )+;3`PcrEMNNr:9I^<þERQEQEQEQEQEQEW) [xUzi",O72, S-c'yWEym4{׆mm}jQoo$f7|˄ǒ@B#_k|=o%q`lh42>2 ?] oc;`и&luV8:5ɩjri75̻%FvX 5Hg?[cNsHo&h9 %$DžR޸bZGk-}A}-$F\Eo'W7I>a"m|Z(?f}VdWˬ^jw*ĩ:3 LAq]emF 2;]NfNwEtyG x;v76Z~isCMi-^g %ui 1ٯcZZ5bY٦X#K1J ʹ)k#⋝jTҚ}bF1KVd86Fg9F;:& !^RLym]$ [  WQIh~A^]{Hn4Mvկt2< q,|w>lg|L095x>x_T𶍨}ȓc yLd~ tW.jUmuk#0YcaRT2PA5[poKxInBI)#p&QYKWF=) LU4; !Tl*[q)jkvewy&sK!r9@SEFVm=3yo.Ny9EBpM^!'IJzW}?BMwOmmB.)QXm:{vԬU=g g{`}kn ( ( ( ( ( ((Nÿ\Y?/-״ۻkMd][IrB2&pU'^#v^.jsxb+;Y+{Bgm/ x/÷{xf81)ki-LU9[6juזSCߍC>"4 ^->Zknk-Qj%op~&zÿh-su4q;ar >cǞ٭|O @ߍ^o*A xD+;`"QBDe]+>z Tƣ43xĮ,%.bX,C9g1l ?xiu3Bf6Ұ:Aˊi%ѯJ4[~!v♅!4jt2?mdR˝NN01j4k^ It ~:,nh>wu8-ۨ?Oq-FE)e#Tc 3y~i-WU5xg_+tS[G|s[Ϣ_opioxMINyO[^E5K$Fy,HYpJHIP?h;xuO{kYZH# r;¤lysAm-C>x+]sJɺG"6-鍏 WzZRΧ$zdVv+<2/## 2y|*WOi|ii-.\iz>Fgđ .<|q=O~!I!md>W[+ty߆Y\%g?ޒRMZjVHxiCC,|,._$4\4 yH 3V (=f'Zγ2KмY{s!iWu}$'mON?; s_=?LCPmZo+umjKЧ*9~?Cbߕ&oyqgB{+[wUEsSqI]+>T:ٴ`NDrFdO?z4u}PmKYӗL|bgL p'$^v~@ WW6֚4D J/Of+n'!1s]T|u6hv>Ҥ]:U.BC ">e n=G9դ4+xOg_. EPJRA|Y}kvc:Oni ƒ"8$D;i_OCG/um%pYLr#G$lz288xwN>i+"YʱLKܒkѨ{5ERQEQEQEQEQEV>u ( Ayrn#5X(=2^gʘS` 5&^62 Q[z$RK <+O:;]BM:572G$BC HI3+_<¨n/,Fsz6 um+_r ?h WĚzNm kţ dFC)XDkwoOJs'r> z o{a) 7 +bs>ⷈ_K_eQe[CB%׃;WN6m =OZe߇n6 '#1x3\vnx^]ω7P;S yWW[HC*b]ɗh*jZt֗ ZFi 9GB+L] ھ64VV)ܘ ˻ A"DާӨ5xgenfIph)/_MwDݏRYY?U?*I;OW֙GsګIu\̰'BnԠAӾ(ΓG9{kyݝ+p\nmԉ"IXpyöbRM] IkT«ZUq?MxoT^7J|d@7w uzd+ȼ#i&}4m+\@4x-#6,﷟=GGwڅĩYc!YϽtփ':iP֯|Cy\\^][[,Rm0 lI9wNj;Mizj^S.mҢ6%cn%q$*X8? _3+~;ǚxnFz\NK+-7NH)+!8e\7K }]jiU:/`0jZ.ekZn{{~6Ɖh<7~#%Ѵὼhifg`VtK8-'kMDԴi;F@>XBFQ0QHaEPEP\ne ŷƉai%<L8tؒHrI]'׼=.k^Įjvl9reV*x@x?uf8ǓUUa;Yh4e0{v -#$RG|WYkomSG-u]2w{c: e]I 2 l;xPxbokzݮt̿i{op$Q3wh⩟(.#]Y_donMo`^2S?f{Hn_XgQO9++{ XPu'CV$q]Uۻ5 jM5Kk֗+'Z8KpkӨw"> SZ{QlE onZuq+ M$n[q+=o>KQckK:+61#xѪ|,ѿ?ZOV[2$!G4X#h J*6]<ľ,?~$j~MEsXؼޯ4S˄&*6?|i[8^jvw@iMd m\= &V#eVZO4EIbhl!}{ oKÚTirK-QYFh+,Ydpc!=MKl ;mz7:7$[xQfԣ=>[G}d{@lԕ7Еx?_]\x[Zt:fֺRV>r=׍*|_>/WN>%C=7v|PwH'994EK &?uQH((((((((^AʢY~UHȫU/Rj/ |=o⇏l#xJz[=P~D3fKQnJK $_?ovX˩Y{bgyH!r3$zօ|aDEIw7wT6Ea"۬I Q9}eY 2^Ijq0P9f rz@*l%;OY_.UQjTVpjS%bOrO|{VYFluLX H/ XϬI/SUCINpJ}pqڴW\aȵ4" />(,jڅ"Tu5k &'ނu!vG*|C~?((мmkZ}o|+%8(S=_ _xOf^b~wEgG;Ơof裫jα%[Y.廒j_eqڜ_/l'n?Hv*1撏rKjNڶˈmdx[ִW\aȵ x_?+ Yuˬj&~K2ao`>iG_e̯u!vG*|C~?*,YZ]Zjs\jbAF0C&F̲_$: ( ( ( ( ( ( ( (9$oīxv]]-^=߷;W?AY~Ͽ m4iC[5?#H ۽]:Ņê{m#7 *~Ոn"b$cP}ǥ=ד^MC@MumRIJ"!O.PGd* r:T"U? DίǫjZkqƷW aTtiʑ$vB'|lzZtOGn8tp-m  5YZ9Ql..跎yLEڵj@((((((()-!WVὤ?t}\|%joώ,갏 *! NW?wAMC>Ҽ2U.qzbnV SȍvnQ䴒'5$DZ['BleY$nW֮24k(.o'V͸$Q,s$s+nqGU7KWi?/xMkJΑLx)KT" -! ۏ,xש|7ƿkè=X,(I0@.s]gxo^n:GxͬM+*mV€F9n? <7}u.KMEtw3#[E-28r ?1hKIɝUY #P1~?Uj{:׈`5/JK"ePd`qRh|/ ?![Yčc➯cjJw0䷑#S+ |<}',n+oG[RiFfHZHuP:^ g9xuQ ?~7/v[|?l/.[>4g?V ꠷_^ij[QfKpLy+Ŀ5#SmHid&%K)#8 Uxš>}j#ѬnSF5ԤnV`FAk9hE6x_xo/^ ҭoQ_mW(ldks,Idč2<Ư_ u_ҵ~󮲲~&0-7W<[>4$Vm2\wwHSρr ?H?jFQڴ ڭNT]Ŭ$o[ppH8 ڀ ( ( ( ( ( ( ( ( +?jo:OW[HKDdE2b2,t xN? x,Zl"nҚXt0@G$_3eC6*ȡ?/͞E|>%Zx~z𥏈.a5ڼs{Y1Bq |h~gۋIu[nI* 9Z)WU=>_utKXD>s$ҬU5\EC/0?? _hu|1a~"k5m7T&ե%l墊;xJSRނ{_}AAQe]eXge%PXZ]-t\+-3P>=j1ہ{unmXx;8\Zmgw1:j-N5^b9\~"MA9v*1撏s2  /*h?~[Fo_pI$G;{)޶[aWu˿ %5uS2  /**,Yd?z̫9"`NFO9Ҭ/|aaEKjө((((((((wZ[6h(.ef7RHHQ]X8Q@%1F(&Ӵm:J.f4. lS,ťiVzkiİo HF@tSAY')<Xiڿ㻾b`Ly 򥬛=Z.-Ai"EK{rcVa:;ue}O[cmcofԕWU+,[p͹mNKG%7%?ƸW&.W&.QZShF пC/% пC/%Ѩ/mַ\4m= uȞߑVblږA<ʧk]K-'ڞX^lm.ɭ$BY3$r>9]GZouoKL['ͷ#iU;KaOUxZ֦ D{3m5Tq]&ca{gȶJH|/\&лg:}^;~Z&M-bn>x|MsMw!+MJx#SjI)QkNevfK_NgHZoIEŽj#3B22 yY!?#~! ǂެlm<o9xt <,̓Czd~&]oڃ/?XA/7|wךѾx{@¯fuh]}#\<}ڡm?̽o-'F/NGk22-aU%x&䋘BW7[)y>g.rKX>xGȑ"7SziGm䷛Ե+Sѵ-\Yڌkjb/,I la#$^E_4cVq\PIL6s\Ej\Ē8w#kվk?0btKM˿4DToGP3*t}T|~ɵzY+URD⦰t];9Oṵm.#M{Y9ETZg|5ïd.o˭F[sqؤ؍*q<U g߇ 0%ie?HemQH`r%iQEQEQEQEQEQEQE^KeSd @Gs-Uպ5*(7V׿r[^pҢ3n {}(պ5*(7V׿rשּׁ9 ?4?ڷ_[^#nI}NH[SYR2eg;W*'ץm),*HQKEQEQEQEQEQEQEQE@6ڦv +џMBW?д'j2. y ,P<둑-64ߏ:V/궚v_o,*ed V!QU7``32H#ş #uNt{=4XZhޝs8ii䍈`s) |}a{Nm"PM2JNx61}H' 8={y4[\jq `Ybp`T1]C>$gKGVH3Žx7ϋ<rڞ]jWPM*Oy-4jWC3A]¯Mz:Cdq!EN7<8U/B5wZzOMlki^$ZVnBr(v^IxKop eq{Oi(4+^nEK#pzzs۶_toX:ޝe|4jN6:gh%L,6 ͱX `\95;]RP.ޫkeV- gTw XgA^-iVLGNZؼ׆5 Qp R#a -_~W5ZïFד0I3 /_M?#͓x"O\j5{i"kC(#YF[{/l;uuK6M#W5(!욼  lQH rF|x{Yxxj:hwi|xbUYda'w'kOxZd|x_X*FX+ɻhBy[ߧv:/[]SB>.xĺ sV[&AQ`yU޼1:WA&!_Zj?ּQSTе .Bn .Z-1G5=Q]̛:GxoƲǯ_EiqMشsj"A"G-. *dY ֮5)s5Gu/n fkĚd4Kim'Ac@c/yYɠ#լ 1000 1000 0 C     C   K" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&^7⨵ ٥ӵ;H1N;2O'aOn|m2WzzTl}'?DV?ڽW+ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±A+Y pAPz@l}'?£]R7W@ Ys#{'_/=j K|@ٗmY~ᕵ%uxkkkh-a T@s)$~Jqoĝ5 >}^ំCax=J[m縍mm;u->:?q?ìI VgpҎ]@GH@?m#"{q躍4FQnu7hvS#D9Hdq\oûj>ɦjShv5V0W/)VKiP8DQ 5F[MhZ.e, ,@D\`H5hEyVwRۥݻuG*H՝\kUuV-}'?DV?ڽWc?}'?±Gڽ]srֶyr"" =$SL"4.0϶s]?5H4/ ErK=䥦0&gVƱ[xI!-ΛBpyܺn@,jFIUM:\^hvϟ$*TS MNK_8Uҝ\>T"ۀ9@;W\MiN#կab-J*īlNxz, ڀȞcT%Mqe[eP#*,[Wl_MH "wΫM2њV`JT1>?1xZ#wk"?,Uyݒ7/Kٚ caו_>š-dk($wn5$Vv}jp]B! ρ:o"81dϞR˱sM+lm~$x[]Vb+ɞ?h_Dggx_cⷌ5{޹Ts_}Gh|K=Ahc|L D|Tly[=f}pasKCR~%_rCQ#~0ywzTښ9f>*Y0C|Ta9^v)Tt%]G?7@3 />yWT{ޟ{%~+M7/W&tD}N'TC|V?S|c9^yQSJN"SsE;??/ O'?c J4;$*~R _)1sgoaA,8i04@S㔇)15ue?~*NWY1gN*7&gp Wow(?G_)1>HQ*phS㔟_r&دk.+\S?>*gJ_?}u+9݂x9hϊcj_}u+b5ơ]_h6uw3K,)fgbI$5Z{d6_.X蕯+0#e0rnM6~Nr#o Oj?U%qi>9'}/?5x-?/?5x-?asZ>}kwKbwKb0Oi>; ^%O1G; ^%O1E\'֏Zee.|Go7ĶdZ..>.CK;ž`(C':^xrQֵ]C]]K*Ȅ7.=+wKbwKbM;|3. \j~ `Mw!0b 63^}o'Ėi56ФqeePN$v_oü_ ü_ *-l6!}b_i5MC56n#ʍA9S>?EI`\FPW_/?5x-?/?5x-?χ; ^%O1G; ^%O1N.|GO}cCW2QcCW2Qf>qcsk?wKbwKb^]׍~GgXIa'e-Փ^> u{Qev5~d7y'?m- iiHطc'G KxwKRinR+u}d(f'  4F*VKcɯZ"Օ'v\?ugƯ{(|W_^ WƍjGrA Ҿ? >J[ML4EN)7dKqS!Wު=)bOZ`=t4Bkq2#ӥ*&G [CH#9$jJaFz _Trd;sާDG452r"qAՅBԊ  ' J'k)CRԑS:爪Qlz/zM  JO,\EWOJڧdLr0f:RHE=ZM66C hI&3M be S[(C+Ne[bZj~عcV|~7Xi.ȧR9WPAܽZεhت-ƛa# +gZϴ?Qk>߱V( ֿgZϴ?U(uC~ֿbk>߱GuC~X+gZϴ?Qk>߱V( ֿgZϴ?U(uC~ֿbk>߱GuC~X+gZϴ?Qk>߱V( ֿgZϴ?U(uC~ֿbk>߱GuC~X+gZϴ?Qk>߱V( ֿgZϴ?U(;Z*EDߖdu3^o?G"+Pu#t*bO~3xڽ\p?ϋO⧌zU1 q}3@Y7^1N5qRFG3XwTXJ(Lf)j6c]6FcR*`sXc-SDbj"tDՄںG-HR`EOF8c\^;~'5e#`z DH[(e8'HaV{TP#}jȣ>1@[R4+W?J|&5ii"'ֵDˊ̒1p([ثE9HNG4qGHmo"U\l.8 \r)M`tBā](\~Ty ʓ60H|xOUIZTYIfA%W xWpړFL@g*1U%,d* RFKUZn3Uy#% s#.HP"{Ui#iśIIPI#8Ya UNtH'Ӓ!U$NMrN'\g}T(5jUg%{f6N*ՂFj@OJ&xx܆L㟯*;??/ն 4,tWuC+M|]B:D[fovxÙ/IC)UvHq捹hH4oƖRyg(N8cO\3~>*CWƺ WrŸO <_ =f|-. xktEOBA܂ހ((((((((((nJ.`CGY׮ȯ ?uzʩW7Oo?{)$;9]_Y6Sle9'aHJ($I Ec*)gA7!H8aq5S zְ99}kXwZ"X2Oyuf#A9[VP kE-Lej0i>տgJvrhʻa. 6gjȿwX:x8ZU"G\2*{kR )4ap=<3[MI_Ɲv<XcTJwD@y`U MQNjr"+MQ Hyo4=EkMRbsOPma@V&!D9RɺBzf3U+AqH9zUWAґqTtsFΖ=RUڸFNJxiPf[yS3Zm F90"MU9ʙ54vPQ4C#4>f$$Nc! 뢼Q޴S[ym&QI?n/X ýPd4 IIO%Yf^NV@ɦ^+g{ .ƛrK;VzJdR3CZ>bm{<3GsK8 !4}Q@Q@Q@Q@Q@Q@Q@Q@]Z]QW_E:?R)zQOLfo*|S񊌓y޹v=}Gdh3*HG$o2sV@oҬŧ80*A=ia[M1浭R@W*EhG!>s[FMXK}<*vp1QCs6RkN]d5fs=EGhۼpVm;Y:DbdRv9$ZiY@4' 3ZtRFuԥ+25c5G^9ԂZs局nvj `Ḙy>K'KC?a&qjO59^<+ȏ"?vʫDr;?zu d&*$pJpsM5xB$ l)œ jnK+hqg~h3޻/Df)aNG_BTSxc5TecPNqY{6rj>5.@\^TY 9k31T#ֺ뉐1YW.t+s:s2ٰW6JnYm9YVF c9RX'bne9TYWt{ $sL0sՙ_ x!qP2*I7i E8OM?%kwe?6.蕯4}O[?BNj1HkB[!7GEZ'QEQEQEQEQECygoMkuw6!Xf@"׍\2/P[:ps63MgYb9?s(oc@erT[jv,jZ.ӥ#"9o Xr*_[kwWf;iei7(H)2Hy{IϨX[Ht; 6)tAm B;YSب@QEQEQEQEQEQEswQt?j:νvD^E%CVmGUH#^r96ֺ5Ny޹20x(Y=Y:R*3TcJH#)pa5q\sV^z(9ͻw**haV2^dt [сZF-gImi @`Z{+1նPI.Ox sX( N$'dez|{¿-`.oqirsͤC,̄ǏLko |WQM [O~@N,mD@ڗ]Eb[-)]#1i^%7bc "\ccr8QMGY2eaA"@Q@Q@Q@Q@]Z]QW_E:?R ?:e!fة&3r!(HUʿc/aWG%I)*ƖE,o~j hQOp3'bщ Ϛߎ[w+:r6ljBu8 0kgL cMP?8gt ,0qPIMO/ZW:P4Vs1 ~~m ^+_`&/}OW/V [!7GEZCqKa^a9x e}?q7"Q\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴQ\ۼ]@M-nw4OMȴUcDӼCa5cm̥$dGR0AR"~h8hw oE(~zGI=]pW"BWӉ`?$yvs~f>w+Ba];w!'bBh8hw oE?hxx²Fʃ"oᮽr֞7-1>$*o}?q7"Vjn|-#0Lv%+G)EM^߳gK_.5ޏOc?=q$V5-tiWRNNW +l~A"'2\_X]y qӂ:Tg?,XRzzm =} dF/\8Pqo>:OƖEg_w1~1J.`CGY׮OU|u-7vB鶺~,LF3cƾQU ?.3y޹aֺT_ 6t-PWTn;#5ZAY~t7ԙcQԭ֣s)\Jȅ ~~m ^+_+OWlcbZ3O>^\Fu> {'<tU5bI5`Rd!nQ$ׯ7 Ɵ m㛽W7ѼI|iڍD0Ip (8u-W!w|_\:EǿoQ׵k=)VI;J4nA`WGZhz%[-^Kd7Ѥ9HF+,L|Of'RO YwhzžidStsٔ]FA"aZ7.'յ]*]/eջ1YT)Rq/-I/K;ў rz֕[x_Wm*{J"q)(ޕ]|e%jxFKF&E8٣9ybSxW>ŗ>da&zfe-n8$OYLyYN@ko[Ҿ YԼ/-fSnx|Xa(2)$gmKz[ZQH(((((((((^>55kw]ԮRf^xDx.&c=q?)KKzkib+ RLexl`dz/|W{./4;5.&Ҧ{,ǛC/it=hw#|?𷈼Qh!nTbd '4gN.cjqgg-K-H3" F:4['Ÿx_[wv`%MI(ЉdD\1a0IrNO[Q|UM|ca̅º|QN_̡k)h”)4h IX^}*u82WeaB~z1Ѓ уf@܊[9&8`Uf+B/9-[N\urKBaҟPUǵtC0y[>^ńx$޶v>5_%.N{-rQbźmdQz/f^Nmgh`F$^o.CȓXTFcgFX4cO2e\'~6+ #U'@[=,xCݜp}&CX=gXokM(yA植Cr>4ANs_]4ڳdL1U\f7^* 3!"֬U<Θ"jt+l)OT.:U˃T'~+r:R^F㡫Tf9&&"tJ'Y=8gb"9n+CYnebZ1+Ww&/܊=㗡ѿAStoToo ӭ%aKp'OEbũWq-Ϳ#rba#2P*C[4nVF1K,Ve>˜ȣ\c+4m;KH0'E$$I'O$zSKg|0A⏃{XVhWu՚Hf#'k 跷pk'욈i$K{ks[cܤd#>Z,-po"m#$>s o&K>00ϫh|w:4#ڪj$B@ȁ@P(c#*IIO-Wwo\X-Zb@VpDAC)m۲k5;Z%m4#Tkk9GGuo(LY$UCI$(c©EG zSmV;{|S "'u< ch`-؎ Ys(fMd,f1\gAskWx|Gemioީk&Ǖs+gi?}vWbL^D^S.қ=1O64) 0kG+Tmk>2*Լ]v"Jko 8JZEfN;M6}JH=.Oix{v~i滽}ԆId@I:d 9~ xo'ZZͭfK,1+@6zfۜ97wumNo7ltnķ{dgg]$L~a!;M}w5W bIPqDJ: ALw{=nËxJ?궷mcsm_ieB̔Aҥk÷/iҼy]xo_ڝnQ]*enw#2ma1_`9mѡlm@cE @Q=*Hvf`3f8}ξ 6|rzgQ-I0%X.&mOF|h;re@ X~e=$c_`P?[]?U/1ƿ&U|E3_qu??ʽ_}kO*~6 iHmxXГoq6Fohպ_~Ik3[vƩwwL-%]xYgCT= d@rڿ4q>޳g~/ZM/!W=6[,vr7dzL1C:,0Ht{جuLJzs^EͱD_\hnrOn+ʹ949ckYAg3Hϋ_Y$\ۤ#Kݣ_Gw^6# G`9[?p==ks6Rքyqp1Sڸ2$lMnZEci<}G2\~I&"?09oZwH W;ץ5mg9RHOK&ajmj{pq$lc8ANG缊2hl`)9. Iߚ眙j(wYӸS?ZϝN1InA3cRMY)Fk ɝ+]e$@9O8}kmC_G术.{]ef5UT< iR)1_&/h9ܿoM_%k̾_ۿΠ7Cƿ&t/[<`#YZ&ba"2M0: (AK:$s^W% ghUQIn FJ{\FuxKtMNw YT\0"wgk~?6K7< =WuȐB1N!@Լ]i/}S6| W9ԗu$K",5l #L1R^8M@H To% )@Y^qZV/~'ȷczV&iNm->,˱yJϨZկ~}[xMH J]AĬ4oɷ*o!+^o * Ki. 9:'=jm/H,Nk@ooP>y,> ͤ4:qxU3Z]j_EIȌIA3#xHu?T۫ ϩ\ܬ_c[#$5X]CcKL4[co[X[.bcBǩ€2{mmYmhGl lř,I#$}atw7zi z^ic>9na-Ė'Wy+|l@uqyYvXd"%c/ SO1:I+ߴuyRÖQh77g7qݘJ_XV $z"a\ğjP/@NL7wbEN?k *6oG,-p\E~*c Wz93VE}oO+x[Bkj>}~յC]6"S%V[;67enZgit/tKk+d,e[ !e5g[y(Z+]?ɉuxD~#gu9Go=fti$S>Mv?=sy6si^;yz f#㊲>" Ko"~Y-QP=)<5;F;iK2Rtjp=Ϲ}-uX٪ģAs1Y__O;#)ڈHqM"-)KxB&oxoǺzIixhC4RB` x}^e|&/tyu-=EBVI߁$'F!xQ)5 ^M1s-#/.X+"O<"{}??:^_י.^>Uw=߈/W1\4:o^¤%eJGk"1': nQbmk[RV-Y.ĐȢА{ʒ(((?p\q1 Q |Nx?\7G8ݰ+"r)4&;Tѯ)AbCZTNVN͏1іS +hQ3rVbp=_cb+1s4TzՈof^:ԓ_},Ozm-o*ٶvkC,x?js3bH穯Vu&}~2nƛI_J6WvGq^z;Mm*K$l6pªcԱm1} kX\M-ݟ-r}+%yo .J!z-Ք2KtG6wWƿ@gGcgbI$P=r}co$'=RmZ 6sW*)\4Kqbގm HdT$Q.@1_*HS7.1CHk) `T7lxJGI\U+(49lhOy 4q=*Mv/aHfnsy&՛i7`3P) toc|g6ԣUG*TмGuk$K60+kĞR1_aɮ{Ikv[k-i5.:V=u˙r*xr+2N1t_ JP qםĶMXIw]NkN(|K4*k>")/Ҹ%-45[pM?x>(O,la/W }y5M.]V0䚻qX-˞6f>1^=LLzщأ]E#tKp+BJln{khQfB7m*ɼ ʈc nZSr!Ep:J{w;cӵ,҂{c=i]ە0ȬE+_guSHcOk1c0_{!UJm-O- GUѿAStoPVC ( ( ( ( ( ( ( (2𦜱iGne"![izMnzރ :=h~Դۤ m9A)B"Vе Lj|OேZ޽i2i+s+] BgAhݵ@JԵ^#m3晫=Z}4ћRQu n1ea7m\.[7o]ߊWo Ы֚}M6Jk>IHFI-1T=y|g/V5+ţkPǦaD|S}h%id 4|\jRrE_P:cyF48vBIu*N;2IVc`7~\.Ҏޟ_}7?UUoQ7?U?UoWWĻgj/6vڧ@nPFFKjÿ~xLN_%ܱj:׉uqHGk+2 3w-l?֟xS]VEewtMSHӯ|=yLYGy*3F@U$t5F?iz wN[`كI|״yd@_Gki-1.umDKۘۿn㷋חK~QxAcQ߬p[#MB kmGU>;OIo^o?G~ |P,bG=sJ>'\A0Qr݉? .+T h0)Mry)4)T(3*1H:ӨSҙNV=j-'ԗLЯ&݆pG_?pp廜ר|Tvob8n\漱Ӝ^.<}{R:Q!R<הLkMnj21cUrVFғmB;Tf١pd^+8_iUFo4:hم 'ߧ^ErסYa".K*d9FaQW ]Ϳʩ/CJg1y޹2k[r85GPXWw2\JŘ[q5L5>-ʏ]k-_G-_G'O0k|2g=86~xo{& ~&11]#gA]#'FG4ֿH 9 z? 9 z=Ed~pRۭ~÷~sm7s)_Eݯ-I",<2Fr+ !I7G9-?D p6%%x&&ϠH( $GfŜx#"V(Oh +^Z㛯"H8Mq?Tep㧽~#>O)oH\mAm#2ķ"Տt2>"S>V]ygxT8UQ4(7Mq/z`I*xGY-)L"?1)=O'FU*32Jmֈb}?nOE -#$?E$$!hUL|# $78?E?'/OO6P~ wo$6*?OJIj#iȸ̹~vb=3ڿXK| ƹPK /?>(A *#jn1U~GlKミ[?D?ҿ3Hj?jc? C!'5,3y /#쭓?gȔ xJdXGo5H_JSpƬ=M],(K&7t~WL̀ki\|K|_v|&5?~quu|66^(UUUI ?fotoxx-18.01.1/images/viewF-check.png0000644000175000017500000003355213222767271016000 0ustar micomicoPNG  IHDR@@% IDAThC׏'q߫3f2KHLK) R%0E~'ۂ_`ɲ 6xIIL{޻;f'b0WHC 4:B6mcZ!cS@"z*THPi%HQPbJ,!1 h-@ HP! }];IƎئ95SժLM;[Fa/gV0\KX]%Q1甘9D/,)h@O`"DHG^<"Rb$TF[@QEbcy1 9_5rmhCT\ igoYW\-g4"U {fZ+D$BH) H!@""" 'SSS@(ʷkNIiU4JH[Őzve9F$Ce}QwnLͳ^|7 s2)@̚־1!D113"_M 1kt!{k!E Q!KK M雲[AXS*! .w6f $Lj7'֎V˧,ϤX/o#J(}ٖ3 =ٽ! u _-%@!fH} zm4.T[@F PgODSVb5L1]u Yssi,Ъ20,(513sJ^@Z+ ]R:;L#(>YcZʺiLG!^ib11d{ek;e Hjo]Fkkܔu,!"" 9:ve߭Ϟ؁'1Ϗ-N7m[UQ@8HVk2G]wU$E^-Z6M^Vl*8HR$OWE]5-251>Í i|Zspz:cdzM"wQ1#3r5f"8J m x}Lv3ܝ뮋Iľ$-PX%CAɣDa9m 2i)XD)Z3Ĩ@_OFFƒ3hƌd$j6[,Wl^겪o7Ǵ58qk/m}JXtMD(aUhtw6f )CwZ%)Qb@$UP8;{R]<+fއ$&DH)lg D:ˋͽ[w7E;o\[ɃjCR]KM7p=Rbc2@SdWA2?Hd۶фJKIuw6'#Dmkf"3 -'/ܱU )`<ʊ.b8Lory~mћw^8}~~t 'OAh<@ ʚ*E:؇~k,{s5ԗu3$Փ;sY+xh<3(JYsq5kj\iY:ƐR"{ujWϟYB"2p:5  `wd5)RB Տ{jb^) `VIxXϸњ7 cmpEz)|k7\=8;կ~uR`H{ewuW3L G_ƒ8KΒFPcj $D@@ }qqZ2 | ,34iC" d*atƃtey3# 1$Ϙ0d(vg/˲ucر%dn}{u >C2Z0B @_gE٬ȳ38ql*†nҌ\e-b1 0.pe45U]rr8Ϧ;V#{A >jg'ߏzub(Q#(RZ(CZ҄DzTeD0Qu1&mzvUALE>tbR,b.z dwq>zHhQE_Mf' CJb@&4*)؇yoUGYx:O E1}S''#U󫿲zyѸSdD U*W4fsSO:{Ѩb0eq~v=o Ida]œ͛e5,& ddR5i4)$`F Q]|oexx43e6[ÏB|.ƀQ9qӡɍfYU!PLt\g}(ԗ/D뽏w}׭˾\1 6: $Zk"L D e᳃Ã"6eYu|AQ(/'?y^}_[r~3egvg{iE',R0dŤMi;].c!bq~q^\ !Y aaT`0sx4X,jvz|v~>=]9Fh4QySdlvcu7K x]ϤhXd}@60Kl5iÌ:Ƙ$ #&ƀuB;aiX֫6hwEiU=>[.}A>p=8@Hz⟶; ק~AơƩ76_Eu'Mu}*d(vbI"Զˣwެ$&m{8uXQP sxYLhYgęupNx})y7;_9Z ?(Ah!:g !t)JDD lVjQ2%N7&z >hx<}<8p7F}uB~d^v@词L0^͵{j>i>6m1FŞ*TO4(BPXb_OzjQ뀫*Y:M!Ml= gtPoyalW^n۸ݭݻ7Ct+_v@[SxrQFh, aH. R  P&JJBNbe\T6i^Ƌ1fm]͖rKBFb}ޫ=?ZGk7^j(= nrV1vguGnCLyI1ް㭁f9R" xv3Nigٕ.7WnEuQ$آ8}W7哷KW۝Hr_,Nq0m/8>K^7nlLfodNgbBԄE11*eWOXʹJQfTat0ts1߿>?z=, \]bEFqqS]3H6ڸNQŪ,몕8tyKGq\XJl 1Ƥ,5o=Q{onȺi%.(]Jm [j|AD1HJiC:1?z姿߿8lxN {Le}>`j>{1ڷ^zGZ~ /c ]h(#cʴ/+_CBL)F@*@bD?Ү>VIpvxÚ?]v/ s|5ew7_F+\A PAfbOmcC 6|`oMLGګ( ʰ&RDH ,{b z^4w<=X/ynuV73T{}޳J fwß n>< X41Aj%)\e3vN.C A"0 !]OIc]|`Q#J0.eݟ#d#wٛz4yg{y*>tm]ej}r˒)˺Hds4ln#EDXV(@J WVdz  wϓ@ٚqaţvE5x?|H?_+G>㪍ECknQ92ƥn)al!} uf Y"J%$*wRM2ν' Y?HÐAyW?||W󭫫Rm|3.4%B$t;4,\{AauJÉ] Bogf"D C[ƺbx". gI Jp4ؿetK7^_-.|YpL"͜""UʶY-Z_uAgZV*]N}Xej,3;{o}R>kÌ\o._[>R]LXA椉T,`6 HRYS"]eңрID9qB΀|wyvg|i oƿ?XO7r՟*Jpq5'"T87<-בbT\GŠ%Fin]ͯʪ lCB@דHd|krM )&,4@v=V re8IݵJ1γ"7Yakne5Е >{|H NɊ/އ?H)P|1ٟ"fMP1,2Ocћ) g#G&(r4F-"{R ! >ĔpT/O=Ilw{I'a8 @|H Nd?kBJ)V^EG2Ҋ)giFj~{mS ؁Vnc @ZB@i >w@4uU.NI_jEYڛJO4'CR~'_b[W1\x8Z##2]寫Pt6!A%IsDMJaJ *Jqj6e@KC$¹ƎO^"vFx~G+Ǐ'l./olHWX6kѶL6)}׾{6cR"g=36tL,JDBxpq4vQj "UTVA]ӐyϾSiWl6_.M+ӭm H4o|:t{g^P>7YM_>Z1zUuM]>jAdF%Qu@lC3ܘզAVS귿^^qh(Ft2ݾy ԖAX~Qݻx8/s3im/ <.veh/}z= p\#DP C86Y)јXXBQ@!db^eV' 2I5G`<nS7YiU^|sSr\UႇwTm%'k2c ;`!dTUJFP%$}t[*xxPP 95f֟XuQ=|rԝH\Pj`=>NF !.6M61IAKBH}-GMڎ'"3k@XEt́u*sYY%"b8( z]=Y/mahujhg>ediYp IT-Y+_޾޻׫K%DB"5XNQIiQ\X4(EHi"P(#+Q@QEXR=zѤPDݺQIh{7/ժ WHƘYJXA~ê,QV),lj- FKӨF!66h Di 0swr9)2m]ݼlzȋ+Y!$_^7VGtCtyH#DDq:4#"NA,DHi ٳSo}ӶMdբ ۇkY뫭6IdHʊ(E!NMˋg{g U^HkC*iQA"dȌPTS0@`RZ+CH=.,䎙сR'gǧW K}q4|7;ޣ'{҇jEȬbzuu|ډ*nyڦXqYI12TҔRO!E0B;v$$H}k;p!xer”XwRT`왺`fːluxs<{֛Ƚ(?ܻqc8) ɫ_8>zK.#k5#,bĒRNw"Foo˂27qs6T OڧOW1D-n]u ̚jU , }h&x;ggE)VgF,O55t#K#+ÔR,so5ƾk>xLDep7߅F{5w{>!Ͻ^/YxD ddy=h[ӗ3Q?|뱻u\S-5մY=ՃZl+_0@Tk+ݹr^sD:ʫ볝Nڊ ij!aa3R?pl_mqg5s1|M^~|'K}M8}u>~PU~&{RgZ^ t 9|fp}O= >Z*lgý{2J%I߽һ)l\X7I󓫿+~_9IR)$մRZIFڼrNܭth;߶YXЩ0YI㽎_X,UZ nŹ셹ǽ?`zy:FIу ϠJomLU:O68W!&4`GuyLs'>^@YXǨ{)"#3bnPpG ݕ0L@|$."#:ehEwG>KOW@뼂ytcyKy,㊌bPi@.!3݀UfuX(#$R;ȫtXxĺsFU< 86T])Qv g+ Xs"wTH<n_pcy1nfn#AqNAC33Xƽ蚜 ȧrilѧC6h҈A%ނ(qآ$rXp0DfIMu p(} ݩ<@CzQKc#%9A ^lOٕ!“9VeUcil ҐK ZN*ѳSVyfl%T HD±Ra}."40$v6R%g#lDA|ɂjQGE!tŬ\!MN8qK6[S/`($?|HBT ])"Pc;Ջ1C0 }T:F*r7 צt,I|zTXtRaw profile type iptcx=1 @yOmg{vc *nhߡ;cEY/6E)ڳR639Р$WxAsS=+]( (iTXtXML:com.adobe.xmp 1 2 3 0 8 2 2 EeIENDB`fotoxx-18.01.1/images/selective-rescale2.jpg0000644000175000017500000005063513222767271017323 0ustar micomicoJFIFHHExifMM*JR(iZHH0230G0100Fotoxx:trim_rotate|trim_rotate| Fotoxx:trim_rotate|trim_rotate|resize|http://ns.adobe.com/xap/1.0/ 600 1000 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?&AsWEH1^B+G+p⬄Y>mTTzUc5-BVܗhK1Q,qA9]٦F#ҥԅJ(QHi[R9w#EXjZb[U[dSn 5FĊgq'y5U$(={]fn'oj)޴C[^E:Q7UуGhQL|u$LЪQְ#Jpm-˅:hdXNPF3 iG2"aW}Ks2R: C9¬s9[m ydX^K7jvJFgN83j_m; &;P$`p ҸMG{=ʹ#Zud4Sx_\xƶpDhv;w5xRvNv@}qֹ?K4v0LdeJֳD|mՁڸ*;%b,åj&Yc*,b8'A޸MwW-5=@U8vz2i+,Q/,*8>ME!1IzTA%&k6W{B2X:śϜy5x_.h/d?gӊYP`S@iɡӚW]ޖp;-*mn(g6Zi^A:PWrߘAZC9>)Qj4W )XqJLHF1g>1C$@nz_-UxbS^I$~剘9+4Fԣ<0'd$Ͼ<<{WsmXLS>b8pE;RYdVrI>~"EC-xG 7\ߊ眵MTa$r؟.Lb9p{X0 \q 5kl][wI?֢&he*HqO:N~D')ݒdMO]+!-Pt K2$@@8%4A۫Լtbvɀ@$ \r}͚Bi3Mc]0j$҆"NM4ojVa\!Lj<ܜQaˑJ YJ>7BhjJ̷rTVRy/Ce!6g*KJ7'EAhKg89Ÿ$sZWxu5&A5pԙPH*I"$@EF_PȨ oLރRH[ҫW%s?h<<:gpt2s\[M:䤋VfZE̲$K n^YehE#x-Ar2\Lխvmp1TNXn$ךmz4E,X˜ =k լDDI)r{8†gR9v(ۃڼłMbOǽbs;d7sXٹv#6$k LRe f aȩD걤x|3ȪXN5F<iNgeZ ,3['>S_y|w\ 8]X0 PU1wIZCK\(L-osyVٺ`px[:ff6S(q)&|IN CQqoT}Y*,=%UHI"NEJHp؞j.US^C]Md_NF0;Vu&kVv_/uM:7QΪPҼ7Dx88&e'R_~6{ԶHhIQ`Jkάܤ:Cm"ΪD۟jXdkmB0]SLw;$i=`ָ)ue݋s@tZgwc_&%*X$W$2;gr]k.ʰrN8z/ۘ<Q#Ip OQ;yqvCW׀)NJ"|ƺQuhvr7. f1T95> ab( sp?i#ӣkE\E#|㯽TZZ4QoS|i6LXç7SA\{ CΖV7?Fl{{אSSkIiu19A۱]JW&Q*GVSgX^AiͫA[d8xGqܒq}G/A%?+дۮ_3m mHHwZ|7v1J+BO)>cz+C+ 2h)/tViǿX֓Wӕܧ*}q[ \]ԚjUFIxdEˮ"SK;:ju;[R3Qdfd 3i Jն#H c#4p($/-T_9W/eǵRdf'j)!u^r8C귓JD=;<߻c6A f*w 56=ݸ`򪁜Ϋk(P6$@ `8GDr`eE# kHFƶ֏y򌻡c+,=ήɳyfy<,(V8wg{(O)F d'뗹ss6XmTI$Oedk'q'HBҜ#1g.4dUr@ȥ6]Z҄s4A,mRsZΝHGxz;X |=3ҽ{׊$a1^H#qrs\xGB$Y(HqVUR؍?7@q/AZh6f;mܔgȭ?xF2CȏFNSB_B>5G^2}Ē䖇j/E[)iV0yCzֳ{K]ϴ`(/T7ep Ue;:{ os~Ezƃk"OGq_5ܸ2TzZY>->9G:qNr)F~n V&x v-ę]SjqvgZwWEf F\UlqNsB2wl6HJ2\/p)"ե`$,u5 [jGY,[Pazb\{TI%)%J$BC(u=XʤDF{֜MY9:U95cVcǷir vu9‘GSZ¢[Ԧr+kZͶ^[xSgWH>cBz橽Xu9L x[u4JΜV1۩>g#Rf5aqYʫgevw1CZ2,[Xg+cWU-OcrO8"^fs}@;\ޔ:ck5*ÿA;{V92 qXK 6\1ί/)d L5Inv({ƁCutс=v;TZ7*QFC9QmlEQʲK[5)Z)-ʽd3N_0 ҭۛIk*H=ޥ1DTg,͓#gdRGM BhD2)^=@ $QNNI\YV";I!vp})4k.O浕%ZG[6pDNkZ/0H j/0x^^ ٝJyocX[+Ie1})sY/X(BC9#c]ܼ3/T<چ%:O$-yrB7:]$wN)"^3 7N#!0{W̚U~c[:n6yF`rx4N14 BWi?/baIlI &{:װOXŦ A#dps=1\.ɩ!HTqQiW֚k7ޙqVUƣut$2ʮFpjąyȪ ӤXu$mbryȩ'E(QD|J3UbĚv{"ܤC$X#"2@5{f 8̝EH`AmIX\8'ʽ':V̅bvxBrTRDOj58<)$r~aמ6ʐGl͚] J$DŖ$=jM.IEǻ\4r]c:\% :!a Z,O 28׍ꖤ*,GRq"O$,qn% drBbʠ Vv1;>:1CV/lfvJ'.M]IqtQ ڼ=;e={UDiKNtҋݺ,Gz:.hb֏{q{6"$d #w1k* ::c]M0CuRO'ךMpaY_wZr:Bw7ٔl 8:}qO4H.pʣgO\w5Hx?0%GRfi1pASkݣs.jɒ^A co~z҉ >VU wS?N0tH|[涥;a#=+EAPC ?6se rM+m3;6}I`U=#P}V(BY5,r2?$9>^r yJLV4@rb{ֽԀx䘪8ln:+kWF&v(nשs Kvf9끏S%w8<z7]2 >eH\cԥj7%8'jSk] ]2֣I$C8U-w\\LEL@Pjgi`⑧D[rFkĔخ<Ē# >eAfO"_̇Γw*9V>Ҫ8طf9E[ҹWO=z5`bz#.ol&B?0'ږxҗSHcEjM7Տ}qEku5m$y^JA_=+Mk[2U5wRCo0wL9Zuu-VTv6A晓\e׋-1if"|cK3ȇ`|\jB;0Jc')yΡƽ:GUNߤ窌:qic*dMYX[$U$s[G8ݗ8iE5zMm Y_<H7mMxĜϵkiA[G, I}ֱWzĺ JȼEkw5N|eӏ~j\ HyZ4^mֹio۴r?z ] ιYn#q z{Vr6oc8 '˨Gw8[H}WNtEH9y^Sx#$jê6 F/V3cq^r=ZŦ>nFJM^qL^R+"]^*'=*^DD {]k7ta,:z̨NCJu6T\9ڃ8V})n"f;P}M3.FNyޚJVw_"cEĵKc%+MYϛ/-!3X J!_±5U+˔jnL8u%d65$iܪO75Re @ 88x4Ffwl7U+^I26 c` 9\ӕCUi#c `$? f9r%+Mf$+.8N̤rWCgE5ʎJBIgi G==}+LSo) `خwH4wYzs]FvxB!u }+&c/̚1/<)`vu(O5d2(PX7a?j.yba[ZtZdWru \HXC}3NFz KUKKXq''Cdz4ٌrԜ{V7pYW+?\֢3b[8##?qZ,{ ސYiWI$uZAED HZ2$BEE40ZJ(q9?krw*6JX͎@0ʌV`e][l}Gt&MrK$ʓ}OYsO:3iqZKkX^5i^7w`2X gpC"$2GExnׂML&P!"G|Wė(|ߖ3sUN~T纖&46OsjrȭvI=Z-=ڜ~\0&ݙb7ү٬e%l1s'"+.k_l:7~-wwLc_H$W z`c<3?r[nvc[IiԆ'%zⴣBi^f:Zu-Byơ9 6z}+SL˅u9#\ լ/dK?Oh؎? 1sԕAhmƑڔ[(cey X$dg:ӆgƣm,R0^2+9npm4ђF8{$j^<ֳ5[fnFr2zcޯGn#;a!pwPv2·3,u[h卼?_I~Ԗ֋ǡѵ}k/[40cYH~k#5s^L:hV''tRczv$߫SxtGNVǘUWTӮK=<܎}r2Bpx7&)L-I'C=ORʎ#2hol+!;&DNt^{j_J r!l(EU<L c 8HuXױ7c.Z#q],N~ ' Iݿ*ު1"O[ |myKL!|§һ'40K(/MnEȰIcW?γM٠U>XfV1FjfMw;nc:aɹqɒ]>6*@ w1}Toyo&th_`sW>9;"A\^IP;Ac,6P\DX:AFP~`q|ĐvԬmG#YFz8%=^0\iFi?U!F: qҲ溞͈11(rq?: +٘Ok Y]܎Xlھg]m ~ȣwrNr/jFVDnz xAn].i=0\3)iV) r"H;4s{NS@Ub0ľ+ak?ηԾܝ99R.fn{%f&NGJ,۝J z~9:ϡ?_[jq;Jh9qLQ Obe2Ź<2mV-O4xell[F 8'??V/3HIZ5(Hg̘mgz@౳AG GQAZ,>2`+_<팶:u+Z]q/׽l@=@sӥ_]5di{$a,L2HVboXK M5[,Ƴ٘'J>p5!Xg}{ wUV(*̩tbB>rO|y-Ǯٿo8`FdDVAmsM'D-1)2.}+U,#9N6Vr&}I]K tOHoZSZ[ddž q89V`"#.F?*5XVXZA2e0xU OAe lĻ֭G q; mcuGId-Z9UK;^A(&%/p5.`!6pGҪI$!xз)jb0՚)˚(w#+6w}io;q2ݑ̀IzfLbxR+^KR] g7?Z2pHJu SCw$&=mt\w3.}xI4;?)p8*xm9NEg'y8ifGg5эhXW zqL}~*۳fm'O2F̭n$iMKۨ}dCHZaCn8R_ʢ"w$ZUI;HX^<~ǬyDMFS++ #ӽO#/珚EVg$zAeԺ܇U{Xܪ $~zHQ!c?[ӝZP]x̿&;g@@=x'^ۙH ?J?*r: ahdU^3LJ쿉zߕI&GwbwZ~ dul+XmOT Fg5JXtj܅u$>µ&v'Y. g3C5,%tW5ċʻO#>v r dy'u'q:?/oΗm=g pcz&$w+ː\YvV6d@p?!X+OQ䶥yj' 'e9B)NOFݗln=sq=y4:򫁴+77LZv I9k^}ҳD@$Z͘nm9:6nx$335\{-$e v}+W5Q9Y0NȆH0PL}:V%Qe*qE/ϗœK_olƦeʮza_|9|C?kkХt6 GO89Rd<܏֠) 8v$px[!Ͻz6<:f;0I# *I$zά cfS+ Ͼ,y?•!Nj(1Ҁ?AU!Y?*IgX,;!So݊#PDp; Ծiu@<>ђ8v>)K@SJl6;뻑—p$Ǹ1&A*&n'mPGP@Ϊ U z@l}M0 D܂V?Q NOS(NUY9ROP,M)cA RQBQNJnW~~bAjbC-$@N|" 2q#Gt&;XA_а^_wڄnm;-?{k)a෹4غQQƅޕi. R@c\@X NOTf#\itrIn$U3?;fWg}ڰC x̛P0k!p>$[1S֠Goo~j4C ||pⳕdVkJ >3Z3kŠ\:̹X'ʰˎ;ք^Rc>R1jʀ+6~MJ8%^ bcje.Mt2>\U)3"6f9Yԯ/ T⪅FnI?\e#qUo0E*sP6 JڿU921e HPu2 sxy$dm#* Y0ѿԀSr_gG+2_$R\hlϓ&Fuf5eOq`" x<)u5?4reTcJ$N;y?ʬ`G1 9G8#ܜP$5|d OEM, }>qg7\vsOZigjl0{xیzb㝀Zɸ쭃X8#>-J@ީ3^j`EcO<`3W+>7})VHקGTJjZ4e^\=xOO%!E"$ct'?!xZN۬1$#8kuV8/HϘ:HTK8!zg<\dFYBIݽ?OStTGTe}i?Er'`~bxmOEm$V9<1֢Yg1Sy๕LZ^մ}v]K2񇀞yqP`pb~l#*;()wjǙKѱ#sNxӑJ+<1R!'9CA }sV3⫏ՃFiFc͓: q%c%\ UX7vk6Z2nN)~&ۺqL3}O s]VtId8ٿ?(?,1Z3Pcc1$ =k OH|lO3Z{Mpc*PH?zGqqnF*13m~Pe Gߌ۱U|Px0 Wۅfa\,6J 9x-7\G=>lS }޵TLt6ia{RDዮ hajn[OCEJ )>Vm*!@M=]j|NAc2yUم0< ArW}@$J?$?W!/zҹ$w'Q"3g1^y{_k[ѵv ]GF=O<]⨴O g-b!7p=5/hn|cA9!w:dG/d-et0by<)D-SwJhbY`A{ Kx[hmyk);23ްTJ"pg-U9]+w_-ec=e qNx=|W+]YI$16lߺ1 fWF[oq!C#z.=ŬZc^iK h<^&\lkK2+hC*y\TFuʌIcǭ:I:3e C;$\c;³ho 59uT/]˃C#8[Xc<׊kcwR)SE}7U.c)MD:Z|҈vC+1ӟҽ*#,#.Ռ ֠iR{}J sc7 2nb q0˃R!BҬ1PO\ۆ1}jHS@<`U7fMp8i`ߋJLpnsT31PaH*0$cx ib9Xjyݜ֔6\+.sSY1$ڑ zܓ~}j"xQך CP[wbA$Ԯ Wc&]SNVopgټ͏'y_ire]J H޶|c_1X9嫦3l.ٸ8qէ)6zJuEJ-$@3MRm#=zkswEA\>iˌJY_ݮsҜ,UlNJ@se[$ @JeM惒sTU^p9c%G ՁaY$7gEsEHD;.HV泮 +e_8+oSh|H 6؎sE0H<ٿ1Y5wUjdGOY_*AǿN0qH|I#'e.i 5 m 1w4ae-ҢA3nqke~8>ĀRp=k/W<V e~5&M%N6dF{ ïy])H&PO EAя$]E )J#8agIK/#`U^@Π::P~4pI@;6-0? *[۾@ 3ٛca֢(ybO9=Щ1R\2˿ВjFB~Q.JÒǩ+73ErGuj4#78,2~ dwKplhF%YCT HqHpxD1'har@sH=)A4'BW*B䅰N:k OEv!G#VQO?_ˊ2TڹMN8eE"ocwecMSº5#:u*xC91`ZZ6~)wGjsg<s^,-SьV^Z6#\WS׎.]4j\[hwwf Yu#,14 8Mq-G]ƙ٥x TUk*ײ3k=QBezK<#+$/GAhD%[y=M=Z+}DTѷ :Vv>}K?&{ 塌o鎕8UPLXN|)Udz8JOhm%̅ߟZRF1i^g "ʮQƲ!O7!s:T43PUdP5"g'i^Ջ{ s&˂Ҩͽʰ{5~wKdQppۇaߞ$ʬ1G#b)o23FrJe۹QX])wpjc냌) Xޘaߕ[FO fotoxx-18.01.1/images/blur-background2.jpg0000644000175000017500000012705413222767271017005 0ustar micomicoJFIFrExifII*   (12i%ssPanasonicDMC-FZ1000Ver.2.12015:06:14 11:13:16PrintIM0250d  ' ''''^''''$Zb"'}00230j~  g|ro4884884880100`@sv  ' 2015:06:14 11:13:162015:06:14 11:13:16 +Fotoxx:resize|cartoon|retouch_combo| Fotoxx:resize| Fotoxx:blur_background|resize| Fotoxx:trim_rotate|Panasonic1< !` "$%j&0408'()*+,-./0123j456789:;<=>?@ACDEFGHIKLMjN*jOUX^a kce@kf@kg@lhi^ljkHfllm lnolpq8mrtuvwxyz|HmHnHHnHnHn oxhoo ooo`o@sP Ts \s0148<q  psDVEPDB AFrA < K  1z۲1%zz7d۶\\#DD`#bd fvhjl;nprvEtvxz|+~VaUaU"$&x(Z8 0' 24T6> ۶ ۸ ۺ ,XXzAEhjlnr$$  Z$$2       *8V$>. "( 0:&@>8H@{6 "$&*>.Z(,02<4FVZXDBDU|~JUFHNP^X@Bb xzK \dbfLTK U:p -2- WB6q  f `d>e@B DF4  hj0@l$ &  " 4(*,.702HJprxz |~ tv  XZ8:^> Lh~U\         q 6        <FD` b @ B D F H J L N P R T V X Z \ ^ dST " Z $ ( * p. 0 2 4 6 8 : < > @  N P R T ` d h b f j p z | v V & X l n `bfhdjxlnz|~vr ES:      YCB   0 (           " $ & ( ^ * d, w. xg0 E#2 X Z \ ^ ` b d @@f @@h j l n p " $ & ( * , . 0 2 4 6               xnATB< " $ & ( * , . 0 2 4 6 8 : > IA      A   :     DSFV4 P  IS`bdrtvx{z|~~~~~~     (.{RCN@B2D+FHIY "$ & (*,.#0QCM  $0p U#DSCP/yJYpSwWtuzUWߖ]H=__}utW1V!UVw5UqS]UDISV8888888888FFFFFFFFFFQPOOMJKIEE;==;;;9;;98XVUVSQSPNKvsrsqprnkh,++)+)**+)453442200/pdvevfuhtgtgugufvgtfu@f@f?f?f>g=hOP:SpMLaq4\\d0A:49N5.19-;&B36!.25" #,3%(31+.1'0 1248-7 %  %) OISG~~{~~:2.5~~~~~85/&~~~~~  ~~~~~'75~~~~~.+#~~~~~~~~~~~~+4~~~~~-#~~~~~~~~~~~#3:=~~~~~<4%~ ~~~~~~~~ ~~~~~~~~!#~~~~~% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|~~~~ ~~~~~ ~~ ~~~~~~~~~ ~~~~~~~ ~~~~  ~~~~~~~~~~~~~~~~~~~ ~ ~~~~~~ ~ ~~~~ ~ ~~~~~ ~~~~-:@~ AEBM#'>(435&%$.1> $1rtz7dgjjkIfnqskWglu~s?LhluNJ<]Jqvsu|PRSTOHa1]Suu>՛V՜YUO=VWuW]uUB-u@E:U6u?daUTM}qwtVjM6GURIU\xpQ_UФ7a$qTEg(Qy9O7it:r_fTV.Hye}UG_P]_SU~yVwElE9QQURGTU]DQTEUSEUU\&QLUEUVUUUQ\UqWI1UEUZ_'U|]yqlu4]%Qt5qu[SHw)OEw1BK՝wy{UPv=A]_E^SUqWmu.n)~Sy1cwZ`AwuW\YaWGayu~urEʹ|7G'/Qqq7՚TPtݝ'_W[\ΘV~\QuNv6V$M5E55}UTU$QY}eYW/Ϸfq("Bk+")F>qI$*;2=.!&7F**b),!tyYAf . 4?! ix$,=8=pC.A`)gjc)"(A) [71#-44PL67KC&%#sP W&{/$-:6, '>.A9.X8.4eLEf'$+8F:\:*(.6@.3]8?*!hJ< EE#=.Cz?> -I9E6]TbB<B=/DmUDMA''4/\W'b)$5O`.(M%>+.%;!"!m(!HD<n909I/UJ//3PVKzD2 $-}&v/'.h:L-#vXFI 7>$&A1* f=~}eKGDc,(-`I|2Z>`L]k>6 +()/0',"'-5:0jP#K}$(B3$Cj8<2e0+/t0I5GSNf:/ $&bkR%&+k*:-R`aB>= .6(4c1O14ٲhT%O1b`2"#5##^9X0`jD+92):7()##"!% ' $Z$"-D 6   1 WEq2#Mb($#>.N)x+1DAC.) "RZ?!"(V -'d1PS07c. +n.&!F/C؃AG@.ZD-#$%$&\:W4Xb**/#!,"!&'$%)!)(*%(H )1j~#   )I`y0<P'#"/:''%;54)( #T_@#$$E#&S+O.2H ' )i/- f$i3@rRfmsUUUW\]E?]G U7NP7NUW5r;q+~WU ѯuMW;WftU% EUyWPcEUW0wGM75W5tR]T]Ux-%^T5u] rXVyB][Wwu_}]4?G_G33EP UzUN-euUUUEUMKUTUUT]TaTUPTPVTMs5UUPUU]USUMUEyV[UUUQeAPU$K QUT5_OZT58y[S{u%VYe.BWQmuqxDquqU=yoW]eO;^y=-5RQMxpU}uQ[C73kNUOUToQQ!u7|]=EOuuCǖU%DEuɑZs]ݖp/pXVԇs]e=ui8ZctsuSU5y'w]M]Spa5kcDOmW]՟ _%{0EVEETfO]OIWܗ5]|u%uS5o]уU'Q55SW^M[F>_UU}QMq%tNA]7YTM]UůV/}lI7[ADŽձi&-19v]uRykUy|{444GO؏UUM_V[W^dQQ05UgcY ZGPG_[eywyyAD}uYEU_TUECTEIDUUQUuU\YTUIGEUQEUVVUTUETuQTUEQEpU-w1WmkqO1E=iO}g=/5'}u 5]}Օ_?3PAMչYe_^OnV8_qU^Ds{5ՖdwWC[ wuU^SK5CRZeՓ4VA1M&Y-R"| LQUT$TEQ ~P͑\uo=5V]|MI_IGUU]xgg_TUU-WYѕU]sUWegs!_nYPL=HP]y]u$]XU{glTYQ ey'_GU}WOG_UV}LQUu$gwUpq}P]Uu_WIU4UsEY'Pߢ_u0Q=XgFϟvQtIQUPc1H=%qD_6_uuABP45U)ĥ5 UإU'+T^ST_ۧSxwU@H`IU[ UQAucU\ NWLIPUBUTVATUUEET-q\I_gU/PUWx\}~DV1WtATlUuQUuUTP?jA=uSq3}DT_TSmE[TwUSG-dgC\SveQ0uU`-QUS1XG 9UDD]UqumV VGeYA%5UEsSIdQPFovWOu9{SuRe\AE]߰ETta=SwgT_ __aѓ^__L{QDvV5`rP߁|]VݕUZ]U 5PU5ztU-?_mREߚu5%Q;qP%YQ~QFXTgR}}DYhp]EWtpME]}t^E]xՅ s]=IQBW]PUUUuU]՗eTUUeUG9M}aU5PtQUGUTU5UqAUs]Yoo]wP'[Atu\40UWPEgwE#Vsr^Tz|TqUQ_YUv!Gv-'_yPzMWF% MH]1O_Mw>PS0%A]%LUC_ Ou=uaA5tu|Su.^FOQ]KEUw5~ڝebQa}gAE_HJuU_@1Zv`YsK{e"aܛ,V:-V>v44} Uu6]u[Uvw@UqE]Iu_Tg5sq]&tx5UNtWGW_OQ_5KYUQqҠ@\Wԇ u w]WUQ2USouW3V_UUue5M5]\}wU4}USPW$*vuVWG }U\yD&g]L=w=Uu|CPqqTX,VZwU\PU]D?@uSWq|5QT%D'Eo@UGq4UWr5Q$WQ%QA5MW}AMWSuU?quWcE[_]MmStz'=e[?EEuV ~pS7WlWueUqU41epS1[e]~E EtW$ߧuuq\ӽA%TG66\W}wGt]AzUAwpT}Umn/G7Vw|0Qsj˸A,@WCI[7SWCU|dws}yYg_K_uGU@qT|AwQy`]QYZ GVA EEZ,Yu|uu}yF]m]]DqQQCSuWVw|yv=te۽UcP]T8Y1uVQ6eUWUDVQ E1T]UUUaT]ЕTQ&TPMWQ_ߝE4tTUuY!!T4UQ)Y}7ůUE]\qqqqxUGO&KL7]P[]z=eWQWEcP{u=EtMUIU-Wڗ}[w5T}G}TUacSuuYe1JO'U59OTEV f2uQjpQuQp9J W^x4-r9V3uX:Q^=E=w?^HEw_cuzEy}Z.e257u-]DC ERwq@qy^ ]=vGq5Q_Ywo_Y}OG_=wSt}UZwwDt_m QWY]7QYF\&]ɝUd]UOU|z_U_UJB5_WTt][ ]7EuDud!WBCZ |y0&M1q3fGB2#  XQ3gXyCG(i1\&n87((X?jv#J'q'#[W  1T  C15 vd   3): E< N }  Q 8 d n \ r [- = O # ,1 tm?37# + :K[<$hV!j8 + * OYQ#Pr a k( 6 6=>A<-BJu O A*$J#F&AyUB !{G D&/ dA_i5 o$'3#U!P%I1P9j"W'AZcmSB<SP|KiI/R.,/$..lR_$=&$ ")UN)9& &. Zo  7S3%a(_)vt                 GAMC w-'"  w-'"  WtuEF9615031000869999:99:99 00:00:009999:99:99 00:00:009999:99:99 00:00:009999:99:99 00:00:00GermanyFreisingddq q 2015:06:14 10:13:16d9999:99:99 00:00:00NsEs0H ,LtTt(\tHH  """"&&&&"""""""""""""""""""""""""""""""""""""""""""""""""""""""x! }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?@i'VtDg9¥+ [/Qw/R?5oj?ĿG>/Qrҟ_xF/Qr'N@C(N9tmaM+R?H?҇V_xl\?տG@][M/CB`S0h}'S?H qEV?z I?5):oހm]~ /Rdj yr\g -&Z ԿܿMsC(s >0;RNpb~Dw*),GZ,RE4 Za}MFH;tk{M a\2#eW{^uGy [g &?LUr}8UGwx˻5;R!#; ׳_AlCQ%¶[!7mGUIݞkYO-Bd[agi-w*-r*Ybʀ;rkLo:~Ua!=O57擒ԛ$)ɦ֥=H*e^0%dt#IZi3`QUԼ{Pwg%Eg%h?ިؒFCM ]D 21jzq&ѐ:ӚEH×sՋi"P(arsJA9KH gh1YGA*I闚f֚3+#yR;E=e/#E v\?JFُD?>R@w6rTtAT' I'*C1#|Z-^4)(RR7r:((3uO# dһ2PКpZ zHD{diR+Zl]M.k+c^v652WEZALĠ3"8kϫI+7pzzǀ<uSb3ڽR4nw=z!ai] Z2p;m$,Yn(=A5dNh)z~z-\tKVeHL ç^G|u5ᾙsN%d`&1,kOF5a56_t-@4(t X$h x9^3I G"i%^*0bhy9GWV9RbfQف(⹓O ?ZNi+6;z pqM$F.*!# c=k4YHj0!SrŽɐrGխďaiw5Ȏp3~jxFI۱4/ h^j/E!- ]zzNא/+mx^5M49 }^$9$f0'V eyʞk Za ]gT>]gZoc.bofcyiŏm}"R7mǁϵtc,*I-mo8Y3屩[7&OSROϩ{N /w)MاXWZј2gtӽ5*F G=zJ ׸֠86C9=1xvS44A%=_b/J\ܷ"H.\!`0 ss[rq*%,B.S8uǥfK9H!Cٝ^QE%$ٝMy!yFb qTM5K+8K`=3tخ,*B~OVFlIp8.x>[+-uqqŌ6LʨN@/ϷjE%sĶ^6Q5 dBS(V5$\4v/89w@441.~SCSދj6l+) #z2JXOJCEI7w"/4۸,/ ?R?.QF };)M*U砯3;oE&&G\ؚHl_x>(5;˙ 柕G+7בa&f=YqR!CBsM4sމ~яZ~c%d2|rkQNO?nSN-zj1[Z]ϯi >u߆Z|O E#UU/SMkw"7dN(8%1A=H+ӛxMr\Y+7~%y-[>Vc:m|ԌZW+t{D'uhPJ5_X/ \$**BA+NR80p:u%mL-8ݓɩRB9fHwnɤ3KoqX2jp4ZhFdAYT"W$}M>;ɵeɠ.IUm4^ T-50ҧK7bzM_a *J}ϭ۳*EKXlr1b\\Ȫ(smbl2*99#1RFʹǥ?#ؚ"S!$!"lj7ylMh)N Xِ*AֹFIp =tFUJJRz!IW9JC\T%$zhdެI'|R 1B9E-ĥcS7pdw  ז#p&r:WVWzrTݻhlF"t|J5LQ Zt{U̬>ˍ$&:LmI,ӱxulg-;9$ Č<5$4n|FdJG9%z6fT ca뚈] 1 376E)GkOOֻ2yrV-N:6NPr[6OA֒,2GjA?$:/xdnOZ@pQKƧMsZmWz,[N9-`?"u?y?w?SLPhotoshop 3.08BIM/gardens, art, fotoxx, ZFreising http://ns.adobe.com/xap/1.0/ Germany 0 C   %# , #&')*)-0-(0%()(C   ((((((((((((((((((((((((((((((((((((((((((((((((((( " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?TԹs ZzTe 5V)Zd9MMf;c\qNEiiԙ;Uyhj UGq"qQ9e s\SH1 A<-DA+S]x5)QMZC"LV<$5 \ I0BQ3R5B@%A˃҆bMb9^ +PQJ#5jRo%cXŒձb03XZd5"6VgQqCU` GJcl.QIR޵"jݪ@Y8 XZxڙOL)wTޥB}j QDGD1֚Px!dWwUN s7-סBgSwA<+!c֦^kۣG5+9▌Rn`ms[#\Ҝ^Wl7oVNf =h8ls^eZ2Y~BO5!R4bZn@+ ]{UgҝYEKX&+c5+&O*z jValBRG^~V+qa ~a1]8qֽVri׾ͻDs-۷8%T`ddEcHA 19=~x+A9wgZ\~d8 \Y^3f'tߥOx$EIh W\{&bqmwi-f&<8#Â1_CW:#| j&ڼc>r1^ gxZZ;o#H5MVUѡ Y\i "G( Gt(,$tq)`9DPBҹP:{滓M>Cf{D$`;#Hnq%\aqǽz6g6̗WB$"Jz/ĹVhEsX+T9p RSWǪxWmI4!i+#(5UwÌ5Rׁk2SrDG TJ' |Ԫ!4Uj7";cdE1SnMfeݠe=럾$WX` wa)+*dV O88WFJ.J -hsХ74q\e ջ{;vi5h ΂z.9s+ nN+Ϟr#pΧ ^zOOftJk{t9Q6-]x;R<6̈́۴m!@0Ld`W30Nc.*DF ;F=Hz7+T**J[G"} R|9'$Hյ:3ȗO Ȯ 0r0q-2M"ᰘ *?RHMa[]7p[äHN'3yndle)HF4bNSO1ӵFVqwUzEcn{_-aMosCC = t'Zm> ᾽Bbvbr[;-W.wQ3`Cy ,d-i&t(X}_I9KaR㜎zӽgnZIt"Z$dYǩ @z@'𗎗B,MAKI!hz08o5QGR*Fi bn\<=eNK4y^0xhuHtM$?$|BpUOԉrn~"˥oĨgXps:qZ> چG kLcyӏ6)dSjWjsK m7*ăP_3RSl7ijD{Wy5剫z!PɩW,@J(ؒ@:seMZϚWe, ɃgywV潚48_An7Q9lHjx5k BiA4(\҆JdShsYRDԊIV84Q?AVwjj:sA 2U8?j覢ڻ&!ki:)Oq^ῇZjlq \nYG"P9={ψW턳0x[*JqǬ}#i k'ưCe⥸ n_[!< YpT9R}xC]͠d^'6/ ňx; [9:`bZ'E@YSG’q6CR{y:*#$pSN_F6ym1KKw?) pT8{Qʥ5K. VHUKW0zpWoh 5 ;Wf\"CH#kߴWtZtQLm8 erT+Tv$dG"o7,#b9A1 9t=NosŦXRog#ef0I'l`Jxk im-Y"IfXAzZ_?lm !Ag 8^+L6[4-[7zdt9'30ġrxxEbKib䁵#$uB;V׍omm-pypĬp2(X qtuj-ZtvϹQ6͂7#X9Fh!W[&:1,x~l@ wۮ^Vn $ZݍKM~QJ˸S;p,;k)9m-E 0|:9 944Mėm\K{#n[jhctϯjq۲XcTm:OͫyX~`G5Y}^=Ցq[֘,{ jWC7VCV=M"D96LQ)`%.j iwQ6Zz@R"ia5bfu H日1Fơv/ZH$+IDX#;WQx ڊn071crD->mZuf&Y"]3vaTzBV:ӭW\F$({ܰ|`5gOxPB*/,ʣn| dGFO$%\ !Ve7X2A2c .!f20`>v cAkV^AOQq4!xY%' cZi6@ŵTr d 9aUxgmcV$;eA}qS oZO%1 e r;?Ea(e鸣eتpx|Hu)Vw6̹ۃNx{ 嵊Kli6z)蠎7m\Ĝq"}vKX[VO 0B|یqHj3Wb)'(f T!ҢU8ք6^MVRek]6 2L] 3 bHmm K·Q 2ՀI#񭥄 C0Ac1@]Nfo:r0eU9k"0pNHZ*٤e\Iq';q^I_> HXIPcHfϸFߺ68=^,\F2h7]j72^V,Aު2w!4eLJ(Ӏr&(RFjAa46mLfSm p6h=jɩҲqHT)`SbƅaBP9(5&"d.kO/|G! (n#+Q¸k|i:|mu4vU;r6P`d)u4&}?Ȓ o1H-etTLmD2%6nrp1ǠO⿄.6M}k=Q j~-[I7#:( \{ou:;4CxRAe\s<1FsI`z^xL}T|x6 {^eH: ,@`q대;g{.Kalm~i$fԝ܌װx֝eR:*rb}{rH@5O py כ[(`8`dc=' V @ݒjϊ>2XٱЖ[| $r}#,+/{p2Kup2`3RI51ZF4r2H8$ Qt6;^=11k4^O_[BgX1`3TkFK{aXb , wfI7ietYRwRp~8s} 8$JW^i&a4W>ia rr~ZHKCŋsM'SA~TsI)j.i HKf\ԀԶi# x[.ң2>9\jfc#91֤iTsZ\i(KÐ&-HDJ)Sܑ?bZfʱ0(`SX8ҧ"qQ'mXzе-Y" +ί 589 440 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|Z_xS|(oo#=|1^vA+ b)$-Vբu Z˗g} s c\QE9.Hdvݧi>ះ3nJϋJn qHd"%Bn\RhV7TWYZ|$ OiID`2Ns.Qn=oG1|>$xW(aOi|>SGÚ} >}A6I0 l|56ūGmSZ `]^K/#vđ=f]A~>gaOm9? |*Ju50 0b:\W)>2JQxQh u%֙=Ig֭+(!'}5h^xDӴ{kkZDex"E'..pGNͮ}aOP/ItW?y [Fi·j܈5#޳}]// h մ <9}%ij}Xj^#c?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`5eԄ  $U>6{ 35RzK..ĂDM( S 6䁖3Ӳ2M@I ld0U Z5MNXWϟ(UAa3U+7x7Q+3'ъw6wao-l.,Qǩd8x7px+e# jޗo/jĂڇ (>gS^%7Rm f$>;ƽj?v$Q* fY%E!KQه&4_,6O͏S_D]_|/O}nM3@Dl(g6zxuZ/}rVi٘9_]xR挒}lbsGO~ù>{kù>{şkY=s'֏Z r|7?.r|7?. i>}O;YvG;YvE\I'ֿBܟ = z?ܟ = z,Oi>oA]#oA]#f?=}hIПw'xw'x0GO~ù>{şkù>{şkYoZ>}k',_G,_G.~{}_?Og=Og=as'ֽ O#i>Эi2S㿏 OCz4$% |ܟ = z?ܟ = zvopN㷎]>I$3:r<2B3){DEu3XTjlsEmv,JJl K"sG;YvG;YvG+.|'Zo{şkù>{şkYoZ>}k',_G,_G.~{}_?Og=Og=as'֏Z r|7?.r|7?. V}wVdyz?*7&,%%g+w'xw'x0-̷LM˿fү>gt5gyv8dg?ܟ = z?ܟ = z,֣%QCx$?!WM៉7:6WmbVD^WOg=Og=qtutzY~e*l,^^i w^'-+g`ZFAlpy\ܜпw'xwc]YoGiiTcdgb3 ξ&\Ւ}U\_~+h;ChQ=ſ5II{vex' Wi_x_.(t.L$Ty`Z<F\n 06ۿpu_ZńG[EIៗI]Zs YA^| $;}b?hvE'̩y[fK;xSU}jl̃T69 ],D 13YJK+m ݯo}et{YSψH4rʂXs^aARPuiS EFT^CI E:lzťWFx %Ѻ/Vaʊe&[%7dϵ_ ]da.f d_ m'{}]\hzmiki8e0,I9$5bh>-tswUBln(tC,yeFU2G8۰_|Ӧ{b[ׄFEI{o3"?Ty?|GeiId-63K8bhkOɴעj:u<5vWiKŷsPɍFnsJ:omMϊ|7{F_q-ˈH ^5Oǟ/m]ɨؤ;H%3\r XXK?ꚕCkqp4ot)8\-p?..־A4ٴ=FkW j^mc \T2M p(_އnxJ]eMeOsF#޹???ot+[+=gʉv0*I cs*t_ \khImm$h.$H㌻%Gd+"> w|E$R3_ȏ>Ȑ [zlJR_\H#Xgk}ޭj1vԬ5H.K;[y^aIK|LՒF%q_A|I^ߧx_\5kFNt^Xb2D 9ȧe׷+KcOVj~/oaF(YcU۽%=ҚDvUƅx^ǣ[Dge#2!,Z979 q^yU[[Z5Bm{Ku:K[˘Q F1;NU2kyK_w:+LRSFS _FVGy-V/̫mn kKV\/u( KX|VUY۸ +Uu5u Vb rT\5={S>|? Lkz5S+i}43}c #ָ/z#xǗ(L%gUhVr ɫdDߗG it-j.K&e߼B9j|m:\;9xBkjYu^'`Pijz_[QE ( ( ( ( ( ( ( ( ( ( ( ([-?/ -tin.-;c xҀ:'i2i֟a#+ w) A hb:xH]dFAՃ 𷃴_Yhzt:uH8˱9,slh?nYi{t6dX d;(ohAxNmmM,76Ϝik]O_yxsg4u:!|Ft#S$\wY[-Yghcb>FNq͓.g5U׼k`]pGr$,)2(lBRQ]Z6?: hß?.?ϋ>i* 'iQܥcyXD lM7CSºzƇ-ݝaCun:IsLFr9rf5?uxsg4uר> O,Ȥ܌q^7:-K/x6TeST-<*{)< sT՛DR[3OLƏN9A3eǞ.y|7|; =o]oQjv ӜGk-ķw03*F쑎hvQ{_{eu ׇ??\\tv-3ß9|Sikk:HgLM3pS,Ϥx{σ|KYfoiM/sreH HX0^voYP7E9YІGSe "[[wV/,>Ay/CnAdv4PܙQ>~_ |Ԡڭ_OgcXfbL}ox C{cZIouq%̷3O+ K+3aTe(//iYȞwᕇ?tX(_[M{LI4HB]C@K ? Կ]]7>ODrU[_3 |9=}&sm4< anϵ?C@K ? Կ]CJQq}S_'oEԔ~6d\|6ԼWx|iM*_ZMJZ#';֝⎭yia|Ol @;rOc ? Կ]C@Kշwh~4xKZ<*No5[Pᕤ Y%p(ܐ 5y/G'+Rqt.ߡ][:z+j_?.O!V~=y/G'+Rqt\ ω ]*w&o^Bz\$U>qf?"ƯR`yk#u5 kkhWs+ۺI W:Ý-?}j#0g)$ʬg|vڡ[ }1E.wa>mG~ ~X#a&I + ufe;>kѹ>nex R~$kZ6q;WN;O =԰-w` #r+n|7"9llW+1fE,{:ּ;ïWs|of m,i +[p |"2.!*M{T7i>3 ղ01 JIn˜#~Uy/SdG"֋Nϑ|M?x~<HM{'+Rqty/D4y[>?|=o V,EީbK˸b CqHύuE<xI|5cmq`0OoڌXxCj_?.O!V~~͒m?~-G5gZ,"%Q4B:R>oSQx{QמBu彾JL$K0L33dz'+Rqty/Q S\o9.gwO!V~j_?.aJ?dv^oMMIֶ}'[dq>6_#5H!m$ r0!p'rb1עok&kէ{]3]?+io[]?kO[*:_f⸹wMs LeS*J#;@Z"|SwxŞмO=jMo2/ClvUSһ_wwkGu'U]̞_G[mGj?CY_FC=P1 T iTQE!8yMxgWдp#/(+- wK˒9qS|?]nW:>Ӡ!~;tڸ˖%rŗ U޽Kv-~lcr~,uIm5K׳d#'I,N)=[{kQMד/jW6ռ/PC<*n/[}ؕYv`p_VZO_^hãx4*kg[i[ $!WS+;z~v>D,|'iEů<-uipJ5ݶ23":ٮi!6VlH6Enʩ2 .HB>[^/,KWisi)̅CV"'AŚ|^/LeԞ_(1+cAa^s_.LUGAj1ϣMX;طbFV">Lҵx3]aI)1X "7ya* keu诂 4m J}/DbGPmǬ³$2L9'~.3DF->i!/ #3I$0Hw"؎m<_k^}: Y!'*=U_xxLQ҇4RWTqbAq5+h&QuA*5P['=$ #x/>]2x/ @l&:g=N, Ցf8UF'ӈ\;׈") ?5ī?&[f7jWbc avݶ 4 =zVዯݤEMpPKj}핕EĖa*Q69?|U=xNy[-T|ҫ z[mK]m=1羱r& #[OZ$6GRtxz]Esmo.qG[$C 7RּMl-\tZDn*1 {*}3Y]Bm3S"O7 #<̍3AFG %X *}(SnT`WJجSTT֙mmi.eA I03OҼyx1H'6Xo"<Тd%@w"8b0IuEqa]46Βϸ~Ag c7z.{d_Sj&hjџˊ <@pp0 vtoxM.$~7ı,m=c􀱨dž 7卨u;KpOޑYgO"/Jiwˤwq )'mt ]+ѭ740{׍c[|/H]Drmeu}:DIX] `1i^?~W-iwi{nOm2J:7  +gxZΩii5ɍvF 9$׬U5bSSU-m>k˧C'$($R@PnDz>l]^mFPq'Um1m/Z3-3΁w~6 cY ahz7%ͬ i ?+)pA tNJu7NEh'Ȱxr-# H5ZEӥ..!̬C4Q4 dwf4v 6enN}T{'$!|*yTCLOEsJ(QjZIl]8jb3:ӓS4B -nVbb,PTaIUjkkaꚅKdFYyjvmhֲ\js(gIecjLzȗWeMGMS.ƶO6}[G,z14_c? EQ۱v?𿑠 ՛wO%[ꭹveaʟqRmF_&DDw(I]J[=vY_c? EQ۱v?𿑠 "2:V*FA\$ל#6vl$®'9__c? LvqGݏ/h۱^mF_wxKeFպQ$߂t {$kuu3;hmF_*ݏ/h۱^چkYkyw6 ߚ~"=rY'[M8ّܫ()91YuxAm4ԍE7J{O/(|_j>Jγq;2M6Kt,,'$nvlob'ljs>i?D⟶[47%ʩ+IL$Taf)k?cA^0F|2.6Kn4MUhH ar5ѧ]u!Ki 4pɥj=Ņ®dJIPt xE}3^-B#-;g  x8LG?> ,ntzݥm4 (#`ī)!0O F\/G߅&5g4[VŲjʐs SkiIHҼ+Q t;wwi {_lB427sNxb]駙Zs[}|U))e m:]jaT[dOIp061 >$3i-Rqw%'n$*2ӑ\7x>yohiwu{W212L9'8{?x;T!%߅<.(پrGdͻdi_OK7|ז/oo_GP.*&id^OF<;ƭǚm\~ ml vmR$Y5gآ:z3+(=#x2o) *Cwh"&PIUc#oc#8?g?k?%5?*6:}4rAu:.<ŷm!~D[qW}ߕBI_#WWt?A=CZ%hI5fpLk$v2x&1|k>}YxXVO% fc4F6R+鯇n$xO-=jAOxŨ,Jg Yel2˭WbWgnUijOVα7gZXAދEYHq)|}f5KK}E E!d0* z_-վ'>)-7Qib+F.#d2R8O#4_uOxڡ[563 .UpzĄ3Mhh|F.i.$Ӿ8], ru x_&xz>:<(tj.XѼ:m$VK]>q/$fڲ6Fs>(CWk OCkۮIw=bܰF F^y`z+~ݾw=-O<[ ^|IhZ-Sqj0D ,@W7'IİiRΣ'$UV%+c\}/U'qjӵŨkwk- b?:/7aX\t+ \$ Ljm4"դ1iWd aa.6W~I*H'WX<{&gXY1H6w;v “p>:.oݒ]j1Fn!rJnQ( Вy ||c^ RgͤɦwMp4|XF<'WZ}2o j!f}6v۴2ݮ#{0h+9eoͧ_12OcRҮaj:l3\^M{=Q*4UdBIn _z-rF*[%~]?+6hR<W#_Mτ'i(WOOEV̸; ~z‘ZѰo_twRp^YG+r kkk^)/L熯{fu)KtNʃXRmmko^4QEQ@Q@Q@Q@Q@Q@Q@skWU[?D?QEyߴ axluSt,-XҌO(bRۊ>Q(C?B6EC+ # J6:6:meKqEU­ r3F?:@-(Pփ H!K)#Zğ]Ac9Mg:|?\$+G"?1Ezzğ]7}wzO9QC炼=m42MBXOs}wzO9G$̟r?]S"hwOto?$̟rIߙ?l» :7E Ÿ,@K2?'算~d㕱 Ÿ,G+ г_McO]/(]_V+ г_M)B΍4 >=t'}wzO9[)B΍4» :7E?$̟rIߙ?l» :7E Ÿ,@K2?'算~d㕱 Ÿ,G+ г_McO]/+>kbWƫd%_ kwOto߅?Yѿ/&1'算~dO]/+c߅?Yѿ/&W~gF]_Q >=t'W~gF?]S"h}wzO9G$̟r?]S"hwOto Iߙ?K2wOto߅?Yѿ/&1'算~dO]/+c߅?Yѿ/&W~gF]_Q >=t'W~gF?]S"h}wzO9G$̟r?]S"hwOto Iߙ?K2wOto߅?Yѿ/&1'算~dO]/+c߅?Yѿ/&W~gF]_Q >=t'W~gF?]S"h}wzO9G$̟r?]S"hwOtoc^u{Fc32:պo| Ҡ+vx%daЂ Zu&sK qI|ǚ߃b< j>9ltVm&D\}g4je󂛸(w|_ ZZMI|!CmRK+(ZEkW|'1:KNP1i"MΥ(2x#PI xjC'{CgK 7ZyJ9:w[_W~[꺗.|w5ܚNb+%v!⨸}yBCQ _Eh;n"*O?].cEq^лuT}yBCQp;+ۯn"*\w'ן.D?U^лuT\Ɗ>v!ۯv4W _G'ן.D?UO?]?>v!⨸}yBCQ _Eh;n"*O?].cEq^лuT}yBCQp;+ۯn"*\w'ן.D?U^лuT\Ɗ>v!ۯv4W _G'ן.D?UO?]?>v!⨸}yBCQ _EDMs^K驡\@QqUsX./#|L*RԿ[U(Կ[U(Կ[U(Կ[S4- !Zޓi4i 2ʩ+ }rp:և!*G$NR5/VyZ+ʻGdI/%GdI/%`)yZ+#[^Ej_k(5/V㔞$մ 2eU) †d= @P5/VyZ+ʿEP5/VyZ+ʿEP5/VyZ+ʿEP5/VyZ+ʷrH23M𧊵:?>$g,n3z?#R_o9GxM]xM]Կ[UBKm4wk$H%o*)U2gZ7Wi?v*sk[x_[{Xҥ, {Y4#t %ľ >si:U֡bw1_,A5ڿ".ddܻA9f\ZSl--r̖I$ so;k2jַ3wrM(͂C#H[F_9 σHr֗Qޥumtq΢hhJgnTfoț? wƿ9cBÃWmR~ԗw@\erʾOx~!Uӭ9X̑f4>tIT6Gu&dHwX]/<9e zZgl9vXP IM^Ys7<={|<ׄ>𯇾$?4w^o<+&ajZ4s?7vωy7HѴ-Լ;HjG-L9$Vς|;w[}CG!Lm^W2G<5QvMh'(g^'&WPt ?G2y4 ڴ3*lb9%0+~)Z"˨-͜ͅS]\$|0ʨ0.H\z/Gm]RR-Hn4lC$HѶCdU=WhaZZ=iv"QK&2 #rݯ_ѯ-_~|SCk OxƺVAmY0*|-^eI72 SZ'MNڧ%Тmm-m#:VFo,[ \µ_I)$tK\=\I%Hdw&!AZ@5{m1A.)oep kr%\ɒ RF?KO߅~$>6+xKQE8;#&Tۂ2k+fτ Ɨe.=J+:ƥxGUg&6b%w>k?0T^%__W_B_F~a_ E(6h?P*6bʓF~a-M0oh=("n^赭b~ zݭl"?ާv&Hv>k9^XsTsuv|K{ko3Ea!WX+(,8#<$iQXkH'{[-@2޹ou&f'A`0a|oZokqgP۝Tf]Eu +L'lʬeNO|g}cMŕ>#nc7UkHv [̌ wޟEMbKk55ԭ\<-w1F:V> xWWWzEʹvEX.~p0Ďep8⥧o.CO4֏[V>iIk>2Ym~Ѵ~qgn |s oG@.kK h[M'd%aĤGऱHu(lUqǩL+Ul0#֝⟆Z0?[-4߅{)yPH{Y a%Ř!Kwk&I?H/^)/xl~bI g8zi=zC.|9wi:՜)mnYQmT0@#Y?ʵ&|?Qh:6ziugrU2 ⣪v=]_)Ѽ#3|T𶩤xW7RɪYj1]kr."{ymZ=۔k3ß{ ɮ^R o<%H%O VdKs9Ѐd>keZFj&][RҮbmWWwjѣ XYX*c@p[$f߲'}TH/45ˋӬPChnp1U+WoYI6/n/JԇQ+ay4Q|Q,v /@&T|HX_XOxB?i xv{q imrᝮ aH̎ L^|%uR^~_ é x^Il5xF}WPQ .9vvI\ȕݿ6yGfmoQV-񬱿`7+ s\~.YxSқD<9Gv:PH)dB2ݹH ;Ѿ|?.Ht&B 3~B(ݘK3$ v9It uܒ9,L%rOiKi_V_ CLZwV4ӥ7C>9mv{S[oj7چ贽fO5R(xbUā7C#BaB7?HW/;~m:_$Q+tH*;|S-mgQ|[ۯ _zthB @+G$rp9 #la xFm5{;/뫋 pM5GF O'czomkM?%w?Η6J/+ů:o 5Ok?fGʊE! te 8$רӶϾ~}Nj6J/(m:_$WJ<[Ws|E6~uѧ/fEVUlyR>.q׎5xG6ދv@M7# ?? $[MkMj.ėv!X6ѕ؇{?1{+K /oG 2r9ZGW韴ׇIh>#_G,-5v&7+|Ռ8RT3K~-%2hRxQEK8%ĩ#IL x zqQ"EUT`:)#o6oEiֲԢD`@I˶"w F']u-7S5 Z;6qhbX,E`+cCۻ I<=[?;E-/@q)f۾6ڨڌA{Q,+kfKZ=_gHБ+QUvOxԯ D&h̤YP@*w#j?}otJ \GwX.Ό:]5xO?ⰱӼ]%,_/1$+wo9 wڤ>_3k![?oG'O`tjn:g{ϵ\_ ]+O_{St侱} s.TAV(pqSuAy8y@U 2 ƟU|9!$DtH hooxwWĚ'ieycmlʆYFytO~$5 -3Ǟq"Wz}sqn=Qgd|Tž*UY^֗_7=Ws|G#oD"> <4l|76lL^C-ŵӋWY'%@]/=wǏ|c{i1Zvg).lq.kȳ='Ws|G#oD",QK?%w?Η4Q`dxvH]:kӴۘakhq Լch&P42a7H4tku/r[hgJ]_rS'J߾+y&wpv=G9|Ʊ7Ke🇢"–-$Gr9| p3z~o@M֛V7ȖwȳDѸG[ 0zzW$PѰ' &:Nm_MV!HMQT`/1g147}+lyE}5 GI֞UhݡXഒ~ɕX6BvPs?<{?tSqbmOٚtE,TS︱o7NjNt*;:[K[]͜N'-ǺVaiMi7mZs<>㱉eP)=1Ok޾x6ዏ >#-%S*1 w vUwOUesimRA:0CFv0 uYō:l[y.$gbq€9MUi^&oRIepL3pZ)p* >0@VVOGS{we*ߑi{׶ھj֭_A 'y (!T' 9@M7Nj4WsV?Vjwm]V /2AEzp1Ge-cg 32rOX/z^T' جX zRxփ?%w2\p" ݰuۀOLW 7 "?4oER-kg}ZY-RO_q<ַ+>ŭ߬bk_7?ůrOZ k(C~|Z. / ȷפ_bE*,$Fde*흪XAm|Qc\QE(((+<{kvV:$nA uO47Ę)v[֗^:m| đ'9x~c62Nqz$4ms ݗ5֓Νvi]yu8Xl`HPR`qFߩ'CE^R_Hx.pA]:O$kد)m]-pYvxa'ˎowMqOly&<#eC}ß&'J:'}Zキuj*g}j>-Y"NQԵ Nay&70UUr2@Wa 3slֺ\%_yJE,'oISP=+ȼ'a%^}xWPCmo$1,.w|4]7÷ t8t6 X$~4[KW__Y51k~#ǩjv:dZڲ 6wmJR ϥ|;>0,KaEZ,壟HBT'})jzg1Ž-%ee=_z։oRo$~ ֟"2#- U@?`z{GDo>,_EM";{|w (đ|UܿomsZ+ItH1ivk{vOn NR[aMWlbxCHOo 唗R]ZK$]<BOȧ}I|M^xetRV;+_4n$7<h8 ]}|S\x:k iVMb6E"VV| d'ѯt LhRU@f6XJ_?@oWm~ؾ9KYT[ hy%`H8f'fKއOZEچYC uvFqD#_x[Ú_OgåM&)sskjʲ~U_eeka+ľL*1epؤdg evc]%u CŧA֛Ski_icfQdV\nmQE ܶh>錭%Fk10 :Bd_/Wu=D[7 hR"ej?{5vڑ+R/sƝbӦj3V߂n#QKf)?CZڑ+R/stռ9X@XW=2@uȦ~cjqGGjE<HP{ejx+Rl_mm3b/HQw [XajzfZ\Cm\A%^ݙ)F@8^~V_(ԋy\;CK95 d@q\կkf?ˊ?"W?G_(4LgZ^nOoٛjE<HPuk:V}s@M+IJ08?j_(ԋy\;W5zBYWlrr_!ҵ5okfjE<HPu{I:^IҝbN:lWew ?"W?@io]3T?& )9^ᤸQjE<HP_>sKE Zdu{[V0.%q yU;c >Qʧw ?"W?L o |;м%ZƝop5=bEk}ڪev؃sa 2ptOR/sjE<+ľ<]65 a ԰ cTj_(ԋy\;\'&$#ڑ+R/srw ?"W?@(ڑ+R/srw ?"W?@(ڑ+R/sr58Ǖqr (|mV *Dƚ\UMZr1He7Rm'gXvvg^'E<= :-=lqw#p&$@'F m,9<;]NеYגX]1YoơE|sr}_J9~"R A$whFȑv31+\?_~[LJx.Pã#dH(\.+x+R?_gqj> H1=-^RľJ:ڤ,`5EXc,`$gV^حem߱ m3g>%A(udW ,}E>ϮgXcyjJHEⷴO~14{M-)Z-J?%)*s:#zv{|p?KSyZ~aeu]^\X;Kpb2d/]Zcj^#ѼSk#GGr2%mc尥&SVv=:?q@CgKNo ƚY\X}TIr~FS HvG7u/iwsk0ͫ^.]>F+yr{jڿoEoGcG??Ouv_.|o [xYX㝤Z8r |®\ox&3d]-: CK? 0MWv3hS1]iсS8?u?!7)>'|8 OH/Wi"h1 $>qt]/Ah /o.<)g &P1eyugo.U Dtw[>GwZzW??OtcR7ƍQt Ej2\V^ גcS łg"~[5o4_ xOkfEϖsg\Z\q@C:jUVHMbՕnl[8[Qʔ,A9|/⧈ iHִ;o[0X|HӉc1ȔwokBW??Otc^E+ _:?*,cG??OuTQ`<:?q@CJ8?u?!SzUX5?:Ҩy??Otc^Eq@C:(kS8?u?!ץQE_:?*,cG??OuTQ`<:?q@CJ8?u?!SzUX*Փq^hm'Lϴu HlG\WjLao$M\<+6>ki=v?f3&]kNQ1aO=1^e˯x6^|Fm<9hV4kg D\%yl$&_Ȋmc X_ >@v7iI)pm-$_2'AGU?A_E]>ȵ)Y[ɯݍܬh"k=OKI PSү}/ЯWsB-A_EnJBE=A_E]>ȴ{"} >OW«_Uw? K"}/УDx*|%hWsB-{"} >OW«_Uw? K"}/УDx*|%hWsB-{"} >OW«_Uw? K"}/УDx*|%hWsB-{"} >OW«_Uw? K"}/УDx*|%hWsB-{"} >OW«_Uw? K"}/УDx*|%hWsB-{"} >OW«_Uw? K"|D$69|R7 L%(}s)0WĞ ^COƷ9U#Hc3vlQ1~%m*@^M#β02jbW"t&3@w}5uoSmߎ$Jͧ 4eFDe gh +oiqOڞ /쵩ʑ*%;rps۟ k";?%nu1[ۤ%yI {!|Ddg /=w^8ƛhl:X *ciN. [Wfc'q]t|&bzKcǫ_iF WI~9  H H S}^>Xx X&Yck3p`UԓSkZ WRk5%U|琖'7ğ5m{Vִxı46mWr.sA')+~%4~ľ3Ѽczh7acč c(W-wZ=>Q}S쬭ਤJƣqsmf8G{G_oxIN>kbYP` ǐx~E}4jv]"Z3Oj^2Oi o~OKC}xnmίa{ FF%d>jF8+Ӽ?gotoMzhQkyc7Iȸmѱ`W[.-6kINu/y*ۥArid< 7Z}a_W |wKMAa$r_ZIx7<4iO,`R|Ӵ5+oVv)A2α6w28YSz.{87:Wuo][Z[Q[Ȁ_EVQ O1A(>cC.[^xQ 01 M-$ck:Za;ק㿕Կ~Ԛ} Wux\to,s,틈LʅPڏVyZݖysgf/剤 JǿnNpqUohRkârٸY@U0+YC-Z5˭$%-?j0b0 ln9Ogo?᫝RUN#a?YPp3.+mj>M7дmPhθ#Cu n\_KV/Ė\@ɾBcwu1F]v;]ױK;WVmE|:o xR?׊Ij6 lYmm.;x"I8ؾt<#B5(,]N;{g3]=dITLKch/}kZuȳᴔjN7Ub#$>aל^71gĂ@iƛI4 R֗WG5׍-.PhV)Pzp8<+B֑15/wSֳZ_OZZ̰,HTa08" @<iWΏaKIYLQe w*%}nvgjTzL˧܋Y\9x*և*}?m5?ma :g§FͿG*}?m5TQ`<>B6~m?S#hZo OG6MzX%OЍߛ&T?k֨y/*}?m4§FͿ^ES#h>B6~m(K OG6Mo׭QE_T?hOЍߛ&j,§FͿG*}?m5TQ`<>B6~m?S#hZo OG6MzX%OЍߛ&toxSWoEtZ+WFnvy {ey/S֫¼ ZG ¿-iY#z/'#lu+ #NRIHʮQ?(+2xQ892PBqEZ?/&WkH_~ $H7NHzuԾwmtʳ؆}>l\#|!]|zݾ*X0!P^Rz r|ZLJ4? ?ƶW>'іٵ]c@^[]JƬ%YIl R^j .|M%xVId3(m[sydXU%ĀlmP1ױN #|$o~EV]n[U"iw#D@wtQZW~N%ҦKDs'` =yʊ. ?i߲ѺOb0 h$8Ž_6߫zjQ_֨%S~?M(Ey/ڟ}mWE+~6߫OSo .Q^K_jQp=j_?M(S~‹TWmWGڟ\ZOSo >6߫zy/S֫}mWKhmoaYɦ;3#H䒙$1i{7>!v:ck۴d[`ʼk#M^.N:d3 C5\m3XUH:{ψ? |9;ش0֏Zlg7pFClvo )c6󑅫~r-/T5>S*ot_j Q# {C5\m3XUFZX7gk?[k~ ouec ݎ-᳸L3Fw߸Y)v>^**%/l>&eh`EˏFn\w f{=S8dt+xGoԒ泚il?]t[vÝ>b_v:? w]SƗijZ4:z6{ske HH%OgFi63kIqk4]7FdU>V(Eprkɗk|!45Oo-beDPt̟3aτRu}VMNfFb# YS>^gȕ̒mfM;<׾8[5X/m&AoEO9NۖfS (lsZDxr]7xM>Gӯ; vdfٳfPpNK5WMƞ)AVe;7پB-$-#*T)hCxz ;wX^Ma&] Aq)6G UQM%dѯSP7nbBf;1Ck_4߉nz&ilMJTY#uG!XpFrxŽ>$_|Gͫ]O$[Yȷ.-yIHu8}5&tm^=c\qHUVvq/e6_FOUz.g-;;٣oA3>nFʱ #+yeo.zueo5\ƨ.C>ƫˏkQB5cǨk캧 캧 _.??=G͏ 3ߜj3ߜj|Fl\#\6?z6˪ϫ~p>˪ϫ~pyqЍq|.C>ƨ.C>ƫˏkQB5cǨk캧 캧 _.??=G͏ 3ߜj3ߜj|Fl\#\6?z6˪ϫ~p>˪ϫ~pyqЍq|.C>ƨ.C>ƫˏkQB5cǨVJʹla0kR]#RYY4--X#D #)'+r?> OF=xF{tRf̦77p3־op2qҾi߉?/Zl4 kr)0{295'>1x5W/\xBdxDv4uҼGԵ//{IWm0Wl Mexg4iz{i'YI:IQxR4d6p(?BM>_̶- j7-yQ=6?>qiG֟>=㧁u+nDhOیF%$BBB!T0a o va_nb/Ǿ#maahR(I,^K;'M ".k TK-X|eeqx/+\5iiQ2vg$k=+zotxɞKK!b_26{'}Wǚ<=84PV%9s3ֿY?4M&}OZ {+2WnX i>>#jpecmz"pYpx84ޖ~_k["?LZux.fI,Scy[nCm '&z/u;Gwx:{CY%9.%3LAUbv7t6O"𶛦^YxCQӘiG6˦Ukok/{t| s!ӵLX-̒B899Ff OX.wN"uT8MNҧpzV_-7/-3g{˨iڬs>o IsW;!H|K&* o1耬c -|N 袊((((o?xzꑟ6a|Tj1 ~9+Wעѭ5P]=SQ+زv56!t~}A9_|BK3m-/ψl#kk zmh6ɨdI7]n=Z5VgGHOQ+3 ??G϶_֏_ր4ҳ>=Z>=ZyJ`h`h+¾NMiZE Kǜobxw^qkFѕ.yǒ2/_3­ySwpnBz<ៅl|;.HH.3I'֞6$wk.o]Q?ֺZ<w;a]=!/Km9YɝlqEgus{KG[\c|3־% h@Oζn/m7#FP_ Q V_k^u٤ ܼ\Kn'het FWƯcꗱivǣ]FyzS<_3s+?K+?K+_ iO5j7ѧn*ahWNr:^o:\ѝȡԎv<Ȃ {)V-1J?U/~6QKq6i]FI湎600rY'Ӿ|q?n51]iw0G-¡2bcs=E%EgusEgus_HxOk{ HdZ23JQ8w#1ʌI=hliOiwL52V+TV@`-?o">%hmƝb0\|S*9Qk\k:m @CVREd?+Ow |%ҬjzKnHG`r`nxu೧j]֟o45ʖ2p Q U}[&㫯 ]jki}m]G\IekqqȎ[CfO20dp> =wu,%Io7lё2U@ qi_oo_x=72?H.o"H.o"_τ/kW/h_&B&)Kl獯~^Ϣݾ6E\bM(DN憬w$V~_7G$V~_7]go jvDdh:Ν5oUm#)A&<+?K+?K/5߇4𿅼7jleaHK[<{-@cu^|)[x[Z[{{16vim$V,x<ЕH.o"f;կ.o$Wʗ86sհj>/7zԧJ_GXC<ഥ6*:n'7k(Ե -6U//m-׵KFN{fgn/x}}ty%}'_w,Ig-ݥׅYJg#"G*~ao|3VtwOk5ΚϸtR끝ϭ;o y/_!x}}uuxC_i:5橬\ U%!ebYK$'x$H!rx}}u/C|>'ծuJj6mZilɞ[tPW-#sE?H|1X_ľ(u*]R=_Bѯ/D 8u! Ų~gIG_!qӾ&uq1iX*(}*{O?7'_ s|<@uɴg<Ɛ܈ e\M I+<Ͼ/3ĿC{:^>6mlgY<Ԭ Hۚ vG>gIG_!׵|Dxږ~X-a-q@HN+>~җ&/xStKo n& W72 JEw[r3ĿC/??kk~Qt{};Bk}4Xы`A0G MC~ؿ4;ms.viz%}dq0r29posgIG_!נ~ѾEjR_koe4fH0V)8a^^]r—,RE )H 4<%}'_gI_GQJ4񍮦olĝK3ڦYlXy4@׭ҮDzJUž+ k,^'2ɓ~]PoW7evklφt KIxz. wz!@-O#& |?kk"CI8tH6KQ^ᯏ _>"]Y-|W\HH r9_S f]5.Ӫ]fQegI!TzTc9>hgW~{咶Sc8oG&mtN qgd,[|Zo/·?YK/)mMJ(BIgUq'4ڢH;Xkt;76v헲\HnUm;)_e(]]~O&ٯV{csݜQܶR_KqI <7y_~Ǿ#bk(ԭ6~Sm,R2(sʝí},6L*"g?ⷆ+4[\z'x%hVF<]Vo/$.{ݟ)~Nj~7>7XSZhqiEֽ{sh3͒+t>H;@7Y6xZmgЄ~ Gh^Lc@ 8v6h\s?r:~*hv4YgvOܮ`KvڿĭY?i/ x?fiwZuv5Vqpn"T]!՜ӗS&|Jnt/ H]%ȸX4~f2޼Ǐ?ֲkZ-lJà2+B,7*JDio<'=Di] Z Hn/4p%o/n\G8~о%|M~Դ Z.`Z[%W[ov8h׏;H}m;SN;|ڷ~͞"uCN^_F? I^ţ_]^^4R4QU+If݅:gφھCk*[إvmYhe1w 4OMho~࣢mލ{3[E%,r]^ +mS u߄׏<5cu5GkU J#v)-ɯRL>#hmc ongk1&#?'SG5 *M|AmOhQ9iS ̱=q`dr+D_b8]_y <ogIn-OW&p(X -Ć &jJcWk ic|^.O xoKULjlu@J-mDx FHU@S+?lx--nl4K)o-B8ԳȤ>t_~2¶ v$- bImnL`Ky$!Y>3iIno/=͞ xfqw?b:lٷrΩ;[Ic~*h%n{y!]Cq+,#[~mvoόxG~34~R O]Bm B>U1˂Km,5u;Z졥ƫSmkWks#ݘ'Tp5Wmn_GE>6oď (Vėv:d:'iXA*4Į 2b 9#k>$|nZ-Zm ۹᲋+s,1/DwͅW#slƟ |2n/!lhkQR&rWЂۙiȊ*=?[:,?ɵsp>$A,l9ɀ8x|qYO#ZxH[Ufcj%0qg dKWֿi]Ğ xW(^Ώ[u1 OFXͅ; OJ4O x cA4"m m3nbhS,θ1m.[5?V;(5[yFNԼ- :0| X.W 2;|mi𾽨#4:YPF[ nkw{aYKqobƥ vE'ac ~_ _?[.çyw--yJ"BAU3|k~uVr]F[nt09$n !דPwm>+[<9)t#r߸2];]ycƥgg 7;39=AҲhoş紷_Po{d ʬF\8ݜVfFkVέhrGfח3i:x xz j[1vGjjI3m#+ F3iGO]K}n.n-ImAA 2w(|E>xmmD<Œ2X9nߌ;=%7ƟOx,fˋ ٌE,96;F/o3tZ.6XЕ[HK=w2mP_FRN3/uiow>sI#ǿ*|AI;xX X}_ɉ|#78`*,mhphe˟3 I G56J\ZUX )5{]i- / /1>7HAnS!?0"?*yqp,enInT%S9|3|VV}g[AVhX V`YH 2:5gV.2w]]w|(Pkw #C񽶞uX.!p0@UY ¾zIuwZ/udԬH-! [\,#"2G$GQ"r@ג?b"]:en]"itu;B(K;IAv> 4[~ eզZ+]>#]}iUSdgq;@&5kvW-;2oVOQlS>`KWck䴥v?D/3E[htH&Ѯ&eU~V ʜ܇ŏz5Xj~4 _ZүMiiund]?k@nj( Kbu_Oe:Vu UTj X`0An@qvإ_։Iө]|u<qxrl5;AVoDxe"2ddF9ړৈgHd4w[6M>_a 1wc' Aӈ\>Vv?Rgx'q^v:))4H6qwc#j+{[|0!6tEta* Aw {j?x?IѴF{}girXH#^a82 sz4w[$Mt%$|>?_-cs|WҞ/oͪN739lsœpxRTo CYX1< g/|ic.anw{u}5R|Ԍ ̂N#~ -t 3eLvlpBZY0wlś*iɷt*?s}t zuoy f20P0R3q\ًľ.Y%k"ӟVk<~":FG2[Nk1nzWuY|5ѭᰊO0gfTLS˓?,AJMk/ w~]-.Kx£\cLC-N⢏4>4;VJ69YE n#?̤9N/W]g|_<~6ͮiwra2SyPFq"AMO4`p1>,>(z޵x&X]M^{x H>D\ eGn+Կj5^Ѽ_m&-,P~s4,\0 F sO^$7.>SﯫV;n_y߂~ÍaI<'%+# [vG˷?1x yfծA:LWzlY]^K"e$X'¼MN5Ohfi5jG|"FUPf,0<4ܛ[XzY}:#ZK?56Kf#4BeED ˓|MW&6iw,fEMAs?~KN <'(x3im{$ieG0P:JAxǷ_ Zz>+Asx.$sPy{OW7+N_xS$מ/X551|e ́cwˌL|k5UoآY6s '|Uh$6rJ0`kzh&nz9]QiS[D"x3#UU"O3%gK]_g~uhw{=B]KΔn~é]^6 Z)ox72]cO3\ FN$ Vn}>meA?vŶc{ugȭ9IR'3~Cnݎ6|~x?]xI"AB_ '4q$'E>Z~]菚ً O-dOڔL,/kt.Hg"QW)p{ׁ#j,Ҥ|Ak^AIjm"33eQ'ھm>nM[loOC4-`> fRnW_|7׃$lpxBml,M%}* g(oί'McRm. ou+ GY0x[G$^eShڗ٢;0 V]v/R)!RX>&}/UK yHUb9rĝ^&`1)`$6YabxʿS'77^1S&y[<ÿuAXxf[\i dIYޱ4Ϸ q[ tį"dX-cr6`3nq^E Ee-?}ò{_g~ʿ i~; އs>%tkG6ېy7,:] -> jWS]2K$"D$7C(psͼcV־%h~3|XlmQ_#w01۴:ǃ8[/5^# c;gͧ<3$-2k2IU&0VC r8J]?u{]ZΖ,%>ǐlztj4KWEu\&oǚ=_M,4$.olI$]Z\I1=^#fIx? %j.m|YMSV4Pqz[r2uzaf6Rkߴt8u;oU4mjOI">P.0ps@3GGڼG@5qtM?V:Y]#n$SO᷂̿t 3NscUTg drk 5uϵZ_XIOad99RwxK7c_| 冩gDsJkǺ+~`~&׾.Z{ymKK,.}lea( C8{'2X੿>$'X7qX>x6σQu-OP~g2Jぜ d/6 _#+PXA+`q[#]"0%v*rʳ8OxvI\\YMg%ZMC#ǃzWP;|AC&0me$x /t?xK7c<8)um4.K4o Cksv Gfec![qo&յXViI} Q0&(S޺Bx%=qu^|!gQGWݥVؾne8kdeSCx{ujSj6槩jDHb+d >I'fc?,T~jםSEO]ķ7>!մKm:46Y?PͻI8]1}c>S+kfu12>ZW>ُ]OOX੿>k2 zp2Ccgw[M;+hhxwRcx7V>4|G sKӟT(u{9&!fCVpA;W+$$RZ^";{{c/0"UQ@af?-ֵֹ? HB[A*C`B 362 266 0 C     C   E" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>މ|\i~\?G?Xj^X 7"7"GڽloDEloDEcޏ{`6>މ|>މ|Wjl}}zk^^4F*sqm qD/ )Mm|{? F@L+ݼe>sCgZ^-Rm>Lcew.Ug CӐGY=֫xHg|=q\ZVJrcgLG.a}y?G?^7E_^Cwj_GjPK4Y\yA8ᶮIY?í[J~ڑ%ߎ}CLӦKw[*nGIkMYogh®g,~E*@=j|i^\\R>w[}9Z6yk7կG}}ys$rW?*/{VjmYٛloDEloDEcޏ{Ӱ7"`چ_7LR2CIKnb#\1c:\T3O eđhp mJя,F#?QNMky jF-!(BQkx!Ķ:|ԒYfVG&Dq(EEAs&k2- -J+ >4߿ii[dqEvU$IMiyٍ:oJd4AT.]WW`8gR؏L,n&8:Si.׾I\4wA[SB% (b|o7S[Cܢ~ GwguÍ@Ϗ'NXv>)$:nh.Y"^gpNTIkcrHea7(I]OTф.4}o;c?o( _h>}o;c?o(Iwe_kuy%};"MѡݿUTXКυ~Su-PӧP+v&d1ٕF@.Ͼz񏭿 ?1 ú>c?o(}S7>8xs:mp&Wu$DN inMě -X`  ?1 ú>c?o({Jkˋ<0P(d¢AwGO x-O[\G~ú>c?o(t|4NJ2S (a+vp,30{޾t|4NJ2QiAe.~j 捧E&8M⢶YX +yns3_O[? ?1 YϹX}o8"5W Nӓ]gPkM$P,:}ߟO[? ?1 Z1N}>&kЗ,>*q-+o9]lOJMa9wbYO;h_x6!Up2'#i]/u!7>i:\hկGLu2LOki;g< Cj>5qΟi}}bҝ~3m($R&RI>R 4RM_Lʥ_.e/8ݵKc=X63_}jE0i 7va &j]cZ7<i>\]y-t`nvȚQهF1w&?\=y:2 ]Q3\ÝRÞGn?X[j>ձHųk TȄeD*Tj(]?6վ?V3G6 +_Yg8l\o1Gz|g[\ܾnU+fR~ZJM]5wo>3 [Zucھd6 )9*qvvVgsѼ? A&6p} w2_MV)a$$(mtzVT4Z}o+ߑ_lSG9z8Ϫ&,G_0/~&ڶ^%o|--VQ}Zv~Xy0muE;/l|Q?|qMZB1gjlYVx+H) =sLl/5kbT$Jcpʊ4,$%Xw_*\nQE"ڕԴC#B' ɸFF{¤߀WWcg-oAk;9Mz5O~kҨ3h.04J[-)[ƒ4 # H㪰$~Z~x7eZm-. @R$dy_mյ;O4V[E}-Zn00]yb8" L񝎻uk6mj/o$v>9 #1Sd|m"f`յY(~5bCk cHd> :^wzݽ61$FP8VJqv~xVB_&M_qF b.F0ۆr`E]$u-U0c#~Wj+Wh_gKKMJ*yGEmReƬͬeTd#t/)܈5jvR`kQE(((((((=3ݾslҭn@Kp1!rFi  ^CG-ON𞹧x xOq-onH3F*\ `⾎O35mEO]I{in L4[\|RGbyM/*v/"2#-cr.mf18'--?௘v<_k? ;B^VKas٠E$p_v ֞SfKW^&n4="y ,!?#+T)rpk>hzk <5׬GsU\A<y5[M36mKzdIckq(I$p1dde溿' //|[=֯GŲRM=s,ka2sQU/xDz:M4t4S2o%c)Pr~A]A],b<9kƧ$1D!~U\m+G_6>{awޯ@,QqTvіt!K 䶌In*ďST|m"f`I:Vgdn6F詖] TX' C&kmCkc񶑬|A/57Lz6<` @W$dd:~?e jάo$ۺfcuq ")8nBH I|! deq߽y{~^ ? <1D`3"ͤj6U2G^*H9mc7q~%xJ[>oUmf ŵݬv-H&[Je`c;Z>rꗓjWVs]ir\I JI,YP{j [oKWҧfWP-tۮ8<UHSGhB[ż =XOrO$H_ЖvM<"/?mVwWEV HO^ß KR7 ( ( ( ( ( ( NO 1ҟLtMcM&e`JA؀hOpwz]EDɅ!ri+eo?"Cc^5&O9T^wNvu%]诘|q⦁FXF(fM*$CY/c4x gk"5~6oZ^43—ڃvsiy0Qexo4R"g$e7eeu7Z+|[g.r9Uh%&HYێ3۠i|Ky[cg :<S v"ɑ]?TV_nMu{)i6H{W͚⯉7W7k.-gO +B=#E/?IkWQEyt(f6zG U#vo'#5yq(U\Ƨ^syGEmR`oQQOsnTߧ @ZiA'JY?ZiA'JY?ZiA'JY?ZiA'JY?ZiA'JY?ZiA'JY?ZiA'JY?s|#%[JqB܀Bѭ!GKk˅Y&k"s=0%%I8JY?Pz~(W<_/=u ^ްgxXtks 8Tvi"եqڵŸh(7_(=dOiA'g+ko֖%ˢZ\K4Ie*Gr$VUPw8^4> y kc4ZmY`fVcGEa@GNMwPz~(҃ONqY%IɢIۨ\1Ӥ"AH%T۳> ׿EhiA'6w{yMtؼsK- ĚN!u!>/ï>|1gJ[gd呜2N:tmߖ >k-\e>/*X>G~k-}~[(5 y>?ޮC=mR֗m§h1hJ4-W>'Ե =#@֍OoG)dP1 @tW7}mgI4)F!A'95KD4iVy p$$d Q@Q@Q@|Wy^glnesąARۜ  k2k]5eе4[Y^8/^u4H;Y'!kpzXZ+lijPXyzpCmneX0".]6#;:3o. M,[OEgu4!̱Cp#F+8(Ѩ7|a]3^.hW#u٬ePvGRp9sߴ'wkX!t%S$g # S=M62AQ7UV9a[RQ4R.<"xD-zYC #Q? "]SH犯Jiv3C 3:n2x hO;.@5͜ccnOs,@;y 6xm _oM Y/ߑ4ykDv_LwzZ3٤z|%34Cq^/E?!%-mشQ\#E*<{SJlw>d&K3ߑ4u:k{nKAs=Հ#57?/WxD_6E-&3z>V s3ƺj_]ϫ__&|>,1YXß26 DtR{^**Dyw \A{-'QDŽ5\Hز@x ǽr$_t|65%|u{Y U)+8 *oů~S,S e )'0ەeT62I}MᦕBLs;j7ʍs$gbʪ9!@8iht(IEȖdp@$g V%-[Qek6x-uĺK+P99 8PAOxRi~ҠѦ5ylD gL[&V!;N2\ʊѱ0ɱQd`@\T/m]s/63monoZՒ.|gMľ(ּ?{[}ETU#l`|z?D&,'nLKtيF6 sZHt .[H, Q Zv[i;;XV%f=XdԧdM^Wv(PQEQEjGUꋨkMܐ]ʐh#m6 Fek,PܓP A{~ۜHxt!]ϕg#j t ]xb4_ 72nysAf=wox-Z=8-DžI7R] mE?0tkڨ_TR?S|t{om m&iI6ʎȅኩ#Tm"f`յY~)?#:ϲ}o;83A[cmS+@I+H5~9¿> Y~|8y'M>&X=͸7eע[\yv'ٰmc>?Q?~ ?h_^~2//x,u ,$2NC$.Z/Cpy>4|9sy=1kdi%uRIU.Xi(]i(]4쬶?W?/WxD_6E-:kyOvRGb~(o0үt[ (o ^I .C^Zi3<_H|Y@]5Xx4W$Viצ9;"$1Oyș>Ey%?iYh1Vmt݅{'kfPX) }o&kmRw[cK:OUG5>bdX OG%.x[*]kzwRGƛؓU:$G#>x}ָ_ f=^ 57ѭE3A;N <'g_@}-X(GsyGEmR׫Ǵ^Q|9`oT7ozlPd[U ]d[Td[Tvo?IQo?IP*E'GE'@h_DUDU]d[Td[Tvo?IQo?IP*E'GE'@h_DUDU]d[Td[Tvo?IQo?IPگecsi0&'?-g??-g?Ai|E* SUtdz & ڟ zڟ z& & 캗jzRiiZu-HcUT6޳LUX-؛?3?&%_Zt/pMyZ@ɲI8=??*3iK`k6RF\ɻ98-|Y4?^*6zEVʮ,9es'5k#R |Tm}.@+5YhPAm`c4uK~aўϧFjӢ.؃!aSbB[nU_+xO2wz5;PF|U0I) άdOuZh:Gu}'z5ͬ6WwT9#WQ}7à } _|y+/ ּOGfw+_e-NI4]22^qPyTʐIk]?̽) ((`o-|;|ANɯ4x!DJ$ ^=6| L\i]ip֗vwn-P G\r A WCii~%"#fC"n"3%F'h=oTխOrV>%՞xiG34rk#n:wvZ=<+gv'q~'|Ol5]'M`/}NG۱`.YGM|m7IYtM/JsSPo&MĕPlMb|6<7MS Z5'Α=Seّ到PXdUK$fnOO3+?i:~yMKQ'kuCk>RJD6 fG\X}79!7Xѧ]6IxT3`B7~_-m3%}[WYt[q{g y?àQԞ_ 5":5 ,e&Is A':, C+j*FAn/Zki4bͭm(H7V/odѝ*С&U CAd?MuP/ (h?,SEl}G_-@XOFxUyS5?"LTkGýIs|/aT~=HVhKI9Mt p|1 j3Q{_EHl<8v+SA{,llവ`(AAP(5P*Ͻo"3Q{_E]y(5P*Ͻo"3Q{_E]y(5P*Ͻo"3Q{_E]y(5P*Ͻo"3Q{_E]y(5P*Ͻo"3Q{_E]y(5P~o'Msx~ypCf>zFņ> +YG:]z|bmZ%<2rZm vWfշ":]HhGbj#`|IπnlG (D~K.CCɾ'|8Q= oazq |Ɗ2X n0 &jDOE%Q@RLǏuxC4͗ҪLDwl *ymo_4?iG@6>/n<^nM5Wov>_'c>x4(/uSL$+td)!s&1XT>|pt_\մ3D_4XY$Kydhݞrc|۹9f\';ZG;UZk[YgEpJT$g ~6zTɠ:1u꾡4M!oE{OLqڝOcNxh#MoPė?g 7 %v-4_xfď*U" ÂJ3537Y{tiXI$M4+~7!f{3ş2h$ş2h$(<_x8$юN?%亥N]kzN:wi3 bI&2=p*ȋ襤ޯ!_v amαݼ@FG2OzSѬ5& weorá ~b>gO)lZ}֟L%K{tGP壨FH$fs*m4溲u)nSg4Jp8 6ҬiȽ PGZTn+h,]yhV廙`A8d;$%k$cc5aZxVEdS)beeKw[oUs>A褴z[/x"3xvO [-څf[K\B2I!{1Ut%/n5 rd y04l.-cYmmuA 28W%JVw^_|4i/ Ίvmޑ 22r[>D2:se|awKoVS4wZա$`2w!6Ym?HQDG˷|oYjK#D:@rg 5~x;JͦcY۳H]w)*pA8&*o(K!ETQEQEp_5?˳K4rw.x/fД]2ͅi廅wg+ wr=J}Q^Wz.5tI\l)f!I |+C֒;yk0lQU&iy:?[VPOu9?`mRYQYN弞8SV׶W^j.edSɱJg!I,> ƾ ӼM8)/-)-mbXVxA;F B_Q@Lc{W;!mmbE!\RH<z\?Nu=;KjWQ_\Eu74DB4w4@Ok-G]ixҼ]QnO8F+μ⟅~>kɴ`TMYI$$o?H?KKVIou?/WxD_6E-uHJr~xàqlޢ*@((((((((B@;ѼykI.e+w\XՐEn7?JGƽMe_옮n^ 4,IsМ93Tk%1>+B-KS徜[Z%Dq#{ 2x5=Υig=P=9$ ґ('$+q/ŚL:j:sKj/6|R0YHېK!}gT=#Vvwzm'/ոv :eBd1k[]̖e]? x,6! b+&kmHl z]g\4Fms}_G0,*sXxxQQ Bu$lIH [|*o|cmzqk"xJ61@NI7awS͡]ŦmKZ%0K#fI?Pi?ʼ?"s)jX(Q@Q@Q@Q@Q@Q@Q@Q@@ 2c\> [X[;MȋB^v wP8xe5)+GVX#pw]t |n_;ڧa|`p<Ͻ騦kdx.<%4c:(bc`E!P!iV---XK=qɴYa![8$VM> i-kivL%IAJ 8tTS/?Edivl4c zWbhnp5n>!hKvIRϸu" -C24Rǧۣa)Սp P6?:@OZR?/֟GZ߅ Т3i~*+'D9i%GԚծ{,Q' qip[W?Ə_=hN|E6 4 5.;F1 Wi8n4&7xl0H!*A@ {KW?Ə_>}(Qɣy;Uۭ+D繴9i%GԚv66i.X]ٽDcb5'3Sw43޿Θھ C4j'tgÿxgE֮%W&b2)qaX ?A <|?hO?\@}^;ki}s+;:S4;?} 9PHC~ٖiW,-5&g&!%:md='jg'_\+6$l`%eTGM|i{ Oّv=3s?_?|?kM4 ܶ2G,rqaUԩ|<׫  )j't?]'ڏj>opYX Č=}ׄl'x.fmN9^%aÓxFi 8~.M7_ tkYqKgz*I>W?Ə_ox^DMKNݭe, dJAepZ>})oW?Ə_>}(Q= n%XHI'k/._x䱻,VKmCx섾Եg7p\dIb2G@KHEҲ/}#_|iú.yeZ꺬o&;1i(TgL xW_5kĚf5Օ6{A;"$P )klgKNt?e^YS:GXԭ`I &C+m2;/gGV[eRiRڥy5"hw2gw|vDŽ=>Yu}C_` M/mqT=9Y^sPot6:i6҄/E€>Z^ j/./f 6`F]22q/%F}Ix~–,O0=[W$䟭t"MXz a[Ee#6a@SӤcEGfXi7O 62xMq]rկg-V:dA!qɧv7 \utsOݹ3l (>~lmS^|b?,׹?(~[r:m3^e._/oqQ#M먃Wx.fkeqG\iV$1T\ ת*)yw4˓2:T" ܧEr:t^eA{}6yQnVD il>{2,tMޮjf2K#Be;gD #^akZi\j]NchGbAJA?Lfv v:? Y9ӵ]&Z>keޛwKbKhm&)X+xLCr|;o藺Oq= ̟G9ft#H!ҏJu[M?_]? _;oc2 'Ȓgt 9em3^10ジgZi:*p?V@EzhU0$g@MI[BQRPQEQEQEQEQEQEQEQEQEQEQEQEQEQEfotoxx-18.01.1/images/trim-rotate2.jpg0000644000175000017500000004552713222767271016177 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 143 504 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?±Gڽl>|?z>EaOl>|{z,(aOcޏ{`6>}G+3kڼ=:O1߇W ͅ>GJvr9>vVo.A }? _3϶DQӜa3WxKVj^&խ.`ƬBD |+mƝr!/|Aom{e5+,Bn!UPF60-+u#OQ OS$_PϢkziQTHot4WY7pemx^^񍿄u4 <χ`Ž#ޟ.<ўi >}^/ -:[Ѵ_5I5lj714jR05|^;:_ZB]ZVmI =ʰ'4v'?DV?ڽW;aOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?H@?zbu}2{Ym,Śj-@ϟ*QfQw]׍ }~/Am+Web;A/NqW|QGխhnq$nsܻ<~&US&%O-Rر9w=ɶ{+3DSbٝG5{.4'K$s$on̷gү6W7k)4A?nYȮ'.{xCz1AaX(RvQisGO~øøϟZ>}kEE.~|_?0-?0-as'֏Zq<?m(q<?m( ?i>}7; >)o1G; >)o1E\I'ֿA?aO[??aO[,Oi> yC"QyC"Qf?>~}hIow|Sbw|Sb0GO~øøYϟZ>}kEE.~|_?0-?0-as'ֵ)o1G; >)o1NK[>"ϊ$Zx5A% r&g\T^3_RZ7uO5)o[Co ˆ'; >)o1G; >)o1KϟZ>}kEEf?>~}hIow|Sbw|Sb0GO~øøYϟZ>}kEE.~|_?0-?0-as'֏Zq<?m(q<?m( ?i>}7; >)o1G; >)o1E\Ii!:euAC_?0-?0-as> ]$[g$)6\j_h+5Q{I?aO[??aO[,vK|6R5?v~Bu >micy*+w|Sbw|Sb0|euy'X[9im7)2\ >*6AajlU.@IҾq<?m(q<?m+ʫakVn> tU^pvz'-ٟ=s(+l2*A0U7 698 IAs w<ۣ)dd:83x!P<-wI'u53*JYSHբ'p oz>VX%InS.n縕 ZIX*'|/kzn'dž\NѴrdSdo8~;|%񝷉\hǂ/$K$R ܲK,)3v [U(  _N}r_*6(dqiqNnty[T1~c=i zk+rhGjƗ%F; |Guq.ǖ>Ց1yyn.m+Ȓ0F`N&oҾ֧Q85?"ҡUo-n{;Ĺ+%)P!L p8|1 C:6}w4t 2r\7 {um]Uڿw$yC_sGƏ>+XG#mcOֱKMn` /(?݌+K5g(iyC~lw4TسiyC~l/?p͟,xC:ݩ/lEѕ({: 2LH'd\,뮻^sYբվ{B.խ;u[z@"-P1-쏨>MiM?XaQH@& mQYҊG%-PXM# $ g?Z^ߛ?1]Xg g?Z^ߛ?1]X?|CiK7v:{#'V$2tGyӦ+vē g> Gm?wKb|2Qd:GVMKT;y84xec$c1(a^E%׷m6::9Ǚ:Y"f LtZJw'kQc F;N8"[GO]9i'4;#EzEx[`2])]# Xdm)oᨼw xGQݟ[5}$sde$U] 1GA-35+ g?Z^ߛ?1]6(Z^ߛ?1G+K5g+|EiSk7v^2\IѕsQqKC}V5q.=fOswӿ?:i+_}SRI?-bq*Ō r=r-Z]_x\&'@oj1\p؇2im2ݮ/HF)n.V/|Iz]"oWJeKF?ga#k|X; Q|ьIzI/G{X|iKVL{?柰^GMYZv;ËKD,*Dm1|WЕVVOOW%;ɵ;7+K5g( .#~lw4RGhCQGY \I 1ęQs<tEwBY#I3@ G3k]xo;þ;mH)DmN x<)Hr;J]YrzK E  g?Z^ߛ?1]6(Z^ߛ?1@iw|ahxΊ/I:ݒ:\CQwFT V8k?W3/4ֿ ~"</<);–@O寥kE8Tcr像jJIɤ{+K5g*NOiJvXRC.DA؊W!twǶo>(>OV)eY]V7  Woj%ABd/x1!YT\!6@+֪KOw3RRW]Ix KHsZ2N3AV?Z^ߛ?1\GE'|1f]w2;1f,[I'ܚ p䓏`Z^ߛ?1@iw|ahQ>k1LepA!@(xoA_W?=R( xoMfnj.#B#\tYx}aF?n7QIiTAry% &fݓ95@'> Z{+DbHQ *Xu&W6ê=W-l3@vcj )ȢX"HaB'< }PLhzgZ &. gDZ_xgz-k^յ$myuhΉ[r*e@ĐIuoizqɪjVjHHFb {X_, Ӣ*04<=|#|I='>|Dmp0<^T?!mA=k|75aϭ=ս pcYE?U<%CNw- o9{u :/ !/t_CR߁s `o=X?dxKT :/ !⨸~F^}XYiɭ.n7^] s]_|E&toچgڎ/"nI]=@0m,-glsw.:bpqTdxKTGZ_\x}N{kUՌ"L =yi~/;]N"բwf4Up9_<%CN?dxKT'm?m?.[~5/GiaG, Ӣ*7~] Ck\_, Ӣ*YE?U/(ti:ihysupɺ v 6G#3Ҧ߆~L֗V5߄[-aY5xOs*TҶn|a^ϭYȸ5\uU?dxKTxWϩxVkkOPeG(t_iiZtZ^\]Fɺ6]N@Ȯ U+X7.4KѨ~EF@l?+0NJs [Ӡϑq-k뵉cڪ :/ !⨸|'ǃ!,u=FQ3a;H'_?x~G-Qģ B$, Ӣ*YE?U;i o9{t7~]q<%CN?dxKT3o=X??m?.YE?U<%CN.g|S)5-CXӧWI2kF f?ߍ-bKgk$1H7aoyxcgmws90O-j' j #_40ewhͼ7sB^Xf@YK8"ּ>xRvWW5Vwؖ`mdxKT :/ !⪯rRIYgn@|c?ML(nNd" - 'on*{❎[k8<%i:4$qg*\(xX[slۂd x>$^3y'=1iXL1%} dd 9/miI?޿/t=G6AF)h|q;!ˮ֟_/#H-Ρjڄ}$"F\"\c,χ:X]wK ĚW"ѭ,_nmᾆy HJ|Y`īUM%Et?lAkՌ4~*ԭtq[- r)`"܊ϗ ⭆.t z^jA(9Ttd`{pAIxBPi_ .e.tOkzML2YNd23H@@\ ]<%^Ǔ\hz=S^7]eeSo 5̡B]U'@qտO7!=w=Gz 6AKExrIs=+#Om:iWoKZ Ŵ1imsȥ;^rW |O__5ˍgöo|-xipsm<8?I L]i$j{E'u/kJo#$V8qnW$WMž#}uo\& ;.x#yQv䟓=(xN/x~L ^xK4{ٷjkABact+|w+KoK~ԵwkUa١1; BL$q#'v+K{_O&iAF)hPGhP?b|o䓹uV"{P/8ⷑ!92g#\`)>Nw\:σ7:fs"9|3֔wW޶"/(tjKtkZ#XgcuN6P*3=5<}A>{  7+&='A- tGp;es_7x^oWǯcYռW\ώm&D!R}FG(d |* a{߉uΨ:߆x÷Լ/+%$c7%ĪUE/6o?/x-5M>I.6agh\*J7*H#k=xXo-~m_Yu=rkmBTstF2|_/a}n-_}Imk_OMQz Z*J<{tܟ+[_VWډ|i:?G[(iw6l+H#.UG A}#Ι\.Y_> ࿈ #x*42;5;;;Q ,2,JYX#tV3W~w?z 6AKEIBmR@A?ǺQx'חx"kom4r}T7>kA+ខy#-|Gyn4(//?h)fhV1 r}V%N|;z_ kf{Y9k6"8ٌ&=+R|r>*AM1۽yJ_ʕ~#28"ovz i Wȭ|Wl_YK43f,8|/n.lIŜѥcNd2G\2n[v#_mRH˼8syJEcz*Ԣ=*fliL`l+fcҢ|_|l1~>6_j4?W/ 5Gύ? *+7Qeƨ:ai4- @r!7鳦;M]Byc<_|l10.mt_ȴmt_ȵKQeƨ6w /fZ?6w /fZeƨT\ x3-x3-RTyc.VahZuz~KzƿiX1ʮzE ]s7"/?W/ 5Gύ? x3-x3-RTyc.M]BM]Byc<_|l1&.wEK ]s7"?W/ 5Gύ?ei}cWo̶޼q.l-W=x]&.wET_|l1~>6_j.mt_ȴmt_ȵKQeƨ6w /fZ?6w /fZeƨT\ x3-(sχt\joE>~>6_j?W/ 5E,5wwKzFhedmqʮsj6w /fZeƨTwoлoEoлoE^~>6_j?W/ 5E ]s7" ]s7"/?W/ 5Gύ?].9h.9jύ?Qp2 oYwqm-ċPU,[n9Us]&.wET_|l1~>6_j.mt_ȴmt_ȵKQeƨ6w /fZ?6w /fZeƨT\ x3-x3-RTyc.Vch:ZZiw1E}^/'PU,[nr9oлoE^~>6_j?W/ 5@6w /fZ?6w /fZeƨT\ x3-x3-RTyc.M]BM]Byc<_|l1Zjr#YI%u˪w\ *FLȉ.>eF,*)%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@5=cT-=>-:ussd-#c#lG~A/%n$$2,$mob1^Iix_ ]kVM5u[ M+DF_3p9jؼEA/]btuкȿXst=E8KbYn!TTB: IfM.-G~:&wቼQe7rE%oUV'uR \b=Y?Ϸ4?ڍ6/sø<;'(qQ^o]ObūZ}Dע"h"GS\a C?N=l/56 'iQHnHK/wK,^G_/ewyZ4 "T347r f^:ԋ?6srT 6|(q/wK,GؼEA/]yG^x  /öZk#EeqO^h",4,X.<' O}ay %qG_K'%}B[g.u=: $9mn+ϷZ_~O5;> )`ַu{-NGH+7ц;%~5 BmBHuZ&l9bz7c袊C ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (8>W+uJ_Yj5Gh&.--mvqān#$rk5Cjy AfхHtnqJ ͭvfyH"mef{ p4}:( vK M PV zky޼:o|4 Jf>ZF@'һVmo4¶?3k+~֣jwZ=kwSBэCˆ#BH JNAlj?7ƥ ĺukҭӶy/$v\ӽ<Ǟ fӼ5m.sqci<VpUTt=@5ޛ6xV9]nBG]sǩµU 264 373 0 C     C   P" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?!mt4h叩w3Ҭfč`~$ҳJK]XF:<}3Ėt2f# }6U39Z1}cZj0/C#*ֿ2pY\_í@>}O~1Q+\yvEͷ#=9kԵ?n6H8'y#Ӟ׃,]U+\pO/'{~}4~K΀x#Mᴓʎ[ˌn0F$*5CKӿ5EhI+>ӂ p].xeuIB쪛#t\ # f.y=\Q YNsRþ$ίx_^/[=*X$ӯ{Y$:ɰ=q1s^^ccO( M/;]UNjXsY67ecu?|FgjIsK46 %URǯZ-/u!ohyT\Ӯ5 "LвZW|LCZ#HC;؊>SkWߴ-4k,`-g s8 'Uẉ^o}'Wy|oj$Z6ARCqs#2p|j]n徿IfyᶯS$h9]v_Q'W*=U47ψ4-5G2PEKeR_ms ́`sj-"F9?T;VHIhAK ?mFL+/ ^ؾty?A@3?!_h8O> t ZHԼ$76؜\ C>3go|3ּea+]:W^'{ A.np-n!Xvߔ `Rr; F}S'Wy|Kb: RZ'k`Vm_IduNM[I#!߂>#;o] > }kGP?*9sǙ߃kxydm~k9c$~j$Xo~ fҦ?7#rf$\uMI&CP?*9ΏٶĞ9кvdzW){gR+"J!FsM{.h$O')\\K:NvF`a⟕bxG# ]7x1HӳI$ojP~|?F4len Zͼ}'\؊AOʎV:]5񁵗?k`$y=9xᗅ~#Z\xKeͪC?4A-mCXlI!~bp֋|&{x<[_VgR]Lc%F#SֽP?*9s˴[t/)#y:Ƌ*,bM ^:~ 9|#`iCE/PNtsV2yIGzgؠ)Q(?~Tr0Ghu*X._}kGP?*9s;֏Z?*>OʎFaִ~OʏA}kGP?*9s;֏Z?*>OʎFay籭?AmuC4W7h }S@j[ZQ^[?6] j>,uAh+"DH9.׋m<;RO,Xjb *:+9] =@qi9CQ^W/ZDžAo[M5k4ign IQNHFbҿhėzMk_R)ghc ,IЇWɢekEyNDiƽx{_m5MN}Wi-Aɏ1ΥNTv$U?Ct5 h6&bAB 18>yÎE|⏋aѼi^&iiv-qڣMy WGU1x߄|C{\Yjבꚝ1mز\: ;1s#իշ/W崛m owᐱd տJڨV ײ ["jn2TV m])8:5rbe SRPiψzƜw{ G}c[,3'_:w𞣡+q_67P<3 C$άAv\0]_L.ZiO3?疧K.gO-O]r--xLږm4FB&K<)+LPАvJOړvZWn B%?ʻ/i/xf쩯/=x&"QBCj|S<V#q&7? k|o?^eq7Z.~~21I_6G濩^i8cS:DϾuo-jn!FHZ0A9@k_xsֿ/+úgtebnp]Hvl$ 1U.QcxrKj61yd]EU~5#I76w]u ;/^ӴmmVWai9|c:<6\נԴYbK-I%Դ@C|@ۮkZN>X'/{ oksEh8۱l: ۰Nk(6ɘ~ѼKWB7żJH83[~>:M4qeue,3̷B&V!e|u$ 7Jgş'Jgş'x.waQR `~"hm.3t(I8~q#5#q?Z޳}B-2OD9n>[\Jw??t7z4Bϋ?NG4Bϋ?NM-/sUKn b Yx?5KXAԺΖZ8/U64vk˿t|Yw?t|Yw \W|^] г гGPch.ҿYg iҕIcŇGq?\9{EK 2W?*VwxP o,w+2.Gc2+յ)P^Zjoܽog\ܣ+c>ƣԱUxD㇂}}z_|,.EkgIkA2$H%p= o^~ O={ 3\6Os؋ T+u EXϝ7 w43&¾fj{.ن)gqV A8.>zfXOwRRV_C͵oilI㰟PYV̓Z4K5Cx c(^3%>FX*e'=F6-7w:}myo@";=Ar?oC@WNl&F$U 먬_Eo7x_G֬,dX4xY2G0ʐARKm#叅k+U+}>$z (jjE QEWwVZhx+rMA ;ՌPĐ0OZ((_o'𰝎 4!1n^ 06@R|;x^5&ñizw{{8ɑC S g>LjojkdiwR#+xc= [ Zklm) i&=R<+'Ƈz9e0@bUa;Vwyoxq 6[#dx̃n9ӰuQ\W卼M$c9\ nk/>0=}͢JfO9>Ѹl1W}E+t}{t>=y\K{ ;Dxo`E.>.9 ǭzx3׍M 2-#G[tA!MdBM{W%F#kJ*%aDVV1ͭj&PMRĢ5$cq d {.k +!UbW;Q+om YZv ]h?߆Wzdlw;;TvMO|Z w^w^us84pynBA:}UO։x{Pȓ\,iߖ}2A݃}qERvVKWwgO xψX`}7f_ o:y 62 }Eޟou%~g0~Ag͂^\FC؅RVg؇p\5QI+QHaEPEPE!^3Zίx"=3T7z]Ikě1FvFyMjݕtW|vԱ6t3k6mƣsf.;<v$?iP2kZn$m)q@F$7 tWk_ 먭Vv+Q˲m#81!6K};%-Ap4LgűqQCBҭ4?u.QL)c8#fY.C17 hZdz^'_tkt? j'"֬dþEDӄVM28 {uைXOkNق1Ev':Bst4ח?Eq ,hRk=:y5 ky/,.,A*#bzQ^YCK|% c|qKCl&Bܥ 5 ⍷K+8n5 Z%ec6$c@gv+Xk!Lsffd"/]i_UU=x[_j3Ϯ7zad,Ʃ"ob5[ߴF|5/G..4?l%'pԶML/]=|A=B;kK[ntTW0،=k=8橧f +h(>3k^%{m]/ZH!a w+eK7N&e%rɬח 1# 3xEN14W ǚVt>+fՅq+6kNI(0((((J/_ iL[mswt-p[$2"`1 aOˎ3_AM6_ i7lm:f[M%&E䓦"SsIoKoK~|m{ω-uYo.4^^Jxനwp~|Q+&ln=MjXXxAavo$mmqtRw__$<&o7|+4Rku."UDH2}ܯ{RZ+Š( Yg?xwEլ/mٮM2O42|^^Ev=mP&C#& m = .BK5- x~ojV7A{-O$!6嘒9kz)~I'^(l-B Ɔz=IsPn*=k_Z\=?\xfFN{NMZLE^_8־,+aLO۷LR=r.ѐ09]6 QJMɶG\|!W|qmj|&iwruhn h6ێG>=);&xf#i \9]i~оW %~|g?FI;molM_Տ?Y|IB𥞣kokH.LE CWO (:(C ( ( ( ( )1Dfqφ,} Z?P1Xif'mZuMzb=)7dvݟTQ_6_~~*-aoEf %܌\A'& B4$?h# ȡ$q((W>O\w354w]wZZ¥wf<jHf'Ybu *=|_ǟ -O}sk_iM /Wpf*O|s<-$dZ2!Y+\m`=ohY>h.<9 LC6%ŰT-o0$ 8kxI0fʿ:^=/k]v;ޅDa IH$pIq5x_F/ڭV-a"F{g%ֶ K>~iZ͍7YDBA GA/x /˨j:o?}\A4w;ok!Ih:n' Yc>oh¿ V*v_+ռ!/Z.&}v>gI2ЪHs>']|Ci-27!,W(N7`Q/h>[+oh/T?wQ`8W"CO9 E ͬrc]X' Z!a&EERjymORAr+ /1}M}NkI2]9+m p}%OM]\Q_8|w[xcíi>աӧMYniN$  PQQ5oD𝞏j> 񦲦xxb)rciKI#q²NB1BNV_'$#;Jkhn.#@+c;TsVk}Cľ'ҡcIsss*j+9I]+6^34[Xe6w jJV@>QrZZ]#z~|Gmk$id3sW!ӭP+DAU$'^ wGfx'|?g'ʛ5i+oh/T?yox )x&=B#.aI4ԮRػpO7-`c gCWMD3Ux|LYDqf#FocjZ[iYj &pJP7. jv ӓ]f # 4NoxFi7]Vwh-r >xg‹k|7eloOw8PtJ@3:Rai ai 'mP=tgc =dUwgGW>/Q>/Qp;M|;A3#~oxx60GV@OW#!R50@(QE #HdQ$W$ <1< ,c$r_O1 |X@>_Z_R\I,3M5qlL68T0r8__/tx,<;Xʍ\k3>_[ ̧-%1"Xp`j/>6irOdmnc2ZUE=9'O$Iy)=wb_+Gsb#yu.15ir'Qki\w@OG@OUvc =dUwgGW>/Q>/R;"hdYbUԝ$B 7ӐAEt0Gmkk@% ( ( ( ( {~/%-I#Tt7v ssEh,WzVY{5XcB_Vm>xLKo%:0fVr#s iOB=_|w?thzVսmAg+WTlvKt~&~!>(Jk VSCIrci0Cvm$xQmKJ֔Wx?>!Bxb]nm.&F8WjQqC -r\vG!PYW<9Z]ȫoh^$Mְ:akezm;:IݱmWէOa?M7%T.>a—Y_hqo |෋?k1G{%e%di (Bs9.U^IǙ@tOMTtOMUtQayOa?M7%Vv>]Ok= )'*#y{WWG#όI!ETQExg\ş xL<5cyc>ye\L:*0;r[$qF%ub^gS!յ%[Ul*ZBF|$㧋tj:Zwɪ, ͌Km(#8ovv+*߃ui]>=5K[GBpK$]M~6յ ^ՔqQ1x.*8S[RRQ_6~>+!u][t}B6ҨKqpO >9o›B{Ica0,yE~V?)<[vJk/xXkOJ6Ɲ$؎7,XNjҲv5ߖsBBI9I >?2O"ʟUUxOgL|qk=4-4 ]LsIV7ZzޡUԼ-wi]!y{G% Z,eUUz6,}J=[Z5I.5]Z$g~F:zOa_VKiR)QEQEQEQEW߂|C.n5MXZ]l3_88Z蘒hKX=Vk۹bxY$-">/#;#9<3Dž#tHSdգaazR&$yq,); )V_y~MFVHTK[f0X*F8sW!oW]{c*.x_>_x_Ɠ{^xWF=&ئm BraQE|/4O~ ax3t?<[Cp}ۥ0DxBğN1N_;.4-2C{8یrHy_t;;ǬУ0Ty7+ г_M<+BΏ4Я2q0] ]!<+BΏ5v æZ;ohaX < WQH/0>!6. zAq<6^pA5@aw>7f+y`jmĊ1_Z xkWḲ~>YY2XnNrI]c̛|ZƫaXޘn 2r=;`zV|NJ4 y{xogJ6_(g|l?^/x}Kxڙ3,cUmwiCs[_jQQ^iR8QBb^kfK2[Đ(,'[_4quSwX u%慦]rOgqI< .3oPMM[ž 5滤. f mϽb|<|#\{KSWlNˬ[n,\Iy_tY/&vY}ǬУ0Ty7+ г_M<+BΏ4\ :|`TjˬUTNxt ]>'m+c!@Wj@(((((yjJ.f 07oZTPoDEV!EfdMA[Q7oZTPoDEV!EfdMA[Q7oZTPoDEV!EfdMA[Q7oZTPoDEV!EfdMA[Q7oZTPoDEV!EfdMA[Q7oZTPoDEV!\g;BKsM& hאAy V!dMA[V!zOj~B>?hdMA[Q7oY?}? P7oGDEgW/ICDEV!^_ڇɡxN\,wW]|Ѵ!H2F7oGDEbHAmb[&BnMOX5k'?@V!dMA[V!zOj~B>?hdMA[Q7oY?}? P7oGDEgW/ICDEZ{U`3\s}0c [~]ڇyYi,U&t\ 2{Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@s^08Լ*}5FKky[GҴmVQX.19 W_zt>Fek|WRHbt@컣F_uDxÞ4-;v-%moo՝żdo;ʣ#.rUq&>?S]eK`,-HV0K`<gW9?xZ_jmK:~Is <,L"eQQxjU֟6Y`0\-+:9<iC4%_g%]WV7MqpQcrv~Ÿdž}7zž8uHìc Px'.x+Oa-ĺta?Jd~*/+_7G&$(袊((((((((((+*v+lO'dCeֺ(lS (O,^<υ?`[> (lS (O,^<υ?`¶<X0]wq[ZMr_xO/o""h^c_jp}sڲlS +ר!O,υ?`½z[> ?lS +ר!O,υ?`½z_¬@]~ɉ($נxԩ0vхoQ@fotoxx-18.01.1/images/blueball.png0000644000175000017500000001336313222767271015425 0ustar micomicoPNG  IHDR@@ IDATx] xU?sߒe $J (jEPQqE6k"VuB)nŅRw (Ǣ" %$em̙kxxț{{g̙sΜ93y~ΏbnRD7~:+L;={”;)L(M{[a܆ ݳ>VhuqPV(H#eiZG#@(/二>B: EkC?i̠ ;ڢRS.ZP2=֕)TmY 3Nt¤K5y}(Qy*鎥 ES{h[1L#E [u Oe1<[cjLe$tU-V }s0_0CtO۔OUz=YΩm"tFI]u-[8DR+m!5#' QDQg31mOv}C%~<4>QaSI} tQP![/iL %5R giX?[\YI,'O2RtM 8P%$^ꙝpӦ9횯G"'aD%>hް0soن>slFg^Omw b/VO7"MSn$H1:0f[MoP)ulfj?"_ LS-Is ȉ$w}y ͪe_}%®7;gK-) ,Yt +\Vl-#gL0^~|X~J䁟֍KF36g$\.5[  h^*[kc9sʍUZzukhGn3[ PJU 9midNbn)m sCx3s0iؑ33#*ۘ~בVw0{ lG^g uiD^A4m\Ý6[踋sq)\{vdrh͛ݒ=UÀwT!d В{nà&fpc6j8, r^lع]\oK:Y-~Q{^ڳCvwm>IS@nfYtQ[1[%6 |;:`J\PBHeƇH o3!R 慐*~m(1|/F8H5 R%q(Ffl(\̗E*>ELS A?6XleFvJ[G} Ƙw M:)8>F+׽W1KMQ8=kʉT}HR6n3:W驧MK xT]ZvU>uA]Uk|[e?k^ ߹Mhؚ^їT{aKxo唹9els #.[~ ƕ#V@LZ.On"V{&~|Ԧ\aerYpoߏ1¡O,WAJd\] P#U_!Ǘ>,РgF*_m\Dr a;OmqF|izfNycFuodmX-} u4NN8c =AH/ٚŌQ.i_.x;:U=q꿊 X9Q=^'=*ޞZH Ĕ!*N+4-Mc\{IDʹgxF.n++l;?<|ѣ}%<|.h  ]>L(E{@[)H{l"\@/ ++iuD{2cFf0=q?1|C6^-e=Ϡ|}MtOXHRp'U`jOQ΋܊{K=hˁO{rPt\JXl&:i?݆l_mR \^DV0*EPIZ1;HPWm l$7 >cVo/]3B3xYq'0xEhXO,w4jabZnaldlkxq~ݧj२vrW@WSvCQ:+l;%[7A>'Aeru1P ]!ã4M-E",54F6mA*UWΌ2&/bq*oclch,WKss_76;L>d LݏxƁ^/$<_?mpjL~ee߽w_OanGo˧QN\,I1ru?+$PJp6[,SZ!';`Lgt2$%g2qwjNjX! /_`GyF`,F [صiԮr_$:yx_=F4lw:.e٫1:$r ǩ1D4,J}4Nl`B'hZ5hQ; 0h47o…U_[[~ M+] :]adeYohe LkX֮hFJ}mD=vmwsqֺk?L3;`kG ** c`LY(Lep`q>jp`uZo"ĺyC΂R]+<~tbXnF'!wj }K9_e]_@q; Cprq5$@l@T6{<* afLEٵ`t(b>zԑC: LI\ U$֕L:ݠX&#l}6ߢ?Nw=a)K`O+Zq\%g/ˁLYV{>gÎb_qg i'5ipGgx"Xm*?,OpfJ0>+ֽCPFwײ湆VAq5~-oD+xZ# kOb Bk.6aJ@e# a"[$7uRH)r/ !?3|A7Vۻ/`+^M*BN]_oECWqDX_9Ϲ&}S>Y! Ro2|eWaI v^@n̬#N47Щ|mparhn} /~$o VPw8辂nGG,p5PH^x8r(F&~cg3Yu@wmn ~QQ1혿S);6?単օN*2mRʅ0o2M4|Q80zTXtRaw profile type APP1xP |WJ -|Nf5 83g\Jq.Vy} 4JA?3"e숶:[pliCQț Nx7hM񂏻>~Ǔ5{` P^19 s2|ۊ9+_-ꃱJePԑ2$E±_Cs&Y:F'y}5+iTXtXML:com.adobe.xmp 132 134 CIENDB`fotoxx-18.01.1/images/add-metadata-items.jpg0000644000175000017500000010520713222767271017263 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 328 500 0 C     C   '" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?D|{꺝ܗ4YbRI8ZF'BmA#ϊ8: %q MǕ_0O63ʹC hzGSha-7Qo]Yڳk-_ 35Qtr*y|3%5~vr/Zl#R HAgf璠 TkiEG}'?DW2O_i:^wAm>ҷ%B+$ègO_x`σt'Jԡ'50: ќ,HkͯɋI'z\cg`OR'?½V|?n|;Yh:um{:q"2)08!>*t=Wݶjh.[̒w0*6GLQm}KW,Q"M'ۏȯo xO qiڌ-Gakde2Jr9=I MFk+޳k6.1V[//BXx$o>[<_lK ~#O6oEgqm oǰ?ܓ#1RGl>BkD.jpjXv!EP D-m}%_E#+>xgBBuu^+-59I/ A6fR]W01V'>2L:P[DIfVXDG)PmSp}$?qOn>|P῀.Iψ+gYDbu;yVX֦p2Aʹg(Kt/Tմ i5 vg m]Tg8;OGڽn>|?z>EqOn>|O{z,ۏ(qOb}ޏ{`6}Gۏ+^}ދ >}Xj^X DQ Wjm'?DV'ڽWo?}'?±>Gڽn>|?z>EqOn>|O{z,ۏ(qOb}ޏ{`6}SYkw:uw,'c)#GڽI韶Wl4KiaHbHi2ZBhE]+#(܇M#W[WD"I$rL~+/&goO9ؿb^7H09/-'77j35Ԛ~*e'xDPz ?[G3'l_MtS 0PC"&Vퟒu5˨XR$}}IE $܂:W'3'l_M s5)W7,w!$#zҨ}3'l_M s4rږa\V$ڈbAU K\'55{-R2_PFQU@9&P᝾? bhv_Dß-Z|y*ħij&`/"uV&ci5{j_w[r6ĥؑc㎀ ?[G3'l_M.]ͿU"U֫\]O}gv˶smR,aUG05gZ[,g-W>TOuMn$g~;|/ ?[O݅}%ǟS5u8 ۤ:(U01G5.Azg&G&i8iI?2nl0qW3'l_M s4ts]'MO}3'l_M s4rߵZ>}k/&goO9ؿ9Boڏj> ?[G3'l_Ms7G֏Zrv_Dß-/&PGڏ~;|/ ?[G(\QGֿ\᝾? bhv_Dß-.~F__ s4;|/?#~}hQ/goO9ؿ?᝾? bh j>}3'l_M s4rߵZ>}k/&goO9ؿ9Boڏj> ?[G3'l_Ms!4W3'l_MrD i ϖtՒ2xC7?@C6?@ IymMDǖt($ca4$cq4OSKne [D8A!A'c>"t6GWK2y3ZFIX\3$kۂ]Ы`^Kg) tJ{;2G Jh&]l@k]{?k{žt{R!Cq\BAXLI ̦I cUPR|a?NӇ?,c=Ι[@s2۪,d=R<' kmV=C:͵S%v,nA$RNjƍ:VzTJђ 'Dk{D-Z7vL/:k,"ՠ^S ]a\+!#)9_ Z^C}ōw:i v[e 0&TlMѼ^]qNhn.dSvypr0x^]V:=Ŷ(h`݈ *Fnm"2HPiuv/S|Gաm.V7zeW6dK +UyWPamhl{AR,zݼ[@ӆfb|ܱ4u@OU5CW4e|M t|+Elq&m ,LK05#H𭞟i*Fu[8c$ήu].kAg__lmn֒Bw=BaHIB29fZhVtZ\ykE4%fYj3vAN tkzφZM̺&A-+Uwf2f.DW|𧇥U|-idQͺUnvzנ$#>[K_qSCE?ȓ8?<3-QO0H:j9XB$"6v-Nx–I3@\?Q?ؠ ~}(j*=k{G}u MQXoUe֪Ikj2Fab eawk{G}y|GҠ֋[ηV^=żG,laG}[ĐxgG6Z޽$4%WGxľd;e. WZ xǺ4^s_]~Xm39pt 9|;M:j 36;Krױd˖@bG %.:n5.GQ|̤9Tl~_ 6.#M˿img/J \]mi]л@vligĭ/&J &Cp_~ 68𶛭]kC|IJK9ܤҾPpqix߆w躞d}$NWmr$XE HʶGKV_8|Dş/xx~7:h62$q0+pD|>a/]DmV[iH6q\02vcu^/߇[^}Y[ɩdbB&hJ&BJ񾱬j:j-BXځ$Ϊ##ÕC\kfA֛O h,>u6u!>g?ixJo~ ak<;5Էa},C?[?6+ٮxRG3g}AΒ_;w΄31*dHv:rksuewu#Z\^ηH4V{Eʺ[8,Fc5/goE#uZǐx|SK֑\Դ+5k/j:<i-悲#a0koĶڊhiu֫uc+j+uio GoYwBE1Sse2r:qw2-A<1HDSp9_< aP};ul_S!w4vv䐿}jBI_>4㏇pWg¶/<=qX%s-Εݦ%pk JR9oWoéI[-c+).fw0' ~Ϟ/tnk57:aHw+ 8]<+|;&w,n/oD$orXxi4V_:J*=k{G}QP(\?PT?k{G}>5b œ8>>( ξ{'G6 `ּ7 RFmRv͖,@|@%c$:}eEuZ?Ž;~ ~  M٭._I$淺RSoh5'RKKyBiڄqG$pՁa$.I;k;{)ְte\ER-.L#,vX;OG=>ױynޙ1ۥ])\w"yQQ $eӑ>Mn#g*2qzjZNHegXs9z_K{dcnvNG9V %j-|9{uob*Ȳ 'jES-tM2df:y-.L#,vX;OWh _l^7]zgn\w"yQQ $eӑ>v}L6r1#/Gw[ޤK2gr9=jQgK6F6js&l2Y0pe?7\ujZNYVdYqxo Ga8`Yc$^H{xvz~oȁN߯cL?ԼmOod0Ke~WV^FAmte@~#5 _z?z*[,:֑(ݶˢ^j4XZqyx߈t![Vrkco UK@Wq/S9ǣxƞ.=G]Y~<gx7R/0Tk~2[QOxwH0hv6CBY!dB*#$@jM/zߍ[ ~Ct 7V#]CY {h!KyČTX^G̀Ăq|J]ᴼ}[>R DF¬XYwPOA_9?oRMx"LiݖV) Dc6.,ósCƟ*G֚ s\$6ZBҮڣZ.~'Y?uߴ?iմK @Cj mZOrg]N%3ğo5ۯxZF%h-/亷Xn$\H8f[ r6tv֋Rggi.f].#A&''x}1XFiWl9aFG\W[i6iԮeIuA#~da{W'#Xj(E_g?g?_g?g?_(`i `i ~:/Q:/U(@OG@OW g?g?_(`i `i ~:/Q:/U(@OG@OW g?y֗ v(PƱDD*Z=?zyu5CkIET5~A]*W~+u)=eqgp.1$Umߛ//K]5hGGŗ.oƯo-TEiTF)v+o9kFRv@Ȗ` QI R3ݳMhg평!WPIy|} _9Z5nBZw}+c7GOMƁVzōΰe̚d%[^ү|ngiN#uZ&4w/_ m ŷ?~Om)|sA zZmݾ]\O/qaV&%B*2D>_:~hݧ",JYE Sg8s Qa'<;Kr'4n4j%88Ηߋvko=%+żyX'<_c5ψt[UJZNJܥǖJ4lĔpvO xNgi26vy T9]yN嚴QEQEQEQEQEQEQE0~rOJo<̒~|6z++hhbOՌRw7Đpl=:P&x#5 gz?z*'akm/GnqGc?|Koڱ>.&5.ěK8vDgWF266~|>]O_hܚn_m?sr+)dw( _HgI; KIfMCQiom-$$"H9e\*sRPM>Ͼ_olx7ό6-wxWLii bMD^N#~)\_MV_.Vb¬mCd ͆8a;u4h\޾{J-J1JW7zޛ@FQ .=ȧ5̚?!1i,>|/yVMgRLӵZOhtFӥqj Qeؓ 4^-O _h-~*{~+9vimqFDѐU"(dFro~t /4FXYX˅ce 4:M!"Ӗ` Fv@ű*#@V=.f񎟅fj6*}-=㷍qZOl _iRhwΫMwxf4y%1U0~K[Ꭽk<-fѵ2%ҴJ\I$*xMcFmR\ "l0q~9o^)O /WI$siE5ayhz+ߌzf'–ouFmF/ߕYcy19m7⿂5ȭ,~xsTݗoM42.xc #u>eEuw|J"K=vŦY[Kcu!B,# $K‚-7;Mz~jߍ_玼WEu5#]j6/h !?,6=l |߈^( hM52j, !hق$ógxƺ-n"Lvo{ U$K:-"8?7Q>7 A|=Z6Qummb!bWlF>V˕nҏKg 6x.?%O i,+N'xmlq=$[e&K{hNJ1J/9ckz֯i6v&|I9⻫;-#[0RROt_1^1|-gMY[n_ U"Hd]Ig/ rj=~紗_OZ[홬ZiWƉ[;+Nv[kmk}Gs۳TA# TDᮞM/˅S)o t '^ob-filʥx9h;߇z~#,-|7kp޳<ۿ4[+"VC |Bu6zI^< f{}rbTEQQmze 뮿i}h:g񯅴kI+CvnQ GveA\\zu_N{%4LeB^ uM"mWX<~!lAMaiO%a+nOi?ey/im(,d滙&,ECI3]Q+{[7ڕZ{Y\P@F~b8'[I +2l&xč55խmJnS̉ Kc8/\mW-~w_k>"HΏ;Kg'n++y^dxMSeQ "mR=COEnda8FHQoT7jF|_ìˠXBswMNN~w A5ǖ#|"d_X#졶'b#\ƃ8$i%_'gxo6Ix/#̈́yq96~~_x]>?V KquwDG(_;|G,q洼G./źfk]x}* `wA3a$ђ(~=xzg}_]}]!̷Y ̪Q'hbp1Nuuwb!uX"FIHA[IMsE5:`?f/j~`eŵ_ \[Z^K cN,aٍ7Hb3^mZ gdjry?> @|Sj~+|?h ?.my%cF$9;M}\.s,)&rTյo&.5hKEQ@Q@Q@Q@Q@x&y۹ү oll~i4cWoˆ{L_هFkUO#5 _z?z*W~x;Z:vsZAO?_x\Cu/xR&ˍJ=,>ʭХ{]T׺ ң|qy6s't=qy,hccqrX7'=&>ui[w_. _JIm%OY+nЋyZ>d-dEMGWIƷ[<'P`i*<-VdTJ.P}ڊov<S{Hc\zkm{i^f#0o6wXxo^ד[<5!B#/ݖM)>UJׯ}ǘM2;NjtOk[tlג[U'̅w,oTqgxgmӼ!+oK& ƴD2N򠕭wgJ<`QE (((((((+ˬncZ/kaX֓R(k//Su_Ě ?3iqJ77q6C)`k?:cp:𦟯]]-M2:ۘQ.yR6;~;OZۥiz>o~ڟrL E!ܓ8;l[ZM׊$"Z"7>bry{n[#Oq~683ƾWO[P^Hydߵm8+#uOUvY/_xݵO(Q@Q@Q@Q@Q@Q@Q@Q@xvz~oN߯o½7VSaIs&x#5 _z?z*'akm/GnqGcШ ( ( ( ( ( ( ( ( ( ( ( ( /kaXֽFFV?!Ԣ*@? )|iM-6Eƽ2H [p|T //<3xEi?oj$_}[͉߿KtO=K-~,5=\>t#KH $HYYe-tԴ͟pYPo6`]2vk ?þ.]|=hKkKż,Rv 3\?GkEgii|[o;YVQ9L!pCF"e{>_O0<+MCYfaeg:_5^kxјG $ TǍwG<7*xGxZ$mIS3ƨNH\::t}YO|u]k:} 0\l!8T\4&0 e>Ú|T#΁'mMպef$ɘy1(ZWɿ[~ |{߇ 4C7k^ 4jȷ #nr+|_xYs.Ş^ 9ed@$,\,}'Ο*En-bҵ-n/)kYDF *. +WέX뗗:h]Ţ:E%hFVIāՃ#'\`~3z.ۋ}·%vfa6ɉ#(vbpxÛ}z/xY.I.mDy()@!xfHӵzwu|%g>L$-|QQ6ڸ~//'þ,O=o5[}[i$242-'P`aц}-ܕ#~_;1],55cd@[gz] g{G{@tj_b횄m/+nvI&Hյ]SLlu-)$͝ʀ2Cd)]iQEfxHԵKGlm0D\#h%2p!TQEQEQEQEQE4ɧM[ ǯ\׾Wiyw@H?3_[i=uهFkT=B((((((((((((((+ˬncZ/kaX֓R(k//!:Gx;^; E]Vx6[(`_NYr %,<߈+IkݼV@B16?ÞGI'?p ^<3/ƍk_O~?@4={AҼ?83J*Go |LZ;_dž/x8lJFf#nPqA2OWo _=hvIqrK=\[ J`O.|\tA(oR?yyvXkO~ ~ӗ3Knt&X?sknB$($TOĚ,s|C+_k3h>-xXHbK݌|+m|Y}NYФ4=?<5|7x5?xDL71;]ۉd$O H~Si~On+[oه{,0ٴtS5ÅŸ AwO𧇛t iȓ0,/1X8qȯR/SJzmcn&8M; ݸV|:7:wp Eu8(N0M'-?{nxaXB,lܜsڗ/ |.R; _[} E Hcc8PqҺd6C1mFz 9$ɠG/mWFפIѯׯ"Gz 4$|HP<9Ay?l~^q8]FA$v6ȽaPGBEs9-Н?e4M;?I_-ݵBѨDl$s|q>oڌ>6ED" rfO,MPN9kOT;hc#0S9a*I⺌4I4gH S}+GOß~_0|MԵ[-,|W&4iK>"IK[%cT$ƺo.I5=ro< x+r-;A;'iZBC ,qUQN:CV|叏kx'/dGyD$˴i7E 7_Ow.+ixYo [PQV'GaDKm+8 yiH>z +qrnBv u[юK~Gʶ$񎧡|ubY#}iHmS31G< Fn |~τ/IxEе]>bmn!6b<ϓj)#,IU`VV/"9=I&>>Cwyc88vzRZk>7a'Z٥\8t(t{ẖ'ͷ*v&UrySh%% }+I=09փ!]6˹s_iakjN yĝ2-/6D,RJDQSMhߝ y_cᵧB]Kw%ı(] q^׭ү=?%i]S-/Gn&*(Š((((((((((((FV?!Q.ƿHmi05(&?:/ gs:ƑzKx4裚>@̍,16#$}?:~׼m[x_O%.=w4!S m9ߌqһKwo-cuUڶ j~ۣOoee,wI:7|BK -C<\|y Mi)vKq7Db8 (}-<yjگox=Z98{rR ;޹Ժv⛍2oxO,> 6ɯPpdT)ES vʿ4zW]Ko~@ø5? h XPTwH [`W2|,r^!ͪ8k '8%Dq9ʂv˰8-m"d{ dY}1KπrLeݖc{iwemf%n akFz':_ ^hVO}>s3[a0;A xO1h5 Ej:Ŏqu2=īeK؁ I;v{gLW_5dTV^g~k}N % 53_x0|Oik,Qkj+oayj-'ú$_˴U_Vk:?<]}_T6-!u)4٢Ǹ,p|ShεG ٬om qXKvs*o qտ$y>GvG/'Z]q<΀oXrѩe):) dׇG\xVOu}}2̓Z-_(;FgVw9qh$K}EP0((((j$~E$)k+&G4~$Rێ3=93BV?B{iWz#ޏ^_JzQLaEPEPEPEPEPEPEPEPEPEPEPEP^]e#w+רחY_ 6QEHYqKR, g¾*lJ+KK9{IkN» //>!PBZD|%|iuuE2iw0by$'J4{f7^-ĺxݭF|0DYlĹ; ''oIǗ!ؚK{Q^E \]p鸪sfdnei+lB,+!F1屌dkK5O5;ѼQRk2H%dݕTsD~5?&r׻/_e+{}-*;A6#:&`sw27GSToxq9^].ZR 42C ʯh_x7rmlu3anvO% . }=xC7M(u&-|6Img̒e}2qI+2Q7Wo}MZh2TvwFi[,|$t:yT(e[$`ά18Wu>/Eg⨬4 _iдXnͬ4q(<7k1POגjMj:\Z6&s3e38BQԯ 4&?jK7ko/í }8jr5ܽK`oivd__newiiz͞FLo'>oۆ*|9r={ŗ^,ifku`Bs Mt/rǗ|M%y#FT6FsۙZBdcj((QEQEQEQE$OnbP|>߽{%x˶.)#:$} LuO+дUg?1^׭ҤESQEQEQEQEQEQEQEQEQEQEQEQEWY_ 65_7x± &QR_t_\9~ij>*;;fw{k&AN;//>%Ϸu$5+X=n|?rAZm.بX6ātB PM&WE+u>r,bY*ʱrV/y2='O"1>-aAx詔%NЁ>R@ۀp~_|PeԵK?z56٧͔r8)#9WVUӺo|@> x[I VβhݦLIU\gd zSJOAjvwKk4J$bnPNxqksa-qjsx3ر%f`$BWk/u/x/*%|=x&ŵfb&/MV[_ajڡ6?; Tn?Lڶo9\u%YqpBQc'+Ҥ}4QE ( ( ( ( ߊfĖM(^>s^^;>X/+d2+9o㛨|We-sgLU6@-+rǖ_Moxڕ?:mP5,-=ۉv_9mm8ȗt^gxWl^)2Kyhsy _ƑZx5iI_}4x\ J-O!@+oҺ@?htX)|/uzjWG,|kv@\ yI.›-mǶ56tH"mh~ a$-onW[:՞񅜷kzE#V:Q-݆$@J*JV z0'KD}G|>P[%?dQIʄkqãP5{f~#YtOx6IX]:4Xn#qc=7>/c6:}]ÉaB-&IBR$Է8ut_O_EƑg*߂G%扪A ^lhyIVoׄ~(^&$} xEGmr9 ` ~ɖ}K°vv:W'E 2G3CRi%[9ou/L ~!iԯ6T m47Ge*rĸmt`Rwݗ='ngͣk".,Mj8g`΃h,jN[5z#acy-6'MfX 4p6lrq'x:fmEhڽyD.RHAozNKwѰU2q[^y~rN89=MRQ;<|/vW|G{tHo"*6X$?1xpmO}S]m^^-[!6LMdD9$$C {KLU:MDi7:Z72骪S6y8Fis֑699R?GQ {@ԋJ4H4fOF6c$,#j&FÑȥh#j&,ޑk.8 p?* {KLFDi:ݴvsoHtYH E~MΖd̺j}& N8R)i#(R?GQjEoDi6[ik70άij0o ps(ҿh'C 7YᑷSP;~g~5Ohڜ_۰FzN nrpkoj?.|R,~w;bC5?̱1F!Vv߇zj^ү]q 7l] ]Z5)cXaydkS=)sfA|g(u\ҭmQaZK,sHFTɴKOT) z-NJ.45nNojGtdI%v[Nv}z<-[]isCG{1 #sOx2?7@җ\iL~"a=7XϽ4M}λF;wClGE y2I fBNQy}f6G[XKޯ;Ϩ v>g;DSAʻACwjvw< 'VOF_Sүt`gH1cR^&@CdrGſ3GAqG<="&YDe 6N0 צh?|mV=OGƍ,w6цvL6>? <6s ̜ rr T؄#{/sZqx^{{Kb12?|zWUxFE ~ VenhCCe7f"+H MLN HBg< izcOPGyl! 9 ᳕Aݛv+^%ռIq\7ĺmͤ4eXgҫǟ wtYWc?ipA Gj\"IKg%x&_ Η xKCn 508 350 C     C   ;" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽ~ ~YO\?t[im$eR0r|܂FiFݭtl>|ޑ^sz~)_Lxs~0BpOöijXH$oxE9_xwL[MDi=+w;[n J_=y_(aOz્ƸOC6<@}lj|?| #[V!Fɩ[71\Jy܊\'~7M4v66W׷rxSKuQC:m䚮] 7>}G+|5/qGjKKˈ !'lX㝼t8<ս3w(;oS 2nbHa/LkWo{~g 8;1qK -?4O^.5m{VΙ-e2!\n࿆}jwx:MƩaqhЋ/,JU%Fϒd?.{Ӕy[LIo >}X*nRT9ԿjVl>|?z>EaOl>|{z,QByHvqŹISSҠEՍ…'h8 ೸5I#kpĈ&`bqݯE܋o-؎l4`cE;!\ 56k*Gohn老|n#)U=ih<{-'XnUm(RāPյkYڼW 1 M(m"眜Gb.sK:/O8=W?Y Fu!:@$zjTCo19N6"<+ռA[iz53nѮ 12x0M+ﶇ<3(ԭZn0m]w:5['@>ס,躮sw k8`D88\ƾ}Vxn4eY݁OAysǚb2G8s|xri30\hI~m|CX}E o}Sτ~}hI/?k)rOi>w0_Cb`3x.|#GO} o}/ !_1G+ _w/?k( _C7>Q?i>} f?rOk[xYLJ1jQ˗qR`=50_Cb`3x>V>H㷏 Eψ,P-qɌ]I&{tgE*^W0ds_\/?k( _C7>Ras㷏|4u\ FK˔ r"dDoG9} wķlSNnF ~7Þ0M}w o}/ !_1Oϐ-]AtOSO*]KMcm]7Obe, *wd/ !_1G0_Cb&_ȺŇ%m6ֶp1(So~4xÞo~& 4b4FG(^0rr9/?k(qn |"'tZ/ !_1G0_CbV>I'־ _C7>Q o}τ~}hI/?k(as7Y.ų]vtp:}z׸ܑ&B1Hu+`3x f|.|# ԶE<2ŔKN~nMWMdi;c-Ò(ʱ=I$g_q/?k( _C7>QwOvIeN8 We|XSzŸvԎ0_Cb`3xpqQTu0o~G|awy3"pqWi|1|DN֤~*sv5 zގzj%dLV.6%'>ETl0aV^B?aEX?ȣR@}(,?TP_ex|>-`1"-`1"-`1"-`1"lTp$48(}F5_m_:^GCaC%v6isAREy23̮B:T t\ W_vOv]V?((((((((((((Oþ?|Ki %D@KRd?+ h sb$!IB. 3]Q@Q@_m_:T_m_:@+cjR%QEQEQEQEQEQEQEQEQE6YRI]cfw8 RM:@+.?E[4,>V &k g<֪?\VNJe`"z{8\6?`ryC̶H+m*H 1J ymᾖN^ѥˎe1Fry wZ__ hvZw{  i5Q`! WĎ / vujg/yY*0B95((((((oڥou![dKWk+W{﬍0>04KF>M@'=nX_k7tK} i/#'IGM@'=nX_k7tK} i/#'IGM@'=nX_k7tK} i/#'IGM@'=nX_k7tK}DRͥ dJ@Ԏ3U,Nڏ˻BM޾~`9M±ǎ:ߋ \-ĭB-.k- 2q KzYJ-loҾhAr]L ҭNS&[ +RX"Yb5l!&K py4^k:%:w;(GE_k7tKcamZEkgo*8a@:T'IGM@'=nX_k7tK} i/#'IGM@'=nX_k7tK} i/#'IGM@'=nX_k7tK} i/#'IGM@'=nX_k7tK} i/#'IGM@'=hjr#uv'${acao#^V NP+ 'ZT+cjQEQEQEQER3RBpx??=ܗ>"`(G-<'9^TWWpXۼ3Go UQMxWſ hڷ/?Hլ-%kE >$ZڵlF-FP~5't~]|Iiz~NN +iΰI%cs35P&jNo4XG/A LZz#ңk$QI3KqѾP J(z57I|SMKS;v0CGR+>Wš;iZ7+Lf8v('] k[D#@ (((((((((((((-?Wj-?Whlݓ-]W_vOv ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ()jr#uvjr#uv)]V?ڥu![dKWh((((((((((((((((((-?Wj-?Wh'U/v3쿳{O7-N- oczm9j?+ 4sOo1Hz+B?*U0?<]~Q_QO`i&?,f1hR KJqFUf?(x -~?$ni- +oH_i?Yiߛ!iޞ? OoPٴWqؐ2?o !ޛ诓co-՘i_d8}SE|/2f?YOӇo诜c KNѿ¬?TӇ Z+ +f?2֜?sV?1I2?1'?+ɣO0-8[j~%]8O5i:jbcTwtӴ9ڔTW$-4ٹڵ2i2tF]>Cod>dn*d&1.?/$UEsQ^4p&ýEAfO? /%U/1 _JuP|c>󫵟|>𝽮5֏t}*H_pn3}+B.D>x;WOReա-$hY".*0 9aR?:!4.3(ɦE~U{ %ծ=Y.mm;%fax ?6b"/]?Kvُ,~-xAsk ^YU~ƞ$V+@$Evׇluwej?/;_qxJ>[mc-*#ƏmrH6xlP??5gj?`fL}ho ˾q##͐F{Vn4{iAtp8js˔8܀$Ha!a-?]NJz`ŧm֐uyiQyTR})g3š%ݑQuc{g=E bCE`*`ag&2tʻa:?)m+(| 'Yt[uvj m7v]}jP(-]!=x<@rgc|+nl{2KDcT8G>^:iV]] M]{ #-]''H fXҹ㏄Iέ(vhr9k,9i n3kM}p.-7T tEޜ`g$W-*c8VQ4[MzEGA*ã+TsHաdI5Ke.xyҢd{9gUUKmWim]دR> 7d1: 7ft`086Z3a>-Χ/>aZ[G)̮T:n2Mp:VF>ân)+^GERMe"J,otoceI\MP_co_|NU- }wL{"' 5-6V1,.9A\&Sol|@p2ؒzC8ֲ~_*ii-Z2` +vzGe/4$^)a}:BobA[q+ꨢH#XP*=)ԚkGv>|9xN.]Ů0gLQnoZajRWh-6FKssbN|/ϸF>[܎_uG3袊 ( ( ( ( ( lҤ<0HK3\?ƭ#>#mA3D~D2 %B\RwZ/xK^+ ]&$խ] g%Fh.qXG&T[G8HƓ0alu2s21݅y޹dmZE\l>#={$$ێwڳǦDVQ9AfPX 1SOWmoR-YJ%Gp-̨HvȻYV$I+Z9k7ڍ_amz8L7kx`)30H G~-~1KFtIiW7:Cc Q`Be@WwLԺj6[7ŶpKͩoB z`g&v?RTu<P𶫬L҅lpX{4ȱs#H*3뎂4+Ln*?d- VG+iq"zotww-k ZE]FLBXۅE[i,Fcgfvۭ=sG?9i6’Wwz}jԚ !d#heA kէiV(ni@yRj.35t=GFE͉D 1g)"w@_hł*74i &S5J}]?ÍbI)_>|Cִ;f%flu+2+ۭũ +=G5 'j%k ]/%;h {e Ֆ&c Х uczwŮ9g`ɵ mDn=CXnq\ KݱH(9Xe'i8+£q5_^Gxz-#wuySyedmGU% g5*~x~0Ƒe k2\$WX2 Pm_*_L(A\?|)s>iu$k4"3+!lF25:晬'_ ˠj_R+@_hUT>x"^RD#]KTѬPy,j",m˜BĀî[-&$4;~gW Tu2KDӧ0GUvvWzLI?iL~fci4=j<_xN/numu$!joѰ|˹O8mU桧x_X-UilK,*$`y`E|h"4lrm~h?z_?^_ cBB:xq0eGWh?h?/>X@нau,@нau,ί?>G=-o#eG-o#eGu~a=-.X{kxG* 7-4xskJ}"q^ [zYQ [zYQ_8}~i2#dEB AK_,?^:T?^:TgW?__ cRMZ,71'Q< Ԁ^tBY4}*X&}Ilz*kڏ!4m-[] $ғ/ cnQ>pp|IN*Kiڌz,9avN:GV%i^Z=ou٦0Cu NsU>/mgrΦ/mn\z\ۼE=-W/"+Qp:+[+((4?#1kMGX 7S-hcodppzN4^Vlג `O;zמ}Ioggi},g8'zߌljz΍i5y-MIs[`!ެ=H}/Fv]."Ibu7PrӵK-b VGdP]I _9x1е1FIAEajИ[TV.k R>\+6E,Z>_ҥо:L ]:8MlZ/]z(h ^hWMw5X osF} Rڦ-ޥcm5C $ا'f^~6%Eg^XkZuFK)b@@t1,aۅ;0|#߅ qYl-&a2QP_[Ws%m4HV4Qb{4MEy~AR*ioQ3Hϳ#=I[Go /*ioQ3H=0sh6QE?4[Go /*g>9+Ϳ᥾F "QE?4ϳx=&oioQ3H?᥾F "sÞ=Ij6!\~#xgί/ں3jiWk+'k9!AG WZ;V-aV-'jiٔz ۭQ$Hy?E$nj;&#ـ[Zj1k:EdCusIA$mo "!i&?-?-ehD_M}_j$X@ªNGAߒMkVO%'4Ed[Z nAk/L j+'_ƏKtO Y֢N+MR͙֥e|Pti_:=B]Ey*05W[|Iwwuhw7z|TOfL6NGz-G¯`IZ׏}0X$ݸ_ NG= s/^xc\bt1YxN\]R y'ir<J*o;N"w߇oO'|{qMhNtjQn:[x%P" p3 8RÞE?~U&yc^;:^G$}k?hfXjJ+2ldB#-׮{Mz]{Rɦ\ͽD yB8/_'E m܏Uw|S G2#(n:(/~b}9  .e_G?PutQ_Xox2#(n?|ACuױG_{aʾUw|S ^E_}>kGx?ޑC\MgLV_j 2QI98dimn偆"8*GֹP5?`' Ds(S9ՓuBX+!;{hRԎ5 =C@KGX^YEɬ)5m?'շ_\VW~5Ap`~vE7;`FIگ|=^\jZ[Y><WqZߝ"5˟c++5~@?;g~>կ,o|AP'QV|SV:hMd…*WU>xL;1#RKSo, 7we)&UFԌU4m{LiZlnY̲ȌQЕ$nVH ]oDr[?QH"ҧlK1+ ҸPOڮkk>7ڹյ hZb61KmqBѧma5uo{+ok|A%οixf?k+W@P1a15b?M2jZ~o#K_W,OPB>F qjd kavl"[HTXpDH*3ɫ=t'7W8ݝI+[[[k_[~_^jK?so]Ҫpv_ž mht +-cU},3vWe+c!b#U X: cWմo N}_XE 9>cly&ջuW|#ĺͿ Bsqk/;[l( IR62{_w8 vώ|Cc&K-+/ж6))P5v |`[xOCMdZl*/$? ̒O͞O \Sx{JAF$VRrx%ŵkVKg5kCsķKPZN,VC{4hWj[j%=ѼW6omf]%YK)'ל!1-OzwɥmȳbMř$II5vc JӈhG&w (e_VQ^#>c?8~//&w (eG&w (e_VQG#>a?e?e>Sro,ro,(>aY}3\F*?3\F*>}V__z#?Lh/QʏLh/QʾycI*I' ƾDţkf,C2%s2}B\>//g&w (eG&w (e_VQG#>a?~_| B 4YW{/^LNqEm<_gknunf%M.87^[Dmuo*[\s x5\LjARI4p'G}מT-Smb7j-w2P\ar7=⡋ľ.A MsɍXv% }2hӴ#k$CNJz@w߈?>";,)/?|E>w??]]XSG_s~ h|~(", DďUE_ 6Qf+wϝy3Ӿ+˾0~/ѵORi1_Z:沓LUI%]_V#鶚KSwN*nkN`5?:Ρi76o5ͤrqFG,Ho&"oT|F@8"QI4|ֿ'_ß BY}R.+Ql[W"o/1tʼ'=SmΩ :/yMquOukg>W02u*yw mw^ߕ,Ӳwh uH-ynMt*ya*cJ -nsk~~'ӼAq.6H e,\mkO\}_^ϣ-^-9d2_\OXʐ<R k_ I7ÿir.RIl^VS١mksbOk ;}_TU 6tKJeIdppjƋArCvnchK)ZSe89{{P[[+#%Ɵr"ZVKܲ䐷Z26 s ]WIddւ! "brbEX*p5ny?U-FHӮ%ZZ)E5bjM]e FA o+%e<. #?cCẀ]U_jGVj^'%<~&4?G??G2Ïտ*eYU?OJMh0tC=eYU??_³V??(s5? wn?~ϞfT)|8- [Pmz?='Mx44:D7N<naP1Mv Α{eQc0Σl7f̄z^wW25 S]No|,άH$c5k>0O>4Em#vUQ'W}A^ڜf~\u-*鬧bo hX֏$?bh~]N;WTPnX-G"(?N ! ~(z߿;2jH,ߧ ? ~*!wes(?N g H,ߧ ~\?ӿ+9FYz «~W?rCQ ~(z߿;2jؒF{l*ƣ}Lq M+e?ʳr';ƴm",絙wC4mIۍZ'_Z}Q"o#EdXWk5xwRj)62%o#b0Ha85 ~ xgGH>4mjx5i-!`*3*liCr<>࿈n<=OS {u_N,8_'b\[b2sr wt |Iv }B?^Z,$V>fI]ݡ^^<{{BFGCSVR6Uw™zz?Ļ{ho^m k&.+s,-b_*ڑ98ֿO5 Vﻎݮtk5 H>\x 'WmgՅ3`5[}J]*鬯O^CkU?-dt{m0\Z\\坘& Eikh:dž#O-o5 FkdzղKt\W|O7-%p3&[O?5Mu業xLk]xEYԵ-g/$kHR+iZ%:Ơo9-o^լw-ֲV~$:EM8y;ɊF`DzM/ [[bQ_"uj:fs+k|;r4)#BJP|SjM[ b,}>:Tggĩ]\ӢQk_C<2ךO_ÿEz_P?]S诔>H߉?}~(>>NW_(}~(/!?~%;Q} }wO>P/!?~%;Q_C~Kw:}Ooyo) e9Mll=^O|_C~Kw쿴G??/u?~}A4&/57"i Eyy@'$!7-?ƃȲ]:kRy&33X}$ 6]7-_4N>{~Nk>k͜yd{۱sEJֹiZpHLOoq :~`8 8"W%6?-Yr<_,ړO*.QQ\Qz'\|]O$/?S?uW#z'y~!I/?S?O(uC<̗6;F*j~h:xN,Aq!C HF{kֵ?%g]Or+J3+}sW7_:zyt932}ѽ7MM'_4Sk# MMU[Gq5Ȁ#E*r*}|[m5NOWGiz=+X֋[Y@8R*߁X|AC_[N[ͭP&1 o.7 Ϟc[^fugGxJSo"܍)_%4;JIi}sz{xKEJ0|$d}jʹ7Dʥ$U `ڪiK6F/o!#_4xR!.(tky+L0Lz8MDz} ө5: ,R&7.R#8N80W|}Bk`Tqn>W⾡/Z7mt{}^[ u#.dH#@~4WN:mԆ :ӭMuY<si۱xZXةRW#Gz/ŽKKE|zY-n4i(J0q_ηmt15 u֢b$,>D alʁi_+N]N+)tY,Gn1 Rͣi$6o.1q^u~ÒiZ։i:l/8uh-КR `+Ri&[u-?S\CkA+F7u/t%-yK|Vr_xkMz6u r:@T0TtOSYxFĖֶmHq]'+*rևiEy|aVZxn&H8o"yȄ(`W M>#tv\&5hes(`o<0#@vWx#G$[:>]Ojjp3G*EAZXq]S֩i>go͠RXRK5DoU}`IG/c(xjU^ӢI.kŬ.I9UOs\M3Ymvoo4~ eቱ$Ndid` U{+Er?< \YC:?#Ly0*w)OPӴiZnlo#KsaI+2α\}^ ;V<5;$\0Ay9BҢ#P[&4=룢X;6G2q)!}hQE0 ( ( ( ( (2=\ɣ\;oiZ 'ϽXEӮ>Z\O8P~ ۜg۴ ӓO4- eH"RNI Mhi5o7ϔxWK M6Bh7erX;F۟>$&O_4;uZ᣹IOhPGtfF#}?EUʭo/G[og]q--/t aݳuOx>g)}%G6|eNB8+(M +4 Ɖ߉&/_Z4&h"$WUrHb'eF[{ "_H;5LFɯ(Мn|~(b\~Nx? S-RqjѼ%$ UJSZIAM-VV[sVْۻwWѝ澇o+Z|s› ux3RZ2w2xܤ23=BƥeC@] 촫_ >Ka$E`*XBs^EUu6oPHN)Ro_Ζ ( ( ( ( ( ( ( ( <1xz> ѴrK &R".;ʫ;$tVsTuMC*RMRQyU= &[ oeGV>gĞ,ִ^:=i|W 32+JARgZZ3n`u\3 jeqm-՛-;Y$BA = .p4/ |!}N.O/jvK$F7˅TS(epqZZK/};JռSOrkqsmH0  '?xmoR]:}7Mm:(I~teWq`' 2j|552)*k#dcQRsA"/9 Eִ_"_ծlI$v[5r\&W! 'qb0Ӫ5[kK[ b ^70gi#^m3PH|Qz5l5˸;h0`dY#<? DY3UžrLX6#O 9m;wuM+M K.^2Fl/|Nt<]4{ﴻkCÿ@B ('9*:UM3:Ú>6h50 3cᏂWï xQ/&Me @rpH2jz>z.ǗEWt7PscbDJ4eٱ8/B+/'Hm(( l[rNpF>Χא|UZkL?f0Em 0"Nxt+-=88>.:75O!N #^T'nOQ/X/fװk|6PcBb +uNӇ]ϋ{}k}G^jXCo 6?9<=Cj1עϳ)U8lF(,IjWV{"|aJi}Gĺum3lly4l4tg d+>:ExGѮ4V5t {AT- n8$C umo62t MaF`3*:6| "=n]D"yJ#r*+$ [+[ydh_o8Rl'gX/lc1y4i $d|*0$u͢i#Ԭ'{i:m^-QVWd*5c -̈́\ 3~+sf8` egٮ| ]x>!:b/̨냀}y7;x ? _o5y#dM wHnq#sXxO?{բi5YvoQ-9x\+גZ×_h,~tU]C,/uc CA-ɦiB%V0St#d_KAnj'[y_LkG~"aZYhd\G =\ c+_w~ 6SԴ;0,–8ar1jEE< >??[/EhogO2Ks=@$ cℤ<1g#>hњ[iKO A;t? >[{Brڼskt TJ>}G5|[&Z5K[x"ՂKp뗧ItۆӡlEb9ڼWFMi\|ė~#O&{up?bIc$ $ 2e ߫ }-mzŗY#jKEqk1ix7SxgČvB$'`~:lj4 i0xeQ,8&DeBE8k'SRJԣs-z+qyiyj0Hpcv/C]!74h?E~i40 }nr@zޫ ulr?è0xnUC$1I$[`XHU^2Ft>NneݬW\G+ܫYONXs>KMs7y=c+%Pa4J*$Q}QtKa" Wq23QVV'] MjSV."knkWV|_J~]gľӴXu2IL6ɬ^6^:Emi@}` ӆi_mݯj5]X-11#cN1C~&k&M>-_Op<^Ec yFF0Kak[f_E΃SyCZ]"$x's i|XD!R` Ug|;[inڃD$*9+,Nqm> ^wuok GOMK kS;99XEO? ^j;h+}PK5H AW*G͐FzQ?/}?9|kᇌ<]&ֲYv7v1yc4B#fb;BVi] E [I{o5 ռ1$E&A#d1WI=^IPԴْ$[I#xX*:Hۇz`3Taƿ5 i/c 8xbyni+7~ڞtM2NӮH.D3nhQH z]xwOizqi\v'OVR:<'eg}oao>Pm\#3koߋkHu5Ԍn0m wds,jO:(AE\<+kbw엷 qo gegz]ee?uH|7}ZEv~\𫎒/`Il +G5c^v-7e}h-d N8NW{MV7b揧8 iLHAE!Mۻվ1#B'>oڬiC=x;+? zmN'ԖO7hO~g$᧎\;- X@$c zJ5j?_]f@-G:ý/Z⾹> fa 3ԁAU-4*E5hM;Ox㺝!i#  s4i%;ѭj?({8^{.^yU ;X{O_Ė) r?1ۍgڰtC7}q vb{k;2F]A^q.wR˫Rk]&A9F\~iW/|8[{m mmIJaIv>f@-GQuhu55vi;lu#yO\,d88ʕxc5]QiN7OvpP2,3揙 QT^ub 2qFz:]>Pg)1D &uZZ{cqݝͬ IȤ0#"/QE((((((((+~!|=ֵͫ|4|ԛMj45LH\YI7v>E&;,Gů{a}=J:^&k{2(-9M,m4ya#s`dFgc"e(6e_+hBF_~Ox 5/r[5s"-nR2Pgc5 vV|(Ǖ>b3JÖ2x{_X 8c[U!ac=9fWhWl"ex4KVG`m@di"&uN6Q٬~ܬ LjU#8'n_?ʺj3ދ.Cw+ڐ<>"gS1᳕P|{@B|; XV)iNuZLVj?U+? 5ZPϕ?Vgƨ_UEgYcVj?U+? 5ZPϕ?VSկΝh(t4,jO. P~~>Vj?U+? 5V i,cOd~W|1~>Vja_Y'ZƟ?V_TyYcN-Ri%x3sc4H"1mL3 q.?U+? 5Gϕ??z4Iֿ1O?@W|1~>Vja_Y'hz1ϕ?ToVh$2g0 =ChbkddrF#c>pW|1~>Vja_Y'ZƟ? +yYc+ԭiZ)d%Y$ISFPrk$-iv2o@J+^|[hwVD4(cVH'q֦:$3?X<;siVޯks, lw2(V ARYw Sxsږumgjm{[A{o,3Ԓ]PȪp$~ˬutYqe%wI^vvV7˰pm*Im6q-ZU|`jXWx Ǎ-Z_-%ߧ&e/ZOe"[vΡmf^4̱G^0NI1q|vxֹ;rRƷVXXI]I2{X/NoK/EQ~>!Fuh?#ܤӥcs2@-ɤiKZ׵;n5WM:xmD6PQ7ʅݕoJ>L2'#Z0̅#,Գrƚ|@_/oTko婟~jqx摧ROܺ[ƲIۼr|ȼ=}k[RM B?6m~@.dk{X,FC,cklWTWs|1ֱۛ\\Ee6 4$R̪#O'~ ԋ6mVutK8I:-rpмNvl$H~ 3\VY^^i~'g#R>ape@$uS+2+끇*V湫m p,P,|Ɍ)r3qҎCwzo^sq /x;,\ צ `)((((((*ag-H9U(>$F4 qCNb)X[ku qCNb(xbP = XGY=Ƨ|9:b5}PX>Ή) d)̇7| γ/W`s;._s_6W4DYJT *$06GČ-0\cC(P 3E}$ "M" 2Y2ORKDŽ?_{E[ϙ,5Cfs<`(^]=׆rNnWX2hm;՜<˓Y_q~9aM?]glW'!H %) 'If )XYA)8lHQdaľG%KC΂4H CtxR#$H Q$e1"sR1t7kY'6oY Y&뙘րV`X$Ix*YʢзɤbYZ&JUb6yyN'~Ka2GC>} e$8B$ABSJ GOP}aUP* 8vG LI%b~AFJI@"Df!+Ӣ* #0yct(k]LrPepsq_xRr@U ]ߡY] lLKtH< [<;j2 5kӿ맟Sd8R(Ѐ,2! )ˆٽ[f:@ӬmKՌ٭{)|k!HáMNR2LmE*eAaE.LK $1l7k+&K>eq.0%G%ڂE"Ir2!DpOٸd$o\߬(JkH/? x#ɌېZs\Z*;\7/Q. C%Y! 1C,HQ 'B59cnUEi܀j"Q%d€WH7ĬUZN->}?300?X~1O;惯Q/A|&D H(eJ(Y4<2GtD%%wr+_'t"(dAL[@bT6+/7@-*9_cz>xObRBDG|>FZ?+$ׯ.ضZvݱv?kn~1S1">8uˮ@q HB#ViB|&ç@AٱFR)R/Qak.>s1@9ۦc>B ^T>9ev+!M3UI. X(ZaxZ@gM? :7dE:G"5.yٔr_o: ~#R&IklG߬aq;.c&wR^(9JO@<<2|6̆IqGzUi)fߤsb*BuTzF fZ?+..pMa-4[K[? COVm<u)L'K^\_"M'ޏO@:bdw;)_֣ 8@r2jP=;ylK Kȉ=@k|"כ5O0}=nݾŢk1p#)FIʪK ;_P"Y.j V5  ֨ Hz.XmӍ+C@0 ^Q[hRHV2@Q^D3JjRIńٝw0J@׵]KV[טjB_}9% B'"D`۶Dד@Kxff Z#8D"Vs^K>΂t^0j,!GLѦ@[?bZvr6ZA=>dZ(|\6 iQZ%lʃGVr#有1b!H̉!$$)KbR"/39`ǿGC5s$LɰjSĔ)ň ~oMS֫UU98`6e_wsi]QR DR #>"e,(PIS9v1cݱق&C6 Aj!>a'4C@!S&.DT!m)d8^7g<:84#N 3 ؓDꌛ<{ݽE3/_sOY}""Ijր ##tv__V~G Cl,> "i'FL!f"~s1z,<da"W6>q:z;c g]Cݱڶ\6,3)F+(4:'%H/m)1,gDPBcB2XTt}AD!%IH3*'&ap>?Gr;d1ĞNK|N%Zpz=ud3Y\+p(ڒ2Ξww9=MYUdmۑHBpXsb@޹M[#O]j +JGrT(ᦹIx%d=Dӯh}ò"( V途c? bdR,%M99eb M hs`R*2.kc :"(DYpx!J nV’s&wnfN,g0tFUc; R Coz\Q6G$(xS9gήټ|p7d-%D\pzRhog47gjIrxrya!&.$oRsH (32| I ZsvvOys./ ~޽|BXWHݎްy  C<>GLiן>AZ Ȉ"#ng(0ڠBJE2z,OY5aM;d,Ržއ iqOL @JqG+88;;< O>lAIly9,2LJ3z۷ 1.ǟ?{=y"m۰iwYr`vF#DF k A{0H5tsbs^[ﮈnL JَEpeo&ef#!Dp>a Kh-Ҍ=.Xo[BC',-|7[&eJScH@KUhǼ{#} [s4vG& TstIU(F"*eR`̸eGv='6FVuC׮Q)l%=3S(ADb\Z!$Lȑapt 87r*X_]c1ͨΓ 2u^"IbltMe&A[c k )R!w83ta^"rijB9A=zBaDe)sdt 3:v}y{d5<FZ6 "!8cY*f3zEu@a4Z2tV :GP K0D)r^$Lc8_,K2RWT[ FEQd%}S^rFH #oXb"m@e wsNijD5硘rR,JEQH!2H!@:!AtAtJ))&5% Vd,Wx֑@1dBΣw]C; (IVb5b_&BeR;)FSj  s{fӊ1{J]P@r 8pGKa rdv@1qrzST8n)0/lɔhc1RjBbl >!807 ) (R&ĸ$7ÑUWK1d-J@"",ńVHbs3hFyFbݬ5)wNOnfAƐ϶qr4)HUՐGLQbaR&Ĺ2V I9hk)281rLEQ 2n(qRX,i柉w~`ʣŞH=!'hcz-QJE$)HF%"0(Tȴj0VǐPdq-bA ZR563qH!(qC JiP`8X2){l?.B"v b~bB@& Jg:^VŽ8)͠ ;:zD{d迥]JA871 H` x8VҌ%-(;I!!QJ"dۍjk@L !jR$bd4NKBEd20*ZBX<~`RT!:n|ŦLO*_.CaeQ3Yw1e"u;Sےv+6TH1c񙼼sxAy3|RYC̉\Bc ^e9#3{=]zMueɃW4(-!' B"JŤ"q+$͐X<ѻLKn^~v>d NHy )wiA%)"YKĭSNW_78V޼[@9_uTi,a{Rj5eUu!&) YYr2GKuEiJf-\z˶i݀.l6o@nɡϟ ~+<|RJn$ ab$Iւ--*JAi4,Fa&O`^s*㧄UUmX,xw988N~h7m˦ a訦GnsbF dIUش36l1BbČ HQ$ :xY|G>ζ3h7EvPP'"SkpE&'!S,R%n (NHZ)łz b@)mᗿD4֌~"bZ_岲TV}2nl۞M>rvq%]k8i(a loE{#"ui(Rw.w>= LDx￀/YH2`( YBba:Kɔ(ʊmKb 4m mK*!zV9\KMGǴ]XмY 2vۆ>=Myxw!yȒ)g]4͖q~a-Siv;_o/m$u]1v<h"bmx۷o/^hTE&֊{PUŌ@Yh?Ja)q(=%@?t0 HNC>%6cָbdA4N yniJǛud*<nfӁ#7H)%֢9sxx@$ZkzB~_o?~V!M}[܀ ) /}Lʊ7|=>=b zϟ=ߣVV+:@X0;e5 ɑ(L+?mUCԁ"i+OMIE`r2/|yE#-J r /FV&DǴ.ֆ]ڰ")fDMz)%O>ū3TeGb#R %Ui4@ yHe~ևx);єn3_θ tY⣠z._~uk rzrLU$BJJzzfm@)m"HEmլsCLELCdI5TwE&]5꒛C1? [O ЄZ2WJk+܁!w;@ &tj999eihvhՒ6k^| |9;E8Rf3@Z)-9>Hx꒳Rhd2!1v+uٴ=M70LR E 5jfIO ("dNL;  k6CFa:$h\";Dy BDu=`XIDAT{7 >[./-EQRDib1;NF ObH Rg4 Ȃ"E<]&K:hQ" J22$- 9i]#5 "B+l!8.{g9r\5g|Q"@)DQ- dQᅡ'儐 `p4ض[ݚF)|[wiv .xP1{Kڶ*&U!K?#ۿ;-w)zXjH)RW# IBbfp1ښ,O/}jMQ@w_RZI&q+ cs$QA, d~CU7);aH0 2)-LJ1E|HtnhĜRRU6z];fC@ L&CyʷI _o圜ެVXS}/CN G"#IҐ#AP)Msm>`-G{,Bx5n`pZڠڋ 1ji@r6J|fq0C*٣DDG )gPgE](@kS~׿Wh0G?n}HR6s !h#-bѣwIyܵϞl6EvHV[\{Od/ 116 "K7 ݻ'?c^^ -ZX~|(xc^= -59A |?[LǼXc?a}Fwqt| 3Y~??w>6JGOk2?b%[(fJ!ć7 ~C(7m7/9??û2]L%5#w @u4mF瘈>dn,{|WtI۬Km2En;~;߆ww"#%Ȯng BIʠ0|ђGn-H#;32>OMcckicPcfH,+ kͦUͳxcRHYl#L5kO*cRO0ʢ \%Kbe3ϟ|Ų4)FEW}qQ7?ݵ'0_o{UהeUTjb:*c!LiF:hG$O?ͺ|' lFY{+r˪MQְRl|'6jxJX1;PL( aI^h` IG>xx" X>;|#H<I芥10Fxs2sPUJi$]bȉ$Z.K9;FrNh#Vn͆&%O:кWTF f2g5g/?G?s@ OU9Zp 2KlUmYK܏rzןݻ|?{ǟsqq֖S޻gp9|) (6 #jhQFa1ӆmӢDFb 1" fDRҔBXAv-fG CPݰf13PǠ,!nց &XkMJ 휹7?͙@aٻ7Cө;v`8Cb3Ꙟ&U{vYU7)w+q@%*? YJnrz_޽CT/MYx yړ:0YTgsՁstޢE%Zgeźi({뱏<}Yʌ˅1r6{\u~"80ZSV5Go~ǫb?lQX?>Ctє+}6_~ ɄGGHKΟū/IOAdG|ݒ5S;̳\h׎VȂ(Uŋ\|)]|θa?MY >zE0 v;| *mK=Olpr.y{Dps#]ZQRyZ ]ܬj| eOHB32+K#]*d¤yB ηf?ˡIdt76Kp/wS9Tl1QH)oBE򌏶Q'G{"hXXW,X E }g-be",hfMx3EG\3x'F&*:j|xr='T,EF9gT)XњyiV<4Ę!ʣ6@Mx\ip 'p '8% IENDB`fotoxx-18.01.1/images/fotoxx.png0000644000175000017500000002332713222767271015173 0ustar micomicoPNG  IHDR@@iqAzTXtRaw profile type exifxڥm$ D*H5]CRRg,bOUokσ>~%'I{SQa﯍}}PE֛͢<64jrfraKoxypw[_߾,uro;/߹?K3ۃ{wT6.0ַ@ߍ;'9PG5fFLQ^ _\.iɵ}&G 9f¬wv~s^uZz4\Т0ylqrSɢ?__g97;_\0~9_A7?_>OV07i~*T/ʋma!x|ߍ*g+Ɛ&UIQ1!g! KD5FZMI=ָs8Zdl7V?^ZkӮÊܮ /^]G+6mAks/hv[} <<ˬSMm9Yu嫭F(QC£EeC]>۶rcO;W /P/ԝ?Qc|a* /fI"w1K=1R/6!i%둟 *rυD ߠ`OޘB|:[e.5-nk]ud=5dejH ŢX~ٻ=#i&ڛl˒ f4B6܋P-IA9yT5X!sK3!щ

#-"+x$Ar`mY׹#^j?~| ğh/Am#jQ$d 1Է|)kMڔIm2!m1A_3|/<uV.%q6$0h"ayr",kgri⏇z=c_[ɠso{=J?, KFR'8]]}[N|?s\ƺM^Ivt.6#hj~YxFtΟ{ \[6@# ȯ0mS,vWn5 SׇN}*Y0[92yr;׃^,l6,xgM᎟}c3٬!"A͍+g!rUq).'RE=46}E|!⏈j)O YjXƠ$Ss7_MQLoVY*/(\IeG7> d׏-'w_7B)uK>M&>%ψg;5l.ї`-Q gWپVb3#Mk]PB@L0! k@?B+߁?h'<8tX/nt_hY q ɾ"U8)؊ݲ+FSM>_Ca{ub!W!du0\ 3ޔum]8;[[_>'L* j:D[Eg姛sYc \n'>;>o]_?,t;oVٮ5^ CxO<WIzv2UͺQl$EEf{i^][h#\F qqZJ\ͿO_,TW~gß4/Ց%|KO[7}P72;"kkЬ .|^MKş~$6|QC uFfk ݜe[5ÐZiRk\vڎ>3^=<݇|s 5__X  Xd,۫-]7HIJ(%vCk_/?ύٺu兝ͩBu[O^5)-~Β̰lso,UwrF|oN.+Qxu]68b+$s V$=⦙wEi{˨4/. -2(X<5Ylێ3cڝŵt\P_+U@lEr_ſ͕?{>~t7-7[<0 0#k;x*_|-]l5ťۧoD|V26ClSZ_Gw#M5#-nCGQ\ 4𖯫աuk؂mRɀ id6smilo"MV+㔱O#ym$W+l$RB0p{sF4 U_O~ kK ^WDt=>n,d >7+-I=|O| ]xy<m<o"yCu$)lm [gYHNp~׾O&iee0z}̰XjDL"5)y`6g=]j7V>:ne,r~U)9͘{}u5y/oOx[_|Q&Kxf oSo KԀ&8\زw4Ky~:jK/u 2GcӮt1 B݀"[eC_ )b+o~_!o>)ҴbkY.]B$,ࣷTGJd>&b],쾢<#wz\ߚ+LJgxgWOW'͵Z{e,Ī.Ve} Օ^jya婷yp$d$;c*¾ƒZvrCi{;ZwqT ^4 ̡#iCKeRa롪FXyqFYW#[y+^} +_˿|iҼ}~4MD`#N<2}##󍶞?iMmJt}'w=O":bMxmoнGRf-̂8zZϵ񧇯w2JF ^F)$(HzM]5{43O߁4QwԍwR?52ofw '~,F{&"ӛN6t$Fo!RU1dNyw?/χ׵_i'OK .pb"70bBjx#T[%揪iWu9"@t) 7zJvz~k%kҥK`4]KPhshZeߥKۤxI``-[~ռ:Nji趚.]pyJ|t |V')pG:Ú7u+5/\N 5[Xڮ4c Y# t8_ګ|Sw{$eNe\Xo"G "NܾvDo+}R>#H%.6ٳ ScK$%Iێφ$|މkY,њ++[9FE^`Iwn7l8 _Wh[}+ɴ3OW%|1*Ci9^_֩-r5g߳jeZhzΊ!WAo$Hs& =I'+IσδCZI=o22K {s#llnOJWō}4}x.{&4(BۼU%I si+]k]*?6y&TPY$$U&Uov>=oߍ4ĝ#Gº"4o,"d8🀴> ؾuå] -4,O+34q 99{~"fڷ 1%LML:#J̨H z +~2x_>"v4!7Pyi)@S8r@ \Mn/1v~|4[蚬. CVyh-H#xeElROL_ite5[kXj=V;}|Fѵ- ×ז%ֻi նuNLvpvpqWO<85x ,jH%g_7n$ddc"wk$gJK- 9l_oͨkw CH>S*ɂ/ĸ\)>-|?::& ^|/FKH쏽+_?/#InXC|Gk7Z~2[kmuӐwH̅DNsqׇ%X7ϊ^&| 3jN;?]hw\<4.iݟxՈ!IW?z$~>|iqat[KN]F$RqX S~~ou{,׺F{] *v~b:co?j0G#B*Ѐ旂63Jv}[mO|7_&^}GLkYdxaH0?.1u߆6~^4'KSy-[2 i*N+Zg) .DR[ԿB91Up9N}+ÚuP$⵽ ϙe88SW>f|m??>۷b?qkf{'kyKY5r҃),|ymIuśձMFO*G53\K;*1ʒ̀yJh~#t= CuKEJ|beTgƟ;6Vִݝ/o,BS 8=ʐ9n6KDY%W*k_7jpP_Tֿo P #*MSvb is4@(A@OTJԛ0q@nŶ"=6^>C 9e`:[×^$sgm Ny1&:x8q?iFm]닛ː<\GUB-:Rs<7B\$Wx',Bv^bC 62'yg8*>2Zt3VG72~ m[p:v:}#[ݤl@WkNr'̈&; *ypJS#{9Rx{iHQAGmmRתKD5=`g89ďTr֐*OA䏩G~䚱D>ZTTQtX {hĂg;zH`02]*3p_aP#[-5iH(5>`*iH5W{V,zds#A^oiu,2EmkR |ç52JQ%ӤYJX㎀W~z,z_;ѣmU?5xuq^$s_9b5EO-X.?qk!F^j  D끬MFZsJ+"TAL縕!Y]Lu|a%[ԉ3V>~~Qi?5=>XeVu__ޡaZ|W_kԾ'6 47sgl`{(ZxRQIkk98ZOtivFZ I꧑^K:pѸF*OCa_]KNg~m%ڨ vV0AZuݯ4RO14vg+Њ_)`Vq,7OIU'p$seem/Qխu/h%=![q˹ @5uJ=a-w};^ּE* 2#OnN9FgsSjV ѯHgw{THe],jX<coI47ivb&me1,x1,'"֙զxQNuosok DI$ ; reۀEBuKFV 5kn}⿆o<'wY'HZFH?D/!ܒ<s?綠u> XZ[-[وL$|㸐HᖚO_ 鍮>Iwqbf?g)ȉ^\Ez$66G%3Wq lC)fVN.ֻ_nK'ٸ>?1oPi5/j䬚]Z#u}c@fTf_V@8!769# /^Wn&`&m[.rv̷+jͷvZ>Mçk:žbh0pdr5mFMSP,ڵXHǍ]s *T4n]Җ~ _|4񞇩/AG3Ŧ wBm8bS=l _㕂;I|E,ki"M&ͱ*+|Oú^-X-mؚb$#;~6bkܼaX-k-tV[7In[a 6y66w_c<q=O$nE#c8jKҹ5h+%%O| /?~jx7XҬMmF|NKbn:o޾'x3뺗u A]{yY+B6&0GsbZU/'V;&hgAvK|\W#<Z/ڵGL~BTѼ9-OJ|+i/wo%+쵑n$(hy+}j÷?9aE#J$xrD8Lݲ3F"H莦L*kuDl4oO{+Oe_p\*rʐOյX=>[! #D` 'nWwt+/H}g{ Տī5Mi&U'OGW)4{0~|_'h( {{v4Se'KwMW".PGz3z?xi^Ze6[+f7GE נkj7* VKOghz2"8 XGb2Fܸuf+쵯k+&,^X_I!* qs ?ig4"/BӤt{^ J6Uv3#z:!_j)RkV0Y$gfƤʠdrj~TcKmZ_ ?j@h<# E]Sʉ&>_;JL1W<;[k[]RХ<{Kwpo߷}w]x/%|AkMP4mQ;GһK9m']*GKӍykvȯmBOZ]_|Ox}cS𗉧.IOi:&Lp"*;⧃xt9FW"|1##nୃډ;>~6]]E>a=BP:/+;Xt;ɣX[;lʓpH!㭾Gx,u*x~U]5"q0!!R]+>H 5 ?OxAX[&u'hPYw@WqU@@4ݎqW,IrYmo[ ß j'Aνw]֯u mVuLV5XȔɀ+g6_/Wc>xROIF+ l$|,G˸s^e9|%iMm!R@ (c!#) ƙhnIm,/YQC,T0*Ob+7hrtj#K\oZVUaWz%J4j̅&+ޅ ]|fxNK];W4[Ohn5-˱yPyn/.-~%bm->2ŪÔ!C@Oźot4;;{H^D^5!*[#~fAY/_7|s+mVLm<doaSXMvS@܌KUO )|}j:MM^8g62Xs(݂6 fDŽ!T^\oD kE-,oN^T#,+aA'Vg>>iq|N>7ubN6S^[au-r1"a!9jRgK!G6}XfTֿoE~EMkm_ YҔDxp4qP14ri0})0ƂO?ceTHK"h%`sON3"2\KT1Ѕ<ל~ZXt5@l QO:ʮ%p {w9 3EVtC-q*c\a #OjNutiV6)wy Ovl⍑"Å/3zԤLdfY?g;+/?"CLr:Rfcx#2~qoֹfTzu֒\sƮ1\:;8q H"'$`` a|:k1-ދ+t``yL;1)h[$F%Elm+ Նq),y Ҵ22ѱHfݦ%_JaLg+H+JD;Rүu?ݵ֏&odqHX"a );N0?5_즪z- t*^0]Ogۯo~x_;XO~&Q[+G@i:M]jx5]ɢ4Ium̑E`]g_xT䭞WFN?WAjP@heÏ /ՖşU}#^^QB4 Gr[.}*o&2J~FP{ C? :$ơo!bF֑d$C_ ӧe)Y"W37~|/X}e kycg#?t}R>88%ͼxXQ_ռ/cx"wf0"! ņYq_T|s'O&?H&&hyƱe#r.K뮾n)[S_kp0 :d"0UGV{v)ŒFx@nwCs,`pW ƥ"W_95jSgzOy~~\VG)~Zjut1W!&#^׵fx?"㯇Tg Iqlү!L'h3[V1\~?g'}%zsFЩ֐qQU1!c<1\O2y6{_)q~P!O.(L)Xw:0$,(dh_ @]A/#zKԟ0+PX%y>\y2~~ʅj9G+Kޒ ?JĺpIlTd}-wVxAYxq>!<# `-5?9^wn?+$a[ Oc~U ͜rKQ&$+0ۑB3oQ^Xd>kzRG<=:!o,@Go_Mc&o8=}eZgSQ-X/'8BgvUOxw}7} #jj _Dž|bC_|uӼ6}[h"^1c򜓙|Q:bLW:P<`-Ĵe81V2pK;?j+_ S_z֚ z?iwxxYnHqwq7+FIɦl,/#*[=O%&dX6F$ $_՚F~G:>⵼OX .gڷvX>1Voy)ïxºݪ]6eEU4%&~>Zv]OP6\h핂%Qr-X' 7-+\K~+;JX%akq:$Rjỹp9RQw Kߑ~g>xK_|g^!2ycqmqr˳-Vi0}@>*<¯Ҭ6 ^hSݛ,d8:HUX5}`Q]B[iHQ6h+AaYw?+]x) #ZťΠڕBeTb*їNҼ̢=qmJ{_e~xgo ou/zezmEvsymc)X zlj=Mk_IX~w†x^@tFR45_Ek68\?y;2Wo? ;=ĒA:eOfp#V1$B$N{RZYy7vdvoUG cEL |I6:שw纶tU^ry@0s^?.? ,4ˉ^~壹ZYX,vԼ;rJ@ ҿ𵥛^ kX,c5 %9Mb aL1Vf cxSMoiK=BvϢjbR*|,@&'TA%w_{n\2G/_↹*ͮݽǀuX|GOKˈ1ᷕV䣂V!SM=t/ELxD R^0.mR9]Q,WW7Bk&SJUk]Մ3X.D+\ĉ,j>Rr7c"_>hN ]7U54[qhpĬ .2[Zkj8uց %e94r 猬d ,kbghk<6l^E&?|OhΖvsCn"Iyp"rs`~;x?r,Όaq>`c?/"ڻWӿgǖ5Mv$9kX~R BO5AKx_^УGcK:]N+]QHmF2,m-!8$ 6kݗ"^-}υŧ5/šZCug3&GIe?gqq^#M㻏k > [7Vxb9U=B>KIW?h?-x V]{ |54/yCq姚 " xW:߃-c.nSNI-*M#U\zE|?կ4+=rKGQ ӓN_-7 y^daU~d;7|=>,t-7G֎{Asugye]dW\XUe2[BP][vm i5$~]z&Əj"uxsJVVM%R2傎sr?5MkxxsT%_x J(ΙF%i )ƷO~E޿i:^ D4 RdY(N-v` )$+z=MO%힕ag-ܫX\JjX8;9 6_q)m2 7k{Mco,yHy;T-􊸑ܬ2+mh?ht?ӵ]'S]bƑRfe!ѶI^{_ Kݴ||l|3xW86Z6} [E4?=F;m2"JʰFS! a{;~%_<% Eaci2Zj^Zo/Cp;?/| ľ)[2m:;Oebe-n`S$k<.<S\aԥ[húNFBY]6ĞXV#xo 2|/>&5_Dh-Mrq+<<+lm}ez'ܺnHaM;9VO/tM!Yd \n W kBֿ[ \I͂3M:$[Ʌ.y M{%oX:/$-O\𦍩{Ÿͥ}ĺ-onE2zJG!~Ћ +*In: ~%i_~xZ.gjf3>oo?bau\W>?͖ixZgқQӼ9*5axAFo3a\>zZ.]6=JUm$-ZnѽiRGSc[p1wU<Z[+{5-*lI-c@W$.ۿvBkE| jS\OCgIl4cĎHwM=_~=*]_k742mq41;Hg Sԟ l[mRMrqh]fXL! %=ǁuGo4HcvDduVVWVR5_H/lڊ懠^.LJZӵY$I].#bS9"& 4tj%ҦCLGu7ml׮|o/z~Vj6zmޯK?uɍ0ָ߈]>.z/u?sqQ6-̬O̡na/lDV/EMkm_ -W}5E7jLsJIGQZQvܚh?-9+ JW? nm5sŠP,red2s^.%uj-*p$e=VF |_ _7{˧k->H{ myBj昨 YKsu#p)\fZ TQMUMӊ~?՚9"8jeÏ Y׵yoWAjŖ~הC_ЍzyD?u@z|_+Ⱦ(x^mZ:=rK(\E`8Ǯ>n]]2e%cxa 6ŭG&H$ I~^h_^xR;>9䓜}m'&$'NXz"8hTRRS/c%O)]٧ma}rԷš<>Ḡ{י#>Vx)i ʭXVlW9J\&) ViЕ -O#o])RnkҶ-{D[lnrZզ7q^udbVd6$}0&٫+s7zO?hhZeo>&[jE( b|Fz٥J>`_/zOP׭lSռl9hE:wPOaڼyNߎgx[<8/I%oi0hO囏%q7l$ۿfj%Sö~S񅯊忓RE;ۋaزy ܃f|g:MDtM1-e'giGQ?ǃ7gMP(iudN+f<;nUmʻ $hj+7+M"8'cqM7qc?gO|+B:x7B1UER Io`q ~_t?vP 0#2V6e '_ΕO:Ưy\}bլu(ClxOtaakZ%̹0>W0 (2կ4|e{aqy{YX̱]-VLĄf>O\ӼM^/xZ*ܾkGuA$o67*0kaJwxkF=C^ֵ#tk?2ad |ij9#:MDc| xsk Ÿ mY<[$`^*?:tk"VLQ@x7&cQr7P+0:-Kiշ&6D_۔W!y)uү/Z(/ꚮj j=/E0g./7x 4l49SwaY Y@pB ?Zxĉ 2iqJ72#rǞZ<3<[KNִ!HWuKU'Yv(WseXxvo{wK>9)}-WS-$2&pTk1/xŸ^.~e%Vi0ݠIa$~7Y}ߊL7ְǩXG) +yd6>*G}dB9WO&kq3iGhU\$; rz7kwV%$YzIG񗌾#xOE߈SL0Ny `O搗S)<+~Ⱥ/xOFL>,7pJA9}65ndU=b?A>%Ñx\ug Ѷ1f-G=\R$w+e>ׂ߈~)5i.o[xa$ʊm9Kj.OAmS4kxPx~N4^jci>1.5]#RX#1ܫā\32;ds^2%qy1Ψ `@XlM9&~'oC4/)]gCiuilg0βNEHe1āFkcž DЯ==b-J# !Y#+"8lU'⿈WiXj6siڈUCy$]~@\xOßCystcKNIc]JgpkuKOy j~kuT5?#SeAr~Z޻,I\ٻ>CiZgKU x?R[NHc"t>K:a*99?~E45I%=j!ks%s8y@9Oo4nuk xal+Wci6IhZK"zwncE+GQxWNe=>jzBXC4MBZ;;[ ׋_`j4Z nvܣ>/=ICtw:ygzFg+yaE$*-UF\xeto˝PY/ QѥA"`p]A` I&6k~ [>mZ]nsYo mgcu$SGz[VX|.2K |]GUg+K;k3(Epm1c]cNJ𥝎Z[i1 m'ƭ%Į>Hpǎא;Ggm+MԼ).&-<]`c^H[V*]f}}ߵf:煼 ]R-g)I$IIn<ɗ2#g:|?>xSZƧ/MsEsX7DŽt)d|Nοumо6d bX ~$_/~#(~ihMWZVIeHQ$d3f/RE]=;*k_7jo *k_7jnVPܙlLIF=@8| l Jt_z*UMNg~^ֺuxWOMIX|t,m\7|zG$xK6βRKX" n@;]WǶ~qosRJ]es\~ xOWk[]>y[H-lfRЗU\.t=YOkqU!S*-kMJxxMx߅Z֋Kuu}DV*t nᴭOݖ-]K$zmofq}gw/d, $Gy@x;ء[SIA[TT dvI}ĭ8&jGMiSeٯ_nZtmƛe+:Bp˻ s^T| мk۽RM W*eHq5=-Ee 9j_G}RLuҙ~nD9X1tyPέxi#Bψ5Ko&)tBTˬ3\a[־nֽΛ+VN[kKE<6n$k!n 9ϊ6> F7uөZ8&kG+%g5j1USFe2aږN h|qӵcm\{kUV{hrלKI,2hcK6ok ;6jLMf!S{W!3vc'&-lJBS\lXRzk5zTjsSΘzJoaUZ]E{GgQ7_Y^QB5}#IOw^(3KXʇHpώyry$tQOR%uDry+D@"{qq,ή]y޶;6G|c_L)F'54棡{ΚE<#z1_&EЦ$_X<$#R!A|n\G $sL"`cs_[|]ne4;RXvї྿^QrN[_o񸿩Ќyҵ5P1,/t8Wii&/!6n0$ٟ|UMWs}牯5"ڇUs:7ShՌ YB <zF-7wWSS)! rq׶]/to\}9hl7d,8qڽ+׳Z|=u,TnvC4 V߆u+oF.t.*m0Lp F2) eѼ9mGAmtojw~VindX%dg. .q^KxP4˟I`^9%߄HHB㡤< r{֊h"6>~㺄V fo1|+qi|iZ[/PզeyФi JwK]2_ :g$sB. $F=ntZ_?r Vc ,u [k~!Xg6WKN 1|&|+C 'i|x~²\mMS8`xWzoni[đ[=֓Vr `` @hkۻ{=&%h!)ڨ<;A^{yߠ{)Zw^:>*jltM>CXn.myRe[rnKdh߲?lKtN0h6-|3n_Ժcft5孜vp7Xy6\x3XVzIu0B GUY<ޥjSaeԟKE#gT<)`RwmQ0O/{< }<3iJ4M&EHٚSr%%! Yƚg-֑uE}a)UoyGt^&% ʑC wvaMg{&ojjȈw7E*2GUÇ)~!>D{̙糦 [m}~3zngg%9fHVqNPmV9"-s|a9oxT׼C^$qH oo]wN]LY}:)Ipix,tr=Hd˰dTCAXOxdeݵDQɐddO{JҵVy$k{[uџR'/|9~x|3\6",2G!ܦە+16=xPeohxRMK$fN6lyaRd'!K(\]q{ JQH;H,Ć؄|\I }5:qy|`;m;siBOWj}nOy|ZoRzTq5oٍ1Uy>Y | _?-NoQ+t.я"K!!UYUK 궺 WKxcx.b 2'##֝x_Uu p 9#Ք1d8UiGɖ}'vy_/7GW|1]#H!7rA+asH>ii&?|cҠ?U$g.DTck){g엺r#Hw?ndjzjsyb!G*`RpeZ}"|յ$6 Ф::]3 x¾?5׋46 " uh2MdXګH!V@CC j*XE d6p˒z,5א:/ ycP:t<97 {% hwW?OxfB_kuC0ty ]H0HH0Ww;3Mrt 𝦟gO"0xrU vP3xgz޳b̑ s% lٓn*0^9?.]KK6$V`P8U#/C Ք~WZֳ{cʣiouv}%e%ގҌRٲe;v[6dyG|_4^'W[s{]iX[ZՋyI쩏gܬTƽRYѯdo^YK 2 wdZqr箧4cGovd7r~ ++k?ml.L΄pI  c;煯!\lY:  S[!\]oL2G dR ,9݈oU趹|'rҒ*N^HakZv>쿮\{kO|@X,-H~L\xU/O<xzdžuA{tb-LIn,҄ '$o<}rt(lt]J+o~%=x'X_4Ѫj6^[]$brHA^wc [r+~~jŻпKwz?e_;|6{?yx46c1 iPU+®&C\3_9u-&aiYf L$`*`ɝNI|j[GUI4ݧw~/"FɯnGPw_|ask%wQ]$5tʦ7m $xo|BA6^iڕ{1V7L,Blt;[nmzlo~x)5oeiZjwkkHcUrk7<1]F_ZxW\[ MJm9.f-vo<鱹2P RE|^mwCkO]b{%2)#Rz۽6#No jzlj̒; w- pd*į#t]Xg'Q/cgĺ]>wyMڿ9E"xEMkm_< WDw&1T J SLJ),C-^=%#[yP_?ؤbp?y㯋~x2M\4$* od_'Y]cwLRX]]XA ?~!<𮹣i3jwwW6Υ|qXbo\FX{y|? k"Fy 2G^0Ew@I_K-Fc9qޝxYxLR rZ]b(B }zEέ駏l.N~03_+QΝܟOH'N*^VkgEGxoVӢǁ5?_>D@2JDL=ܼ'AXts"G )|#SVWk\˨Lsc򮌆Z_TsLsUTԁ)yF)Qy'H0OD:pbR)Cjk'!!]"dҀU1N- >aDhq+k\zZ}T^ Z5Ġn>TEWV!M+,֤b??o JCdW S|7_;\g]yD?u׫הC_ЍJ('W]IWg\ɓ$}0X1bA׻>x6)mw#FtxFaȯ :kiKTȔo_/t??&YtY嵱WYm Tigbn?1h|Inʖ^. 8O.=+H5M;Yյ bT$eF08y>.+Tx_yyfΛᦅqZx/n_Rx_#D$.gl5Ң@ vcZ9rrz}_MQUØS&|46lڵv=MpzV<! ~G,kfJu-F1RHkYߛ5w:"4XdǵcG1-1Tn νR`^b ՙ_O KܱIn@U$ 0cu^%(~pI]d5cN⯇%,#yO:"qМo"x{iZA nc2|p㝇j1I 3lZc-[?qY<񵇈?++m<+*G>k;~"Y8-}?ؐR#$ ?oHf'mi&~yIG#T}i?*UQZ},4r¦[vFy+UxCm{w)0lt}{Aۜ}A_zh;o[7ZZ4V[G&w@ֺAend毧/F{uO9qORTk?==g[Gس8n<&8?'ǽGk{[hVڏ.o~7HJc) 69+6 q 'P_gPkڶZ{kql\;< o [^{۟ ktM.##v<ҿB(N-J8}WO%u9_W_Pۗ7Jimry$YڗY5 mQ]np|=+BTt&Be9;~}[WCXӯƸEEm(M5NU[ o=խ#RU3Cq/Bȭ:ʐ*YCB*ʇR''yVWm:[HRojsYZR%6*1$ay^եԯ|9=ZNu-J)>"DQ e_U."J\ʆ' m?¿M["Ox<4$DWmY3$oT`o꺶E#x[QAZ\Z;|xa-yHz*enT/2N (ֵ>|U;_]jBRig[{w B,dI5So tYgq%)"hV$K($ݴV;uj](]-73V;--UݝM\I6[BI十pqֲkͫ&-4XI#Κ] q_~[+|Oodr~}~χW} RDWtT>{^mmKQj6hR܌x?(DhgĪoo|孵~wujծ4ɿrЊoYӦp// <~Q."t?gݻiԵ.J5;`?1X2$^}_\##4"5_GM ilН>q8BqSMP{ȤսK|+n>wm#ƙ-iiy=Y'b޷4z%B]By.'i1(P1: E(Ԓ+??/6(dR\cY/?8|?XhиFpz4fonnb34j띤XrT(E\&9.|~vx)n<1qw=5fKu6yRH!'tQu" <75+KǓsP#V |Ct/>"4uu?6WH:=żQq"_l\͸9<=~X.<-wcqy 0?,{` n< W=SEQ*ؘ. o$^?-8_I2I%>^]oSZlZDŽMa6xX"x} ~QMq"eGG;\O 撓_~~iׇ#\$\M8yQp@u V[W2 Ee v3WM[G 5 _UCn4w/1Kե[_~yn''Z| 9 %۴~8|AZŦxRP%mF$YZ5؛I,FNI5EeǛZlh9(Z w?7)oc\Qɉ]|%;]ֺ+/6z& ]2iOgq(YU0ʲq|+>j~~^lT)QVfK=凈FZ؂o%F0:p&6emx{XOeIlxBY$T=H _TS|GGCO>Z2Ws˪Ee Ko Q1e~Y.N;_e{FkuI"cl\hd\)P*om,,[!k"QwdH=3DwktAj~o_6y狿Tֿo?v"xEMkm_-2ؑFqR t׊]$SphѸ݊q8&+[&+3Zv:c졈=qYAϵD%qrD~6$L=wpJcj^$ {b@ ,WqFᾃ MdRI1=}y<}L 5RK]WRLSmmuȼukwJimhqI @xlpT.>{&,a)5V1}ܮMq ~cPa<g2e8硯0y.Ouu-]޵ɝNI蛞\|;Idi!x5W## 띿Ф tPfÇ"c;1Jݵ Q uO*ʹk\/<2Z]jw$Vu3\qe£ l[ߖvFTjvgXMGbQtvy5.:Fx#qCVg,'U O>m"m A:yi?#7;[7ƳvbI޼ֽ)&(ZDȐ2ɍs] if~.Qx)A'<{֓ȓcys%ZIg *ڸ,$!Cp$-o-bM 7,p ?Rҡ3F^uprI?J|GomKY(/$mE~~χ??fZon,ne*:EP0(((((((((((((((((((((ylZT[ܮqt+F>SHn[yn dt|<*k_7jn%*k_7jna8h.E'SHN(SYOV+UG-WW%Xx0sS U|mh#''gY.739$2kOwA O>0uTY<'dqeXyqeA9?0ªxk&hPn6?(d2ᛋෟ÷i~>n `cGJK(9R֟yafUI{gI>bøn>ZcT JXUvC}8D?) =+h;|^~g_Vx^ f JĞ1_?dMju6٤{e8%y͓i8fɨ^S0IeUE\#5w} ]?VҥRZRFݬa=I`xx x-_va-go~Ȟ5MүiKYl/g8h˩x7,|AuK[/qm| Bu&7m#Fgmw?b^a5/}"*4@D:Eb L p儵ך?."JVA}O) 46sQ# Kt>{ xe*ҸoXyu uo:$`RjtSf'# _-4=Mr$b<6XrɥK# ,դ|Zu)[ ǭQxFd.rpEږTTd gi"p=DAu>8 /uUt',봆5a/s;{T$e#i>3^WAjA{O`)U{~WC_ЍzyD?u(6u<w ?viln]  3sxikQ`Q3}3k/,R5 jbrN g'[+Y藻}I+^/|ieچx$Zm]yH܃\ [|"5IZ5Y\iھ!`W?ps[ #?i#`q'\7>hmַl2F2^iKO<I[ 9]wcsKM^.{F56P#fe?1889z-5"SDׄ _Kk]K$b8$o- @ʨarsg\ѼGxc쵘ubIb2(3`G#w6ib,J42~bNz:Dy+ϪG|u5)uk.YWϷ.v a#y5UZãyKĸ I .KOsUV:lt]X9 8]qp>I+t?4ݞgm{"Z9F9QKȥNvZVLh}f[[FxՒY%`*v7<61wxn#!bv ,9N:s],Z1i F2 <خW93MZQ_G,m7Wܱ]kS](eV&D!bWYkm94%FWq#ב־G⿴>f7qY9C,lyBaK<=~Z^F̥DC#8 Q[S^+b~) ެ\ZWFAbT)bӥ=M}>qT3tqi i^/U, rH-(6)HlKb"Flj%g \4fWmOiS~ZT3Ny;B*z״~?qQUZ{c_ڗg S{ (z~G#Y\Wȡ!S?lO蠖c+&>~h)h]XX>]suUO_?v?1?Ə_?v?1?ƾ_ o,?&.f{Pa<><+UkZ\.`m?JXL$ JRC8D3|-miAo',rhm =OčOj_,>ڥ}FrI|cܭS Tc 9Vi=>KG20І^; yPUdּW;ۃo;W <_wDԣৃSug/3kkj)?{) ]9~Azi'cY3aԄ Ҧ֖e {YE3?o.xJA҉^ɕC?V Vl¾wqun_>^Sd]=>xJ?0&G?٥95 k\) ϱ}(Ý- /OOC.Q{?ϐ/ʙ%`/#IcuZUͳ f?6O\ʌϞ$9q*I5dJͨ{?φ#O줏;2p Ҿ~b I$d|A q#mۏ9Wd5ғ?Z]?3sqk?>/L!h,vHWc9'c_FW/CNJpR]PQE(((((((((((((((((((((Mdk3-Y~\FeO,=jWm4 ݬl\F{!sp>.SZ)[Wgd[#je" q݂Ǡ6aGoSϨ-4EM\QEiГƽaO#q@zyKS?h-_*9O!]2O~ ҊGï{/xUy {qJͣ_䐷<+vSxb;[BQ2pz3$51hP|:@F z?*O>'qU+ "刔=o~ F0۲dHU#dW<ѩ|SFw5ĚrGq$q,pmxDg'b(\D/J$U=*?c1H#ljF:p?EmGoy3j#/`eܫ' s1_@3'ƍg^nm<7c3|A'%cך? ߉nz7텣ٛK au󨩇ƤV0K4s: ڊY?b_F, 5brKBzG@kw?D{WЁwۅ\~⺯?kn WnэU*G=uQNz]r\.+ch%^vOỖے cǚG=+¬bX/$Hz(O&ӗSf'#l3Co8=2oK-u> 9{i:H }*< |:HЭOF, \U?A_ѷ|o+u2Q]ƅQ@Q@Q@Q@!KHzP}5x5w7t O,oXң^^ &?׽W/EQRtQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@eUFc!3=ݭSY㉏+RuZ=OK"3+$A??W_%+$;OKWR@QE0 ( ( ( ( ( ( ( ( ( ( ( ( _R?,]J(z~@z|_*9mE0@L⤋R}G5Xfd9dRJ>."3xlI$47 sc9g8+gπ&S-gXu-":(MIPW~C <:9x[4U+,rsgoLױ~Q7:ޓigCy$;OKWWHw]薯`(`QEQEQEQEQEQEQEQEQEQEQEQE.߇FoVX~^QB5}#R-z.JcO|Gƣew4nr:n^>e,3i^_e{;m kk0I>p{t> O|`K4J+y^↉ާ}ދmRKpy(.:ב^xoQ߉&5+w}zzπ .'BK%}^WY𶜽pjSKػGܿWҴ[M%% UK?A_ѷ|o+_+EQEQEQEQE-!@t O\7>TXnu mc_'J{x/Xң^^D&~ =QEIQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEiCu'0xIsʣGoy[.wc9Iq@e#IߊuZG*D{$ES((((((((((((ȱu?5+z|s"Ԡ ~W(4+Bd*d¾S?mYjD,H*ڥ r(xUXR 6泺ō2/qkثK~doskпl gJt ڑd?޵ao!:GE ,^xYS%e#'%5ws_>4Zk5mBN}:=>Yyy 1ov~Fޓŝt2MY<H`N>~:YI~z(O ( ( ( ( CҖ ߺk'jo~ğqY7:݂"\331P.$s^ &AK/Ok/іaG'c뷚|>[9&p6"_?BF>H' &AK/Ou[ЭeG.o*l$G,!?ƏI4 YB| k/??u[ЭeEs#I4 YBiU _Z"0Gi?$?)e 5.o(mBEȁ$?)e 4IRk]V+YQ k/?,ÙIRhH'?𺭿VU _Y2>H' &AK/Ou[ЭeG.o(d} &AK/OM#__Z"]V+YQfM#_G$G,!?ƾmBE𺭿V9$G,!?ƏI4 YB| k/??u[ЭeEs#I4 YBiU _Z"0Gi?$?)e 5.o(mBEȁ$?)e 4IRk]V+YQ k/?,ÙIRhH'?𺭿VU _Y2>H' &AK/Ou[ЭeG.o(d} &AK/OM#__Z"]V+YQfM#_G$G,!?ƾmBE𺭿V9$G,!?ƏI4 YB|Eov.M̲ 8EưoF$G,!?ƏI4 YB|a>6kKi?$?)e 5?5ߥ ??/P>H' &AK/O?/Qa>6€$G,!?ƏI4 YB|a>6kKϦ?$?)e 5}VR+}j*.9ADaϿm~)hj€?W_%+$;OKWPQE0 ( ( ( ( ( ( ( ( ( ( ( ( _R?,]J(z~@z|_)dbFw`$RE>k~-kq'9kJW]l3]xzTTcZ6shK[u2ڂ zdFkپgnmež~5]7Z@?ge!U$rZ {]unyqYƳ\FXm$0޾ҾQE`S(U}rWOwY~_gKH!w 0>|At, "2J D“$nkEӨԺSEǫ_Wvߵ:Χ.K(w{JY-158'"DLȪ#g _Q587x#ZX}.-lzye˸]WBxSRi.T 5{[~WGW;m>7HQ6m.fw]SMs[_W|ܩgՔWr49mV?]BXE cfG cK4ƒXv ck,_e$QvG cK4ƒXv cgM C-7P/o;#0V4]ƒX7>V?^P]%jX'{vGƒX7>V?]ƨzxcPǽݐ  c]ƒX7>V?]BXE c]ƒX7>V?]}׉.in;(P5\Z:Āed[]7>V?G(? ϕ/צV]_q\k,X)&WAFvG cK4ƒXv ck,_e$QvG cK4ƒXv ck,_e$QvG cK4ƒXz~5 2[ȬRHe۹ui#$sV #Ao| ƏP~+_BoƟKqǰ@9$*BXEadq7>V?G(? ϕ/iб?GBXEadq7>V?G(? ϕ/iб?GBXEadq7>V?G(? ϕ/as;>K/5Z:ă$kr<h%( #Ao| ƏP~+_L<M7+oi# T_/׮.9W?|^(X?_/ף_zc|^W?믢C_z?_/׮.9W? 9R7 (c7Ě?$>t;nۻf+sc_ 'ʽE(lx>$Gǎ3Oɕz.g$(|=I2QE {e:χI?&U($WQ|=I2g$*\ ?:χI?&Q {e^Gc_ '?$Gǎ3Oɕz.g$(|=I2QE {e:χI?&U($WQ|=I2ouYѤh#֭WC_ЍzyD?uЀ'TWwKml`#?i^N\`pnz]^ ΫŅ i6? k9rKĩ,KNNI$I5|=%T47`pI޾Pt#}hVՏJ d,ԢA%CєDQұTGxhlPF ozM]Q`:EƂ=q<$c0\cJ?cz5bK>=-5#grc17#)NH9&bO6_ mxZy^;0n&0J:LuMkQ޿Lu4rf&aQԶ_׼=|Ϝg7 ~, ;@/<=,mm:7KQK|穫w| NH5iϩX:]ܳ92GpH3Ҹx_~EM6af Tj72m(l`vm\oZ!ԼOj77ҵ7jSd/}E`1˹84N}BSi^nkz&O5 O[{k&9C#\JVWAS8߲ۜ i#޵ǚ*"Yzlk+;ԧ桧XOk/$0y R~J\ xoto~#4DRIf[Dy *84/Z[Mr^ߡ|W/|C ֑/K4o_ۅRHcf+-_)͜p~K -cɎ5\ _xHּeH5 h>,[YlΞ`ge98B/ 9ԽoWhZv~Kϯ UIZO{ff12Hg(no/xmWPOxNOKf-*a$0.RiNR՞~۟m+sև[nj}5 ?(JfJ}WpyZ`Vv U#QS#T/5?1y|ͳǖb$yYߊž|7EǪ<ޕqvw1\`7 |~ZX4K PҘLխJ aj|M,^)|34+=OWy=nl-u*u;8$5$;bnra^?79kqaå/KE^)$;q倻Tn<kz^Dww3\6N/ ivBDkw9Œқᖙ?j< 3G ,SN"c-ur0q?>Qyiמ1jl _5mGM[>h[rVW!7;i*b2\yx)igoOn<|Ś[ơtXS{;GktOŲ_M|]fK xٝL 0_|:;EW6s܈qK;(*͜`>4y$ִ.7E{Z%n_F(((((((((((((((((((((((k!F@F63i^i+)9P ̓@Bsʨmwc^ e5{P,S@Ȯ[GBmkM=5w:6 d'=ױaHtÔ]BQ)yNЖcx5Bf~B0 SQ\y?T@U}+wxoABq>Xm$O2c|bI|=ɩtW]kGԴ-Sm"Mm JrDve@/4a[{oUuY~@5}Qm4Y]^14#bۣrNy5AIMiMFlHJ۩bĞ5__5O(R6|xk]yuTBy^f/gUo ֻ>kdWS|us+vTeI$RW?_%o(MYg__5]+S/t FJh|LЛeL5@ <К٧[m2Ş,tVPG e1 `cpl"f/i{+%VZuCh/#W9>Fס- ʾ <MfJd/;ZY.fu*c-) 9\_DaG4:d (VUV w ˹oZXE+$4f_vsG/o^t}ovYuhHQI&ܰ@7NM}!иݤ'ohOΙ:EVM-ťsk"%rd`QW; cN ]R_ƥhՓaX>f6Tpsk[?-p~&Ѱȥ27>jZVMn쯴tIaąd$v}kޓ%RnI#rRFFF@"EUW?Z|sUj䊏cG??_7/cD|zo>7?Gs,4OǨK??_7ύ9QK??_7º WP]L/mz|sT}_?T}N5K??_7ύ9QK??_7/cD|zo>7?Gs! mB75m)fSoe*HF)2 _?>/U\S5 L_i֖7?@O"^1Ɖx' }_?>/Uq?x' # h/Wm|sT_úTF\̗%Id )b{NkR_?>/UW>46ʲRH0=##q\"^1Ɖvo|n*>7?@O"^1Ɖx' }_?>/Uq?x' % h/Wm|sTkj2#!႓)pkЪsύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P*sύ9P~QiX1y`~FmnUSk( 1@>ƾxSk( 1@>ƾxSk( 1@>ƾxSk( 1@>ƾٮ:URa_ٮymF6|C/gmэ[-ߗF5nԀQE(((((((((((((((((((((((((((((((y|m(뙃2!pjq$ΙmZ])~WC⯊0 ? xš}SBKֱO\%څ;+@m?^?Ӻ ׃4mkR }*STkmp4RyVkkSӛNo}: >Y-m8+ U[[ =?Ie}h+8(°#\8Au"zr_U4b} 7i'w q`?-.Jlc(h8, ~7To Mq^*|:y<iyrpe`U3'{HԾ.hƇ{ <5hWR,HCΒv h~}q@zύ&7To Myz~^FEYXke5,P$!)^GCrl/4ROhP7 )O7To Mn>kSm֎}*o+s3k)yZũ|W'G剼9{]&5Ak|=aiY"y&ٳwbrqbq+n?#M Z(Q@Q@Q@Q@Q@Q@Q@Q@nFT1@h9 FMʶQ$=Ns~0c00Q@_O 1486 2448 C    #%$""!&+7/&)4)!"0A149;>>>%.DIC;C  ;("(;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;td" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?]]kERɣhFwna0ڹ!xLQ<@::#BjJ+ MbG_gxe9j֪xDީH5~Q{c򞞕%>!c?Xǎ6PR <帵/ =s!lz>Qu嵰b~ s^$N= rq2olm5&Gf,=*V*5'kv, [ki h-lfVs p`隟i;JI[|] .a3.?EW'1?ҭ_L2hoaPS",+.yN0E?!XɧD?=ՇXm?⫐#nXJ=Ue{z}uj=GԾ+xVikfUsL#+nZH or>E>?SAw<מ<BF tZ]|=j9]=K:KXWF<{as↷ kaU駶IP\S3IsϸGG7ōz7-?c|~Ӿ'x]Nߏ~BydTR <xu=ԖCUm9aƽ+; y_GTuMYAwF&_m⪜)Ys$<|X1>K c9ӳ?[w534!F}kxh5GTiFrGsLY*6?O>IǑi|.*޸QԾ$k'<ɏV$"JI?T5!n+ TarsF%;?4e mR~\f9 ;T$rg aGz.ק?4M)?\_>#nZ_6[w|g*Pq+VEw2E=7.@O.K_%gx{כ:3M&HhzR}#[,ȹkZ\Fl7OUwg(s-IW-oau,H8>ƏiZ6ZcGm;ƥ␱gֽDլnd ՛$&s,K}ɴ?̿r_>) #+:& i+r*Im w||4/ϊH221Ezg/φ~eOi_9^gEzg/φ~eOi_9^gEzg/φ~eOi_9^gEzg/φ~eOi_9^gEzg/φ~eOi_9^gEzg/φ~eOi_9^gEzg/φ~eOi_9^gEzg/φ~eOi_9^gEzg/φ~eOi_9^gREm,F'QrO(?||4/NJO`?̿r8S- 6qV޼WY'Bjv)Y=8/*EҢSڋrjDۣ'Z#>UvYb;.N|· \{.p';_Os׷-۝ϐq㊅:C4Q+_v}R"JUpYi)ecpsԓI|I$s[ӥ|BqCd^R5W5Eq򥷸0#uq9'4cηH+YXT0WiIs\6 )E;囶 'jXF9eF*X_ZY.i\-Wߎ3#`~J6-(&GzѹIc*Ì+3mB5Rcx^o?ޠ݅;^T.U3w o۳u47)l*lqMezt3C*$`*a3Sab]_̹_8=Ұk~{ea#)]8+Nn?ZYX z^!6.yUb3TnE$$s1<1+HcrQީPqjft8Gy+՗vHOMh U[758a'l9]!kU5F.<_7ng֭DlA WQW;>ῇo̵I- ) \k~obP +ӵ&!BkKUO3[jQ]BjqQe*pAv5:@d#(R>e @*+U[# Eu0^" 1A+I [Tnk -I%^k#2G݊tk/b+Ԃ*ԓtgi#2:- pqXvXDzܒ%SvHŢ1S3@3B<#8H5XjsTVDHޥңy^9C:SFnɢdH$ReO)!84A n*I cjEYLFzsOgΣ^ 'f9 @WYXt,\qןƪ/W3-'5d'tHP3'QSY^XG43Gs\Un1" ch?Znk=u^?.:ҮdOki`ZHۦ:abAbYSO?QFdY0V85KP 7نs^|3Ԙ.tIbT2ҞGE!*8{] @jh܈4j?W/s޸< O`ַ+#֪3icL,FzZEZבn}vl;`>zF N\)J[w'QDY7x$3G.ÌM& ilmyzf.DPr*5`0y mn33m㚊yX9{T;96($Q|54z޳Ƿ54Y۵Mh\*3Y/Շ/Y!|ԑH> qҨ4}AO4lr>?MσtC[赢5{/I C^5sgc/&7 /ʌ5kVsQf'?Br5qZCb$KY?[ѫ=0[L1mo1.7r*I"5T^]AvA~ ф-wE?_Fpsz{s-J]2NTj}1^xN@pLӷ V5j2F4,"!H\FL<;w1yC61m zVƝqI<Y΄tRXb[ I9ҹ۷W]HJZޏ6)[K 2O5l0Gd=yl*7s3kVh\r]tk_,??8n'i{#Fd |?qp. yI`+o1q Ĩ ]cRڦm.ـ+dvV#]Mx?X~nq\X9"#qYJ}9+mK2 X#PVt7xzL4qX~5%VN0j)4]Bq5coO*EcP\x#(AR6t+bxٽ.O6GK=#SPqSDZs*AV#)VճkT-#ٍ}kyӥ\'tVyfg%,{K4¢A ͖1\EY4+4+) <F?hR@ ^!1s\9㰭ddoᔊt@SN@HIsVGs4I8óBӏLf7Q7ɩ6kC|#ǃ41[赢<}#Bje^K%); ulɝgܮYJ/!#m uxE09 Gfdn V{Du6A-E2?4a|Q^}b,  ת/V:7 n@okЄY̯wnNeH~jcar.bsk#-/?ױq9pzSN29A #8b7 w ~;EՌDy*pr_fO21u 9x:TbU涷I*N5Ci^ Ji[k0@Z}ՔV91?Z3͐oK+4lМMhm>P1=ZoRԥ|aP~uˌy\EVPKMQC#m U 0< Qo'kQޚFcJeMFW0kNDY$*3?W7AĞU-5\m[y#V8JqzJzETB94kӜmG\dc9Z7J(N֚Moe|L 8[`C;a t  hA]ǹ[g6O"@1TB\U80>)bGr]OEK6iڡoDnw+u٣2FNvy~i >Z r5c,UǽTVGrAޕMtr ;Niwz>QjYplwS2r|u|HF")NnciVv9-@i7hA#@:?º V&F* +`[[!nQw?OcGn2TB.i1iTAl 1ԟ?ԟde@[8QHҭ7f Lդ-zq߱#1λYFPzd4+it,̑mDf?c_hڒjQ;~>|mB"3+*Y`e0sNTCsP:*= Yb,S>R0>A{sV۟™d杪5FK4aHd0Aj󱖎fjnZXu8I3tGiVmg< 5R+mP͕x <ۖۜ^_<{jqd.:)mBp98wPW=Eɜpk)nKWw.7jvnF1Vp5klʤj+[uyDG*`8Nr*gi%˰OҜ76"\}MN&X'/Οc"mGYQrkCD%͂Egy z~Mw09ͫ+{wͽ9 xJ|Zꑅ/FU7Kl>kq,&U[#hD\2lJ%s/m8n<9/-R0W5-}=)n΍a@c#f䯱yVmasɬP3ƕkEX[;hIM0'vؠ+NaGF&@_J*2Zʹ9u@k9%}"tːֶH[$Lqaܼ\B 9ऻp>*A.)Β4^~'/umF u*3QIms"w*B9Z9^A=ȭ+IгZ\;WT%EO\sjZjEb7*;vvX'4VsSY6zl6$"UjT3"V|A>EBZHAJZ\ͥqo*g,O=}]Λʌ+3M}q]|Ex LDăN(H4c?)r!s]_XCM6GA  )U#>+GN5!-fy=6_Z>E;mlTQLI.&3,-;-ִmuKX6 hT$>=0inh',ҸĒPք4y Wyg>{^{*'GEDҢ2Z6ZH{g6ݓ6-wL om.[snܳnmV8-c؏+;Xq&'̙ǻ(קּΗ?/+.W ̺qxSNyxH .d=*th0YZͲSTVex98F-3nnƝ9Q.YpL1KV,éy N1cQѲ.Nz*O0Olw,n(gWynsa$E 54YsڼĞ#/0$cuk4cP+5>I|OOt_׭% wcR|q3M+۽$"re)npV`7ɰzlY:02C!{wFs N.>R@6S+^=:B *e=?¬Zd0ܣJXhP1vթOK-9)}5n[,8xJ+JX洼7B@w48gHH %PN\TjW%GљKO-$4Nqt" PwZ5&73 yfgylǓ)m.ΧSU̪Smc9dGVZw y$S@6k(mC+G-ϫ]1FZ.3y#JSWpNV럗⥶ፖfteem|`kMƓԾI0 6X]ߖݶ]E3$EP?{${W-\|Xd'Ok@C ޴s]Lڶ᳻7+JE֛y{i+GE-Y}FU!a7K1!GAV44ŮK1}1YN~=5%(݂i鑎+f x -I *ofҡInn[vB0<;=8!I評W<18Y$ AV\*'H۹f,!xViWdA.*=1 H $g^[o>6UJqR`˕-˚oc hۤ#^cU0] -8HR-lcdrƼ  ݳ>\Ovڛ,5! I5T9'1m_?:\Q]dNnKZ,>6XpyWI vZY*F|?oPd _V!y &wpLZVR^6u0߁Tϴy243 *^3sv3З\EEksXV0fpX58  8Ȭ]"Df\+dx효ȥd`7n>dj^O΋PXlD[F' ?Ui E\pZK Y!YO)R>fjuamihYcbRkQFsJ$2v%cOz qm)?:2H0͎X)TH +d2Wuw ߑP4q:Ջ^d;7 ArsNEYzR.O#Js8&Eڮ;Aoc(ؤ8?1`J~#N[© g;OT/vN@+{GiH&s8p˷{YReΌn;==W(ߨ#ta9Q$Au{|VЪ\pIflǹA)fe.-k_)0Wig5ԑj}N+X1ho@b0@՘)e3nNz~c4Yx|բV+[bY3Kk[[Ghct1T=)ċ"H $RQ5HlJ"Y<>nد%kKmYmg6I`>onZ_i\.B؊u%;6;r2L9ko2R$ךfo41 y28#ڛmoݔd{ Τ)js1Q61SGqp>tb8ʰ?X b,12D$}zzoXJADX<?JóYLp#үCuU #Ӄ[N009'VKo-O チQMǼI~ (E_;}fHf -NP/#9wMXMQR0rl¨#XNGT0 T0#s^5U6Lb)P *fUG$5$m1N8ǭaEɢ]Ӛ{|өUy^IיWN `.r}u C$)%ʈF2~^cQU< z+ZUUy\\V2\ iuOexÑ#62=jN C2;~uqab'&>ЄydgR[,l2\,RϹ6'?e24|U=+oݧ2O#?*ث#ަs ?:pʓ*kNjhJ q'<#ůnP mֵ[8ĊÒƽAL\GI:''*\yp+ t]5c(#y>q{d HB|yR$QΕ7E:5 {7s1>jڈL隓HYaWUBN@b*g$n܉JtGzVm^HQz3MgBOS & %deD)M lEhck0+*LBSJU~j{fWCr n/A?$ ?›nOnsbstYhqx'=GNf+t/&h9!|eojy%f8fEZ1R0 Y ֌CzRdqy6*!,yZdBjE%{(M#e^=9w$uLx*ivn[W%c7 @.\t}ǿqR6A1ga\ D<G^޽ZHX33]scqk8kui 8 o`(zK-E ~T*Gy Il3=Cy8#A$tc8Ly72v`gִ-4eBgq+䟔pWK sS'.j. m30~7q]v豪F1A `֜km܀?!Sv4lX[pH@r*+8arGcS ϶E2S9!ɨ*Kt'V]Ʋ 1<`j ayeojKk238=jeXާf>WѠ7E5a\!9G"M=3SHޱg!9B@FTB)@=*FL'، &4?p5:A~ BnWwz*݃M];(]Et\<~m#[ĉ:Iloqd^ V8lEGly"IT>gr'Rv mR6+RS rX))*#$*OBh5y9kEVF4\UUc;23efO_j5dbCB_|~)%)B*]XDvȀ;}ׇukTMQϖ9˕u(,{c 9r"w1 "mD+>Az֭yh'R:+QF4Tpw'hk b"ݚH*g%NsXiwTW&VV5y^< ={CEK8`FGa֪g X"UKƒ@u;Uq1|9v?01ڲVѓbe@SԨ?3Hߊ5mH!\L0DIx<޴ܾ͸R:gS#hPU2s#n${vi5tWcBx\W1fMb$|y].)mj#9ڹMj%HVI8l]w^FYc?r\zTLF G\DG<6IsҲk]M39*_5өA?t%\,9'b0Ld1ס8\,WXcffbIqɧIs *8 NnzM?Jʸ%b庍gy*ㆩ-RLLV,O}2:dzs(E9/!oӥfK=Z`Z(cֺ\ wzxuPO'y/$r/I ]Qt5KYq# w=ZC8c9>]WPVܷC_Mig)ySM~a.0̅9? }85PMpɄ^3֑ jOO=!\#$HNY N:F c䑎3Q A`8#%I3վpvߵVpԵsNln!xgԱ3HlNȬ1D^H]TX;gwQQ@<̡lwf"@`廟ZVbny;fu( RD(#6VrSs Idb FL ŷ܏zl8.'g5̰ǻb;#de=Iy)e'kzS䑢\FGz.T#t=i K;ۯ@-0kb7:lJ5%Pd邹#Hь>NZGmg =Hd HFwy֧Xs4d<࿚Fp[vHvx{V c9 l8<ǫ+/[~y,yȫ[Cᝄ<丒ت 9jftTR qwy8pnģ0=W p*v7֣eeA%U&a.0K$HX=qL;fvqOj]؀;uG"rF>]jrKpಓ1R%ц\I*{*]x ztTKNF Zҕ9 ͸`VtE$jnldw>棁 r2ܑ,3{v\$|~e4X>93X>XPv?VT͹9EzZƜ;/݀3Q?2cȊ |RV6YB=띐1q{q}MIaaUmČdc5i냞ZV'<{=? l#NwG3dըؕ8 ܏ǜ*lrAqQܻ sDro(bmzwP$ם °5f ]vgj-B6VnKW CQKX+F-ۉڙf' |umo[+i\A9I]SNRұMMZ@)#y1?9XMy䔓(tFaE@Q1@bPEQF(Q1@bP6܎UB kYY+Hz,pkRc%f~']F@P?2T>?ѯH"g+ʩkx:2byQ0j q3.w|:T!'Z& Gu8{?`*_"|$oKƇ`:b?QU-3KG@914WdfEӵ V,vn$ 1Z*]8{<3 ?yƒ? >@޻ֵð-}سX$`4]0,Cu7ֽr~({H4ui%┓FmwKCGVw}7zA\KqXX+ dsȮR u/Y{)/.#, qGQ~AoNjVQ)ow`{*}|?ZZ%c }D-ۿϮ縁" ngkX򈑈RXa'ޮ&_<"[=kVQT@8hP+#?Г:"Y_%\5)>d6VI$O,$rT޺- 3\JtUc&gò4/߰C۩o"9:|doz(PFR_E@BF20y?OOiIj1Ңeg7 E81Uд-@hQKT 4=6Q-U?M\ƴhoLM{n?N~f(@/Ɠ'|?ZTSPFjJV%l7Hrɓj8-Y_h#'K֎X1L?ZRpCxWC|n<֏*{*ZSp}SCt;jA$ $hiKCQ$sFY˛{o1IE#<܅AB_;-A5?O_"Ƶ3d*S?l8CY" ?;{nn?Z|9@[$91me2M kdK-$F>l/tx< lI8 ~dv#4`Cd@,zuK6Kʦ]''L_7VY^wl{X ð:Am xGBG_ hXG\Εj8MK 2o^ѬmBٯ.md-)6қWvpPQ,<;hp<:aQ?\ 6ן9A2r!lG=1+ӆq'Jtx49 4i e;d(T 3G9k}KQ}){ʃC5|=.qf?N:F>ʿ (4 3҉?S4<'r9<~E?e²(ãi[*Ƚ'ՏuR\I4]1'$騪TvC9OU ?O*U ?O**?Wx+??Wx+?Sw? aWYA9~[3;?|6:I1vq+4>zӵ{+l&u4?Y mB ,.ܬ c\&.]$f1B$?/ßbݗ4|<bɳ {eT-[[m7}x:}*-!eTR"ek rG@+I`w2?P&E-u?Z\xi'`'YczsfjMͧZ| +'B῁;ke@9 Iß\e]9՟{o.̉cqk-dLHP͂3g_-ޅq cZ(#B@"eIH]` YѭcW )y>jմ6ҵ[؁-N,,6n*H4/l]=4e9 +(m^4Q|W{w6pMlΦ_ܸ@dbEoP) _IQ _IWWEr@U@UutP) _IQ _?]] 6pF#8UaEMEQEQE"IF$UaFE>gSEwf.K$NBvjhvg$pAYl ``+C\|xN#kx܂ȤH**~(((((((((((#4ݴ(ʫwJZ@ ?Jko=r䘌ֱ97U Wl{X49 K,b+P٤i$9QKpHc7Snz N R1us3HAX6Rs䱀c m);GSs疝~疝~GS VP ²6CB+D؉BPv=疝~疝~:=疝~疝~:=疝~疝~:=疝~疝~:=疝~疝~:=|EOYwd!vL'uG_(g@5QEQEQEQEQEQEQEQEQEQEQEQEQEy5u=Qu/c3g= ^'\BzA*sW׼k:;g$^yiv&IM 2=Ev]ƛ,RJErSc*ell {1c$,RfTu#E?(((((((((((((x3&\k†#=Ű?W%O":9YLlj]$ʽyA#jZG=6O0V&]AyirGdž܉0p'9'w0 OebnNܜXkp.F)ojQRUtQ+}=]:L<_吻d$ds?Q)s;ZPTdH9[Quq <| :5r7@k?QԒh#L qv5T"܆k$7(3֫Oc'$H%E[*%̨5=YYpr6jm-*q9d]E +BN(gk(Iڮ23VKSJ{PAl)ַY\F'0ErcUI#^* b@Mc~}SdX fkk%ȿ}oҽFM.DA}N&uۺ,E"F-yJ}B12F+2cQ:hVT;h$sUo㷇$pBk^,:ԗ>$7q#sGgzBRup;fYmSVk2uITN 涍>Us 7npV| 0h~)<ͬCd0;d=5gnOҒ8\B17?Zؤ7AkeD#9f⚦ٴm̫?:B8|Z{ `wv;O~ a:JyXZ\|Î}nl Oɺ1TMXUor/8aU}o`v9 T.A2BA-5Bk`O?ZL\j s00q)+Gsrao!F*Y>F>Us[ S⒧Gt՘QE+? ~/JJH>_Ҁ<֊()ѩv :mh~ZݬxoPX[qGw9ҠH& V&?xmU^Lmrqjʚ7dV.t Tk4hY]׵sw+SGj*0N>lh-vLxrkv9ՒSeߔ!@#+V($NPOQȎñ=qmY@Ņ#S 3L*VGOh2g ک4l>\cף^ZNkf+q`xFis,uTJçJK1 5fBqcZ%2pJqO} rr?΢OK^Ϊ[i(!$W2 'v9Orxh1Q=j цV\G ֭;CN܊Zoݳ*WI 8VT`QEv ?G_(g@5QETWW ik-ā"R"$@:24 tY(@R9ƒź}Ɵ q\j"Ec l97sҀM– +ލi\X2K=lAj8P+n-B+VEUܢD(",I3@ E&G.6r 34+*/xt1\Q׏_jĚcxs8w9Ӯs(V.m0vHF⟻ڀE&j3@ E&E-QEQEp^;_WIyy2C#"O#:#Kg7Ƅzq~OEsCb$*e'*OcS-jpQD{x˥fRP[?֧7zӘ5K$|Ҹ%Vro*pG8&isKKR{Tz讴د#7Q2\ټj{M5h&6PQ|?1,>2[]POmEXV!J2ۿZVpCr1cn!ǒF?J8^v*ܺ3tkE uVɖ%q^uu{1{sY7u'kyF(V<`V`bt5 OH2J&U]A 1,hwl2T{UWLnqRE ԗ4r` dd痙]NFF0cqh@vJ;mvEcئ排(?D5)b銚ӽ8'=(H8cDLmjCfUT7eKtyݎnXƪU%QE H>_Ҽҽ/C44=h ( 4Fx~ gTu~{ǡʴ!vLodVgqN=?ҳ`Y)f-wecǿo%VccHtӧa:8:X_}Dq 20qVnp0r>5_Lcӝp`svjon_1+ԋU8ͮxQ8h݉* TnLP+W?\U)]E& =bPB%T*;C$zǵ\W #=̍3JĂ 7#*4]0: }̿fi>let׷\*:A)!8ԺF~_v$9dŜ(9bcfyϵnk~2P}1 \D$#54wr9䉗֒fF7g;{Whh[ g\jrL6A]ՑEVaEPgJ. }/_4|%_KEPzWq?`ϭ]e]1ZidX0>39K{ [t# 8]8=f)tخ솔/; ǜ{+cMԞGWӌc psMAO6#ًCһg:5uWIJk3,`}1P.9QӦN#F"b9sx2LbVQ b2>B(R2T rO<ѿ"-O(Lm_3`6Z7PMJGEkox~klu[êꚼڜR^%c4'Y>e|Ger7vzb"k^l]i3u(*0(QEQEy;ȭu*fpI5p!VK/wm[j}8WCq.HYr8Ȕ])bbDNkԐkш?n_v Pxs:'G+Ǹhb7Y}U&A Hq:RO:6>|=-Ux6t_QM]r]fKtې`0zsǚT%rg|`ֵ%Lgs, ʒM2HvC`/1܎; y(2ح )yl d&#YTmR !:اL:K 7ێ2wo-wlYn'84k[0Տ5Mhޞ6]iW;I-NPFriM2{S-hi#9A4P8cç[%/EAuDGTWl+bǷJglH^%Wi҅L&3o?$#R=N7#b[֖v#PsFA\~#f[Jc%CvKK9mJ ˸sou=I6eݔ<s#dtkI< s$MvvɊQ&Ǚ]JV/6NA0EݸJr;i-I}.mX$\r ^ʳ"~"zsU[V,#\K4s N={U*aUq:UӶUn"}ǭq}VẖXmɮiMwBӎgTҞCDmܗ%lfRmٚ)ju'{v 2'֗N"ۉQr=3/cu]]@,O*M%gB VWh^2U݀{aiZ4 0'X:֡wsze$+籴k':!;ľ"b [N˅U#=k)|c ~j2 $;{Am0?Ν)TEyԼXND(u/O OH%Į,ֳmͫI,G3\NqQǿ#9kmvIpdF@9u' ]*InT*uCSUO5B{L/7$-^qMIihtJŕHQ1+qޕrd#Aup=QΎӔp6ڰT/㢆Ȕ گ%=+h'f1{#9䲎^^.1Tn_3.AȠ>G;B2Ǵh%t^e`0=kh'گ0͂}]@BⲼ[߅/Žqo]ZOS(n6 H>_Ҽҽ/C44=h (`77BzG ?@m ȑyꭄ2*[SlKF8Z$S5=7H#T0< [G`a4M-wB>;Y:7RJ׏z 89S<͒"WH>+6g;5ΟwW`v9T-Lo s֋ssz!!Xg=j2}VE=V64ַ*WyK#lTn ]*{t:m֨ͦ">Sji)]]ᾎ6#<@AOZ< a\S+6`0Gloh?#ڸq6,*ޑ")QQl}sNt)]r ڼ{=%uZȶA6|Avouۂwo}(Òct-(FQEv ?G_(g@5QEQEQEQEQEQEQEQEQEQEQEQEQEym~3)ǚhRb+{xmBjʼn.pz5xYݩ[w) _5JM \<9*$`9nkV*a8*}(֔^is19>QxOH1e+NkxyRV9TΫt&k M ydfgu[tV;_1b\Wr=y= A(wOZe1}7Fc3ZY_LG4ŸR9dekvڗV,YWR8=ib0\?뵹E/CIinVkn5EqyW4W rѐs+|mq<2'U? 8S cRWf9M74\PWb[Hڣ/5]zDd P2l$3!/th[#k|NJmO5TuQTM\Lw2A'(BS0tbϪ.P9־*C~[nNL9i`'0Z}xc@afN6@;0H ysJ?*T[y#L4s` 瞵丙JFyl=]_ ƞ%BzzL6)ީJPkKapq*t~='0DJ1s\}l%^?WWA_c<֊)UH#IEnYxT^Apd]k|mqw [?^߭p4d3N{ǥVJ|:ejXh#g McAVsXh] INN{dUoe.AMOLrj"دi&,М =*(/>^OZ Ncu.8=i`XJR`@ sHK9*e|1bQrG oz=Yr`gpl \rŀҤ a5դrhf9#:&I̓Vn<3Gl8nOq*(ph1>JV461~U\GGcr:!?cE=41ڢjwWWQ["YKQrb}{P1uwvzlRɼ1+~#9kT$R O"3\׭T3VEi N]/eOPƓ`"1S?JE/cOPY?*?쟕[cOPGH'&3ӿ?ʯQGvFeχt epk7σ^OKE\bs5z/ iRQip%ͺH;XEP+нg|σ^OKEs_|BƏW>z?]-¹w ?\;?tP5 /Y'hs57 }w8N~a)k8ҤӢި0iK}K /Y'hs5OA:%N4fn,PKxݞ3UIɰYśdJZ(F; 9W>z?G+нg|񮖊9W>z?G+нg|񮖊\;?|BƺZ(O_ļѭ#pG((((((((((((((((E)"+ah y8R%L@ >Q@}:mcYRPpv!0$g8EE# wh;7{Q@ | @)WVب!IPJ=($TDTPv cJ(((((((((#43tnvAi-c 6Ӄ=h,* eVA#Q@.8E`m8 483 386 0 C     C   [" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wjl}'?DV?ڽWc?}'?ºVFXZrk{ IVW[o|IxU5ѫZմ[>,ىLX0a]_yW*<Ob79UpA@> |? k r}^}o^]Ν`4"쒈+4vzc<hx>$Z߉M2 U-|S+,LĶߑH8'Vmc?׵youG_ tu?bdE! *݅b~ϖg>!\AZM[j6Ƭ6éRˁ; +ߍ;?}'?½[o?F"&jqgu[7c&pϵgCMCHX|37n;{kf sTggoO!4ξ}G+5=cX;V(٧qq7#F"I+F$qV!=[ <e\yb5Qʑ߾)[1I Fڤ$^+Wⶏ0xw wr9. Y z_1h4.:L:V3ZM|?z>EՓQ.2IԎ8;EP2MO|n./+c- (m{wZ$m=պD.hcWv2\o-6UX0ON1"y|H5FK\VcWšIiFGSʫx~x;Z\K,Q*J(R] 5f?],0FPas'ز :&2䏸8#"YHѮH$4@GP@MA >)CPHN6"<+յ\xMm!;vtTEPc2sOfBMrxfQZa ^ڻ&tk+FmnPGC85Un-gȇg_ּ- ֣FvnO'.{]X ZVGoWVM?&f i>kAe#kAe#קfxW?<}hIw?_ x-w?_ x-0GO~ùk_oùk_oY?Z>}k3^*G^*G.~y_W[=W[=as'֏Z s5׊2s5׊2 i>}g;UG;UE\þ-|#Ūhͧ1+,w 9Pp$tZZwk/l-چ[ȹy }kAe#kAe#f>(x|?Y'{![kycܖy$lI$ L>Esqox%6I$S_mùk_oùk_orφt+GM3[tlD@L.\IzUu_HQhf;;v]U){lU8\ ^*G^*G>)x0ZGZOc`mhԴdEK[fqMF] 4in^$Sr$ }N~s5׊2s5׊2Z_ג5Vk(#ƝoqD! FQJrx1 [~'cçA,5ĐD~ܱf$mJ^*G^*G˷_guW:uj$aWlj vyTԵmD^EJĞ\q@H^5;UG;UBMl sOGkAe#kAe#f?<}hIw?_ x-w?_ x-0k7:Eٸ`K.#n;}z׸ܑ*B1F/_vùk_oùk_oY(ne9┭o,X'994ď?,%rYWc$k^*G^*G.~zKM= $m p{(Fuai]q?kw?_ x-w?_ x-qX8ب׍zvkʪ:ITL+ľ/N~ùk_ooO[=oG =5NG&+[ZUݟTWͿ>#xKkMOǗ 4ZBm3R&7R/ F0 AIW l&4zfj}Cl1e@v$WN~/fְj\ơkkݳZE*iciv5f*3Ed韴;h >'Ėo?^m/&ش)neyO J @T.;D)9)J1RYd^(^ֿfnuGYu8uGWY%ͷ cmV@ e?cL4Oc\~=Ufc]FmsS0⧋l%үV}qsy^ldK?P݌8}ohkjZlWee{j7>I-af!-?(6큐< Zo5knX6vW1KsAÆlwpAz_?k|Q:uxWF֬-|K!}>I 7YNU@X08|yIG.eEڥŵvkysyBJ,f܎2  C{Ys.K/13|^Aa6|=e}Mj/nn[.6㝠g+_-yc7G+ouw}j:65νgws%)шu!$Tcq֨`FOhΝ͎%,fG7XfW+crsLeuY-KYǦh1\"O->H\Qgy3a*әv+}枓@Xj!_ hƑc)cwWP@h+blh{o ]^![7lM37FU;2֮Yu=2_A?^ZӴdҶ\4rf!r"VAcj/.x:oyc)-{6Y2]D^zG>VT5]N]R 9'cy2!2%(FWw;xK.7V[rA,'zA$rC$ȸle\W|qҵ|yq/-+4p܋|':BMMz%k-KβUbC0]ۆ$ k;Z66u{xNRiн@ Vtg6 U7v{MYhpj\i6tq$WӘ s22Ua9|)#<ӬJEd6\fq9h*F gxR*x Zr;Eh-bϸ A]]&^J|Z>}әXkH <タdVݴyW'޲Õ4F^d|'f+>-.;{7L"Dp2嶒ԚšM6ūAcwlַqG< JQ%_I }_&!a>}_C-ԡK8Rrҡ@6% Wy<qv.eiO$}x NT: 7zOԬ9?iKyo-[خuZ]F-6]j 3 rznxĺ2j]ZG8ΞSnidXŀ8cI}JxAxSaNo ɮ$6]ssk.y\XHOvTVhÝVZw[_Kƚl_ İaӢ I^`U,IyMWtMϧoi>z.#M4F"H\ʲ!o)d :|cIt=TԼ)s~k{KuMe:$tnEwP~ͺjm3DᲓUñ__$ANm@"CG& bEf~6hxi-%4Xe[} |#D]hj*_N ˻Б#Wb(@drGxj1qjd3 H&uO66rpc(ca _HԴ)5X9dr;YC+ yoxf_rjw:wBy"HYogݸ 8ÇW)5%\tdFe®F]]?ފ((((((((+ļQfkGt%_<$RgW EI;F^^G|2񗍭t]+īeE֗c#i;(xjO h w<-%ЧLRKaj._)J"s&p7fmZzշU]Y!ŹD!Ld89RhW={FtZ j?đ208^h}5̗{[)]КώJ/x:kۋ-uqƛ# 4?i/RH"d A#l !.1|? U:ݼ\r?;-=1++{ |i)<:xqI0Yb ^>9uH턩mȒXGJx^j#k.oO4kFO?h7ӣgLj" /ƍ"/"1RTtMwX𵦝Yw>oؠK$ K)ܡhf?ْ\q="[d;FX z~]ݤZ-5_-Y(ZXѤMdV)ݷʶ~C:Z-K[,&(XP| 0+Skii 7DQЭZ!X p ͮTKRO>-xW4+'>-|ͦ>w-29F6#q#68}_̰i7}[Ť A)+BX45סti=-v4dR1_Qx'_ѯc}6tA5YCK4dFT8#=y~i_s~цC>k{m/=.RgFB p+Ӿ4H ΍mwuYKW9nU[-$ +VR2i1x~+M֐š%v\]ƣ:[j|)լ]:Kw ֗Vj.PmeYNTF*,Gh}u5? nj_ˬ ^2yx5ڲ+`~Ѻީy<,4vHk}a..K"S}UI ā4 ]_5jzνi>u^!f6Mf*$M@9j#;xwA亮ҵ-SRBFL]C*U\nKITt]D u6B=FyRɼ*UJz &eckOM()1Evg`x۷K8-.|gštA[Hc¨ 8FE`N0r8pot@$~ݢ?uQEIAEPEPEPEPEP\6}յ.71QJYpG]xǎ&u ZMBn`yXmb̃sI膷:F{e.55[|?%B@Kq TnPY2 ox 5}[Lo$:H >Hf+;W91“; &>\j]c2p'+D,,xs^/_*jl/V q'_0@ [Jw]O⎖2HjuJ=_us?9mZo"D~S$Z;p[uL~`^+u/:%ɽKd1<^lN7+mpdyֿZ |mxM|29f4JULL$ c =>,j+c-(xÅx\ad6mj*^o'G7RAlL!GElEYjmŽglʰbk>3J^Dzk:!h PWg1'|7 XGGV3>=37AnESo.3*._gu ?^Ηy\iַSêKrCsFs^3xF[hSDz[e5K2KyAd[Yg RXxj'`xwŚo"di4ǰY-eoQr>e=K:Ot\ /%$nQl);WǞ7?!=:]o\-nMj:[G5ں)h7'fE{kRcFYa. nTۑ+VrvOu?G4iSRD4`˪L>د-KJ' q%ԣ/ĆHlnfi[C:3Z<[&jZ9o纏@6pw],m)3NnNNH k%Q_+vZ<~m)uIAsv:X?}+)V*\2}- ڮyChڒ z%*;Dvio1<_vjm|KO˖Zxm; Lذe+NAxF55ҥvu2a:fQ_.E4F%6"b [Cxcšk:ߋmOFTkyY*I HR_BzV{&R%VSm #;083B9ϗm( "((B0O^ N߈m/&O˯(K;(UI@\>/.$ښ9ioa d 5h|>?-=WյK04h&0Vegg#ut `%t !=$^ u'UhxKKu닻 |Esِ.b|N.zo+ӽjxXC- Q["<۶d04(Gnik4s6PYI3T'o xZg4OHԠ[Kcam P(R?35[R_h_ZC$7:j:1 =MPD[W"=RDqpqU'j砬'Z%ߊӢShDp A_%t t (;g?5h;g?5h Es/9M<,dE 0BJ:)*F@#jOxg|9k)&w$rQDp2%pz7~|]#RtZƺvbB  ֏)3>!EJ)N e?N e,QK wLψk/ wLψk/`|7߆SVF閑n/ouPugv (՛=Sk*Xy]H E}t t (;g?5h;g?5h E/)3>!G)3>!EJ)N e?N e,QK wLψk/]WF闗Q^U JFUIһ$0'|e/x6Tq"pGog=UGR+ث&/ڟ|-kXM]H)Hʓ; W}kZMoBUt S~IzԱK<0+ |1^?񝞓nu0jd~=ôLd>nåYnM6O? ,gɶZ^?>ƽV֠y".$o]( wJD!2X]\FP0W\w q|l7><,u/Nu+KCSz\'Q,<`B̓+m>gsKYO2 3NrA=@?P({ 4kƶN3^Rn#σZV#vO h]Fdԓ̍'0FC-^H-{߅~%Zuc{=MN-9ylm8^_axKÚyI柧"I-$][a иK=OQlG+ghͦYjɳ42 b laQdٷݵg~՘|C~ 5ծuڥ߈.,kK!vŭ=?i[F-٭a0>ckBu_N,umD.338tw6om|c߇??>bmm펛7ټ7ED;`f895g 7O48DenC1eܧj9uxWn> >-u(t$Q*懸zX]/i&%< @4Cݨ-m?zmC|j+B֧Qi7Zώ.KExW:k!C,LTdkg]`j_xk v5{#YB(@ ~eY i~c9\^}> N V-j#GWŖr}9]9Z|E77_|Mx?Nemuo15 mOatBW:8*SEI ,ka5=R×P[$SxVEEgs(.$jw?(tz5^ndmF?~kV]Asm_GZ@2svIH Tg[!&oK5|MMm ct#7u;|) IL^FHMs@\[e]k쵏ۼEZXw/ x1Wm;⏄5GOEe 8\T9PqIA۶&DR+>zֺǃe|N׵].}BSL/]L|HdrRC2A"|Sgisb`$Cʋ? |yw~/=vZjj,z$_Mtf-,vpm} rG dDLjG9Fi$2Řo̼py?+yGeu FA;V&>dqd%ѕeFRV_uo+4T? tK$ԵS/%ǁΩsw'u'SbdyS^?x2Afξu$sb9QM,Ҙ(0$>1[-C[P( LcVsជ.5QBҺ!qԁ[J\Ҕ2Qb_#QEQEQE?O@5Y#{TY@k=`?Z`Do>PUhV[#?I猴|$&G_խF,ˎhV8v`OבMwχ(όɬdme8+H[͵8߬ G>'ך=NA-ԶܼQ#VmxOß|9.Rҭm ̻Veq8&]?_%I:?ړ^H|C3$!L g?%p$@ xQM蚋Ǚ`m*yhHv=zn|ោoZ,.Kˉlm-.@2G傘Iޜ7qI#sF޹⬮r"kA2qǥ ݌R\n_WZxmV?i&k]>[Oc{hQXCn h֛y_^j5=vYkZ0K4vH@+ҼGk֕4+J펟o8+uQ!M¾Ծ h~j)]GDΑ^ ]ѩfp_#n/[77vyl-,[ vGdV#К[[m{ %s|'[+ojr_kKKƶML|#Ol ?ixVlZj5ĚsK+H݌Vox z];[ş¸ZCvr%&04O$PwӘC1+uSz?_qq62Z1nW6XPna t –:ʦ E+" 1Ik]4;CZY??}Y O'>0xbhBR4upw~J]DsdgKNƞ=W^F }2o"".-.1 _Gg:nI-+^ڱ-^䇕KἽ )[Ӵ-n [kaeG9wvyRK翗#f;&l9lua/A# y<[lٸګV`vf>[~ze曬Z1Vc*]$?e1 (\oW+}]ZůC)e8a9<ڹ|\<7 Yj"&7[ic ۗrE&#){?[K_֗}{E8+W>Z-iafF+3Iq*GҾ.~?'[N a4-WIړ liZdኀ#{/VzC2[p":9GZU.7w-_?>4}/ i%4V:4/~S?]2x%vv2} K3G.~\F~o&U tۧ:uo=@38)A++QHaEPEPY#{TYZ5?O@4EoVFk[[o9&o3m:h |[{φzE[M72((;bOkm߉>Zmb׬S m|"M` #(ϑc=_e}[:Npm%"{ D" N/ƍOi]k)}̞%8MyqWx޳iz 5Ğ'#&"Ufۛbfwr˶<<"c> NKfư< $%$_7k /V񆇦j7Q"I-\u< Еwm|}ooτ{3_jGVmV;-O>f]oJd2&$eA!5-I6'&o=KRViٻ Ox (g_(6TG4c_%7+Vx/>!/:04}: &S彮d, Rʪ#E%X?mO|]QҾ-@#$Q'5>_)nN[$Ԭm<]C De ]Ogk/ ꗉ[xjTgM7/ݤ 9ehlWoY}ɟ,|k ߇xwTXg.ck: g<$ep=sWྏy{Vq<1q=6"XܪHRM-?HKC|;λ8RZ YrDbVV 8hh?4_x7?oafm^+H^@1 /'=3DW*_I~ Nֶ|Ew~q<'1{[Z3Ky_4g`cG<9xÿ>Pix6effFvZ5}ǿ_|Cy W6 xWva4w?Hܲ,[7`4~_Z_ܬK6ώZaD|+bwo/<7_6i~/1m.2"6$]D6F18]G:W5KbjVDk %4U.n+4̌UA Joѯn_8xOG,[lE߄Ne1fm< y{\z.__RE}xgjh$̕W!@"vU5oym?S|qJL]ͮ Ad`Tǎ5{5w y.=(OXȶh mci/zw2bmncž- +]N$ZVDt߷\Ovέ$1iFgfb{>iZ QoC{i:A6m.7K2"b0ew3_5>2D 'Nկ ٺmXn] Iۊ|gѼ]Sռ;iq$YEkB h1ǿj[ Bz[P_|FܤK{ž%N Sӵ42GnyL}M>d|vaB0߈>63Ϫ‘K l2Myox{Q,uGqBb gǷw:Q?1K|$/|EaKi>.6u&D1p-sƑI]Vx?ҭOk{w7vʮX #b<nQ5~t˄0Co!1ҺB{~Q-}Jn>;<)sZeƳW{m%ʷ"%V. ( ( ( ( ( ڧzѬȽ׬5|-kz|"7Zި g~4uf|-?o@i`S>d_ωbk;㇉e[?k~-^߅W ~X D^+kvxkK,6 \8:l|=c:Ky;p Wnxŏڗ>*k1 |;,wZ{'dYYPe\+ȼY;=&{k<ċO2!dv + nqQӗ+ߘmH[V ?y /'awdLO&Yn 8bCc_^EQi+$9H0nي4{bѓW{OxrMo,O^K-2y;6yĆM<޹ܲ|F\7麞4W m5)[xbW9d,6+a1K!^W(v=7:|AXitV6ZNdXn^ieˌd %N>YW:<94kfd g&GmI_(c^/Hg>]xVtM?:7 F/̟S KA|[jx{X“\,iQ3df;Dd%eoNi_͕-[Vx_=SRwO4ج<6.*\۶wu SMkcJjm|;4/Fc&K#$eFڧ,𾽩WW,+JBGRAI)RFOH6W?xΛoiY[NzT7Aq,lKhH7G˓#n_yj^1/ _EakX]xtiyl/&a 󼓼,#?n> P4S╅t /)hpO'mAkf!.m-c徒KRE"@g ӽ^2y."Ⱦ! wzXNҕX"wE^:~_bZӿ %ZZMNPMX}>hYoOr/l0Ok=r/ -%ruk-6 JN; %I5|^Ӿ%|7[iwZNݍz=ivvu11*vRoD~%@ķj_$Rȩ;\-̆ <3&v0Xo\Z'_Sߧi|6_t߄5յ7[CxY7zi2y Cy6%> n#MЭ<'|,u;N{{q̱rΨ}|~.(+o\.4V֙w[')bk䐤Mp?-~0x:j\wrM:+-Mϐ]XBymkl 313S<9k oo=+G\ +4/cᯈM[W*ǧiZj7WwlX1[ƦHæq<=)5&ЧsJG]XZ*l/m4k3PH T bvbk8E%+/7MI=㟂|S'cҵoYK;x—ރ9`:WyTQE ((((+;/j/kFG"^_sRG&Oup]^&,Ȓpmiyqu/i6-!ɚAhÀvPI݌\Om{TCig\+sy|uҪ<u2$yq3?_߅^-G^oQi3xoV:$ۥ?Ma]5{EsѼW>w+u5g쥍VQ65 H>^tw'][nXƍoKi_Bn[i*@$)v?SsoG,__?n$K'[mݽ`ih P ߒqVօGWmy{Ěo|Oh>jp dg5;Lˆo"lmˎ3V4G?z?#>zuֶg YH 6 / xz/nMZq"I$Й#YSw5NbmxΙ| ?A{>q;Cj~jǽc;b2v!@$4GI.n77~ܓ,r0>/:|wg/ ]WL+ KЀB7c?-~[;WxL_kBb1Y͍&i v^~Ӛ\%x(тJ FAހdwƟ'bE.o-tf;hB:)rIi+N[~֗49OZ7sKo_\^uu1k%Kv_+iK7dy_e/:y4+rjw1zU:u_"R #w.֖1i>}:YI10&/qykOD֛[{1ĚYJ%ݵYXHW낝Q8W/Sj_ t_ j%ot2es pGƳD\5 _&+Ѿk}gj.> yl|N}eAh·+˫;WM0"qdlX.\o_'|Ef Yr-WWӍ|#lm ҍߣ_N]?;yg^! k5ڮ,eTȬ@r^o/'>*ԭR~X;/]#ry)nT] W|?k:-+Ko9乳„$  (* |;ᅳ!nҩ@Hʻ&2y7]/wԈ%o3v4/ZsGV-,|O kMYʾ$U;Ao ~!xuMu?K[2r=$+-!Ip8O'ӴŚ>5-Hz]kK?%#x++N57ͬ7QY*`"0ʑAQJм~?{ xwL4:U5Sj)nPFcFe4^-wP+?j<%0ܡT p+?/<3=:‹Xm&nV:?@0gLAA\˫o՟'E[7_䗿?~&g|Sg|oZEYN^H\ϰ%O|^5-,|CRU6mwIs z  KYvH6| .%|Gt˻mKZ&6iFaKyo.d||exj9}c@ E5T{+V(((((Ƚ׬ڧz_ȍַ?#x{赭 FGVjs_M4= ž(wh ]wPAlH>K${&PtI1Nw3om-4'vPG1JQdl>fMveo%0@fV嶙ן-*wH$0|tͮ|mgQZMdRFmvwX|@`(͐,;o l?o׏|p3|)uP^ipcv>[vbl6]V=6E#&Iq#s3P$7o{nowxG(׮t]r W &ƬO>]@θɭ~φƍ ޺5GE߯DK, HTmP6d?m+FΉyz 8uɖ٭EP68chx ZRG忲ėQVLn2ѕY0J-]6ЗʜWK < a31eqswq_]Ա5K KHrƵ1:k6LJ1syl{IpV fJ5o;z ]ܮb pEwյKGm&8n.-]vXEz]Xd 1߳ =׆|:.,..m/ S;FBI~mS`/O~Z|AcZJjZ5Ŋ\J=ͬI!|zm|ki6[._N1}O2! !/ #׾sY&#5 XʰF|Geux~6|,=Nj϶Ȟ{?: VEi&3v1 mQkg?RNgp"Rpė {SZ-?%?~⟊^| _fMORk=,-P)-kiilΫ ʡd2`Mq@pE4E_7~L-DMWቭ꺭θ+kZW(K!X~F l |ýoߋxg@>?]NdK'oBd_8`+k?FqhWEͥvZ)x#^Ⲽ7jhτu WlmQqZIœ04nr|WfKIa_|F̂BbuL)qY_w/.-퇍5(," ɟN֞eܶ>Re`@Vh®h0.4 <9>%:R7r+OL5=Qn&F$8̒U(o5/4sj~$5Z噞;c^C(*VC3Iܺҿ +&zt~|Q_ xhhzy5̺t<3ěmqk_x_`>!b|L|i8&6|VXP9o;Eo^_g]Fir@7 ?_zhV -6V%䶊;.wҡwa3[[kc=5F}>s[fDbfbwRE,dPαz>˛YGQE ( ( ( ( ( ڧzѬȽ׬5|-kz|"7Zި g~4uf|-?o@i^V:4?5OEjg-Q&Nq?_~x^֠^ ص!41KhHbݿa-G٥%)"ITnUH<#k2 GhKx;/ ,ɤi`Hg4UI/)a>bkio_a-~y@ה]w|Nûi]"cIwu_ 3f#0F;zp#GnSVyvVЩi.."8"P#^7m{H-};49xǺ׈Coc9|7q2 eܠy[Tgx^yցy|AJҵMѼI:PgKTX̿4Knт0UUHdY|9Pl5u w;D8Z?4cOzzօ_xsJӵ}nPq7(%ua; l< z4)5۹tkk.-~B% /g`"w]|BNmWӼ\%7QCwpgEH^dܥ#8!+io+|on]__g4Tx~IX^!28;ٟYW \x5?\\xsXӴGAgK|a8?ieҼg-|?XoWj92R`Y9Q_SgxAiUuGaeXǙq+ً)@"|[q+6ƺBj }\S ;n7|yOVC4Mkf'5!@0.G޺|ch5~=|=Lڴ?$Ō6nErfU~RHb1}~>O-Ο=YOLӜ#XUe\muV.Gm:o k?5?f[6Óɺ[VK*BEW;ɯOq/)'MQecf.e[G.f]FZ6 #[O3&*Bgφ{hbut-fMÇ{@`n!oh>+*kM6_?dK+>6MSBhan]+=ϞwD+. Mg> x'm!]E o6A<8Q8\.rZ~V֞hnqx_^[=R̦[SiO|gA牡Pk7P閺&9vyFY;op~(`?xxb c[" IEHIv@n?VrlK~qi4mVk'}歡 놁Zfiu/)e[~Ͼ(Ƽ׃mA]Ij]@u\ $\ʤlʒ@azGc^h>$ $p[4!uF7xD{_ Y.5 li vy2olX+?K Ag}kGwM^-fM7Q˽Z8.ZE vBX}о_'㦷aJBuc(JY꺼[:X4JK`*,XTkk+^[&_ES(((((Ƚ׬ڧz_ȍַ?#x{赭 FGVjs_M4ŏK_fֵ Q,M,d9t`$<T|#{K/wv6ť抌cy`E+R2c;H o6v' fů7l^1J ~e<=kcxL(-VXfhtko En)[TPzo~70> wsaU糎%{ee2ǵQ;w>[KDŽ?g|-u]WWծ(Eofm/V-̱[H+SF/{qݪ֣nT{{]I3VɚtgrFNkUiOi>;'<7ҧ飚50XeE)gE Hr|E {m]?RZyV׭"iZ93Hso/~-~RWc࣪K67df;9k´r^E>'}ٵoOvdݭ惩:k@QSˀ#)RCʱ+~|{I~ҵ[}u i /oc~d] w^ūgd|7i4Ӵ>;'{IeiI HҺ"))qG{q~w gFGmMhG kAq47ax=@Wby^}b_@Ol%vLBw1 )a ЎG>xlV_}DԼ%$]jky$h2O:0Baf㶳SF_Wkջ<8M=.~wKf |y]'k_&ѧKӞ;'TlW\ uڭz޵pMq;h@/6m>춈lpD>=ּ9@| m/lm+8M.ᤙȊP06$$:+O4Ԗ\iiHfڲYp*gQF=!=~zF}SOLvA+yeH@`9'>˚t~,w5/-u}:7٬y#&;8&ϧhwsZM_TҢH8ѭ#@ ٶaH9*X)]7E04ռx;]N.bh팒IYv\7oN_?M_E.gב2=2iYe"tnͅ Z@rsk߲x_>⛛~*O__|?+oɭP^mAsw3ޤyt{VY!1$PY& >,qU_ >jPnuKO ְhVI1fV RR5gkmooa3'/ߏOohvd֛#$±9vg\ߋ5\ͤ[跱wo '+"yS Qڗx#SGӾjzKڧFk;K2ƶlѴMlΘmwqx9}/1]SBvKb ͶW #p$0ku7/U4MCZ`a E{}NYKd1F|'6>rssVV_O~aES(((((Ƚ׬ڧz_ȍַ?#x{赭 FGVjs_M4χ:_]gZ-&1tV9/qКF[{ 69l=R a*\,9u&ERP1?tҪ|qѴs/mnZZXq6avXcx$B(T6:`+cj-vs m3ivo'¿ Pݶ\uKKD}7\㟈ׯ5 4_ Ks~Gx&g"G - ?+0 |=Y~aF6,znX5yP$)%jנY=Ƈw~55]GÒxv[{{-\Ȉ9Y7W'_cMat=&5YRbHYpA8w--1Ok]]wM#KmUF@USt}#5+RT2$v+*kܜ4~ջu=u_q:I{%zw<~yovMh|=oI4NsM6oNs\͐nٖ239k_?K!XGQ:EZ-.?ͰcS>k9߀'Юa[K(i`@< GY4~&w~m~i:g<[ccy זu:-+'@ 'ֺ֍sŸ t]XI@{YReʎ4a'ּ?h=[6s[`KԇfA$*$1U. P_[x5ge{3>S#dVws,0WC#4v_Vw)U_ ;TIC=Vx^^2H$fK=v21~0x9>|AhpsOqGIkF) F]W#/ޝzbFˁQZ,$IE9V'G @:|Tٗq:D46 YieL)Q2,:GTz66}/W='X)hz≯4i.[`d|(*xWMMĿ ַ[B,^WSqs朳;1w|a|Wux8ֵm_S-ƌ V[ vCVGvmNw~(z\m=AKE )#+Hwwuk%ۯk?g@tOxZBAdz~!k~65]3SUޖ+2'^n]?GdKwh{f)JTW[yLR.VBU F8#Q|9hLmse"ڄ+91ϧ|Q׾#8h:uM{(˽o^Ḙ c@v䃓I&ݭv2Y\t]3GXt+Z%!fRK9 }w|7y-,.;xt>Dc 0q׆m/3K+zLJtum9gFy|.ZK1c;w ]LjoN燭4-R-%y%K]BғcF!*1Mkk|Tks , цSFҼOyq/&單Dae/o3Jhx|yoZ4FyPUhV[#?I84]IK6HFæ2 Oc[[ټ:͌zlӥ̢9 "ydf#88 Q_ -ܖZbs:?3~j.Gs֊<}G^M_xPu-15[=s87;hbNu/ٗឯw\xZ)ծې2 6C/isz-5_pz6g/Zܽ熣Ktf[ndl Yo68񝿆_[d7 nnY &RɳqZ(`x%vx=u _aucismHmV &-3;ih[(W\5Fةxչ$s֧ٯZ^[ih[6YыyLι 3iʀ鿳o/ܞD:ȶWڭBeS.`WF_˿ 5? ZׇKS^}). 7 7>f!ȭ ߈wHDm4 IZ5' ؔz(µq/^?ZmM#M"mVh, $lcrII-j2.|7433]Ow?$Ig4]֭:Cos2姙1uU@# ~x ᳷4 B"^Eaq46qJc(=Z_El.Zjv`Ԯ#A !>BE!bр)/?e G.+/IYu5{/'dKIs}XF`q^E?^ ~ x^ú5~Uũv:ByA3mSK{^hnN+X>EoTUo3m:\]RĚײ,g%QP}_^ `s~vfGuAw w\s~vfW;C׈?;3E+??zohw\z=x4;p=~GuA??zohEy#^ =x4\_s~vfGuA.^AC׈?;3G#^  w\s~vfW;C׈?;3E+??zohw\z=x4;p=~GuA??zohEy#^ =x4\_s~vfGuA.^AC׈?;3G#^  w\s~vfW;C׈?;3EڧzGuAkgY%4L nm=ACE?#x{赭ꥣiZ|r4+*  MX#OӡXO滮]+ 7[hFi%`v(DcxfKm?kŐ,5#׈<1xw×kJ8 yW6]|S]B!7w2Pw1D/èyc\ȣ c/=׌={˛]jh"bhԣpcR~`z_> !դ&ťEjzyաy4]O(00QE/ x_J{{ispO}2b g"YzƟ\6>;交 8 arrE^kdžtjzN_ڦ&Ḭ37cO~.-?ZZJ ѺβO㟄ީg~8t7PglY'Apb&F<+!n[gP\[dv+/Q4荫xHj"2_ clP4i lu kB|eEY]FŖ]?O oYE>h+5Y?i:v&(b)PVF`}J[lnal8-mZ8EU\ wվL\[%'<.^(P(:\Ҽm} !e\[f!%\ ]j5 aOxR42v/o$o)JeA,3?Bn4n4ٮ~%Ϳ{-`u2JhPH[ #˓^9⏁^)>xKB<=Koz#2Di@_eG'+TgӦdQ3Eŵ P#pAۺo-?ۺo-?Swrg3gk0ŭe@at5GwM_wM_񡻻++?ۺo-?ۺo-?!?ۺo-?ۺo-?^n4n4zBB*A OGA O@?ۺo-?ۺo-?^n4n4zBB*K}B5\YI \*# A 9f=ey8o͸Gl 8#8}z SOj7OfI\#*9f8i]إxֽFV~Mfn͋'r k8emп 4ov_gOַչ{S^_[ɓY&(jÔy.Z *Kľ!՟ᦋotk˹uhDcou楳s5uo-6/_w_OM|τj7~='Ӷ}N3 FdY0JpEf| zC$e]QtXo- ITC݁*kG_%;O&O<1k>qҴmnfHT4l@˖$ @Oi~ 4[k'ÿ?&|3mk೦*4)tBy23r_Ek-OjXHE=WJt]HyQ ]0|p ǩv_K׵cZ[Ww0MzfKo J-v'9TϺ 43:<-{w/GK$J@KvhzD\ρ|~ڽ߄lZw{{cY-tH3|]V%mnZ񾛣ۈli=>9bB7_mq!J1|-{)âYaMGLK&/Rc\~P]|1\xQ-^_7w`]A=fO#|w ޣ}|Uxf{G6~լ#ɏTnޢ|f|rw(/i~:Ôiy_>'ÿ?&B|;@ /kxkOOy1¿V[='Ha3A h85W+zӦ+xwg7LM12/Ր(O o_ֿg^~K OcK{Sg!ujߎ<~'|dW]$]7TdW.s:vѯYo5 [kwlM ׷M9+o݆yҾm_? ωtm .u}6?VYe͒x@UUOk|յxNJRX CuIi#1PdbOϧ7˯?Q'mSvZL<˭F=7Wcs~_9[_o.u_6x/46-g7PįvrI%IJ,@: uYQܤ։l$Ȟ3ihI 1t~62~W.} LY䵊3+;̭9-F[i-9ƛf3mnk;:|@v x?qɪ^_^vS\" ɣ(7W 9bn㸊WDwXҾW>9LDž3MB͡k2i7 u䮪6 edv{sgl4/{=u 6d6ҤȱGX9lW/W%?W(#rKxWѬ5;@(4Kh;ie-!lۂ򍧜z GĿx/E48uynaVɼ0A' B3g8-/A4k![;Y4}>8"2C;G(lKr.1\Ake72HBT"v1KewoE쿭.m"Oe08PYU\H#9n6Ӝ0x\A I5;ղOfmGd88ʜ"|i k>( ƣkkk;Ad.ĉ^A_Oa_0h>'xKojShm,ˋ[6rUDY VR I7ω:n fT ZV+ 丷G8+qY$-q+]ɕ4}7QqBHdI=6 +ɾXU4mgƷ],MGc$AJ%9`k~=#sP<ήu1BFZi[.`= jv>ӭxӖg'S+PC)t5"dGMɣ+[ȿ#0(d auOi6<?t1-_/& RiOOs}>K㶷nyfpԓUe!A'Ŀxֹ߳=[źG_AlۊQ/^ssm3C!o+e-/6wͧ k+z\ngC@cN*|x_eM_X,n|i$[( {2;v㞛/x^T 2* KrbiW?t W2\#[T\@z@51!A$$Ǿ(_Yh\VR^6)1LH 3-Ԯ@n$׈uHFszM2BKxy6󯘑T;sQw[z~?ǣjvYEwm(ܓ@Ǩa繞;xI+Qےx +Ǘ on4Yrie[lD@WJO>$喛[`ҵK$ pD{ ڤ&vĝWe!AƏ&$ԭ/-<%>tm6儲]ɹwFCc<|C/~)Mis+0ur65s=A4B)+'#?N_4x4lt=2 e8IP#scf\5q@9Go?;/|'. K6Ww,0`*lm`a 09; i:jVI> uӸMN7?r-3/4{=BFO"bΰ}~g?h:[mV^XAw<7>ӦxUd8qyeUP xWk]WMhZPQutw i&SZ|-g[xf +}m4jUVdCl5ZugGNX`D(Hٙ6ɹlOi&Qq@9HfZ|7xFq`=eYa'8GD gh1Y ~^dRIqNrîfo;P??y}C)$_ <#oiorh cUrKI㜞|uiLJlmF)ȫfCQq@9Go?-b x{mJē19$+?/հŢ|M.5UKpǒ &;P??y}C)m[P^xY5s[Эv a018k)gU!KoEּfUj%`lt+WN7?r;P<5IšoA5qG/"'Kc׆f ƺ(wuxd_#?y}C(ӸMsQ$>'QWM[_%CKz1sjDž>x7ږ?IJ4Ѕ`UHq@9Go?=s/Qo[^ڼDN.vxC9hLc=kCIƙ\jڋykyzؙ(9<Tw i&R.Nmg m=!<͓"_ 뭗~!ͻ|Mw_zGc͡˦RhMm%4 ʬCgQӸMN7?rEgi&Qq@9H +;N7?r;PѢ?y}C(ӸM *:ʨmJN EÓU(((((((((((+3S'xVyH %;֝sޑ`k@~?_4WOGt"-<{7Yu;,`ؿgFe7V>?^Ժ։mCAR)r<*E.)!OZkUOPxzW $z[9 6,m;_Y*N3ڼ'wXo5{/E4> 'esMϏ|yŲAycsikugyaCZ"$ A4^;[S:r$q n `gr8UCӡjI5 MD4>iu ^i}gi]#L$ Z2B«[O[ xzW $z>?_4~˯hUoC4j 6do- 3~,l9댞+gF7GMmi-͔ڍ٘mB$'%)AMXI?_4WOG Fx[:uzuRPCA&ApoJvG?Qm)&N"YH}s4` 6v¾;ϵxzW $z>?_4WT׮-6M%=Q,q*`oNj)6j"6sۮl[Yn@ARCeܣ#"7:_ -B U ۿ^\{Ou(@QEQEQEQEQEQEQEQEQEQExHݏmV;>^e9J6 )]T@<#h/m..ֵ7& M3an+yd̖ԵݧdEU n"B 8 ^x9C#G,WfQ"F#`Mz|=3Ŀ2i)Hɧ"dտǚ|oK;_ A [#i]bܴ{ciAʀX 9ŇtE+G{k[hUwqܻ"Knw {'!%OKExM?/'mP޻Zs(?ܱx~/𮞹 nIQcn<0y $;Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@fotoxx-18.01.1/images/blackball.png0000644000175000017500000002030613222767271015545 0ustar micomicoPNG  IHDR@@IDATx]|Uҟ=䦇$$@)EAQPA~* ևD, g""E ;һRB-@ 7eH彻?gϖrQPs^&t Q\ȓztmM8a݊`Xʘ͠+R5b\taܮ•0yzy 3U]N)#:R>QݞjGi.iDV͝PN%з\B3"V!adȀL:~1aUqZE9L1>\$Ivnԙ*>I9{>&H r=&yM8D7&ua8'ac4@'F:KWx8E1/Мг8]d 6`\`v' 4ƻfAqà x=Wg3vלCu.tdbLQ UB{@ !#n퍜F9IhQ{`z+&ԇLrvfϘ ,@X&T]  *r\K\yҹ?8\q)i>%Paw #]B(WlcXZG"]#7PLm0BR0ap?h4p ~##賠W5 ګ[V/N"^Lߨ+26 X]2&(Zdzw`:Wz>l9'qCG鋙zLe v`z+ _o` @[Oe`M 6o =rX=@uu ݖL7Tkneo\sق9=!e;4f ݀/]1[sFW2o]5[ 쏉F6,ݦx / Sr2  5&*S҃' c1~tFb>o˵il@Lñ7(h  a XPZsf&+VlUSwq'ӆrM=,ep ֍N‹*pBla&a#cd=$ EL[F`z}8+ao/քZx/œM䙒(fDd|ƒ( 1QE G{!c: gee/69t m8LW1sӠG=|k)s`O&`(g&oiO58~)` ؂kzv GLٰVq=IigﳚbV,bTܝ\lʨG^,UsU:㝸Lb~M@V ~Gp ")'1M@" %/ҮŴcgi;Tv5 vha\[z*OIƒзqcJ4iƢG,QC8zw@12=ݚJۓ ۞J,e(/c$&FY5ؖ4Feprm#c->,6N G`PIJvB+Cȗلͱ(/i!3IpUfGph xQhGˁ?9з4ρJ9~M2v`<׌}S?S\ pɰ8z/ÁmE;R*h&m%6&%,3[EV淠|2I)7lv_ k QKp`6ƦCGTGz=I%?#/84Q2^`>*z9לW5Ƙ335GeT-%]Q`8)Qž{˛sA2+m{fv?dTE,>|J~GdJlNU3RAY-?уn(9KD(Aay:SpFw pxg B $Tz9%%|Hg-ğx,ۃ3 \~*8g<(3BCH!s 苈 %"ZǛ3|`+%r[nIUooa&tzB8ah??,5T{LM>NQ)98bWy..P f*L{< 7rٍmYgI\5WayjZOj"8NK${67,1Z-8^+p{nlzsd_C3גOă(Tc(&;&RBIdQ^&>D )@ay[8 ӆ^Kr@PⳃcRb"yўw4Z@Oƾ_٩)?W'#cǹHty+S9[P!|ifFϙŞ`dd: PipnCдsG @ʃ7<4zLL/B<淔n5 ?wָ).pbDTa|r7x̱4WRo /fA_|-)pmOi×3,xC4mcVAgPT(C~|Otս5壮@ N<25bn쓉0RkH>GC `N>(/Ǭ{`Y}{HNXYnJq֤[[? 8x:8(5VPM^-)[톌} cOآ4-"C%"$nu1<}gեEP4wOdMC$.%t꺴(fmrLTq RScdbw MV'Wm*& g x[>2(سg} I8sTE ^-8Dv9MW00!']-{8H䥠]R4;8vBl'܁Tuy M+Hݾ ԙx8O5@e\1vq:?z{}Lz3= bN\NjqbȴKoNYjթ+.ٕ!>tAo5 ؾ\cM%T(m2`"7H|:7~^h<%t+y0"A\!6r9jS+R˩ULv-pX.pDwn?`uA?5/{DHoAAZ,ոeiLXO qHZoDs yxXKEq!m 1"S%(ll"m&3H%Q$D\wη–ѫ75/7#TWoAƙHD:Ž 8 oB ]ńA"s5 ^ey*{sc)W"iI޾5YZ mZNA% ]6Tk5sf?s*Ox Ę16nH(:u] 2-=xƑ }0sK4b!wɤu1c[3>dZ*i98#.);V{p)ГFأc{"8DKmGצ${:YVȢ0(<OxYTv>!ה&pq|n瀛L4 # 8xO[[D{#%/Si1qRz VUQtT%+9Ra,6PèH5kj]wLY;=2tZ͢Q;ͻk26w:y*Z-Hr_h\`db]D9=r="y547 6^ƆhǏ8dEGV^͓W 8;ރ>L(l16!)&/I'PvlK-:@n+w)6CK:4/W:1a\sLWq/#t{[z`\W#6{w3 }<%H{-idb2n zg|Z9ٞ{I}cg wԎEo>]x_v蹢:R^(ޜ86W"ЃE!aLj1HϊP@g&+ay@`z:F5=lq|gam:"N.}W)XV!iE={hvBu;e.`Wa@1QP\_uP UqzSHuЉe=m9N rL3;п*NoNsa[To:|u`>aܠTT^Vnxu*}J'S`"쨩M7رw]"nøFnRy޾Ƿu)gƹdzdQw]pɁФ;^q Pbp2b0<0#'"d`AgD1f/P6(h25>iV§t>86aˬo5RjB] 9I/hxD(^:B2]Afl+>xH^c(%iXwvLA4){mo}\ڗm^r.I>Tz wt -.T ͧZ-uEE@!bd{PtiY{"Ao~Wt bA :b0<"DF| G.ƃ(8t6[\a*Op݆ҡЃj➄ D:ƕf($bVJO]9|O7d,^ȝ;v[ys|/2B%B>j78<fƐIo؛ c &eޗ00R op@D b'Z]|{~ 1)q9k5|h&;}ؓ ݊{wX.YdzEl]=*^X+#{PVdOv^o;nAmFQZsW>6 vCbQߚ.Ƈ_/tu7CU{CëÑa V+Θ#yL4n;jj(8/.כU{&#޺!GP ĹT]Hq/OMtQ2UaS;e|~T޳9]N08=O}}z"&N|S&&TVbl|r-O -0ZgjV_:}_U?Pq]_~-[g&L_{i+ؼAj /r}uhSQ_|>a b-I4soVzTXtRaw profile type APP1xP |WJ -|n̘5p6%Pz\ p-25=۽@WAD? RƆh+sy lyh5:DlURu<-)>`ۇκ1Y^XG5Cjih?m65hۊ9+_-ꃱJePԞN(E¹_p"˒4[Dx3iTXtXML:com.adobe.xmp 132 134 CIENDB`fotoxx-18.01.1/images/clone-image.jpg0000644000175000017500000005646113222767271016025 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 274 296 0 C     C    " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ x<>މ|\i~\?G?Xj^X 7"7"GڽloDEloDEcޏ{`6>މ|>މ|Wjl}}zg>"}WmVOj>y$QH#i받 f ;*${d}(+?> x\Ŝ);#5[G[Fľ,Z-_3bDh& S ;1ڟ+v_]+]~މ|>މ|>GY8t{!8)"::-s{zNmW xN RI%V @"$_?_qloDEloDEnhz&m7A[_[M Ld@u|aZVxZh( Q¸#ko#މ|>މ|"͋ \Yj0`7yFCc~v&xK_-5bHWN Q&I"*ێ z?ɠz;]ɞ/OQOWwᧃ(/+\kU5k>Z !-|U@8ϵs?/i~{èHSi['dE0FA&}lrloDEloDEcޏ{Ӱ7"7"Gڽoak>D犖 \i!d%I7) Hڌ:F}d Em$qՏԚ$Z m4, cӲ4S&(@OOYbshwZc>k[* aϓ kX@g/ɿG?ރV֠𾋫5if:km,Ь6drqd9DѴl2*Z4{\7n{fEPFAw}Nuw8_32}l׫x▥j&}LPy#͔ r9$xx*F#y5}{]dhO^n0l b"\c}yE=XFeO }[y]+R*έx 8kO5`KB5srefɫsGO~ú>c?o(t|4NJ2Wfx?<}hIwGO x-O[,Oi>iAe)1E\I'ֿC ?1 ú>c?o( _i>}o;|H&ц }iAe)1N܂| |al}4ϟvvgw8ۏj?◊]rb驍e]bo@ǞPpHy] _IBo5[ *m+; fd #%3s_cú>c?o(t|4NJ2PVO?>M7FwkwP>?>xB^sx|\oĈl">ںXk`/1Fڹ-;}o;c?o( WOj$OU>ʬ彿tnc!A `#A k )1G;O,|[_4Yon2F'&69MJ+S]dk&H kg, R219Xv<3y\1]}Q4bX̑=Fi!@V$ wE;ka]ZkESW 4FxʌTp c [ Eg\> cn04lѺRU*pz\3k i"R ̥$;F[i;r7c"ڊ+^tս;ƍÓGy(zFZj$u2brQE ( (g[xsPԞ}4k's#Gk1e r2 E@tVnAzB-/&]<bܙ 8a$VVGU/EUէ6QL9$rΣr1@Q@Q@Q@|^5,8㺼-ůe-u+]0K]n1G"y?ĝ/^W9hu+(cFiLmvH+]ɅoSi~dүoಿ[٤ifK%rwpXc# ι[Iծ--?בL\,DڰzteCG5 "Q;HڽXZx 2`dfRT=Fk b*Ȃ@+U&7_.o~Қxzŭ;[xF#K]ԫ s%Ss7N+nhc8yUOjvqB&{&RF u9Jܧ5 o[Of^:lK5JQ*N@kҼ#/+^~%\7tڎMSv?b!m:Y؅]OLjwb?5x8On5&-*[m2[P_L:Į 9g_݌rWq`_vwby|3ȿih9Ql/㷗L$o ]3S5=+Cm"XҬAY<ܒKI$Nj|UsjV_>.w6ئ{">oٖ}-|W%]x1y_Xz -^|;|1֍DJԧ-k%ͽ_gc4LFTxԜzV$eU_\1H wտ;DKyXZ^mX|=6ibޒ\#EʐBA 6 4CsֺU9XȚơ*1uVɸW/}mD80in/-191zН}+_>H.fu| NJE)o3BCdA'*\J*֒m;ᶗy]0LfebAc__ICM%) FoNe6p =jz%v|v6:쳥:vѝIdRp@\%EU&vٰE%D1 ?tm~[-woֶ힍unRQ c'hJ@W o*I0Vy<3_B-ȷʬp~7nޗ'志G|x ?O7[4ptLc!f㴷[kk~\_[Wڶ+ xc9eCE%BA' װU->5 X^Kx>cdW;N0 Jx㟄4+čxC[.eBM,1I#QK2ؤ$m$c gjtk"ASm5wG!D.ņ-U_^-~ +'u)Swx.ⱽecyQyf B >_ Ľ;+ wL\\>+X4 Fџ. TUV#_xzx2?Kn. dvvm8?Sī>Yh:\Ak [ߙ->~X o|xZJeMI].16 m;K}?Ǭi &htլn1yo0qկ|=_xvŎ1ңwۙFtÝ SShqA[aljp8ǻpb1 K6 Re >mּ5mBm*@K ;՞i'@5%4ҌbI2wTmn2|Uyu7rh< <FgYdlTm~^x/Rxebiβ16'85xǿ #:ߌ<7e=}u bSO&@S0:U'i_Z[>l&xEtyH<{>Hl/Twa\OKFyM%<@+菊>Z+fe[Xkq!F3WE1Ӿ&h7ZxX<" CVH$S@rpN*hxB6]kVIdl#ߵDT)5^ߥ_{;~G+)ǧ|[|߼E^!ArR&;c2}'L|:ޫ:Ѵck4M:O O5pB96PU]/$ZA'x:5әYQ$DPF/VvUr `&F@|Wjef*pH. Srwmv#_i&Iľ uj:|>6!6vf,L>i{y ;'cFMGF+Mf0-Axx6h6ίw[ Jxml/ծ7 zTCK 9# vo_k7U ]Ϸ T*LeaGj"f+ZqAm~ xSg>ϩXTfy!QnH $r'ee/<;ug♯,RDVPn9Bp+t KnkVA6iC-F%xl)ݠy[cH"[i^26mϊ5φ]{"wiZWZ{l% 2:g>Ki=ߋ>4 OC#y$}߸PZQ ERN~W^i:{HYK:NQXAjĕ揓4?^cᦱ|4Mi? }(, dryA±Bku K lj#ťm$un!Hi0rvKD}AdMԼ~ wĨ5ȁ ?N% '㋛XҵޱQt<áK ]l 'vhnG0ya]^r_w$|6g'LQMAr}22 1YN y5Q~ ~B(PQEW^mMk VCaHn#xFI[5XZ^x_ >mJ֟e_YN6գyY<>@9#Ѿ 1Px|54w5Cn9N~5~k ]FoصC$#rI'knתIJ!k>-ek $L<,2Z oc-#oc6,a[O'7" tojQO=G͒ ' 'zi_ xzo4=;7`ZN+UP ]A%=??8OZ>oZN9-kUwB$i"R>վÏzG h%JZvL֣;.G2+>n+<;6׮~j~ |O\8gs6,v!Q85|_O*~=P<>t?GsfTŽcd* :7|a{Vi{ycStѧ1l"-YN0FH'[[{9\g`t5-"h,vfTfi,lx91KYoMoY"v#%2[lr}E$n;oM~ǖ^ ߵx[Q!2NKYׅg`0! Vo+ Pxj}^:^l6M <W|ԫF# |/>]]iz|6\9$󞦟5]-fω] :/[ulFؐٮo»b ֊)q%`*J ( ( ( ( ( ( ( +>4 ||; W5vrn6 O(vdh[xJ𵿃3SWj-ZkPNdG$RUNHuºz]vRy/v\WrIgop$F+)cXO n89=x>Sga_k-?9EK@E-f8\0*CHmy|&s+egŷ/u"K|9-闏uo.xVGxo($s^1OKX)V-txl4MZ-4gmW*Z3 OQ|@<<qQG\yc,{#[Hڣ%':VK;Xk+3w)v\-23КݗY̭shm$BBBT#ݚgY\~LukzwkkeZ<3,ڴk 3#+ @3~2;gR,-.n L9Pܱ g>cM=on—0񆟦cMB洿i d"C4+9% m,0mf x]dյuk9/f s%3W_֭~_[γ²SigZdV|E,@ڽOK]`ԬQETQEVVze14K+9m2pk>"xX\躜]hKq9xe(B$F*hyӶzr4m?MyoZ>0j ˇ5k*%*[_]q+ Y$Džu}4'| `Y`HJI~q4{cѶzr5'⦫zlhi5q}u<(21*PH}G$񌺽ygIa%VBѬ3#)V i밹v[o筏'Fcח|n?Wޣ>yڤjT7RG*$\Ʃa\xZ\rШ.(faā{pn}=l9?ds k8Gg@Qׁxω hz95>ܻ\Cyk3`<I{5'V[W6(C (8_ -~!j:&hwi2Čf=I³aH*sIs>Cbˋ]SyqRW~6_Z[hY_YjHr&ɚ<#6U|(< )y h5cSƾ#qݥѨ[6``)cKnT~"-FR75HќC8U7\MER[Loc?t߉x_fm;YѤ.`d|GF:*ZirBshИ{qT?W)?diz,z?}vd WȉJ FA>&?'43NCPm>IDqZew4 pRJ??W(gL^_{ s֫$ M\#:g Tմ] N4AI3v'+( ( g?ihww:v.aehY$pU#^\¹gKO]1)ij-@S%=P M_QռMs덣hcmc O9YĄ PW$σV%J~2'3khFi'\`2qqg Λt¦lVG^|Եv׏ 0Z[h[Ce- 2:3|8_:N]]U$qwڡTuv$9*k(':o! Λt` |U{;&:tUWT[!_8;Mgx{iKo㈴D/C*& ?\nFyiר¦𩯿xKy_ncԮGl`Յ۬<3F^[T3H3npgے?5ߝ74\J@BTv(P9?!X(C W㿃_뫻w VWQgmq(SR܄1F0Wׅk|iyxAA7ĚΣqq0m&XRI7kShr6-|I=:{7㼿IQI7rw)fq-rcˡR3ejoeޡl]7VB3,$j**Myïx5 ]ί5K0Ԣ-bTD1Da)FÀL^=Ѝzn:k׷W]ҬTn21›_W~~ͦ%%_o{[ F}v`Կ h/ƶb-ZGM itc6&eyQw0 *9V<*x";+]IKyٯaYWZ#õ!P˗%^w_z< u{I[Bg7m u.7 ˫+wko^sXo\[71$/%tkV;2P2w+ h 2 :rܽ/%ԖD \5fPX>uW*x Ӭ|:vڞYjZ|wG]b,+1^e>?񖿧xVӼlo$-[$ZЅ"FnJV!dw-Ik=KʶZFH, 6v7 [Ѻ?&7"t𭦋cEn~FVUo"YT/QMc%Kx0H6OʼnRJ\- OҥEkY-GylZl8mxZz F:̚ňIdltK.įK1&T;pĀ $FYjwNqEVP2Uyk%tZ]#:~"ԧ.n$U>n^i-> qFs ?~Luk|UB׋/l-'41n66s$ G# 親Ap]Ə>?G? j]X[5+$2“Q+nis+y~-Au~#x_׈5 b[xRUbdbk~E$cѾ DvIgH'RVDV<Hמj {k O м=sRT8^9 15S |;W7wylIb^WPz O{v-\7e+7W)cIwu>,.wnSTQE@Q@s:ιYA}aa}X ޥ4ra,lMƺ*6h+c^O> ???m[w2Py*+ sj-G v'-x'㶧xº^k~8fejegq;4 L'< ~@!|4\ye!ap{"7/Z4DyΒ̻tr~1#|#2tx=;J'4{_X4,L#MO<(_/^' G?mqu|Y<^8F"GJkO5ѾAk)s}źV_GkGsXN@ |Еݽ?oq6?}:QPܼ˜Z8#Ksj_k]gTW DP2y=I<@Շ>)kEQE6I$.쨃1*{n"Y B6\`4=qqFuhz*).hп nzkROh7>_W:۠@K6vll'SڊniV!*<@%8#7\  YɊGIԄ dcDHcIgG^)yQQCu |R\?*ul -;[bHY)*pʎԿz ߛ"[K"H$oyhV0A>k○'E^utFn*BZ8UݙT<&տdi2K~m_<{#$k=F8m.!PdIqH ]@{O`8=[IͿTĽ״[6sK 2܄kWD=W/GzE_eW.%V18"QEHQ@pcHR%Ws\=kCiJر1G ,cd{ux o;& [jZ7n)D,O;_r|$O xFmItR[ӳm瑊Z_9j iƨk5GEեC3 fXALN2)sSVM7ƶ^{ nLvlo)$q!\{ak5C֟j[gKvҮ:UwIEbCdYb(:>ϡzNs_\e͕W_1ȑ 0}OC֟jVZZ.ӿN)?C3ᧅ_P]D{~&X<}ImwۜtH6V'+]K-W?nc¯IKVy@_6#R2p*7袊+?i- ZD/5"ͯ$ P$_.<+Y$qu;˝rR:B$i<G+|jy<Kuwo9n5Rƪ|Mn[a,٘K*41U𦗭k:Fylf]䳔J"gBveI0=k^K:ݿB݌:t;o+;K4tgI2إ.bq;V@ Z{}Oxڍ7:B[҈"lw\}U-: _O{rhi8u!O.Vt WQSDE> ;dV$@yπ.>/3j>כ/-?V;5IHDjU.ahXǦVVh`$$I$I$ h>,o2^&zՇk~%EȐ4̗a6Mib >X8!{ Q)x>ky{q`֒]jg]w\;} ̧־56GP)A\g> x;G3Monf-fŽ찡l8ܯּ~~qjzha᷸ˠ[0bq4I$?4*x6>m"L.Ҙ Psph:0hABbVv si;tY&I#%G#88S tj_]OfWF?[F'.D_V_Dby$hwYkygCMKokMKokoe%+ +_sS&E5G @h7?Wľִ!kU26sҸTQEHQ@Q@Q@Q@Q@Q@Q@VŮzNu=6W+L,嵑 y29Xtv7?+5oJkF~.d%akmyLɳ{[!b|n5O[o{-uK_Itiv\7;H9S[] [+[˙֎xH\ x$a{_y,ČtѾch0g~-Ս:s[ʉy?#FB9Rp@(}/}EyVIxOIMRsyivַ׺Akq8e[2F@5}K9yZZZZܼJ*!ؒKdp8~(Wit-O0iKa[͍s^O rBdd`18'ßug6o^ŧoiS++{ =cŵσ~"eu5ީb[I*+4p&[wXsZw<%Z6m ڄ#"e VN}k)V_yW`t(M"{I4Aq-N|#P ssI&uZhzuRsW'kH~ȡTkF ^E]q? 2Gڴ&6H.nZ9 nvJ\sOĚ/|Ei/uH#ImډH) CF $#o>}v+šUtcY-0[c X*WnOol5KMB^{GhuE$ "򠌸%/p$=EIl|{,x+|7kuxR×w -+Iq(g-e[8g_Sp8=֑mI}B@9?`P^s4R?c<V>Kޕ>"m}:AK5fx$kn 1]<S)屛E7'* #]v!J)[v>mfZxúhjW:*̒nk( (ՙf>|,ѵ  xT^LZt1*; Y(mG&KEd ]o٫[MDҖ÷z\VWVzHJYgYЬ@Kw0xmbY1qZ[_6 JI":2&F S[l{{HbVEV0X(ER((( 4UҨSL{HVķ7ZP%ɿ?-MVo=o?2o*KyUEgb[[ zdUhQ@ؖ&ķ7ZP%ɿ?-MVo=o?2o*KyUEgb[[ zdUhQ@ؖ&ķ7ZP%ɿ?-MVo=o?2o*KyUS<|˛h\ yG2G\|Yމsx%wƍ2sdgw?ؖ&ķ7\xu] @ٲ#)cbA^ҿ?-MQo=o?2o*%*ͅhv6ڮqkHgp~dS>_ؖ&ķ7XVom5Xom-$i$FǹST?W<ň,R;ZiO;͟ʊcrCD%}Gjij;o6sgb#'O+x^UkLv@VV)m7d/\qnVp̞5veR}|˚þ8CZ58缇X-+!,6"ŧJo5 "9fmYB'q"sXRx]ܚ%p.aiWQWnkzil4[=g_/2, aHHH<5A/G}kVuOzK\^[E]  _$q^'<4-ރ,I Mr$dFzpǃahbyr6w:1aOEo?ω7:O3Wīoc.gusd%nn%tP9*JWzIi!4/֧^ $2ne0?*edeZh|uw4H^v¦n9{1|iആRB\KIF㣨թ$KՎF;Q7_[QM]r^Z5IVE9V+j ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (?fotoxx-18.01.1/images/albums.png0000644000175000017500000002675113222767271015133 0ustar micomicoPNG  IHDR@@iqIDATx{y\esj߫z^tgi !"Ȩ38^W8W茣 `@Bzw{9u$%I:>>r g28ֿ;3bqlԭ;/Y,(rz;34 Ӄ6}nkZцh1 \ڊ ?v@0>vo=X,"`ps)xi38D Fg"YCv M#W`P(`絳O 8 H.mOʾS^o]?)gYz_N-8'XR䇚Md:@U Gydmu;ZZ?%>02\24ػiRs9 ΟQMUԼL3.9Q8vźe_l6?6p|ﳏzBR E` "hBQ+ iTT!8W.|mdC oީ.T~wh1- YhJ\~h[#@Q NC=XrEhv=xC=Go|fLn l(9} \} ^" +FmBLbWmf]/& . ?,I}01J>PYi= & pN|'+ n'6p[ ? O.[~u,Yi9[7|uchBq٧qsS@,ѐCeMn9qO0t |J8Q,>{߶MWk? 22!'̖tmPts/^,KDx܏KF*pzO5I蔡@C1) GfnZ\k ~#}L3zw?~q*%Ԋ*4N^9I1<0k*$_:dwC;PȠ 05 LtZ( Lڠ1X) '+/#KmeLpJL}O ٌI(,V2\_EN>< K՟F䁝?΁#K`?xBF&9*\}\~ pӓĬ'\.w8wcodʆ}q65wZ/ NuBשf2@3fw[`רN\05hFtᄱxI]GҼfI$Ld~]M=W\6 PrA>EߞZBw9 *'(6:bn?nFl8FCx!,f##j` F\O!-+[->eb SKj.J [esŧ{ob>fV(|t߈&B),:(+.%.hJ ^E }_L[Qfp\>kUN^WIuK36TL&VOUloL2SyCɥ|܈YMhO1ZgaΌbM(oyNxm&8Ӈcc\,nQntD!ӝk^rån!7*]h7T N_εq齭=H9kd[ JR%eRJɎLp~YΏʍj )F8!ÿiY~f~L](&џhz(|Q tL}dHq5!>U[f?y2@V~^3(NvrYe%8Iqb2xʑYx-8S#9ŁlBE!C̕tgn2O^fZyhK߳IT,BsIfQ퓱=[C+en"~BhԈBHlM.F^̥5Dd@Nad4V6*AJ҄BE=04? ;E-[e)lFֲJ~lh#\bgoW+JdZ(deB d8\_M^3y1"yLd U@Ռ/32t]^X{r zp;YlF][YK}%mՏ͍Ȏސ m*y$;rm^Ҳ0L֐ι`W58+clISJby= ƕ&ZL{Y/bةb* tyO%e?t274usϧ8~ob|ybJB.&O (-cw[ %fRXLQ_V F>X)|a `[6 - ֓DCGid9 ̅0+6!YYqѕhsNyr#?..'} 44g B;s `":\tLqs\HL4ml+!ه`dEeb+ak13x1>fֿJzS}kDi__ Y@;Ic#3iD3*fD^?脳pS*JvY\U>2%].`Rوƥ|t- WБ'k-30S_@% Owdpͷq6c}xԎ`;&fWۘm5Tҿ+84$3O28|l:#lCVUf[ o{-rrth#<6qu jW {IHG230KW#Uy`lO|Vu`pQ񨆺UBȈ!CIKJVÁTt۽JoTfL&j3@F"oܹZ'R2~[Ũ.ט/'#z'+SRfwʧa:"m8^-vÈԆ{w/$aqz/ Ù!OX& D@;C`Æ[6טivzmڙD,J\.\O'A8$5@|hCHhp)1 RLCa8X81\ !nK oUֻ 4;Gub*SaC݇K=Xb;5@vdSţwEyƁ(~w fb9#Q6F i SO]1}3WPjրEy=dB ֝y)y%.$"Z@4ƂT{1Ѹ5wPy 뽯IdC=2J ތ8}# 0e|I8d|iMNĊ%3 G[vCw~?K>ELf2WRO#A@yY=Df'C'PDIeb,`T.ǰkz`4mEX !V# 0lDy9EgWT3yR ̼cxneשK}1b+reD8-~.04BޝcvrȰe3aydaS " UW'Ia]h)=BhzC8:q/W2o6̀Iuxn8l>;W !7ih̰i-rྦྷa#Z+t7{h c\n Y/wЇ΀lPmsHk$kfجXigNGHCf0S 9lvLK &To"Ϗea4Ul!uoΑYe5st-R2D]ei6IT:_YʷKWF^Kፊ,)iY,q?G+'?๽ҟwᬎrDrnd4}i"j/w&'RhZ-fg1jg!/ ةB*㽠h10sQH"m(m0mA#^O^]@Mo.:Ɵ5 `2(`rl YҳG:ehyq:TJʙkعf;utj_^M` C/Mr5Hd~o+;S&BXl0#wsBc |.ꂺK9A&: .;H{<G{9%G|=&hoÕx7C;'3ٗ2o(wZfFB]\ pRn]g'LUgFa >l;؎ZY~P)ǂn%Iclۧu%HK?Aa姷渜25p#DuNP_1XkN-G`$b \KȨ &gR/mu̦R Pwfo`(flrSۨrN1{3;4Vr"y& lƐgd=MP,ϗF Fj4睅r~'# s`R( `hԠX61b ,4j|v|j| ]~i`ccp 業l˗!JmIRFс{=zGƴR*dW@745 D2jW-+ 7ű`6LđƍdCG AK(C`d(:nRߏqLmb!@ d騼@a@E}%>L ߰\ք<{ Ǐ31&CtCB!$8_j_Mەߧ6!%q G \S{S/-sc9uVs49ADhy,2˼=ƨ81< N.ư҉77aZN&s::([>Mt%+f8*N [fT9o@l;|/Omy~' MvSN>N3IL+N8i|l%K#H*pDL ,t:Fb&< 6p=EM=3SgtpZV{+=#| ?Ո`- (@eL|P^>g! L+n?~skr~,sRRKaH(F] 6!ISꂓ1*ddՁ*&ӈLЀ01/CfCM%)q GWS 9;>zng,_H֫Lm@鞅fBfx4jnX ,b/"& Y= v=7TNRbY1ց VVa]W5>5% *{U A$|+Ȳ96 FA}al1eZtX,6"#oaU]r }OmZkh WP`& b%§eBIJ$f H3-f+/ rb vvv)\ . 2YY@u2L "$ƋVP195E73"X05S!8&/aDMIԆiuoKQy!y4M{ۺVz\"@Q$y*uZRZ"Car&J@p^ƴݍm8t-Ě~4d S3|dn"U`WP 8㽟x0>qޏT1UaŬu go|庫K}_$z7+gF;G H=W0- 'NM7=liFxc;Vjm/ǒ ncafz vkiBa>L}J=e|AEr-խL=*KN$3ƍCgW/..}PBI"'`{7":Y"ls<Ь IA@*1ܴ GjAMk#-ҍxI e F2xle{1AyUk6ٸsoE K2raq1cߝw}ޝKʥvyҭqȓd$3x8niCzEp-[r/4 2TvW=w<WW#b*0'9$O͕rS+j-1KՆ՛\NzuK^2^>O 9Q@DbΝ'?Ym4Z}?#M}ȝ\L4`rrx+\*C,s9l\VaKT,bo'zc8ٸ1hiLc+J0p0lb@NxP7/NO}Ѧ7[ԂMtAB"D gc+6wuq Hw -t'eZߋtbldƎEYjQkk6Zƻh1 @${8}W6qaDTtd> cvu|-Q$rTvjl.Bwūc984}%@d:6عfƦ<\N D&pO,9ћmnF ) .tes蒏z1*{2ndXF0t j+j/[7XΖ֥~w2ߟJ f;vm\k\Zgsr^ʊ щDMS,:>H>4/"`o󲬧kM۲ˊ{Q(4NgvϺgǿgjĘ2nH#9]AaZ#<9T-?wm?m [Zl KanO'lqgSz_BJ5U4#û1;,|ʤp؊sK8g{eH[q;u&Rg5/YjO0Ob魋YkG+.|s?ohϼ;honG& nWhˣ見Yݽe^ֽ-"p7u::2)0>5z_m{J`foYzswSs˼ᛋ1TPA4!Lq|p߆G{k,&CO{{sog2hYʇxÒa6$񉍌:o'K?<bRtIME :.!%; eXIfII*  n|(2i%@gThumb 3.2.8HH2015:05:05 13:58:4602210100@@Fotoxx:paint_transp|clone|NE(HHJFIFC  !"$"$C@@"7!1Aa"Qq2Bb#RCr+!1AQ"2aq# ?X$3'4xh"W]TMXR~O4SAqfTBPJu4ŧj6jkU$r5؀sWsӋ۲ɷo Zaԡ̱.:Z'PVބsǭJ%oTysRO._ES!76S)I qL$:ⲟrt@{(j-!j+(LwH5ú)JѕMR= ^C1ȏ."yyV- Ob;$8Ɦnu*Ңw"~> 6l}>5s0ձNi;Heקּ[Ni!I# xfgkQ'\w:-9Jp 6@楫)zq`EIáRs m589! ^ďfZ@r^o&'#|lh{H/PGTB:'uD C(Lnm +NGOtp1 U<* Zb.']&e^ Az`׼|{-M7qI?2N^-N[^;_Dg>~\y[v2$em""$kqE$%'Bߊ\mNdqaH@ȥeohTFd4ccee BTpIN ROALJjbZY12ޭ0h - ֶ:g~[BA*IH>:ͻ[QZ}[6vA L.ڣ(e{+i QBnii,= Fzxj. Yv^*7%DP(qݒM0 WUڈsYF4^Y~t\ $Os^ TE~jÚȌr:fE=>Osy0#pӠ J"<SIԍ2r邓= DJ>xIo$# ?|%:&SQOCʱ(n=`VJ@HEn[[Q<%H\Ėe0Tkieᎄ3o FaR:N'[Oyi;)'5pۯ\wbulRC+{)wuG1qak{v1lGSV5)L5/ðqcMdQ9{ۮ)-S9J'K $s*^QJ{.nס GٙKt|: 'QzMt6NNNlV[Q8{=(2B3Vgڛ-IP\{6sC-^Vn;*4|3[1 > s=5[i sՊY9m_iTXtXML:com.adobe.xmp 1 2 3 0 6 64 64 1 0 ,sW`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-18.01.1/images/line-drawing.jpg0000644000175000017500000012203513222767271016214 0ustar micomicoJFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?O_(n渕#ˤXNDV8sGڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}Xj^X DQ Wz-'L? fSGeIVd4ҸKGoxcv'Tl>|o3RP-<N"ȡ*ilti'|_7"|5e9e -.c|+lONjWMg'?DW'{[~63aOl>|g>s[ŷ./t}|Y"Kbb꜍k^?notT74;Ր?pU@ g]Vl>|7Ĺ  ~6k>}k\Fmneiv9! }/|y>[?ĺ5]CUy2%j k۷EaOl>|{z, dRY.@g.m>|PB=TcZ JvGuI} H>k؛Ig#gw"?/8NGǨfxk RZMw\<}ڄ%!=}FM'Hv(BzZk6t}3UN[-g2FNxAs{Kkwi%=AGКXWoowoS:+_ijwA\3iw1esx W9PcDb:tlsַŢ_\ա mJW>p@BvMyTpT}W{Y>Z\RçefG)Xk]i@In!Rr> T^?.?d睬gFwr6 !ao"YYP2{Pm(m;l)֣>hN 3]5}|gᶹ\CZx󞢾;qq 9Lޒ|Sq#.~ ,e`RGP\|cj+i6@}&;NB žb)'|72V>QiiyirI/N횳o qڹm[GTIc7&]34ضd坶#}.j}5>S0W5յ6fǠbn,o.N(eT =귈'MK&IbFS&FJ?*|}iZK*}4M{et}"qR+Iho-RpHdLs@P%qJy%'20+{ mmлs3Ir2`t:KQ82ẒTTFΟ&6[6z ьJD̑I\wǂmn+ g%:X19!TJYE4W2K"CAFZ'Kk7sDj֫Ru8l^~;3a.Ne' Ƨ.V^]a7$,֍nG}yp[/uzv}';3cf|1|A.4v1l1sTܧyVR] >=?cue puξD>ּ[u3ieʹCPW?| YBp7kw{}yQijǵQ`*vK .ab~^) ׵Ndy&۱QA nx-7Ƽ.ҷ[[=CybI*WSA*9:"9t(RX.IXg 'z#5^%ᏁF^ZĐHwf?)`@5៉]:-֗lä3Hb8R_$1k--4J^uW6K3j?<{SSA5Ln#w-nO\d|tەՙZ|Ǭng/BFakaaX$m0 xTcռg<6vHM$>~B3є1FR`}kF> ⺚;"}8@'@ \b|5>U/4[P6y|+Se@=vख,S&;\};?ZI8DD^1Tb vFGsK+/E/o1YYs'Q+/o1G; ^(b0(I{C?/o1E\I}q|=Q{C?.~~}>}E~øm(q|=Qf??>}ElxKǁ5}kA<ۑȬQ_uøm(q|=P[ F|o?9hj7 -sz]!(f\1Gf_(:|E#IXy>_møm(q|=RZw_q}_x=-ss<"ݭaS T WĿ Úz{ưܙ,D7d{۟/1x?_bGikkx}sM$]:FV -ZOqQ?< :p^N3%m($%XH*owQ/1xZ/}χK?Z˪YWSy6vN~R]/_j/o1G; ^(b"U]3 yrlϋ7ֲCahl65Ĭ (dsl, z; ^(bCv)G xE5g-XZ2T%9Gz-ykfbvlnudS־3ZK|,\tb-я4@g"cʚٚ>q箣Y3] {+6h&sPg^ڠvL%ioU}Y!{)eS(\d FktUx35E"A՝%aM`#APY˪isۉVŔn%@?X|G6x;g<eM߈5CMwQ 'g#sץ)AfM`j$tI+m2Xぢz䟔7O$6̲ +-Uv[@\CmKRK8|cO㍹WuٟX񭭥Ɠ1-Ǔs#lf!xAO5v֫:1]N:oc2Wj|=?Ne5]sKS`bz]w/z±]Ym=ȉws0@#  ۩xYc_U]8iזSzurlYyAUy|#h>i{vF˘#i $\U5&2FE UVQq8p@8=wii+.b#f~e#}:JqD>8ifa(aeX{ר_Gӥy/ ȈUGN>Y7Gz__8UC:̦Vlp YbYOH%AGLzg?tiGZf4O!?inR4I2J[ [7 #ahF=ƺχZvBA`W 3 5&BI)+K>7Atɧ/[l?x`q_&x^TjnAƾ2k+:L6E-`)vgvI;i <{d87nYyf3-H벝OsCEV}K@F#׀PX~ id714OLVmd/n2wtoj4-:F]bd:ҋ]7i_ u? ٮ Mp9< 75}Vmqnʛ.FjR?5ˍzl(v!QX{֖-i Kq, (`y >-Kr-_Z}kg>> L -#8q [sY[=~{+ke$K|)o w_WHxt𖣬5voI_;lV$Em|:@5 kn]]~gшbەB'=3\߈~8i~CvXyQT\mc Q" >$>,עhLVes6+ |~#4+LlQ,- 9E?x3Ow-x'+J̬H]+{0Vڹ3Ew >0jO2mΏ=ս6q b2eyň> ^bZI!2[U \pi%h>(׶w$7T!ny,HS <7ZfmqiRh+$$Kg-$z|[w _喛{WEҭm X#)RmoPqAbIo*l:mnJzhlR++XP;U6OI,{gJkYѼAj%ܲ*Nbsç]}|# t^:ȗ6.$U]nϖzg> Z'ïkS}V7 ۆF>-Ȼ84}jrִڿ""v[[VL#p;FC_fOZm:4fPpXKhf)-ye:<28<[O+<ۋm^1F ATr[c sI{HoY^'cjֺJ ,C$dsºj mw/ ȆIcnb8vy V5>1aI{ž&HE1 [;83ޭ+Y1^NC;A99%U-:}̖P%D$ڥA8sҼ;ǁ|4VĭɲO$_`e;Ctq+ʴ kvq 4WEeiyVy@ y?4;_Mt=\I]fBmZ9g).B0X[>ImZ~6Rn-2;dW~2Aɮim.-s%2Jʖ lxᧉtOxgX<5q\h``"DE0i+]J?mo5qAxC&eKB(1²2#MWnoQ=!\vZ<=؉ lPվxZ^eo6[OkoY캄w3Hxbφ(> _M[iakRo06Յ "N0/_wo]꺦xD'iǥqZGퟋ*WoR}U vD㝥w t4/fOͧ#w([CmwJ%3y5oTacsmǺA~˯(MUn[;PYr4[WTt}֋~c{$K0G3D!el 8}X?2y3KO/V?ƾ\wxS+G;Ÿ1\Mz=~{LW_?Ht Y?!%gkww?b.û)C tG`ϵ>KO/V?ƾ\wxS+G;Ÿ1\Mz=~{LW_?Ht Y?!%gkww?b.û)C tG`ϵ>KO/V?ƾ\wxS+G;Ÿ1\Mz=~{LW_SX],- (VH1w pǝ;|4p{y }kn? 4ُmku[Yn屵X&BLJL95ɨ;샓sVg#ﮥ,cpG <270! #-B|aka{DY]-یOU_? ea} >OW ~G[?V+o7H0T/b??\o*/m[c9 X #4V"?ҲW?L|Tf3 )ȃ5־]y淎Ǜ4PN jf]Ȫ$ 2nFk&ּIP'|^VZw WשqW*QLu6HQy`cNgd-[Zgr&0}x\Fխ' BNz+gx [xJ<R `} Y$yr߼ڸ $qȮ'ZcPDҾ sl8FtΤ Ե Q;y|4TW&pޜh|AHd7QCdu89WԅRfJ9]I3ltM_tѲK`~l `tkV7R.p;!]|AcL#9rM-,0 a)^V?|灓a[>Y3:fU__޵f8ZzOtF _ E4؆ՓS6y>Qqkr%[X`'' N.j. ťxDďp1W=L4vl"Ir`m-n4ɢul ڹ~uƸkBn*}oRc6#9T)<+o9=P\4^ש1"/KO9 ^xKTq[ԗ,N Q敏ȥ׌-{տ+{]B5BxE-ogm_ޭ!["Q@uZgjQi?+ΖpA c@*v <;kouܯXtM봠;_ݯ%Ο=Xh18d?+74[chn5 ݌'ڢ]  fķ#]HL˻AW57Zڵ+F%,~$UC9W&1{?JOhgW܎$,]j5~X#u̬}=>NNO&H A1ZXĚ|~}i hZS!vrx?6,Ɲf+A%ơb("3$E# GZz-Cz$t--dpH G]¹/ٟ:|Vkb+U.n]XpĆP3sX>;4fƚr˳Pʭ#8Q$)ћbx_[]/%КsùEgAJN'mo!o?uwlqGxn'_zhn>}:׉\xY58t};zls4e孥YWL6Csq5?.ei:mM2k9`|W_NyEGXӵēE&ĪƍX7#9Z~'~߳῏&K_,P1r+*`3]d/ 6h9n>ݩ8G'G PXJMA /SеmRVhlP*똕:fP?4 Xjhd6.mn }ت 0wymߧ޴_3X9>T9C}<"Y<ѬdWR[6$_A g=ge닯:r8)4Y-oaBp2yz{;\jS6wF `m88D^Hlgs=IksnU3os].sJ'o~ E 0-% ︃ӎie|?JfY.hC 3<mE tj[\82‚BXў38hx'_i>]+X̽^*iɴ,VoEC9)ӮՅ:* 5?TuC[~Hݔd43 rZ ֹ~_Z\$*ȱ3a<&U!"d>4[u;Wmw^ $+DY7gNB ՗JY gKɉI= Vd9kEo{o;[Od`0ڻ;+ç@F;3< }}&`$A T*잤8QTA4QE|O|NV;u߈$%nBSk"=ҍ:pk6W׿tgVѬ|6p-h+C.GtoxㆳXI'UWvz o2ެclxÁɻko;~6'Z>Ygw:ķ^'/@1Hfw*B#9$#o;J #Ulqx<;QExkoom|Atm.e[{>ܧގ!ы]֧A^^g^# o/_7 G]٘/OEx5 o.k -]٘/~GQ^# o/_7 GGf7|aW[K&h|M2ha?~{u?/Z??%@_tfc̾OEx5 o.k -]٘/~GQ^# o/_7 GGf7|aW[K&h|M2ha?~{.[jSZ^AլRHeP xcB𞜟midc{ć"g.@T{W! o/GSqk 1y-[Crح`Mil`̓N\:qtoKFjӷfgOK]O[q:u` 4X2A$5֖iiXe}wv I rkͼ1⑦khE{ FQ&'hPMroEa_k>G%w~ݢ/kצR  OVUjoB')ϊCZ[F5PE<T{ҽ>Sj6q42mݴ} GP+7Q5\=ϖEG*ֈOWoC?w|,d˱>,8[F&ݸG'\.MʫWA;qr:4h M4ZH75'~>gO>$OꗗwhNw]HBnim ;DW |;}? n[H[h`V#*rECi#h& !qkkDܷjِWP~ +&W>?uEr> _ r;[mB,? rR͝9ErA!z:9W ~յ+a]i'? -m_c1K'd{0);Tm}q0V"hO-Cp 8d[[tA\dyߌ>-'ȶ!lp`>:OO¾ĸKI]'.ƭV^m}4_U[w$G$ݟ[n|-<83wm`Oc8'ImXF+C̖_ ~z!drԜ+yH{z&ՒN/UQrzʾ,um,RΧ 8`ݽ,F|3)iTFϽzí\]k!<6͌u^8;Qߋ=Jt]"AuV_Oa[7yկ5{o^KdybOmwgoZKtq@t-գ𾇧xz!cK8.vq1˫ݽ\jw +i۟Y$;>~%ѬN4bA;{@Xkb t⪳9'5H<(K Xj f!3 U2S$ `q\ơ2~ PxFGq,OӚ4g]/~({ KM*7ԯ[+w{}r:tuV9oO{pMEoI9HkO5 V 0wrz$r>I V)]7~Gٓ,Mz=ωb*VM7- ʽ {.$p{>GAK 'MN7}.|Ϣ47a 6 FnL*ۊ_ǩHV:}G)iegX0ZQ+3EXZo+QZOwow۞.5k*#lQ|k:ZqmhtHVذ2QIͽRK'y%ysF,}I=j:ZnŮjWDdCi J(&m_{o"[Ny3I\{ ـZRrHEZ}y=G)=7_׿|+ŪS.Am웗^,A8[5> k_ľ#HG?dSQҶ+yiz4tF xpwGʐ8ex&٩X̀"K_2(FF۰>޼zL6Aq{Y*aR;A\߈u]JW72R+u|pw5֫N#IJ =Hyn++YNu,LQn==+r鞼U7Db};O{&D$I))} !OCgDrdqI1m{%wAky\*"ŏcӭnh-PWSЂ8#ޅMtA4<n<?ZoZtZսŨ\[UHwr{ t|5&FO|#jݰ,THhdkyU:2=GQRCyHFE%%?g <{y;%mdF'%dOs^E4-s~*Onf?JHچoYus' czxu4!U'l?x' ?7]7|g~}?út2jmG } 늹ks{&g&Fa,8"dq?x' ?7G3O%^ vפQG3G4!U'l?x' ?7^EϸYo Wt4!U'lzEs>dy3O%^ v WuQ?x' ?7G3O%^ vפQG3G4!U'l?x' ?7^EϸYo WukD@f䴏Mɧ=K{%lfF+1]5dRdRac燥'P_'jZir 88QPsM{i~^I{u$&r0dEDA]D֤m∞ ?A7BQ4 (o пM:?ztu9 qEOSm|uѬ5[Ag~L,) *F:*ֿ8xܞeG_pU~$E"`dzAI=:mԉVTxUQ'Iu0CS½S{Ze֚fFi bO]FWrΚz֑jeŜGxR\\M }Q>FfTԥLW$ݎO+Y-1a-oopm,0}{1_0nbV!MVbsqS޽ q/ƶ:Š *| n؜*\/nևƹZ> ҅1]$Bvnxׇn=Wkp5l\,kQ_7xij^/م0(MPDT@ǧx+WzYt~'cԏS\{KH9|B-l|]ej a7d.sx<}*?kj E+7ʨ6Ìiŝ/Hxm4ueܗG$x'48(DEZҌnM;ޥu;}*$zWܭm5O\@̵f?| 7&6=w]I"J`Rrs I:ڂ WV#"8ң)7.X++0MEۢ$zώ-#N4 ۷Ii̲t?r^#FW1*٦=\/xp֮G*!C 9&kB;HI׻k[Ÿ#wJ¦2cFxmޡ$+#_Cmk)Uzu~FzeRyf =x}]7L#tRL 18RY> ]|M4ܛ{~:֛T7}o)|ͫKy\$8{C;GG6Ejsx1D? @sCFu=Fcwwfv Ev w$#V.ZlJԴT{ndm5y'W;?gg9qp#޼'l{/ x^Y(yySHp#ӂ׳hZd:sx#;F8=bGE=>u&.>`2=kUoʴzc2Pxঃ|:r7YL%|TH o!m>HZlGk[j6yl&o|KqU\bXxƠvQ hоKgS }j^.?u(|3L.Yv ;Fs~t_K֧\I\6S+ӿg3FŤZ?oI,H#b}`jS;h-p81+r=D F /ep gc}=4ϱ􏍚f76:&[ &<1N,8'մEqwc"2bR+uVn$ucE ړ3 |lb8{"^-wz}#Gnn KIG+9ju܆lHtcf$(czWxr o|6,6vD̎LC9Ul$ N*|_𭷄t=?W7Z]/-Θ $&7Rc]K$88S_sE;-O~$JC_Y#g/8i?ȖKgPo(e3iV64q"k2G(8vцv(i5j*ڿӭm,?12UNjK:Tcd? k7|B,׺OiH4}"ojd->Ȉo\Ζ6;\{}FkJQ[}tBϰQz?&v)]xKkki&@)v%?[H[Djf-zrt)WN<AFj;FX[n V#< vUNmOYmlVM<8`2:sߡo :Jz/D6r @;=F%EssÊe6F}/4.q"{J>!>"Σy֖d[[o.<"~5wLuԾX$IQ]CTP6W7*s#uiNoɽw&2Q7cMR,-j{:$d C2̤~&o >g5\?`פV[y WZeBg .\c(3Ի -~R+ Q}}oleM"e'/j)N #Kf2*2m?t' rQvbmkM;ĺ7KC^.Z-;@$2,'aa׼?h>nb"KoVXP*Us>*}46=>ß+m_+V栗xm,o- ʣ'Mnot !yOE_15;@־ hpi2=Xu]nbx`{JOUe*I9b:fER\.x ;sԀQEuE|W[>Gx+^"IkjfHvBv7?2HjʼÏޱ+SկZ1TEV}uiZOOi_ f;T-Iཌ 2;IIƓx ^:~k* ik+&y 6QkEu5tV[8È qD?5>2.:Cxw-m+:(C;.1W}Ϳ؄KMC ksJ3i*G}X@K <_44*aTnMb#fKNӵGMi|5kL\~ӭMH`glP 'i}3@d76r398oR5pmη$ 6Q 5KGr<ٖ%H[ \끛x/@<"Ӽ1Yk;%6겳K}[''yx}|C 7$_%X~8tRcy':F9Pz"YwHFxҮt],nYa`B3`mok:χuYZ4ncS Ǽsq t.zio:m 쩐1v!iL2>؞(X (G2ھkL"yquǴ+)ެ+<M¨8a̦!* !k-B{-J -]pѸ}z׹k6iӷ>bxBr>y?Ֆ.OiH; -ZgZb1<+98Tt ZeE.[IHn]KU^<q*hC^]Km,0|on5)'NooD}L] KSG_ʩW⯇<6Fs#;o?hFj,VǟiM+jw/,e9Ҿ.v[_8ԗ͞/z>F+->O\>/|ZX[`MCGx9.O H oǹ:޺n/$E`HPp1}0tVwyT-Ϣ"|Wӣ񵎯ݢW2"U8p*SKմIm;8ﯷja+r}sLS*m,lROTN 5F&:R5'%Pp޺-&;H}.H6mL=d\}ҌTvG,:*U@\ת|1 y 1w xBRr?mE((Io?$-x|_7I?SW???κD2HEma d,; ~ZjO<>sO Oq@:?msiiP^G(G20 xC_U:w[B̖Y1Ԝv]?yHs@ڪ zޱφt V4Miɏj)>l p{RR-+ݜ[g@f @sGN>sK\-UaȺ)s ZYk>G紂#dD[ɴHs࿅~3I7mZGw$1Q(.$(ĠsNN JѨ|pԴwvmp*Zqa^#q#$9!~O➷~&xWk{dW@ 0 sʒHE,3;zUz?EMA\vwrydjLQ,lW n-x 6 B%q[Ix0|mTb3d֧'v@ڇt5+ka2I|.~V/ %EXs./nWj% a/͡[Qp;Hf\Z1䞛s2.!M;M]崣.vXW ^k ioK!Bsێ.5}>JMC{w0WNB@݀2[qOzRqrN.(,~5eFI\b%9.Xg$ʹx6mM;Өt.00v H+|2k8'wHidcX"(,#-Lu~K,MB[|tS Ls]L7"%C@5-nm~,Jen-^GHV vb WxtߵQ6+C]c$W#`3_YN!706VF0F.~^m7kWxzygIwoce2Ԋ֛Y忸[ ڥmKs8cߋbMI6GƓۺ.2ra&^Hmٯ}AҵA x`h/@C2U }ao<.NXI-hĐFɝa{w3◊n|#^iSǚcilR qmUg1O+/bmeLZ˔Hd%O ӛOk5{/~~=/ ^E障q=q"kk.dh#`ʔn MBs ì{ʾ?[Qo-~]r/Dbfi)0꠰ ȮžkqH$,{ 3?@|@? ڛEԵ-GG՞Q6)R@HFf*snngѿ\myNspq·/@5oll5WdxotHVV A_7gB*r3+"U.I*X~FzgWw>'fkGOB@4|#g,_K"#(>㛏iViBA.mNZ_cˑb4eTE_*?xj֗uEQF.R+,tCp&TZ+i[ݯ_lf2Xizeođ8/ +x 9iPkm>޾{C^4SBV|=5:wq,HR5Yf$d.sڣh>$Sյ-)"^HХ\-9Br7d_gw!7}AR=:4[&gqiՠ ".L.00"RwW (aEPEPZpJʭ_ D01 njSvg?OY/;b.^U;T4 \k7ѠwW)|XҟxMV[熑>~(i'C^&eVwnnV~gRq[]3|Q#蚖6jSqWU9.}k|]%ơ\iޅ3#~Uj-?*kEs}X"[EE,pFq_eE4?X# 2+&]sJWN4KHŷmb8n<)VF~Gաh^) ߆h-d"^Wo]vzJ%%qV~['MCXOU??/j NR (=ΗY{Vi7[y_Mqw^'M[e6~6IۿRI-X㵎gv#{lL tRb*[Mm{:ߛ;m.!F!O+.uW(b?tO֮\Z(v,Ĝۚɤ;O6܀;c^ù<{WgBg.n\~KVI_|U7OEZCo4z\/A'1~xsźCiMͬ@hӎvAۊo4t x1JoF|[9SQ׹W:QRє#;KYfh|E* *} Ұ|3!yn!\p>]"i265fM9Hȷk7|^3@r8";\yKT/C gy ̌V;5sZq!{G_sTPSxsIX5-[tb~P0}O'UƨO`#'քjOOA\ѧԩx}MYKM ̂*;MԿ,E%}Yon-q0O#<כW zu;=OlxStf\gbv=v5y^4LbN>R:+(GA7  J*e XNԀ—(F:|Ewko /!gGAW1jbIjy;6Zr֟M*G\'W$wbr0\Ï _k:5݄np:Tr2 +پ|Sc]JXeƆ-=C!YF2 ճ^+g$a:Vݖ.d |Vk2mr҆ ǖ--Ojz3ku\)g{܃}k G0@-'N{MzꗖnMfNX$ߒ3sV׼nJwi3Ua΍J*+)ͶMMg:<7ghSiݤR.%ϘXeL|\ sҹߑkKlkjīN oY|_>M"mOWԢ['q9[# XsҝZnN[x9n5 }VIܯg, @MŸ ^Ek*JOQVJU]e-ux?YլO[q~蠜ᚧ躆)AgV߈|wk6YyY/˕Ӟ:`U5Ԯcc90&3C`34^I](c淥륋E3'=T X-vjExVu[GVV+Owq3vEg 3{ mk3=j4oFFѰFUԂzwe(SĩDr<7cWv& c#b@%I o{t`[[ *+7zt]J?GҢ-Om=o:.4 o{t`[[ *+7zt]Jkz΁l*ã+Tr)?@) X/f|NͽYZYgbXzw?-Om=o:. o{tX|N- ŭAL6!( )!Ԗ''%kkE]xCS@܇Vᱏֿ$t]OP {>[+wCqxrXr]jv1ɧfYG_y\z/Ý?IXmJoGy39 *d+bAnHԒHͻwaѴmKEoQ9ɯJ)rej}Ɨhuh#.) 8#(V0L~8+?'×Z/ï_<u.Rmzggx㿉m_ζQe /9X4# ^0M_TR+7c¼;C޼nb*6I кeD w˥sO qN`#Rr Ucʞ8&^f -okFDo1AVF89?ֽ_ jH5rl-N?0R`&tŒGrz}JF2~?mm:\^J@u5xÞ +,mc} sORkYX#[vt3?x񓩤4GG k=Yf+kK+U(hܬ(y;u8=gx6\2)dL|WҼ3y,d8/X}M<>S*!Gݎe2IFȕxujPO>F|HZG9gf9cU,= Y%Mrl)g:WWcWk.%'8=k58o2C·!qޯ[h`zYDKpyXDk燉bڕbNN|ծ)RJ\N{lky5ſ7"ǻ&Vrd_NQ@Oȍ/\覯}k"uj^ֻLSE%Ͷ)s;E\kRK;wY&*]X{A^5jΒ*\>gnO$~+b8nxH~ Y=+ aYZEN;GxC]~X_˦yUܰ"L I=+'|+[i>|G[MG޻!p`1W;S,0^ZL*GRO@❦y-o e%؜+¨$\*no_2xju-5XO(B=1;}7| ^RQa$f I+4CQ4aoGk#=vsq>8xXx!Tf 8,pf,gS$kuu#wtbKqu28-9/uD"( bڤȎ`>]w=[~i*4B{g?-܀}FhGͻu#ˈ(ȉ :Vȟ695>;59k ui^E{s0(9 $c^}Iv_/6KT-kU sPpA\[7IUS"C (¯nML)ڽF'tz]F;dl&/b˻onk|YjK:UuOuȧJGUK[O-E$ltE"2J&,XܵwX?T(FX5f-^dOAI$IEt:w_.042?}O{JVܤmm-pA<#K31Mux[HKqk=Ul$\K'"?{ q$ջipȦ9oXw:.>pIighUO j_>=Y6Kkԡ fDV:ncgwbOs_k2$UpO@R = ;ec4&Fj_t.V.W皟U+-"ݔO 5GnntGtAS_k4t[~Ɉ)ۿyךB=qֵMē lIe1])(\t[4S\irkDIQEQQEQEQEQEQEQEQEQEQEWzNx[n#kU-rX|灓a]piy 8iI640|;,ҿiЧ03F7J?  +_g Z֛CϰI!+\U$02*{IsGPӼi>.=F:j[k$Z- (RI.[!;^c p41+w|#š=kZVMB#LsGXVYʿ6>`ǯ{ĺ;tM9Ӓ壵EX5Iq I%K]?[KoJ-ԒݲIbe^?u7kZPcIf'ܓLzWz2j~_ּ-BO>64I!<W' ѳoCu'<1W,tb2L#Eo4/W~*x;Uյ=%eӬ/SF X&,;'bDD ΃z倮g¾"5sPKTޗr4}#Y!X$es_i4_nד?!cB\Ec p֕ww:/ [KHbտ{kԆ)n8])lmk__]>qm tV_ "k(n^$.*ɡj@ݛG:DŽ/|' i*\66-QPAS{"?Ҿtw^,r_H#p\@27!k| N~U}-"҈`>in3KyWHsS dv)$RYHZId$j3Gk!1s黺*p4prDI'=:!BF2Z1 IʊNbڨq2K1,5"6dߎ)dوɀc'N%*\#Xw7OSP^Bב䞙!\oK}qZ0ͧ]#:`w~S" W[?J?e??tKٲ@r6\C5 HU-0o.7Zk8aԴMΝu$#)Wq{EHϨEUk!>)"6t~}% fTRO#༚y1 J&`Z<=_\ͫr\2;sֺo|D\'G@`*o xQXrP04T,k9Nm9Y?]z5ʴ[q02B-t%#,iքA jQǩ.n{؟DO],t.5?1n \Gzn6hY7m wi%!ӐaD:l6p#npp=1\Lv:Yiodʺy| C(3^՛=;D{)s`ħ,KP:n՛\5XMurjnm./\lFR 㟘-涷yKKOMcj _2Tә>麐sDXKfgf15(B-Yʦuf_}b+nF KVvTQ 0N~n2qIYwH͝Gw##" Gz8=kK8±)J 6Ugn=GK{գ6*+s~A GUZ~|fO GO!DZxDo6E5/ ֿȍ/覯IQE(yi(4c4}hb9c|]?xGDmf[Y/!8\ׂ3^Łc5# RG8~jw km25&\H < AfaRAA߅u%$s+[Y茗od#\ӥ7{ޯurtK%e>݆PpT,z#Ҹ?2wz0 [ iahv%"ִ|c.GCO xkNmRK?̸T\3y-Cvzmw/H'+Jq~ҢolO(_ ͬ_nVנ[ᆁ%ƿ}]>&EVa4 >D<"+KeӮnlfH0 [8PɐV5 U]Tmo|uoJPc𦃡[yֶi-߈~{mղ72ͅT~kz+V95| ޤW&\SHv)߃ ,=ݹMþ:b v!c5̪+S՛8$ׅܿ$jH8%ݷrFm9f>*,DFهPݱ-P?zsKwMUsäj}H?k_U颊r~g(r~g*@ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʢ_ ?_ ʯ<kڴZ6z2 ȞR!#k?_ ?_ KoJC%΍k!2@F}TOl$M*Pcԏc^QQ-- BiQ R"1q:>R]c7R5\~/\~/-@&["2;"R0Htq:?#mxnA2"˟˟d6kţu/(S)G|*Bee)PA H{O#?ߋ?G#?ߋ?OP,=yZw:l,m"Ӱ>- "E'kTU<7wOjk*>,#F}/.V^Twh=4JЍ'=UJF7 _ۿEVKJٗ8EFZrbT Zvj%ؐч~ҽJ *5"VG;Syx‘9%ҷm\M*ӎjjY.kwѵ),E2/Zxj.58m{pN;)'o E enǾE?Z^/>/k x‘-p`tMaj|~Y O=[#⼯YC;=QŸO}dLLoG$$nM42>)qCjշd[A'hh K~o&W=#UVa|UܥuyuΣ6>:qb<]'"͟ }VaA}~uOg\> h׷]c)+|pdݡڼ?Q{wW|W"i>Z(#8n9!{5:8kEì~,vm"s&|w: t?? dڶU ccP#*m[mNgb XLuUJ(<sJ(AJYt#J  m,Rwx̊zL˺RO͓>_%.0pr(ɜV-laT3`'PDf$QbMcgGaqa/dBܭW}n-:Wf4%2fk: #?>՜ॹqY%xX[9~Ѵqm9 JVs%4Iy!f,Ttܸ/z.kZϡR0UGz!2¾ 204 305 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ 5HGLo@^D+u]J]}u ;QSy8{:wi Z΋,^&XErJ%x$D}v8]Tou' Az9`\`#zN&.tۍr!dhmFe2F8'8ڎ|#?{usC5lQxUPp215-ZoE-v#?}'?½@ޏON'ڂ]hRbܗ''=VO>xw÷3{{ImGc[Ρ)%$Ӝ8{ܫ6\~l>|nAaQ~󈲫铌ƽ_w| č+?þ)NWI"V̏ A'\d} ^O>2xT]tB<72?LRMuoE[[z#`1$/z2#'+4A/?OӴ~ IJjQcUQGcx?c=4]OTԵ- Kjk9'*2 }?Q&kuR/I "DWs{ O*t.[d-ĂI% }8^6 |k zJJ+[6T{ 1c\G=rsEZݮy(aOu:^?ku~)ӬZ3u2a%#xSh8'>o |3x×Jv%ыdXډrIlc&R4̒]jqHi`:@$z )uk;So:q-!}zA-iZA5vOx@QG}7; >)o1G; >)o1E\I'ֿA?aO[??aO[,Oi> yC"QyC"Qf>>4φ>AsgG;߿n=ϊt5a zT7JvcL|gpsv}_nøøY^_\rot95%(LAdPnO/GGitU" I#9??aO[??aO[opN_O= Z&gl-%3,/4} |ad}]ʷBh!YSdq7O?6vyh*u XoBu} ¾%6>2ֵuy!cg,cI'#dcF⧴5[YuI_rGb#}WC?:QWC?:R/ ǺOurijGj+^;[[Sjn1ϽMaB@4-l}hvy-;B}sWC?:TO?WnVxf%cc oc\d ,T[{&U\:g2|{Sg|uG Tӣ&o:UH@,ǩ??aO[??aO[o'm,mxvAWG[W' 1?x>VAJS{u;.*8V˃Lо8x:U曥kI"^od1M0v?aO[??aO[\[3'֬Ώv6 $lpB2:EEf+ \XJ22V/X]kzڅ\m5-2R!q  dEy?}'EӦo ᴓȖXgDn`%ROMɲ~hG~"x;Iao٥Z_kq.7+HG=OYU7phjǐ7~Hȯ߄s§\^ uMbᴗTw# VBpmw&O*M}Lct:s t; %m-$%#eִڟ2C~~gTsڱ< vlzhNTBO G~wB(Gwy{.F F80꺇m y}"Pk|WlRe_ou|[ZǪY8;c#YT^3k7]uk>e2XHKPΫs+mᤚ"|4ִE4=i ɴx5~4믵yLMe81XѴ{;YA-"$-?DQ9a8W}l.ʹL?2: JFR옭(~ {lP"I#yѣ` b9K{ܾFMǙmi/Jq2@=G9TڅȖ0ќ: b+ 2Ac{ +/Mm{ɕlmU3K5 -]h'c$Rj m@c̊3>f)%v?cZw <1̫iD yg?*o zW?Du:U|Imamog GId E2*q]O?Z5^'όmI{5-\qC-c:L;>Њw-{KmÖr[K{U<"x#׶E}dQ!?1rWpq Wcϕ_ߋ?>' ך>(|3˭O_/Q%r$aX *ô'5M|o}0%Oȩa>V¸oCi 8U5 c8:JA?ym1*@(((((((((K*A#q,@I+K+Z@KM[Tm>Y avmG)H.;+ͤX] mu"#Hir$BV TA+M'[uhv\hv-u',8Mk\+9g}VH4+)l.cR ݸWf>woL6n[-S]4K<_aӳW=r_)&t-u{j?(GA$+o a[ϢΑj1jPd Tv 1<CV.?dGoMѿ)5(ROЇQ8,Eά <%kV)nu<͚l:/r[rb0ێGVVhXg/jZH4Fim}Z:TLJq=hf/>,N[x~;KwT6%rm#llg3)Zٶo3ė$"nf+2QXuR񯇴kǴ52-qdeK2*,Ӣ*;zG B/i~CӢomfWwX5i=Wv@WeOY>E?U|#CN )t_VM'"SjZg]3gN6տ? ѷ(^ǜͿ|?4>Zۛ t ]ݝ}VE.hHH1Y%prr},IwV.m? M Ir֗unah͛I j6qt4Aeti@HW9v;bnoɾ+I{!KEզk=Z 9tՆ?#k,=4>[Dp9w|?Y𾌃VKs)[{xb,H;<#<pÎ6/!w#>htF' >aĢk.pX?Uի]ku4: Bnw\I &!=X<=G?FBѧFս?z7 z?3g4o\gWjSXۺ ? _GTGjPZZ=ŭi;L?v1bY@ JIHJ `Њ5>F?V}5jXA"\#+I))m-O޵'[_ ~.%Ԧ[A' |=[KLtM#N>U IIڊ$MhU?І~7>I_/ZxZ}Wu)/}&--ppZQ"FuquGៈo?cxagt=[b"H$;rDl0N _FQSwOJǖ?_yLojoݭ2 n*߅e2iwY> /,CIӤݣD\BU9HRp3_TCI]i YQF !kk5azk-_GǨh:IOE%bD\q *տԕE|5'%x[XtxOGI-}fڲ>bP珕իtGd"X>Syɬ? Njoytt|˞8=ψ>4sIt 7KԴDH7V0Kc6cۤȺR>1Dhg1FjOJn7hms:-CV w+8ZiUK`p&QдdWᧉuzź$]f[mI%U7N0:j/e k;)|:yo.ډ#R5SDaE~_rgo{tgƚoo5m]noI.:9,NKXTMwʲmeJ2W=Xִjw}_~Uā\L'G5曯!Ჰ{*p9TfgvgW0_AwmEsdr |{M/RjVy6omo;XoN``%dX 4{0u~74}>g\{EnuKK+6)$+Ua*O|nF KN /K{{lJ!F*ܶ>5W4gHVs^iퟕuz.$$ vw0M"[/ư[6N ̲`? k蚹$D[v |BsiNi_:\oi7cBT"rp>a]xGO Ǡj5ġi-6v>+V)z~!iu-y y-緙@&9"F)p+FKN4hmeHWܼew* j^OLJQ)O[c 8K19Y_ݯX#ФT ;7'Az6u+>=v;']Qv E*9pnvt7We=tTkEų"Sh^_|_xwz+d3!Q~QVJ2H}&-2íi]<7L'9's#y#T'w̹%ma?=|;ohLy0Mv]>@cF/{^IFyfWWQY[Mq; T  [|P"E׈,/f_jrd[եx<,NRڽf充FX108+o]f~6[N~-w_fOgb@2ܹ0kjqx5-9͗<'{g<((((((((((((((((((((((((((((((((((((((((((((( te[?1# # F?_v #W/;G*(7jeokJ57yYҢ3|_ F?_v #W/;G*(7jeokJ57yYҢ3|_ F?_v #W/;G* ͥY;ڛT`C$PwAҀ'57yYکsC>|5?3͗#P#W/;G?3͗#QsC>|5[57yYکsC>|5?3͗#P#W/;G2AuIo7u5Lv~eKVRG2~UG}oj[ӵu J)q% 2)ww#Nhr((((((((((Iq]%s4%.='dbO4JYG\cf𾏪g 2m;*~`` } :ҮAŦ,NK+!##`p=K\7nDyhxN :i+$XGL#z5JyީŞ{U޵agyo,H@TU VH"x/PZku hQF'e6B3Pv`~ x7Loi+ks]Cog*UjB(Tqxs7 y-V,C9mх9v/m[so@x-lti)mHoe}noo>*'^.!HMMNڮ DFp^I[XK d5;9w5pBnuY.#hܒ:kҴi<ÑmYEQ@Q@Q@Q@Q@Q@Q@Q@Q@StMnͭob2GB A( V73°?Xx{xQ w_r(a};9G+( Wt].7 =m ;85{[ZTFޅ%xN1A}h1Xx{xQ w_r(a};9G+( kh~|8%K,+3؜rxPQ@fotoxx-18.01.1/images/copy-move.jpg0000644000175000017500000005335713222767271015564 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0230Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 187 400 0 C     C   h" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?*PfobIg^xKy#foRA"|ć#zW>75:{Cl}nZ#{W>O 1JVmyYjڄ>d d}j)udK"8[ƸCYk>PQ+Nc.2 )޽vYŕ7I!bۜ=k X*矘^_Nu:km^+# t3D=umƳS"k0FI`WVwCOQ"FlcqO;>*9gD>)oêjIl/pare6+}e%a'`XEJv?QeW=Kqubt;im-dw$eN8ÿ/{u u]VZю ZĒCȷf,JF[?Sj/ci_| ſ_x?߄5ٮy]NJ^{ߖ)yec c.WO5H;:LJoUnk)+$p6}_ϰ>ҿݏ}+-Gi[~+`uXa''V%`@jbvx_j:Z]ZLP>Ѓ=9ZW?4S/ci_|  Et3z\Wl֢gsa~f$/hGWPxB7McZ"[X>H } :S' [ϭ>ҿݏ}++OڟZ]ic{Yf :Aa7r,o#U#ZWĭtMK~m0o008 K.>Jv?Q ;-Ե7ZQ,2jqippd #ܥH ~".AZ[Ƌ|Pm̏$_w{~z6}ͥ;~d숻=ɫi_| '_XXĂxu^Y$v"/^@E44 \Coqf\3@VDpr@ .kW>ҿݏ}+ eh1Fꤎ_ck+GW~G _i_| >ҿݏz>G0XJv?Q_z9Wcbޏ6ҿݏ}+~}ގ`+Xk~sci_| _k,]-lFN#YObIwMs D! r KQKX>^,mQ(_KM# 02sMųg,<{MH2M!1o,,EC'sOnxG|>o<#EmNhW*u*D8y|\Q§s?SI4.&i-Ül2xG|>o<#E_}̹cyQ&ިC,H0 Zgj}xG|>o<#Eu|,yZ+;HgUv#el{g?o4KW\L$(3 Ē7b_V?Q+(l96 x> =?OŬTso&]c z_O,h/=^-}@ʒF*7˜fV?Q+)KYXzTeݤW:tyZʡhX| }ޓiḭ쵫cg|#I$$7d +o<#Eb"}Ύ^E GAmE"۷imnY>q_FiE,6 <\MjcFi!W}+({79c$iY5kC5ۜyO4?[x:_(Ȳ[M-Bæo<#E͇:<ǂdI˩M<<규دݏo:п%g7:,~.ofI,# L!RAFnk>o<#Eb"f}Ý_dxn/?hZB:[[܏MyZRn nSs ?gO(5Ь~m*YJ9[LW}xG|>o<#E>G9_GlқLR1דl'm'*}$u6h5]mKyVV^FQ08qvb"[KپΏ-~ x6*ԴFs5 b7_~p [;z π_qz׆R(]@cKjvzW}xG|>o<#E>G9c,]`tz}xG|^͠F_=gj}xG|>o<#E͇:2G=SV?Q+(l9їz>Zb"[GaΌ{z}xG|=teޏ{֧حȣV?Qs/}޵>o<#Eb"fÝl+({6ɒocEkr?G|){7j] uOLѮe>k&Ov\G#c>x]+ثCŚOMVҮY/eY6ʳQ:PYz`m͝>i>M2\wkvͅv,2G z:bT (UWy#+^)-M<}cqq$,`3 >ן=m/Omvmqa~ǫ_joqm$ROrCR-rD]o~O@e>ծk4}Ϋz=բMkX̱m۝5 MMNP&X,'R@"BQʑWfJC֊x?TeỒdk'+4qsn*su%Tgu^ᲆ 4XKq,Q1 b ]Fi^Ke<]xvRt"l&I )پؠHP8sh?L5} _ٝi{tW0Y@%a/,e+/ WXXO,I rFUݜ0s`ggwּGxc΍ Yf2+M$&T# 2͞>Mv7ct[@os<Nıݭ^ Et3 1v|2)'lh7Qß|-ˣ$]ήhGo1Iu9ÂF:%y%Iĺ&u 9i%teY9+`uV5; _VnTrт[lJz#G4>?tZ_j- #w22J$u1?i k2ɫڋ{Nnyh*T+sGa]AEMoVO#R% 8RHI 0b߸+~-ҤNxþ-55+-xR ax/ K)f+8B&VO$5+cOi5eXYhW{tGt#wfWېeA22{F{w7|CI}+j:]C! ~ *Ux{?^niyjw\cڤjdېK`F> <7<5eiKOBR"cxwG*7~ _kߩkZ6w-ZzUĶ$Qȭ4 T;Ohi^#G}Ni"M,iW25H#VP7ˆ6R_̈́"BVT*`8뙹>G/_}ER4 ( ( ( ( ( ( ( ( ( ( ( (:h_6_xMbCHw.u-2DE+>:vvW>N@~|xg Z_m6_U~]3@Heҵ_$ ".BgN^O&_[N7gg'fEph1B ?{Ra5d7$sƿ:^B59? #ºo6 Bkkmz*$g"8aۀwH6zIF n>#uEnR!xq)[c-QuʤZM?~$л_).?.W K@_jg<5k6ЄK]]jwHF10BYoJLt> X g$n@D$Β/ cR‹~izwğ|+% Q' Isz˨x'Neanm,y!kwdf]lDǖ4u?RÞ!>Z$:u>/ j)1V7FM~Q# ]Y=Gg|Iw¿R\]?^C@tm+G3h6ܿaMNf*$:ZXbgܺ7!T!9_ wMwI_Oº-ާk0 /߱ -ȒE7Q8t~c%%uCB ?$л_).'ßyNxS~(t.d;KgM?ey!w6+ע{bů-4QhҵVKmOvhiAIҢCd,1|ѼIaƿC$)sG,z8Yc%$\é4-Bj(y%3-%B۴v =Ih<WGtgwa힃sIx/,bI. 쒬R@<Fg-*>&ֵ;k =*F# WQF݁|SkkxjQsp׊,$/0W%UńD5[ \]\\\~T<$b,Hsf8f [߉}D 3aʊέJ^P#@F=I$ֿf<[ꖾ%5۩,d-;oMDF!|.wE]G\3]q/f/MnE{2Am!.LO+(j;.Zvym>VUwRe?zF7CPWZ4ZXM "YaB1u4SR?ׅ>'F}%5=WZcmJ8r}R`hbC򑏔uɬ{fxm;GkArB$FYOW_P_ꖓѯɲ?#.D.rZMv=[ȶعw$Q"q+?ix;XsoECkA"_W_P_?^AǩZ;; Vz߹j2RvsÞ-/u8[(-b1K#-!ݕyg~ƺkt)>k8o/ml iY-qmv, cPW_P_?^Aǩ%ktejx u:ƳjZns X$-О /k? $L[BM)eyc;xt½]‚U]_/D򣙽 ៈZ烵dQͷK28;_.!G$xkm=hͪly$&dH77_W_P_?^AǪloo쥣o*~.6𭖥=kR;fAn9Q98@H&_َ |-dU>Ԯ#I ~n $*,Jq UW_P_?^Aǩta$sά`;MǺ՝ܺuƕ}qK<]]0&ksp0k x6:6m"MFK[^U6msmƋ$uQЖ?^AǨ{޻VOe_[-d'%Ѽ?sk>E~rG!E|6 fź^kCZ\iZ5wkoHr6UH^AǨ{޻n_ՈTcʌ?Tu}H]ޫooo՛sm! Iu-b>&yakOyƦTB$zޒ}\fcۻ370C__viqXhk}@nQ@²U׎ks% YE@xqzծsUuuKIo-(4l wW,btxn$Y7!YUUc<5Zs%῭mi_uitmrP6:54q]$e-B8Ѿ,hׂu\;,qń& ;I'~86g|CܝK6uxhxgKX,.ly fihڧGe "2YOK.wѴԒžߖO|_]?`ht M5ojr7:n[N wA Yʶ=V s{xĆR<3uymV4hd89v4?2hׇе3]s@͗[i:H#rV=Wּ/ܒG4a[edgi;.n_< Ӿ5i,5 յH{K:e-y? ~г[uu2Imo3Ec-["L:.{6x9'/7xVS<1}׺|h[VV $aJw)꺍ü啸[v'sz: r#["o6{z_Ff4'-uM)m]-anѢVG(pl׉hzdʼnb$y>i^f, w;uռ]c^Y͊YᝠFir'_ v]džm!Yx k;i-%` +,|ZK'燵LV:ڥĶnqbRTFvXyTrCm;I> fVTN <1~G:h5ݛ?švM+J1<3ו=!ie7\h#gv;QO?/_fMժicZҮ,ˈ)# u! ((A xSzF⟈-Uu"UtlHdº.O&D1t.6JHzEx?>2q5c[=&.(v$o*}xoÚEzٯ+uf-5b8-YXみмm[Cu'LZMEoe#̞s6˜+#hnqJ/o…5c=e5W0\iOGw+ClmJ`P)87x{(`W|7n%/ b̎K~CX(uĖݾYh%xdRKLN(Nox7v@Ե#FO[P՛Ek;U.~%k7@G*XqTd<9'M _iy M2k'W3 "B:Vm6{/tyGtuXGCu`fy,sG=_O|Yt4ac&#tΣɏVvd\[`E:[Q}*-G]lFgMȁyrq [oٺz2AsitRX6QOwn<`EqTVuu1 bM!x,2͍-Noks~ҵ KE.$[,V4P@I`h'(a'AkkĶ:lŨ6dcɚF W}K0ܪ%eZM.ʶ.;K;w2WpL4L"JvbN~O4[_PHtt4JL^Bc6:'nƯDJr{g^[{beKom[ߞ*IxGz.,GgwnEwe )UR "5ͯV4A'$Hm kl%?+YR귟+9tmb[_ZU#.H@GݍTF/Ek~gx⾝^IM3V5[8"{-bC#!`&)*FVE=}Wm6=N=o%m6אF>c"۰3,oS뷶eNLHٙmco;>Mok&ueX5| 5ki]ۣnن1b0N V˷~u}Ա xP$a/Fs{#NݙIZE۰ڱiT:98ڳBvV! _HBAone7~n^;P: {fH6^4}wFJ9O5gPIZ|zѵ ='^jw:-qa{w2JyW¾3RݮK֢qڋ0+c|8-foZF:Odc5X'.-yb TbbDC|/[|; YMZma1컉R2xe [:?O k <%9L}G JV8&`7b8f;w&z׾UJ<6QE&EPEPKIYz]qXXLFDwWI"5KԓIYW}vGM7UYmnRRԅ'@Q@Q@Q@Q@Q@Q@Q@Q@ 4Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@s 5He1C-kZ<`ƙ>ȈEt5}c@^Yuqxsqo$m4+ 0$]2Cxaa}B$W~Wi 춓 .-"g15 P;뾱ѵ_U]&%YU4i2dD Lڬx_m5X; 5(7'N7 i5{[ãО/ZVvu 藗Sy+d+D7cN_n=_QK{i"9"EB: ~> "K+tg۽M5G 03)!sw5x BҴ,L[k ^t(ª[sA=b{v6k _V:w# dHxFJf ! _z/$}ͧ#LRΠꮑ:M'*dk콣KTEg?EvWȳ$"HHeuo7$ko ds<"R t!G wXQ6_Ϟ z9q;O-N&7b $mYeϨox/z~MN]KĮi:ln9bfeg]9\P1^'.[^MeVכ;-չ!n Dpps8 UčgYNfu9%U\N42xWfQ^SwOnmkusS$[ dd({tnRN&䠭K$HlO61|)=/kv(((((((((((((O/"cW5WzMY}@ha_?4}*WzMa_?4fGWzMY}@ha_?4}*WzMa_?4fGWzMY}@ha_?4}*WzMa_?4fGWzMY}@ha_?4}*WzMa_?4fGWzMY}@ha_?4}*WzMa_?4fGWzMY}@hn%Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@7"5XYKOFvƏ|HV=D<:5dY!i F=j3ľ(𵤓qr Y$+qpIvW,XFH S?oMo#]:[XXƎ@!ڌF&Rou|{%t>TR>M2;K\WI 8Ŀ#3ߌ ޣ˭Z6cjv4[p;8Rs+߀𞙧Auwm"jˮ"X2ark||X+h W|3cxv駗J{u qs\o+|K[#xDh#QYYxOZ:}מ[VW( e1~d9w46u0(XK9Qջf~! NJ"W$ռfXMqMr69/x:ַqqrk]Z·$ʉr% |x^-ɪx“ze-tnm!$Hd# !Lx-Oq-]1-]k?]_"5e{8O3kp JѼS|Ftk&]j nUD%Ux+xg<5o xB@5h|7]COKf)#fo!l%w S+gew)|:"RAr3c3 +9 Um?_Gxk̍岳]Kd;QyEAcNqڅ~wE"Κ(((((((((((((((( S4YȮ-^@;cW?WK{+BU)-)u=ՁҊ(EuWĬ20Gd`UJ@!xA%h˩]=C)himV~c M%C:Mm[mooѮāUǥPQN6SFB4_`( =}nkyǨBtO}nkyǨBtO K/Q@A$FR8jQEfotoxx-18.01.1/images/viewG-check.png0000644000175000017500000003306013222767271015773 0ustar micomicoPNG  IHDRPP pHYs   IDATxYdu9uj<lN"[$ERe'-1-Ŋm@A [%H^I##eE$6mJIQfϷ<<:]MXr`YTTꜽ׸iwgB.w(w "pߕ!wx] Cn70Kǒ6v[>[:>dl`Ђȷ!wGzCzF}~7k +=h_57i~}x1  zOgkɄO2`0^IrEQbyȂ}M1Ƽ'u;9(Y>$ AZ<Y"?-S$VoeVkXLEVi4zҰAY |a^o`n^*Ƚ⣄^Q4cDd˦5pzn{#~oD`zq^fĘGȚ 6 OvfNVڨVTY}YMc]#IEC6N5@pؤ5,ڐKqbׇHE[լ`*,`nn޲t.m {ޕ|`^qg5JƋ{3@LGl3F6bl:cfo~3`aA,a!ccg,R,Y@z<+TNng\օ+688dMKO,7dQ&g| ;u7':xi-,OZ!T MEdgWO]YinV*琀VH',z w?tSlH$,@K,X, kK@@y|_%u .G6SXܞfB0a`o{[h',[bzh4ҊOʖ噢ʱ {X`WKUo HNde@dHa"5mg',-Iz2h\džP 4U )K'lxL:XUTkVc+[׭uZuQh#>ώ4~ײq( .ls {1覐+#X>a1ܔT>{8go~ܸj--?p%Æ%MVk7Zƨr` uy)!vmM$sàov˚ry Yn’ $:dc,q`rN&yv&æw~ÂR̾aHa\VylMT6eeu{@ bWNlu"l. YmlʬmҌ1ɞ'~?DD~n\•pc➇HXeaȎ8R$h7^|ڱ5sYc=NVҥEyg+ةsZ8@qfs`B sܗ-Xn h\Fd{7F*0ۇvڳ{R7^Īo^rnrskcC/^z aʚzi[ ZpNsNXR:bO"usDت(B܂LqbXm/AƶVo恁t{"}[_ $ťdVv㨊&mnz3oa,pd]`VAPP\J3$ 2@ 8-v$jnCI>azIBj֬ϾlXvG_qh t'b$F"=ST ˮYA* m̎-w*)ʢS]X̼.R'iۨx6lH$:QD)IΞɉI"@hf& kKY󙞷FGw:''q ~ ;;~wp$;ͣ԰ΨaX޾uL&R0 ;˖lca.hQP}ٵ ō8sB.U:g7kCٝbˤaQI̲NٹY+b̡3fȳ󥐸{?1hFjh[O`(f,8n!'x518qJv]uq*j N2YStrcz"a&$!#|LkP Ge1Tc/ #܇)&ty d]F"D"&?􎥏Ṗ^t  9Ъ fĪIB7? m64[_Է^3O*lr._7Sg}K.gHeiڀ66x1G<Ӗf/ZpX*Ĉׯ[g$L& Hc!)B`$#+)qIrZT| )ȖhG28ϭz z1v#Jn/FW `*w-p7;yY?joÑmv>L`X!b)rԡ(Ilڍ uv,!soU*BKr5jd8ce1Pyry̚M%Hh`ǵ6%X߱S/)A@ ?Ng#&Q%׮"5pՑJP42tkg+i.yD+ܣp@_!gj6ff vewg?{V^Za@<.:o,\|*ORH: !po%ZŇ 0 ->W5T(;>;x7՜DSq*!x`ȚuNi^jbQGR>ܧm `~0!7)G$M$|jJ+V^uW7lu}\J-y2'aO*-g7m iե%54 TPoDGf=";hW$#Q|wQ aB=52ɤg)L*<$t)DW,l O=9}׌43$7GUFJJ%˳Jݨo__g+>x<ʚ|2i;?vPW̌~L mmmɵ#4CU Uf% ߼wu76,1Q `|Lu`bӓ.bT붜'x K'uBXĤ\%ALiZ~`CoZBαEF\$ItbLj Meyx<1ܿzߡhX߷⡔ʥ :i{G$1P׊9 Kv_}0ep\t 2JW_% lBi$+Rkd*93 ɪIE\#J>uPv Y•RBPbLHKOZkso?x c~)}4(֤ kY$mnmX[VB© xArGF|#@%'E҂+\tγ` 0U&mS Mq҇֬=ɽٞ:dFgs JI8kǢh~1itԢ3 eԉek{5+0ɫj[{V}R52پI8Ƭ Pe 8&-K`䫆hTLD^27'& %j݌OY(l` Se':Q"xSEs-k1LuKer8 S)ESP&eQ^ے_=.^{P7 -qPDN4ǐTVj}mīTRڊpITԥ(ڦ'7,=poHG'G J]{_̿3FZC`ri '!K9M4rmM4};v{P'RVZ1+؄r8r%wB! j̊qcN͉%pӱq*N"qt!Gb9GdG"ť*&+_ƻ< <م|oZ-nPO],aGI%d%pqc&yԸtJbYGջѻdI<=0.5^ YW'm #4FDc sX;Wc߰޴@o[ /g,-}<CV#Z5,.-dL )~` G)}9Ev$K+PL"KQYSYtlGp-l16=;w-ط}6n^0qE0z!.JrzBr,rʵ2kH4[R,P#wYWSYatň TU֙_辭Ad^C&O:>mrjnӯ^3%y j+/M?/lrzŪ*wIW1ei4Ƥlǽx >ZR zNؔPv$z * ȋ9uA(N8vmI~D'FR"sHŅw܂[^o Hx?m3rE0` pDIon9III.Xmpk4P@Z&'#P%uU'CbO#Ӑm|mq8*t$Pfx \k.LY%YO`1i$ohZE L@NqKo7O"U|[?/aJ:Ao#{]# ddkHѾ8m!I> LKT0*0l9R Jٶu*W'iP{0-vO:D%J,/!n.cadK:*ZL.^ѷӻ~FgMOw8,-1Z|9'((SMLqQ}$@H* 6uJ{R6/QDBpѿiP4&ۺFFJZ~b;[tEup(8>ÀMdQs FlDCcdΡdWPL a-IFxRܕ),sZPP,I׈%PQ"| }]Nf|*[jd:'(I "(:=LJ#<ڎm< NӒ!aUBOGT_+<$~bd  ^޶sXbyƣ#lF%ٛT'nON1U@$]ʖQ(1HgqIy B@JM\rMԐ. T8k?=կ~.\|ǎ.#@ Ɓ=B)G!Lǁ]~-/,ڙ3'lۭviR` :xσf8'uܜےH^<\k$/c9nf1mdlF&WE%"kg9eW(G]Y'zFEVX$ \_-TZtr*QBiۣ} X :`\{8in9{5b 3o ~K٠wݪlF 'z}tV$PN]3g _8MO|j /$R!u8F(cC׀K 'Jƃ΄n{der.RxOVgjvxWXu׷|S.,4d/ʲh8 tlen i5,41/N t>~%- h̊FtRjó80$-L\ImKz"$Kw]=_8gOц[UD\TzI`x[*HR Q]VayJZE)|Z@E{O-{" $Dye3SKc,c! 93K֧18w}qX (~-oT,;uyU=H&m!(==JC>o2} %=cˡEz̥ߑ)8A$|,#!-8ȼxczYKx2ҲSYnMjo}%;w^5G\}BEjݥQ*&ջHaCr!IU{~Q%s˗߳u[K;='b}P~_f۬ucz,HI@=/ˎ@VCζ6zXRp_i;4RdD kQRmЂ_BS _XXWlKM aܧ~'4u﬏{m,C(1e;2_=ܢVUa NNwOMm׾HZL<;%o%,E:6!,'u_!$egQ'G:DGTl\p :Ry~gH۰Z<܆0U Lmm\ʚ?blXa>_X''jk=cf>嬜Z$EHp셆T&PҘ"+k]Z #qtH>#$,MKH#vRA'8vߕt; )˂IXՍithl;qV׸!Tӥb:] e 06\7ʗ.>Y*vri53Kx: CTKMseRz{fe{@Jp8y(3F-RJpjۛ>-ScvC2tN=u.٠v@:¯#<]t]]UЗh9):)%11D t"DϦ@ 'HãxȆ QRJpt 7dQwēvu._xn]Hdά(uD a.\b'>nKkg:{Q]hɋ_Vj#ƿ`bi%ͼ Uu`t_GfI܋t}ąʪ4 rҾ 8S:C$Ӵ6D𚗮<]x +mI 5R,mĴʉt& T;!nwl{cur%vvnњGJN"]萍:Uxr D+ ' zPCR:O:<=Uό!ö;eԨ.UuȮ Um$ ar*ǰG"Kxb57o CI TJs%bgIB^Ďkv G.1dNѧ+z@~=2ͅ9zqx5v>%Nf>T݄=,jn޼ -AXXx"X}͵$Hlt+MK1 s퀂;qM#a!1^=,C2G]ժKI5Qu4;FvƮE6*9b^$vqoTͷV2 hCi$@47\;1ĥsӇ_y iHEwC'N{cqA$[ta=:zMtyD&/8ȵ[DP=lEzHhèIxnCzv]A>`NQߌirrWh؉mЄ.=pA;OuO;.G7d}uLܽ9ZT>ޘCW.$Œ ="ҁj @B}<؀Ds&JpGO#.Zddj'XV,znn\_G4j9vmϔcv9E8GvQ&2*x14G j!ks.>lmв 1:-.YWh%#Pqe,k|I]G~|A#TML!** F5bPbZ1-m+,y{3`-=:Qj{љh}Ȑa;!/Yo.e>j k*CF bY?*fJ lڶ-eI0pžWcO{ MKŭ-r<Id r eL4 8gzҜMCY$01lv[GoܣD6G'0A(K?qJLy#@u 8/+p?wB焚*=J~{M 2ՎN PTRqZ_I}/=ûv+‡+ڏN‘w}s&|ݮ3YT SеGmjyٱ"X*(FeTbÑj*2/'U4}GCJ}IYVxlrpZVE+I*[b>N#MDJ8\j ZtKXNg¿GJYS)gAVXGiZ&_V'xLI ӻ'&˯ o0d!BUhQa#9=j)o(i!7dAp|h@ L|oNs%Rf^v,IHKJ9*GUJgٸ4^ءIZ!˖Hx2LXs$={u9*TK "hMJy?xөHO|UD& ]+%,TID]d5j13XO}qBb&Aׇ‡=#sϗ$[[*uy G՞z)jԑ6sSXfbrh@=2\MC ع?9Cި-y.=)o񘎀*[+]~ROʌn 8[cV# C!|ꖂ}M W~=qzSA.Vbd(> (RQLqw#st:eqUb]z>(1DD7i(S8d]Lvvݣy^7⤑w}_$w U$; j|FҦ.tC D+i8c svD F㩫tܧ~~{OYNI9 RY*2O'T Ei{>E {`glnnrgE@uxֻJ];!gJQ;-ItIME - AJeXIfMM*bj(2ri% HH2014:11:03 14:45:090230ԑ701002015:06:04 09:34:31Fotoxx:paste| Fotoxx:paint_clone| Fotoxx:paint_transp|NE's/iTXtXML:com.adobe.xmp 80 80 0 k`zTXtRaw profile type iptcx qV((OIRc#.c #K Cd#CK ,-- @2`E ҄ }ZǀIENDB`fotoxx-18.01.1/images/batch-upright.jpg0000644000175000017500000002033513222767271016375 0ustar micomicoJFIFExifMM*V^(1fixgnome-screenshot0230%0100Fotoxx:resize|retouch_combo|sharpen| 5http://ns.adobe.com/xap/1.0/ 117 239 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((m" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?6*c, ׀#R݊JQb(o ^Q]pGSJYw (o ^Qe7(.pGSJYw (ːr=d~8PZG&Q6БQpTMES$(((((((*=%._E%IU,_mƢoTKfqvyWvsw"Ć$h RsKYXIf('[*; BO9Mf)4 &rO9&mSX]ÐKuiv-:I K 'pwbn\Ը"P6 .q|$w5y.|J,Kb"4` ܚ";T twh̆3&c=wJe$I&*FǮih5.I+sE缁cgxuMB5M!{# ær+BX`eIֺ MłOBGRMaF}ƢӼ7ֵղX8-~Ҟ]?;7q;Cb,x붷]OI$e3pVEp4tg^_L9ߗ܌곲 )Jkv,0}NII>PXMuw*o Fu&okss-#$ ?Zj) ɽfʾc~WwGtZ3UNpN ]3YS)6 )X((((((({7OoDʊX8Ia??jl5'so?b?̚8_\=2%_sG2HXtP?ejE-<ؤ%Wdm Vn"#MBE>&{ROFDx,=+:Vu:m^F<֕9Zmkt2t<`c(Z^jyj?kĖrb/ѷS=~JQ,4Cmjʯ-Z@#YB9M9݌צ[cl8*k 996{;K3iqim5ОD+GtH(篪jӬ5_Rg-p%uV2vI.to}Z[ȭ"JS6qvM461V 6e9;-zlvJI--exdxgϝDe]oƗ ykG ŵPݹ6Y=s dMw~Qwln 3BLGtgm-#mƍnUT @((aEPEPEPEPEP>6?SM̿܏?OŒ(g(̿܏?OŒ(g(̿܏?OŒ(g(̿܏?QC%b[;PB݃+SKo hٗg(̿܏?M?Ə+SKo hٗg(̿܏?M?Ə+SKo hٗg(̿܏?M! {\4A7ң#G,Mѻq}f_G}2r?2IX (dӭ/ϥ4f_G}Z[OcGϥ4f_G}Z[OcGϥ4>`9$)0]'zbPEPEPEPEPEPAho~{ɢxu(YQ> ~U%v~EWK)JH `v\լ%<nѬ:tq)R5JQjM;DYXȌr')ѻޗh.aԣuO6ek93yy8CVn,![Hֶ*B`0ۚͪShXK{tέ2R?)#9tmWaqݘZSu+,LlT*+i f>1cn&<9EKpxjm4Ă7DX %rk|mGӴkEdYEp-ۺvVShog`[YfݬC1ncsidv[Hof&pp[ڧqsu%ׅ"o-Rсnj}ѭ3g(BT(8Twbc{+)QEQEQEQEQEdFB"?6Y)h*?Z(+OG4}SIƵh j ?O'բ2L(UgFk1,|( EP.V#yo9QPSVGuEe}SIƏj ?֭O'>ѩ$TuEe ͱore8c֒fotoxx-18.01.1/images/panorama.jpg0000644000175000017500000006316213222767271015437 0ustar micomicoJFIFExifMM*bj(1ri%gnome-screenshot0231Ơ0100Fotoxx:resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 315 263 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?{ h<>}X/ڽaOl>|{z,(aOcޏ{`6>}G+^}ދ >}^  /<=MPE%i7,$|ռDž[Uvm] ~moHcfg*Ax{DQ 5#(Oizz\~ڹVi U࿅Vmk)~}G+uO5OxsSUDԭ+]E+* j^|Ո.|53kPMa4]NsT4M&y(aOz'Kt-θNo6ky5 3 E!`J2k?Z͙m/Яail+kTM )C&tU]zM@C;dQ*Ahn^=BUOb iadݠ慠ٍ$o_ -GVD%Q_Njk [yӴAAuO4߈~>6Pkg?UdjFlm,#|sD/Z,/M$2m#G_#CS}Ua6whCz(Sx^\sZjwvbuA,ԱU$*p+F|N{t+u;N 73G;^TԒn +խ(\}pڜ:tڮ>;Ź@ea'םxn+.5PGB0OoN~!x Y]]c9Kl~<͑1(ZKx]fPHN]euiEٚgYJM\К?zsGO~ø z?aW={6gI'ֿAaW=?0m0GO~ø z?aW=as'֏Zq;_kø z,McDҴ{۱qE1$ 1ORjK7NO;hOQۗmUCN;bq;_kø z,>0?>"_VEcYA "FO'֪x3⇉~E{4\C%r27P`aW=?0m/sKϋ-JzeRR^tȮ&<]ww]܍ M>ƱgHBm.[k#wÿ|U6G; >*_G& O-kޥ) :v&59]G&M̅Tp@*_GM#|ƚP/5ޑ3\XlmT+(Vb_q8i3ğ..*_Gf?>o{I(\ҭ9 kwÿ|U6G; >*_G.~jvĐEggc RI%8'5o\f4yc"NNFybM}wC&q;_kˈutwq,NuG>'s Ilk7fI^k{~ø z?_GmKgF6E,Ng[b-$>+#Oxj_Wӭ-lH#y2y Ƽ0x;\r\h:$Pj\,:\ЀdHJ*.zKߩ2v~}Ru$Q|tk.\z Y{YZ)mqUV8ܐ?D(zn-n6] K+qnZH Tig 7oju65,(moN}ZUKy?E;)'[F ܫ^kcJ*.~\LYeK]O[W.(s\摵{6\ʻ<ƺEյ{jKZLu1yFwwm9Ci%u/ ZY8 ^IuӁ]O L|+3FTU+UdEZO+? HQ7!{=^g,g[ƖC$qYt>+hF^+D}"6 ͠]pJ|b#uWɞ48Z6{}J/-gЅ꺜bpd#~0[2*|E> -'QOhDݻ;scvصό2x^hbsL5-æ93 s_/ǁotiG'|dݞep0oU^vWϳ ]AnaE;KC pѪ4ܺ,Uʅ+ hRLu6^}[kqz#1[wTbUۆ<mxH~(.|ai=K]j:Lr=Po dGXL 2[՚_$m3CۼR7YTA'چR9m!ʑ-tq|Yz?mPcn:>ycwmǮkO kzckk HROM,bV#8P8F4j+^j:z ޑ{gpӌIC@%r1- ;wc_3 'Wo-#$f%XvSdaqҾQ߄V}fF<)۴^H85#,.$` Ut i:+/ڷZ==m._3B̀<\Rnɿkjl#xկ 3(,Hr9m^g0q0ݘ̫mgJ|)S! _DܮV*2 AK>jZ߂n.u[eFі!9/f ؟,~cjZ_+K_>͐uK9|8WUiomVP༪S=z{~+f}Z6~(:O8h\;n6= #o:T^$f}[cW1M&)D[,2E9H;qEok>x͕R;"wdN1<3|6/q5Ҡk_OquL4,Vm _YZ\Hgs!G\#I+ylQEQ@Ox#P no~"~Ed ʎ:Yt<;?}a|?Z#9ěq|eum.xKɴۈᶈy\QK2N$y"^?߈$$b\hO2AI61,A$ kS׭|Ux|kŅƆa7S%v9+/n~z'zoԛZ;oEߵxOZ~x;VS-t&{u*6y5i־?]vLjm_ hmo7$o/rqdFSb}N-?N*9w`}RWI$-nmF9eKß~)"EӵRGp cs`=H,sw=jtK93Ag2YlrT)&g |sxsxf h!϶b`2Э?/o><u;H2u(Rjqx~V:T:wi$}zC QeUb&|V2',ɯ5-W[xoZk!~Зl6ƿgmY`0 `c+aXm%Ҥ+g3$k"&BTh-]^&@xRfz{-`K3IpP2kkRF<3cfn^\\g@Pdr+uIx|0OqF;ǓL ˽IZ6o0)^:DӾcq( T; p2=?~"Nz~u^ ҆ J: ?l^9s0r9\[~ҵ)QMVk{琍+#?*1 |K摫xK^OiڵQ1`9Rb5K`\_je$eKweNO@jSȞft_z  K`dGm}1jȖ.JKQ 5$_< qui49xUH.#+/)m<%kMifku}f FHUrU!_M'rxA|aϤ5KPi52K K6#R}kn~66CqO;_=I=Q3o+[MZ e$oUuʑW9^ 񽗆UڶkϮ.=[/)]2y]Ч ~'jZԫlc~T1xῈ|s;KSrXR~f3$m'wߗz/I.EavߜFHo c((۽N)&{ln' tr05Kß|AhLgqgCS-H=76}\i]_j*/ǨfBz+OWg ωuyNfk5=Oyw!r7q&:ߏ? K>?V~q\h7ƭM<%}Z-_h횿 K>٫ЫN }>,ۭ洖 RC>wu-hVxexrȯ%3[4_QЬmZ}+LKK4"B09'H^Oԭ]O{W75.TKXjhwҽ_0@:<*~8xs>)}YW6I~lg$3$ï84ֻ +ͼmum+UVi(5lnbD0vqjߴc76s@3$ttI6P@ps' ˫N-Ԭ"x~eoI_UB `0.?h v%4:TEixlg# 2o^GK]յm+N[.l7;( HIJ杞b;J+mK/ύt>+|8=}:[5y$u̅Nϊ~1x#u+ORoTu#4R u2d+^(~ MƑ dAf-}]$G23̱Pgݹs1WܸIIcnXoWg)?PZY=G?rտ\+ynd3Ks* ||߲mXXçUԥ6X4LK>Z2&RJ;ɧ}By3Iqp3DUQ]Z;: fH^0ulHEmjȵlÚ_)*xM6U湶VF+5@Zb]<${ӳ~[g|B9xWsg>^*͖g h[w$ Ɓ/nU9'O[pY ~V支gmƭusƥx_V%.nrbERQcy bO|Ij^_5o=+.}wX-JuӼ7suTIfC>@ȭhmrxZV&<[^CdoD3L쑪C(F$$ko~鮉C 3E[xz]LxfXIuA61~z2+oqw7ak E*WGGVԅ̓fDFltjqxG᫤Ksi% h|!I;Ku1ofoj^24z&mjMYIcwllV3һ}Wj/t`[],Ф{ (T+RlN|{O()/ZxB]?᭙ohk:HāUR!\lLC!A!n7p"oth.𮘚 Ěb-/)UIϖ*7d;| t?._h4ITE-& <#n;?e k6xvW[1cm4m["2x$g9%ko%~ƛm[ _߅(ٵi9x#P.#Yԏj>h^ֺ|=*YhX]IqTe4yyhVSL |ggƑ^xE4}qtM[ 7XZKOJ#H+g w+׬+-u9OבI0HNvz6zZuoY+3u2qZ:[,<*ÚM֟i 9౉xr~AY_I$~pnmмYLʻnb;^ D y@aSg Xg8%ixw7FI6GG1߉xwŞ+闞4 xjMk9RimV ͪm71]6PYCggⷷ$q誣?54X~lXL1[4 (Q@Q@Q@Q@Q@Q@Q@`WIg <# 4  qެOѵSeks;Ϯ ojE=yR:w"Ks| 0&c&KQeG].Y-vvID iIs&ӳ}2;D`6k\8mG7pO<Fԑ#G%4Qz5[3J(7F 1_cj_V-iӿK>p~Os]n^Ktyn!+!$@$~x[3SkYծ.chP6$ IG3 8 Y[ '5֖!ׯ K2x3;Ty诧XPZYj;xkRH: Gƍ7Xykk =Q YV2Dۗ@+`|۶;oe/ï؏kzy3$7;VXX1$Nյ)& ;V-!`D@,+׮CWC1xR}2jWhp#9\ ݨZ?]}.>]ZwpbXu14VG) br6=([/I[o (AEPEPEPEPEPEPEP\xKGukcg_ٽ́XEgl(Q8W^ qJ3O yG7e֬<--׬kh5m?P mc3{U6PN'5{0ߥ ╷|;xZEeϊmu%Эn`kkH8SV28 _i&<}Od|?'&N7xμvIf8ɰlmZDo3}W6:=k=,ZUeR#]3`g9"wG|-k[9`Ĭ:V 23?| ~jv}[wkWNun[l.e#0$HdA<+g~&Rt/Ě:^ C{k &4,h5*7f.[VW}E|2]4`]?_ K.ikm~R}ouu(?5UmM#?]|KFݣoYPJ.4xؐǔ~ Pܶ_>׾$ʷ&Qi`dxŷ~O F{g+k@&|EI{(1,nm])E1s]_ĸeF=?BѮt}P F;g6\3~@Hi=+}Ac=+Qv&2EER2F5_ϬN/k'ަ գǬk^ݺzN۬?!TQzmS5n9˙&Mm}e~b4tGw9S{;[_(fm._z{]E.<,av2ǣI kml/PN (((((/o!yyeqauFIE!≗Fsk+ fS˦[Omf"|(F2!~% `w7\xkQ SR-c)Iqw?hſhſpJ6}eK?"'UnO>ӑúQ]51o3G51o3@3|F]M,ӿʾo',R@@lNI1AmMU*tv\c`o^FA EPEPEPEPEPEPEPEPEPEPEP^]Qw¯Mjz$>lʯ@H^p8FPo|G_Kuj/oړ'[\d7)s&ksqwRյqjkJC\i,2aibut Y:|ض7zd%պ<(.At0n^k-[=Dcot՟BLD$a;0XNԟN>%~_"gkf:Űcc[B?yCxwJҵ J[M͕ŭƞh v;vXԒcF28{=b:{p ipFvIvAl ~kz2gMnt+׶[&vҰis1tcdFn {~!}{5ųKwhqI)7ޑ7i$)_g#\Ьag5 x/1\ſ[4wM {hEG(A,&/5]*RѮ/--FCBȌ!y;|#񗌵 [ >5=d/Ʌ*U3䎢o~C{/tWǿ jV-V[+:Xཊ423B|E,YFW#7-#Vu =oKMMRDӞ)$iy< U^a"V0x'D4"s+BE<EnLO!|73Wu)"lڎ&qK$t&U2L2/i=*ޟr\i!Ԯɔehd4u'*B`7RxhvZeG$]/02pp>ZirkE{ٽLR  n%TySx>mVWw/K_ِw0`!Wi-硱g=t=fC_v!w\Sal.HS?|$-=gao\]BI-`>s[_}OZt="o;yX$>#Ú)*{HSq-͸Y3,QMܱ9 l{w5*ǎ<=eycqiwV?6MΒyM1$ve:h:ny4=m`gmq*#*<xB+O/skPմK.9ffcy!1$vs]CQ}+:oͬj$wzTg!"4lbXūsO3Sz6:޵oAmZC: y^7$FCd ѡxVӭ5xK+˽>HFEfCD8$G5#Úfo4h6ծnmXn-Z5Bw% V֟Soxu}v[#Cn&I%9~Ѿak u5)uGM:kfѮlK9&'j|HK{4K֭|d\vڣzTu~.ycU~$6<3MKe/ l ~>izݹo.4k2H!\1ÅN:רWhu9|]i4%),̆4Mp0k/wX(C ( ( /k}p6X+^OpşXֻ4[8+Oҵ.."vFmgmId7FvP6 9$ֱ_We~ѯ TEoxr[y$ygt#7fe 0H$c<+MlNy$i "6x';sw^AᏭ'R Yw>+x{Zީ{tM?uHX4Ē9!(Gks+IlV]Rϋ}'Z׋9e}{QKQx,sCq )$9elpKpZ gmk-HF|CnU23p=G\[OMKhvɹi ^Oف997>YO?y#ƥox֡K~4?D>cwc,/??d ٞh?Ÿm@[xs۳<}ht#LO,GAa;9oO hL6ZZ[3M:=WEa_s▖.|>#|[a-= h#i,WCxDO@0$6װ|1ּGOx_^׏/n+gA84ec YS ht,mtˋew %2Cgk𶕦mfjPur" ۽JN;{O]Ə$.ѳAbXVI_ 0c/XjAom/t+:6J1YrBʾY'4T7R^\䴳9;= %S>k{[ ( It BҢ3%?+e 4KVkJH' .A[/O*(7]#_G$Gl!?ƴ It B4o+qgcu$?%G^x}`q@%?+e 4KVj%Q mk_$Tg]#_G$Gl!?ƫ[i>Z /ϖK5@%?+e 4KVj%Q uXrdHz4@MYH' .A[/OwWYZ=@%1uKlY?MPIt B mk_$T[i>Z /YH' .A[/OϖK5G%PIt BhPC)+*WW[I!ڟl-3"xYDK `zqz?);-xR_mm6iyuqu{fmpBo'G -?V:ݾ"tvڭ8 yVEr A@F5ЏmNM!Σ}kh/n#e,n)@ O",W¶Z?lfLZoPyqo* {v`ƠBWX7-"]γ]RqGoh!,U5j^_]_xA_B[O2]5.7(w⛣!kҵ7Sk)v,#۰$aq_:\x]ƫA2j:>4H3t J]d8l +_#,|]ar<>lib[+C#ZA'Jv6ZC{_>Ӿ"_XѮ{i7U~}oC@WMeWuh{=$@VI47~l"L{m3x:Y4 * f4ȏx*esx'>_C?𝖱sksmqFH!z֩%﵂Wrr.tsOV_Ɵzľ HB)GPX: vJ.'0k~14iR鷶sFnm9UC)A q t_FtB57 ^>El"jB2T淯#[JKivo -ID bO&G^^O­kažmwW&ILQI (P@ɪ </Z5XmGnoos߯w*m>+LQIcF1>w֔CݛBMkO}z(i 1pq޾i/ /I>][zQs[Ya40xW! 493 650 C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(((((((((((((((((1EQ1@\RPE.)1@bPE.(%b((((((((((((()qIJ(k7m?œxzB5;tXѪyeqaezwr|Jz{7MJ+HR4d9]Z1\G~$#)4#Y:+?O5An4 j5:УBC v8\u|Q˯iψ]F[{++[5fxCqsIr1گ'c :=FCoyeH` rr;PkEy&|WT̖v[O%QpUt84ψ>%~ hz>.-ƲE`:g'#zX^I|GVnn'Ö:O'6K7Y:^l~a ?x?.aeǃ->7ȿv%@ >T|DBmZ_L[f깊@~xP  ƚKQٵ5 = !ʱ\<d{U'4 $z`t>+"[Y [u3?ۃ5շ.gV'|Nq.@`Z?t=q⻧kE܂S]라gӊr__?lu]_TaIcK2 &B3JYh̋*V 0?Wmmclʶ|:yP[yydH Þ,`g-?0/>x'>2=RJ!im.Зݧͻ $Fw| xʰӼ3-[6ww>lzsѼjK.wNڻ`n3qKK}é亇]jMGRԭ;I5ӵ.bY-.&ʇrA 5};T 2c\`\kW`ֽrQ܅U$c ZᆻIk-Re6Ҙn @5x-5KytW-"`chszpkX9u)::d})=m֟םGj}2g~_Zd)1xUAysnE9q3mBQ˴)\J 3@}542bX*iev\3n aGZK %Q@Q@Q@Q@Q@Q@ (JZ(eȱ7ptHT pq-m#H.m;5Ƭ,m*<95IKQ7WwwM ];H''[@Rft ҡi{…·dA 펵(IEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE A 2|?"$Wp&O+dP4 4j 8cYbX592ƅ|.$`yr?2;*J( ( ( ( ( ( ( ( ( xmc$.T  5/6?EZuR9̶w\y&t1X7»<?;??G,?ivNڊ]wWv4~'mEq??;??G+OQ]\[đve'ܚWvݧ|G(~.߉Q\W+OU_ >IP:\W ]WSQG$Zv{ (4 (+LZ7W{Am+۾P~Zw´P!»<?;??O4~'mPmNM۵p]wWv4~'mEq??;??G+OQ]Wvݧ|G(~.߉Q\O+OQ #ro+|G) ~%仺ʅdv6n/a);ۚ)M%dhQEQEQEQEQEQEQEQEQEQE(+R<ڊQ%#^ ZO"5R ȳ>'Ѽ:˭jZgkMac2Y\d]@OH#ozj_i.iٓX0K$|yޗo iɠ][Lo* |Ĝq1֒`?<*4A[e"C5j2 Ouo 6Ũ._˂ebc򪁒d`W~l~jkQiyP<+'Cui 5Ǘ-kPk2S:=RSv=>#nmwC/MԚ/=!x#i8!xRuF9e~Nұd*mi8Su(|+w^YVm5cp!m \ޭb[M,,VX1ǜTOЮ<;%u<4ȨE* )#ֵ4VWi۔)wNDbWQoK][-|,/=w8b;x_V`UO)G1c[WM7sE%-!_/sWi\_/sWiZ#"Cgы]Ex5BNF-um\ ׫|oE%fOIxnfYˈ0,62BO*ǎ|3oYjkH*X1bSYfo_kI%0 WљpwFspx8&.(ZAuuoaMk/o /^{_ju֩%f"tCg/48tm۝֞գukANx/'̑}F2mT;]m4J`-qX:-_%iOvfAVeW/'ָ{_O]i/?2#0,(Zέai_jאǀTpԚ?qiM$: ge{Wl|,jd !CMk~-~Bz_>ѵ+uk4CHÂq׭i˥iŮmywgx^ơ}[Mu?O>!u<"T9o<6u+iQi_j:lqH7H!H p2c34.6{Ef;U;;XA*T+;O3ZnnvWkhW?C^GDX-%-qPo]qPo]k[_9NF-um\ .:OXǍs_(^O&VEνk u;;1acߴd'j+>Gͷ˶k>ﻅN{z7.mCùE6vwrO p8B_';כj}G. y0A-;)S9vsI;]迯3ۧW'F`PJI6959״g%셬Wdy|ڳo哜Bk5k/ŗzv4ZS\چ.,n (.,(˜josA志şmS kĔJ% @$M8oziKA:d72k:D+$w !Ou]?{ijYZ;iA^h9xi'mU`3lS@@Ɍ?Ozh/t:>5Ŗ4mx8sjl-?6{%)2KWg\e+)"}=N(4 ̳%a_W?xK붭%GD~&%R̳ڢhIҭg1n涱[L d+ 81ҵɼEyi gNq{f@7@4P??ϟl5)37OfVoLs/@vA!W@%>,?fkŔJ%zKr׽Boxl/\<^6䗃h|c }į7S-o+! .eV*{zWuE O%\O|ia?Wm\O|ia?Veu"_NRJȰ((((((((((Eq~(r< ?C5'1:yAZS"iEqCCi%CCi']ҳlKcPbh}k2 +v03Ԁ=+OI?Ə@!4h~yv;J+OI?ƕ<?x4rcW4 ^'G,?iv:-'DҮu 8\_Y`OI?Ə@!4h~.iEqCCi'< ^'r`F1w 薾c f}ҾfbI$MjwRJSm )3$Yq?bQa6W״msHok\}U@S*+vԖtҊ/Ob? $\o|vW?1xCCi']Ҋ/OoN"0jro9/b? $?/O4 ^'G I4rc2?GI?1xixwVzqޡyw4B`OlևDhbhq_F[_O]c֞$KR[ctu>IZM~i ^'G I4a߀(/b? $?/O4 ^'JHL2s4rc@!4hOI?ƎX7vW?1xCCi']Ҹ|ga?RCCi%mxcÖ-%deS#c$^1Ih44 ( ( ( ( ( ( ( ( ( ( (xtӴ )_EקPğ/lwRqmRNӯm_~u?ķ7[\^I4,G4f\מ|K6WLLKwFQkRmt#{1Q[um-I,I#qe#PldtG*JgE*@ev1]ܧ#M6Ǣ{5% њ(4fZ,|QRѣ@ N[=1ڻ/Nw[j@Fh њ((Q\CN-@:8${1"xU_~IEvqu<; P4;Ƹo^ :m֡H~*\w}rS њJ(sE%(]x{-YlY^;~T< \gm(maTZTg_^_|-㛯3}9X>mރ@ 3F)q@ E.)1@b((((((((((P_%~lb{j%Ӯ ~_Zג|:F>}+OߣYTuy"n7hRD=}=8 "νxS+7 5¡}Sۏc=PpGqX[7lmvō-g^VE>Ǩ(מ,%熼WHF8K/Un+Ш(!()E_b?>^KֿZGR(((hUޥNVP2k>i~6/,o@DUnx30*|n<$hWjr!OkUU*`@ APEPJ))EyK᫿|(-6{K.!.KygsR ¿'<[koeJpHdnB d#J&N?x2H/ݐ&p`O7<bh{Hӭ.NCkH\UdOS]Egi5ˈIU$B, :Ŝc@F w1{?[Rn㊻HZ6^ O J(ldg)4?xc\ԞJ-.@2U8fF Rk,ֻ3]+@}K7uvHZ#ogV>sC!/tGb_u1CP,q17r>`X[tNKƟyx~%c#Wk8uH|xe`6izmCӼAmuo%sX`w0qԍU-#Yq?E.a'ލg?~Gi׶\ )`FAҧkᾏ2K%Ui>w2,eع rǧtؐQEQEQEQEQEQ1@RPQK1@ KF(?5ۻ/[xB|xēgYƧlECױ" hV~ޛ驶8,qܜMp> ?=ŬZiE}СM<^@41EQEQEPOwZvzfK/9%_OTn' 7C|-m<3 Z3k"IG;{8[^WZӦ-MH z:RF,3a>4[xsSѯ%w2=_¿>2Zxf %gV"LP %kskDV7e ^i@4QERR6zGws-v[t©q\NV?VφO}YiVˁ#XO^⼧Gt@[崸8?DT#\ƚkhww&Wu{|%;žtxD6vQ\v=؜}Mkc `,F2}ih(((^!{r_~IK Dz3}7=kmHX`=c^1𞥡jCr=Հ?sMyxf]7_8.1$'u^s;}2Km46$ ;@U<:՚ %-P(((((RRʽarƝmp(3 A a?6?cFxƭwlp3²<"E|=@-+ Q]ӔC?/ :G4_t i{ZW+i_T~K5Ck#c"q{ZW~=!k#c?/ :G4E|=@-+ ?(ߘ{C?/ :G4_t i{ZW+i_Q0_t h_0(Wҿ?~a _0 `?6?+i_QG,i擪Ji72[Β=HMyu=>Fv+K'@HB J((QE-dxZ?4i_YH9Sꧪqk|Z.5 Fz׻+'V&\,kk3.#֚M[bN|/A??9t*1sǹbW9 υ;G'>E}sǹטOh|W -Kuc1M]_'>E_{9aGAs wON|/A?=0sw wON|/A?=0s)3\'>ExcQه<{I?ixRĀ7Oq1ĤSD s{1G I(ƀ*X%pYNA>(((P+^%6u̗R1Cmwfx ?Z/*T%tsvlk6!^uvmk[0?pau ہ ?Z/*X B׉T ǹW ?Z/*X B׉T{kk1\W,kĿ??`' ^%!Qg={(q_-xG`vOKCе_U}ǹW<~eo"%`6::dʜݑE),()i)h?^8PڸE Jҷ_<ōFU"-Tݬv٭?^M$H"ci2,z$6; ±<?xc>᳧֥Q[׻HHUQ%鎼ZT5WNVP]b3o*~I@pyTlqMF)ctOz܋([(bfwGeZ A|T!<6rJՒt2I刂ݖ \W/xo:<~_Ks6j2u.R$*x}twu_xG\(tQƗ`yb-]1ѿo^|Iǩh?hCd!ᕕ*A@5D𾡫kHvTV;%e9 ǡ~h>ۛy.5{ۘl$Rqsx?F]uآMeh I"E&Md2>nM !gWFd{vh ,+9.x1t<\>G"o/ @*9>c6x|xiY7P\2ۼ(bmێ(F'~:tXjmo*6plZoyxDҮm5yeF!zͶዋ[O Xx_U3^g|7FYkꉧš4|hrTayFE[n!dmˡco&zI 0fuU%Az(~_y[;Ko%tM'OQ]V)%XΈ2?{2yםůiouggaxc56XbvWSbGs%"K5_GWoZT>ïR;*)g`9$YR@ E.(% \*%8ZkŪ'noC9:_-#VG@O*9rQ*(?aEϸr; HUQ*+F9pFw@O HUVs>ʌ-#VG@O(}ÕZG??² ?/VaDEn&PRT%iXh?oTo5|nsϵZQ@W<a?' V#u3 Y yXyQd;2p;T5K?ƾ E=I( ZRЌ9x{T.-`ad$ 'vép_p=oJZ.jk^71c!}ܒIRxgž)T>UԚ-a3-dXv0h՛~[Ⅾݷ=Wq_]k[/Ȋ|Y~f=&ŵz6@psC|@qs5Ö܊bSY)#޹+:k4 \aaD>?ie/ƒ^Mb̨"őP3ݿ?/ww? 6Zvv{1x 2eӏ_%,κa7 T<#W8^wx-wIot]ڔdHRw(N*t.?ugi4^ItG0G ,nMv!'yu:-RN{eo5j yK6@r}j/Jݬ:Yj6s@ z;[.Oį LP&ūT0[hYr$Vu?cL[J#b;Ւ@6!9 AW k~+ϩjZ3%,9 ?jh=:"fMf 0AF'*cx1R?鞫'HCSf~E`HIF*<<ڴ+JŕDm&6+pDŽut'+]^獩4#9% o<|֋i)\ Gb(LJuӵ-DG,4s4袹,]kS^8uk? z==Дiu<VԷҼ/ſYoH5۸X\/_~(UP`<??^)cúi|1]f_E!'Yv5+^ziz})5@_O pƌrU@'׊}yo. ;Ŀ((xzk\Il.]KEP@$ }Z]wQ/Q _/ % *()vl7’J[4 r9{,Zѯ]q6X_^OS4,^1QeѵH5h:msޅi~kdzeX3^1_; R4˝ZK/%;C4sv9K?Mu}iGjv@9";ڕu;ӣ@.CCnq( ڛF=j??13y$3[\^xTm[PryϠ_/w2[ڦk$:9]ReWK)$Y*l ^čOR—xv-镣,] X}+G^ 4{KuYZ R2؆Y7GBdkICFkq x4˛[fnbe)+Dj1If;xMa}z-Ew[[>|q3X7i%-hO8|YX'x͞m/"ԞXL0H@ܟtk@5+ ;gnԧ(#w j|R~ |;FCkyqDc^%7 Ec TK8Dk'9)9滏hvӅ4,K1'5|C&3BDɪ63VZw#EhF`ͽryǧ|3uSC>$k y퍳]B0RS'i SLG\i(Ův+ab/JSIYQEG/?(Wm\Or~Cvխo")?bxhVMgKkS|'F `x^l! 3~L,lvf8˒qb@$W!}+QjJ^Cmhz|7{o By94|] kG0{򧅼J%Pkm"%j:1,GV}+۴O SWzΛY}XpO2g zj~#xcNѮ$V6CwV/RVbuOS5iv\D2"x8R@b@]4=Z|Qwx7_lOkvBQIz9אo Ydv9kYֵM H/Brs[Zw$7EeDA +|G]g>KlȸDWp+m87_-sGL0ç%ZKo,j|y y9''H+t w] x_UygyhaR@8"mImInc.om_Jtz4B m'  s^kE$9Щ8l $מMWмY$ӗb[HXdfC?e[[ug,I]\&K`x?km^wqi7FMZ$aTkDakZIdQz%w ǤdG'>HƧ˱\tTW; υc?2? ǤdG`sN|+CW=#=<{sO h¿19訮w ǤdG'>HƏg.D+ƿ&qګs_ k+Ş2υu-AI4S""]!fcMT)˙h)N6z^/D8[1Ӥͩ?.l.v .8h"xG nHtҴ_j[_jncVv%*%{5ݴ]I"C+<YvqZhZ\mֱFH@0L?z=4kjImLҰ?9bU[ N7zUɩz׋ޱ]ZPi&- _:ŊKW:e4Nm句+UXejޣc[h%[0m͎֍Rq7Z? uI|Qx]K-H_*/W}Y~|57E-l`H`rܜkP;lW))E!O<a?#PW}VWliv/,B?ZbXOT*x3Wsw^>zDS%A!X%_Ð'$W> tDգԡ{5oI#* b Щ*XOT{i1|,0IeJm[mFX}<WU帻N{Ry2?XϮOXOT{)#ܖ։4M=ڜwv9ltR V4K}MS#K$S4rYj`#F+?Tg OG,B?Z=svp?Tg OGخ~ͯX-=QGɱWW#%ryvƒV&EPKIK@Or~CvIxM} )RR.*qS=|)|V󊓺keQ\NψŸ>#_ 7ڊv|Fn(zSgmQSLvF#sݓSNKB%`A{_ Ht˙:\#[«j}^iת^C%J5'fx3h$\|:Na쭹N}ُŚLλ>}h<#]jvb"&j҈uKۑ7HѠ<#v}Sl4-m'V啊SvOa)5ój6jd`Uw:WĞ,vlږl՞'&3BA@>OrO>hk] ,WE7\yR'N;u/_ލ ;=.]9yAUbPwt O&]&a E}ڍVmVC FO!Nzi\u=?Tu+#Sgigu$ٌ2GAW-nvP9uq[9+[Þt ۛVEK˗M3N=.+;Ulo$]N`4+=9hBvv.(?c`^Du&_9a I霃^*i>]-#v?G@O (}Õv3:ZG?´(U?cai t Тg9Wc? H]Q.+BQ*gai t ?:p>:?A5˹fvvbIaN=!^ r ϸr"(TPQJi*F4f\QEZ(?hߎ |19]Whr,'O_-ޔ}+I |#nz,sSs\juHЃЦ )(Z)(Z)(EQERQ@ )(Z)(Z)(Z)((P\Q@ Fi(PMJlK3D͓{Uʵ5 EdY㵷f8'FScQ#'A Mڟ93 1+%T08W)i((VnͮjvZ|R{0͌dhKk~Lo56 \/NUŖgĚ>lN{"Lac)_jEdh'еGtKkg e|D𝦓j iPnV$u<z6eOڬB[+،R/G > k,afC`O^0ЁIBѵ;=gKtȄߌ%i;E XOX{⍃sө gxo[cRl"{؃((((RnjѵFu )$1P2;dgpq'z'3埆;:촲8N5Ю爼EuJo#x쿧^θlw+`!kMEimvpEcjEU>Y(I]Qd=-:G<2YӁA@ E-QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE bGʛn $Pn>rUJG&Eb 9Io v 5;0V`(K'i3Ikь~g]((q^sb'ԴOZyQ,dYXx+yjle>mj:Df s{n۝2 8@;wX4uf4dXϹ]9L^jNAo'J,4+-i|QK,쬈5 bNWkMp B 9 {H/,l5[ #oAi[KzO2;{f|zug4,m-V v皹v|94ڵi4(?ȏ—k{6u 9@ 2jҵXxt[OdIZ3 N?Ҧĕ ~H w=e`xe&~_ fVWc!pG=x8Mc?mxQ\iZi$C:pOѺzgz6iZ(0F`8<Jh((-QZxkú6K(Zi1p8|I'O@KiVu}ppcSoH^P^o?C߉ƿi1FPB8=2Gj^u[E/4mZ 2/Hx_.yp1֪O|JеtNNєc,oKף <1Qo- JSqqnr&c6@L>_W7nP]Ʒ*NWu |xO>8wO/,d6IFNi`pk5ݯɴ짎ymRȥy=0}k_gvkkjwqCy,QϴmVtVsׁ[ׇkÓZ]6hDьa jogthCQM }7XkXBC{)/1)Nk<-W!pܴc@nkRmE\i-e7Q.pay#-뭠(-XB"(U?Il>(aEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEV:꺣<3[~Ч>j8sKܳIk$_kR̞w"C"㜍UKx-!pXs8$~%((@ ھ{=Ju? SJd>ና\_BQI]i:EΙFV<~IF֒q9ox aط6kV2I$ddV9{;Z$8x}4Ky5 ǂI,#D91g'8P}BëXM4V6Z*dr@ܮۉl{nKu]Cy$2jϐJ)8$ WcF1ZNJ*+m:L5=2 1[vU(dݎ@s^EeTon4  N8Am,Rޛ.3:/M\u;G}RIyA NO~W|j7|FkDv!qz$}v2왭jf.q-dwWOռx/Vkz#8ەpc:FS9Ѡ(QE-yĜ(>}2n|5?ܑHFgjΦ,i#p8z\_~$ףwhGb*hEP^,׼_m?Ǡ}5/%1)i 'ڹ?5 [8fH{ m2[۽fTo3x֏Wꖍ{C C&vu 8­S]Z n5XMۋ.A0(S@+&x_lt[G8|.zs\- i^>}hV>*̱{m<&A^פ^3/k`,;ߴ`i nq\qCYP %,p3['ԼEqy2iXæC:]/p8Fm̀:`g9mF܊##8=Eq4^+b+ɖ񢷹ceB;Hݎs] `{EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPMA /! A;Td`;}bfO_xBYѢ7f_>ЀDY^9y&)i(((((%R撊- aV܁=H HE4&EyօxVluM ?ieƛm35[qUȅ6uXZI 5(1_UA8{ \G#^/WG#^/WK?̿3_ފ?-?-9# \G#^/WG#^/WG$~?/7|[/źH5Ύf9P0 +jF_C?*F_C?*H29_vW׋yoQ׋yoQ_3_MG4+Tmo-gh(W Ҝ9m.`.i(((((((((((((Z1@ E-R@ E-CV8?6wTڲl3h9'jͤf2I| F́Ԟ84R@ E.(%Q@Q@Q@Q@Q@țׅj_umU [2?ȗל_z'y4Ob…  _N2i6 ݵs' iCݼ~nŷ\\ۅN` gӿNW\}㯽(4e lͤls ¶pz*WMصmRnx-ѰD- rsֲ[%Kx7'3nVA0Xӏ~#u.m5u+)4땝LK|WeQP}=A&gSߜ~)O[Űe[&v渳ym9pP=2#U#CĺnsiRB)'n"OBWn`k}BXbA$ʊFCy4FSFymyq[ʲ.}2 VkoţA KEHK+۪?1\|z+?jzn x1ia.d 1=OPYx]4YWkZ>"Q(Ȱ(((((((((((((Ex-W  mUgXJCoA8OL sqˬi{1(21 2GIWV6Ѯn-?{G ca0p\0!ޝ+_8մ [R7:yęA x[$-NR{k)#Ip!8XO{>څޓ&, hXz㜜wz?qxii(ՒoDLiH1X:&o|C/9ԫuDn$sSO Y.&Biaw.+v۝sjQi:^ :KM"9-?" ǜvU~ 1|-VѬtDI.t0:IGg[͞ks.wW4p#;8g'zWxsSZLʎ720q府㞕4m*{M?X೺5 q[ˑ{csI/;]vuhڀ7q"c)ɯZjJm LI f mfBs3XwѤn5MfxN$-yp=yĚ5|?h_@ 0 1$0:NOM?D7<[{xĵxiU̥Y2󂣌|+?G "Ok4[HpIZ*@]``+,>V:O,"h;{ "R)gbqqQhm4+SakKҍ}"aҒ]?KR|YcunW[}7_Ryٷ>yA۬YR[8iJ%cr͌朽o_koJ4uR VGl,u&k+B{@\Z\RAV Ђpk?m_I6K̖#+#U0Ǟi=?_/]oouFEei!v(]%|cqYbSu}RM:6.OΧ [$W1xQ2PJQj%(3o++Y4ִ}?VD7~QEaEPEPEPEPEPEPEPEPEPEPEPEPEPQ@ 3IE.h%4PQ@ 3IE.h%QEQEQEQEQE xOڶURүldbsXu/D8[XxKĶPZZxƿaGAS;ֲ[o~?g4ҊgMwş97 "GiEq&|t; x>i?gMqesҊgMwş97 A;J+wş97 /g_;_MgJφv #š>%5q)mxbOAC=:ךU'{ lJ(,((((((((((((((((((((((((()sIE.h%4PQ@ 3IE.h%4P撊((((((((((((((((((((((((((((((((((((((((((fotoxx-18.01.1/images/cartoon.jpg0000644000175000017500000015572213222767271015312 0ustar micomicoJFIFExifMM*V^(if%0231#0100Fotoxx:trim_rotate|resize|sharpen|NE0Photoshop 3.08BIMfotoxx,  http://ns.adobe.com/xap/1.0/ 1000 1000 0 C     C   " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ??_(n渕#ˤX3s_l>|au^jl}'?DV?ڽWc?}'?±Gڽl>|?zG*A ,(aOz/t/1xKKDmϪ`Y3)8x<I MN{7z5毧թ[C-˰ ayEo kʻHOQesC#&j&/MtᶐrqsOt55\y4,H"˃c ;- >}^ <* kaچjMy 3cn9S)^ hͮFo[4 y!SyҦ?{l>|Ms $Ot?<-3[ƍ-ZoǨ5"@s|Oւ_spen4a|ST]5$bE1T=kcJ"&DT| e 5ŕn#WCFAqgqkj6Y%@TM囹8QI{ %7RmH}O,憗mjËhWjBͅ9uM!U={kC x/[WwY.SZD^6WQ' }+)[#P3Lp p}j͛S7.]b` \wwr^lv0C*zsVTbBef H ?\u,uѕ^FaS<2uP `8~{B Y 0TxȶjC wܤv:eV5n NX'f?4_ WZ[`Ln=o^ΑǍ߇^rjj\_0)sJFaM+  eYոf$,g=2p}+З:x-yTG8s8ھGm&ʼR,) a)Q<޺cӡEwPrHGL+1٦6~'c"oo-ZY?)>'ksmmżO|Lqּ7C+$B0N(8=tĺfmo%c) pA @x>'nvz,I]'6{K)Fr(Og.jT2ehi95ox]x,#joD@#-:b_,RL*ܰBĞ31޾?$RkRT6@:rkƩˢv gգo> 96DǓ'tpF8o^[@Ю|t$¢~o8s^5]]Y٥W`wAkQW bكq %9C!^3}Cr:'¯IᏇK4X|<q_00+|!a+/<xxE S?!y5q7?$KU@e#9W&Y$a]Pƀ.T*A#w[q+ 8{߂~| iC/i%[c+oOE;'_}Tpvmל*!Zj>>PUNG/|HJi.BN+X2Ȥl)7?{]Dxv)%c` mǮ}ksSi;i.n QeH?"Y: ?i]cq:UG#& 0{¿0xɷ%|*CZ649,sV44F=#r| 5jƻs8 v5|1Rl= S>Znx[Xس$ClC")9$.3Ҿ]mj<6PZPijr d͜b 5 Du{dYU6Wi |F>LFkTQr~},Vrj\9!y#z3_$o'»)c 5=ėK9L888+fVu 2)o~(.7mKV9n-~jTgkIJ\n7Zkw%9}PBOI韴w/\e++kD!D;+PA}5|CmͼUyj S}ȹEM(xu5]L.φsjB8A-`2[ub•$2HXvum*e^y6|+(PImt¾CMwyi:Ok6a\;ɐ"r6H;}ϋx\3Ma<1'Y?,x@8E g$ִi2IdxʒeDGs ArA2Tjcu%u4GSR.c_Au/<>M t/m,!I@Qo h 𕾡o4wh֌ѐSzqvxVլ"mSP` a]EnTIWAjCtPT)EMώ~7Xjs\VX?.fHdf 8*Tco.<ђg?*+v_<7|g{ ž-gӍI"i[hC+j=|-xf?V l;`襰 ~J.½'Lu͆K-_<BAЏ$uq,lVuݩ6ȨjG x$M<>!^ϳkM蟴fxfiv͘wnyh Q'H/ 4p/˝ pΝN+pn$i px'SƜBpzVM>g8 {1 yw>*Ӯ^{FO.0Dx'Q qa+/-D/\֡ث 9WCkQUiI@JlqF?=InoYL2dWC#9<;$𯞻thA CeJA{R٣fciijMhMte2KgsؾC%b!n8Q=;]x^XͲ,ɺHIǨ$u4)KTOܒ6nJQ8UoE/C1 ?xz3. *H%r22>U+_X˩~RZ--V7Y0Ñ^U@llK{y|U,YN>nrGC4sX)IKG!R{4_? oi(uM.Mۛn\}>\&3F_%av,m{ +~1_ios)Y0AhYLHP$psڶ!OFz}k8)ۛl‚QGBs_5ޔ]ߎgK/&;E׼y: ᭈ],^iV}4X␦c}[''5jQ5 xJYfj*3,k4 #43YGAUu?m~:Դm.U -2Ϲ,䐰vg<.ۺO=6Z/_G[hq^fBɦDd;ط+`b?tu_G+teε%hOB9 e&S@v[b. V, s^fqK Ulz/s3᷈Yx#iڞic9Q^ʵ? 援Z q7?.r|8?.ls'֏Z r|8?.r|8?. i>}O;YvG;YvE\I'ֿBܟ?? j?ܟ?? j,^>!xCIJ|UqoV],cC4NTrxj 㗎<;}&;ymݬ-%dQ#Y^U v}qA]#QqA]#P{ߨ'mO uyc K㍭"Z>{q3uRYV#qo,ıw3I$_Og5Og5מxiy'oRxapztZ:h$ܿr5=Ӂ_ߕ_:u=gcmd/!#YZƩcn{cgH/`IsmRAON{W_oK]i Ff~+KmB-ۤYV+4e{b8ힽõźְ<=7e_2I)[rO$ 4W!$uf+#.Yio_:;r$t\q*[_ Zhrڢ%K0=?yJsB25qk>Rږef$w]皜:BAl%8$HHP@^xiNWSziYo-m.4GnrO~[xRHnff nb>ly^U 䖒ȷh?,!An93s[|@ 6KǾLc.iTOW.5tnǩk[̱LoHh@0y 15z:o\[(vTpScռ}m$=3?Gw"[6MYg?.^^McwQlUkgVs\9s>qR RBڥAW@16kʌ#EekJOMi֝ ď+K:}GM+_-_m^ZH r\cn 7CѓX[H[#g#O1a^]|(>״y `6:$8U-kMI'SV( spy5XRRT@Ⱥmhxc{ww#ǃAqw|^Ee , U1\ 9`N&9qI\tH>ƴ(h͊Q1Td 8n7h~7*&6F2IZS<b3 fȓf9gyV6>ZnY^,p J59#I+ƒ%'6]+~5kkN>JM5/t՘wpv)reޛbVĴ}ov5$[39&8a:"\\+'(q+Y< P Qhb1~`IvKr8Z;-;7S^2Ln.pH \V-ԚѴeؘ `n83u.'ġEDNXO=WeGޒHwį?4.2X\Q^yj\9g,ξY\c WȾ69C\_=ˈW  ^/ [\߉dյ3yv|{Xgd9LV#[6+Y/ ;ZBč̻vwG\ך*ϱC u ]i"kM2iD; Bv21ZLi*_g{ eC`q^EmCKaw\ID%r:;/ Q9Y (qǕ;cӭ!y rw`y3=#6,Vnߔ \]Ֆ9#@Cl,sVזjјéĞJY!Z+Ǟi5+_ wq$RZ^yq0Wb5k?\ם/ Jd6O:Z+o%F@PNKiFI<_oϪ[-jҸa' 8d.@0tt*>W y%Fo*L̝HPtucW%#ysK4gpY 80:yPwSMrc!@ ;GoSW=e]d4^P@6V bL_9,>.YG1-."dFDA?08>Wjs\06sV<5/2.CG͸$F{c⳯FKƪM6nG Z?6~&{m $,Jq|!r2j>t$v1 6 @G9qekj&ͧ=K9 d֟]Mֺ\LQF/: z:tڧ%ك+^;vZ!K8_V6X~@8]I"h$`-VXb0n1^ Iw26H`q< m!f,YT#y˜]Iw6jVVLk9l76:ҮZUVg[(Ix#E0ۉ:vɩ| qx*=džYUZ2@8W5s{!8\ڴ{o~&)K]Q-M.so-vHc2>|r2G8Ocykhup+r[b7e$x 5ڈ<%%wS3B#^X*AᯉzՓUi25H9$z kV}fuK5^c?g2/s'|P᎛{KMcZ,m %G`9P7F2x,~3|R ,>)_ͨFGK3fnXhJ`y')~ imΣ^hV#UUUA$ED̷Tuo|C߄u˪j0Od"-$r8>ޝ)A}3냊&/|\e5mz܋+b%+X O/ .&<^{) ui"YN/ZJEa;4E;s76|CRqk[  !o o&~\suu&~=zy7?R֫Բ('t?W+j ȸ8m۱?BS_Zu/v7[;Ec y%2@ʹsl qoq&XxS=H"S`bDX a~Tc#= mFuW%ʔq\QTVy=+ռ7>Oˑn[O!ӊNotHfy'kc?ϯzo&9v-S^\-,l92YkSW$2[ep7 ^.|E-(ؤtbF m_éf5ffܛ[vSOUc>5ȧ"<~ɜ(r@xmK0l҄fXrefNzRx[E:T%l-tq:{Exp\is)]S VGiѼ`빔w (rL^zf{u{}z8q *猱8Oö;I+8?98}߇Lky2ʘC8UzpG ]2H;^G!\mO1wieR(:u}JuKu=%rfe`3K2s߈ nt297!{I} p,M42ZD,OxeGFRmOls3í"]յX4};OF]opIr킊CGZ|D nj5}6W9_*=>q!_u z澂I?[[L QNο+Ն )N[s18S覹+UjGs!cS$UZVOOhַ[.'&;I6o݅ yu7x-X286iߣɫ[\+$ےq9׌A=)G[M>Ndv9-FDxp3';]5/#XA|4~βlx7=Hϙ ی*eR]uax8n'@\+4|IG6gl^Nj_6D6q鷆e۝Xs/3^J(BO}]O[ɧm",%yKrNʏᯀ5ދ;E;B*1=q} IY¨@ 1 WD՗cR?#~/7?m Z]er9<(cڼU}Kkas1.d1Yd߂^O=a>, cYcdfBAE| K"bI$Or$.I'kCe'oOC$GZ5oC5:FRUexj k,$3Ⓚm>sύ_W/Ze͇Vթ4kD8PO2ĀX^)v˨fg : 5ЕGOU7_?Og ܬe\mFF92?+,Kt*_HtYha8q\F:Zݤpl /?/\kI:c 6=k*ɿHZ:}^?GaxZD<3fA?$q1Xl=}-4N5wШ^1Xk[{]F!R"Yٳ74aAS; YcmG"|Eْ3Xs:u{٣ d]Eb2GkO Ow ǁ "[NM4wxVhn VAOݗ]~8{;/=O4 'Av,_j.1Uo3\G⍀0}ieX?j Hϰ'bOm^+Yƾd. aKs '鞂ΞsZ)na:ik݂ 28 h_Z/ɧZ 듵^IXb%]?öC5W~"ūaӤyY6>p%_ܬNy>O_SDi$n`v.ȋ5ϊf8AQ{Q\F T`LӐ?DER~p Yv4mNүxmGLTn4PEc2p `uEΛs-cP}:ͭ Z8mvis򑼥e->>mH irZ$v;5Ū1u1 Vb[!p͐*|ߍu湻O05ʺ,8"k~;ѮRK{c>`-\d;d׶i6 Ȯ:I+6Y? cV:i3c6V`a`a<=jKFeg:eGu$19,Ǣ+<ƺi`TDk~Zgtk U!x\ޮ9v_)ac߰/Z\ꖚ} 8Pkѯ,^H c޼ x+~@f壛{ _zi \ρix ot.X*L'q@M<}ﯸPiaM{ۀ҈DKdehGL^&ᣕdT;Z&e>ş%鿉/<{k0ZN }5~u\x=|aoW~j[KLc A0#y]uŷ%HYbiJ\lRd0($<Îw\D˨gfڲLZúnNВ Ŧ gTL}*9犗RU~[.Iz)F0V%>p>ɶc4zQR|OÉbޚ! k? eEUWU6ӧXmgޫF^.#;^Gbiw6z5,|0n.zDz]XZX$rhᕗ"'c~-Z *IK)OϾKxZRFx@rv@&J+7[oVڴVik V(xdoJ`U>hԂ9u3[%<`d,.DwN>剁8O\i>,o'WV6^vZO K;?^:_,OYih!bh]=9c?>/]^Wn/V*x`"]ool'н)59FZW$eP냒zG9SԯY+%;p8 GDhi ƝgKNG8Tu=A}\}V3WrJ7y͓zeq^oiGF lӖ,,GG~5Pkˬ1H@}+Ξ8xγ^W}F$2,Jֻ*5cYV#J䠈 NZ6cs9kkHiųgQbF[=+'y`E RO^ӥpAӌ{q]V"mftz{ҍKEh7f5H* 9'۠CDr ^E =yJ="L# }? I'4vp+nn9V̋ :V64q‚@U_C9<סFz|㨞:Ni@.0duM _MAKIy _e ˔kjoV~jV&^$3ˌǷt*3m;I4%i-q֮ǖ23x}~nl9;`Fe8+c¾pBLdzףW/{I */Kk-v~ʒD{p{=;·w9l\\cK,`fz<%[lT6S$n!p<,UNH>'Xy BVc㓟+:nuR%yhX>9x h#kQJ2 znE9 O?ͿzՇ!2Jַ~$E y }q_N Q ִ%F X,I$M{,:r+xR{+{HH^7RжXx>YamWQ̺v@y19!H== |GE,rCcw[$HNc K]eza=|;^6>0ֵu~в6h%16ጓ^3B*pο' PMO>2|E8dP4{a"qBqxzg, hFq[.2qgEKOsk U%VC${C+aX9o|E41=O pЧs-hz]!RpkN2 sTx$w#ڳG~~44;yX5Jq»y }MwPwvR:jvZAsL :V#ҿ6n؎ ^|@yGu H~P$Ҿ ^|5WZgXmN d::q¼R͡+hCsc!WR@5WS@~[p9Z#[ek㶥 Z]^#7ge$ofRv ^we%dc8>Z*Sk^o]2(,T Vs޸Yk/A$?S%fѶ;Ue'i_ .CGx񳽨\1;r@x21dF>b%R,zo%"^5mj嵝v LH@Vچ`GjBѼ|D's6Eߝp;z_7hz;E]Z0dEĊ{SQp<LoxLrtb!:vλz.4bX$HIyKs_u?2хѽ9 ;2wos:qӥx_d Me0 @8\D}𸯮cH"(TQQ3Ta1Cޣsfj1]m [־RS]GC$zg@*+`z/n6Sa.s;Y(FWkH/#"s+xI\2)ׇ&nC&oi:dve=U#Ұ.he7|'ky exu Zz/ECuiڵ'^G Jc.ꟳ=u R!"6ˑE~Y{W.b=5[jZ=ӮerTW85ѮFnRI[7k:D\ Evp$qsVr彎qiٳ;암8D!>++| tV@|9?E}\>yrݝ}QZyk5m/G:M Ȗ-ct?9:wG#sC?6\񽅔KtfVpj_%׵i4 8/ +=*70#WQGo/?+-|_~Km+Ne6",U@Tn~w)#>^h)̈́X,Đ|.p:ײC~ i ~uMZJduURU>SIYWHռ[^Ӯ4E$xȍc+ tǿMk߯XIr?~ i#HR<ݤT9ʟZ *tKi/ IHqVmJ)[ *J6>ȅע*6˟4xVX(5{fuqy+*1ɵʓ<#wRΛui]KLYil Bђ@rk_?Cտ U-@V֍~M?>ZF,xT=1FT^ÒVi0Ii4r~ITO?1 s_7kRWPFrwT~J SskU"/xV39ehl B7`{ou?gnl77ri# T YӤN\b7@>c4P|9I~{kO`zUøO_!kGk`Ă;vZFTo7? 9Ӛ4:WŌݻ<J>!~ӗ=lN|cwOlw$gӰW|bd>P8H<#f/~{`޵OQCwFxS+O//ܽնM,TpA-g8ꉜRT?^ɉ1'ϥo~ |osE6ss֕[HeԍN:]4:=Et$7 n"9Fq߯Xu?u覩2:ݟ <\.6#W kE»*+kN$_p||,Ķ2aC~%ow݂OخfZ2JɘS>B,5Pi:e܌I6nbC\Yp=> `7Ǹ,C6{{X+* A:*&Lt$˯x{[^ė=>L7 Uo,#Qt?=XN0IGpQׇp"l4hF2$޼u*K ]J)ug/";bD0|( Xӥs5>an$!mLH_Q]]ҺvGW۬gwktsc&,jGjE>f0{t85;\zΏ2\` q]>- ?q7kۨ279$r@xםAfY ^]Ӓ;}B-9"di*BAT-0'!zWBJOCkTy7k?~Y.K)`c-?o|I4$ gE.h$WE.X^G?h13f9IW;jdZ ]v 95c,,-QQAi2MkHIeufrByϯ|ycxMcC5(ehNb,J0H gf~c|k7O_OcZfA% ]H+@Z~vo^|K;c&r WsI 1]<_}icY꿷>.|-[Ҥ1x=ż>.?RGo 5~Mʪ 7F˟q5=A_hf8KTPvf< xVilK4c2rW#W/c'kDʛ"Yn8݆:NlW!% |Ekr~ ޺.NQ6 `iVޚ[V^k 8RGʕU#q_I>JKa Κ-r>q! W4\o=[inǟ u.?u./k,ћQ{gF_^sMYӌisGS-c|%?5ʾ6QIn-mm3WXM,*J$B$R9}V{Gb+oqbv΀򍃆+#&56Uծ4 ڀp&4 ~=#TW6ό?>fyd]Mga3LQO٤s5o0Xs-,eQ+Ǟ*F}w0faIp%#3 qֻ_wl Y5mI*4?zFoF{^6" zԠ#;|RK.X*|n!`^dget[k+x>X vܣo}+%^lr:;mBq\>TbC_5-$z B+5Qq^w3[d3(D3طc-6uڮrmt_KE|y/m\\^%NmlUmX<ǯbbֵ]DI (ѼŌeqӊ=B=(]¨nJXh'M^oLe Tzs~ WoUK\1:ȜI# c (e?ztV: 8$Q؊_~3C ֠׳cʳӗϕӅjڏZ%I\0i ,Y.XH=\uW5 ' qt|"mo¯A'Yy$PDŽ3```t??W+:V?ymC꬧ЁSV+#7G*H*A8 56V2| ⨭xzdsۯcz\eXv5YǞ 'inKxbqu ;@pΠg'wN.2!Lr ̓Ӟg=-_!ng5Q{; &G8aG|GQ\Jis}di[??gφ>QC"^ŒYqHE'%>lM0ʬdp랔WGdxos* #ƾ7/ kOwӛ m{9Ec|/Co[5ku?LR?)B eTz:/Ƿ5QMfk x#L{Fi%}N 7`c$TdOeH6 Eݽ!7.de^ʍgpk>4_@XmvZ]ncWFcu1rJ O,p074 [=*^[jkқŵk_9bafS,Ani_ͧ_2QN֖G1|2Wѭ<[uA{(rP~f^m7BA*8k{0 m,ibMx:?eխ}PT1\rʬ|]Ny|[c9'u2kiR$P3E sI~~ tώu qɦG{â:= X=ٕ||玕z]_OdtKgPodE;m*yItbO 6{+]ou4k7L\ b`줫3*M7wm5$ \jWS qnw|( _$$گ>!|K񮃩}@O'Q)o52vHD0IQ[sx_ajz]B~˥\sF r5+r)~̿OFtQo6"ȑΡivsx+ƁjGEmJqqyq{}=PZY܅UUUU]ku!$QEw:bzEz>Euj%rm2#APIr1\à&)e 7e[{ayn> 4L`ryjXK=#ܓufFN_!W}/QäZ1ww-dhYORUCkgh^x L,wd/jii ̪)$q#W14MbE$3ϡaZg+zuSPHߴBzG}^ Cj9EھzXuym-aSby4}~'+$H ~C߸u#/v7_sᭊ]\\Na5lDD #z󟃗׆݉$`߅}ԴERWqhJUlwWsLZHN.?Fy:^?S~%K/>UI`f,V4 ھ!O'|Fu,̍8@IU9=b}? H[}sѯIt<ڐug~mSikh|#a >Rv Ϯ~qFlRIc=þ }(Y_Nk&0BGʥI3¹]1:Q_hvNMUH&|ptk7#+|qpk^;B*v?k]6oáiY3820I=S۩St>i-ZM ౽E|t^kao)זTk %|zW+c,R4"erZInk..4츦EkS+ƥG{Y䙖M.~ڬv>fFi?U?R'Ҿ#y~_s wms@b /:1sSkNE?b !c[zk %052.H+*Ngtcړz ݲivv n+8HتJ2,qq׊P};VԤ׷WS6'bz0=+ۿoº}٤X#{n#